diff mbox

[16/23] Alternative mmc structure to support pxa168, pxa910, mmp2 family SD

Message ID 501F2432-7895-49DC-9A47-824868A4769B@marvell.com (mailing list archive)
State New, archived
Headers show

Commit Message

Philip Rakity Dec. 22, 2010, 7:10 a.m. UTC
None
diff mbox

Patch

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index d42fe49..d9eddbd 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -166,18 +166,52 @@  config MMC_SDHCI_S3C

          If unsure, say N.

+
+config MMC_SDHCI_PXA_CORE
+       tristate
+       help
+         This is silent Kconfig symbol that is selected by the drivers that
+         need PXA driver base support
+
+
 config MMC_SDHCI_PXA
-       tristate "Marvell PXA168/PXA910/MMP2 SD Host Controller support"
-       depends on ARCH_PXA || ARCH_MMP
+       tristate "Marvell MMP2 SD Host Controller support"
+       depends on CPU_MMP2
+       select MMC_SDHCI
+       select MMC_SDHCI_PXA_CORE
+       help
+         This selects the Marvell(R) MMP2 SD Host Controller.
+         If you have a MMP2 platform with SD Host Controller
+         and a card slot, say Y or M here.
+
+         If unsure, say N.
+
+config MMC_SDHCI_PXA9xx
+       tristate "Marvell PXA9xx SD Host Controller support"
+       depends on CPU_PXA910
        select MMC_SDHCI
+       select MMC_SDHCI_PXA_CORE
+       help
+         This selects the Marvell(R) PXA910 SD Host Controller.
+         If you have a PXA910 platform with SD Host Controller
+         and a card slot, say Y or M here.
+
+         If unsure, say N.
+
+config MMC_SDHCI_PXA168
+       tristate "Marvell PXA168 SD Host Controller support"
+       depends on CPU_PXA168
+       select MMC_SDHCI
+       select MMC_SDHCI_PXA_CORE
        select MMC_SDHCI_IO_ACCESSORS
        help
-         This selects the Marvell(R) PXA168/PXA910/MMP2 SD Host Controller.
-         If you have a PXA168/PXA910/MMP2 platform with SD Host Controller
+         This selects the Marvell(R) PXA168 SD Host Controller.
+         If you have a PXA168 platform with SD Host Controller
          and a card slot, say Y or M here.

          If unsure, say N.

+
 config MMC_SDHCI_SPEAR
        tristate "SDHCI support on ST SPEAr platform"
        depends on MMC_SDHCI && PLAT_SPEAR
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index d91364d..1de1c77 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -8,7 +8,10 @@  obj-$(CONFIG_MMC_IMX)          += imxmmc.o
 obj-$(CONFIG_MMC_MXC)          += mxcmmc.o
 obj-$(CONFIG_MMC_SDHCI)                += sdhci.o
 obj-$(CONFIG_MMC_SDHCI_PCI)    += sdhci-pci.o
-obj-$(CONFIG_MMC_SDHCI_PXA)    += sdhci-pxa.o
+obj-$(CONFIG_MMC_SDHCI_PXA_CORE)       += sdhci-pxa.o
+obj-$(CONFIG_MMC_SDHCI_PXA)    += sdhci-mmp2.o
+obj-$(CONFIG_MMC_SDHCI_PXA168) += sdhci-pxa168.o
+obj-$(CONFIG_MMC_SDHCI_PXA9xx) += sdhci-pxa910.o
 obj-$(CONFIG_MMC_SDHCI_S3C)    += sdhci-s3c.o
 obj-$(CONFIG_MMC_SDHCI_SPEAR)  += sdhci-spear.o
 obj-$(CONFIG_MMC_WBSD)         += wbsd.o
diff --git a/drivers/mmc/host/sdhci-mmp2.c b/drivers/mmc/host/sdhci-mmp2.c
new file mode 100644
index 0000000..6824177
--- /dev/null
+++ b/drivers/mmc/host/sdhci-mmp2.c
@@ -0,0 +1,245 @@ 
+/**************************************************************************
+ *
+ * Copyright (c) 2009, 2010 Marvell International Ltd.
+ *                             Philip Rakity <prakity@marvell.com>
+ *                             Mark F. Brown <markb@marvell.com>
+ *
+ * This file is part of GNU program.
+ *
+ * GNU program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNU program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.
+ *
+ * If not, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ *************************************************************************/
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <plat/sdhci.h>
+#include "sdhci-pxa.h"
+#include "sdhci.h"
+
+#define DEBUG
+
+#define DRIVER_NAME "sdhci-mmp2"
+
+#define HOST_CTRL_2            0x3E
+#define ASYNC_INT_EN           (1 << 14)
+
+#define SD_CFG_FIFO_PARAM       0x100
+#define SDCFG_GEN_PAD_CLK_ON   (1<<6)
+
+#define SD_CLOCK_AND_BURST_SIZE_SETUP   0x10A
+#define SDCLK_DELAY_MASK     0x1F
+#define SDCLK_SEL_MASK       0x1
+#define SDCLK_DELAY_SHIFT    9
+#define SDCLK_SEL_SHIFT      8
+
+#define SD_CE_ATA_2          0x10E
+#define SDCE_MISC_INT          (1<<2)
+#define SDCE_MISC_INT_EN       (1<<1)
+
+#define DISABLE_CLOCK_GATING 0
+
+
+static int platform_mmp2_probe(struct sdhci_host *host);
+
+/*
+ * MMC spec calls for the host to send 74 clocks to the card
+ * during initialization, right after voltage stabilization.
+ * the pxa168 controller has no easy way to generate those clocks.
+ * create the clocks manually right here.
+ */
+static void generate_initial_74_clocks(struct sdhci_host *host, u8 power_mode)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+       u16 tmp;
+       int count;
+
+       if (pxa->power_mode == MMC_POWER_UP
+       && power_mode == MMC_POWER_ON) {
+
+               pr_debug("%s:%s ENTER: slot->power_mode = %d,"
+                       "ios->power_mode = %d\n",
+                       __func__,
+                       mmc_hostname(host->mmc),
+                       pxa->power_mode,
+                       power_mode);
+
+               /* set we want notice of when 74 clocks are sent */
+               tmp = readw(host->ioaddr + SD_CE_ATA_2);
+               tmp |= SDCE_MISC_INT_EN;
+               writew(tmp, host->ioaddr + SD_CE_ATA_2);
+
+               /* start sending the 74 clocks */
+               tmp = readw(host->ioaddr + SD_CFG_FIFO_PARAM);
+               tmp |= SDCFG_GEN_PAD_CLK_ON;
+               writew(tmp, host->ioaddr + SD_CFG_FIFO_PARAM);
+
+               /* slowest speed is about 100KHz or 10usec per clock */
+               udelay(740);
+               count = 0;
+#define MAX_WAIT_COUNT 5
+               while (count++ < MAX_WAIT_COUNT) {
+                       if ((readw(host->ioaddr + SD_CE_ATA_2)
+                                       & SDCE_MISC_INT) == 0)
+                               break;
+                       udelay(10);
+               }
+
+               if (count == MAX_WAIT_COUNT)
+                       printk(KERN_WARNING"%s: %s: 74 clock interrupt "
+                               "not cleared\n",
+                               __func__, mmc_hostname(host->mmc));
+               /* clear the interrupt bit if posted */
+               tmp = readw(host->ioaddr + SD_CE_ATA_2);
+               tmp |= SDCE_MISC_INT;
+               writew(tmp, host->ioaddr + SD_CE_ATA_2);
+       }
+       pxa->power_mode = power_mode;
+}
+
+static void set_clock_and_burst_size(struct sdhci_host *host)
+{
+       u16 tmp;
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+
+       pr_debug("%s:%s: adjust = %d\n",
+               __func__, mmc_hostname(host->mmc), pxa->pdata->adjust_clocks);
+
+       if (pxa->pdata->adjust_clocks) {
+               tmp = readw(host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+               pr_debug("%s:%s: (B) SD_CLOCK_AND_BURST = %04X, "
+                       "delay = %d, sel = %d\n",
+                       __func__, mmc_hostname(host->mmc), tmp,
+                       pxa->pdata->clk_delay, pxa->pdata->clk_select);
+               tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
+               tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT);
+               tmp |= (pxa->pdata->clk_delay & SDCLK_DELAY_MASK) <<
+                       SDCLK_DELAY_SHIFT;
+               tmp |= (pxa->pdata->clk_select & SDCLK_SEL_MASK) <<
+                       SDCLK_SEL_SHIFT;
+               writew(tmp, host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+               pr_debug("%s:%s: (A) SD_CLOCK_AND_BURST_SIZE_SETUP = %04X\n",
+                       __func__, mmc_hostname(host->mmc), tmp);
+       }
+}
+
+static void programFIFO(struct sdhci_host *host, int enable)
+{
+       unsigned short tmp;
+
+       tmp = readw(host->ioaddr + HOST_CTRL_2);
+
+       if (enable)
+               tmp |= ASYNC_INT_EN;
+       else
+               tmp &= ~ASYNC_INT_EN;
+
+       writew(tmp, host->ioaddr + HOST_CTRL_2);
+}
+
+static void platform_reset_exit(struct sdhci_host *host, u8 mask)
+{
+       if (mask == SDHCI_RESET_ALL) {
+               /* reset private registers */
+               programFIFO(host, DISABLE_CLOCK_GATING);
+               set_clock_and_burst_size(host);
+       }
+}
+
+
+#ifdef CONFIG_MMC_CLKGATE
+static void platform_hw_clk_gate(struct sdhci_host *host)
+{
+       int enable;
+
+       enable = host->mmc->clk_gated;
+       programFIFO(host, enable);
+}
+#endif
+
+static unsigned int get_f_max_clock(struct sdhci_host *host)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+
+       pr_debug("%s:%s f_max = %d\n",
+                       __func__, mmc_hostname(host->mmc),
+                        pxa->pdata->max_speed);
+
+       return pxa->pdata->max_speed;
+}
+
+struct sdhci_pxa_data sdhci_platform_data = {
+       .ops = {
+               .platform_reset_exit = platform_reset_exit,
+               .platform_send_init_74_clocks = generate_initial_74_clocks,
+               .get_f_max_clock = NULL,
+#ifdef CONFIG_MMC_CLKGATE
+               .platform_hw_clk_gate = platform_hw_clk_gate,
+#endif
+       },
+#ifdef CONFIG_MMC_CLKGATE
+       .mmc_caps = MMC_CAP_HW_CLOCK_GATING | MMC_CAP_BUS_WIDTH_TEST,
+#else
+       .mmc_caps = MMC_CAP_BUS_WIDTH_TEST,
+#endif
+       .platform_probe = platform_mmp2_probe,
+       .quirks = 0,
+};
+EXPORT_SYMBOL_GPL(sdhci_platform_data);
+
+static int platform_mmp2_probe(struct sdhci_host *host)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+       struct sdhci_pxa_platdata *pdata = pxa->pdata;
+       struct sdhci_ops *p_ops;
+
+       host->quirks |= sdhci_platform_data.quirks;
+       host->mmc->caps |= sdhci_platform_data.mmc_caps;
+
+       /* If slot design supports 8 bit data, indicate this to MMC. */
+       if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
+               host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+
+       if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
+               host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+               host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+       }
+
+       /*
+        * we cannot directly copy our operations into host->ops
+        * since it is read only.  So we do this indirectly.
+        */
+       p_ops = kmalloc(sizeof(struct sdhci_ops), GFP_KERNEL);
+       if (!p_ops) {
+               printk(KERN_ERR "no memory");
+               return -ENOMEM;
+       }
+
+       memcpy((void *)p_ops, (void *)&sdhci_platform_data.ops,
+               sizeof(struct sdhci_ops));
+
+       if (pxa->pdata->max_speed)
+               p_ops->get_f_max_clock = get_f_max_clock;
+
+       memcpy((void *)host->ops, (void *)p_ops, sizeof(struct sdhci_ops));
+       kfree(p_ops);
+       return 0;
+}
diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
index 1a58c7b..5a02777 100644
--- a/drivers/mmc/host/sdhci-pxa.c
+++ b/drivers/mmc/host/sdhci-pxa.c
@@ -1,42 +1,46 @@ 
-/* linux/drivers/mmc/host/sdhci-pxa.c
+/**************************************************************************
  *
- * Copyright (C) 2010 Marvell International Ltd.
+ * Copyright (c) 2009, 2010 Marvell International Ltd.
  *             Zhangfei Gao <zhangfei.gao@marvell.com>
  *             Kevin Wang <dwang4@marvell.com>
  *             Mingwei Wang <mwwang@marvell.com>
  *             Philip Rakity <prakity@marvell.com>
  *             Mark Brown <markb@marvell.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-/* Supports:
- * SDHCI support for MMP2/PXA910/PXA168
+ * This file is part of GNU program.
+ *
+ * GNU program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNU program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
  *
- * Refer to sdhci-s3c.c.
- */
+ * You should have received a copy of the GNU General Public License along
+ * with this program.
+ *
+ * If not, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ *************************************************************************/

+#include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/mmc/host.h>
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/err.h>
+#include <linux/slab.h>
 #include <plat/sdhci.h>
 #include "sdhci.h"
+#include "sdhci-pxa.h"
+#include <mach/cputype.h>

 #define DRIVER_NAME    "sdhci-pxa"

-struct sdhci_pxa {
-       struct sdhci_host               *host;
-       struct sdhci_pxa_platdata       *pdata;
-       struct clk                      *clk;
-       struct resource                 *res;
-
-       u8                              clk_enable;
-};

 /*****************************************************************************\
  *                                                                           *
@@ -53,17 +57,6 @@  static void enable_clock(struct sdhci_host *host)
        }
 }

-static unsigned int get_f_max_clock(struct sdhci_host *host)
-{
-       struct sdhci_pxa *pxa = sdhci_priv(host);
-
-       return pxa->pdata->max_speed;
-}
-
-static struct sdhci_ops sdhci_pxa_ops = {
-       .get_f_max_clock = NULL,
-};
-
 /*****************************************************************************\
  *                                                                           *
  * Device probing/removal                                                    *
@@ -125,29 +118,24 @@  static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
        }

        host->hw_name = "MMC";
-       host->ops = &sdhci_pxa_ops;
-       host->irq = irq;
-       host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
-
-       /* enable mmc bus width testing */
-       host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST;
-
-       /* If slot design supports 8 bit data, indicate this to MMC. */
-       if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
-               host->mmc->caps |= MMC_CAP_8_BIT_DATA;
-
-       if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
-               host->mmc->caps |= MMC_CAP_NONREMOVABLE;
-               host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+       host->ops = kmalloc(sizeof(struct sdhci_ops), GFP_KERNEL);
+       if (!host->ops) {
+               dev_err(&pdev->dev, "no memory for host->ops\n");
+               ret = -ENOMEM;
+               goto out;
        }

+       host->irq = irq;
+       host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
+
        /* do not rely on u-boot to enable the clocks */
        enable_clock(host);

-       if (pxa->pdata->max_speed)
-               sdhci_pxa_ops.get_f_max_clock = get_f_max_clock;
-       else
-               sdhci_pxa_ops.get_f_max_clock = NULL;
+       if (sdhci_platform_data.platform_probe) {
+               ret = sdhci_platform_data.platform_probe(host);
+               if (ret)
+                       goto out;
+       }

        ret = sdhci_add_host(host);
        if (ret) {
@@ -161,6 +149,7 @@  static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
 out:
        if (host) {
                clk_put(pxa->clk);
+               kfree(host->ops);
                if (host->ioaddr)
                        iounmap(host->ioaddr);
                if (pxa->res)
@@ -193,6 +182,7 @@  static int __devexit sdhci_pxa_remove(struct platform_device *pdev)
                                           resource_size(pxa->res));
                clk_put(pxa->clk);

+               kfree(host->ops);
                sdhci_free_host(host);
                platform_set_drvdata(pdev, NULL);
        }
@@ -251,4 +241,5 @@  module_exit(sdhci_pxa_exit);

 MODULE_DESCRIPTION("SDH controller driver for PXA168/PXA910/MMP2");
 MODULE_AUTHOR("Zhangfei Gao <zhangfei.gao@marvell.com>");
+MODULE_AUTHOR("Philp Rakity <prakity@marvell.com>");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-pxa.h b/drivers/mmc/host/sdhci-pxa.h
new file mode 100644
index 0000000..4161fc1
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pxa.h
@@ -0,0 +1,65 @@ 
+/**************************************************************************
+ *
+ * Copyright (c) 2009, 2010 Marvell International Ltd.
+ *                             Philip Rakity <prakity@marvell.com>
+ *                             Mark F. Brown <markb@marvell.com>
+ *
+ * This file is part of GNU program.
+ *
+ * GNU program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNU program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.
+ *
+ * If not, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ *************************************************************************/
+
+/* Supports:
+ * SDHCI support for MMP2/PXA910/PXA168
+ *
+ * Refer to sdhci-s3c.c.
+ */
+
+#ifndef __SDHCI_PXA_H
+#define __SDHCI_PXA_H
+
+#include <linux/types.h>
+#include "sdhci.h"
+
+struct sdhci_pxa {
+       struct sdhci_host               *host;
+       struct sdhci_pxa_platdata       *pdata;
+       struct clk                      *clk;
+       struct resource *res;
+
+       u32             delay_in_ms;
+       u32             delay_in_us;
+       u32             delay_in_ns;
+
+       u8              clk_enable;
+       u8              power_mode;
+};
+
+struct sdhci_pxa_data {
+       struct sdhci_ops ops;
+       unsigned int quirks;
+       unsigned int mmc_caps;
+       int (*platform_probe) (struct sdhci_host *host);
+};
+
+extern struct sdhci_pxa_data sdhci_platform_data;
+extern void sdhci_free_host(struct sdhci_host *host);
+extern void sdhci_remove_host(struct sdhci_host *host, int dead);
+extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
+       size_t priv_size);
+
+#endif /* __SDHCI_PXA_H */
diff --git a/drivers/mmc/host/sdhci-pxa168.c b/drivers/mmc/host/sdhci-pxa168.c
new file mode 100644
index 0000000..8f85d65
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pxa168.c
@@ -0,0 +1,322 @@ 
+/**************************************************************************
+ *
+ * Copyright (c) 2009, 2010 Marvell International Ltd.
+ *                             Philip Rakity <prakity@marvell.com>
+ *                             Mark F. Brown <markb@marvell.com>
+ *
+ * This file is part of GNU program.
+ *
+ * GNU program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNU program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.
+ *
+ * If not, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ *************************************************************************/
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <plat/sdhci.h>
+#include "sdhci-pxa.h"
+#include "sdhci.h"
+
+#define DRIVER_NAME "sdhci-pxa168"
+
+#define SD_FIFO_PARAM          0xE0
+#define DIS_PAD_SD_CLK_GATE    (1<<10) /* Turn on/off Dynamic Clock Gating */
+
+#define SD_CLOCK_AND_BURST_SIZE_SETUP  0xE6
+#define SDCLK_DELAY_MASK       0xF
+#define SDCLK_SEL_MASK         0x3
+#define SDCLK_DELAY_SHIFT      10
+#define SDCLK_SEL_SHIFT                8
+
+#define SD_CE_ATA_2                    0xEA
+#define SDCE_MMC_WIDTH         (1<<8)
+#define SDCE_MMC_CARD          (1<<12)
+
+#define DISABLE_CLOCK_GATING 0
+
+static int platform_pxa168_probe(struct sdhci_host *host);
+
+/*
+ * MMC spec calls for the host to send 74 clocks to the card
+ * during initialization, right after voltage stabilization.
+ * the pxa168 controller has no easy way to generate those clocks.
+ * create the clocks manually right here.
+ */
+#if 0
+static void generate_init_clocks(struct sdhci_host *host, u8 power_mode)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+       struct pfn_cfg *cfg = pxa->pdata->pfn_table;
+       mfp_cfg_t clk_pin;
+       int i;
+
+
+       if (cfg == NULL) {
+               DBG("Cannot generate init clocks!\n");
+               return;
+       }
+
+       if (pxa->power_mode == MMC_POWER_UP
+       && power_mode == MMC_POWER_ON) {
+
+               DBG("%s: ENTER %s: power_mode = %d, ios_.power_mode = %d\n",
+                       __func__,
+                       mmc_hostname(host->mmc),
+                       pxa->power_mode,
+                       power_mode);
+               /* CMD/CLK pin to gpio mode.  */
+               mfp_config(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CMD), 1);
+               mfp_config(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CLK), 1);
+
+               /* ensure at least 1/2 period stable to prevent runt pulse.*/
+               udelay(3);
+
+               clk_pin = *(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CLK));
+               if (gpio_request(MFP_PIN(clk_pin), "MMC_CLK")) {
+                       printk(KERN_ERR "Cannot obtain MMC_CLK GPIO %ld\n",
+                               MFP_PIN(clk_pin));
+                       goto err;
+               }
+
+               DBG("Generating init clocks on pins CLK %ld\n",
+                       MFP_PIN(clk_pin));
+
+               for (i = 0; i < INIT_CLOCKS; i++) {
+                       gpio_direction_output(MFP_PIN(clk_pin), 0); /* low */
+                       udelay(3);
+                       gpio_direction_output(MFP_PIN(clk_pin), 1); /* high */
+                       udelay(3);
+               }
+
+               gpio_free(MFP_PIN(clk_pin));
+       }
+
+err:
+       pxa->power_mode = power_mode;
+
+       /* CMD/CLK pin back MMC mode.   */
+       mfp_config(pfn_lookup(cfg, PFN_FN, PIN_MMC_CMD), 1);
+       mfp_config(pfn_lookup(cfg, PFN_FN, PIN_MMC_CLK), 1);
+}
+#endif
+
+/*
+ * we cannot talk to controller for 8 bus cycles according to sdio spec
+ * at lowest speed this is 100,000 HZ per cycle or 800,000 cycles
+ * which is quite a LONG TIME on a fast cpu -- so delay if needed
+ */
+static void platform_specific_delay(struct sdhci_host *host)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+
+       mdelay(pxa->delay_in_ms);
+       udelay(pxa->delay_in_us);
+       ndelay(pxa->delay_in_ns);
+}
+
+static void set_clock(struct sdhci_host *host, unsigned int clock)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+
+       if (clock != 0) {
+               pxa->delay_in_ns = (1000000000/clock);
+
+               /* need to delay 12 clocks for 8787/8786 */
+               /* need to delay 8 clocks for controller -- so just use 12 */
+
+               pxa->delay_in_ns = pxa->delay_in_ns * 12;
+
+               pxa->delay_in_ms = pxa->delay_in_ns / 1000000;
+               pxa->delay_in_ns = pxa->delay_in_ns % 1000000;
+               pxa->delay_in_us = pxa->delay_in_ns / 1000;
+               pxa->delay_in_ns = pxa->delay_in_ns % 1000;
+       } else {
+               pxa->delay_in_ns = 0;
+               pxa->delay_in_us = 0;
+               pxa->delay_in_ms = 0;
+       }
+}
+
+static void set_clock_and_burst_size(struct sdhci_host *host)
+{
+       u16 tmp;
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+
+       if (pxa->pdata->adjust_clocks) {
+               tmp = readw(host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+               pr_debug("%s:%s: (B) SD_CLOCK_AND_BURST = %04X"
+                       ", delay = %d, sel = %d\n",
+                       __func__, mmc_hostname(host->mmc), tmp,
+                       pxa->pdata->clk_delay, pxa->pdata->clk_select);
+               tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
+               tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT);
+               tmp |= (pxa->pdata->clk_delay & SDCLK_DELAY_MASK) <<
+                       SDCLK_DELAY_SHIFT;
+               tmp |= (pxa->pdata->clk_select & SDCLK_SEL_MASK) <<
+                       SDCLK_SEL_SHIFT;
+               writew(tmp, host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+               pr_debug("%s:%s: (A) SD_CLOCK_AND_BURST = %04X\n",
+                       __func__, mmc_hostname(host->mmc), tmp);
+       }
+}
+
+static void programFIFO(struct sdhci_host *host, int enable)
+{
+       unsigned short tmp;
+
+       tmp = readw(host->ioaddr + SD_FIFO_PARAM);
+       if (enable)
+               tmp &= ~DIS_PAD_SD_CLK_GATE;
+       else
+               tmp |= DIS_PAD_SD_CLK_GATE;
+       writew(tmp, host->ioaddr + SD_FIFO_PARAM);
+}
+
+static void platform_reset_enter(struct sdhci_host *host, u8 mask)
+{
+       /* Before RESET_DATA we need to wait at least 10 sd cycles */
+       if (mask == SDHCI_RESET_DATA)
+               platform_specific_delay(host);
+}
+
+
+static void platform_reset_exit(struct sdhci_host *host, u8 mask)
+{
+       if (mask == SDHCI_RESET_ALL) {
+               /* reset private registers */
+               programFIFO(host, DISABLE_CLOCK_GATING);
+               set_clock_and_burst_size(host);
+       }
+}
+
+
+#ifdef CONFIG_MMC_CLKGATE
+static void platform_hw_clk_gate(struct sdhci_host *host)
+{
+       int enable;
+
+       enable = host->mmc->clk_gated;
+       programFIFO(host, enable);
+}
+#endif
+
+static unsigned int get_max_clock(struct sdhci_host *host)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+
+       pr_debug("%s:%s clk_get_rate = %lu\n",
+                       __func__, mmc_hostname(host->mmc),
+                        clk_get_rate(pxa->clk));
+
+       return clk_get_rate(pxa->clk);
+}
+
+static unsigned int get_f_max_clock(struct sdhci_host *host)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+
+       pr_debug("%s:%s f_max = %d\n",
+                       __func__, mmc_hostname(host->mmc),
+                        pxa->pdata->max_speed);
+
+       return pxa->pdata->max_speed;
+}
+
+static int platform_supports_8_bit(struct sdhci_host *host, int width)
+{
+       u16 tmp;
+
+       tmp = readw(host->ioaddr + SD_CE_ATA_2);
+       tmp |= SDCE_MMC_CARD | SDCE_MMC_WIDTH;
+       writew(tmp, host->ioaddr + SD_CE_ATA_2);
+
+       tmp = readw(host->ioaddr + SD_CE_ATA_2);
+       if (width != 8)
+               tmp &= ~(SDCE_MMC_CARD | SDCE_MMC_WIDTH);
+       else
+               tmp |= SDCE_MMC_CARD | SDCE_MMC_WIDTH;
+       writew(tmp, host->ioaddr + SD_CE_ATA_2);
+       return 0;
+}
+
+struct sdhci_pxa_data sdhci_platform_data = {
+       .ops = {
+               .platform_reset_enter = platform_reset_enter,
+               .platform_reset_exit = platform_reset_exit,
+               .get_max_clock = get_max_clock,
+               .set_clock = set_clock,
+               .platform_specific_delay = platform_specific_delay,
+               .platform_send_init_74_clocks = NULL,
+               .get_f_max_clock = NULL,
+               .platform_8bit_width = NULL,
+#ifdef CONFIG_MMC_CLKGATE
+               .platform_hw_clk_gate = platform_hw_clk_gate,
+#endif
+       },
+#ifdef CONFIG_MMC_CLKGATE
+       .mmc_caps = MMC_CAP_HW_CLOCK_GATING | MMC_CAP_BUS_WIDTH_TEST,
+#else
+       .mmc_caps = MMC_CAP_BUS_WIDTH_TEST,
+#endif
+       .platform_probe = platform_pxa168_probe,
+       .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE
+               | SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+};
+EXPORT_SYMBOL_GPL(sdhci_platform_data);
+
+static int platform_pxa168_probe(struct sdhci_host *host)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+       struct sdhci_pxa_platdata *pdata = pxa->pdata;
+       struct sdhci_ops *p_ops;
+
+       host->quirks |= sdhci_platform_data.quirks;
+       host->mmc->caps |= sdhci_platform_data.mmc_caps;
+
+       /*
+        * we cannot directly copy our operations into host->ops
+        * since it is read only.  So we do this indirectly.
+        */
+       p_ops = kmalloc(sizeof(struct sdhci_ops), GFP_KERNEL);
+       if (!p_ops)
+               return -ENOMEM;
+
+       memcpy((void *)p_ops, (void *)&sdhci_platform_data.ops,
+               sizeof(struct sdhci_ops));
+
+       /* If slot design supports 8 bit data, indicate this to MMC. */
+       if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) {
+               host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+               p_ops->platform_8bit_width = platform_supports_8_bit;
+       }
+
+       if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
+               host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+               host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+       }
+
+       if (pxa->pdata->max_speed)
+               p_ops->get_f_max_clock = get_f_max_clock;
+
+       memcpy((void *)host->ops, (void *)p_ops, sizeof(struct sdhci_ops));
+       kfree(p_ops);
+       return 0;
+}
diff --git a/drivers/mmc/host/sdhci-pxa910.c b/drivers/mmc/host/sdhci-pxa910.c
new file mode 100644
index 0000000..282a1e0
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pxa910.c
@@ -0,0 +1,271 @@ 
+/**************************************************************************
+ *
+ * Copyright (c) 2009, 2010 Marvell International Ltd.
+ *                             Philip Rakity <prakity@marvell.com>
+ *                             Mark F. Brown <markb@marvell.com>
+ *
+ * This file is part of GNU program.
+ *
+ * GNU program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNU program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.
+ *
+ * If not, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ *************************************************************************/
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <plat/sdhci.h>
+#include "sdhci-pxa.h"
+#include "sdhci.h"
+
+#define DRIVER_NAME "sdhci-pxa910"
+
+#define SD_FIFO_PARAM          0xE0
+#define DIS_PAD_SD_CLK_GATE    (1<<10) /* Turn on/off Dynamic Clock Gating */
+
+#define SD_CLOCK_AND_BURST_SIZE_SETUP  0xE6
+#define SDCLK_DELAY_MASK       0xF
+#define SDCLK_SEL_MASK         0x3
+#define SDCLK_DELAY_SHIFT      10
+#define SDCLK_SEL_SHIFT                8
+
+#define SD_CE_ATA_2            0xEA
+#define SDCE_MMC_WIDTH         (1<<8)
+#define SDCE_MMC_CARD          (1<<12)
+
+#define DISABLE_CLOCK_GATING 0
+
+static int platform_pxa910_probe(struct sdhci_host *host);
+
+/*
+ * MMC spec calls for the host to send 74 clocks to the card
+ * during initialization, right after voltage stabilization.
+ * the pxa168 controller has no easy way to generate those clocks.
+ * create the clocks manually right here.
+ */
+#if 0
+static void generate_init_clocks(struct sdhci_host *host, u8 power_mode)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+       struct pfn_cfg *cfg = pxa->pdata->pfn_table;
+       mfp_cfg_t clk_pin;
+       int i;
+
+
+       if (cfg == NULL) {
+               DBG("Cannot generate init clocks!\n");
+               return;
+       }
+
+       if (pxa->power_mode == MMC_POWER_UP
+       && power_mode == MMC_POWER_ON) {
+
+               DBG("%s: ENTER %s: power_mode = %d, ios_.power_mode = %d\n",
+                       __func__,
+                       mmc_hostname(host->mmc),
+                       pxa->power_mode,
+                       power_mode);
+               /* CMD/CLK pin to gpio mode.  */
+               mfp_config(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CMD), 1);
+               mfp_config(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CLK), 1);
+
+               /* ensure at least 1/2 period stable to prevent runt pulse.*/
+               udelay(3);
+
+               clk_pin = *(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CLK));
+               if (gpio_request(MFP_PIN(clk_pin), "MMC_CLK")) {
+                       printk(KERN_ERR "Cannot obtain MMC_CLK GPIO %ld\n",
+                               MFP_PIN(clk_pin));
+                       goto err;
+               }
+
+               DBG("Generate 74 clocks on pins CLK %ld\n", MFP_PIN(clk_pin));
+
+               for (i = 0; i < INIT_CLOCKS; i++) {
+                       gpio_direction_output(MFP_PIN(clk_pin), 0); /* low */
+                       udelay(3);
+                       gpio_direction_output(MFP_PIN(clk_pin), 1); /* high */
+                       udelay(3);
+               }
+
+               gpio_free(MFP_PIN(clk_pin));
+       }
+
+err:
+       pxa->power_mode = power_mode;
+
+       /* CMD/CLK pin back MMC mode.   */
+       mfp_config(pfn_lookup(cfg, PFN_FN, PIN_MMC_CMD), 1);
+       mfp_config(pfn_lookup(cfg, PFN_FN, PIN_MMC_CLK), 1);
+}
+#endif
+
+static void set_clock_and_burst_size(struct sdhci_host *host)
+{
+       u16 tmp;
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+
+       if (pxa->pdata->adjust_clocks) {
+               tmp = readw(host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+               pr_debug("%s:%s: (B) SD_CLOCK_AND_BURST = %04X, "
+                       "delay = %d, sel = %d\n",
+                       __func__, mmc_hostname(host->mmc), tmp,
+                       pxa->pdata->clk_delay, pxa->pdata->clk_select);
+               tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
+               tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT);
+               tmp |= (pxa->pdata->clk_delay & SDCLK_DELAY_MASK) <<
+                       SDCLK_DELAY_SHIFT;
+               tmp |= (pxa->pdata->clk_select & SDCLK_SEL_MASK) <<
+                       SDCLK_SEL_SHIFT;
+               writew(tmp, host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+               pr_debug("%s:%s: (A) SD_CLOCK_AND_BURST = %04X\n",
+                       __func__, mmc_hostname(host->mmc), tmp);
+       }
+}
+
+static void programFIFO(struct sdhci_host *host, int enable)
+{
+       unsigned short tmp;
+
+       tmp = readw(host->ioaddr + SD_FIFO_PARAM);
+       if (enable)
+               tmp &= ~DIS_PAD_SD_CLK_GATE;
+       else
+               tmp |= DIS_PAD_SD_CLK_GATE;
+       writew(tmp, host->ioaddr + SD_FIFO_PARAM);
+}
+
+static void platform_reset_exit(struct sdhci_host *host, u8 mask)
+{
+       if (mask == SDHCI_RESET_ALL) {
+               programFIFO(host, DISABLE_CLOCK_GATING);
+               set_clock_and_burst_size(host);
+       }
+}
+
+#ifdef CONFIG_MMC_CLKGATE
+static void platform_hw_clk_gate(struct sdhci_host *host)
+{
+       int enable;
+
+       enable = host->mmc->clk_gated;
+       programFIFO(host, enable);
+}
+#endif
+
+static unsigned int get_max_clock(struct sdhci_host *host)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+
+       pr_debug("%s:%s clk_get_rate = %lu\n",
+                       __func__, mmc_hostname(host->mmc),
+                        clk_get_rate(pxa->clk));
+
+       return clk_get_rate(pxa->clk);
+}
+
+static unsigned int get_f_max_clock(struct sdhci_host *host)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+
+       pr_debug("%s:%s f_max = %d\n",
+                       __func__, mmc_hostname(host->mmc),
+                        pxa->pdata->max_speed);
+
+       return pxa->pdata->max_speed;
+}
+
+static int platform_supports_8_bit(struct sdhci_host *host, int width)
+{
+       u16 tmp;
+
+       tmp = readw(host->ioaddr + SD_CE_ATA_2);
+       tmp |= SDCE_MMC_CARD | SDCE_MMC_WIDTH;
+       writew(tmp, host->ioaddr + SD_CE_ATA_2);
+
+       tmp = readw(host->ioaddr + SD_CE_ATA_2);
+       if (width != 8)
+               tmp &= ~(SDCE_MMC_CARD | SDCE_MMC_WIDTH);
+       else
+               tmp |= SDCE_MMC_CARD | SDCE_MMC_WIDTH;
+       writew(tmp, host->ioaddr + SD_CE_ATA_2);
+       return 0;
+}
+
+struct sdhci_pxa_data sdhci_platform_data = {
+       .ops = {
+               .platform_reset_exit = platform_reset_exit,
+               .get_max_clock = get_max_clock,
+               .platform_send_init_74_clocks = NULL,
+               .get_f_max_clock = NULL,
+               .platform_8bit_width = NULL,
+#ifdef CONFIG_MMC_CLKGATE
+               .platform_hw_clk_gate = platform_hw_clk_gate,
+#endif
+       },
+#ifdef CONFIG_MMC_CLKGATE
+       .mmc_caps = MMC_CAP_HW_CLOCK_GATING | MMC_CAP_BUS_WIDTH_TEST,
+#else
+       .mmc_caps = MMC_CAP_BUS_WIDTH_TEST,
+#endif
+       .platform_probe = platform_pxa910_probe,
+       .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE
+               | SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+};
+EXPORT_SYMBOL_GPL(sdhci_platform_data);
+
+static int platform_pxa910_probe(struct sdhci_host *host)
+{
+       struct sdhci_pxa *pxa = sdhci_priv(host);
+       struct sdhci_pxa_platdata *pdata = pxa->pdata;
+       struct sdhci_ops *p_ops;
+
+       host->quirks |= sdhci_platform_data.quirks;
+       host->mmc->caps |= sdhci_platform_data.mmc_caps;
+
+       /*
+        * we cannot directly copy our operations into host->ops
+        * since it is read only.  So we do this indirectly.
+        */
+       p_ops = kmalloc(sizeof(struct sdhci_ops), GFP_KERNEL);
+       if (!p_ops)
+               return -ENOMEM;
+
+       memcpy((void *)p_ops, (void *)&sdhci_platform_data.ops,
+               sizeof(struct sdhci_ops));
+
+       /* If slot design supports 8 bit data, indicate this to MMC. */
+       if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) {
+               host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+               p_ops->platform_8bit_width = platform_supports_8_bit;
+       }
+
+       if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
+               host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+               host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+       }
+
+       if (pxa->pdata->max_speed)
+               p_ops->get_f_max_clock = get_f_max_clock;
+
+       memcpy((void *)host->ops, (void *)p_ops, sizeof(struct sdhci_ops));
+       kfree(p_ops);
+       return 0;
+}