From 0f8c4cb56f3ac531aee13033a501538dd119eced Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Tue, 7 Nov 2006 23:40:20 +0000 Subject: linux-openzaurus-2.6.18+git: Attempt to update tosa patches to 2.6.19-rc4 - untested --- .../sharpsl-pm-postresume-r1.patch | 30 + .../linux-openzaurus-2.6.18+git/tmio-nand-r7.patch | 593 +++++++++++++ .../linux-openzaurus-2.6.18+git/tmio-ohci-r6.patch | 929 +++++++++++++++++++++ .../tmio-tc6393-r8.patch | 800 ++++++++++++++++++ .../tosa-keyboard-r18.patch | 515 ++++++++++++ .../tosa-lcdnoise-r1.patch | 157 ++++ .../tosa-tmio-lcd-r10.patch | 472 +++++++++++ .../wm9712-reset-loop-r2.patch | 44 + .../wm9712-suspend-cold-res-r2.patch | 16 + packages/linux/linux-openzaurus_2.6.18+git.bb | 16 +- 10 files changed, 3564 insertions(+), 8 deletions(-) create mode 100644 packages/linux/linux-openzaurus-2.6.18+git/sharpsl-pm-postresume-r1.patch create mode 100644 packages/linux/linux-openzaurus-2.6.18+git/tmio-nand-r7.patch create mode 100644 packages/linux/linux-openzaurus-2.6.18+git/tmio-ohci-r6.patch create mode 100644 packages/linux/linux-openzaurus-2.6.18+git/tmio-tc6393-r8.patch create mode 100644 packages/linux/linux-openzaurus-2.6.18+git/tosa-keyboard-r18.patch create mode 100644 packages/linux/linux-openzaurus-2.6.18+git/tosa-lcdnoise-r1.patch create mode 100644 packages/linux/linux-openzaurus-2.6.18+git/tosa-tmio-lcd-r10.patch create mode 100644 packages/linux/linux-openzaurus-2.6.18+git/wm9712-reset-loop-r2.patch create mode 100644 packages/linux/linux-openzaurus-2.6.18+git/wm9712-suspend-cold-res-r2.patch diff --git a/packages/linux/linux-openzaurus-2.6.18+git/sharpsl-pm-postresume-r1.patch b/packages/linux/linux-openzaurus-2.6.18+git/sharpsl-pm-postresume-r1.patch new file mode 100644 index 0000000000..409daf03e6 --- /dev/null +++ b/packages/linux/linux-openzaurus-2.6.18+git/sharpsl-pm-postresume-r1.patch @@ -0,0 +1,30 @@ + arch/arm/common/sharpsl_pm.c | 3 +++ + include/asm-arm/hardware/sharpsl_pm.h | 1 + + 2 files changed, 4 insertions(+) + +Index: git/include/asm-arm/hardware/sharpsl_pm.h +=================================================================== +--- git.orig/include/asm-arm/hardware/sharpsl_pm.h 2006-10-31 16:09:33.000000000 +0000 ++++ git/include/asm-arm/hardware/sharpsl_pm.h 2006-11-07 22:08:41.000000000 +0000 +@@ -26,6 +26,7 @@ struct sharpsl_charger_machinfo { + void (*presuspend)(void); + void (*postsuspend)(void); + void (*earlyresume)(void); ++ void (*postresume)(void); + unsigned long (*read_devdata)(int); + #define SHARPSL_BATT_VOLT 1 + #define SHARPSL_BATT_TEMP 2 +Index: git/arch/arm/common/sharpsl_pm.c +=================================================================== +--- git.orig/arch/arm/common/sharpsl_pm.c 2006-11-07 22:03:48.000000000 +0000 ++++ git/arch/arm/common/sharpsl_pm.c 2006-11-07 22:04:20.000000000 +0000 +@@ -584,6 +584,9 @@ static int corgi_pxa_pm_enter(suspend_st + if (sharpsl_pm.machinfo->earlyresume) + sharpsl_pm.machinfo->earlyresume(); + ++ if (sharpsl_pm.machinfo->postresume) ++ sharpsl_pm.machinfo->postresume(); ++ + dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n"); + + return 0; diff --git a/packages/linux/linux-openzaurus-2.6.18+git/tmio-nand-r7.patch b/packages/linux/linux-openzaurus-2.6.18+git/tmio-nand-r7.patch new file mode 100644 index 0000000000..b6ce56eea4 --- /dev/null +++ b/packages/linux/linux-openzaurus-2.6.18+git/tmio-nand-r7.patch @@ -0,0 +1,593 @@ + drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/tmio.c | 554 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 562 insertions(+) + +Index: git/drivers/mtd/nand/tmio.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/drivers/mtd/nand/tmio.c 2006-11-07 23:31:12.000000000 +0000 +@@ -0,0 +1,554 @@ ++/* ++ * A device driver for NAND flash connected to a Toshiba Mobile IO ++ * controller. This is known to work with the following variants: ++ * TC6393XB revision 3 ++ * ++ * Maintainer: Chris Humbert ++ * ++ * Copyright (C) 2005 Chris Humbert ++ * Copyright (C) 2005 Dirk Opfer ++ * Copyright (C) 2004 SHARP ++ * Copyright (C) 2002 Lineo Japan, Inc. ++ * Copyright (C) Ian Molton and Sebastian Carlier ++ * ++ * Based on Sharp's NAND driver, sharp_sl_tc6393.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define mtd_printk(level, mtd, format, arg...) \ ++ printk (level "%s: " format, mtd->name, ## arg) ++#define mtd_warn(mtd, format, arg...) \ ++ mtd_printk (KERN_WARNING, mtd, format, ## arg) ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* tmio_nfcr.mode Register Command List */ ++#define FCR_MODE_DATA 0x94 // Data Data_Mode ++#define FCR_MODE_COMMAND 0x95 // Data Command_Mode ++#define FCR_MODE_ADDRESS 0x96 // Data Address_Mode ++ ++#define FCR_MODE_HWECC_CALC 0xB4 // HW-ECC Data ++#define FCR_MODE_HWECC_RESULT 0xD4 // HW-ECC Calculation Result Read_Mode ++#define FCR_MODE_HWECC_RESET 0xF4 // HW-ECC Reset ++ ++#define FCR_MODE_POWER_ON 0x0C // Power Supply ON to SSFDC card ++#define FCR_MODE_POWER_OFF 0x08 // Power Supply OFF to SSFDC card ++ ++#define FCR_MODE_LED_OFF 0x00 // LED OFF ++#define FCR_MODE_LED_ON 0x04 // LED ON ++ ++#define FCR_MODE_EJECT_ON 0x68 // Ejection Demand from Penguin is Advanced ++#define FCR_MODE_EJECT_OFF 0x08 // Ejection Demand from Penguin is Not Advanced ++ ++#define FCR_MODE_LOCK 0x6C // Operates By Lock_Mode. Ejection Switch is Invalid ++#define FCR_MODE_UNLOCK 0x0C // Operates By UnLock_Mode.Ejection Switch is Effective ++ ++#define FCR_MODE_CONTROLLER_ID 0x40 // Controller ID Read ++#define FCR_MODE_STANDBY 0x00 // SSFDC card Changes Standby State ++ ++#define FCR_MODE_WE 0x80 ++#define FCR_MODE_ECC1 0x40 ++#define FCR_MODE_ECC0 0x20 ++#define FCR_MODE_CE 0x10 ++#define FCR_MODE_PCNT1 0x08 ++#define FCR_MODE_PCNT0 0x04 ++#define FCR_MODE_ALE 0x02 ++#define FCR_MODE_CLE 0x01 ++ ++#define FCR_STATUS_BUSY 0x80 ++ ++/* ++ * NAND Flash Host Controller Configuration Register ++ */ ++struct tmio_nfhccr { ++ u8 x00[4]; ++ u16 command; /* 0x04 Command */ ++ u8 x01[0x0a]; ++ u16 base[2]; /* 0x10 NAND Flash Control Reg Base Addr*/ ++ u8 x02[0x29]; ++ u8 intp; /* 0x3d Interrupt Pin */ ++ u8 x03[0x0a]; ++ u8 inte; /* 0x48 Interrupt Enable */ ++ u8 x04; ++ u8 ec; /* 0x4a Event Control */ ++ u8 x05; ++ u8 icc; /* 0x4c Internal Clock Control */ ++ u8 x06[0x0e]; ++ u8 eccc; /* 0x5b ECC Control */ ++ u8 x07[4]; ++ u8 nftc; /* 0x60 NAND Flash Transaction Control */ ++ u8 nfm; /* 0x61 NAND Flash Monitor */ ++ u8 nfpsc; /* 0x62 NAND Flash Power Supply Control */ ++ u8 nfdc; /* 0x63 NAND Flash Detect Control */ ++ u8 x08[0x9c]; ++} __attribute__ ((packed)); ++ ++/* ++ * NAND Flash Control Register ++ */ ++struct tmio_nfcr { ++union { ++ u8 u8; /* 0x00 Data Register */ ++ u16 u16; ++ u32 u32; ++} __attribute__ ((packed)); ++ u8 mode; /* 0x04 Mode Register */ ++ u8 status; /* 0x05 Status Register */ ++ u8 isr; /* 0x06 Interrupt Status Register */ ++ u8 imr; /* 0x07 Interrupt Mask Register */ ++} __attribute__ ((packed)); ++ ++struct tmio_nand { ++ struct mtd_info mtd; ++ struct nand_chip chip; ++ ++ struct tmio_nfhccr __iomem * ccr; ++ struct tmio_nfcr __iomem * fcr; ++ ++ unsigned int irq; ++ ++ /* for tmio_nand_read_byte */ ++ u8 read; ++ unsigned read_good:1; ++}; ++ ++#define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd) ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ struct nand_chip *chip = mtd->priv; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ u8 mode; ++ ++ if (ctrl & NAND_NCE) { ++ mode = FCR_MODE_DATA; ++ ++ if (ctrl & NAND_CLE) ++ mode |= FCR_MODE_CLE; ++ else ++ mode &= ~FCR_MODE_CLE; ++ ++ if (ctrl & NAND_ALE) ++ mode |= FCR_MODE_ALE; ++ else ++ mode &= ~FCR_MODE_ALE; ++ } else { ++ mode = FCR_MODE_STANDBY; ++ } ++ ++ iowrite8 (mode, &fcr->mode); ++ tmio->read_good = 0; ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ writeb(cmd, chip->IO_ADDR_W); ++} ++ ++static int tmio_nand_dev_ready (struct mtd_info* mtd) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ return !(ioread8 (&fcr->status) & FCR_STATUS_BUSY); ++} ++ ++static irqreturn_t tmio_irq (int irq, void *__tmio) ++{ ++ struct tmio_nand* tmio = __tmio; ++ struct nand_chip* this = &tmio->chip; ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ /* disable RDYREQ interrupt */ ++ iowrite8 (0x00, &fcr->imr); ++ ++ if (unlikely (!waitqueue_active (&this->controller->wq))) ++ printk (KERN_WARNING TMIO_NAME_NAND ": spurious interrupt\n"); ++ ++ wake_up (&this->controller->wq); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * The TMIO core has a RDYREQ interrupt on the posedge of #SMRB. ++ * This interrupt is normally disabled, but for long operations like ++ * erase and write, we enable it to wake us up. The irq handler ++ * disables the interrupt. ++ */ ++static int ++tmio_nand_wait (struct mtd_info *mtd, struct nand_chip *this) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ long timeout; ++ ++ /* enable RDYREQ interrupt */ ++ iowrite8 (0x0f, &fcr->isr); ++ iowrite8 (0x81, &fcr->imr); ++ ++ timeout = wait_event_timeout (this->controller->wq, tmio_nand_dev_ready (mtd), ++ msecs_to_jiffies (this->state == FL_ERASING ? 400 : 20)); ++ ++ if (unlikely (!tmio_nand_dev_ready (mtd))) { ++ iowrite8 (0x00, &fcr->imr); ++ mtd_warn (mtd, "still busy with %s after %d ms\n", ++ this->state == FL_ERASING ? "erase" : "program", ++ this->state == FL_ERASING ? 400 : 20); ++ ++ } else if (unlikely (!timeout)) { ++ iowrite8 (0x00, &fcr->imr); ++ mtd_warn (mtd, "timeout waiting for interrupt\n"); ++ } ++ ++ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); ++ return this->read_byte (mtd); ++} ++ ++/* ++ * The TMIO controller combines two 8-bit data bytes into one 16-bit ++ * word. This function separates them so nand_base.c works as expected, ++ * especially its NAND_CMD_READID routines. ++ * ++ * To prevent stale data from being read, tmio_nand_hwcontrol() clears ++ * tmio->read_good. ++ */ ++static u_char tmio_nand_read_byte (struct mtd_info *mtd) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned int data; ++ ++ if (tmio->read_good--) ++ return tmio->read; ++ ++ data = ioread16 (&fcr->u16); ++ tmio->read = data >> 8; ++ return data; ++} ++ ++/* ++ * The TMIO controller converts an 8-bit NAND interface to a 16-bit ++ * bus interface, so all data reads and writes must be 16-bit wide. ++ * Thus, we implement 16-bit versions of the read, write, and verify ++ * buffer functions. ++ */ ++static void ++tmio_nand_write_buf (struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite16_rep (&fcr->u16, buf, len >> 1); ++} ++ ++static void tmio_nand_read_buf (struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ ioread16_rep (&fcr->u16, buf, len >> 1); ++} ++ ++static int ++tmio_nand_verify_buf (struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ u16* p = (u16*) buf; ++ ++ for (len >>= 1; len; len--) ++ if (*(p++) != ioread16 (&fcr->u16)) ++ return -EFAULT; ++ return 0; ++} ++ ++static void tmio_nand_enable_hwecc (struct mtd_info* mtd, int mode) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite8 (FCR_MODE_HWECC_RESET, &fcr->mode); ++ ioread8 (&fcr->u8); /* dummy read */ ++ iowrite8 (FCR_MODE_HWECC_CALC, &fcr->mode); ++} ++ ++static int tmio_nand_calculate_ecc (struct mtd_info* mtd, const u_char* dat, ++ u_char* ecc_code) ++{ ++ struct tmio_nand* tmio = mtd_to_tmio (mtd); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned int ecc; ++ ++ iowrite8 (FCR_MODE_HWECC_RESULT, &fcr->mode); ++ ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[1] = ecc; // 000-255 LP7-0 ++ ecc_code[0] = ecc >> 8; // 000-255 LP15-8 ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[2] = ecc; // 000-255 CP5-0,11b ++ ecc_code[4] = ecc >> 8; // 256-511 LP7-0 ++ ecc = ioread16 (&fcr->u16); ++ ecc_code[3] = ecc; // 256-511 LP15-8 ++ ecc_code[5] = ecc >> 8; // 256-511 CP5-0,11b ++ ++ iowrite8 (FCR_MODE_DATA, &fcr->mode); ++ return 0; ++} ++ ++static void tmio_hw_init (struct device *dev, struct tmio_nand *tmio) ++{ ++ struct resource* nfcr = tmio_resource_control (dev); ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nfhccr __iomem * ccr = tmio->ccr; ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ unsigned long base; ++ ++ /* (89h) SMD Buffer ON By TC6393XB SystemConfig gpibfc1 */ ++ tdev->ops->clock (dev, 1); ++ tdev->ops->function (dev, 1); ++ ++ /* (4Ch) CLKRUN Enable 1st spcrunc */ ++ iowrite8 (0x81, &ccr->icc); ++ ++ /* (10h)BaseAddress 0x1000 spba.spba2 */ ++ base = nfcr->start - tdev->iomem->start; ++ iowrite16 (base, ccr->base + 0); ++ iowrite16 (base >> 16, ccr->base + 1); ++ ++ /* (04h)Command Register I/O spcmd */ ++ iowrite8 (0x02, &ccr->command); ++ ++ /* (62h) Power Supply Control ssmpwc */ ++ /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */ ++ iowrite8 (0x02, &ccr->nfpsc); ++ ++ /* (63h) Detect Control ssmdtc */ ++ iowrite8 (0x02, &ccr->nfdc); ++ ++ /* Interrupt status register clear sintst */ ++ iowrite8 (0x0f, &fcr->isr); ++ ++ /* After power supply, Media are reset smode */ ++ iowrite8 (FCR_MODE_POWER_ON, &fcr->mode); ++ iowrite8 (FCR_MODE_COMMAND, &fcr->mode); ++ iowrite8 (NAND_CMD_RESET, &fcr->u8); ++ ++ /* Standby Mode smode */ ++ iowrite8 (FCR_MODE_STANDBY, &fcr->mode); ++ ++ mdelay (5); ++} ++ ++static void tmio_hw_stop (struct device *dev, struct tmio_nand *tmio) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nfcr __iomem * fcr = tmio->fcr; ++ ++ iowrite8 (FCR_MODE_POWER_OFF, &fcr->mode); ++ tdev->ops->function (dev, 0); ++ tdev->ops->clock (dev, 0); ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++static const char *part_probes[] = { "cmdlinepart", NULL }; ++#endif ++ ++static int tmio_probe (struct device *dev) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_nand_platform_data* tnpd = dev->platform_data; ++ struct resource* ccr = tmio_resource_config (dev); ++ struct resource* fcr = tmio_resource_control (dev); ++ struct resource* irq = tmio_resource_irq (dev); ++ struct tmio_nand* tmio; ++ struct mtd_info* mtd; ++ struct nand_chip* this; ++ struct mtd_partition* parts; ++ int nbparts = 0; ++ int retval; ++ ++ if (!tnpd) ++ return -EINVAL; ++ ++ retval = request_resource (tdev->iomem, ccr); ++ if (retval) ++ goto err_request_ccr; ++ ++ retval = request_resource (tdev->iomem, fcr); ++ if (retval) ++ goto err_request_fcr; ++ ++ tmio = kzalloc (sizeof *tmio, GFP_KERNEL); ++ if (!tmio) { ++ retval = -ENOMEM; ++ goto err_kzalloc; ++ } ++ ++ dev_set_drvdata (dev, tmio); ++ mtd = &tmio->mtd; ++ this = &tmio->chip; ++ mtd->priv = this; ++ mtd->name = TMIO_NAME_NAND; ++ ++ tmio->ccr = ioremap (ccr->start, ccr->end - ccr->start + 1); ++ if (!tmio->ccr) { ++ retval = -EIO; ++ goto err_iomap_ccr; ++ } ++ ++ tmio->fcr = ioremap (fcr->start, fcr->end - fcr->start + 1); ++ if (!tmio->fcr) { ++ retval = -EIO; ++ goto err_iomap_fcr; ++ } ++ ++ tmio_hw_init (dev, tmio); ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = tmio->fcr; ++ this->IO_ADDR_W = tmio->fcr; ++ ++ /* Set address of hardware control function */ ++ this->cmd_ctrl = tmio_nand_hwcontrol; ++ this->dev_ready = tmio_nand_dev_ready; ++ this->read_byte = tmio_nand_read_byte; ++ this->write_buf = tmio_nand_write_buf; ++ this->read_buf = tmio_nand_read_buf; ++ this->verify_buf = tmio_nand_verify_buf; ++ ++ /* set eccmode using hardware ECC */ ++ this->ecc.mode = NAND_ECC_HW; ++ this->ecc.size = 512; ++ this->ecc.bytes = 6; ++ this->ecc.hwctl = tmio_nand_enable_hwecc; ++ this->ecc.calculate = tmio_nand_calculate_ecc; ++ this->ecc.correct = nand_correct_data; ++ this->badblock_pattern = tnpd->badblock_pattern; ++ ++ /* 15 us command delay time */ ++ this->chip_delay = 15; ++ ++ if (irq->start) { ++ retval = request_irq (irq->start, &tmio_irq, ++ SA_INTERRUPT, irq->name, tmio); ++ if (!retval) { ++ tmio->irq = irq->start; ++ this->waitfunc = tmio_nand_wait; ++ } else ++ mtd_warn (mtd, "request_irq error %d\n", retval); ++ } ++ ++ /* Scan to find existence of the device */ ++ if (nand_scan (mtd, 1)) { ++ retval = -ENODEV; ++ goto err_scan; ++ } ++ ++ /* Register the partitions */ ++#ifdef CONFIG_MTD_PARTITIONS ++ nbparts = parse_mtd_partitions (mtd, part_probes, &parts, 0); ++#endif ++ if (nbparts <= 0) { ++ parts = tnpd->partition; ++ nbparts = tnpd->num_partitions; ++ } ++ ++ add_mtd_partitions (mtd, parts, nbparts); ++ return 0; ++ ++err_scan: ++ if (tmio->irq) ++ free_irq (tmio->irq, tmio); ++ tmio_hw_stop (dev, tmio); ++ iounmap (tmio->fcr); ++err_iomap_fcr: ++ iounmap (tmio->ccr); ++err_iomap_ccr: ++ kfree (tmio); ++err_kzalloc: ++ release_resource (fcr); ++err_request_fcr: ++ release_resource (ccr); ++err_request_ccr: ++ return retval; ++} ++ ++static int tmio_remove (struct device *dev) ++{ ++ struct tmio_nand* tmio = dev_get_drvdata (dev); ++ ++ nand_release (&tmio->mtd); ++ if (tmio->irq) ++ free_irq (tmio->irq, tmio); ++ tmio_hw_stop (dev, tmio); ++ iounmap (tmio->fcr); ++ iounmap (tmio->ccr); ++ kfree (tmio); ++ release_resource (tmio_resource_control (dev)); ++ release_resource (tmio_resource_config (dev)); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int tmio_suspend (struct device *dev, pm_message_t state) ++{ ++ tmio_hw_stop (dev, dev_get_drvdata (dev)); ++ return 0; ++} ++ ++static int tmio_resume (struct device *dev) ++{ ++ tmio_hw_init (dev, dev_get_drvdata (dev)); ++ return 0; ++} ++#endif ++ ++static struct device_driver tmio_driver = { ++ .name = TMIO_NAME_NAND, ++ .bus = &tmio_bus_type, ++ .probe = tmio_probe, ++ .remove = tmio_remove, ++#ifdef CONFIG_PM ++ .suspend = tmio_suspend, ++ .resume = tmio_resume, ++#endif ++}; ++ ++static int __init tmio_init (void) { ++ return driver_register (&tmio_driver); ++} ++ ++static void __exit tmio_exit (void) { ++ driver_unregister (&tmio_driver); ++} ++ ++module_init (tmio_init); ++module_exit (tmio_exit); ++ ++MODULE_LICENSE ("GPL"); ++MODULE_AUTHOR ("Dirk Opfer, Chris Humbert"); ++MODULE_DESCRIPTION ("NAND flash driver on Toshiba Mobile IO controller"); +Index: git/drivers/mtd/nand/Kconfig +=================================================================== +--- git.orig/drivers/mtd/nand/Kconfig 2006-10-31 16:09:03.000000000 +0000 ++++ git/drivers/mtd/nand/Kconfig 2006-11-07 22:13:09.000000000 +0000 +@@ -63,6 +63,13 @@ config MTD_NAND_AMS_DELTA + help + Support for NAND flash on Amstrad E3 (Delta). + ++config MTD_NAND_TMIO ++ tristate "NAND Flash device on Toshiba Mobile IO Controller" ++ depends on MTD_NAND && TOSHIBA_TC6393XB ++ help ++ Support for NAND flash connected to a Toshiba Mobile IO ++ Controller in some PDAs, including the Sharp SL6000x. ++ + config MTD_NAND_TOTO + tristate "NAND Flash device on TOTO board" + depends on ARCH_OMAP && MTD_NAND && BROKEN +Index: git/drivers/mtd/nand/Makefile +=================================================================== +--- git.orig/drivers/mtd/nand/Makefile 2006-10-31 16:09:03.000000000 +0000 ++++ git/drivers/mtd/nand/Makefile 2006-11-07 22:13:09.000000000 +0000 +@@ -22,5 +22,6 @@ obj-$(CONFIG_MTD_NAND_TS7250) += ts7250 + obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o + obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o + obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o ++obj-$(CONFIG_MTD_NAND_TMIO) += tmio.o + + nand-objs = nand_base.o nand_bbt.o diff --git a/packages/linux/linux-openzaurus-2.6.18+git/tmio-ohci-r6.patch b/packages/linux/linux-openzaurus-2.6.18+git/tmio-ohci-r6.patch new file mode 100644 index 0000000000..9fdd2962c9 --- /dev/null +++ b/packages/linux/linux-openzaurus-2.6.18+git/tmio-ohci-r6.patch @@ -0,0 +1,929 @@ + + drivers/usb/host/Kconfig | 1 + drivers/usb/host/ohci-hcd.c | 1 + drivers/usb/host/ohci-tmio.c | 894 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 896 insertions(+) + +Index: git/drivers/usb/host/ohci-tmio.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/drivers/usb/host/ohci-tmio.c 2006-11-07 21:48:33.000000000 +0000 +@@ -0,0 +1,894 @@ ++/* ++ * OHCI HCD (Host Controller Driver) for USB. ++ * ++ * (C) Copyright 1999 Roman Weissgaerber ++ * (C) Copyright 2000-2002 David Brownell ++ * (C) Copyright 2002 Hewlett-Packard Company ++ * ++ * Bus glue for Toshiba Mobile IO (TMIO) Controller's OHCI core ++ * (C) Copyright 2005 Chris Humbert ++ * ++ * This is known to work with the following variants: ++ * TC6393XB revision 3 (32kB SRAM) ++ * ++ * The TMIO's OHCI core DMAs through a small internal buffer that ++ * is directly addressable by the CPU. dma_declare_coherent_memory ++ * and DMA bounce buffers allow the higher-level OHCI host driver to ++ * work. However, the dma API doesn't handle dma mapping failures ++ * well (dma_sg_map() is a prime example), so it is unusable. ++ * ++ * This HC pretends be a PIO-ish controller and uses the kernel's ++ * generic allocator for the entire SRAM. Using the USB core's ++ * usb_operations, we provide hcd_buffer_alloc/free. Using the OHCI's ++ * ohci_ops, we provide memory management for OHCI's TDs and EDs. We ++ * internally queue a URB's TDs until enough dma memory is available ++ * to enqueue them with the HC. ++ * ++ * Written from sparse documentation from Toshiba and Sharp's driver ++ * for the 2.4 kernel, ++ * usb-ohci-tc6393.c (C) Copyright 2004 Lineo Solutions, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include /* for consistent_sync() */ ++#include ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * USB Host Controller Configuration Register ++ */ ++struct tmio_uhccr { ++ u8 x00[8]; ++ u8 revid; /* 0x08 Revision ID */ ++ u8 x01[7]; ++ u16 basel; /* 0x10 USB Control Register Base Address Low */ ++ u16 baseh; /* 0x12 USB Control Register Base Address High */ ++ u8 x02[0x2c]; ++ u8 ilme; /* 0x40 Internal Local Memory Enable */ ++ u8 x03[0x0b]; ++ u16 pm; /* 0x4c Power Management */ ++ u8 x04[2]; ++ u8 intc; /* 0x50 INT Control */ ++ u8 x05[3]; ++ u16 lmw1l; /* 0x54 Local Memory Window 1 LMADRS Low */ ++ u16 lmw1h; /* 0x56 Local Memory Window 1 LMADRS High */ ++ u16 lmw1bl; /* 0x58 Local Memory Window 1 Base Address Low */ ++ u16 lmw1bh; /* 0x5A Local Memory Window 1 Base Address High */ ++ u16 lmw2l; /* 0x5C Local Memory Window 2 LMADRS Low */ ++ u16 lmw2h; /* 0x5E Local Memory Window 2 LMADRS High */ ++ u16 lmw2bl; /* 0x60 Local Memory Window 2 Base Address Low */ ++ u16 lmw2bh; /* 0x62 Local Memory Window 2 Base Address High */ ++ u8 x06[0x98]; ++ u8 misc; /* 0xFC MISC */ ++ u8 x07[3]; ++} __attribute__ ((packed)); ++ ++union tmio_uhccr_pm { ++ u16 raw; ++struct { ++ unsigned gcken:1; /* D0 */ ++ unsigned ckrnen:1; /* D1 */ ++ unsigned uspw1:1; /* D2 USB Port 1 Power Disable */ ++ unsigned uspw2:1; /* D3 USB Port 2 Power Disable */ ++ unsigned x00:4; ++ unsigned pmee:1; /* D8 */ ++ unsigned x01:6; ++ unsigned pmes:1; /* D15 */ ++} __attribute__ ((packed)); ++} __attribute__ ((packed)); ++ ++/*-------------------------------------------------------------------------*/ ++ ++struct tmio_dma_pool { ++ struct device* dev; ++ unsigned int size; ++}; ++ ++struct tmio_hcd { ++ struct gen_pool* poolp; ++ struct usb_operations ops; ++ struct tmio_dma_pool td_pool; ++ struct tmio_dma_pool ed_pool; ++ ++ struct tmio_uhccr __iomem *ccr; ++ void __iomem * sram; ++ size_t sram_len; ++}; ++ ++#define hcd_to_tmio(hcd) ((struct tmio_hcd*)(hcd_to_ohci (hcd) + 1)) ++ ++struct tmio_td { ++ void* data; /* td's data buffer */ ++ void __iomem * bounce; /* dma bounce buffer */ ++ dma_addr_t dma; /* bounce buffer dma address */ ++ size_t len; /* bounce buffer length */ ++ u32 info; /* parameter for td_fill */ ++}; ++ ++struct tmio_urb { ++ int td_add; /* next index to be added */ ++ int td_queue; /* next index to be HC enqueued */ ++ ++ struct tmio_td td [0]; /* private td data */ ++}; ++ ++static inline struct tmio_urb *urb_to_turb (struct urb *urb) ++{ ++ urb_priv_t* urb_priv = urb->hcpriv; ++ return (struct tmio_urb*)(urb_priv->td + urb_priv->length); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* gen_pool_alloc page allocator callback */ ++static unsigned long tmio_pool_callback(struct gen_pool *poolp) ++{ ++ return 0; ++} ++ ++static inline void tmio_pool_destroy(struct tmio_hcd *tmio) ++{ ++ struct gen_pool *poolp = tmio->poolp; ++ ++ if (!poolp) ++ return; ++ if (poolp->h) ++ kfree(poolp->h); ++ kfree(poolp); ++ tmio->poolp = NULL; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define BOUNDED_XYL(x,y,ylen) (((y) <= (x)) && ((x) < ((y)+(ylen)))) ++#define BOUNDED_XYY(x,y1,y2) (((y1) <= (x)) && ((x) < (y2))) ++ ++static inline dma_addr_t tmio_virt_to_dma (struct usb_hcd *hcd, void *vaddr) ++{ ++ struct resource* sram = tmio_resource_mem (hcd->self.controller); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ ++ return BOUNDED_XYL (vaddr, tmio->sram, tmio->sram_len) ++ ? sram->start + (vaddr - tmio->sram) ++ : ~0; ++} ++ ++static inline void* tmio_dma_to_virt (struct usb_hcd *hcd, dma_addr_t handle) ++{ ++ struct resource* sram = tmio_resource_mem (hcd->self.controller); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ ++ return BOUNDED_XYY (handle, sram->start, sram->end + 1) ++ ? tmio->sram + handle - sram->start ++ : NULL; ++} ++ ++/* ++ * allocate dma-able memory in the device's internal sram ++ * ++ * The generic pool allocator's minimum chunk size is 32 bytes, ++ * which is the cache line size on the PXA255, so we don't need ++ * to do anything special for smaller requests. ++ */ ++static inline void *tmio_dma_alloc (struct device *dev, size_t size, ++ dma_addr_t *handle, gfp_t mem_flags) ++{ ++ struct usb_hcd* hcd = dev_get_drvdata (dev); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ void* virt = (void*) gen_pool_alloc (tmio->poolp, size); ++ ++ return (*handle = tmio_virt_to_dma (hcd, virt)) == ~0 ? NULL : virt; ++} ++ ++static inline void tmio_dma_free (struct device *dev, size_t size, ++ void *cpu_addr, dma_addr_t handle) ++{ ++ struct usb_hcd* hcd = dev_get_drvdata (dev); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ dma_addr_t dma = tmio_virt_to_dma (hcd, cpu_addr); ++ ++ if (unlikely (dma == ~0)) { ++ dev_err (dev, "trying to free bad address 0x%p\n", cpu_addr); ++ return; ++ } ++ ++ if (unlikely (handle != dma)) ++ dev_err (dev, "dma address mismatch for 0x%p: %08x != %08x\n", ++ cpu_addr, handle, dma); ++ ++ gen_pool_free (tmio->poolp, (unsigned long) cpu_addr, size); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void *tmio_dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags, ++ dma_addr_t *handle) ++{ ++ struct tmio_dma_pool *tdp = (struct tmio_dma_pool*) pool; ++ return tmio_dma_alloc (tdp->dev, tdp->size, handle, mem_flags); ++} ++ ++static void ++tmio_dma_pool_free (struct dma_pool *pool, void *vaddr, dma_addr_t addr) ++{ ++ struct tmio_dma_pool *tdp = (struct tmio_dma_pool*) pool; ++ return tmio_dma_free (tdp->dev, tdp->size, vaddr, addr); ++} ++ ++static void *tmio_buffer_alloc (struct usb_bus *bus, size_t size, ++ gfp_t mem_flags, dma_addr_t *dma) ++{ ++ return tmio_dma_alloc (bus->controller, size, dma, mem_flags); ++} ++ ++static void tmio_buffer_free (struct usb_bus *bus, size_t size, ++ void *addr, dma_addr_t dma) ++{ ++ tmio_dma_free (bus->controller, size, addr, dma); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void tmio_hc_stop (struct usb_hcd *hcd) ++{ ++ struct device* dev = hcd->self.controller; ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ struct tmio_uhccr __iomem* ccr = tmio->ccr; ++ union tmio_uhccr_pm pm = {0}; ++ ++ pm.gcken = 1; ++ pm.ckrnen = 1; ++ pm.uspw1 = 1; ++ pm.uspw2 = 1; ++ ++ iowrite8 (0, &ccr->intc); ++ iowrite8 (0, &ccr->ilme); ++ iowrite16(0, &ccr->basel); ++ iowrite16(0, &ccr->baseh); ++ iowrite16(pm.raw, &ccr->pm); ++ ++ tdev->ops->function (dev, 0); ++ tdev->ops->clock (dev, 0); ++} ++ ++static void tmio_hc_start (struct usb_hcd *hcd) ++{ ++ struct device* dev = hcd->self.controller; ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ struct tmio_uhccr __iomem* ccr = tmio->ccr; ++ union tmio_uhccr_pm pm = {0}; ++ ++ pm.pmes = 1; ++ pm.pmee = 1; ++ pm.ckrnen = 1; ++ pm.gcken = 1; ++ ++ tdev->ops->clock (dev, 1); ++ tdev->ops->function (dev, 1); ++ ++ iowrite16(pm.raw, &ccr->pm); ++ iowrite16(hcd->rsrc_start, &ccr->basel); ++ iowrite16(hcd->rsrc_start >> 16, &ccr->baseh); ++ iowrite8 (1, &ccr->ilme); ++ iowrite8 (2, &ccr->intc); ++ ++ consistent_sync (tmio->sram, tmio->sram_len, DMA_BIDIRECTIONAL); ++ ++ dev_info (dev, "revision %d @ 0x%08llx, irq %d\n", ++ ioread8 (&ccr->revid), hcd->rsrc_start, hcd->irq); ++} ++ ++static void tmio_stop (struct usb_hcd *hcd) ++{ ++ struct ohci_hcd* ohci = hcd_to_ohci (hcd); ++ ++ /* NULL these so ohci_stop() doesn't try to free them */ ++ ohci->hcca = NULL; ++ ohci->td_cache = NULL; ++ ohci->ed_cache = NULL; ++ ++ ohci_stop (hcd); ++ tmio_hc_stop (hcd); ++ tmio_pool_destroy (hcd_to_tmio (hcd)); ++ ++ /* We don't free the hcca because tmio_hc_stop() turns off ++ * the sram and the memory allocation data is destroyed. */ ++} ++ ++static int tmio_start (struct usb_hcd *hcd) ++{ ++ struct device* dev = hcd->self.controller; ++ struct ohci_hcd* ohci = hcd_to_ohci (hcd); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ int retval; ++ ++ tmio_hc_start (hcd); ++ ++ tmio->poolp = gen_pool_create(0, fls(tmio->sram_len) - 1, ++ tmio_pool_callback, 0); ++ if (!tmio->poolp) { ++ retval = -ENOMEM; ++ goto err_gen_pool_create; ++ } ++ ++ gen_pool_free (tmio->poolp, (unsigned long)(tmio->sram), ++ tmio->sram_len); ++ ++ ohci->hcca = tmio_dma_alloc (dev, sizeof *ohci->hcca, ++ &ohci->hcca_dma, GFP_KERNEL); ++ if (!ohci->hcca) { ++ retval = -ENOMEM; ++ goto err_hcca_alloc; ++ } ++ ++ /* for our dma_pool_alloc/free hooks */ ++ ohci->td_cache = (struct dma_pool*) &tmio->td_pool; ++ ohci->ed_cache = (struct dma_pool*) &tmio->ed_pool; ++ ++ if ((retval = ohci_init (ohci)) < 0) ++ goto err_ohci_init; ++ ++ if ((retval = ohci_run (ohci)) < 0) ++ goto err_ohci_run; ++ ++ return 0; ++ ++err_ohci_run: ++ err ("can't start %s", hcd->self.bus_name); ++err_ohci_init: ++err_hcca_alloc: ++err_gen_pool_create: ++ tmio_stop (hcd); ++ return retval; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static inline void *tmio_urb_dma_to_virt(struct urb *urb, dma_addr_t dma) ++{ ++ if (BOUNDED_XYL(dma, urb->transfer_dma, urb->transfer_buffer_length)) ++ return urb->transfer_buffer + dma - urb->transfer_dma; ++ ++ if (BOUNDED_XYL(dma, urb->setup_dma, sizeof (struct usb_ctrlrequest))) ++ return urb->setup_packet + dma - urb->setup_dma; ++ ++ return NULL; ++} ++ ++static struct tmio_td* tmio_td_find (struct td *td, int *index) ++{ ++ struct urb* urb = td->urb; ++ urb_priv_t* urb_priv = urb->hcpriv; ++ struct tmio_urb* turb = urb_to_turb (urb); ++ int i; ++ ++ for (i=0; i < urb_priv->length; i++) ++ if (urb_priv->td[i] == td) { ++ *index = i; ++ return turb->td + i; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * map the td's data to dma-able memory ++ * ++ * if this td transfers data, ++ * sets tmtd->data to the urb's data buffer ++ * sets tmtd->dma to dma-able memory ++ * sets tmtd->bounce to non-NULL if a bounce buffer is allocated ++ * copies the urb's data buffer to the bounce buffer if necessary ++ */ ++static int tmio_td_dma_map (struct ohci_hcd *ohci, struct urb *urb, ++ struct tmio_td *tmtd, int idx) ++{ ++ struct usb_hcd* hcd = ohci_to_hcd (ohci); ++ struct device* dev = hcd->self.controller; ++ dma_addr_t dma; ++ ++ if (!tmtd->len) ++ return 0; ++ ++ if (tmio_dma_to_virt (hcd, tmtd->dma)) ++ return 0; ++ ++ tmtd->data = tmio_urb_dma_to_virt (urb, tmtd->dma); ++ if (unlikely (!tmtd->data)) { ++ dev_err (dev, "TD has bad dma address 0x%08x\n", tmtd->dma); ++ return 0; ++ } ++ ++ tmtd->bounce = tmio_dma_alloc (dev, tmtd->len, &dma, GFP_ATOMIC); ++ if (!tmtd->bounce) ++ return -ENOMEM; ++ ++ if ((usb_pipecontrol (urb->pipe) && !idx) || usb_pipeout (urb->pipe)) { ++ consistent_sync (tmtd->bounce, tmtd->len, DMA_TO_DEVICE); ++ memcpy (tmtd->bounce, tmtd->data, tmtd->len); ++ } else ++ consistent_sync (tmtd->bounce, tmtd->len, DMA_FROM_DEVICE); ++ ++ tmtd->dma = dma; ++ return 0; ++} ++ ++/* ++ * unmaps the td's data from dma-able memory ++ * ++ * if a bounce buffer has been allocated, ++ * copy the bounce buffer to the urb's data buffer if necessary ++ * free the bounce buffer ++ */ ++static void tmio_td_dma_unmap (struct ohci_hcd *ohci, struct td *td) ++{ ++ struct device* dev = (ohci_to_hcd (ohci))->self.controller; ++ struct urb* urb = td->urb; ++ int idx; ++ struct tmio_td* tmtd = tmio_td_find (td, &idx); ++ ++ if (!tmtd->bounce) ++ return; ++ ++ if (usb_pipein (urb->pipe) && (usb_pipecontrol (urb->pipe) ? idx : 1)) { ++ memcpy (tmtd->data, tmtd->bounce, tmtd->len); ++ consistent_sync (tmtd->data, tmtd->len, DMA_TO_DEVICE); ++ } ++ ++ tmio_dma_free (dev, tmtd->len, tmtd->bounce, tmtd->dma); ++} ++ ++static int tmio_urb_runqueue (struct ohci_hcd *ohci, struct urb *urb) ++{ ++ struct tmio_urb* turb = urb_to_turb (urb); ++ urb_priv_t* urb_priv= urb->hcpriv; ++ int start = turb->td_queue; ++ int retval = 0; ++ int i; ++ ++ for (i = start; i < turb->td_add; i = ++turb->td_queue) { ++ struct tmio_td *tmtd = turb->td + i; ++ ++ if ((retval = tmio_td_dma_map (ohci, urb, tmtd, i))) ++ break; ++ ++ td_fill (ohci, tmtd->info, tmtd->dma, tmtd->len, urb, i); ++ } ++ ++ if (i <= start) ++ return retval; ++ ++ /* kickstart the appropriate list */ ++ wmb (); ++ switch (urb_priv->ed->type) { ++ case PIPE_BULK: ++ ohci_writel (ohci, OHCI_BLF, &ohci->regs->cmdstatus); ++ break; ++ case PIPE_CONTROL: ++ ohci_writel (ohci, OHCI_CLF, &ohci->regs->cmdstatus); ++ break; ++ } ++ ++ return retval; ++} ++ ++/* ++ * This needs to be called with ohci->lock held so the pending urb list ++ * isn't modified. ++ */ ++static int tmio_ohci_runqueue (struct ohci_hcd *ohci) ++{ ++ urb_priv_t* priv; ++ int retval = 0; ++ ++ list_for_each_entry_reverse (priv, &ohci->pending, pending) ++ if ((retval = tmio_urb_runqueue (ohci, priv->td[0]->urb))) ++ return retval; ++ ++ return retval; ++} ++ ++static void tmio_td_fill (struct ohci_hcd *ohci, u32 info, ++ dma_addr_t data, int len, struct urb *urb, int index) ++{ ++ struct tmio_urb* turb = urb_to_turb (urb); ++ struct tmio_td* tmtd = turb->td + index; ++ ++ tmtd->data = NULL; ++ tmtd->bounce = NULL; ++ tmtd->dma = data; ++ tmtd->len = len; ++ tmtd->info = info; ++ turb->td_add = index + 1; ++} ++ ++static void ++tmio_td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td) ++{ ++ tmio_td_dma_unmap (ohci, td); ++ td_done (ohci, urb, td); ++} ++ ++const static struct ohci_ops tmio_ops = { ++ .dma_pool_alloc = tmio_dma_pool_alloc, ++ .dma_pool_free = tmio_dma_pool_free, ++ .td_fill = tmio_td_fill, ++ .td_done = tmio_td_done, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static irqreturn_t tmio_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) ++{ ++ irqreturn_t retval = ohci_irq (hcd, ptregs); ++ ++ if (retval == IRQ_HANDLED) { ++ struct ohci_hcd *ohci = hcd_to_ohci (hcd); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ohci->lock, flags); ++ tmio_ohci_runqueue (ohci); ++ spin_unlock_irqrestore (&ohci->lock, flags); ++ } ++ ++ return retval; ++} ++ ++/* ++ * This is ohci_urb_enqueue with: ++ * dma address sanitization for tmio_urb_dma_to_virt() ++ * allocate extra space in urb_priv for our private data ++ * initialize urb_priv->td[0]->urb for tmio_ohci_runqueue() ++ * call tmio_ohci_runqueue() after submitting TDs ++ */ ++static int tmio_urb_enqueue ( ++ struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep, ++ struct urb *urb, ++ gfp_t mem_flags ++) { ++ struct ohci_hcd *ohci = hcd_to_ohci (hcd); ++ struct ed *ed; ++ urb_priv_t *urb_priv; ++ unsigned int pipe = urb->pipe; ++ int i, size = 0; ++ unsigned long flags; ++ int retval = 0; ++ ++#ifdef OHCI_VERBOSE_DEBUG ++ urb_print (urb, "SUB", usb_pipein (pipe)); ++#endif ++ ++ /* make sure we can convert dma offsets back to virtual addresses */ ++ if (!tmio_dma_to_virt (hcd, urb->setup_dma)) ++ urb->setup_dma = 0; ++ ++ if (!tmio_dma_to_virt (hcd, urb->transfer_dma)) ++ urb->transfer_dma = sizeof (struct usb_ctrlrequest); ++ ++ /* every endpoint has a ed, locate and maybe (re)initialize it */ ++ if (! (ed = ed_get (ohci, ep, urb->dev, pipe, urb->interval))) ++ return -ENOMEM; ++ ++ /* for the private part of the URB we need the number of TDs (size) */ ++ switch (ed->type) { ++ case PIPE_CONTROL: ++ /* td_submit_urb() doesn't yet handle these */ ++ if (urb->transfer_buffer_length > 4096) ++ return -EMSGSIZE; ++ ++ /* 1 TD for setup, 1 for ACK, plus ... */ ++ size = 2; ++ /* FALLTHROUGH */ ++ // case PIPE_INTERRUPT: ++ // case PIPE_BULK: ++ default: ++ /* one TD for every 4096 Bytes (can be upto 8K) */ ++ size += urb->transfer_buffer_length / 4096; ++ /* ... and for any remaining bytes ... */ ++ if ((urb->transfer_buffer_length % 4096) != 0) ++ size++; ++ /* ... and maybe a zero length packet to wrap it up */ ++ if (size == 0) ++ size++; ++ else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 ++ && (urb->transfer_buffer_length ++ % usb_maxpacket (urb->dev, pipe, ++ usb_pipeout (pipe))) == 0) ++ size++; ++ break; ++ case PIPE_ISOCHRONOUS: /* number of packets from URB */ ++ size = urb->number_of_packets; ++ break; ++ } ++ ++ /* allocate the private part of the URB */ ++ urb_priv = kzalloc (sizeof (urb_priv_t) ++ + size * sizeof (struct td*) ++ + sizeof (struct tmio_urb) ++ + size * sizeof (struct tmio_td), ++ mem_flags); ++ if (!urb_priv) ++ return -ENOMEM; ++ INIT_LIST_HEAD (&urb_priv->pending); ++ urb_priv->length = size; ++ urb_priv->ed = ed; ++ ++ /* allocate the TDs (deferring hash chain updates) */ ++ for (i = 0; i < size; i++) { ++ urb_priv->td [i] = td_alloc (ohci, mem_flags); ++ if (!urb_priv->td [i]) { ++ urb_priv->length = i; ++ urb_free_priv (ohci, urb_priv); ++ return -ENOMEM; ++ } ++ urb_priv->td [i]->urb = urb; ++ } ++ ++ spin_lock_irqsave (&ohci->lock, flags); ++ ++ /* don't submit to a dead HC */ ++ if (!HC_IS_RUNNING(hcd->state)) { ++ retval = -ENODEV; ++ goto fail; ++ } ++ ++ /* in case of unlink-during-submit */ ++ spin_lock (&urb->lock); ++ if (urb->status != -EINPROGRESS) { ++ spin_unlock (&urb->lock); ++ urb->hcpriv = urb_priv; ++ finish_urb (ohci, urb, NULL); ++ retval = 0; ++ goto fail; ++ } ++ ++ /* schedule the ed if needed */ ++ if (ed->state == ED_IDLE) { ++ retval = ed_schedule (ohci, ed); ++ if (retval < 0) ++ goto fail0; ++ if (ed->type == PIPE_ISOCHRONOUS) { ++ u16 frame = ohci_frame_no(ohci); ++ ++ /* delay a few frames before the first TD */ ++ frame += max_t (u16, 8, ed->interval); ++ frame &= ~(ed->interval - 1); ++ frame |= ed->branch; ++ urb->start_frame = frame; ++ ++ /* yes, only URB_ISO_ASAP is supported, and ++ * urb->start_frame is never used as input. ++ */ ++ } ++ } else if (ed->type == PIPE_ISOCHRONOUS) ++ urb->start_frame = ed->last_iso + ed->interval; ++ ++ /* fill the TDs and link them to the ed; and ++ * enable that part of the schedule, if needed ++ * and update count of queued periodic urbs ++ */ ++ urb->hcpriv = urb_priv; ++ td_submit_urb (ohci, urb); ++ tmio_ohci_runqueue (ohci); ++ ++fail0: ++ spin_unlock (&urb->lock); ++fail: ++ if (retval) ++ urb_free_priv (ohci, urb_priv); ++ spin_unlock_irqrestore (&ohci->lock, flags); ++ return retval; ++} ++ ++static const struct hc_driver tmio_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "TMIO OHCI USB Host Controller", ++ .hcd_priv_size = sizeof (struct ohci_hcd) ++ + sizeof (struct tmio_hcd), ++ ++ /* generic hardware linkage */ ++ .irq = tmio_irq, ++ .flags = HCD_USB11 | HCD_MEMORY, ++ ++ /* basic lifecycle operations */ ++ .start = tmio_start, ++ .stop = tmio_stop, ++ ++ /* managing i/o requests and associated device resources */ ++ .urb_enqueue = tmio_urb_enqueue, ++ .urb_dequeue = ohci_urb_dequeue, ++ .endpoint_disable = ohci_endpoint_disable, ++ ++ /* scheduling support */ ++ .get_frame_number = ohci_get_frame, ++ ++ /* root hub support */ ++ .hub_status_data = ohci_hub_status_data, ++ .hub_control = ohci_hub_control, ++#ifdef CONFIG_PM ++ .bus_suspend = ohci_bus_suspend, ++ .bus_resume = ohci_bus_resume, ++#endif ++ .start_port_reset = ohci_start_port_reset, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* configure so an HC device and id are always provided */ ++/* always called with process context; sleeping is OK */ ++ ++/** ++ * tmio_probe - initialize TMIO-based HCDs ++ * Context: !in_interrupt() ++ * ++ * Allocates basic resources for this USB host controller, and ++ * then invokes the start() method for the HCD associated with it ++ * through the hotplug entry's driver_data. ++ */ ++static int tmio_probe (struct device *dev) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ struct resource* config = tmio_resource_config (dev); ++ struct resource* regs = tmio_resource_control (dev); ++ struct resource* sram = tmio_resource_mem (dev); ++ struct resource* irq = tmio_resource_irq (dev); ++ struct usb_operations* ops; ++ struct tmio_hcd* tmio; ++ struct ohci_hcd* ohci; ++ struct usb_hcd* hcd; ++ int retval; ++ ++ if (usb_disabled ()) ++ return -ENODEV; ++ ++ if (dev->dma_mask || dev->coherent_dma_mask) { ++ dev_err (dev, "DMA not supported\n"); ++ return -ENODEV; ++ } ++ ++ hcd = usb_create_hcd (&tmio_hc_driver, dev, dev->bus_id); ++ if (!hcd) { ++ retval = -ENOMEM; ++ goto err_create_hcd; ++ } ++ ++ tmio = hcd_to_tmio (hcd); ++ tmio->td_pool.dev = dev; ++ tmio->ed_pool.dev = dev; ++ tmio->td_pool.size = sizeof (struct td); ++ tmio->ed_pool.size = sizeof (struct ed); ++ ohci = hcd_to_ohci (hcd); ++ ohci_hcd_init (ohci); ++ ohci->ops = &tmio_ops; ++ ++ retval = request_resource (tdev->iomem, config); ++ if (retval) ++ goto err_request_config_resource; ++ ++ retval = request_resource (tdev->iomem, regs); ++ if (retval) ++ goto err_request_regs_resource; ++ ++ retval = request_resource (tdev->iomem, sram); ++ if (retval) ++ goto err_request_sram_resource; ++ ++ hcd->rsrc_start = regs->start; ++ hcd->rsrc_len = regs->end - regs->start + 1; ++ tmio->sram_len = sram->end - sram->start + 1; ++ ++ tmio->ccr = ioremap (config->start, config->end - config->start + 1); ++ if (!tmio->ccr) { ++ retval = -ENOMEM; ++ goto err_ioremap_ccr; ++ } ++ ++ hcd->regs = ioremap (hcd->rsrc_start, hcd->rsrc_len); ++ if (!hcd->regs) { ++ retval = -ENOMEM; ++ goto err_ioremap_regs; ++ } ++ ++ tmio->sram = ioremap (sram->start, tmio->sram_len); ++ if (!tmio->sram) { ++ retval = -ENOMEM; ++ goto err_ioremap_sram; ++ } ++ ++ /* drivers should use our coherent buffer allocator */ ++ ops = &tmio->ops; ++ memcpy (ops, hcd->self.op, sizeof *ops); ++ ops->buffer_alloc = tmio_buffer_alloc; ++ ops->buffer_free = tmio_buffer_free; ++ hcd->self.op = ops; ++ ++ retval = usb_add_hcd (hcd, irq->start, SA_INTERRUPT); ++ if (retval) ++ goto err_usb_add_hcd; ++ ++ return 0; ++ ++err_usb_add_hcd: ++ iounmap (tmio->sram); ++err_ioremap_sram: ++ iounmap (hcd->regs); ++err_ioremap_regs: ++ iounmap (tmio->ccr); ++err_ioremap_ccr: ++ release_resource (sram); ++err_request_sram_resource: ++ release_resource (regs); ++err_request_regs_resource: ++ release_resource (config); ++err_request_config_resource: ++ usb_put_hcd (hcd); ++err_create_hcd: ++ return retval; ++} ++ ++/* may be called without controller electrically present */ ++/* may be called with controller, bus, and devices active */ ++ ++/** ++ * tmio_remove - shutdown processing for TMIO-based HCDs ++ * @dev: USB Host Controller being removed ++ * Context: !in_interrupt() ++ * ++ * Reverses the effect of tmio_probe(), first invoking ++ * the HCD's stop() method. It is always called from a thread ++ * context, normally "rmmod", "apmd", or something similar. ++ */ ++static int tmio_remove (struct device *dev) ++{ ++ struct usb_hcd* hcd = dev_get_drvdata (dev); ++ struct tmio_hcd* tmio = hcd_to_tmio (hcd); ++ ++ usb_remove_hcd (hcd); ++ iounmap (tmio->sram); ++ iounmap (hcd->regs); ++ iounmap (tmio->ccr); ++ release_resource (tmio_resource_mem (dev)); ++ release_resource (tmio_resource_control (dev)); ++ release_resource (tmio_resource_config (dev)); ++ usb_put_hcd (hcd); ++ return 0; ++} ++ ++static struct device_driver tmio_ohci = { ++ .name = TMIO_NAME_OHCI, ++ .bus = &tmio_bus_type, ++ .probe = tmio_probe, ++ .remove = tmio_remove, ++}; ++ ++static int __init tmio_init (void) ++{ ++ dbg (DRIVER_INFO " (%s)", TMIO_SOC_NAME); ++ dbg ("block sizes: ed %d td %d", ++ sizeof (struct ed), sizeof (struct td)); ++ ++ return driver_register (&tmio_ohci); ++} ++ ++static void __exit tmio_exit (void) ++{ ++ driver_unregister (&tmio_ohci); ++} ++ ++module_init (tmio_init); ++module_exit (tmio_exit); +Index: git/drivers/usb/host/Kconfig +=================================================================== +--- git.orig/drivers/usb/host/Kconfig 2006-11-07 21:46:32.000000000 +0000 ++++ git/drivers/usb/host/Kconfig 2006-11-07 21:48:38.000000000 +0000 +@@ -84,6 +84,7 @@ config USB_OHCI_HCD + depends on USB && USB_ARCH_HAS_OHCI + select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 + select I2C if ARCH_PNX4008 ++ select GENERIC_ALLOCATOR if TOSHIBA_TC6393XB + ---help--- + The Open Host Controller Interface (OHCI) is a standard for accessing + USB 1.1 host controller hardware. It does more in hardware than Intel's +Index: git/drivers/usb/host/ohci-hcd.c +=================================================================== +--- git.orig/drivers/usb/host/ohci-hcd.c 2006-11-07 21:46:32.000000000 +0000 ++++ git/drivers/usb/host/ohci-hcd.c 2006-11-07 21:48:33.000000000 +0000 +@@ -944,6 +944,7 @@ MODULE_LICENSE ("GPL"); + || defined(CONFIG_ARCH_OMAP) \ + || defined (CONFIG_ARCH_LH7A404) \ + || defined (CONFIG_PXA27x) \ ++ || defined (CONFIG_TOSHIBA_TC6393XB) \ + || defined (CONFIG_ARCH_EP93XX) \ + || defined (CONFIG_SOC_AU1X00) \ + || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \ diff --git a/packages/linux/linux-openzaurus-2.6.18+git/tmio-tc6393-r8.patch b/packages/linux/linux-openzaurus-2.6.18+git/tmio-tc6393-r8.patch new file mode 100644 index 0000000000..2f1b47d783 --- /dev/null +++ b/packages/linux/linux-openzaurus-2.6.18+git/tmio-tc6393-r8.patch @@ -0,0 +1,800 @@ + arch/arm/common/Kconfig | 3 + arch/arm/common/Makefile | 1 + arch/arm/common/tc6393xb.c | 668 ++++++++++++++++++++++++++++++++++++++++ + arch/arm/mach-pxa/Kconfig | 1 + include/asm-arm/arch-pxa/irqs.h | 10 + include/asm-arm/hardware/tmio.h | 44 ++ + 6 files changed, 727 insertions(+) + +Index: git/arch/arm/common/tc6393xb.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ git/arch/arm/common/tc6393xb.c 2006-11-07 22:14:49.000000000 +0000 +@@ -0,0 +1,668 @@ ++/* ++ * Toshiba TC6393XB SoC support ++ * ++ * Maintainer: Chris Humbert ++ * ++ * Copyright (c) 2005-2006 Chris Humbert ++ * Copyright (c) 2005 Dirk Opfer ++ * ++ * Based on code written by Sharp/Lineo for 2.4 kernels ++ * Based on locomo.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef TMIO_SOC_TC6393XB ++#error "TC6393XB SoC not configured" ++#endif ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* cell ids must be 0-based because they are used as array indexes. */ ++#define TC6393_CELL_NAND 0 ++#define TC6393_CELL_SD 1 ++#define TC6393_CELL_OHCI 2 ++#define TC6393_CELL_SERIAL 3 ++#define TC6393_CELL_LCD 4 ++#define TC6393_NUM_CELLS 5 ++ ++#define TC6393_RESOURCE(_name, _start, _end, _flags) \ ++ { \ ++ .name = _name, \ ++ .start = _start, \ ++ .end = _end, \ ++ .flags = _flags, \ ++ } ++ ++#define TC6393_MEM(name, start, size) \ ++ TC6393_RESOURCE(name, start, (start) + (size) - 1, IORESOURCE_MEM) ++ ++#define TC6393_IRQ(name, irq) \ ++ TC6393_RESOURCE(name, irq, irq, IORESOURCE_IRQ) ++ ++const static struct resource tc6393_NAND_resource[] = { ++ TC6393_MEM (TMIO_NAME_NAND, 0x000100, 0x100), ++ TC6393_MEM (TMIO_NAME_NAND, 0x001000, 0x008), ++ TC6393_MEM (TMIO_NAME_NAND, 0, 0), ++ TC6393_IRQ (TMIO_NAME_NAND, IRQ_TC6393_NAND), ++}; ++ ++const static struct resource tc6393_SD_resource[] = { ++ TC6393_MEM (TMIO_NAME_SD, 0x000200, 0x100), ++ TC6393_MEM (TMIO_NAME_SD, 0x002000, 0x200), ++ TC6393_MEM (TMIO_NAME_SD, 0, 0), ++ TC6393_IRQ (TMIO_NAME_SD, IRQ_TC6393_SD), ++}; ++ ++const static struct resource tc6393_OHCI_resource[] = { ++ TC6393_MEM (TMIO_NAME_OHCI, 0x000300, 0x100), ++ TC6393_MEM (TMIO_NAME_OHCI, 0x003000, 0x100), ++ TC6393_MEM (TMIO_NAME_OHCI, 0x010000, 32 * 1024), ++ TC6393_IRQ (TMIO_NAME_OHCI, IRQ_TC6393_OHCI), ++}; ++ ++const static struct resource tc6393_SERIAL_resource[] = { ++ TC6393_MEM (TMIO_NAME_SERIAL, 0x000400, 0x100), ++ TC6393_MEM (TMIO_NAME_SERIAL, 0x004000, 0x100), ++ TC6393_MEM (TMIO_NAME_SERIAL, 0, 0), ++ TC6393_IRQ (TMIO_NAME_SERIAL, IRQ_TC6393_SERIAL), ++}; ++ ++const static struct resource tc6393_LCD_resource[] = { ++ TC6393_MEM (TMIO_NAME_LCD, 0x000500, 0x100), ++ TC6393_MEM (TMIO_NAME_LCD, 0x005000, 0x200), ++ TC6393_MEM (TMIO_NAME_LCD, 0x100000, 1024 * 1024), ++ TC6393_IRQ (TMIO_NAME_LCD, IRQ_TC6393_LCD), ++}; ++ ++#define TC6393_CELL(_NAME) \ ++ [TC6393_CELL_##_NAME] = { \ ++ .name = TMIO_NAME_##_NAME, \ ++ .id = TC6393_CELL_##_NAME, \ ++ .resource = tc6393_##_NAME##_resource, \ ++ .num_resources = ARRAY_SIZE (tc6393_##_NAME##_resource), \ ++ } ++ ++struct tc6393_cell { ++ const char* name; ++ unsigned int id; ++ const struct resource* resource; ++ unsigned int num_resources; ++}; ++ ++const static struct tc6393_cell tc6393_cell [TC6393_NUM_CELLS] = { ++ TC6393_CELL (NAND ), ++ TC6393_CELL (SD ), ++ TC6393_CELL (OHCI ), ++ TC6393_CELL (SERIAL ), ++ TC6393_CELL (LCD ), ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* ++ * TC6393 System Configuration Register ++ */ ++struct tc6393_scr { ++ u8 x00[8]; ++ u8 revid; /* 0x08 Revision ID */ ++ u8 x01[0x47]; ++ u8 isr; /* 0x50 Interrupt Status */ ++ u8 x02; ++ u8 imr; /* 0x52 Interrupt Mask */ ++ u8 x03; ++ u8 irr; /* 0x54 Interrupt Routing */ ++ u8 x04[0x0b]; ++ u16 gper; /* 0x60 GP Enable */ ++ u8 x05[2]; ++ u16 gpi_sr[2]; /* 0x64 GPI Status */ ++ u16 gpi_imr[2]; /* 0x68 GPI INT Mask */ ++ u16 gpi_eder[2]; /* 0x6c GPI Edge Detect Enable */ ++ u16 gpi_lir[4]; /* 0x70 GPI Level Invert */ ++ u16 gpo_dsr[2]; /* 0x78 GPO Data Set */ ++ u16 gpo_doecr[2]; /* 0x7c GPO Data OE Control */ ++ u16 gp_iarcr[2]; /* 0x80 GP Internal Active Reg Control */ ++ u16 gp_iarlcr[2]; /* 0x84 GP Internal Active Reg Level Con*/ ++ u8 gpi_bcr[4]; /* 0x88 GPI Buffer Control */ ++ u16 gpa_iarcr; /* 0x8c GPa Internal Active Reg Control */ ++ u8 x06[2]; ++ u16 gpa_iarlcr; /* 0x90 GPa Internal Active Reg Level Co*/ ++ u8 x07[2]; ++ u16 gpa_bcr; /* 0x94 GPa Buffer Control */ ++ u8 x08[2]; ++ u16 ccr; /* 0x98 Clock Control */ ++ u16 pll2cr; /* 0x9a PLL2 Control */ ++ u16 pll1cr[2]; /* 0x9c PLL1 Control */ ++ u8 diarcr; /* 0xa0 Device Internal Active Reg Contr*/ ++ u8 dbocr; /* 0xa1 Device Buffer Off Control */ ++ u8 x09[0x3e]; ++ u8 fer; /* 0xe0 Function Enable */ ++ u8 x10[3]; ++ u16 mcr; /* 0xe4 Mode Control */ ++ u8 x11[0x14]; ++ u8 config; /* 0xfc Configuration Control */ ++ u8 x12[2]; ++ u8 debug; /* 0xff Debug */ ++} __attribute__ ((packed)); ++ ++union tc6393_scr_fer { ++ u8 raw; ++struct { ++ unsigned usben:1; /* D0 USB enable */ ++ unsigned lcdcven:1; /* D1 polysylicon TFT enable */ ++ unsigned slcden:1; /* D2 SLCD enable */ ++} __attribute__ ((packed)); ++} __attribute__ ((packed)); ++ ++union tc6393_scr_ccr { ++ u16 raw; ++struct { ++ unsigned ck32ken:1; /* D0 SD host clock enable */ ++ unsigned usbcken:1; /* D1 USB host clock enable */ ++ unsigned x00:2; ++ unsigned sharp:1; /* D4 ??? set in Sharp's code */ ++ unsigned x01:3; ++ enum { disable = 0, ++ m12MHz = 1, ++ m24MHz = 2, ++ m48MHz = 3, ++ } mclksel:3; /* D10-D8 LCD controller clock */ ++ unsigned x02:1; ++ enum { h24MHz = 0, ++ h48MHz = 1, ++ } hclksel:2; /* D13-D12 host bus clock */ ++ unsigned x03:2; ++} __attribute__ ((packed)); ++} __attribute__ ((packed)); ++ ++/*--------------------------------------------------------------------------*/ ++ ++struct tc6393 { ++ spinlock_t lock; /* read-modify-write lock */ ++ struct device* dev; /* TC6393 device */ ++ struct tc6393_scr __iomem *scr; /* system configuration reg */ ++ ++ struct resource rscr; /* system config reg resource */ ++ struct resource* iomem; /* entire TC6393 iomem resource */ ++ unsigned int irq; /* hardware cascade irq */ ++ ++ struct tmio_device tdev [TC6393_NUM_CELLS]; ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static u32 tc6393_ioread32 (const void __iomem *addr) ++{ ++ return ((u32) ioread16 (addr)) | (((u32) ioread16 (addr + 2)) << 16); ++} ++ ++static u32 tc6393_iowrite32 (u32 val, const void __iomem *addr) ++{ ++ iowrite16 (val, addr); ++ iowrite16 (val >> 16, addr + 2); ++ return val; ++} ++ ++u32 get_tc6393_gpio (struct device *dev) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ ++ return tc6393_ioread32 (scr->gpo_dsr); ++} ++EXPORT_SYMBOL (get_tc6393_gpio); ++ ++u32 set_tc6393_gpio (struct device *dev, u32 bits) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ u32 dsr; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ dsr = tc6393_ioread32 (scr->gpo_dsr) | bits; ++ tc6393_iowrite32 (dsr, scr->gpo_dsr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++ ++ return dsr; ++} ++EXPORT_SYMBOL (set_tc6393_gpio); ++ ++u32 reset_tc6393_gpio (struct device *dev, u32 bits) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ u32 dsr; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ dsr = tc6393_ioread32 (scr->gpo_dsr) & ~bits; ++ tc6393_iowrite32 (dsr, scr->gpo_dsr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++ ++ return dsr; ++} ++EXPORT_SYMBOL (reset_tc6393_gpio); ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void ++tc6393_irq (unsigned int irq, struct irqdesc *desc) ++{ ++ struct tc6393* tc6393 = get_irq_chipdata (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned int isr; ++ unsigned int bit; ++ unsigned int i; ++ ++ desc->chip->ack (irq); ++ ++ while ((isr = ioread8(&scr->isr) & ~ioread8(&scr->imr))) ++ for (bit = 1, i = IRQ_TC6393_START; i <= IRQ_TC6393_LCD; ++ bit <<= 1, i++) ++ if (isr & bit) ++ desc_handle_irq (i, irq_desc + i); ++} ++ ++static void tc6393_irq_ack (unsigned int irq) ++{ ++} ++ ++static void tc6393_irq_mask (unsigned int irq) ++{ ++ struct tc6393* tc6393 = get_irq_chipdata (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ iowrite8 (ioread8 (&scr->imr) | (1 << (irq - IRQ_TC6393_START)), ++ &scr->imr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void tc6393_irq_unmask (unsigned int irq) ++{ ++ struct tc6393* tc6393 = get_irq_chipdata (irq); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ iowrite8 (ioread8 (&scr->imr) & ~(1 << (irq - IRQ_TC6393_START)), ++ &scr->imr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static struct irqchip tc6393_chip = { ++ .ack = tc6393_irq_ack, ++ .mask = tc6393_irq_mask, ++ .unmask = tc6393_irq_unmask, ++}; ++ ++static void tc6393_attach_irq (struct tc6393 *tc6393) ++{ ++ unsigned int irq; ++ ++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) { ++ set_irq_chip (irq, &tc6393_chip); ++ set_irq_chipdata(irq, tc6393); ++ set_irq_handler (irq, do_edge_IRQ); ++ set_irq_flags (irq, IRQF_VALID | IRQF_PROBE); ++ } ++ ++ set_irq_type (tc6393->irq, IRQT_FALLING); ++ set_irq_chipdata (tc6393->irq, tc6393); ++ set_irq_chained_handler (tc6393->irq, tc6393_irq); ++} ++ ++static void tc6393_detach_irq (struct tc6393 *tc6393) ++{ ++ unsigned int irq; ++ ++ set_irq_chained_handler (tc6393->irq, NULL); ++ set_irq_chipdata (tc6393->irq, NULL); ++ ++ for (irq = IRQ_TC6393_START; irq <= IRQ_TC6393_LCD; irq++) { ++ set_irq_flags (irq, 0); ++ set_irq_chip (irq, NULL); ++ set_irq_chipdata(irq, NULL); ++ } ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int tc6393_bus_match (struct device *dev, struct device_driver *drv) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ ++ return !strcmp (cell->name, drv->name); ++} ++ ++static int tc6393_bus_suspend (struct device *dev, pm_message_t state) ++{ ++ struct device_driver* drv = dev->driver; ++ return drv && drv->suspend ? drv->suspend (dev, state) : 0; ++} ++ ++static int tc6393_bus_resume (struct device *dev) ++{ ++ struct device_driver* drv = dev->driver; ++ return drv && drv->resume ? drv->resume (dev) : 0; ++} ++ ++struct bus_type tc6393_bus_type = { ++ .name = TMIO_NAME_BUS, ++ .match = tc6393_bus_match, ++ .suspend = tc6393_bus_suspend, ++ .resume = tc6393_bus_resume, ++}; ++EXPORT_SYMBOL (tc6393_bus_type); ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tc6393_cell_clock (struct device *dev, int enable) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ union tc6393_scr_ccr ccr; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ ccr.raw = ioread16 (&scr->ccr); ++ ++ switch (cell->id) { ++ case TC6393_CELL_SD: ccr.ck32ken = enable; break; ++ case TC6393_CELL_OHCI: ccr.usbcken = enable; break; ++ case TC6393_CELL_LCD: ++ ccr.mclksel = enable ? m48MHz : disable; ++ break; ++ } ++ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->ccr = %04x\n", ccr.raw); ++ ++ iowrite16(ccr.raw, &scr->ccr); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void tc6393_cell_function (struct device *dev, int enable) ++{ ++ struct tmio_device* tdev = dev_to_tdev (dev); ++ const struct tc6393_cell* cell = tdev->soc_data; ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ union tc6393_scr_fer fer; ++ unsigned long flags; ++ ++ if (cell->id == TC6393_CELL_NAND) { ++ if (enable) { ++ /* SMD buffer on */ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": SMD buffer on\n"); ++ iowrite8 (0xff, scr->gpi_bcr + 1); ++ } ++ return; ++ } ++ ++ spin_lock_irqsave (&tc6393->lock, flags); ++ fer.raw = ioread16 (&scr->fer); ++ ++ switch (cell->id) { ++ case TC6393_CELL_OHCI: fer.usben = enable; break; ++ case TC6393_CELL_LCD: fer.slcden = enable; break; ++ } ++ ++ printk (KERN_DEBUG TMIO_NAME_CORE ": scr->fer = %02x\n", fer.raw); ++ ++ iowrite8 (fer.raw, &scr->fer); ++ spin_unlock_irqrestore (&tc6393->lock, flags); ++} ++ ++static void ++tc6393_lcd_mode (struct device *dev, const struct fb_videomode *mode) ++{ ++ struct tc6393* tc6393 = dev_get_drvdata (dev->parent); ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ ++ iowrite16 (mode->pixclock, scr->pll1cr + 0); ++ iowrite16 (mode->pixclock >> 16, scr->pll1cr + 1); ++} ++ ++static struct tmio_cell_ops tc6393_cell_ops = { ++ .clock = tc6393_cell_clock, ++ .function = tc6393_cell_function, ++ .lcd_mode = tc6393_lcd_mode, ++}; ++ ++static void tc6393_device_release (struct device *dev) ++{ ++} ++ ++static int ++tc6393_device_register (struct tc6393 *tc6393, struct tmio_cell *tcell) ++{ ++ const struct tc6393_cell* cell; ++ struct tmio_device* tdev; ++ struct device* dev; ++ int i; ++ ++ for (i = 0; strcmp (tcell->name, tc6393_cell [i].name); ) ++ if (++i >= ARRAY_SIZE(tc6393_cell)) ++ return -EINVAL; ++ ++ cell = tc6393_cell + i; ++ tdev = tc6393->tdev + i; ++ dev = &tdev->dev; ++ ++ tdev->ops = &tc6393_cell_ops; ++ tdev->iomem = tc6393->iomem; ++ tdev->soc_data = (void*) cell; ++ ++ dev->parent = tc6393->dev; ++ strncpy (dev->bus_id, cell->name, sizeof dev->bus_id); ++ dev->bus = &tc6393_bus_type; ++ dev->dma_mask = tc6393->dev->dma_mask; ++ dev->coherent_dma_mask = tc6393->dev->coherent_dma_mask; ++ dev->release = tc6393_device_release; ++ dev->platform_data = tcell->platform_data; ++ ++ for (i=0; i < cell->num_resources; i++) { ++ const struct resource* cr = cell->resource + i; ++ struct resource* dr = tdev->resource + i; ++ ++ dr->name = cr->name; ++ dr->start = cr->start; ++ dr->end = cr->end; ++ dr->flags = cr->flags; ++ ++ /* convert memory offsets to absolutes */ ++ if (cr->flags & IORESOURCE_MEM) { ++ dr->start += tc6393->iomem->start; ++ dr->end += tc6393->iomem->start; ++ } ++ } ++ ++ return device_register (dev); ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tc6393_hw_init (struct tc6393 *tc6393) ++{ ++ struct tc6393_scr __iomem * scr = tc6393->scr; ++ struct tc6393_platform_data* tcpd = tc6393->dev->platform_data; ++ ++ tcpd->enable (tc6393->dev); ++ ++ iowrite8 (0, &scr->fer); ++ iowrite16(tcpd->scr_pll2cr, &scr->pll2cr); ++ iowrite16(tcpd->scr_ccr, &scr->ccr); ++ iowrite16(tcpd->scr_mcr, &scr->mcr); ++ iowrite16(tcpd->scr_gper, &scr->gper); ++ iowrite8 (0, &scr->irr); ++ iowrite8 (0xbf, &scr->imr); ++ iowrite16(tcpd->scr_gpo_dsr, scr->gpo_dsr +