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=8a585f69742f0ccb3ddcff04ff87d0ad1cdb2e88;hp=56f6cfc8cec73d45b90816128cb2cd2cff3acdee;hb=c5016130971e795e1ad44bf23058794fd5ebaeed;hpb=adfef33c8b351697ab1998c7f579c2a037092764 diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 56f6cfc8cec7..8a585f69742f 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -584,7 +584,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); } @@ -852,6 +852,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. */ @@ -1091,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)) { @@ -1404,8 +1449,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); @@ -1425,7 +1470,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) { @@ -1451,10 +1496,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. */ @@ -1464,14 +1509,14 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data), "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch); - if (ret != 0) { + 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; } ret = omap_hsmmc_pre_dma_transfer(host, data, NULL); - if (ret) + if (unlikely(ret)) return ret; host->dma_ch = dma_ch; @@ -1482,11 +1527,8 @@ 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) +static void set_data_timeout(struct omap_hsmmc_host *host) { - unsigned int timeout, cycle_ns; uint32_t reg, clkd, dto = 0; reg = OMAP_HSMMC_READ(host->base, SYSCTL); @@ -1494,25 +1536,8 @@ static void set_data_timeout(struct omap_hsmmc_host *host, 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; - } + /* Use the maximum timeout value allowed in the standard of 14 or 0xE */ + dto = 14; reg &= ~DTO_MASK; reg |= dto << DTO_SHIFT; @@ -1535,13 +1560,13 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req) * busy signal. */ if (req->cmd->flags & MMC_RSP_BUSY) - set_data_timeout(host, 100000000U, 0); + set_data_timeout(host); 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); + set_data_timeout(host); if (host->use_dma) { ret = omap_hsmmc_start_dma_transfer(host, req); @@ -1584,6 +1609,62 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, 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 */ @@ -1594,7 +1675,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 @@ -1613,10 +1694,19 @@ 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 && req->data != NULL + && req->data->sg_len == 1 && req->data->sg->length <= 16) { + host->use_dma = 0; + } else { + host->use_dma = 1; + } + 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; @@ -1626,6 +1716,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 */ @@ -1928,6 +2021,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; @@ -2062,6 +2156,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); @@ -2170,9 +2273,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"); }