tifm_sd.c
来自「linux 内核源代码」· C语言 代码 · 共 1,096 行 · 第 1/2 页
C
1,096 行
} else cmd->error = cmd_error; } else { if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) { if (!(host->cmd_flags & CMD_READY)) { host->cmd_flags |= CMD_READY; tifm_sd_fetch_resp(cmd, sock); } else if (host->cmd_flags & SCMD_ACTIVE) { host->cmd_flags |= SCMD_READY; tifm_sd_fetch_resp(host->req->stop, sock); } } if (host_status & TIFM_MMCSD_BRS) host->cmd_flags |= BRS_READY; } if (host->no_dma && cmd->data) { if (host_status & TIFM_MMCSD_AE) writel(host_status & TIFM_MMCSD_AE, sock->addr + SOCK_MMCSD_STATUS); if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF | TIFM_MMCSD_BRS)) { local_irq_save(flags); tifm_sd_transfer_data(host); local_irq_restore(flags); host_status &= ~TIFM_MMCSD_AE; } } if (host_status & TIFM_MMCSD_EOFB) host->cmd_flags &= ~CARD_BUSY; else if (host_status & TIFM_MMCSD_CB) host->cmd_flags |= CARD_BUSY; tifm_sd_check_status(host); }done: writel(host_status, sock->addr + SOCK_MMCSD_STATUS); spin_unlock(&sock->lock);}static void tifm_sd_set_data_timeout(struct tifm_sd *host, struct mmc_data *data){ struct tifm_dev *sock = host->dev; unsigned int data_timeout = data->timeout_clks; if (fixed_timeout) return; data_timeout += data->timeout_ns / ((1000000000UL / host->clk_freq) * host->clk_div); if (data_timeout < 0xffff) { writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); writel((~TIFM_MMCSD_DPE) & readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); } else { data_timeout = (data_timeout >> 10) + 1; if (data_timeout > 0xffff) data_timeout = 0; /* set to unlimited */ writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); writel(TIFM_MMCSD_DPE | readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); }}static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq){ struct tifm_sd *host = mmc_priv(mmc); struct tifm_dev *sock = host->dev; unsigned long flags; struct mmc_data *r_data = mrq->cmd->data; spin_lock_irqsave(&sock->lock, flags); if (host->eject) { mrq->cmd->error = -ENOMEDIUM; goto err_out; } if (host->req) { printk(KERN_ERR "%s : unfinished request detected\n", sock->dev.bus_id); mrq->cmd->error = -ETIMEDOUT; goto err_out; } host->cmd_flags = 0; host->block_pos = 0; host->sg_pos = 0; if (mrq->data && !is_power_of_2(mrq->data->blksz)) host->no_dma = 1; else host->no_dma = no_dma ? 1 : 0; if (r_data) { tifm_sd_set_data_timeout(host, r_data); if ((r_data->flags & MMC_DATA_WRITE) && !mrq->stop) writel(TIFM_MMCSD_EOFB | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), sock->addr + SOCK_MMCSD_INT_ENABLE); if (host->no_dma) { writel(TIFM_MMCSD_BUFINT | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), sock->addr + SOCK_MMCSD_INT_ENABLE); writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) | (TIFM_MMCSD_FIFO_SIZE - 1), sock->addr + SOCK_MMCSD_BUFFER_CONFIG); host->sg_len = r_data->sg_len; } else { sg_init_one(&host->bounce_buf, host->bounce_buf_data, r_data->blksz); if(1 != tifm_map_sg(sock, &host->bounce_buf, 1, r_data->flags & MMC_DATA_WRITE ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE)) { printk(KERN_ERR "%s : scatterlist map failed\n", sock->dev.bus_id); mrq->cmd->error = -ENOMEM; goto err_out; } host->sg_len = tifm_map_sg(sock, r_data->sg, r_data->sg_len, r_data->flags & MMC_DATA_WRITE ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); if (host->sg_len < 1) { printk(KERN_ERR "%s : scatterlist map failed\n", sock->dev.bus_id); tifm_unmap_sg(sock, &host->bounce_buf, 1, r_data->flags & MMC_DATA_WRITE ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); mrq->cmd->error = -ENOMEM; goto err_out; } writel(TIFM_FIFO_INT_SETALL, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); writel(ilog2(r_data->blksz) - 2, sock->addr + SOCK_FIFO_PAGE_SIZE); writel(TIFM_FIFO_ENABLE, sock->addr + SOCK_FIFO_CONTROL); writel(TIFM_FIFO_INTMASK, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); if (r_data->flags & MMC_DATA_WRITE) writel(TIFM_MMCSD_TXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); else writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); tifm_sd_set_dma_data(host, r_data); } writel(r_data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); writel(r_data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); } host->req = mrq; mod_timer(&host->timer, jiffies + host->timeout_jiffies); writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); tifm_sd_exec(host, mrq->cmd); spin_unlock_irqrestore(&sock->lock, flags); return;err_out: spin_unlock_irqrestore(&sock->lock, flags); mmc_request_done(mmc, mrq);}static void tifm_sd_end_cmd(unsigned long data){ struct tifm_sd *host = (struct tifm_sd*)data; struct tifm_dev *sock = host->dev; struct mmc_host *mmc = tifm_get_drvdata(sock); struct mmc_request *mrq; struct mmc_data *r_data = NULL; unsigned long flags; spin_lock_irqsave(&sock->lock, flags); del_timer(&host->timer); mrq = host->req; host->req = NULL; if (!mrq) { printk(KERN_ERR " %s : no request to complete?\n", sock->dev.bus_id); spin_unlock_irqrestore(&sock->lock, flags); return; } r_data = mrq->cmd->data; if (r_data) { if (host->no_dma) { writel((~TIFM_MMCSD_BUFINT) & readl(sock->addr + SOCK_MMCSD_INT_ENABLE), sock->addr + SOCK_MMCSD_INT_ENABLE); } else { tifm_unmap_sg(sock, &host->bounce_buf, 1, (r_data->flags & MMC_DATA_WRITE) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, (r_data->flags & MMC_DATA_WRITE) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); } r_data->bytes_xfered = r_data->blocks - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; r_data->bytes_xfered *= r_data->blksz; r_data->bytes_xfered += r_data->blksz - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; } writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); spin_unlock_irqrestore(&sock->lock, flags); mmc_request_done(mmc, mrq);}static void tifm_sd_abort(unsigned long data){ struct tifm_sd *host = (struct tifm_sd*)data; printk(KERN_ERR "%s : card failed to respond for a long period of time " "(%x, %x)\n", host->dev->dev.bus_id, host->req->cmd->opcode, host->cmd_flags); tifm_eject(host->dev);}static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios){ struct tifm_sd *host = mmc_priv(mmc); struct tifm_dev *sock = host->dev; unsigned int clk_div1, clk_div2; unsigned long flags; spin_lock_irqsave(&sock->lock, flags); dev_dbg(&sock->dev, "ios: clock = %u, vdd = %x, bus_mode = %x, " "chip_select = %x, power_mode = %x, bus_width = %x\n", ios->clock, ios->vdd, ios->bus_mode, ios->chip_select, ios->power_mode, ios->bus_width); if (ios->bus_width == MMC_BUS_WIDTH_4) { writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG), sock->addr + SOCK_MMCSD_CONFIG); } else { writel((~TIFM_MMCSD_4BBUS) & readl(sock->addr + SOCK_MMCSD_CONFIG), sock->addr + SOCK_MMCSD_CONFIG); } if (ios->clock) { clk_div1 = 20000000 / ios->clock; if (!clk_div1) clk_div1 = 1; clk_div2 = 24000000 / ios->clock; if (!clk_div2) clk_div2 = 1; if ((20000000 / clk_div1) > ios->clock) clk_div1++; if ((24000000 / clk_div2) > ios->clock) clk_div2++; if ((20000000 / clk_div1) > (24000000 / clk_div2)) { host->clk_freq = 20000000; host->clk_div = clk_div1; writel((~TIFM_CTRL_FAST_CLK) & readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); } else { host->clk_freq = 24000000; host->clk_div = clk_div2; writel(TIFM_CTRL_FAST_CLK | readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); } } else { host->clk_div = 0; } host->clk_div &= TIFM_MMCSD_CLKMASK; writel(host->clk_div | ((~TIFM_MMCSD_CLKMASK) & readl(sock->addr + SOCK_MMCSD_CONFIG)), sock->addr + SOCK_MMCSD_CONFIG); host->open_drain = (ios->bus_mode == MMC_BUSMODE_OPENDRAIN); /* chip_select : maybe later */ //vdd //power is set before probe / after remove spin_unlock_irqrestore(&sock->lock, flags);}static int tifm_sd_ro(struct mmc_host *mmc){ int rc = 0; struct tifm_sd *host = mmc_priv(mmc); struct tifm_dev *sock = host->dev; unsigned long flags; spin_lock_irqsave(&sock->lock, flags); if (TIFM_MMCSD_CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE)) rc = 1; spin_unlock_irqrestore(&sock->lock, flags); return rc;}static const struct mmc_host_ops tifm_sd_ops = { .request = tifm_sd_request, .set_ios = tifm_sd_ios, .get_ro = tifm_sd_ro};static int tifm_sd_initialize_host(struct tifm_sd *host){ int rc; unsigned int host_status = 0; struct tifm_dev *sock = host->dev; writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); mmiowb(); host->clk_div = 61; host->clk_freq = 20000000; writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL); writel(host->clk_div | TIFM_MMCSD_POWER, sock->addr + SOCK_MMCSD_CONFIG); /* wait up to 0.51 sec for reset */ for (rc = 32; rc <= 256; rc <<= 1) { if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) { rc = 0; break; } msleep(rc); } if (rc) { printk(KERN_ERR "%s : controller failed to reset\n", sock->dev.bus_id); return -ENODEV; } writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS); writel(host->clk_div | TIFM_MMCSD_POWER, sock->addr + SOCK_MMCSD_CONFIG); writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); // command timeout fixed to 64 clocks for now writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO); writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND); for (rc = 16; rc <= 64; rc <<= 1) { host_status = readl(sock->addr + SOCK_MMCSD_STATUS); writel(host_status, sock->addr + SOCK_MMCSD_STATUS); if (!(host_status & TIFM_MMCSD_ERRMASK) && (host_status & TIFM_MMCSD_EOC)) { rc = 0; break; } msleep(rc); } if (rc) { printk(KERN_ERR "%s : card not ready - probe failed on initialization\n", sock->dev.bus_id); return -ENODEV; } writel(TIFM_MMCSD_CERR | TIFM_MMCSD_BRS | TIFM_MMCSD_EOC | TIFM_MMCSD_ERRMASK, sock->addr + SOCK_MMCSD_INT_ENABLE); mmiowb(); return 0;}static int tifm_sd_probe(struct tifm_dev *sock){ struct mmc_host *mmc; struct tifm_sd *host; int rc = -EIO; if (!(TIFM_SOCK_STATE_OCCUPIED & readl(sock->addr + SOCK_PRESENT_STATE))) { printk(KERN_WARNING "%s : card gone, unexpectedly\n", sock->dev.bus_id); return rc; } mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev); if (!mmc) return -ENOMEM; host = mmc_priv(mmc); tifm_set_drvdata(sock, mmc); host->dev = sock; host->timeout_jiffies = msecs_to_jiffies(1000); tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd, (unsigned long)host); setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host); mmc->ops = &tifm_sd_ops; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; mmc->f_min = 20000000 / 60; mmc->f_max = 24000000; mmc->max_blk_count = 2048; mmc->max_hw_segs = mmc->max_blk_count; mmc->max_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE); mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size; mmc->max_req_size = mmc->max_seg_size; mmc->max_phys_segs = mmc->max_hw_segs; sock->card_event = tifm_sd_card_event; sock->data_event = tifm_sd_data_event; rc = tifm_sd_initialize_host(host); if (!rc) rc = mmc_add_host(mmc); if (!rc) return 0; mmc_free_host(mmc); return rc;}static void tifm_sd_remove(struct tifm_dev *sock){ struct mmc_host *mmc = tifm_get_drvdata(sock); struct tifm_sd *host = mmc_priv(mmc); unsigned long flags; spin_lock_irqsave(&sock->lock, flags); host->eject = 1; writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); mmiowb(); spin_unlock_irqrestore(&sock->lock, flags); tasklet_kill(&host->finish_tasklet); spin_lock_irqsave(&sock->lock, flags); if (host->req) { writel(TIFM_FIFO_INT_SETALL, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); host->req->cmd->error = -ENOMEDIUM; if (host->req->stop) host->req->stop->error = -ENOMEDIUM; tasklet_schedule(&host->finish_tasklet); } spin_unlock_irqrestore(&sock->lock, flags); mmc_remove_host(mmc); dev_dbg(&sock->dev, "after remove\n"); mmc_free_host(mmc);}#ifdef CONFIG_PMstatic int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state){ return mmc_suspend_host(tifm_get_drvdata(sock), state);}static int tifm_sd_resume(struct tifm_dev *sock){ struct mmc_host *mmc = tifm_get_drvdata(sock); struct tifm_sd *host = mmc_priv(mmc); int rc; rc = tifm_sd_initialize_host(host); dev_dbg(&sock->dev, "resume initialize %d\n", rc); if (rc) host->eject = 1; else rc = mmc_resume_host(mmc); return rc;}#else#define tifm_sd_suspend NULL#define tifm_sd_resume NULL#endif /* CONFIG_PM */static struct tifm_device_id tifm_sd_id_tbl[] = { { TIFM_TYPE_SD }, { }};static struct tifm_driver tifm_sd_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE }, .id_table = tifm_sd_id_tbl, .probe = tifm_sd_probe, .remove = tifm_sd_remove, .suspend = tifm_sd_suspend, .resume = tifm_sd_resume};static int __init tifm_sd_init(void){ return tifm_register_driver(&tifm_sd_driver);}static void __exit tifm_sd_exit(void){ tifm_unregister_driver(&tifm_sd_driver);}MODULE_AUTHOR("Alex Dubov");MODULE_DESCRIPTION("TI FlashMedia SD driver");MODULE_LICENSE("GPL");MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl);MODULE_VERSION(DRIVER_VERSION);module_init(tifm_sd_init);module_exit(tifm_sd_exit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?