[PATCH 2/3 v2] mmc: mmci: refactor ST Micro busy detection
Ulf Hansson
ulf.hansson at linaro.org
Mon Nov 7 04:43:14 PST 2016
On 25 October 2016 at 11:06, Linus Walleij <linus.walleij at linaro.org> wrote:
> The ST Micro-specific busy detection was made after the assumption
> that only this variant supports busy detection. So when doing busy
> detection, the host immediately tries to use some ST-specific
> register bits.
>
> Since the qualcomm variant also supports some busy detection
> schemes, encapsulate the variant flags better in the variant struct
> and prepare to add more variants by just providing some bitmasks
> to the logic.
>
> Put the entire busy detection logic within an if()-clause in the
> mmci_cmd_irq() function so the code is only executed when busy
> detection is enabled, and so that it is kept in (almost) one
> place, and add comments describing what is going on so the
> code can be understood.
>
> Tested on the Ux500 by introducing some prints in the busy
> detection path and noticing how the IRQ is enabled, used and
> disabled successfully.
>
> Cc: Srinivas Kandagatla <srinivas.kandagatla at linaro.org>
> Signed-off-by: Linus Walleij <linus.walleij at linaro.org>
Thanks, applied for next!
Kind regards
Uffe
> ---
> ChangeLog v1->v2:
> - Rebase on new register naming.
> ---
> drivers/mmc/host/mmci.c | 113 +++++++++++++++++++++++++++++++++++-------------
> drivers/mmc/host/mmci.h | 2 +-
> 2 files changed, 85 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index 6a8ea9c633d4..7f68fa7a961e 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -71,7 +71,12 @@ static unsigned int fmax = 515633;
> * @f_max: maximum clk frequency supported by the controller.
> * @signal_direction: input/out direction of bus signals can be indicated
> * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
> - * @busy_detect: true if busy detection on dat0 is supported
> + * @busy_detect: true if the variant supports busy detection on DAT0.
> + * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM
> + * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register
> + * indicating that the card is busy
> + * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for
> + * getting busy end detection interrupts
> * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
> * @explicit_mclk_control: enable explicit mclk control in driver.
> * @qcom_fifo: enables qcom specific fifo pio read logic.
> @@ -98,6 +103,9 @@ struct variant_data {
> bool signal_direction;
> bool pwrreg_clkgate;
> bool busy_detect;
> + u32 busy_dpsm_flag;
> + u32 busy_detect_flag;
> + u32 busy_detect_mask;
> bool pwrreg_nopower;
> bool explicit_mclk_control;
> bool qcom_fifo;
> @@ -178,6 +186,9 @@ static struct variant_data variant_ux500 = {
> .signal_direction = true,
> .pwrreg_clkgate = true,
> .busy_detect = true,
> + .busy_dpsm_flag = MCI_DPSM_ST_BUSYMODE,
> + .busy_detect_flag = MCI_ST_CARDBUSY,
> + .busy_detect_mask = MCI_ST_BUSYENDMASK,
> .pwrreg_nopower = true,
> };
>
> @@ -199,6 +210,9 @@ static struct variant_data variant_ux500v2 = {
> .signal_direction = true,
> .pwrreg_clkgate = true,
> .busy_detect = true,
> + .busy_dpsm_flag = MCI_DPSM_ST_BUSYMODE,
> + .busy_detect_flag = MCI_ST_CARDBUSY,
> + .busy_detect_mask = MCI_ST_BUSYENDMASK,
> .pwrreg_nopower = true,
> };
>
> @@ -220,6 +234,7 @@ static struct variant_data variant_qcom = {
> .qcom_dml = true,
> };
>
> +/* Busy detection for the ST Micro variant */
> static int mmci_card_busy(struct mmc_host *mmc)
> {
> struct mmci_host *host = mmc_priv(mmc);
> @@ -227,7 +242,7 @@ static int mmci_card_busy(struct mmc_host *mmc)
> int busy = 0;
>
> spin_lock_irqsave(&host->lock, flags);
> - if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY)
> + if (readl(host->base + MMCISTATUS) & host->variant->busy_detect_flag)
> busy = 1;
> spin_unlock_irqrestore(&host->lock, flags);
>
> @@ -294,8 +309,8 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
> */
> static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
> {
> - /* Keep ST Micro busy mode if enabled */
> - datactrl |= host->datactrl_reg & MCI_DPSM_ST_BUSYMODE;
> + /* Keep busy mode in DPSM if enabled */
> + datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag;
>
> if (host->datactrl_reg != datactrl) {
> host->datactrl_reg = datactrl;
> @@ -973,37 +988,66 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
> unsigned int status)
> {
> void __iomem *base = host->base;
> - bool sbc, busy_resp;
> + bool sbc;
>
> if (!cmd)
> return;
>
> sbc = (cmd == host->mrq->sbc);
> - busy_resp = host->variant->busy_detect && (cmd->flags & MMC_RSP_BUSY);
>
> - if (!((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
> - MCI_CMDSENT|MCI_CMDRESPEND)))
> + /*
> + * We need to be one of these interrupts to be considered worth
> + * handling. Note that we tag on any latent IRQs postponed
> + * due to waiting for busy status.
> + */
> + if (!((status|host->busy_status) &
> + (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND)))
> return;
>
> - /* Check if we need to wait for busy completion. */
> - if (host->busy_status && (status & MCI_ST_CARDBUSY))
> - return;
> + /*
> + * ST Micro variant: handle busy detection.
> + */
> + if (host->variant->busy_detect) {
> + bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY);
>
> - /* Enable busy completion if needed and supported. */
> - if (!host->busy_status && busy_resp &&
> - !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
> - (readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) {
> - writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND,
> - base + MMCIMASK0);
> - host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND);
> - return;
> - }
> + /* We are busy with a command, return */
> + if (host->busy_status &&
> + (status & host->variant->busy_detect_flag))
> + return;
> +
> + /*
> + * We were not busy, but we now got a busy response on
> + * something that was not an error, and we double-check
> + * that the special busy status bit is still set before
> + * proceeding.
> + */
> + if (!host->busy_status && busy_resp &&
> + !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
> + (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {
> + /* Unmask the busy IRQ */
> + writel(readl(base + MMCIMASK0) |
> + host->variant->busy_detect_mask,
> + base + MMCIMASK0);
> + /*
> + * Now cache the last response status code (until
> + * the busy bit goes low), and return.
> + */
> + host->busy_status =
> + status & (MCI_CMDSENT|MCI_CMDRESPEND);
> + return;
> + }
>
> - /* At busy completion, mask the IRQ and complete the request. */
> - if (host->busy_status) {
> - writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND,
> - base + MMCIMASK0);
> - host->busy_status = 0;
> + /*
> + * At this point we are not busy with a command, we have
> + * not recieved a new busy request, mask the busy IRQ and
> + * fall through to process the IRQ.
> + */
> + if (host->busy_status) {
> + writel(readl(base + MMCIMASK0) &
> + ~host->variant->busy_detect_mask,
> + base + MMCIMASK0);
> + host->busy_status = 0;
> + }
> }
>
> host->cmd = NULL;
> @@ -1257,9 +1301,11 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
> mmci_data_irq(host, host->data, status);
> }
>
> - /* Don't poll for busy completion in irq context. */
> - if (host->busy_status)
> - status &= ~MCI_ST_CARDBUSY;
> + /*
> + * Don't poll for busy completion in irq context.
> + */
> + if (host->variant->busy_detect && host->busy_status)
> + status &= ~host->variant->busy_detect_flag;
>
> ret = 1;
> } while (status);
> @@ -1612,9 +1658,18 @@ static int mmci_probe(struct amba_device *dev,
> /* We support these capabilities. */
> mmc->caps |= MMC_CAP_CMD23;
>
> + /*
> + * Enable busy detection.
> + */
> if (variant->busy_detect) {
> mmci_ops.card_busy = mmci_card_busy;
> - mmci_write_datactrlreg(host, MCI_DPSM_ST_BUSYMODE);
> + /*
> + * Not all variants have a flag to enable busy detection
> + * in the DPSM, but if they do, set it here.
> + */
> + if (variant->busy_dpsm_flag)
> + mmci_write_datactrlreg(host,
> + host->variant->busy_dpsm_flag);
> mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
> mmc->max_busy_timeout = 0;
> }
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index 7cabf270050b..56322c6afba4 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -174,7 +174,7 @@
> /* Extended status bits for the ST Micro variants */
> #define MCI_ST_SDIOITMASK (1 << 22)
> #define MCI_ST_CEATAENDMASK (1 << 23)
> -#define MCI_ST_BUSYEND (1 << 24)
> +#define MCI_ST_BUSYENDMASK (1 << 24)
>
> #define MMCIMASK1 0x040
> #define MMCIFIFOCNT 0x048
> --
> 2.7.4
>
More information about the linux-arm-kernel
mailing list