On Fri, Jun 18, 2010 at 12:17 AM, Alan Cox <alan@xxxxxxxxxxxxxxx> wrote: > From: JiebingLi <jiebing.li@xxxxxxxxx> > > 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 > > Signed-off-by: JiebingLi <jiebing.li@xxxxxxxxx> > Signed-off-by: Alan Cox <alan@xxxxxxxxxxxxxxx> > --- > > 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(-) > > > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c > index 569e94d..10d1c8d 100644 > --- a/drivers/mmc/core/core.c > +++ b/drivers/mmc/core/core.c > @@ -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); > diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c > index 65483fd..9ce725b 100644 > --- a/drivers/mmc/host/sdhci-pci.c > +++ b/drivers/mmc/host/sdhci-pci.c > @@ -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--) > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c > index c6d1bd8..e720106 100644 > --- a/drivers/mmc/host/sdhci.c > +++ b/drivers/mmc/host/sdhci.c > @@ -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; > + } In the previous patch. I committed the 8-bit support. If this patch is merged. there's conflict with previous patch. > > - 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 > @@ -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; > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h > index c846813..cf96152 100644 > --- a/drivers/mmc/host/sdhci.h > +++ b/drivers/mmc/host/sdhci.h > @@ -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 */ > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h > index f65913c..648740e 100644 > --- a/include/linux/mmc/host.h > +++ b/include/linux/mmc/host.h > @@ -211,6 +211,8 @@ struct mmc_host { > > struct dentry *debugfs_root; > > + struct mutex *port_mutex; > + > unsigned long private[0] ____cacheline_aligned; > }; > > diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h > index 4eb4679..f5ebea1 100644 > --- a/include/linux/pci_ids.h > +++ b/include/linux/pci_ids.h > @@ -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 majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html