From b358a64c1fdd1eb80da57f919c893d910db95e37 Mon Sep 17 00:00:00 2001 From: Ian Molton 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 +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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 + * (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. + * + */ + +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 +#include + +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