Re: [PATCH] MMC driver full patch for Moorestown platform

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux