omap24xx_mmc.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,027 行 · 第 1/2 页
C
1,027 行
if (req->data == NULL) { host->datadir = OMAP_MMC_DATADIR_NONE; OMAP_MMC_WRITE(host->base, BLEN, 0); OMAP_MMC_WRITE(host->base, NBLK, 0); OMAP_MMC_WRITE(host->base, BUF, 0); return; } DBG("MMC%d: Data xfer (%s %s), DTO %d cycles + %d ns, %d blocks of %d bytes\n", host->id, (req->data->flags & MMC_DATA_STREAM) ? "stream" : "block", (req->data->flags & MMC_DATA_WRITE) ? "write" : "read", req->data->timeout_clks, req->data->timeout_ns, req->data->blocks, 1 << req->data->blksz_bits); /* Convert ns to clock cycles by assuming 20MHz frequency * 1 cycle at 20MHz = 500 ns */ timeout = req->data->timeout_clks + req->data->timeout_ns / 500; if (timeout > 0xffff) timeout = 0xffff; OMAP_MMC_WRITE(host->base, DTO, timeout); OMAP_MMC_WRITE(host->base, NBLK, req->data->blocks - 1); OMAP_MMC_WRITE(host->base, BLEN, (1 << req->data->blksz_bits) - 1); host->datadir = (req->data->flags & MMC_DATA_WRITE) ? OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ; if (host->use_dma && mmc_omap_start_dma_transfer(host, req) == 0) { host->buffer = NULL; host->bytesleft = 0; } else { /* Revert to CPU copy */ OMAP_MMC_WRITE(host->base, BUF, 0x1f1f); host->buffer = (u16 *) req->data->req->buffer; host->bytesleft = req->data->blocks * (1 << req->data->blksz_bits); host->dma_ch = -1; }}static inline int is_broken_card(struct mmc_card *card){ int i; struct mmc_cid *c = &card->cid; static const struct broken_card_cid { unsigned int manfid; char prod_name[8]; unsigned char hwrev; unsigned char fwrev; } broken_cards[] = { { 0x00150000, "\x30\x30\x30\x30\x30\x30\x15\x00", 0x06, 0x03},}; for (i = 0; i < sizeof(broken_cards) / sizeof(broken_cards[0]); i++) { const struct broken_card_cid *b = broken_cards + i; if (b->manfid != c->manfid) continue; if (memcmp(b->prod_name, c->prod_name, sizeof(b->prod_name)) != 0) continue; if (b->hwrev != c->hwrev || b->fwrev != c->fwrev) continue; return 1; } return 0;}static void omap24xx_mmc_request(struct mmc_host *mmc, struct mmc_request *req){ struct mmc_omap_host *host = mmc_priv(mmc); WARN_ON(host->mrq != NULL); host->mrq = req; /* Some cards (vendor left unnamed to protect the guilty) seem to * require this delay after power-up. Otherwise we'll get mysterious * data timeouts. */ if (req->cmd->opcode == MMC_SEND_CSD) { struct mmc_card *card; int broken_present = 0; list_for_each_entry(card, &mmc->cards, node) { if (is_broken_card(card)) { broken_present = 1; break; } } if (broken_present) { static int complained = 0; if (!complained) { printk(KERN_WARNING "MMC%d: Broken card workaround enabled\n", host->id); complained = 1; } if (in_interrupt()) { /* This is nasty */ printk(KERN_ERR "Sleeping in IRQ handler, FIXME please!\n"); dump_stack(); mdelay(100); } else { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(100 * HZ / 1000); } } } mmc_omap_prepare_data(host, req); mmc_omap_start_command(host, req->cmd);}/* * Turn the on/off the power to the MMC socket. */static void mmc_omap_power(struct mmc_omap_host *host, int on){ unsigned long reg_val; if (on) { reg_val = OMAP_MMC_READ(host->base, CON); OMAP_MMC_WRITE(host->base, CON, reg_val | (0x1 << 11)); } else { reg_val = OMAP_MMC_READ(host->base, CON); OMAP_MMC_WRITE(host->base, CON, reg_val | (0x0 << 11)); }}/* * PRCM clk setup */static void mmc_clk_setup(int on){#if 0 unsigned long reg_val; if (on) { reg_val = omap_prcmreg_read(PRCM_CLK_EN_PLL); omap_prcmreg_write(reg_val | (0xf << 0), PRCM_CLK_EN_PLL); reg_val = omap_prcmreg_read(PRCM_CLK_SEL1_PLL); omap_prcmreg_write(reg_val | (0 << 3), PRCM_CLK_SEL1_PLL); reg_val = omap_prcmreg_read(PRCM_FCLK_EN1_CORE); omap_prcmreg_write(reg_val | (1 << 26), PRCM_FCLK_EN1_CORE); reg_val = omap_prcmreg_read(PRCM_FCLK_EN2_CORE); omap_prcmreg_write(reg_val | (1 << 26), PRCM_FCLK_EN2_CORE); } else { reg_val = omap_prcmreg_read(PRCM_CLK_EN_PLL); omap_prcmreg_write(reg_val | (0x0 << 0), PRCM_CLK_EN_PLL); reg_val = omap_prcmreg_read(PRCM_CLK_SEL1_PLL); omap_prcmreg_write(reg_val | (0 << 3), PRCM_CLK_SEL1_PLL); reg_val = omap_prcmreg_read(PRCM_FCLK_EN1_CORE); omap_prcmreg_write(reg_val | (0 << 26), PRCM_FCLK_EN1_CORE); reg_val = omap_prcmreg_read(PRCM_FCLK_EN2_CORE); omap_prcmreg_write(reg_val | (0 << 26), PRCM_FCLK_EN2_CORE); }#endif}static void omap24xx_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){ struct mmc_omap_host *host = mmc_priv(mmc); int realclock; int dsor = 0; //host->id, ios->clock, ios->bus_mode, ios->power_mode, //ios->vdd / 100, ios->vdd % 100); DBG("MMC%d: set_ios: clock %dHz busmode %d powermode %d Vdd %d.%02d\n", host->id, ios->clock, ios->bus_mode, ios->power_mode, ios->vdd / 100, ios->vdd % 100); if (ios->power_mode == MMC_POWER_UP && ios->clock < 400000) { /* Fix for broken stack */ realclock = 400000; } else { realclock = ios->clock; } if (ios->clock == 0) { /* Disable MMC_SD_CLK */ mmc_clk_setup(0); } else { /* Enable MMC_SD_CLK */ mmc_clk_setup(1); dsor = MMC_REF_CLOCK / realclock; if (dsor < 1) dsor = 1; if (MMC_REF_CLOCK / dsor > realclock) dsor++; if (dsor > 250) dsor = 250; } switch (ios->power_mode) { case MMC_POWER_OFF: dsor |= 0 << 11; break; case MMC_POWER_UP: case MMC_POWER_ON: dsor |= 1 << 11; break; } host->bus_mode = ios->bus_mode; OMAP_MMC_WRITE(host->base, CON, dsor); if (ios->power_mode == MMC_POWER_UP) { int wait_counter = 0; /* Send clock cycles, poll completion */ OMAP_MMC_WRITE(host->base, IE, 0); OMAP_MMC_WRITE(host->base, STAT, 0xffff); OMAP_MMC_WRITE(host->base, CMD, 1 << 7); /* We wait for the mmc command to be completed. If its not done, then the following * timeout mechanism will ensure that the system does not hang */ while ((0 == (OMAP_MMC_READ(host->base, STAT) & 1)) && (wait_counter < MMC_WAIT_TIMEOUT)) { wait_counter++; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(10); } if (MMC_WAIT_TIMEOUT == wait_counter) { printk(KERN_ERR "%s: Timed out waiting for cmd status - further results undefined\n", __FUNCTION__); return; } OMAP_MMC_WRITE(host->base, STAT, 1); }}static int omap24xx_mmc_probe(struct omap_dev *dev){ struct mmc_host *mmc; struct mmc_omap_host *host; static unsigned short card_status; int ret = 0; host = kmalloc(sizeof(struct mmc_omap_host), GFP_KERNEL); if (!host) { ret = -ENOMEM; return ret; } memset(host, 0, sizeof(struct mmc_omap_host)); mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), (struct device *)dev); if (!mmc) { ret = -ENOMEM; goto err2; } host = mmc_priv(mmc); host->mmc = mmc; host->use_dma = OMAP_USE_DMA; host->dma_ch = -1; host->irq = OMAP_MMC_IRQ; host->base = (unsigned long)ioremap(OMAP_MMC_BASE, MMC_REG_SIZE); if (!host->base) { printk("MMC: cannot map MMIO\n"); goto err2; } mmc->ops = &mmc_omap_ops; mmc->f_min = 400000; mmc->f_max = 24000000; mmc->ocr_avail = MMC_VDD_33_34; omap2_cfg_reg(H15_2420_MMC_CLKI); omap2_cfg_reg(G19_2420_MMC_CLKO); omap2_cfg_reg(H18_2420_MMC_CMD); omap2_cfg_reg(F20_2420_MMC_DAT); omap2_cfg_reg(F19_2420_MMC_DATDIR); omap2_cfg_reg(G18_2420_MMC_CMDDIR); ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); if (ret) goto err2;#ifdef CONFIG_MMC_HOTPLUG /* Request an irq for card detection */ ret = request_irq(MMC_HP_IRQ, mmc_omap_irq_cd, 0, DRIVER_NAME, host); if (ret) goto err2;#endif dev_set_drvdata((struct device *)dev, host); card_status = omap_get_gpio_datain(0); if (card_status != 1) mmc_add_host(mmc); else printk("\n MMC card not present\n"); return 0; err2: free_irq(host->irq, host); kfree(host); return ret;}static int omap24xx_mmc_remove(struct omap_dev *dev){ struct mmc_omap_host *host = dev_get_drvdata((struct device *)dev); omap_set_drvdata(dev, NULL); mmc_remove_host(host->mmc); free_irq(MMC_HP_IRQ, host); free_irq(host->irq, host); mmc_omap_power(host, 0); iounmap((void *)host->base); kfree(host); return 0;}#ifdef CONFIG_PMstatic int omap24xx_mmc_suspend(struct omap_dev *dev, u32 state){ return 0;}static int omap24xx_mmc_resume(struct omap_dev *dev){ return 0;}#else#define omap24xx_mmc_suspend NULL#define omap24xx_mmc_resume NULL#endifstatic void omap_24xx_release(struct device *dev){}static struct omap_driver omap24xx_mmc_driver = { .drv = { .name = DRIVER_NAME, }, .devid = OMAP24xx_MMC_DEVID, .busid = OMAP_BUS_L4, .clocks = 0, .probe = omap24xx_mmc_probe, .remove = omap24xx_mmc_remove, .suspend = omap24xx_mmc_suspend, .resume = omap24xx_mmc_resume,};static struct omap_dev omap24xx_mmc_device = { .name = "omap-mmc", .devid = OMAP24xx_MMC_DEVID, .dev = { .release = omap_24xx_release, }, .busid = OMAP_BUS_L4, .mapbase = (void *)OMAP_MMC_BASE, .res = { .start = IO_ADDRESS(OMAP_MMC_BASE), .end = IO_ADDRESS(OMAP_MMC_BASE) + 0x7f, }, .irq = { OMAP_MMC_IRQ, },};#ifdef CONFIG_MMC_HOTPLUG/* card detect function - detects mmc card's presence by reading gpio datain reg */int card_detect_fn(void){ unsigned card_status; int card_present; card_status = omap_get_gpio_datain(GPIO_0); if (card_status == 1) card_present = REMOVE; else card_present = INSERT; return card_present;}static struct hotplug_structure mmc_hp_str = { .card_detect = &card_detect_fn,};/* Schedule a worker thread to add the host upon card insertion interrupt */static void start_state_machine(void){ if (in_interrupt()) { cancel_delayed_work(&set_hp_work); schedule_work(&set_hp_work); } else { BUG_ON(!in_interrupt()); }}/* Schedule a worker thread to remove the host upon card removal interrupt */static void stop_state_machine(void){ if (in_interrupt()) { cancel_delayed_work(&stop_hp_work); schedule_work(&stop_hp_work); } else { BUG_ON(!in_interrupt()); }}/* Worker thread to add the host */static void set_hp_handler(void *data){ mmc_hotplug_insert(saved_host->mmc); run_sbin_hotplug(INSERT);}/* Worker thread to remove the host */static void stop_hp_handler(void *data){ mmc_hotplug_remove(saved_host->mmc); run_sbin_hotplug(REMOVE);}/* Function to invoke /sbin/hotplug script passing ACTION and AGENT parameters */static void run_sbin_hotplug(int insert){ char *argv[] = { hotplug_path, "mmc", NULL }; char *envp[] = { "HOME=/", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL, NULL }; if (!hotplug_path[0]) return; envp[2] = (insert) ? "ACTION=add" : "ACTION=remove"; call_usermodehelper(argv[0], argv, envp, 0);}#endifstatic int __init omap24xx_mmc_init(void){ int ret; ret = omap_driver_register(&omap24xx_mmc_driver); if (ret != 0) return ret; ret = omap_device_register(&omap24xx_mmc_device); if (ret != 0) goto err1;#ifdef CONFIG_MMC_HOTPLUG omap_mmc_pin_out(CONTROL_PADCONF_sdrc_a14, 0x3); omap_set_gpio_debounce(GPIO_0, DEBOUNCE_ENABLE); omap_set_gpio_debounce_time(GPIO_0, DEBOUNCE_TIME); omap_set_gpio_direction(GPIO_0, OMAP2420_DIR_INPUT); omap_set_gpio_edge_ctrl(GPIO_0, OMAP_GPIO_BOTH_EDGES); gpio_unmask_irq(GPIO_0); mmc_cd_register(&mmc_hp_str);#endif return 0; err1: omap_driver_unregister(&omap24xx_mmc_driver); return ret;}static void __exit omap24xx_mmc_cleanup(void){ omap_device_unregister(&omap24xx_mmc_device); omap_driver_unregister(&omap24xx_mmc_driver);}module_init(omap24xx_mmc_init);module_exit(omap24xx_mmc_cleanup);MODULE_DESCRIPTION("OMAP Multimedia Card driver");MODULE_LICENSE("GPL");MODULE_AUTHOR("Texas Instruments");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?