Commit cb0335b7 authored by Ludovic Barre's avatar Ludovic Barre Committed by Ulf Hansson
Browse files

mmc: mmci: add busy_complete callback



This patch adds busy_completion callback at mmci_host_ops
to allow to define a specific busy completion by variant.

The legacy code corresponding to busy completion used
by ux500 variants is moved to ux500_busy_complete function.

Signed-off-by: default avatarLudovic Barre <ludovic.barre@st.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 8266c585
Loading
Loading
Loading
Loading
+72 −62
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@
#define DRIVER_NAME "mmci-pl18x"

static void mmci_variant_init(struct mmci_host *host);
static void ux500_variant_init(struct mmci_host *host);
static void ux500v2_variant_init(struct mmci_host *host);

static unsigned int fmax = 515633;
@@ -184,7 +185,7 @@ static struct variant_data variant_ux500 = {
	.irq_pio_mask		= MCI_IRQ_PIO_MASK,
	.start_err		= MCI_STARTBITERR,
	.opendrain		= MCI_OD,
	.init			= mmci_variant_init,
	.init			= ux500_variant_init,
};

static struct variant_data variant_ux500v2 = {
@@ -610,6 +611,67 @@ static u32 ux500v2_get_dctrl_cfg(struct mmci_host *host)
	return MCI_DPSM_ENABLE | (host->data->blksz << 16);
}

static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
{
	void __iomem *base = host->base;

	/*
	 * Before unmasking for the busy end IRQ, confirm that the
	 * command was sent successfully. To keep track of having a
	 * command in-progress, waiting for busy signaling to end,
	 * store the status in host->busy_status.
	 *
	 * Note that, the card may need a couple of clock cycles before
	 * it starts signaling busy on DAT0, hence re-read the
	 * MMCISTATUS register here, to allow the busy bit to be set.
	 * Potentially we may even need to poll the register for a
	 * while, to allow it to be set, but tests indicates that it
	 * isn't needed.
	 */
	if (!host->busy_status && !(status & err_msk) &&
	    (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {
		writel(readl(base + MMCIMASK0) |
		       host->variant->busy_detect_mask,
		       base + MMCIMASK0);

		host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND);
		return false;
	}

	/*
	 * If there is a command in-progress that has been successfully
	 * sent, then bail out if busy status is set and wait for the
	 * busy end IRQ.
	 *
	 * Note that, the HW triggers an IRQ on both edges while
	 * monitoring DAT0 for busy completion, but there is only one
	 * status bit in MMCISTATUS for the busy state. Therefore
	 * both the start and the end interrupts needs to be cleared,
	 * one after the other. So, clear the busy start IRQ here.
	 */
	if (host->busy_status &&
	    (status & host->variant->busy_detect_flag)) {
		writel(host->variant->busy_detect_mask, base + MMCICLEAR);
		return false;
	}

	/*
	 * If there is a command in-progress that has been successfully
	 * sent and the busy bit isn't set, it means we have received
	 * the busy end IRQ. Clear and mask the IRQ, then continue to
	 * process the command.
	 */
	if (host->busy_status) {
		writel(host->variant->busy_detect_mask, base + MMCICLEAR);

		writel(readl(base + MMCIMASK0) &
		       ~host->variant->busy_detect_mask, base + MMCIMASK0);
		host->busy_status = 0;
	}

	return true;
}

/*
 * All the DMA operation mode stuff goes inside this ifdef.
 * This assumes that you have a generic DMA device interface,
@@ -953,9 +1015,16 @@ static void mmci_variant_init(struct mmci_host *host)
	host->ops = &mmci_variant_ops;
}

static void ux500_variant_init(struct mmci_host *host)
{
	host->ops = &mmci_variant_ops;
	host->ops->busy_complete = ux500_busy_complete;
}

static void ux500v2_variant_init(struct mmci_host *host)
{
	host->ops = &mmci_variant_ops;
	host->ops->busy_complete = ux500_busy_complete;
	host->ops->get_datactrl_cfg = ux500v2_get_dctrl_cfg;
}

@@ -1235,68 +1304,9 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
		return;

	/* Handle busy detection on DAT0 if the variant supports it. */
	if (busy_resp && host->variant->busy_detect) {

		/*
		 * Before unmasking for the busy end IRQ, confirm that the
		 * command was sent successfully. To keep track of having a
		 * command in-progress, waiting for busy signaling to end,
		 * store the status in host->busy_status.
		 *
		 * Note that, the card may need a couple of clock cycles before
		 * it starts signaling busy on DAT0, hence re-read the
		 * MMCISTATUS register here, to allow the busy bit to be set.
		 * Potentially we may even need to poll the register for a
		 * while, to allow it to be set, but tests indicates that it
		 * isn't needed.
		 */
		if (!host->busy_status && !(status & err_msk) &&
		    (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {

			writel(readl(base + MMCIMASK0) |
			       host->variant->busy_detect_mask,
			       base + MMCIMASK0);

			host->busy_status =
				status & (MCI_CMDSENT|MCI_CMDRESPEND);
			return;
		}

		/*
		 * If there is a command in-progress that has been successfully
		 * sent, then bail out if busy status is set and wait for the
		 * busy end IRQ.
		 *
		 * Note that, the HW triggers an IRQ on both edges while
		 * monitoring DAT0 for busy completion, but there is only one
		 * status bit in MMCISTATUS for the busy state. Therefore
		 * both the start and the end interrupts needs to be cleared,
		 * one after the other. So, clear the busy start IRQ here.
		 */
		if (host->busy_status &&
		    (status & host->variant->busy_detect_flag)) {
			writel(host->variant->busy_detect_mask,
			       host->base + MMCICLEAR);
	if (busy_resp && host->variant->busy_detect)
		if (!host->ops->busy_complete(host, status, err_msk))
			return;
		}

		/*
		 * If there is a command in-progress that has been successfully
		 * sent and the busy bit isn't set, it means we have received
		 * the busy end IRQ. Clear and mask the IRQ, then continue to
		 * process the command.
		 */
		if (host->busy_status) {

			writel(host->variant->busy_detect_mask,
			       host->base + MMCICLEAR);

			writel(readl(base + MMCIMASK0) &
			       ~host->variant->busy_detect_mask,
			       base + MMCIMASK0);
			host->busy_status = 0;
		}
	}

	host->cmd = NULL;

+1 −0
Original line number Diff line number Diff line
@@ -369,6 +369,7 @@ struct mmci_host_ops {
	void (*dma_error)(struct mmci_host *host);
	void (*set_clkreg)(struct mmci_host *host, unsigned int desired);
	void (*set_pwrreg)(struct mmci_host *host, unsigned int pwr);
	bool (*busy_complete)(struct mmci_host *host, u32 status, u32 err_msk);
};

struct mmci_host {