Merge branch 'drm-intel-next' of git://git.kernel.org/pub/scm/linux/kernel/git/anholt...
[pandora-kernel.git] / drivers / mmc / host / davinci_mmc.c
index 3bd0ba2..33d9f1b 100644 (file)
 
 /*
  * One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units,
- * and we handle up to NR_SG segments.  MMC_BLOCK_BOUNCE kicks in only
+ * and we handle up to MAX_NR_SG segments.  MMC_BLOCK_BOUNCE kicks in only
  * for drivers with max_hw_segs == 1, making the segments bigger (64KB)
- * than the page or two that's otherwise typical.  NR_SG == 16 gives at
- * least the same throughput boost, using EDMA transfer linkage instead
- * of spending CPU time copying pages.
+ * than the page or two that's otherwise typical. nr_sg (passed from
+ * platform data) == 16 gives at least the same throughput boost, using
+ * EDMA transfer linkage instead of spending CPU time copying pages.
  */
 #define MAX_CCNT       ((1 << 16) - 1)
 
-#define NR_SG          16
+#define MAX_NR_SG      16
 
 static unsigned rw_threshold = 32;
 module_param(rw_threshold, uint, S_IRUGO);
@@ -171,6 +171,7 @@ struct mmc_davinci_host {
 #define DAVINCI_MMC_DATADIR_READ       1
 #define DAVINCI_MMC_DATADIR_WRITE      2
        unsigned char data_dir;
+       unsigned char suspended;
 
        /* buffer is used during PIO of one scatterlist segment, and
         * is updated along with buffer_bytes_left.  bytes_left applies
@@ -192,7 +193,7 @@ struct mmc_davinci_host {
        struct edmacc_param     tx_template;
        struct edmacc_param     rx_template;
        unsigned                n_link;
-       u32                     links[NR_SG - 1];
+       u32                     links[MAX_NR_SG - 1];
 
        /* For PIO we walk scatterlists one segment at a time. */
        unsigned int            sg_len;
@@ -202,6 +203,8 @@ struct mmc_davinci_host {
        u8 version;
        /* for ns in one cycle calculation */
        unsigned ns_in_one_cycle;
+       /* Number of sg segments */
+       u8 nr_sg;
 #ifdef CONFIG_CPU_FREQ
        struct notifier_block   freq_transition;
 #endif
@@ -568,6 +571,7 @@ davinci_release_dma_channels(struct mmc_davinci_host *host)
 
 static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host)
 {
+       u32 link_size;
        int r, i;
 
        /* Acquire master DMA write channel */
@@ -593,7 +597,8 @@ static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host)
        /* Allocate parameter RAM slots, which will later be bound to a
         * channel as needed to handle a scatterlist.
         */
-       for (i = 0; i < ARRAY_SIZE(host->links); i++) {
+       link_size = min_t(unsigned, host->nr_sg, ARRAY_SIZE(host->links));
+       for (i = 0; i < link_size; i++) {
                r = edma_alloc_slot(EDMA_CTLR(host->txdma), EDMA_SLOT_ANY);
                if (r < 0) {
                        dev_dbg(mmc_dev(host->mmc), "dma PaRAM alloc --> %d\n",
@@ -905,19 +910,26 @@ static void mmc_davinci_cmd_done(struct mmc_davinci_host *host,
        }
 }
 
-static void
-davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data)
+static inline void mmc_davinci_reset_ctrl(struct mmc_davinci_host *host,
+                                                               int val)
 {
        u32 temp;
 
-       /* reset command and data state machines */
        temp = readl(host->base + DAVINCI_MMCCTL);
-       writel(temp | MMCCTL_CMDRST | MMCCTL_DATRST,
-               host->base + DAVINCI_MMCCTL);
+       if (val)        /* reset */
+               temp |= MMCCTL_CMDRST | MMCCTL_DATRST;
+       else            /* enable */
+               temp &= ~(MMCCTL_CMDRST | MMCCTL_DATRST);
 
-       temp &= ~(MMCCTL_CMDRST | MMCCTL_DATRST);
-       udelay(10);
        writel(temp, host->base + DAVINCI_MMCCTL);
+       udelay(10);
+}
+
+static void
+davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data)
+{
+       mmc_davinci_reset_ctrl(host, 1);
+       mmc_davinci_reset_ctrl(host, 0);
 }
 
 static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
@@ -1121,15 +1133,8 @@ static inline void mmc_davinci_cpufreq_deregister(struct mmc_davinci_host *host)
 #endif
 static void __init init_mmcsd_host(struct mmc_davinci_host *host)
 {
-       /* DAT line portion is diabled and in reset state */
-       writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_DATRST,
-               host->base + DAVINCI_MMCCTL);
-
-       /* CMD line portion is diabled and in reset state */
-       writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_CMDRST,
-               host->base + DAVINCI_MMCCTL);
 
-       udelay(10);
+       mmc_davinci_reset_ctrl(host, 1);
 
        writel(0, host->base + DAVINCI_MMCCLK);
        writel(MMCCLK_CLKEN, host->base + DAVINCI_MMCCLK);
@@ -1137,12 +1142,7 @@ static void __init init_mmcsd_host(struct mmc_davinci_host *host)
        writel(0x1FFF, host->base + DAVINCI_MMCTOR);
        writel(0xFFFF, host->base + DAVINCI_MMCTOD);
 
-       writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_DATRST,
-               host->base + DAVINCI_MMCCTL);
-       writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_CMDRST,
-               host->base + DAVINCI_MMCCTL);
-
-       udelay(10);
+       mmc_davinci_reset_ctrl(host, 0);
 }
 
 static int __init davinci_mmcsd_probe(struct platform_device *pdev)
@@ -1202,6 +1202,12 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
 
        init_mmcsd_host(host);
 
+       if (pdata->nr_sg)
+               host->nr_sg = pdata->nr_sg - 1;
+
+       if (host->nr_sg > MAX_NR_SG || !host->nr_sg)
+               host->nr_sg = MAX_NR_SG;
+
        host->use_dma = use_dma;
        host->irq = irq;
 
@@ -1327,32 +1333,65 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM
-static int davinci_mmcsd_suspend(struct platform_device *pdev, pm_message_t msg)
+static int davinci_mmcsd_suspend(struct device *dev)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+       int ret;
 
-       return mmc_suspend_host(host->mmc, msg);
+       mmc_host_enable(host->mmc);
+       ret = mmc_suspend_host(host->mmc);
+       if (!ret) {
+               writel(0, host->base + DAVINCI_MMCIM);
+               mmc_davinci_reset_ctrl(host, 1);
+               mmc_host_disable(host->mmc);
+               clk_disable(host->clk);
+               host->suspended = 1;
+       } else {
+               host->suspended = 0;
+               mmc_host_disable(host->mmc);
+       }
+
+       return ret;
 }
 
-static int davinci_mmcsd_resume(struct platform_device *pdev)
+static int davinci_mmcsd_resume(struct device *dev)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+       int ret;
+
+       if (!host->suspended)
+               return 0;
 
-       return mmc_resume_host(host->mmc);
+       clk_enable(host->clk);
+       mmc_host_enable(host->mmc);
+
+       mmc_davinci_reset_ctrl(host, 0);
+       ret = mmc_resume_host(host->mmc);
+       if (!ret)
+               host->suspended = 0;
+
+       return ret;
 }
+
+static const struct dev_pm_ops davinci_mmcsd_pm = {
+       .suspend        = davinci_mmcsd_suspend,
+       .resume         = davinci_mmcsd_resume,
+};
+
+#define davinci_mmcsd_pm_ops (&davinci_mmcsd_pm)
 #else
-#define davinci_mmcsd_suspend  NULL
-#define davinci_mmcsd_resume   NULL
+#define davinci_mmcsd_pm_ops NULL
 #endif
 
 static struct platform_driver davinci_mmcsd_driver = {
        .driver         = {
                .name   = "davinci_mmc",
                .owner  = THIS_MODULE,
+               .pm     = davinci_mmcsd_pm_ops,
        },
        .remove         = __exit_p(davinci_mmcsd_remove),
-       .suspend        = davinci_mmcsd_suspend,
-       .resume         = davinci_mmcsd_resume,
 };
 
 static int __init davinci_mmcsd_init(void)