?? s3cmci.c
字號:
//if(cmd->flags & MMC_RSP_MAYFAIL) debug_as_failure = 0;#ifdef CONFIG_MMC_DEBUG dbg_dumpcmd(host, cmd, debug_as_failure);#endif //Cleanup controller writel(0, host->base + S3C2410_SDICMDARG); writel(0, host->base + S3C2410_SDIDCON); writel(0, host->base + S3C2410_SDICMDCON); writel(0, host->base + host->sdiimsk); if (cmd->data && cmd->error) cmd->data->error = cmd->error; if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) { host->cmd_is_stop = 1; s3cmci_send_request(host->mmc); return; } // If we have no data transfer we are finished here if (!mrq->data) goto request_done; // 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); } else { mrq->data->bytes_xfered = 0; } // If we had an error while transfering data we flush the // DMA channel and the fifo to clear out any garbage if (mrq->data->error != MMC_ERR_NONE) { if (host->dodma) s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); //reset fifo mci_con = readl(host->base + S3C2410_SDICON); mci_con|= S3C2410_SDICON_FIFORESET; writel(mci_con, host->base + S3C2410_SDICON); }request_done: host->complete_what = COMPLETION_NONE; host->mrq = NULL; mmc_request_done(host->mmc, mrq);}void s3cmci_dma_setup(struct s3cmci_host *host, enum s3c2410_dmasrc source){ static int setup_ok = 0; static enum s3c2410_dmasrc last_source = -1; if (last_source == source) return; last_source = source; s3c2410_dma_devconfig(host->dma, source, 3, host->mem->start + host->sdidata); if (!setup_ok) { s3c2410_dma_config(host->dma, 4, (S3C2410_DCON_HWTRIG | S3C2410_DCON_CH0_SDI)); s3c2410_dma_set_buffdone_fn(host->dma, s3cmci_dma_done_callback); s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART); setup_ok = 1; }}static void s3cmci_send_command(struct s3cmci_host *host, struct mmc_command *cmd){ u32 ccon, imsk; imsk = S3C2410_SDIIMSK_CMDTIMEOUT | S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT | S3C2410_SDIIMSK_RESPONSECRC; enable_imask(host, imsk); if (cmd->data) { host->complete_what = COMPLETION_XFERFINISH_RSPFIN; } else if (cmd->flags & MMC_RSP_PRESENT) { host->complete_what = COMPLETION_RSPFIN; } else { host->complete_what = COMPLETION_CMDSENT; } writel(cmd->arg, host->base + S3C2410_SDICMDARG); ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX; ccon|= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART; if (cmd->flags & MMC_RSP_PRESENT) ccon |= S3C2410_SDICMDCON_WAITRSP; if (cmd->flags & MMC_RSP_136) ccon|= S3C2410_SDICMDCON_LONGRSP; writel(ccon, host->base + S3C2410_SDICMDCON);}static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data){ u32 dcon, imsk, stoptries=3; /* write DCON register */ if (!data) { writel(0, host->base + S3C2410_SDIDCON); return 0; } while(readl(host->base + S3C2410_SDIDSTA) & (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) { dbg(host, dbg_err, "mci_setup_data() transfer stillin progress.\n"); writel(0, host->base + S3C2410_SDIDCON); s3cmci_reset(host); if (0 == (stoptries--)) {#ifdef CONFIG_MMC_DEBUG dbg_dumpregs(host, "DRF");#endif return -EINVAL; } } dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK; if (host->dodma) { dcon |= S3C2410_SDIDCON_DMAEN; } if (host->bus_width == MMC_BUS_WIDTH_4) { dcon |= S3C2410_SDIDCON_WIDEBUS; } if (!(data->flags & MMC_DATA_STREAM)) { dcon |= S3C2410_SDIDCON_BLOCKMODE; } if (data->flags & MMC_DATA_WRITE) { dcon |= S3C2410_SDIDCON_TXAFTERRESP; dcon |= S3C2410_SDIDCON_XFER_TXSTART; } if (data->flags & MMC_DATA_READ) { dcon |= S3C2410_SDIDCON_RXAFTERCMD; dcon |= S3C2410_SDIDCON_XFER_RXSTART; } writel(dcon, host->base + S3C2410_SDIDCON); /* write BSIZE register */ writel(data->blksz, host->base + S3C2410_SDIBSIZE); /* add to IMASK register */ imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC | S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH; enable_imask(host, imsk); /* write TIMER register */ writel(0x0000FFFF, host->base + S3C2410_SDITIMER); //FIX: set slow clock to prevent timeouts on read if (data->flags & MMC_DATA_READ) { writel(0xFF, host->base + S3C2410_SDIPRE); } //debug_dump_registers(host, "Data setup:"); return 0;}static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data){ int rw = (data->flags & MMC_DATA_WRITE)?1:0; if (rw != ((data->flags & MMC_DATA_READ)?0:1)) return -EINVAL; host->pio_sgptr = 0; host->pio_words = 0; host->pio_count = 0; host->pio_active = rw?XFER_WRITE:XFER_READ; if (rw) { do_pio_write(host); enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); } else { enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST); } return 0;}static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data){ int dma_len, i; int rw = (data->flags & MMC_DATA_WRITE)?1:0; if (rw != ((data->flags & MMC_DATA_READ)?0:1)) return -EINVAL; s3cmci_dma_setup(host, rw?S3C2410_DMASRC_MEM:S3C2410_DMASRC_HW); s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, (rw)?DMA_TO_DEVICE:DMA_FROM_DEVICE); if (dma_len == 0) return -ENOMEM; host->dma_complete = 0; host->dmatogo = dma_len; for (i = 0; i < dma_len; i++) { int res; dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i, sg_dma_address(&data->sg[i]), sg_dma_len(&data->sg[i])); res = s3c2410_dma_enqueue(host->dma, (void *) host, sg_dma_address(&data->sg[i]), sg_dma_len(&data->sg[i])); if (res) { s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); return -EBUSY; } } s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START); return 0;}static void s3cmci_send_request(struct mmc_host *mmc){ struct s3cmci_host *host = mmc_priv(mmc); struct mmc_request *mrq = host->mrq; struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd; host->ccnt++; if (cmd->opcode == SD_SWITCH && cmd->arg == 0x80fffff1) { struct scatterlist *sg = cmd->data->sg; memset(page_address(sg->page) + sg->offset, 0, sg->length); cmd->error = MMC_ERR_NONE; cmd->data->error = MMC_ERR_NONE; dbg(host, dbg_conf, "suppress SD_SWITCH @%ukHz\n", mmc->f_max/1000); mmc_request_done(mmc, mrq); return; }#ifdef CONFIG_MMC_DEBUG prepare_dbgmsg(host, cmd, host->cmd_is_stop);#endif //Clear command, data and fifo status registers //Fifo clear only necessary on 2440, but doesn't hurt on 2410 writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT); writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA); writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA); if (cmd->data) { int res; res = s3cmci_setup_data(host, cmd->data); host->dcnt++; if (res) { cmd->error = MMC_ERR_DMA; cmd->data->error = MMC_ERR_DMA; mmc_request_done(mmc, mrq); return; } if (host->dodma) { res = s3cmci_prepare_dma(host, cmd->data); } else { res = s3cmci_prepare_pio(host, cmd->data); } if (res) { cmd->error = MMC_ERR_DMA; cmd->data->error = MMC_ERR_DMA; mmc_request_done(mmc, mrq); return; } } s3cmci_send_command(host, cmd); enable_irq(host->irq);}static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq){ struct s3cmci_host *host = mmc_priv(mmc); host->cmd_is_stop = 0; host->mrq = mrq; s3cmci_send_request(mmc);}static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){ struct s3cmci_host *host = mmc_priv(mmc); u32 mci_psc, mci_con; //Set power mci_con = readl(host->base + S3C2410_SDICON); switch(ios->power_mode) { case MMC_POWER_ON: case MMC_POWER_UP: 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 (ios->clock == 0) mci_con|=S3C2410_SDICON_FIFORESET; break; case MMC_POWER_OFF: default: s3c2410_gpio_setpin(S3C2410_GPE5, 0); s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP); break; } //Set clock for (mci_psc=0; mci_psc<255; mci_psc++) { host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1)); if (host->real_rate <= ios->clock) break; } if(mci_psc > 255) mci_psc = 255; host->prescaler = mci_psc; writel(host->prescaler, host->base + S3C2410_SDIPRE); //If requested clock is 0, real_rate will be 0, too if (ios->clock == 0) host->real_rate = 0; //Set CLOCK_ENABLE if (ios->clock) mci_con |= S3C2410_SDICON_CLOCKTYPE; else mci_con &=~S3C2410_SDICON_CLOCKTYPE; writel(mci_con, host->base + S3C2410_SDICON); if ((ios->power_mode==MMC_POWER_ON) || (ios->power_mode==MMC_POWER_UP)) { dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n", host->real_rate/1000, ios->clock/1000); } else { dbg(host, dbg_conf, "powered down.\n"); } host->bus_width = ios->bus_width;}static void s3cmci_reset(struct s3cmci_host *host){ u32 con = readl(host->base + S3C2410_SDICON); con |= S3C2440_SDICON_SDRESET; writel(con, host->base + S3C2410_SDICON);}static struct mmc_host_ops s3cmci_ops = { .request = s3cmci_request, .set_ios = s3cmci_set_ios,};static int s3cmci_probe(struct platform_device *pdev){ struct mmc_host *mmc; struct s3cmci_host *host; int ret; mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; goto probe_out; } host = mmc_priv(mmc); host->mmc = mmc; host->pdev = pdev; spin_lock_init(&host->complete_lock); tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host); host->sdiimsk = S3C2410_SDIIMSK; host->sdidata = S3C2410_SDIDATA; host->clk_div = 2; host->dodma = 0; host->complete_what = COMPLETION_NONE; host->pio_active = XFER_NONE; host->dma = S3CMCI_DMA; host->irq_cd = IRQ_EINT2; host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!host->mem) { dev_err(&pdev->dev, "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) { dev_err(&pdev->dev, "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) { dev_err(&pdev->dev, "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) { dev_err(&pdev->dev, "failed to get interrupt resouce.\n"); ret = -EINVAL; goto probe_iounmap; } if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) { dev_err(&pdev->dev, "failed to request mci interrupt.\n"); ret = -ENOENT; goto probe_iounmap; } disable_irq(host->irq); s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2); if (request_irq(host->irq_cd, s3cmci_irq_cd, \ SA_TRIGGER_RISING | SA_TRIGGER_FALLING, DRIVER_NAME, host)) { dev_err(&pdev->dev, "failed to request card detect interrupt.\n"); ret = -ENOENT; goto probe_free_irq; } if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) { dev_err(&pdev->dev, "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)) { dev_err(&pdev->dev, "failed to find clock source.\n"); ret = PTR_ERR(host->clk); host->clk = NULL; goto probe_free_dma; } if ((ret = clk_enable(host->clk))) { dev_err(&pdev->dev, "failed to enable clock source.\n"); goto clk_free; } host->clk_rate = clk_get_rate(host->clk); mmc->ops = &s3cmci_ops; mmc->ocr_avail = MMC_VDD_32_33; mmc->caps = MMC_CAP_4_BIT_DATA; mmc->f_min = host->clk_rate / (host->clk_div * 256); mmc->f_max = host->clk_rate / host->clk_div; mmc->max_sectors = 4095; mmc->max_seg_size = mmc->max_sectors << 9; mmc->max_phys_segs = 128; mmc->max_hw_segs = 128; if ((ret = mmc_add_host(mmc))) { dev_err(&pdev->dev, "failed to add mmc host.\n"); goto free_dmabuf; } platform_set_drvdata(pdev, mmc); dev_info(&pdev->dev,"initialisation done.\n"); return 0; free_dmabuf: clk_disable(host->clk); clk_free: clk_put(host->clk); probe_free_dma: s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client); 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 s3cmci_remove(struct platform_device *pdev){ struct mmc_host *mmc = platform_get_drvdata(pdev); struct s3cmci_host *host = mmc_priv(mmc); mmc_remove_host(mmc); clk_disable(host->clk); clk_put(host->clk); s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client); 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_PMstatic int s3cmci_suspend(struct platform_device *dev, pm_message_t state){ struct mmc_host *mmc = platform_get_drvdata(dev); return mmc_suspend_host(mmc, state);}static int s3cmci_resume(struct platform_device *dev){ struct mmc_host *mmc = platform_get_drvdata(dev); return mmc_resume_host(mmc);}#else /* CONFIG_PM */#define s3cmci_suspend NULL#define s3cmci_resume NULL#endif /* CONFIG_PM */static struct platform_driver s3cmci_driver_2410 ={ .driver.name = "s3c2410-sdi", .probe = s3cmci_probe, .remove = s3cmci_remove, .suspend = s3cmci_suspend, .resume = s3cmci_resume,};static int __init s3cmci_init(void){ platform_driver_register(&s3cmci_driver_2410); return 0;}static void __exit s3cmci_exit(void){ platform_driver_unregister(&s3cmci_driver_2410);}module_init(s3cmci_init);module_exit(s3cmci_exit);MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");MODULE_LICENSE("GPL");MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>");
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -