diff mbox

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

Message ID 20160828165829.21882-1-martin.blumenstingl@googlemail.com (mailing list archive)
State RFC
Headers show

Commit Message

Martin Blumenstingl Aug. 28, 2016, 4:58 p.m. UTC
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@googlemail.com>
---
 drivers/mmc/host/meson-gxbb.c | 81 ++++++++++++++++++++++++-------------------
 1 file changed, 45 insertions(+), 36 deletions(-)

Comments

Kevin Hilman Sept. 14, 2016, 12:16 a.m. UTC | #1
Martin Blumenstingl <martin.blumenstingl@googlemail.com> writes:

> 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

FYI, I've dropped this part from v3 of my MMC driver which will be
posted shortly..  That was leftover from some debugging and not needed
(as you noticed.)

Kevin

> - 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@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;
>  }
diff mbox

Patch

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;
 }