Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / drivers / mmc / host / omap_hsmmc.c
index bc27065..26e9f6e 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/platform_device.h>
-#include <linux/workqueue.h>
 #include <linux/timer.h>
 #include <linux/clk.h>
 #include <linux/mmc/host.h>
@@ -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 */
@@ -854,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.
  */
@@ -1280,17 +1327,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 +1351,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;
 }
 
@@ -1498,11 +1531,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);
@@ -1510,25 +1540,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;
@@ -1551,13 +1564,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);
@@ -1919,7 +1932,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 +1957,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 +2060,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 +2092,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 +2152,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 +2198,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) {