Index: linux-2.6.17.7-new/drivers/mmc/Kconfig =================================================================== --- linux-2.6.17.7-new.orig/drivers/mmc/Kconfig 2006-08-07 09:32:34.000000000 +0200 +++ linux-2.6.17.7-new/drivers/mmc/Kconfig 2006-08-07 09:33:51.000000000 +0200 @@ -82,6 +82,15 @@ If unsure, say N. +config MMC_S3C2410 + tristate "Samsung S3C2410 Multimedia Card Interface support" + depends on ARCH_S3C2410 && MMC + help + This selects the Samsung S3C2410 Multimedia Card Interface + support. + + If unsure, say N. + config MMC_AU1X tristate "Alchemy AU1XX0 MMC Card Interface support" depends on MMC && SOC_AU1200 Index: linux-2.6.17.7-new/drivers/mmc/Makefile =================================================================== --- linux-2.6.17.7-new.orig/drivers/mmc/Makefile 2006-08-07 09:32:34.000000000 +0200 +++ linux-2.6.17.7-new/drivers/mmc/Makefile 2006-08-07 09:33:51.000000000 +0200 @@ -20,6 +20,7 @@ obj-$(CONFIG_MMC_IMX) += imxmmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_WBSD) += wbsd.o +obj-$(CONFIG_MMC_S3C2410) += s3c2410mci.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_AT91RM9200) += at91_mci.o Index: linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c 2006-08-07 09:33:51.000000000 +0200 @@ -0,0 +1,781 @@ +/* + * linux/drivers/mmc/s3c2410mci.h - Samsung S3C2410 SDI Interface driver + * + * Copyright (C) 2004 Thomas Kleffel, All Rights Reserved. + * + * 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 +#include +#include + +#include +#include +#include + +#ifdef CONFIG_MMC_DEBUG +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) do { } while (0) +#endif + +#include "s3c2410mci.h" + +#define DRIVER_NAME "mmci-s3c2410" +#define PFX DRIVER_NAME ": " + +#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1) + +static struct s3c2410_dma_client s3c2410sdi_dma_client = { + .name = "s3c2410-sdi", +}; + +/* + * ISR for SDI Interface IRQ + * Communication between driver and ISR works as follows: + * host->mrq points to current request + * host->complete_what tells the ISR when the request is considered done + * COMPLETION_CMDSENT when the command was sent + * COMPLETION_RSPFIN when a response was received + * COMPLETION_XFERFINISH when the data transfer is finished + * COMPLETION_XFERFINISH_RSPFIN both of the above. + * host->complete_request is the completion-object the driver waits for + * + * 1) Driver sets up host->mrq and host->complete_what + * 2) Driver prepares the transfer + * 3) Driver enables interrupts + * 4) Driver starts transfer + * 5) Driver waits for host->complete_rquest + * 6) ISR checks for request status (errors and success) + * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error + * 7) ISR completes host->complete_request + * 8) ISR disables interrupts + * 9) Driver wakes up and takes care of the request +*/ + +static irqreturn_t s3c2410sdi_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct s3c2410sdi_host *host; + u32 sdi_csta, sdi_dsta, sdi_dcnt; + u32 sdi_cclear, sdi_dclear; + unsigned long iflags; + + host = (struct s3c2410sdi_host *)dev_id; + + /* Check for things not supposed to happen */ + if(!host) return IRQ_HANDLED; + + sdi_csta = readl(host->base + S3C2410_SDICMDSTAT); + sdi_dsta = readl(host->base + S3C2410_SDIDSTA); + sdi_dcnt = readl(host->base + S3C2410_SDIDCNT); + + DBG(PFX "IRQ csta=0x%08x dsta=0x%08x dcnt:0x%08x\n", sdi_csta, sdi_dsta, sdi_dcnt); + + spin_lock_irqsave( &host->complete_lock, iflags); + + if( host->complete_what==COMPLETION_NONE ) { + goto clear_imask; + } + + if(!host->mrq) { + goto clear_imask; + } + + + sdi_csta = readl(host->base + S3C2410_SDICMDSTAT); + sdi_dsta = readl(host->base + S3C2410_SDIDSTA); + sdi_dcnt = readl(host->base + S3C2410_SDIDCNT); + sdi_cclear = 0; + sdi_dclear = 0; + + + if(sdi_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) { + host->mrq->cmd->error = MMC_ERR_TIMEOUT; + goto transfer_closed; + } + + if(sdi_csta & S3C2410_SDICMDSTAT_CMDSENT) { + if(host->complete_what == COMPLETION_CMDSENT) { + host->mrq->cmd->error = MMC_ERR_NONE; + goto transfer_closed; + } + + sdi_cclear |= S3C2410_SDICMDSTAT_CMDSENT; + } + + if(sdi_csta & S3C2410_SDICMDSTAT_CRCFAIL) { + if (host->mrq->cmd->flags & MMC_RSP_136) { + DBG(PFX "s3c2410 fixup : ignore CRC fail with long rsp\n"); + } + else { + DBG(PFX "COMMAND CRC FAILED %x\n", sdi_csta); + if(host->mrq->cmd->flags & MMC_RSP_CRC) { + host->mrq->cmd->error = MMC_ERR_BADCRC; + goto transfer_closed; + } + } + sdi_cclear |= S3C2410_SDICMDSTAT_CRCFAIL; + } + + if(sdi_csta & S3C2410_SDICMDSTAT_RSPFIN) { + if(host->complete_what == COMPLETION_RSPFIN) { + host->mrq->cmd->error = MMC_ERR_NONE; + goto transfer_closed; + } + + if(host->complete_what == COMPLETION_XFERFINISH_RSPFIN) { + host->mrq->cmd->error = MMC_ERR_NONE; + host->complete_what = COMPLETION_XFERFINISH; + } + + sdi_cclear |= S3C2410_SDICMDSTAT_RSPFIN; + } + + if(sdi_dsta & S3C2410_SDIDSTA_FIFOFAIL) { + host->mrq->cmd->error = MMC_ERR_NONE; + host->mrq->data->error = MMC_ERR_FIFO; + goto transfer_closed; + } + + if(sdi_dsta & S3C2410_SDIDSTA_RXCRCFAIL) { + host->mrq->cmd->error = MMC_ERR_NONE; + host->mrq->data->error = MMC_ERR_BADCRC; + goto transfer_closed; + } + + if(sdi_dsta & S3C2410_SDIDSTA_CRCFAIL) { + DBG(PFX "DATA CRC FAILED %u\n", sdi_csta); + host->mrq->cmd->error = MMC_ERR_NONE; + host->mrq->data->error = MMC_ERR_BADCRC; + goto transfer_closed; + } + + if(sdi_dsta & S3C2410_SDIDSTA_DATATIMEOUT) { + host->mrq->cmd->error = MMC_ERR_NONE; + host->mrq->data->error = MMC_ERR_TIMEOUT; + goto transfer_closed; + } + + if(sdi_dsta & S3C2410_SDIDSTA_XFERFINISH) { + if(host->complete_what == COMPLETION_XFERFINISH) { + host->mrq->cmd->error = MMC_ERR_NONE; + host->mrq->data->error = MMC_ERR_NONE; + goto transfer_closed; + } + + if(host->complete_what == COMPLETION_XFERFINISH_RSPFIN) { + host->mrq->data->error = MMC_ERR_NONE; + host->complete_what = COMPLETION_RSPFIN; + } + + sdi_dclear |= S3C2410_SDIDSTA_XFERFINISH; + } + + writel(sdi_cclear, host->base + S3C2410_SDICMDSTAT); + writel(sdi_dclear, host->base + S3C2410_SDIDSTA); + + spin_unlock_irqrestore( &host->complete_lock, iflags); + DBG(PFX "IRQ still waiting.\n"); + return IRQ_HANDLED; + + +transfer_closed: + writel(sdi_cclear, host->base + S3C2410_SDICMDSTAT); + writel(sdi_dclear, host->base + S3C2410_SDIDSTA); + host->complete_what = COMPLETION_NONE; + complete(&host->complete_request); + writel(0, host->base + S3C2410_SDIIMSK); + spin_unlock_irqrestore( &host->complete_lock, iflags); + DBG(PFX "IRQ transfer closed.\n"); + return IRQ_HANDLED; + +clear_imask: + writel(0, host->base + S3C2410_SDIIMSK); + spin_unlock_irqrestore( &host->complete_lock, iflags); + DBG(PFX "IRQ clear imask.\n"); + return IRQ_HANDLED; + +} + + +/* + * ISR for the CardDetect Pin +*/ + +static irqreturn_t s3c2410sdi_irq_cd(int irq, void *dev_id, struct pt_regs *regs) +{ + struct s3c2410sdi_host *host = (struct s3c2410sdi_host *)dev_id; + mmc_detect_change(host->mmc, S3C2410SDI_CDLATENCY); + + return IRQ_HANDLED; +} + + + +static void s3c2410sdi_dma_done_callback(s3c2410_dma_chan_t *dma_ch, void *buf_id, + int size, s3c2410_dma_buffresult_t result) +{ unsigned long iflags; + u32 sdi_csta, sdi_dsta,sdi_dcnt; + struct s3c2410sdi_host *host = (struct s3c2410sdi_host *)buf_id; + + sdi_csta = readl(host->base + S3C2410_SDICMDSTAT); + sdi_dsta = readl(host->base + S3C2410_SDIDSTA); + sdi_dcnt = readl(host->base + S3C2410_SDIDCNT); + + DBG(PFX "DMAD csta=0x%08x dsta=0x%08x dcnt:0x%08x result:0x%08x\n", sdi_csta, sdi_dsta, sdi_dcnt, result); + + spin_lock_irqsave( &host->complete_lock, iflags); + + if(!host->mrq) goto out; + if(!host->mrq->data) goto out; + + + sdi_csta = readl(host->base + S3C2410_SDICMDSTAT); + sdi_dsta = readl(host->base + S3C2410_SDIDSTA); + sdi_dcnt = readl(host->base + S3C2410_SDIDCNT); + + if( result!=S3C2410_RES_OK ) { + goto fail_request; + } + + + if(host->mrq->data->flags & MMC_DATA_READ) { + if( sdi_dcnt>0 ) { + goto fail_request; + } + } + +out: + complete(&host->complete_dma); + spin_unlock_irqrestore( &host->complete_lock, iflags); + return; + + +fail_request: + host->mrq->data->error = MMC_ERR_FAILED; + host->complete_what = COMPLETION_NONE; + complete(&host->complete_request); + writel(0, host->base + S3C2410_SDIIMSK); + goto out; + +} + + +static void s3c2410sdi_dma_setup(struct s3c2410sdi_host *host, s3c2410_dmasrc_t source) { + + s3c2410_dma_devconfig(host->dma, source, 3, host->mem->start + S3C2410_SDIDATA); + s3c2410_dma_config(host->dma, 4, (1<<23) | (2<<24)); + s3c2410_dma_set_buffdone_fn(host->dma, s3c2410sdi_dma_done_callback); + s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART); +} + +static void s3c2410sdi_request(struct mmc_host *mmc, struct mmc_request *mrq) { + struct s3c2410sdi_host *host = mmc_priv(mmc); + struct device *dev = mmc_dev(host->mmc); + struct platform_device *pdev = to_platform_device(dev); + u32 sdi_carg, sdi_ccon, sdi_timer; + u32 sdi_bsize, sdi_dcon, sdi_imsk; + int dma_len = 0; + + DBG(KERN_DEBUG PFX "request: [CMD] opcode:0x%02x arg:0x%08x flags:%x retries:%u\n", + mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags, mrq->cmd->retries); + DBG(PFX "request : %s mode\n",mmc->mode == MMC_MODE_MMC ? "mmc" : "sd"); + + + sdi_ccon = mrq->cmd->opcode & S3C2410_SDICMDCON_INDEX; + sdi_ccon|= S3C2410_SDICMDCON_SENDERHOST; + sdi_ccon|= S3C2410_SDICMDCON_CMDSTART; + + sdi_carg = mrq->cmd->arg; + + sdi_timer= 0xFFFF; + + sdi_bsize= 0; + sdi_dcon = 0; + sdi_imsk = 0; + + /* enable interrupts for transmission errors */ + sdi_imsk |= S3C2410_SDIIMSK_RESPONSEND; + sdi_imsk |= S3C2410_SDIIMSK_CRCSTATUS; + + host->complete_what = COMPLETION_CMDSENT; + + if (mrq->cmd->flags & MMC_RSP_PRESENT) { + host->complete_what = COMPLETION_RSPFIN; + + sdi_ccon |= S3C2410_SDICMDCON_WAITRSP; + sdi_imsk |= S3C2410_SDIIMSK_CMDTIMEOUT; + + } else { + /* We need the CMDSENT-Interrupt only if we want are not waiting + * for a response + */ + sdi_imsk |= S3C2410_SDIIMSK_CMDSENT; + } + + if(mrq->cmd->flags & MMC_RSP_136) { + sdi_ccon|= S3C2410_SDICMDCON_LONGRSP; + } + + if(mrq->cmd->flags & MMC_RSP_CRC) { + sdi_imsk |= S3C2410_SDIIMSK_RESPONSECRC; + } + + + if (mrq->data) { + host->complete_what = COMPLETION_XFERFINISH_RSPFIN; + + sdi_bsize = (1 << mrq->data->blksz_bits); + host->size = mrq->data->blocks << mrq->data->blksz_bits; + + sdi_dcon = (mrq->data->blocks & S3C2410_SDIDCON_BLKNUM_MASK); + sdi_dcon |= S3C2410_SDIDCON_DMAEN; + + sdi_imsk |= S3C2410_SDIIMSK_FIFOFAIL; + sdi_imsk |= S3C2410_SDIIMSK_DATACRC; + sdi_imsk |= S3C2410_SDIIMSK_DATATIMEOUT; + sdi_imsk |= S3C2410_SDIIMSK_DATAFINISH; + sdi_imsk |= 0xFFFFFFE0; + + DBG(PFX "request: [DAT] bsize:%u blocks:%u bytes:%u\n", + sdi_bsize, mrq->data->blocks, mrq->data->blocks * sdi_bsize); + + if (host->bus_width == MMC_BUS_WIDTH_4) { + sdi_dcon |= S3C2410_SDIDCON_WIDEBUS; + } + + if(!(mrq->data->flags & MMC_DATA_STREAM)) { + sdi_dcon |= S3C2410_SDIDCON_BLOCKMODE; + } + + if(mrq->data->flags & MMC_DATA_WRITE) { + sdi_dcon |= S3C2410_SDIDCON_TXAFTERRESP; + sdi_dcon |= S3C2410_SDIDCON_XFER_TXSTART; + } + + if(mrq->data->flags & MMC_DATA_READ) { + sdi_dcon |= S3C2410_SDIDCON_RXAFTERCMD; + sdi_dcon |= S3C2410_SDIDCON_XFER_RXSTART; + } + + s3c2410sdi_dma_setup(host, mrq->data->flags & MMC_DATA_WRITE ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW); + + /* see DMA-API.txt */ + dma_len = dma_map_sg(&pdev->dev, mrq->data->sg, \ + mrq->data->sg_len, \ + mrq->data->flags & MMC_DATA_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + + /* start DMA */ + s3c2410_dma_enqueue(host->dma, (void *) host, + sg_dma_address(&mrq->data->sg[0]), + (mrq->data->blocks << mrq->data->blksz_bits) ); + } + + host->mrq = mrq; + + init_completion(&host->complete_request); + init_completion(&host->complete_dma); + + /* Clear command and data status registers */ + writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT); + writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA); + + /* Setup SDI controller */ + writel(sdi_bsize,host->base + S3C2410_SDIBSIZE); + writel(sdi_timer,host->base + S3C2410_SDITIMER); + writel(sdi_imsk,host->base + S3C2410_SDIIMSK); + + /* Setup SDI command argument and data control */ + writel(sdi_carg, host->base + S3C2410_SDICMDARG); + writel(sdi_dcon, host->base + S3C2410_SDIDCON); + + /* This initiates transfer */ + writel(sdi_ccon, host->base + S3C2410_SDICMDCON); + + /* Wait for transfer to complete */ + wait_for_completion(&host->complete_request); + DBG(PFX "[CMD] request complete.\n"); + if(mrq->data) { + wait_for_completion(&host->complete_dma); + DBG(PFX "[DAT] DMA complete.\n"); + } + + /* Cleanup controller */ + writel(0, host->base + S3C2410_SDICMDARG); + writel(0, host->base + S3C2410_SDIDCON); + writel(0, host->base + S3C2410_SDICMDCON); + writel(0, host->base + S3C2410_SDIIMSK); + + /* Read response */ + mrq->cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0); + mrq->cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1); + mrq->cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2); + mrq->cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3); + + host->mrq = NULL; + + DBG(PFX "request done.\n"); + + /* If we have no data transfer we are finished here */ + if (!mrq->data) goto request_done; + + dma_unmap_sg(&pdev->dev, mrq->data->sg, dma_len, \ + mrq->data->flags & MMC_DATA_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + + /* Calulate the amout of bytes transfer, but only if there was + * no error + */ + if(mrq->data->error == MMC_ERR_NONE) { + mrq->data->bytes_xfered = (mrq->data->blocks << mrq->data->blksz_bits); + } else { + mrq->data->bytes_xfered = 0; + } + + /* If we had an error while transfering data we flush the + * DMA channel to clear out any garbage + */ + if(mrq->data->error != MMC_ERR_NONE) { + s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); + DBG(PFX "flushing DMA.\n"); + } + + if(mrq->data->stop) mmc_wait_for_cmd(mmc, mrq->data->stop, 3); + +request_done: + + mrq->done(mrq); +} + +static void s3c2410sdi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { + struct s3c2410sdi_host *host = mmc_priv(mmc); + u32 sdi_psc, sdi_con; + + /* Set power */ + sdi_con = readl(host->base + S3C2410_SDICON); + switch(ios->power_mode) { + case MMC_POWER_ON: + case MMC_POWER_UP: + DBG(PFX "power on\n"); + s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK); + s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD); + s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0); + s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1); + s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2); + s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3); + + if (host->pdata->set_power) + (host->pdata->set_power)(1); + + sdi_con|= S3C2410_SDICON_FIFORESET; + break; + + case MMC_POWER_OFF: + default: + if (host->pdata->set_power) + (host->pdata->set_power)(0); + break; + } + + /* Set clock */ + for(sdi_psc=0;sdi_psc<255;sdi_psc++) { + if( (clk_get_rate(host->clk) / (2*(sdi_psc+1))) <= ios->clock) break; + } + + if(sdi_psc > 255) sdi_psc = 255; + writel(sdi_psc, host->base + S3C2410_SDIPRE); + + /* Set CLOCK_ENABLE */ + if(ios->clock) sdi_con |= S3C2410_SDICON_CLOCKTYPE; + else sdi_con &=~S3C2410_SDICON_CLOCKTYPE; + + writel(sdi_con, host->base + S3C2410_SDICON); + + host->bus_width = ios->bus_width; + +} + +static struct mmc_host_ops s3c2410sdi_ops = { + .request = s3c2410sdi_request, + .set_ios = s3c2410sdi_set_ios, +}; + +static void s3c2410_mmc_def_setpower(unsigned int to) +{ + s3c2410_gpio_cfgpin(S3C2410_GPA17, S3C2410_GPIO_OUTPUT); + s3c2410_gpio_setpin(S3C2410_GPA17, to); +} + +static struct s3c24xx_mmc_platdata s3c2410_mmc_defplat = { + .gpio_detect = S3C2410_GPF2, + .set_power = s3c2410_mmc_def_setpower, + .f_max = 3000000, + .ocr_avail = MMC_VDD_32_33, +}; + +static int s3c2410sdi_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + s3c24xx_mmc_pdata_t *pdata; + struct s3c2410sdi_host *host; + + + int ret; + + mmc = mmc_alloc_host(sizeof(struct s3c2410sdi_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto probe_out; + } + + host = mmc_priv(mmc); + + spin_lock_init( &host->complete_lock ); + host->complete_what = COMPLETION_NONE; + host->mmc = mmc; + host->dma = S3C2410SDI_DMA; + + pdata = pdev->dev.platform_data; + if (!pdata) { + pdev->dev.platform_data = &s3c2410_mmc_defplat; + pdata = &s3c2410_mmc_defplat; + } + + host->pdata = pdata; + + host->irq_cd = s3c2410_gpio_getirq(pdata->gpio_detect); + s3c2410_gpio_cfgpin(pdata->gpio_detect, S3C2410_GPIO_IRQ); + + host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!host->mem) { + printk(KERN_ERR PFX "failed to get io memory region resouce.\n"); + ret = -ENOENT; + goto probe_free_host; + } + + host->mem = request_mem_region(host->mem->start, + RESSIZE(host->mem), pdev->name); + + if (!host->mem) { + printk(KERN_ERR PFX "failed to request io memory region.\n"); + ret = -ENOENT; + goto probe_free_host; + } + + host->base = ioremap(host->mem->start, RESSIZE(host->mem)); + if (host->base == 0) { + printk(KERN_ERR PFX "failed to ioremap() io memory region.\n"); + ret = -EINVAL; + goto probe_free_mem_region; + } + + host->irq = platform_get_irq(pdev, 0); + if (host->irq == 0) { + printk(KERN_ERR PFX "failed to get interrupt resouce.\n"); + ret = -EINVAL; + goto probe_iounmap; + } + + if(request_irq(host->irq, s3c2410sdi_irq, 0, DRIVER_NAME, host)) { + printk(KERN_ERR PFX "failed to request sdi interrupt.\n"); + ret = -ENOENT; + goto probe_iounmap; + } + + set_irq_type(host->irq_cd, IRQT_BOTHEDGE); + if(request_irq(host->irq_cd, s3c2410sdi_irq_cd, 0, DRIVER_NAME, host)) { + printk(KERN_ERR PFX "failed to request card detect interrupt.\n" ); + ret = -ENOENT; + goto probe_free_irq; + } + + if(s3c2410_dma_request(S3C2410SDI_DMA, &s3c2410sdi_dma_client, NULL)) { + printk(KERN_ERR PFX "unable to get DMA channel.\n" ); + ret = -EBUSY; + goto probe_free_irq_cd; + } + + host->clk = clk_get(&pdev->dev, "sdi"); + if (IS_ERR(host->clk)) { + printk(KERN_ERR PFX "failed to find clock source.\n"); + ret = PTR_ERR(host->clk); + host->clk = NULL; + goto probe_free_host; + } + + if((ret = clk_enable(host->clk))) { + printk(KERN_ERR PFX "failed to enable clock source.\n"); + goto clk_unuse; + } + + + mmc->ops = &s3c2410sdi_ops; + mmc->ocr_avail = pdata->ocr_avail; + mmc->f_min = clk_get_rate(host->clk) / 512; + mmc->f_max = clk_get_rate(host->clk) / 2; + mmc->caps = MMC_CAP_4_BIT_DATA; + + if(pdata->f_max && (mmc->f_max>pdata->f_max)) + mmc->f_max = pdata->f_max; + + /* + * Since we only have a 16-bit data length register, we must + * ensure that we don't exceed 2^16-1 bytes in a single request. + * Choose 64 (512-byte) sectors as the limit. + */ + mmc->max_sectors = 64; + + /* + * Set the maximum segment size. Since we aren't doing DMA + * (yet) we are only limited by the data length register. + */ + + mmc->max_seg_size = mmc->max_sectors << 9; + printk(KERN_INFO PFX "probe: mapped sdi_base=%p irq=%u irq_cd=%u \n", + host->base, host->irq, host->irq_cd); + + if((ret = mmc_add_host(mmc))) { + printk(KERN_ERR PFX "failed to add mmc host.\n"); + goto clk_disable; + } + + platform_set_drvdata(pdev, mmc); + + mmc_detect_change(mmc, 0); + + printk(KERN_INFO PFX "initialisation done.\n"); + return 0; + + clk_disable: + clk_disable(host->clk); + + clk_unuse: + clk_put(host->clk); + + probe_free_irq_cd: + free_irq(host->irq_cd, host); + + probe_free_irq: + free_irq(host->irq, host); + + probe_iounmap: + iounmap(host->base); + + probe_free_mem_region: + release_mem_region(host->mem->start, RESSIZE(host->mem)); + + probe_free_host: + mmc_free_host(mmc); + probe_out: + return ret; +} + +static int s3c2410sdi_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct s3c2410sdi_host *host = mmc_priv(mmc); + + mmc_remove_host(mmc); + clk_disable(host->clk); + clk_put(host->clk); + free_irq(host->irq_cd, host); + free_irq(host->irq, host); + iounmap(host->base); + release_mem_region(host->mem->start, RESSIZE(host->mem)); + mmc_free_host(mmc); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c2410mci_suspend(struct platform_device *dev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + struct s3c2410sdi_host *host; + int ret = 0; + + if (mmc) { + host = mmc_priv(mmc); + + ret = mmc_suspend_host(mmc, state); + + clk_disable(host->clk); + + disable_irq(host->irq_cd); + disable_irq(host->irq); + } + + return ret; +} + +static int s3c2410mci_resume(struct platform_device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + struct s3c2410sdi_host *host; + int ret = 0; + + if (mmc) { + host = mmc_priv(mmc); + + enable_irq(host->irq_cd); + enable_irq(host->irq); + + clk_enable(host->clk); + + ret = mmc_resume_host(mmc); + } + + return ret; +} +#else +#define s3c2410mci_suspend NULL +#define s3c2410mci_resume NULL +#endif + +static struct platform_driver s3c2410sdi_driver = +{ + .driver = { + .name = "s3c2410-sdi", + .owner = THIS_MODULE, + }, + .probe = s3c2410sdi_probe, + .remove = s3c2410sdi_remove, + .suspend = s3c2410mci_suspend, + .resume = s3c2410mci_resume, +}; + +static int __init s3c2410sdi_init(void) +{ + return platform_driver_register(&s3c2410sdi_driver); +} + +static void __exit s3c2410sdi_exit(void) +{ + platform_driver_unregister(&s3c2410sdi_driver); +} + +module_init(s3c2410sdi_init); +module_exit(s3c2410sdi_exit); + +MODULE_DESCRIPTION("Samsung S3C2410 Multimedia Card Interface driver"); +MODULE_LICENSE("GPL"); Index: linux-2.6.17.7-new/drivers/mmc/s3c2410mci.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17.7-new/drivers/mmc/s3c2410mci.h 2006-08-07 09:33:51.000000000 +0200 @@ -0,0 +1,55 @@ +/* + * linux/drivers/mmc/s3c2410mci.h - Samsung S3C2410 SDI Interface driver + * + * Copyright (C) 2004 Thomas Kleffel, All Rights Reserved. + * + * 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 clk; + +#define S3C2410SDI_DMA 0 + +#define S3C2410SDI_CDLATENCY 50 + +enum s3c2410sdi_waitfor { + COMPLETION_NONE, + COMPLETION_CMDSENT, + COMPLETION_RSPFIN, + COMPLETION_XFERFINISH, + COMPLETION_XFERFINISH_RSPFIN, +}; + +typedef struct s3c24xx_mmc_platdata s3c24xx_mmc_pdata_t; + +struct s3c2410sdi_host { + struct mmc_host *mmc; + s3c24xx_mmc_pdata_t *pdata; + + struct resource *mem; + struct clk *clk; + void __iomem *base; + int irq; + int irq_cd; + int dma; + + struct scatterlist* cur_sg; /* Current SG entry */ + unsigned int num_sg; /* Number of entries left */ + void* mapped_sg; /* vaddr of mapped sg */ + + unsigned int offset; /* Offset into current entry */ + unsigned int remain; /* Data left in curren entry */ + + int size; /* Total size of transfer */ + + struct mmc_request *mrq; + + unsigned char bus_width; /* Current bus width */ + + spinlock_t complete_lock; + struct completion complete_request; + struct completion complete_dma; + enum s3c2410sdi_waitfor complete_what; +}; Index: linux-2.6.17.7-new/include/asm-arm/arch-s3c2410/mmc.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17.7-new/include/asm-arm/arch-s3c2410/mmc.h 2006-08-07 09:33:51.000000000 +0200 @@ -0,0 +1,32 @@ +/* linux/include/asm-arm/arch-s3c2410/mmc.h + * + * (c) 2004-2005 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * Ben Dooks + * + * S3C24XX - MMC/SD platform data + * + * 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. + * + * Changelog: + * 26-Oct-2005 BJD Created file +*/ + +#ifndef __ASM_ARCH_MMC_H +#define __ASM_ARCH_MMC_H __FILE__ + +struct s3c24xx_mmc_platdata { + unsigned int gpio_detect; + unsigned int gpio_wprotect; + unsigned int detect_polarity; + unsigned int wprotect_polarity; + + unsigned long f_max; + unsigned long ocr_avail; + + void (*set_power)(unsigned int to); +}; + +#endif /* __ASM_ARCH_MMC_H */ Index: linux-2.6.17.7-new/include/asm-arm/arch-s3c2410/regs-sdi.h =================================================================== --- linux-2.6.17.7-new.orig/include/asm-arm/arch-s3c2410/regs-sdi.h 2006-08-07 09:32:34.000000000 +0200 +++ linux-2.6.17.7-new/include/asm-arm/arch-s3c2410/regs-sdi.h 2006-08-07 09:33:51.000000000 +0200 @@ -47,7 +47,8 @@ #define S3C2410_SDICMDCON_LONGRSP (1<<10) #define S3C2410_SDICMDCON_WAITRSP (1<<9) #define S3C2410_SDICMDCON_CMDSTART (1<<8) -#define S3C2410_SDICMDCON_INDEX (0xff) +#define S3C2410_SDICMDCON_SENDERHOST (1<<6) +#define S3C2410_SDICMDCON_INDEX (0x3f) #define S3C2410_SDICMDSTAT_CRCFAIL (1<<12) #define S3C2410_SDICMDSTAT_CMDSENT (1<<11) @@ -73,6 +74,7 @@ #define S3C2410_SDIDCON_XFER_RXSTART (2<<12) #define S3C2410_SDIDCON_XFER_TXSTART (3<<12) +#define S3C2410_SDIDCON_BLKNUM_MASK (0xFFF) #define S3C2410_SDIDCNT_BLKNUM_SHIFT (12) #define S3C2410_SDIDSTA_RDYWAITREQ (1<<10) @@ -115,4 +117,14 @@ #define S3C2410_SDIIMSK_RXFIFOFULL (1<<1) #define S3C2410_SDIIMSK_RXFIFOHALF (1<<0) +#define S3C2410_SDICMDCON_ABORT (1<<12) +#define S3C2410_SDICMDCON_WITHDATA (1<<11) +#define S3C2410_SDICMDCON_LONGRSP (1<<10) +#define S3C2410_SDICMDCON_WAITRSP (1<<9) +#define S3C2410_SDICMDCON_CMDSTART (1<<8) +#define S3C2410_SDICMDCON_SENDERHOST (1<<6) + +#define S3C2410_SDIDCON_BLKNUM_MASK (0xFFF) +#define S3C2410_SDIDCNT_BLKNUM_SHIFT (12) + #endif /* __ASM_ARM_REGS_SDI */ Index: linux-2.6.17.7-new/arch/arm/mach-s3c2410/mach-qt2410.c =================================================================== --- linux-2.6.17.7-new.orig/arch/arm/mach-s3c2410/mach-qt2410.c 2006-08-07 09:33:27.000000000 +0200 +++ linux-2.6.17.7-new/arch/arm/mach-s3c2410/mach-qt2410.c 2006-08-07 09:34:20.000000000 +0200 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -45,7 +46,9 @@ #include #include +#include #include +#include #include #include "devs.h" @@ -236,6 +239,13 @@ .sets = qt2410_nand_sets, }; +static struct s3c24xx_mmc_platdata qt2410_mmc_cfg = { + .gpio_detect = S3C2410_GPG10, + .gpio_wprotect = S3C2410_GPH8, + .set_power = NULL, + .ocr_avail = MMC_VDD_32_33, +}; + static void __init qt2410_map_io(void) { s3c24xx_init_io(qt2410_iodesc, ARRAY_SIZE(qt2410_iodesc)); @@ -259,6 +269,7 @@ s3c2410_gpio_setpin(S3C2410_GPF7, 1); s3c_device_nand.dev.platform_data = &qt2410_nand_info; + s3c_device_sdi.dev.platform_data = &qt2410_mmc_cfg; s3c24xx_fb_set_platdata(&qt2410_lcd_cfg); s3c2410_pm_init();