diff options
author | Dmitry Baryshkov <dbaryshkov@gmail.com> | 2008-12-09 00:35:08 +0300 |
---|---|---|
committer | Graeme Gregory <dp@xora.org.uk> | 2009-01-29 01:46:20 +0000 |
commit | f186c8103c9195eb53238c928fe03f7af1d43479 (patch) | |
tree | 1be5cb46bf7e480bfac63231c94534910eb34a89 /packages/kexecboot/linux-kexecboot-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch | |
parent | a1514b599c421d2816e25aabab8578b30303c4ab (diff) |
linux-kexecboot: support tosa
port patches from linux-rp-2.6.24 to linux-kexecboot to enable booting
on tosa.
Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
Diffstat (limited to 'packages/kexecboot/linux-kexecboot-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch')
-rw-r--r-- | packages/kexecboot/linux-kexecboot-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch | 891 |
1 files changed, 891 insertions, 0 deletions
diff --git a/packages/kexecboot/linux-kexecboot-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch b/packages/kexecboot/linux-kexecboot-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch new file mode 100644 index 0000000000..6ff752d1ff --- /dev/null +++ b/packages/kexecboot/linux-kexecboot-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch @@ -0,0 +1,891 @@ +From b358a64c1fdd1eb80da57f919c893d910db95e37 Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Sat, 29 Dec 2007 15:26:19 +0000 +Subject: [PATCH 11/64] MMC driver for TMIO devices + +--- + drivers/mmc/host/Kconfig | 6 + + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/tmio_mmc.c | 633 +++++++++++++++++++++++++++++++++++++++++++ + drivers/mmc/host/tmio_mmc.h | 205 ++++++++++++++ + 4 files changed, 845 insertions(+), 0 deletions(-) + create mode 100644 drivers/mmc/host/tmio_mmc.c + create mode 100644 drivers/mmc/host/tmio_mmc.h + +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 5fef678..f8f9b7e 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -130,3 +130,9 @@ config MMC_SPI + + If unsure, or if your system has no SPI master driver, say N. + ++config MMC_TMIO ++ tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support" ++ depends on MMC ++ help ++ This provides support for the SD/MMC cell found in TC6393XB, ++ T7L66XB and also ipaq ASIC3 +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index 3877c87..7ac956b 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -17,4 +17,5 @@ obj-$(CONFIG_MMC_OMAP) += omap.o + obj-$(CONFIG_MMC_AT91) += at91_mci.o + obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o + obj-$(CONFIG_MMC_SPI) += mmc_spi.o ++obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o + +diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c +new file mode 100644 +index 0000000..735c386 +--- /dev/null ++++ b/drivers/mmc/host/tmio_mmc.c +@@ -0,0 +1,633 @@ ++/* ++ * linux/drivers/mmc/tmio_mmc.c ++ * ++ * Copyright (C) 2004 Ian Molton ++ * Copyright (C) 2007 Ian Molton ++ * ++ * 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. ++ * ++ * Driver for the MMC / SD / SDIO cell found in: ++ * ++ * TC6393XB TC6391XB TC6387XB T7L66XB ++ * ++ * This driver draws mainly on scattered spec sheets, Reverse engineering ++ * of the toshiba e800 SD driver and some parts of the 2.4 ASIC3 driver (4 bit ++ * support). (Further 4 bit support from a later datasheet). ++ * ++ * TODO: ++ * Investigate using a workqueue for PIO transfers ++ * Eliminate FIXMEs ++ * SDIO support ++ * Better Power management ++ * Handle MMC errors better ++ * double buffer support ++ * ++ */ ++#include <linux/module.h> ++#include <linux/irq.h> ++#include <linux/device.h> ++#include <linux/delay.h> ++#include <linux/mmc/mmc.h> ++#include <linux/mmc/host.h> ++#include <linux/mfd-core.h> ++#include <linux/mfd/tmio.h> ++ ++#include "tmio_mmc.h" ++ ++/* ++ * Fixme - documentation conflicts on what the clock values are for the ++ * various dividers. ++ * One document I have says that its a divisor of a 24MHz clock, another 33. ++ * This probably depends on HCLK for a given platform, so we may need to ++ * require HCLK be passed to us from the MFD core. ++ * ++ */ ++ ++static void tmio_mmc_set_clock (struct tmio_mmc_host *host, int new_clock) { ++ struct tmio_mmc_cnf __iomem *cnf = host->cnf; ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ u32 clk = 0, clock; ++ ++ if (new_clock) { ++ for(clock = 46875, clk = 0x100; new_clock >= (clock<<1); ){ ++ clock <<= 1; ++ clk >>= 1; ++ } ++ if(clk & 0x1) ++ clk = 0x20000; ++ ++ clk >>= 2; ++ if(clk & 0x8000) /* For full speed we disable the divider. */ ++ writeb(0, &cnf->sd_clk_mode); ++ else ++ writeb(1, &cnf->sd_clk_mode); ++ clk |= 0x100; ++ } ++ ++ writew(clk, &ctl->sd_card_clk_ctl); ++} ++ ++static void tmio_mmc_clk_stop (struct tmio_mmc_host *host) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ writew(0x0000, &ctl->clk_and_wait_ctl); ++ msleep(10); ++ writew(readw(&ctl->sd_card_clk_ctl) & ~0x0100, &ctl->sd_card_clk_ctl); ++ msleep(10); ++} ++ ++static void tmio_mmc_clk_start (struct tmio_mmc_host *host) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ writew(readw(&ctl->sd_card_clk_ctl) | 0x0100, &ctl->sd_card_clk_ctl); ++ msleep(10); ++ writew(0x0100, &ctl->clk_and_wait_ctl); ++ msleep(10); ++} ++ ++static void reset(struct tmio_mmc_host *host) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ /* FIXME - should we set stop clock reg here */ ++ writew(0x0000, &ctl->reset_sd); ++ writew(0x0000, &ctl->reset_sdio); ++ msleep(10); ++ writew(0x0001, &ctl->reset_sd); ++ writew(0x0001, &ctl->reset_sdio); ++ msleep(10); ++} ++ ++static void ++tmio_mmc_finish_request(struct tmio_mmc_host *host) ++{ ++ struct mmc_request *mrq = host->mrq; ++ ++ host->mrq = NULL; ++ host->cmd = NULL; ++ host->data = NULL; ++ ++ mmc_request_done(host->mmc, mrq); ++} ++ ++/* These are the bitmasks the tmio chip requires to implement the MMC response ++ * types. Note that R1 and R6 are the same in this scheme. */ ++#define APP_CMD 0x0040 ++#define RESP_NONE 0x0300 ++#define RESP_R1 0x0400 ++#define RESP_R1B 0x0500 ++#define RESP_R2 0x0600 ++#define RESP_R3 0x0700 ++#define DATA_PRESENT 0x0800 ++#define TRANSFER_READ 0x1000 ++#define TRANSFER_MULTI 0x2000 ++#define SECURITY_CMD 0x4000 ++ ++static void ++tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd) ++{ ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ struct mmc_data *data = host->data; ++ int c = cmd->opcode; ++ ++ if(cmd->opcode == MMC_STOP_TRANSMISSION) { ++ writew(0x001, &ctl->stop_internal_action); ++ return; ++ } ++ ++ switch(mmc_resp_type(cmd)) { ++ case MMC_RSP_NONE: c |= RESP_NONE; break; ++ case MMC_RSP_R1: c |= RESP_R1; break; ++ case MMC_RSP_R1B: c |= RESP_R1B; break; ++ case MMC_RSP_R2: c |= RESP_R2; break; ++ case MMC_RSP_R3: c |= RESP_R3; break; ++ default: ++ DBG("Unknown response type %d\n", mmc_resp_type(cmd)); ++ } ++ ++ host->cmd = cmd; ++ ++/* FIXME - this seems to be ok comented out but the spec suggest this bit should ++ * be set when issuing app commands. ++ * if(cmd->flags & MMC_FLAG_ACMD) ++ * c |= APP_CMD; ++ */ ++ if(data) { ++ c |= DATA_PRESENT; ++ if(data->blocks > 1) { ++ writew(0x100, &ctl->stop_internal_action); ++ c |= TRANSFER_MULTI; ++ } ++ if(data->flags & MMC_DATA_READ) ++ c |= TRANSFER_READ; ++ } ++ ++ enable_mmc_irqs(ctl, TMIO_MASK_CMD); ++ ++ /* Fire off the command */ ++ tmio_iowrite32(cmd->arg, ctl->arg_reg); ++ writew(c, &ctl->sd_cmd); ++} ++ ++/* This chip always returns (at least?) as much data as you ask for. ++ * Im unsure what happens if you ask for less than a block. This should be ++ * looked into to ensure that a funny length read doesnt hose the controller. ++ * ++ * FIXME - this chip cannot do 1 and 2 byte data requests in 4 bit mode ++ */ ++static inline void tmio_mmc_pio_irq(struct tmio_mmc_host *host) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ struct mmc_data *data = host->data; ++ unsigned short *buf; ++ unsigned int count; ++ unsigned long flags; ++ ++ if(!data){ ++ DBG("Spurious PIO IRQ\n"); ++ return; ++ } ++ ++ buf = (unsigned short *)(tmio_mmc_kmap_atomic(host, &flags) + ++ host->sg_off); ++ ++ /* Ensure we dont read more than one block. The chip will interrupt us ++ * When the next block is available. ++ * FIXME - this is probably not true now IRQ handling is fixed ++ */ ++ count = host->sg_ptr->length - host->sg_off; ++ if(count > data->blksz) ++ count = data->blksz; ++ ++ DBG("count: %08x offset: %08x flags %08x\n", ++ count, host->sg_off, data->flags); ++ ++ /* Transfer the data */ ++ if(data->flags & MMC_DATA_READ) ++ readsw(&ctl->sd_data_port[0], buf, count >> 1); ++ else ++ writesw(&ctl->sd_data_port[0], buf, count >> 1); ++ ++ host->sg_off += count; ++ ++ tmio_mmc_kunmap_atomic(host, &flags); ++ ++ if(host->sg_off == host->sg_ptr->length) ++ tmio_mmc_next_sg(host); ++ ++ return; ++} ++ ++static inline void tmio_mmc_data_irq(struct tmio_mmc_host *host) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ struct mmc_data *data = host->data; ++ ++ host->data = NULL; ++ ++ if(!data){ ++ DBG("Spurious data end IRQ\n"); ++ return; ++ } ++ ++ /* FIXME - return correct transfer count on errors */ ++ if (!data->error) ++ data->bytes_xfered = data->blocks * data->blksz; ++ else ++ data->bytes_xfered = 0; ++ ++ DBG("Completed data request\n"); ++ ++ /*FIXME - other drivers allow an optional stop command of any given type ++ * which we dont do, as the chip can auto generate them. ++ * Perhaps we can be smarter about when to use auto CMD12 and ++ * only issue the auto request when we know this is the desired ++ * stop command, allowing fallback to the stop command the ++ * upper layers expect. For now, we do what works. ++ */ ++ ++ writew(0x000, &ctl->stop_internal_action); ++ ++ if(data->flags & MMC_DATA_READ) ++ disable_mmc_irqs(ctl, TMIO_MASK_READOP); ++ else ++ disable_mmc_irqs(ctl, TMIO_MASK_WRITEOP); ++ ++ tmio_mmc_finish_request(host); ++} ++ ++static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ struct mmc_command *cmd = host->cmd; ++ ++ if(!host->cmd) { ++ DBG("Spurious CMD irq\n"); ++ return; ++ } ++ ++ host->cmd = NULL; ++ ++ /* This controller is sicker than the PXA one. not only do we need to ++ * drop the top 8 bits of the first response word, we also need to ++ * modify the order of the response for short response command types. ++ */ ++ ++ /* FIXME - this works but readl is wrong and will break on asic3... */ ++ cmd->resp[3] = tmio_ioread32(&ctl->response[0]); ++ cmd->resp[2] = tmio_ioread32(&ctl->response[2]); ++ cmd->resp[1] = tmio_ioread32(&ctl->response[4]); ++ cmd->resp[0] = tmio_ioread32(&ctl->response[6]); ++ ++ if(cmd->flags & MMC_RSP_136) { ++ cmd->resp[0] = (cmd->resp[0] <<8) | (cmd->resp[1] >>24); ++ cmd->resp[1] = (cmd->resp[1] <<8) | (cmd->resp[2] >>24); ++ cmd->resp[2] = (cmd->resp[2] <<8) | (cmd->resp[3] >>24); ++ cmd->resp[3] <<= 8; ++ } ++ else if(cmd->flags & MMC_RSP_R3) { ++ cmd->resp[0] = cmd->resp[3]; ++ } ++ ++ if (stat & TMIO_STAT_CMDTIMEOUT) ++ cmd->error = -ETIMEDOUT; ++ else if (stat & TMIO_STAT_CRCFAIL && cmd->flags & MMC_RSP_CRC) ++ cmd->error = -EILSEQ; ++ ++ /* If there is data to handle we enable data IRQs here, and ++ * we will ultimatley finish the request in the data_end handler. ++ * If theres no data or we encountered an error, finish now. ++ */ ++ if(host->data && !cmd->error){ ++ if(host->data->flags & MMC_DATA_READ) ++ enable_mmc_irqs(ctl, TMIO_MASK_READOP); ++ else ++ enable_mmc_irqs(ctl, TMIO_MASK_WRITEOP); ++ } ++ else { ++ tmio_mmc_finish_request(host); ++ } ++ ++ return; ++} ++ ++ ++static irqreturn_t tmio_mmc_irq(int irq, void *devid) ++{ ++ struct tmio_mmc_host *host = devid; ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ unsigned int ireg, irq_mask, status; ++ ++ DBG("MMC IRQ begin\n"); ++ ++ status = tmio_ioread32(ctl->status); ++ irq_mask = tmio_ioread32(ctl->irq_mask); ++ ireg = status & TMIO_MASK_IRQ & ~irq_mask; ++ ++#ifdef CONFIG_MMC_DEBUG ++ debug_status(status); ++ debug_status(ireg); ++#endif ++ if (!ireg) { ++ disable_mmc_irqs(ctl, status & ~irq_mask); ++#ifdef CONFIG_MMC_DEBUG ++ WARN("tmio_mmc: Spurious MMC irq, disabling! 0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg); ++ debug_status(status); ++#endif ++ goto out; ++ } ++ ++ while (ireg) { ++ /* Card insert / remove attempts */ ++ if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)){ ++ ack_mmc_irqs(ctl, TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE); ++ mmc_detect_change(host->mmc,0); ++ } ++ ++ /* CRC and other errors */ ++/* if (ireg & TMIO_STAT_ERR_IRQ) ++ * handled |= tmio_error_irq(host, irq, stat); ++ */ ++ ++ /* Command completion */ ++ if (ireg & TMIO_MASK_CMD) { ++ tmio_mmc_cmd_irq(host, status); ++ ack_mmc_irqs(ctl, TMIO_MASK_CMD); ++ } ++ ++ /* Data transfer */ ++ if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) { ++ ack_mmc_irqs(ctl, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ); ++ tmio_mmc_pio_irq(host); ++ } ++ ++ /* Data transfer completion */ ++ if (ireg & TMIO_STAT_DATAEND) { ++ tmio_mmc_data_irq(host); ++ ack_mmc_irqs(ctl, TMIO_STAT_DATAEND); ++ } ++ ++ /* Check status - keep going until we've handled it all */ ++ status = tmio_ioread32(ctl->status); ++ irq_mask = tmio_ioread32(ctl->irq_mask); ++ ireg = status & TMIO_MASK_IRQ & ~irq_mask; ++ ++#ifdef CONFIG_MMC_DEBUG ++ DBG("Status at end of loop: %08x\n", status); ++ debug_status(status); ++#endif ++ } ++ DBG("MMC IRQ end\n"); ++ ++out: ++ return IRQ_HANDLED; ++} ++ ++static void tmio_mmc_start_data(struct tmio_mmc_host *host, struct mmc_data *data) ++{ ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ DBG("setup data transfer: blocksize %08x nr_blocks %d\n", ++ data->blksz, data->blocks); ++ ++ tmio_mmc_init_sg(host, data); ++ host->data = data; ++ ++ /* Set transfer length / blocksize */ ++ writew(data->blksz, &ctl->sd_xfer_len); ++ writew(data->blocks, &ctl->xfer_blk_count); ++} ++ ++/* Process requests from the MMC layer */ ++static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ ++ WARN_ON(host->mrq != NULL); ++ ++ host->mrq = mrq; ++ ++ /* If we're performing a data request we need to setup some ++ extra information */ ++ if (mrq->data) ++ tmio_mmc_start_data(host, mrq->data); ++ ++ tmio_mmc_start_command(host, mrq->cmd); ++} ++ ++/* Set MMC clock / power. ++ * Note: This controller uses a simple divider scheme therefore it cannot ++ * run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as ++ * MMC wont run that fast, it has to be clocked at 12MHz which is the next ++ * slowest setting. ++ */ ++static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ struct tmio_mmc_cnf __iomem *cnf = host->cnf; ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ if(ios->clock) ++ tmio_mmc_set_clock (host, ios->clock); ++ ++ /* Power sequence - OFF -> ON -> UP */ ++ switch (ios->power_mode) { ++ case MMC_POWER_OFF: ++ writeb(0x00, &cnf->pwr_ctl[1]); /* power down SD bus */ ++ tmio_mmc_clk_stop(host); ++ break; ++ case MMC_POWER_ON: ++ writeb(0x02, &cnf->pwr_ctl[1]); /* power up SD bus */ ++ break; ++ case MMC_POWER_UP: ++ tmio_mmc_clk_start(host); /* start bus clock */ ++ break; ++ } ++ ++ switch (ios->bus_width) { ++ case MMC_BUS_WIDTH_1: ++ writew(0x80e0, &ctl->sd_mem_card_opt); ++ break; ++ case MMC_BUS_WIDTH_4: ++ writew(0x00e0, &ctl->sd_mem_card_opt); ++ break; ++ } ++ ++ /* Potentially we may need a 140us pause here. FIXME */ ++ udelay(140); ++} ++ ++static int tmio_mmc_get_ro(struct mmc_host *mmc) { ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ return (readw(&ctl->status[0]) & TMIO_STAT_WRPROTECT)?0:1; ++} ++ ++static struct mmc_host_ops tmio_mmc_ops = { ++ .request = tmio_mmc_request, ++ .set_ios = tmio_mmc_set_ios, ++ .get_ro = tmio_mmc_get_ro, ++}; ++ ++static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state) { ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ int ret; ++ ++ ret = mmc_suspend_host(mmc, state); ++ ++ /* Tell MFD core it can disable us now.*/ ++ if(!ret && cell->disable) ++ cell->disable(dev); ++ ++ return ret; ++} ++ ++static int tmio_mmc_resume(struct platform_device *dev) { ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ struct tmio_mmc_cnf __iomem *cnf = host->cnf; ++ ++ /* Enable the MMC/SD Control registers */ ++ writew(SDCREN, &cnf->cmd); ++ writel(dev->resource[0].start & 0xfffe, &cnf->ctl_base); ++ ++ /* Tell the MFD core we are ready to be enabled */ ++ if(cell->enable) ++ cell->enable(dev); ++ ++ mmc_resume_host(mmc); ++ ++ return 0; ++} ++ ++static int __devinit tmio_mmc_probe(struct platform_device *dev) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct tmio_mmc_cnf __iomem *cnf; ++ struct tmio_mmc_ctl __iomem *ctl; ++ struct tmio_mmc_host *host; ++ struct mmc_host *mmc; ++ int ret = -ENOMEM; ++ ++ mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &dev->dev); ++ if (!mmc) { ++ goto out; ++ } ++ ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ platform_set_drvdata(dev, mmc); /* Used so we can de-init safely. */ ++ ++ host->cnf = cnf = ioremap((unsigned long)dev->resource[1].start, ++ (unsigned long)dev->resource[1].end - ++ (unsigned long)dev->resource[1].start); ++ if(!host->cnf) ++ goto host_free; ++ ++ host->ctl = ctl = ioremap((unsigned long)dev->resource[0].start, ++ (unsigned long)dev->resource[0].end - ++ (unsigned long)dev->resource[0].start); ++ if (!host->ctl) { ++ goto unmap_cnf; ++ } ++ ++ mmc->ops = &tmio_mmc_ops; ++ mmc->caps = MMC_CAP_4_BIT_DATA; ++ mmc->f_min = 46875; ++ mmc->f_max = 24000000; ++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ++ ++ /* Enable the MMC/SD Control registers */ ++ writew(SDCREN, &cnf->cmd); ++ writel(dev->resource[0].start & 0xfffe, &cnf->ctl_base); ++ ++ /* Tell the MFD core we are ready to be enabled */ ++ if(cell->enable) ++ cell->enable(dev); ++ ++ writeb(0x01,&cnf->pwr_ctl[2]); /* Disable SD power during suspend */ ++ writeb(0x1f, &cnf->stop_clk_ctl); /* Route clock to SDIO??? FIXME */ ++ writeb(0x0, &cnf->pwr_ctl[1]); /* Power down SD bus*/ ++ tmio_mmc_clk_stop(host); /* Stop bus clock */ ++ reset(host); /* Reset MMC HC */ ++ ++ host->irq = (unsigned long)dev->resource[2].start; ++ ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED, "tmio-mmc", host); ++ if (ret){ ++ ret = -ENODEV; ++ DBG("Failed to allocate IRQ.\n"); ++ goto unmap_ctl; ++ } ++ set_irq_type(host->irq, IRQT_FALLING); ++ ++ mmc_add_host(mmc); ++ ++ printk(KERN_INFO "%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc), ++ (unsigned long)host->ctl, host->irq); ++ ++ /* Lets unmask the IRQs we want to know about */ ++ disable_mmc_irqs(ctl, TMIO_MASK_ALL); ++ enable_mmc_irqs(ctl, TMIO_MASK_IRQ); ++ ++ return 0; ++ ++unmap_ctl: ++ iounmap(host->ctl); ++unmap_cnf: ++ iounmap(host->cnf); ++host_free: ++ mmc_free_host(mmc); ++out: ++ return ret; ++} ++ ++static int __devexit tmio_mmc_remove(struct platform_device *dev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ ++ platform_set_drvdata(dev, NULL); ++ ++ if (mmc) { ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ mmc_remove_host(mmc); ++ free_irq(host->irq, host); ++ /* FIXME - we might want to consider stopping the chip here. */ ++ iounmap(host->ctl); ++ iounmap(host->cnf); ++ mmc_free_host(mmc); /* FIXME - why does this call hang ? */ ++ } ++ return 0; ++} ++ ++/* ------------------- device registration ----------------------- */ ++ ++static struct platform_driver tmio_mmc_driver = { ++ .driver = { ++ .name = "tmio-mmc", ++ }, ++ .probe = tmio_mmc_probe, ++ .remove = __devexit_p(tmio_mmc_remove), ++#ifdef CONFIG_PM ++ .suspend = tmio_mmc_suspend, ++ .resume = tmio_mmc_resume, ++#endif ++}; ++ ++ ++static int __init tmio_mmc_init(void) ++{ ++ return platform_driver_register (&tmio_mmc_driver); ++} ++ ++static void __exit tmio_mmc_exit(void) ++{ ++ platform_driver_unregister (&tmio_mmc_driver); ++} ++ ++module_init(tmio_mmc_init); ++module_exit(tmio_mmc_exit); ++ ++MODULE_DESCRIPTION("Toshiba TMIO SD/MMC driver"); ++MODULE_AUTHOR("Ian Molton <spyro@f2s.com>"); ++MODULE_LICENSE("GPLv2"); +diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h +new file mode 100644 +index 0000000..d4d9f8f +--- /dev/null ++++ b/drivers/mmc/host/tmio_mmc.h +@@ -0,0 +1,205 @@ ++/* Definitons for use with the tmio_mmc.c ++ * ++ * (c) 2005 Ian Molton <spyro@f2s.com> ++ * (c) 2007 Ian Molton <spyro@f2s.com> ++ * ++ * 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. ++ * ++ */ ++ ++struct tmio_mmc_cnf { ++ u8 x00[4]; ++ u16 cmd; ++ u8 x01[10]; ++ u32 ctl_base; ++ u8 x02[41]; ++ u8 int_pin; ++ u8 x03[2]; ++ u8 stop_clk_ctl; ++ u8 gclk_ctl; /* Gated Clock Control */ ++ u8 sd_clk_mode; /* 0x42 */ ++ u8 x04; ++ u16 pin_status; ++ u8 x05[2]; ++ u8 pwr_ctl[3]; ++ u8 x06; ++ u8 card_detect_mode; ++ u8 x07[3]; ++ u8 sd_slot; ++ u8 x08[159]; ++ u8 ext_gclk_ctl_1; /* Extended Gated Clock Control 1 */ ++ u8 ext_gclk_ctl_2; /* Extended Gated Clock Control 2 */ ++ u8 x09[7]; ++ u8 ext_gclk_ctl_3; /* Extended Gated Clock Control 3 */ ++ u8 sd_led_en_1; ++ u8 x10[3]; ++ u8 sd_led_en_2; ++ u8 x11; ++} __attribute__ ((packed)); ++ ++#define SDCREN 0x2 /* Enable access to MMC CTL regs. (flag in COMMAND_REG)*/ ++ ++struct tmio_mmc_ctl { ++ u16 sd_cmd; ++ u16 x00; ++ u16 arg_reg[2]; ++ u16 stop_internal_action; ++ u16 xfer_blk_count; ++ u16 response[8]; ++ u16 status[2]; ++ u16 irq_mask[2]; ++ u16 sd_card_clk_ctl; ++ u16 sd_xfer_len; ++ u16 sd_mem_card_opt; ++ u16 x01; ++ u16 sd_error_detail_status[2]; ++ u16 sd_data_port[2]; ++ u16 transaction_ctl; ++ u16 x02[85]; ++ u16 reset_sd; ++ u16 x03[15]; ++ u16 sdio_regs[28]; ++ u16 clk_and_wait_ctl; ++ u16 x04[83]; ++ u16 reset_sdio; ++ u16 x05[15]; ++} __attribute__ ((packed)); ++ ++/* Definitions for values the CTRL_STATUS register can take. */ ++#define TMIO_STAT_CMDRESPEND 0x00000001 ++#define TMIO_STAT_DATAEND 0x00000004 ++#define TMIO_STAT_CARD_REMOVE 0x00000008 ++#define TMIO_STAT_CARD_INSERT 0x00000010 ++#define TMIO_STAT_SIGSTATE 0x00000020 ++#define TMIO_STAT_WRPROTECT 0x00000080 ++#define TMIO_STAT_CARD_REMOVE_A 0x00000100 ++#define TMIO_STAT_CARD_INSERT_A 0x00000200 ++#define TMIO_STAT_SIGSTATE_A 0x00000400 ++#define TMIO_STAT_CMD_IDX_ERR 0x00010000 ++#define TMIO_STAT_CRCFAIL 0x00020000 ++#define TMIO_STAT_STOPBIT_ERR 0x00040000 ++#define TMIO_STAT_DATATIMEOUT 0x00080000 ++#define TMIO_STAT_RXOVERFLOW 0x00100000 ++#define TMIO_STAT_TXUNDERRUN 0x00200000 ++#define TMIO_STAT_CMDTIMEOUT 0x00400000 ++#define TMIO_STAT_RXRDY 0x01000000 ++#define TMIO_STAT_TXRQ 0x02000000 ++#define TMIO_STAT_ILL_FUNC 0x20000000 ++#define TMIO_STAT_CMD_BUSY 0x40000000 ++#define TMIO_STAT_ILL_ACCESS 0x80000000 ++ ++/* Define some IRQ masks */ ++/* This is the mask used at reset by the chip */ ++#define TMIO_MASK_ALL 0x837f031d ++#define TMIO_MASK_READOP (TMIO_STAT_RXRDY | TMIO_STAT_DATAEND | \ ++ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT) ++#define TMIO_MASK_WRITEOP (TMIO_STAT_TXRQ | TMIO_STAT_DATAEND | \ ++ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT) ++#define TMIO_MASK_CMD (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT | \ ++ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT) ++#define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD) ++ ++#define enable_mmc_irqs(ctl, i) \ ++ do { \ ++ u32 mask;\ ++ mask = tmio_ioread32((ctl)->irq_mask); \ ++ mask &= ~((i) & TMIO_MASK_IRQ); \ ++ tmio_iowrite32(mask, (ctl)->irq_mask); \ ++ } while (0) ++ ++#define disable_mmc_irqs(ctl, i) \ ++ do { \ ++ u32 mask;\ ++ mask = tmio_ioread32((ctl)->irq_mask); \ ++ mask |= ((i) & TMIO_MASK_IRQ); \ ++ tmio_iowrite32(mask, (ctl)->irq_mask); \ ++ } while (0) ++ ++#define ack_mmc_irqs(ctl, i) \ ++ do { \ ++ u32 mask;\ ++ mask = tmio_ioread32((ctl)->status); \ ++ mask &= ~((i) & TMIO_MASK_IRQ); \ ++ tmio_iowrite32(mask, (ctl)->status); \ ++ } while (0) ++ ++ ++struct tmio_mmc_host { ++ struct tmio_mmc_cnf __iomem *cnf; ++ struct tmio_mmc_ctl __iomem *ctl; ++ struct mmc_command *cmd; ++ struct mmc_request *mrq; ++ struct mmc_data *data; ++ struct mmc_host *mmc; ++ int irq; ++ ++ /* pio related stuff */ ++ struct scatterlist *sg_ptr; ++ unsigned int sg_len; ++ unsigned int sg_off; ++}; ++ ++#include <linux/scatterlist.h> ++#include <linux/blkdev.h> ++ ++static inline void tmio_mmc_init_sg(struct tmio_mmc_host *host, struct mmc_data *data) ++{ ++ host->sg_len = data->sg_len; ++ host->sg_ptr = data->sg; ++ host->sg_off = 0; ++} ++ ++static inline int tmio_mmc_next_sg(struct tmio_mmc_host *host) ++{ ++ host->sg_ptr++; ++ host->sg_off = 0; ++ return --host->sg_len; ++} ++ ++static inline char *tmio_mmc_kmap_atomic(struct tmio_mmc_host *host, unsigned long *flags) ++{ ++ struct scatterlist *sg = host->sg_ptr; ++ ++ local_irq_save(*flags); ++ return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; ++} ++ ++static inline void tmio_mmc_kunmap_atomic(struct tmio_mmc_host *host, unsigned long *flags) ++{ ++ kunmap_atomic(sg_page(host->sg_ptr), KM_BIO_SRC_IRQ); ++ local_irq_restore(*flags); ++} ++ ++#ifdef CONFIG_MMC_DEBUG ++#define DBG(args...) printk(args) ++ ++void debug_status(u32 status){ ++ printk("status: %08x = ", status); ++ if(status & TMIO_STAT_CARD_REMOVE) printk("Card_removed "); ++ if(status & TMIO_STAT_CARD_INSERT) printk("Card_insert "); ++ if(status & TMIO_STAT_SIGSTATE) printk("Sigstate "); ++ if(status & TMIO_STAT_WRPROTECT) printk("Write_protect "); ++ if(status & TMIO_STAT_CARD_REMOVE_A) printk("Card_remove_A "); ++ if(status & TMIO_STAT_CARD_INSERT_A) printk("Card_insert_A "); ++ if(status & TMIO_STAT_SIGSTATE_A) printk("Sigstate_A "); ++ if(status & TMIO_STAT_CMD_IDX_ERR) printk("Cmd_IDX_Err "); ++ if(status & TMIO_STAT_STOPBIT_ERR) printk("Stopbit_ERR "); ++ if(status & TMIO_STAT_ILL_FUNC) printk("ILLEGAL_FUNC "); ++ if(status & TMIO_STAT_CMD_BUSY) printk("CMD_BUSY "); ++ if(status & TMIO_STAT_CMDRESPEND) printk("Response_end "); ++ if(status & TMIO_STAT_DATAEND) printk("Data_end "); ++ if(status & TMIO_STAT_CRCFAIL) printk("CRC_failure "); ++ if(status & TMIO_STAT_DATATIMEOUT) printk("Data_timeout "); ++ if(status & TMIO_STAT_CMDTIMEOUT) printk("Command_timeout "); ++ if(status & TMIO_STAT_RXOVERFLOW) printk("RX_OVF "); ++ if(status & TMIO_STAT_TXUNDERRUN) printk("TX_UND "); ++ if(status & TMIO_STAT_RXRDY) printk("RX_rdy "); ++ if(status & TMIO_STAT_TXRQ) printk("TX_req "); ++ if(status & TMIO_STAT_ILL_ACCESS) printk("ILLEGAL_ACCESS "); ++ printk("\n"); ++} ++#else ++#define DBG(fmt,args...) do { } while (0) ++#endif +-- +1.5.3.8 + |