X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-kernel.git;a=blobdiff_plain;f=drivers%2Fmmc%2Fhost%2Fomap_hsmmc.c;h=348f577b66cf2753a78cc2518a4f3cc49eae95ec;hp=d5fe43d53c51894ff9e861dbced02bfde10ef705;hb=b34d06c93b0f4586a16aecf5a723746e02ff6797;hpb=1cac8e884fbea0b10ee5692cd97e456b58c34ce5 diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index d5fe43d53c51..348f577b66cf 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -163,7 +162,6 @@ struct omap_hsmmc_host { */ struct regulator *vcc; struct regulator *vcc_aux; - struct work_struct mmc_carddetect_work; void __iomem *base; resource_size_t mapbase; spinlock_t irq_lock; /* Prevent races with irq handler */ @@ -177,6 +175,7 @@ struct omap_hsmmc_host { int suspended; int irq; int use_dma, dma_ch; + int dma_ch_tx, dma_ch_rx; int dma_line_tx, dma_line_rx; int slot_id; int got_dbclk; @@ -586,7 +585,7 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host, irq_mask &= ~DTO_ENABLE; OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); - OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); + OMAP_HSMMC_WRITE(host->base, ISE, host->use_dma ? irq_mask : 0); OMAP_HSMMC_WRITE(host->base, IE, irq_mask); } @@ -854,6 +853,55 @@ omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL); +/* for hosts with 35xx erratum 2.1.1.128 */ +static ssize_t +omap_hsmmc_show_unsafe_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); + int val = 0; + + if (!(mmc->caps2 & MMC_CAP2_NO_MULTI_READ)) { + val = 1; + if (mmc->f_max == OMAP_MMC_MAX_CLOCK) + val = 2; + } + + return sprintf(buf, "%d\n", val); +} + +static ssize_t +omap_hsmmc_set_unsafe_read(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 0, &val); + if (ret) + return -EINVAL; + + switch (val) { + case 0: + mmc->caps2 |= MMC_CAP2_NO_MULTI_READ; + mmc->f_max = OMAP_MMC_MAX_CLOCK; + break; + case 1: + mmc->caps2 &= ~MMC_CAP2_NO_MULTI_READ; + mmc->f_max = 32000000; + break; + case 2: + mmc->caps2 &= ~MMC_CAP2_NO_MULTI_READ; + mmc->f_max = OMAP_MMC_MAX_CLOCK; + break; + } + + return count; +} +static DEVICE_ATTR(unsafe_read, S_IWUSR | S_IRUGO, + omap_hsmmc_show_unsafe_read, omap_hsmmc_set_unsafe_read); + /* * Configure the response type and send the cmd. */ @@ -1009,7 +1057,6 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno) dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->data->sg_len, omap_hsmmc_get_dma_dir(host, host->data)); - omap_free_dma(dma_ch); host->data->host_cookie = 0; } host->data = NULL; @@ -1093,19 +1140,15 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status) struct mmc_data *data; int end_cmd = 0, end_trans = 0; - if (!host->req_in_progress) { - do { - OMAP_HSMMC_WRITE(host->base, STAT, status); - /* Flush posted write */ - status = OMAP_HSMMC_READ(host->base, STAT); - } while (status & INT_EN_MASK); + if (unlikely(!host->req_in_progress)) { + OMAP_HSMMC_WRITE(host->base, STAT, status); return; } data = host->data; dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status); - if (status & ERR) { + if (unlikely(status & ERR)) { omap_hsmmc_dbg_report_irq(host, status); if ((status & CMD_TIMEOUT) || (status & CMD_CRC)) { @@ -1280,17 +1323,16 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) } /* - * Work Item to notify the core about card insertion/removal + * irq handler to notify the core about card insertion/removal */ -static void omap_hsmmc_detect(struct work_struct *work) +static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id) { - struct omap_hsmmc_host *host = - container_of(work, struct omap_hsmmc_host, mmc_carddetect_work); + struct omap_hsmmc_host *host = dev_id; struct omap_mmc_slot_data *slot = &mmc_slot(host); int carddetect; if (host->suspended) - return; + return IRQ_HANDLED; sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); @@ -1305,19 +1347,6 @@ static void omap_hsmmc_detect(struct work_struct *work) mmc_detect_change(host->mmc, (HZ * 200) / 1000); else mmc_detect_change(host->mmc, (HZ * 50) / 1000); -} - -/* - * ISR for handling card insertion and removal - */ -static irqreturn_t omap_hsmmc_cd_handler(int irq, void *dev_id) -{ - struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)dev_id; - - if (host->suspended) - return IRQ_HANDLED; - schedule_work(&host->mmc_carddetect_work); - return IRQ_HANDLED; } @@ -1333,23 +1362,39 @@ static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host, return sync_dev; } +static void omap_hsmmc_config_dma_params_once(struct omap_hsmmc_host *host, + struct mmc_data *data, + int dma_ch) +{ + if (data->flags & MMC_DATA_WRITE) { + omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, + (host->mapbase + OMAP_HSMMC_DATA), 0, 0); + omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_16); + omap_set_dma_src_data_pack(dma_ch, 1); + } else { + omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, + (host->mapbase + OMAP_HSMMC_DATA), 0, 0); + omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_16); + omap_set_dma_dest_data_pack(dma_ch, 1); + omap_set_dma_write_mode(dma_ch, OMAP_DMA_WRITE_LAST_NON_POSTED); + } +} + static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host, struct mmc_data *data, struct scatterlist *sgl) { - int blksz, nblk, dma_ch; + int blksz, nblk, dma_ch, sync; dma_ch = host->dma_ch; if (data->flags & MMC_DATA_WRITE) { - omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, - (host->mapbase + OMAP_HSMMC_DATA), 0, 0); omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, sg_dma_address(sgl), 0, 0); + sync = OMAP_DMA_DST_SYNC_PREFETCH; } else { - omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, - (host->mapbase + OMAP_HSMMC_DATA), 0, 0); omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, sg_dma_address(sgl), 0, 0); + sync = OMAP_DMA_SRC_SYNC; } blksz = host->data->blksz; @@ -1357,8 +1402,7 @@ static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host, omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, blksz / 4, nblk, OMAP_DMA_SYNC_FRAME, - omap_hsmmc_get_dma_sync_dev(host, data), - !(data->flags & MMC_DATA_WRITE)); + omap_hsmmc_get_dma_sync_dev(host, data), sync); omap_start_dma(dma_ch); } @@ -1370,7 +1414,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data) { struct omap_hsmmc_host *host = cb_data; struct mmc_data *data; - int dma_ch, req_in_progress; + int req_in_progress; if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n", @@ -1399,12 +1443,9 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data) omap_hsmmc_get_dma_dir(host, data)); req_in_progress = host->req_in_progress; - dma_ch = host->dma_ch; host->dma_ch = -1; spin_unlock(&host->irq_lock); - omap_free_dma(dma_ch); - /* If DMA has finished after TC, complete the request */ if (!req_in_progress) { struct mmc_request *mrq = host->mrq; @@ -1420,8 +1461,8 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host, { int dma_len; - if (!next && data->host_cookie && - data->host_cookie != host->next_data.cookie) { + if (unlikely(!next && data->host_cookie && + data->host_cookie != host->next_data.cookie)) { pr_warning("[%s] invalid cookie: data->host_cookie %d" " host->next_data.cookie %d\n", __func__, data->host_cookie, host->next_data.cookie); @@ -1441,7 +1482,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host, } - if (dma_len == 0) + if (unlikely(dma_len == 0)) return -EINVAL; if (next) { @@ -1467,10 +1508,10 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, struct scatterlist *sgl; sgl = data->sg + i; - if (sgl->length % data->blksz) + if (unlikely(sgl->length % data->blksz)) return -EINVAL; } - if ((data->blksz % 4) != 0) + if (unlikely((data->blksz % 4) != 0)) /* REVISIT: The MMC buffer increments only when MSB is written. * Return error for blksz which is non multiple of four. */ @@ -1478,16 +1519,31 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, BUG_ON(host->dma_ch != -1); - ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data), - "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch); - if (ret != 0) { - dev_err(mmc_dev(host->mmc), - "%s: omap_request_dma() failed with %d\n", - mmc_hostname(host->mmc), ret); - return ret; + if (data->flags & MMC_DATA_WRITE) + dma_ch = host->dma_ch_tx; + else + dma_ch = host->dma_ch_rx; + + if (dma_ch == -1) { + ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data), + "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch); + if (unlikely(ret != 0)) { + dev_err(mmc_dev(host->mmc), + "%s: omap_request_dma() failed with %d\n", + mmc_hostname(host->mmc), ret); + return ret; + } + + omap_hsmmc_config_dma_params_once(host, data, dma_ch); + + if (data->flags & MMC_DATA_WRITE) + host->dma_ch_tx = dma_ch; + else + host->dma_ch_rx = dma_ch; } + ret = omap_hsmmc_pre_dma_transfer(host, data, NULL); - if (ret) + if (unlikely(ret)) return ret; host->dma_ch = dma_ch; @@ -1498,41 +1554,15 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, return 0; } -static void set_data_timeout(struct omap_hsmmc_host *host, - unsigned int timeout_ns, - unsigned int timeout_clks) +/* pandora wifi small transfer hack */ +static int check_mmc3_dma_hack(struct omap_hsmmc_host *host, + struct mmc_request *req) { - unsigned int timeout, cycle_ns; - uint32_t reg, clkd, dto = 0; - - reg = OMAP_HSMMC_READ(host->base, SYSCTL); - clkd = (reg & CLKD_MASK) >> CLKD_SHIFT; - if (clkd == 0) - clkd = 1; - - cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd); - timeout = timeout_ns / cycle_ns; - timeout += timeout_clks; - if (timeout) { - while ((timeout & 0x80000000) == 0) { - dto += 1; - timeout <<= 1; - } - dto = 31 - dto; - timeout <<= 1; - if (timeout && dto) - dto += 1; - if (dto >= 13) - dto -= 13; - else - dto = 0; - if (dto > 14) - dto = 14; - } - - reg &= ~DTO_MASK; - reg |= dto << DTO_SHIFT; - OMAP_HSMMC_WRITE(host->base, SYSCTL, reg); + if (req->data != NULL && req->data->sg_len == 1 + && req->data->sg->length <= 16) + return 0; + else + return 1; } /* @@ -1546,18 +1576,11 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req) if (req->data == NULL) { OMAP_HSMMC_WRITE(host->base, BLK, 0); - /* - * Set an arbitrary 100ms data timeout for commands with - * busy signal. - */ - if (req->cmd->flags & MMC_RSP_BUSY) - set_data_timeout(host, 100000000U, 0); return 0; } OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz) | (req->data->blocks << 16)); - set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks); if (host->use_dma) { ret = omap_hsmmc_start_dma_transfer(host, req); @@ -1588,18 +1611,77 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, bool is_first_req) { struct omap_hsmmc_host *host = mmc_priv(mmc); + int use_dma = host->use_dma; if (mrq->data->host_cookie) { mrq->data->host_cookie = 0; return ; } - if (host->use_dma) + if (host->id == OMAP_MMC3_DEVID) + use_dma = check_mmc3_dma_hack(host, mrq); + if (use_dma) if (omap_hsmmc_pre_dma_transfer(host, mrq->data, &host->next_data)) mrq->data->host_cookie = 0; } +#define BWR (1 << 4) +#define BRR (1 << 5) + +static noinline void omap_hsmmc_request_do_pio(struct mmc_host *mmc, + struct mmc_request *req) +{ + struct omap_hsmmc_host *host = mmc_priv(mmc); + u32 *data = sg_virt(req->data->sg); + u32 len = req->data->sg->length; + int stat; + int i; + + for (i = 0; i < 10000000; i++) { + stat = OMAP_HSMMC_READ(host->base, STAT); + if (stat == 0) + continue; + + //dev_err(mmc_dev(host->mmc), "stat %x, l %d\n", stat, i); + + if (stat & (DATA_TIMEOUT | DATA_CRC)) + omap_hsmmc_reset_controller_fsm(host, SRD); + + if (stat & ERR) { + req->cmd->error = + req->data->error = -EINVAL; // ? + omap_hsmmc_xfer_done(host, host->data); + return; + } + + if (req->data->flags & MMC_DATA_WRITE) { + while (len > 0 && (stat & BWR)) { + OMAP_HSMMC_WRITE(host->base, DATA, *data++); + len -= 4; + } + } else { + while (len > 0 && (stat & BRR)) { + *data++ = OMAP_HSMMC_READ(host->base, DATA); + len -= 4; + } + } + + if ((stat & CC) && host->cmd) + omap_hsmmc_cmd_done(host, host->cmd); + if ((stat & TC) && host->mrq) { + omap_hsmmc_xfer_done(host, host->data); + break; + } + } + + if (len > 0) { + req->cmd->error = + req->data->error = -ETIMEDOUT; + omap_hsmmc_xfer_done(host, req->data); + } +} + /* * Request function. for read/write operation */ @@ -1610,7 +1692,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) BUG_ON(host->req_in_progress); BUG_ON(host->dma_ch != -1); - if (host->protect_card) { + if (unlikely(host->protect_card)) { if (host->reqs_blocked < 3) { /* * Ensure the controller is left in a consistent @@ -1629,10 +1711,15 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) return; } else if (host->reqs_blocked) host->reqs_blocked = 0; + + /* pandora wifi hack... */ + if (host->id == OMAP_MMC3_DEVID) + host->use_dma = check_mmc3_dma_hack(host, req); + WARN_ON(host->mrq != NULL); host->mrq = req; err = omap_hsmmc_prepare_data(host, req); - if (err) { + if (unlikely(err)) { req->cmd->error = err; if (req->data) req->data->error = err; @@ -1642,6 +1729,9 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) } omap_hsmmc_start_command(host, req->cmd, req->data); + + if (host->use_dma == 0) + omap_hsmmc_request_do_pio(mmc, req); } /* Routine to configure clock values. Exposed API to core */ @@ -1910,6 +2000,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) host->use_dma = 1; host->dev->dma_mask = &pdata->dma_mask; host->dma_ch = -1; + host->dma_ch_tx = -1; + host->dma_ch_rx = -1; host->irq = irq; host->id = pdev->id; host->slot_id = 0; @@ -1919,7 +2011,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) host->next_data.cookie = 1; platform_set_drvdata(pdev, host); - INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect); mmc->ops = &omap_hsmmc_ops; @@ -1945,6 +2036,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) omap_hsmmc_context_save(host); mmc->caps |= MMC_CAP_DISABLE; + if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) { dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n"); mmc->caps2 |= MMC_CAP2_NO_MULTI_READ; @@ -2047,10 +2139,11 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) /* Request IRQ for card detect */ if ((mmc_slot(host).card_detect_irq)) { - ret = request_irq(mmc_slot(host).card_detect_irq, - omap_hsmmc_cd_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - mmc_hostname(mmc), host); + ret = request_threaded_irq(mmc_slot(host).card_detect_irq, + NULL, + omap_hsmmc_detect, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + mmc_hostname(mmc), host); if (ret) { dev_dbg(mmc_dev(host->mmc), "Unable to grab MMC CD IRQ\n"); @@ -2078,6 +2171,15 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) goto err_slot_name; } + if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) { + ret = device_create_file(&mmc->class_dev, &dev_attr_unsafe_read); + + /* MMC_CAP2_NO_MULTI_READ makes it crawl, try a different workaround */ + mmc->caps2 &= ~MMC_CAP2_NO_MULTI_READ; + mmc->max_segs = 1; + mmc->f_max = 32000000; + } + omap_hsmmc_debugfs(mmc); pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); @@ -2129,7 +2231,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev) free_irq(host->irq, host); if (mmc_slot(host).card_detect_irq) free_irq(mmc_slot(host).card_detect_irq, host); - flush_work_sync(&host->mmc_carddetect_work); pm_runtime_put_sync(host->dev); pm_runtime_disable(host->dev); @@ -2176,7 +2277,6 @@ static int omap_hsmmc_suspend(struct device *dev) return ret; } } - cancel_work_sync(&host->mmc_carddetect_work); ret = mmc_suspend_host(host->mmc); if (ret == 0) { @@ -2188,9 +2288,7 @@ static int omap_hsmmc_suspend(struct device *dev) } else { host->suspended = 0; if (host->pdata->resume) { - ret = host->pdata->resume(&pdev->dev, - host->slot_id); - if (ret) + if (host->pdata->resume(&pdev->dev, host->slot_id)) dev_dbg(mmc_dev(host->mmc), "Unmask interrupt failed\n"); } @@ -2248,9 +2346,19 @@ static int omap_hsmmc_resume(struct device *dev) static int omap_hsmmc_runtime_suspend(struct device *dev) { struct omap_hsmmc_host *host; + int dma_ch; host = platform_get_drvdata(to_platform_device(dev)); omap_hsmmc_context_save(host); + + dma_ch = xchg(&host->dma_ch_tx, -1); + if (dma_ch != -1) + omap_free_dma(dma_ch); + + dma_ch = xchg(&host->dma_ch_rx, -1); + if (dma_ch != -1) + omap_free_dma(dma_ch); + dev_dbg(mmc_dev(host->mmc), "disabled\n"); return 0;