[RFC / POC] mmc: meson-gxbb: experimental SDIO support

Martin Blumenstingl martin.blumenstingl at googlemail.com
Sun Aug 28 09:58:29 PDT 2016


This adds experimental SDIO support to the meson mmc controller.
The patch consists of four main changes:
- remove meson_mmc_check_cmd to allow the SDIO commands
- implement meson_mcc_enable_sdio_irq - this requires adding the
  irq_mask field to our priv struct so we always configure the
  correct IRQs.
- get rid of the magic value IRQ_EN_MASK and replace it with an OR
  of the corresponding bit/masks (when initializing host->irq_mask)
- it adds a small bugfix in meson_mmc_probe when probing failed (in
  my case: probing had to be deferred because the mmc-initseq was not
  ready yet) which disabled and unprepared a clock which was not
  enabled

I am sending this patch because I only have limited access to my board
in the next 2 weeks. Please see this as "proof of concept" and not a
finished patch. If someone wants to work in this: feel free - otherwise
I'll do it when I have full access to my board again.

An additional patch which enables wifi on the Tronsmart S95 Vega Meta
can be found here:
https://github.com/xdarklight/linux/tree/meson-gxbb-integration-4.8-20160828

Signed-off-by: Martin Blumenstingl <martin.blumenstingl at googlemail.com>
---
 drivers/mmc/host/meson-gxbb.c | 81 ++++++++++++++++++++++++-------------------
 1 file changed, 45 insertions(+), 36 deletions(-)

diff --git a/drivers/mmc/host/meson-gxbb.c b/drivers/mmc/host/meson-gxbb.c
index 10eac41..77a7a1f 100644
--- a/drivers/mmc/host/meson-gxbb.c
+++ b/drivers/mmc/host/meson-gxbb.c
@@ -123,7 +123,6 @@
 #define   STATUS_BUSY BIT(31)
 
 #define SD_EMMC_IRQ_EN 0x4c
-#define   IRQ_EN_MASK 0x3fff
 #define   IRQ_RXD_ERR_SHIFT 0
 #define   IRQ_RXD_ERR_MASK 0xff
 #define   IRQ_TXD_ERR BIT(8)
@@ -181,6 +180,8 @@ struct meson_host {
 	unsigned long clk_rate;
 	unsigned long clk_src_rate;
 	unsigned short clk_src_div;
+
+	u32 irq_mask;
 };
 
 #define reg_read(host, offset) readl(host->regs + offset)
@@ -457,28 +458,6 @@ static int meson_mmc_request_done(struct mmc_host *mmc, struct mmc_request *mrq)
 	return 0;
 }
 
-static int meson_mmc_cmd_invalid(struct mmc_host *mmc, struct mmc_command *cmd)
-{
-	cmd->error = -EINVAL;
-	meson_mmc_request_done(mmc, cmd->mrq);
-
-	return -EINVAL;
-}
-
-static int meson_mmc_check_cmd(struct mmc_host *mmc, struct mmc_command *cmd)
-{
-	int ret = 0;
-
-	/* FIXME: needs update for SDIO support */
-	if (cmd->opcode == SD_IO_SEND_OP_COND
-	    || cmd->opcode == SD_IO_RW_DIRECT
-	    || cmd->opcode == SD_IO_RW_EXTENDED) {
-		ret = meson_mmc_cmd_invalid(mmc, cmd);
-	}
-
-	return ret;
-}
-
 static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd)
 {
 	struct meson_host *host = mmc_priv(mmc);
@@ -588,14 +567,11 @@ static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
 	/* clear, ack, enable all interrupts */
 	reg_write(host, SD_EMMC_IRQ_EN, 0);
-	reg_write(host, SD_EMMC_STATUS, IRQ_EN_MASK);
-	reg_write(host, SD_EMMC_IRQ_EN, IRQ_EN_MASK);
+	reg_write(host, SD_EMMC_STATUS, host->irq_mask);
+	reg_write(host, SD_EMMC_IRQ_EN, host->irq_mask);
 
 	host->mrq = mrq;
 
-	if (meson_mmc_check_cmd(mmc, mrq->cmd))
-		return;
-
 	if (mrq->sbc)
 		meson_mmc_start_cmd(mmc, mrq->sbc);
 	else
@@ -629,6 +605,7 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
 	struct mmc_request *mrq = host->mrq;
 	struct mmc_command *cmd = host->cmd;
 	u32 irq_en, status, raw_status;
+	bool sdio_irq = false;
 	irqreturn_t ret = IRQ_HANDLED;
 
 	if (WARN_ON(!host))
@@ -675,8 +652,9 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
 		dev_dbg(host->dev, "Unhandled IRQ: Descriptor timeout\n");
 		cmd->error = -ETIMEDOUT;
 	}
+
 	if (status & IRQ_SDIO)
-		dev_dbg(host->dev, "Unhandled IRQ: SDIO.\n");
+		sdio_irq = true;
 
 	if (status & (IRQ_END_OF_CHAIN | IRQ_RESP_STATUS))
 		ret = IRQ_WAKE_THREAD;
@@ -698,6 +676,9 @@ out:
 	/* ack all (enabled) interrupts */
 	reg_write(host, SD_EMMC_STATUS, status);
 
+	if (sdio_irq)
+		mmc_signal_sdio_irq(host->mmc);
+
 	if (ret == IRQ_HANDLED) {
 		meson_mmc_read_resp(host->mmc, cmd);
 		meson_mmc_request_done(host->mmc, cmd->mrq);
@@ -757,10 +738,34 @@ static int meson_mmc_get_cd(struct mmc_host *mmc)
 	return status;
 }
 
+static void meson_mcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+	struct meson_host *host = mmc_priv(mmc);
+	unsigned long flags;
+	u32 irqen;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	irqen = reg_read(host, SD_EMMC_IRQ_EN);
+
+	if (enable) {
+		irqen |= IRQ_SDIO;
+		host->irq_mask |= IRQ_SDIO;
+	} else {
+		irqen &= ~IRQ_SDIO;
+		host->irq_mask &= ~IRQ_SDIO;
+	}
+
+	reg_write(host, SD_EMMC_IRQ_EN, irqen);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
 static struct mmc_host_ops meson_mmc_ops = {
-	.request	= meson_mmc_request,
-	.set_ios	= meson_mmc_set_ios,
-	.get_cd         = meson_mmc_get_cd,
+	.request		= meson_mmc_request,
+	.set_ios		= meson_mmc_set_ios,
+	.get_cd         	= meson_mmc_get_cd,
+	.enable_sdio_irq	= meson_mcc_enable_sdio_irq,
 };
 
 static int meson_mmc_probe(struct platform_device *pdev)
@@ -847,26 +852,30 @@ static int meson_mmc_probe(struct platform_device *pdev)
 
 	clk_prepare_enable(host->core_clk);
 
+	host->irq_mask = IRQ_RXD_ERR_MASK | IRQ_TXD_ERR | IRQ_DESC_ERR |
+			 IRQ_RESP_ERR | IRQ_RESP_TIMEOUT | IRQ_DESC_TIMEOUT |
+			 IRQ_END_OF_CHAIN;
+
 	ret = meson_mmc_clk_init(host);
 	if (ret)
-		goto free_host;
+		goto free_host_clk_prepared;
 
 	/* Stop execution */
 	reg_write(host, SD_EMMC_START, 0);
 
 	/* clear, ack, enable all interrupts */
 	reg_write(host, SD_EMMC_IRQ_EN, 0);
-	reg_write(host, SD_EMMC_STATUS, IRQ_EN_MASK);
+	reg_write(host, SD_EMMC_STATUS, host->irq_mask);
 
 	mmc->ops = &meson_mmc_ops;
 	mmc_add_host(mmc);
 
 	return 0;
 
+free_host_clk_prepared:
+	clk_disable_unprepare(host->core_clk);
 free_host:
 	dev_dbg(host->dev, "Failed to probe: ret=%d\n", ret);
-	if (host->core_clk)
-		clk_disable_unprepare(host->core_clk);
 	mmc_free_host(mmc);
 	return ret;
 }
-- 
2.9.3




More information about the linux-amlogic mailing list