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?

On Thu, Jun 17, 2010 at 04:17:00PM +0100, Alan Cox wrote:
> 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
> 
> Signed-off-by: JiebingLi <[email protected]>
> Signed-off-by: Alan Cox <[email protected]>
> ---
> 
>  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;
> +     }
>  
> -     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;
> 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
> 
> --

-- 
Chris Ball   <[email protected]>   <http://printf.net/>
One Laptop Per Child
--
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

Reply via email to