Skip to contentSkip to conversation

MMC driver full patch for Moorestown platform

PATCH In review Alan Cox 4 participants 4 messages Jun 17, 2010 – Aug 27, 2010

Subsystems (3)
lore
Share & tools
lore

Alan Cox

author
#0 lore raw

PATCH 1/1 MMC driver full patch for Moorestown platform

From: JiebingLi <[email protected]>

This patch enables Moorestown Langwell A-3 platform.

The main thrust of this is adding support for serialization of devices as we do on various other bits of hardware with bugs. We also deal with spurious interrupts and clock rewriting.

The PCI handling code is slightly tweaked so that the driver specific methods can override the number of slots, and a method provided so devices can indicate they only support one slot even if the PCI config says otherwise.

The serialization support is as clean as we can see how to make it and as general as possible - with just one pair of tiny changes to the core code that can be used by any future driver with such limits.

Quirks tidied up by Alan Cox

---

 drivers/mmc/core/core.c      |    6 ++
 drivers/mmc/host/sdhci-pci.c |   48 ++++++++++++++++++-
 drivers/mmc/host/sdhci.c     |  108 ++++++++++++++++++++++++++++++++++--------
 drivers/mmc/host/sdhci.h     |    7 +++
 include/linux/mmc/host.h     |    2 +
 include/linux/pci_ids.h      |    2 +
 6 files changed, 151 insertions(+), 22 deletions(-)
drivers/mmc/core/core.c +6 -0
index 569e94d..10d1c8d 100644
@@ -213,9 +213,15 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
 	mrq->done_data = &complete;
 	mrq->done = mmc_wait_done;
 
+	if (host->port_mutex)
+		mutex_lock(host->port_mutex);
+
 	mmc_start_request(host, mrq);
 
 	wait_for_completion(&complete);
+
+	if (host->port_mutex)
+		mutex_unlock(host->port_mutex);
 }
 
 EXPORT_SYMBOL(mmc_wait_for_req);
drivers/mmc/host/sdhci-pci.c +47 -1

84 lines not on this page · raw message

drivers/mmc/host/sdhci.c +87 -21

180 lines not on this page · raw message

drivers/mmc/host/sdhci.h +7 -0
index c846813..cf96152 100644
@@ -67,6 +67,7 @@
 #define  SDHCI_CTRL_LED		0x01
 #define  SDHCI_CTRL_4BITBUS	0x02
 #define  SDHCI_CTRL_HISPD	0x04
+#define  SDHCI_CTRL_8BITBUS	0x20
 #define  SDHCI_CTRL_DMA_MASK	0x18
 #define   SDHCI_CTRL_SDMA	0x00
 #define   SDHCI_CTRL_ADMA1	0x08
@@ -240,6 +241,12 @@ struct sdhci_host {
 #define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN		(1<<25)
 /* Controller cannot support End Attribute in NOP ADMA descriptor */
 #define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC		(1<<26)
+/* Controller can only handle full speed mode */
+#define SDHCI_QUIRK_FORCE_FULL_SPEED_MODE		(1<<27)
+/* Controller has an issue with software reset all function */
+#define SDHCI_QUIRK_BROKEN_RESETALL			(1<<28)
+/* Controller has an issue when its two slots enabled together */
+#define SDHCI_QUIRK_SERIALIZE				(1<<29)
 
 	int			irq;		/* Device IRQ */
 	void __iomem *		ioaddr;		/* Mapped address */
include/linux/mmc/host.h +2 -0
index f65913c..648740e 100644
@@ -211,6 +211,8 @@ struct mmc_host {
 
 	struct dentry		*debugfs_root;
 
+	struct mutex		*port_mutex;
+
 	unsigned long		private[0] ____cacheline_aligned;
 };
 
include/linux/pci_ids.h +2 -0
index 4eb4679..f5ebea1 100644
@@ -2398,6 +2398,8 @@
 #define PCI_DEVICE_ID_INTEL_82375	0x0482
 #define PCI_DEVICE_ID_INTEL_82424	0x0483
 #define PCI_DEVICE_ID_INTEL_82378	0x0484
+#define PCI_DEVICE_ID_INTEL_MRST_SD0	0x0807
+#define PCI_DEVICE_ID_INTEL_MRST_SD1	0x0808
 #define PCI_DEVICE_ID_INTEL_I960	0x0960
 #define PCI_DEVICE_ID_INTEL_I960RM	0x0962
 #define PCI_DEVICE_ID_INTEL_8257X_SOL	0x1062
#1 lore raw
Quoted 264 lines · Alan Cox

From: JiebingLi <[email protected]>

This patch enables Moorestown Langwell A-3 platform.

The main thrust of this is adding support for serialization of devices as we do on various other bits of hardware with bugs. We also deal with spurious interrupts and clock rewriting.

The PCI handling code is slightly tweaked so that the driver specific methods can override the number of slots, and a method provided so devices can indicate they only support one slot even if the PCI config says otherwise.

The serialization support is as clean as we can see how to make it and as general as possible - with just one pair of tiny changes to the core code that can be used by any future driver with such limits.

Quirks tidied up by Alan Cox

---

 drivers/mmc/core/core.c      |    6 ++ drivers/mmc/host/sdhci-pci.c |   48 ++++++++++++++++++-

 drivers/mmc/host/sdhci.c     |  108 ++++++++++++++++++++++++++++++++++--------
 drivers/mmc/host/sdhci.h     |    7 +++
 include/linux/mmc/host.h     |    2 +
 include/linux/pci_ids.h      |    2 +
 6 files changed, 151 insertions(+), 22 deletions(-)
drivers/mmc/core/core.c +6 -0
index 569e94d..10d1c8d 100644
@@ -213,9 +213,15 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
        mrq->done_data = &complete;
        mrq->done = mmc_wait_done;
 
+       if (host->port_mutex)
+               mutex_lock(host->port_mutex);
+
        mmc_start_request(host, mrq);
 
        wait_for_completion(&complete);
+
+       if (host->port_mutex)
+               mutex_unlock(host->port_mutex);
  }
 
  EXPORT_SYMBOL(mmc_wait_for_req);
drivers/mmc/host/sdhci-pci.c +42 -0
index 65483fd..9ce725b 100644
@@ -39,6 +39,8 @@
 
  #define MAX_SLOTS                      8
 
+static DEFINE_MUTEX(port_mutex);
+
  struct sdhci_pci_chip;
  struct sdhci_pci_slot;
 
@@ -364,6 +366,30 @@ static const struct sdhci_pci_fixes sdhci_via = {
        .probe          = via_probe,
  };
 
+static int single_slot(struct sdhci_pci_chip *chip)
+{
+       chip->num_slots = 1;
+       return 0;
+}
+
+/*
+ * ADMA operation is disabled for Moorestown platform due to
+ * hardware bugs.
+ */
+static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
+       .quirks         = SDHCI_QUIRK_BROKEN_ADMA |
+                         SDHCI_QUIRK_SERIALIZE |
+                         SDHCI_QUIRK_BROKEN_RESETALL |
+                         SDHCI_QUIRK_FORCE_FULL_SPEED_MODE,
+};
+
+static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1 = {
+       .quirks         = SDHCI_QUIRK_BROKEN_ADMA |
+                         SDHCI_QUIRK_BROKEN_RESETALL |
+                         SDHCI_QUIRK_FORCE_FULL_SPEED_MODE,
+       .probe          = single_slot
+};
+
  static const struct pci_device_id pci_ids[] __devinitdata = {
        {
                .vendor         = PCI_VENDOR_ID_RICOH,
@@ -445,6 +471,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
                .driver_data    = (kernel_ulong_t)&sdhci_via,
        },
 
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_MRST_SD0,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_mrst_hc0,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_MRST_SD1,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_mrst_hc1,
+       },
+

       {       /* Generic SD host controller */ PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) },

drivers/mmc/host/sdhci-pci.c +5 -1
@@ -643,6 +685,9 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
 
        host->irq = pdev->irq;
 
+       if (host->quirks & SDHCI_QUIRK_SERIALIZE)
+               host->mmc->port_mutex = &port_mutex;
+
        ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc));
        if (ret) {
                dev_err(&pdev->dev, "cannot request region\n");
@@ -728,6 +773,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
                return ret;
 
        slots = PCI_SLOT_INFO_SLOTS(slots) + 1;
+
        dev_dbg(&pdev->dev, "found %d slot(s)\n", slots);
        if (slots == 0)
                return -ENODEV;
@@ -769,7 +815,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
                        goto free;
        }
 
-       for (i = 0;i < slots;i++) {
+       for (i = 0;i < chip->num_slots;i++) {
                slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i);
                if (IS_ERR(slot)) {
                        for (i--;i >= 0;i--)
drivers/mmc/host/sdhci.c +18 -7
index c6d1bd8..e720106 100644
@@ -162,9 +162,11 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
        /* hw clears the bit when it's done */
        while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
                if (timeout == 0) {
-                       printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
-                               mmc_hostname(host->mmc), (int)mask);
-                       sdhci_dumpregs(host);
+                       if (!(host->quirks & SDHCI_QUIRK_BROKEN_RESETALL)) {
+                               printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
+                                       mmc_hostname(host->mmc), (int)mask);
+                               sdhci_dumpregs(host);
+                       }
                        return;
                }
                timeout--;
@@ -179,10 +181,19 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
 
  static void sdhci_init(struct sdhci_host *host, int soft)
  {
-       if (soft)
-               sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
-       else
-               sdhci_reset(host, SDHCI_RESET_ALL);
+       u32 intmask;
+
+       intmask = sdhci_readl(host, SDHCI_INT_STATUS);
+       sdhci_writel(host,
+               intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
+               SDHCI_INT_STATUS);
+
+       if (!(host->quirks & SDHCI_QUIRK_BROKEN_RESETALL)) {
+               if (soft)
+                       sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
+               else
+                       sdhci_reset(host, SDHCI_RESET_ALL);
+       }
 
        sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,

               SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |

drivers/mmc/host/sdhci.c +38 -5
@@ -195,6 +206,9 @@ static void sdhci_init(struct sdhci_host *host, int soft)
                host->clock = 0;
                sdhci_set_ios(host->mmc, &host->mmc->ios);
        }
+
+       /* disable wakeup signal during initialization */
+       sdhci_writeb(host, 0x0, SDHCI_WAKE_UP_CONTROL);
  }
 
  static void sdhci_reinit(struct sdhci_host *host)
@@ -625,11 +639,8 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data)
                        break;
        }
 
-       if (count >= 0xF) {
-               printk(KERN_WARNING "%s: Too large timeout requested!\n",
-                       mmc_hostname(host->mmc));
+       if (count >= 0xF)
                count = 0xE;
-       }
 
        return count;
  }
@@ -873,6 +884,36 @@ static void sdhci_finish_data(struct sdhci_host *host)
                tasklet_schedule(&host->finish_tasklet);
  }
 
+/*
+ * HW problem exists in LNW A3 so clock register has to be set
+ * for every command if both SDIO0 and SDIO1 are enabled.
+ */
+static void sdhci_clock_reset(struct sdhci_host *host)
+{
+       u16 clk;
+       unsigned long timeout;
+
+       clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+       clk |= SDHCI_CLOCK_CARD_EN;
+       sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+       /* Wait max 10 ms */
+       timeout = 10;
+       while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+               & SDHCI_CLOCK_INT_STABLE)) {
+               if (timeout == 0) {
+                       printk(KERN_ERR "%s: Internal clock never "
+                               "stabilised.\n",
+                               mmc_hostname(host->mmc));
+                       sdhci_dumpregs(host);
+                       return;
+               }
+               timeout--;
+               mdelay(1);
+       }
+}
+
  static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
  {
        int flags;
@@ -940,6 +981,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
        if (cmd->data)
                flags |= SDHCI_CMD_DATA;
 
+       if (host->quirks & SDHCI_QUIRK_SERIALIZE)
+               sdhci_clock_reset(host);
+
        sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
  }
 
@@ -1159,12 +1203,23 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
        ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 
-       if (ios->bus_width == MMC_BUS_WIDTH_4)
+       if (ios->bus_width == MMC_BUS_WIDTH_8) {

drivers/mmc/host/sdhci.c static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) · line 1215

Quoted 10 lines
drivers/mmc/host/sdhci.c +7 -1
+               ctrl |= SDHCI_CTRL_8BITBUS;
+               ctrl &= ~SDHCI_CTRL_4BITBUS;
+       } else if (ios->bus_width == MMC_BUS_WIDTH_4) {
+               ctrl &= ~SDHCI_CTRL_8BITBUS;
                ctrl |= SDHCI_CTRL_4BITBUS;
-       else
+       } else {
+               ctrl &= ~SDHCI_CTRL_8BITBUS;
                ctrl &= ~SDHCI_CTRL_4BITBUS;
+       }

In the previous patch. I committed the 8-bit support. If this patch is merged. there's conflict with previous patch.

Quoted 11 lines
drivers/mmc/host/sdhci.c +6 -1
-       if (ios->timing == MMC_TIMING_SD_HS)
+/*
+ * For LNW A3, HISPD bit has to be cleared in order to enable 50MHz clock
+ */
+       if (!(host->quirks & SDHCI_QUIRK_FORCE_FULL_SPEED_MODE) &&
+               (ios->timing == MMC_TIMING_SD_HS ||
+               ios->timing == MMC_TIMING_MMC_HS))
                ctrl |= SDHCI_CTRL_HISPD;

       else
               ctrl &= ~SDHCI_CTRL_HISPD;

it's also added. no HISPD support. of course ths situation is different. in case of s5pc110 there's no HISPD bit at controller so I added no HISPD quirks, but in your case, it has HISPD but you don't want to use it.

Thank you,
Kyungmin Park

Quoted 112 lines
drivers/mmc/host/sdhci.c +17 -6
@@ -1365,11 +1420,18 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
  {
        BUG_ON(intmask == 0);
 
+       /*
+        * Intel MRST:
+        * HW problem exists in LNW A3 which leads to fake interrupt on SDIO1
+        * if SDIO0 and SDIO1 are both enabled.
+        */
        if (!host->cmd) {
-               printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
-                       "though no command operation was in progress.\n",
-                       mmc_hostname(host->mmc), (unsigned)intmask);
-               sdhci_dumpregs(host);
+               if (!(host->quirks & SDHCI_QUIRK_SERIALIZE)) {
+                       printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
+                               "though no command operation in progress.\n",
+                               mmc_hostname(host->mmc), (unsigned)intmask);
+                       sdhci_dumpregs(host);
+               }
                return;
        }
 
@@ -1676,7 +1738,8 @@ int sdhci_add_host(struct sdhci_host *host)
        if (debug_quirks)
                host->quirks = debug_quirks;
 
-       sdhci_reset(host, SDHCI_RESET_ALL);
+       if (!(host->quirks & SDHCI_QUIRK_BROKEN_RESETALL))
+               sdhci_reset(host, SDHCI_RESET_ALL);
 
        host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
        host->version = (host->version & SDHCI_SPEC_VER_MASK)
@@ -1796,8 +1859,11 @@ int sdhci_add_host(struct sdhci_host *host)
        if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
                mmc->caps |= MMC_CAP_4_BIT_DATA;
 
+       if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
+               mmc->caps |= MMC_CAP_8_BIT_DATA;
+
        if (caps & SDHCI_CAN_DO_HISPD)
-               mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+               mmc->caps |= (MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED);
 
        if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
                mmc->caps |= MMC_CAP_NEEDS_POLL;
@@ -1855,7 +1921,7 @@ int sdhci_add_host(struct sdhci_host *host)
        } else {

               mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;
-               if (mmc->max_blk_size >= 3) {
+               if (mmc->max_blk_size > 3) {
                       printk(KERN_WARNING "%s: Invalid maximum block size, " "assuming 512 bytes\n", mmc_hostname(mmc)); mmc->max_blk_size = 0;

drivers/mmc/host/sdhci.h +7 -0
index c846813..cf96152 100644
@@ -67,6 +67,7 @@
  #define  SDHCI_CTRL_LED                0x01
  #define  SDHCI_CTRL_4BITBUS    0x02
  #define  SDHCI_CTRL_HISPD      0x04
+#define  SDHCI_CTRL_8BITBUS    0x20
  #define  SDHCI_CTRL_DMA_MASK   0x18
  #define   SDHCI_CTRL_SDMA      0x00
  #define   SDHCI_CTRL_ADMA1     0x08
@@ -240,6 +241,12 @@ struct sdhci_host {
  #define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN              (1<<25)
  /* Controller cannot support End Attribute in NOP ADMA descriptor */
  #define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC              (1<<26)
+/* Controller can only handle full speed mode */
+#define SDHCI_QUIRK_FORCE_FULL_SPEED_MODE              (1<<27)
+/* Controller has an issue with software reset all function */
+#define SDHCI_QUIRK_BROKEN_RESETALL                    (1<<28)
+/* Controller has an issue when its two slots enabled together */
+#define SDHCI_QUIRK_SERIALIZE                          (1<<29)
 

       int                     irq;            /* Device IRQ */ void __iomem *          ioaddr;         /* Mapped address */

include/linux/mmc/host.h +2 -0
index f65913c..648740e 100644
@@ -211,6 +211,8 @@ struct mmc_host {
 
        struct dentry           *debugfs_root;
 
+       struct mutex            *port_mutex;
+
        unsigned long           private[0] ____cacheline_aligned;
  };
 
include/linux/pci_ids.h +2 -1
index 4eb4679..f5ebea1 100644
@@ -2398,6 +2398,8 @@
  #define PCI_DEVICE_ID_INTEL_82375      0x0482
  #define PCI_DEVICE_ID_INTEL_82424      0x0483
  #define PCI_DEVICE_ID_INTEL_82378      0x0484
+#define PCI_DEVICE_ID_INTEL_MRST_SD0   0x0807
+#define PCI_DEVICE_ID_INTEL_MRST_SD1   0x0808
  #define PCI_DEVICE_ID_INTEL_I960       0x0960
  #define PCI_DEVICE_ID_INTEL_I960RM     0x0962
  #define PCI_DEVICE_ID_INTEL_8257X_SOL  0x1062
 
--

To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

#2 lore raw
In reply to Alan Cox
Quoted 8 lines

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Alan Cox
Sent: Thursday, June 17, 2010 11:17 PM
To: [email protected]; [email protected]
Subject: [PATCH] MMC driver full patch for Moorestown platform
...

drivers/mmc/host/sdhci.h struct sdhci_host { · line 249

Quoted 9 lines
drivers/mmc/host/sdhci.h +6 -0
@@ -240,6 +241,12 @@ struct sdhci_host {
 #define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN		(1<<25)
 /* Controller cannot support End Attribute in NOP ADMA descriptor */
 #define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC		(1<<26)
+/* Controller can only handle full speed mode */
+#define SDHCI_QUIRK_FORCE_FULL_SPEED_MODE		(1<<27)
+/* Controller has an issue with software reset all function */
+#define SDHCI_QUIRK_BROKEN_RESETALL			(1<<28)
+/* Controller has an issue when its two slots enabled together */
+#define SDHCI_QUIRK_SERIALIZE				(1<<29)

The 'quirks' is a 32bit variable, but now bit0 - bit29 has already been used. And I think the bit30 and bit 31 will be used soon. So, after that, how to add a new quirk? Do we need to change the 'quirk' variable to 64bit?

#3 lore raw
In reply to Alan Cox

Hi,

This wasn't merged -- Kyungmin Park pointed out a conflict with other newly-added 8-bit support. Alan (or Kyungmin?), are you interested in rebasing and sending to Andrew?

Quoted 393 lines · Alan Cox

From: JiebingLi <[email protected]>

This patch enables Moorestown Langwell A-3 platform.

The main thrust of this is adding support for serialization of devices as we do on various other bits of hardware with bugs. We also deal with spurious interrupts and clock rewriting.

The PCI handling code is slightly tweaked so that the driver specific methods can override the number of slots, and a method provided so devices can indicate they only support one slot even if the PCI config says otherwise.

The serialization support is as clean as we can see how to make it and as general as possible - with just one pair of tiny changes to the core code that can be used by any future driver with such limits.

Quirks tidied up by Alan Cox

---

 drivers/mmc/core/core.c      |    6 ++
 drivers/mmc/host/sdhci-pci.c |   48 ++++++++++++++++++-
 drivers/mmc/host/sdhci.c     |  108 ++++++++++++++++++++++++++++++++++--------
 drivers/mmc/host/sdhci.h     |    7 +++
 include/linux/mmc/host.h     |    2 +
 include/linux/pci_ids.h      |    2 +
 6 files changed, 151 insertions(+), 22 deletions(-)
drivers/mmc/core/core.c +6 -0
index 569e94d..10d1c8d 100644
@@ -213,9 +213,15 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
 	mrq->done_data = &complete;
 	mrq->done = mmc_wait_done;
 
+	if (host->port_mutex)
+		mutex_lock(host->port_mutex);
+
 	mmc_start_request(host, mrq);
 
 	wait_for_completion(&complete);
+
+	if (host->port_mutex)
+		mutex_unlock(host->port_mutex);
 }
 
 EXPORT_SYMBOL(mmc_wait_for_req);
drivers/mmc/host/sdhci-pci.c +47 -1
index 65483fd..9ce725b 100644
@@ -39,6 +39,8 @@
 
 #define MAX_SLOTS			8
 
+static DEFINE_MUTEX(port_mutex);
+
 struct sdhci_pci_chip;
 struct sdhci_pci_slot;
 
@@ -364,6 +366,30 @@ static const struct sdhci_pci_fixes sdhci_via = {
 	.probe		= via_probe,
 };
 
+static int single_slot(struct sdhci_pci_chip *chip)
+{
+	chip->num_slots = 1;
+	return 0;
+}
+
+/*
+ * ADMA operation is disabled for Moorestown platform due to
+ * hardware bugs.
+ */
+static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
+	.quirks		= SDHCI_QUIRK_BROKEN_ADMA |
+			  SDHCI_QUIRK_SERIALIZE |
+			  SDHCI_QUIRK_BROKEN_RESETALL |
+			  SDHCI_QUIRK_FORCE_FULL_SPEED_MODE,
+};
+
+static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1 = {
+	.quirks		= SDHCI_QUIRK_BROKEN_ADMA |
+			  SDHCI_QUIRK_BROKEN_RESETALL |
+			  SDHCI_QUIRK_FORCE_FULL_SPEED_MODE,
+	.probe		= single_slot
+};
+
 static const struct pci_device_id pci_ids[] __devinitdata = {
 	{
 		.vendor		= PCI_VENDOR_ID_RICOH,
@@ -445,6 +471,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
 		.driver_data	= (kernel_ulong_t)&sdhci_via,
 	},
 
+	{
+		.vendor		= PCI_VENDOR_ID_INTEL,
+		.device		= PCI_DEVICE_ID_INTEL_MRST_SD0,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= (kernel_ulong_t)&sdhci_intel_mrst_hc0,
+	},
+
+	{
+		.vendor		= PCI_VENDOR_ID_INTEL,
+		.device		= PCI_DEVICE_ID_INTEL_MRST_SD1,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= (kernel_ulong_t)&sdhci_intel_mrst_hc1,
+	},
+
 	{	/* Generic SD host controller */
 		PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
 	},
@@ -643,6 +685,9 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
 
 	host->irq = pdev->irq;
 
+	if (host->quirks & SDHCI_QUIRK_SERIALIZE)
+		host->mmc->port_mutex = &port_mutex;
+
 	ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc));
 	if (ret) {
 		dev_err(&pdev->dev, "cannot request region\n");
@@ -728,6 +773,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
 		return ret;
 
 	slots = PCI_SLOT_INFO_SLOTS(slots) + 1;
+
 	dev_dbg(&pdev->dev, "found %d slot(s)\n", slots);
 	if (slots == 0)
 		return -ENODEV;
@@ -769,7 +815,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
 			goto free;
 	}
 
-	for (i = 0;i < slots;i++) {
+	for (i = 0;i < chip->num_slots;i++) {
 		slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i);
 		if (IS_ERR(slot)) {
 			for (i--;i >= 0;i--)
drivers/mmc/host/sdhci.c +87 -21
index c6d1bd8..e720106 100644
@@ -162,9 +162,11 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
 	/* hw clears the bit when it's done */
 	while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
 		if (timeout == 0) {
-			printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
-				mmc_hostname(host->mmc), (int)mask);
-			sdhci_dumpregs(host);
+			if (!(host->quirks & SDHCI_QUIRK_BROKEN_RESETALL)) {
+				printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
+					mmc_hostname(host->mmc), (int)mask);
+				sdhci_dumpregs(host);
+			}
 			return;
 		}
 		timeout--;
@@ -179,10 +181,19 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
 
 static void sdhci_init(struct sdhci_host *host, int soft)
 {
-	if (soft)
-		sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
-	else
-		sdhci_reset(host, SDHCI_RESET_ALL);
+	u32 intmask;
+
+	intmask = sdhci_readl(host, SDHCI_INT_STATUS);
+	sdhci_writel(host,
+		intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
+		SDHCI_INT_STATUS);
+
+	if (!(host->quirks & SDHCI_QUIRK_BROKEN_RESETALL)) {
+		if (soft)
+			sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
+		else
+			sdhci_reset(host, SDHCI_RESET_ALL);
+	}
 
 	sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
 		SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
@@ -195,6 +206,9 @@ static void sdhci_init(struct sdhci_host *host, int soft)
 		host->clock = 0;
 		sdhci_set_ios(host->mmc, &host->mmc->ios);
 	}
+
+	/* disable wakeup signal during initialization */
+	sdhci_writeb(host, 0x0, SDHCI_WAKE_UP_CONTROL);
 }
 
 static void sdhci_reinit(struct sdhci_host *host)
@@ -625,11 +639,8 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data)
 			break;
 	}
 
-	if (count >= 0xF) {
-		printk(KERN_WARNING "%s: Too large timeout requested!\n",
-			mmc_hostname(host->mmc));
+	if (count >= 0xF)
 		count = 0xE;
-	}
 
 	return count;
 }
@@ -873,6 +884,36 @@ static void sdhci_finish_data(struct sdhci_host *host)
 		tasklet_schedule(&host->finish_tasklet);
 }
 
+/*
+ * HW problem exists in LNW A3 so clock register has to be set
+ * for every command if both SDIO0 and SDIO1 are enabled.
+ */
+static void sdhci_clock_reset(struct sdhci_host *host)
+{
+	u16 clk;
+	unsigned long timeout;
+
+	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+	clk |= SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	/* Wait max 10 ms */
+	timeout = 10;
+	while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+		& SDHCI_CLOCK_INT_STABLE)) {
+		if (timeout == 0) {
+			printk(KERN_ERR "%s: Internal clock never "
+				"stabilised.\n",
+				mmc_hostname(host->mmc));
+			sdhci_dumpregs(host);
+			return;
+		}
+		timeout--;
+		mdelay(1);
+	}
+}
+
 static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 {
 	int flags;
@@ -940,6 +981,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 	if (cmd->data)
 		flags |= SDHCI_CMD_DATA;
 
+	if (host->quirks & SDHCI_QUIRK_SERIALIZE)
+		sdhci_clock_reset(host);
+
 	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
 }
 
@@ -1159,12 +1203,23 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
 	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 
-	if (ios->bus_width == MMC_BUS_WIDTH_4)
+	if (ios->bus_width == MMC_BUS_WIDTH_8) {
+		ctrl |= SDHCI_CTRL_8BITBUS;
+		ctrl &= ~SDHCI_CTRL_4BITBUS;
+	} else if (ios->bus_width == MMC_BUS_WIDTH_4) {
+		ctrl &= ~SDHCI_CTRL_8BITBUS;
 		ctrl |= SDHCI_CTRL_4BITBUS;
-	else
+	} else {
+		ctrl &= ~SDHCI_CTRL_8BITBUS;
 		ctrl &= ~SDHCI_CTRL_4BITBUS;
+	}
 
-	if (ios->timing == MMC_TIMING_SD_HS)
+/*
+ * For LNW A3, HISPD bit has to be cleared in order to enable 50MHz clock
+ */
+	if (!(host->quirks & SDHCI_QUIRK_FORCE_FULL_SPEED_MODE) &&
+		(ios->timing == MMC_TIMING_SD_HS ||
+		ios->timing == MMC_TIMING_MMC_HS))
 		ctrl |= SDHCI_CTRL_HISPD;
 	else
 		ctrl &= ~SDHCI_CTRL_HISPD;
@@ -1365,11 +1420,18 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
 {
 	BUG_ON(intmask == 0);
 
+	/*
+	 * Intel MRST:
+	 * HW problem exists in LNW A3 which leads to fake interrupt on SDIO1
+	 * if SDIO0 and SDIO1 are both enabled.
+	 */
 	if (!host->cmd) {
-		printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
-			"though no command operation was in progress.\n",
-			mmc_hostname(host->mmc), (unsigned)intmask);
-		sdhci_dumpregs(host);
+		if (!(host->quirks & SDHCI_QUIRK_SERIALIZE)) {
+			printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
+				"though no command operation in progress.\n",
+				mmc_hostname(host->mmc), (unsigned)intmask);
+			sdhci_dumpregs(host);
+		}
 		return;
 	}
 
@@ -1676,7 +1738,8 @@ int sdhci_add_host(struct sdhci_host *host)
 	if (debug_quirks)
 		host->quirks = debug_quirks;
 
-	sdhci_reset(host, SDHCI_RESET_ALL);
+	if (!(host->quirks & SDHCI_QUIRK_BROKEN_RESETALL))
+		sdhci_reset(host, SDHCI_RESET_ALL);
 
 	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
 	host->version = (host->version & SDHCI_SPEC_VER_MASK)
@@ -1796,8 +1859,11 @@ int sdhci_add_host(struct sdhci_host *host)
 	if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
 		mmc->caps |= MMC_CAP_4_BIT_DATA;
 
+	if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
+		mmc->caps |= MMC_CAP_8_BIT_DATA;
+
 	if (caps & SDHCI_CAN_DO_HISPD)
-		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+		mmc->caps |= (MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED);
 
 	if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
 		mmc->caps |= MMC_CAP_NEEDS_POLL;
@@ -1855,7 +1921,7 @@ int sdhci_add_host(struct sdhci_host *host)
 	} else {
 		mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
 				SDHCI_MAX_BLOCK_SHIFT;
-		if (mmc->max_blk_size >= 3) {
+		if (mmc->max_blk_size > 3) {
 			printk(KERN_WARNING "%s: Invalid maximum block size, "
 				"assuming 512 bytes\n", mmc_hostname(mmc));
 			mmc->max_blk_size = 0;
drivers/mmc/host/sdhci.h +7 -0
index c846813..cf96152 100644
@@ -67,6 +67,7 @@
 #define  SDHCI_CTRL_LED		0x01
 #define  SDHCI_CTRL_4BITBUS	0x02
 #define  SDHCI_CTRL_HISPD	0x04
+#define  SDHCI_CTRL_8BITBUS	0x20
 #define  SDHCI_CTRL_DMA_MASK	0x18
 #define   SDHCI_CTRL_SDMA	0x00
 #define   SDHCI_CTRL_ADMA1	0x08
@@ -240,6 +241,12 @@ struct sdhci_host {
 #define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN		(1<<25)
 /* Controller cannot support End Attribute in NOP ADMA descriptor */
 #define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC		(1<<26)
+/* Controller can only handle full speed mode */
+#define SDHCI_QUIRK_FORCE_FULL_SPEED_MODE		(1<<27)
+/* Controller has an issue with software reset all function */
+#define SDHCI_QUIRK_BROKEN_RESETALL			(1<<28)
+/* Controller has an issue when its two slots enabled together */
+#define SDHCI_QUIRK_SERIALIZE				(1<<29)
 
 	int			irq;		/* Device IRQ */
 	void __iomem *		ioaddr;		/* Mapped address */
include/linux/mmc/host.h +2 -0
index f65913c..648740e 100644
@@ -211,6 +211,8 @@ struct mmc_host {
 
 	struct dentry		*debugfs_root;
 
+	struct mutex		*port_mutex;
+
 	unsigned long		private[0] ____cacheline_aligned;
 };
 
include/linux/pci_ids.h +2 -1
index 4eb4679..f5ebea1 100644
@@ -2398,6 +2398,8 @@
 #define PCI_DEVICE_ID_INTEL_82375	0x0482
 #define PCI_DEVICE_ID_INTEL_82424	0x0483
 #define PCI_DEVICE_ID_INTEL_82378	0x0484
+#define PCI_DEVICE_ID_INTEL_MRST_SD0	0x0807
+#define PCI_DEVICE_ID_INTEL_MRST_SD1	0x0808
 #define PCI_DEVICE_ID_INTEL_I960	0x0960
 #define PCI_DEVICE_ID_INTEL_I960RM	0x0962
 #define PCI_DEVICE_ID_INTEL_8257X_SOL	0x1062
 
--
Signature · 2 lines
Chris Ball   <[email protected]>   <http://printf.net/>
One Laptop Per Child