All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ulf Hansson <ulf.hansson@linaro.org>
To: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: "open list:ARM/Amlogic Meson..."
	<linux-amlogic@lists.infradead.org>,
	DTML <devicetree@vger.kernel.org>,
	"linux-mmc@vger.kernel.org" <linux-mmc@vger.kernel.org>,
	Rob Herring <robh+dt@kernel.org>,
	Jianxin Pan <jianxin.pan@amlogic.com>,
	Mark Rutland <mark.rutland@arm.com>,
	Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	Linux ARM <linux-arm-kernel@lists.infradead.org>,
	lnykww@gmail.com, yinxin_1989@aliyun.com
Subject: Re: [PATCH v5 3/3] mmc: host: meson-mx-sdhc: new driver for the Amlogic Meson SDHC host
Date: Mon, 27 Apr 2020 21:19:24 +0200	[thread overview]
Message-ID: <CAPDyKFqsG4kyABmxn__gAbe4fBmuZ=4mdFpRaCL0ih7QZEhwzQ@mail.gmail.com> (raw)
In-Reply-To: <20200328003249.1248978-4-martin.blumenstingl@googlemail.com>

On Sat, 28 Mar 2020 at 01:33, Martin Blumenstingl
<martin.blumenstingl@googlemail.com> wrote:
>
> The SDHC MMC host controller on Amlogic SoCs provides an eMMC and MMC
> card interface with 1/4/8-bit bus width.
> It supports eMMC spec 4.4x/4.5x including HS200 (up to 100MHz clock).
>
> The public S805 datasheet [0] contains a short documentation about the
> registers. Unfortunately it does not describe how to use the registers
> to make the hardware work. Thus this driver is based on reading (and
> understanding) the Amlogic 3.10 GPL kernel code.
>
> Some hardware details are not easy to see. Jianxin Pan was kind enough
> to answer my questions:
> The hardware has built-in busy timeout support. The maximum timeout is
> 30 seconds. This is only documented in Amlogic's internal
> documentation.
>
> The controller only works with very specific clock configurations. The
> details are not part of the public datasheet. In my own words the
> supported configurations are:
> - 399.812kHz:   clkin =  850MHz div = 2126 sd_rx_phase = 63
> - 1MHz:         clkin =  850MHz div = 850  sd_rx_phase = 55
> - 5.986MHz:     clkin =  850MHz div = 142  sd_rx_phase = 24
> - 25MHz:        clkin =  850MHz div = 34   sd_rx_phase = 15
> - 47.222MHz:    clkin =  850MHz div = 18   sd_rx_phase = 11/15 (SDR50/HS)
> - 53.125MHz:    clkin =  850MHz div = 16   sd_rx_phase = (tuning)
> - 70.833MHz:    clkin =  850MHz div = 12   sd_rx_phase = (tuning)
> - 85MHz:        clkin =  850MHz div = 10   sd_rx_phase = (tuning)
> - 94.44MHz:     clkin =  850MHz div = 9    sd_rx_phase = (tuning)
> - 106.25MHz:    clkin =  850MHz div = 8    sd_rx_phase = (tuning)
> - 127.5MHz:     clkin = 1275MHz div = 10   sd_rx_phase = (tuning)
> - 141.667MHz:   clkin =  850MHz div = 6    sd_rx_phase = (tuning)
> - 159.375MHz:   clkin = 1275MHz div = 8    sd_rx_phase = (tuning)
> - 212.5MHz:     clkin = 1275MHz div = 6    sd_rx_phase = (tuning)
> - (sd_tx_phase is always 1, 94.44MHz is not listed in the datasheet
>    but this is what the 3.10 BSP kernel on Odroid-C1 actually uses)
>
> NOTE: CMD23 support is disabled for now because it results in command
> timeouts and thus decreases read performance.
>
> Tested-by: Wei Wang <lnykww@gmail.com>
> Tested-by: Xin Yin <yinxin_1989@aliyun.com>
> Reviewed-by: Xin Yin <yinxin_1989@aliyun.com>
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> ---
>  drivers/mmc/host/Kconfig         |   14 +
>  drivers/mmc/host/Makefile        |    1 +
>  drivers/mmc/host/meson-mx-sdhc.c | 1064 ++++++++++++++++++++++++++++++
>  3 files changed, 1079 insertions(+)
>  create mode 100644 drivers/mmc/host/meson-mx-sdhc.c
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 3a5089f0332c..2c8361aa7eed 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -405,6 +405,20 @@ config MMC_MESON_GX
>
>           If you have a controller with this interface, say Y here.
>
> +config MMC_MESON_MX_SDHC
> +       tristate "Amlogic Meson SDHC Host Controller support"
> +       depends on (ARM && ARCH_MESON) || COMPILE_TEST
> +       depends on OF
> +       imply COMMON_CLK_MESON_MX_SDHC
> +       help
> +         This selects support for the SDHC Host Controller on
> +         Amlogic Meson6, Meson8, Meson8b and Meson8m2 SoCs.
> +         The controller supports the SD/SDIO Spec 3.x and eMMC Spec 4.5x
> +         with 1, 4, and 8 bit bus widths.
> +
> +         If you have a controller with this interface, say Y or M here.
> +         If unsure, say N.
> +
>  config MMC_MESON_MX_SDIO
>         tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
>         depends on ARCH_MESON || COMPILE_TEST
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 21d9089e5eda..9a0c22a8139a 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -68,6 +68,7 @@ obj-$(CONFIG_MMC_VUB300)      += vub300.o
>  obj-$(CONFIG_MMC_USHC)         += ushc.o
>  obj-$(CONFIG_MMC_WMT)          += wmt-sdmmc.o
>  obj-$(CONFIG_MMC_MESON_GX)     += meson-gx-mmc.o
> +obj-$(CONFIG_MMC_MESON_MX_SDHC)        += meson-mx-sdhc.o
>  obj-$(CONFIG_MMC_MESON_MX_SDIO)        += meson-mx-sdio.o
>  obj-$(CONFIG_MMC_MOXART)       += moxart-mmc.o
>  obj-$(CONFIG_MMC_SUNXI)                += sunxi-mmc.o
> diff --git a/drivers/mmc/host/meson-mx-sdhc.c b/drivers/mmc/host/meson-mx-sdhc.c
> new file mode 100644
> index 000000000000..b92400ee488b
> --- /dev/null
> +++ b/drivers/mmc/host/meson-mx-sdhc.c

[...]

> +
> +static void meson_mx_sdhc_wait_cmd_ready(struct mmc_host *mmc)
> +{
> +       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +       u32 stat, esta;
> +       int ret;
> +
> +       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT, stat,
> +                                      !(stat & MESON_SDHC_STAT_CMD_BUSY), 1,
> +                                      100000);

Please use defines for timeout values.

> +       if (ret) {
> +               dev_warn(mmc_dev(mmc),
> +                        "Failed to poll for CMD_BUSY while processing CMD%d\n",
> +                        host->cmd->opcode);
> +               meson_mx_sdhc_hw_reset(mmc);
> +       }
> +
> +       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, esta,
> +                                      !(esta & MESON_SDHC_ESTA_11_13), 1,
> +                                      100000);

Ditto.

> +       if (ret) {
> +               dev_warn(mmc_dev(mmc),
> +                        "Failed to poll for ESTA[13:11] while processing CMD%d\n",
> +                        host->cmd->opcode);
> +               meson_mx_sdhc_hw_reset(mmc);
> +       }
> +}
> +
> +static void meson_mx_sdhc_start_cmd(struct mmc_host *mmc,
> +                                   struct mmc_command *cmd)
> +{
> +       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +       u32 ictl, send;
> +       int pack_len;
> +
> +       host->cmd = cmd;
> +
> +       ictl = MESON_SDHC_ICTL_DATA_TIMEOUT | MESON_SDHC_ICTL_DATA_ERR_CRC |
> +              MESON_SDHC_ICTL_RXFIFO_FULL | MESON_SDHC_ICTL_TXFIFO_EMPTY |
> +              MESON_SDHC_ICTL_RESP_TIMEOUT | MESON_SDHC_ICTL_RESP_ERR_CRC;
> +
> +       send = FIELD_PREP(MESON_SDHC_SEND_CMD_INDEX, cmd->opcode);
> +
> +       if (cmd->data) {
> +               send |= MESON_SDHC_SEND_CMD_HAS_DATA;
> +               send |= FIELD_PREP(MESON_SDHC_SEND_TOTAL_PACK,
> +                                  cmd->data->blocks - 1);
> +
> +               if (cmd->data->blksz < MESON_SDHC_MAX_BLK_SIZE)
> +                       pack_len = cmd->data->blksz;
> +               else
> +                       pack_len = 0;
> +
> +               if (cmd->data->flags & MMC_DATA_WRITE)
> +                       send |= MESON_SDHC_SEND_DATA_DIR;
> +
> +               /*
> +                * If command with no data, just wait response done
> +                * interrupt(int[0]), and if command with data transfer, just
> +                * wait dma done interrupt(int[11]), don't need care about
> +                * dat0 busy or not.
> +                */
> +               if (host->platform->hardware_flush_all_cmds ||
> +                   cmd->data->flags & MMC_DATA_WRITE)
> +                       /* hardware flush: */
> +                       ictl |= MESON_SDHC_ICTL_DMA_DONE;
> +               else
> +                       /* software flush: */
> +                       ictl |= MESON_SDHC_ICTL_DATA_XFER_OK;
> +       } else {
> +               pack_len = 0;
> +
> +               ictl |= MESON_SDHC_ICTL_RESP_OK;
> +       }
> +
> +       if (cmd->opcode == MMC_STOP_TRANSMISSION)
> +               send |= MESON_SDHC_SEND_DATA_STOP;
> +
> +       if (cmd->flags & MMC_RSP_PRESENT)
> +               send |= MESON_SDHC_SEND_CMD_HAS_RESP;
> +
> +       if (cmd->flags & MMC_RSP_136) {
> +               send |= MESON_SDHC_SEND_RESP_LEN;
> +               send |= MESON_SDHC_SEND_RESP_NO_CRC;
> +       }
> +
> +       if (!(cmd->flags & MMC_RSP_CRC))
> +               send |= MESON_SDHC_SEND_RESP_NO_CRC;
> +
> +       if (cmd->flags & MMC_RSP_BUSY)
> +               send |= MESON_SDHC_SEND_R1B;
> +
> +       /* enable the new IRQs and mask all pending ones */
> +       regmap_write(host->regmap, MESON_SDHC_ICTL, ictl);
> +       regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
> +
> +       regmap_write(host->regmap, MESON_SDHC_ARGU, cmd->arg);
> +
> +       regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
> +                          MESON_SDHC_CTRL_PACK_LEN,
> +                          FIELD_PREP(MESON_SDHC_CTRL_PACK_LEN, pack_len));
> +
> +       if (cmd->data)
> +               regmap_write(host->regmap, MESON_SDHC_ADDR,
> +                            sg_dma_address(cmd->data->sg));
> +
> +       meson_mx_sdhc_wait_cmd_ready(mmc);
> +
> +       if (cmd->data)
> +               host->platform->set_pdma(mmc);
> +
> +       if (host->platform->wait_before_send)
> +               host->platform->wait_before_send(mmc);
> +
> +       regmap_write(host->regmap, MESON_SDHC_SEND, send);

Isn't there a configurable timeout to set for the command?

I mean the driver sets mmc->max_busy_timeout to 30s in ->probe(), but
can the timeout be configured to a lower value?

[...]

> +static void meson_mx_sdhc_wait_before_send_meson8(struct mmc_host *mmc)
> +{
> +       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +       u32 val;
> +       int ret;
> +
> +       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, val,
> +                                      val == 0, 1, 200);

Please use defines for timeout values.

> +       if (ret)
> +               dev_warn(mmc_dev(mmc),
> +                        "Failed to wait for ESTA to clear: 0x%08x\n", val);
> +
> +       if (host->cmd->data && host->cmd->data->flags & MMC_DATA_WRITE) {
> +               ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT,
> +                                              val,
> +                                              val & MESON_SDHC_STAT_TXFIFO_CNT,
> +                                              1, 200);

Ditto.

> +               if (ret)
> +                       dev_warn(mmc_dev(mmc),
> +                                "Failed to wait for TX FIFO to fill\n");
> +       }
> +}
> +

[...]

> +
> +static int meson_mx_sdhc_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct meson_mx_sdhc_host *host;
> +       struct mmc_host *mmc;
> +       void __iomem *base;
> +       int ret, irq;
> +
> +       mmc = mmc_alloc_host(sizeof(*host), dev);
> +       if (!mmc)
> +               return -ENOMEM;
> +
> +       ret = devm_add_action_or_reset(dev, (void(*)(void *))mmc_free_host,
> +                                      mmc);
> +       if (ret) {
> +               dev_err(dev, "Failed to register mmc_free_host action\n");
> +               return ret;
> +       }
> +
> +       host = mmc_priv(mmc);
> +       host->mmc = mmc;
> +
> +       platform_set_drvdata(pdev, host);
> +
> +       host->platform = device_get_match_data(dev);
> +       if (!host->platform)
> +               return -EINVAL;
> +
> +       base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(base))
> +               return PTR_ERR(base);
> +
> +       host->regmap = devm_regmap_init_mmio(dev, base,
> +                                            &meson_mx_sdhc_regmap_config);
> +       if (IS_ERR(host->regmap))
> +               return PTR_ERR(host->regmap);
> +
> +       host->pclk = devm_clk_get(dev, "pclk");
> +       if (IS_ERR(host->pclk))
> +               return PTR_ERR(host->pclk);
> +
> +       /* accessing any register requires the module clock to be enabled: */
> +       ret = clk_prepare_enable(host->pclk);
> +       if (ret) {
> +               dev_err(dev, "Failed to enable 'pclk' clock\n");
> +               return ret;
> +       }
> +
> +       meson_mx_sdhc_init_hw(mmc);
> +
> +       ret = meson_mx_sdhc_register_clkc(host);
> +       if (ret)
> +               goto err_disable_pclk;
> +
> +       host->bulk_clks[0].id = "mod_clk";
> +       host->bulk_clks[1].id = "sd_clk";
> +       host->bulk_clks[2].id = "tx_clk";
> +       host->bulk_clks[3].id = "rx_clk";
> +       ret = devm_clk_bulk_get(dev, MESON_SDHC_NUM_BULK_CLKS,
> +                               host->bulk_clks);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       host->sd_clk = host->bulk_clks[1].clk;
> +
> +       /* Get regulators and the supported OCR mask */
> +       ret = mmc_regulator_get_supply(mmc);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       mmc->max_req_size = SZ_128K;
> +       mmc->max_seg_size = mmc->max_req_size;
> +       mmc->max_blk_count = FIELD_GET(MESON_SDHC_SEND_TOTAL_PACK, ~0);
> +       mmc->max_blk_size = MESON_SDHC_MAX_BLK_SIZE;
> +       mmc->max_busy_timeout = 30 * MSEC_PER_SEC;
> +       mmc->f_min = clk_round_rate(host->sd_clk, 1);
> +       mmc->f_max = clk_round_rate(host->sd_clk, ULONG_MAX);
> +       mmc->max_current_180 = 300;
> +       mmc->max_current_330 = 300;
> +       mmc->caps |= MMC_CAP_ERASE | MMC_CAP_HW_RESET;

Should you also set MMC_CAP_WAIT_WHILE_BUSY? It sounded like the
driver supported this.

> +       mmc->ops = &meson_mx_sdhc_ops;
> +
> +       ret = mmc_of_parse(mmc);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       ret = devm_request_threaded_irq(dev, irq, meson_mx_sdhc_irq,
> +                                       meson_mx_sdhc_irq_thread, IRQF_ONESHOT,
> +                                       NULL, host);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       ret = mmc_add_host(mmc);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       return 0;
> +
> +err_clkc_pdev_unregister:
> +       platform_device_unregister(host->clkc_pdev);
> +err_disable_pclk:
> +       clk_disable_unprepare(host->pclk);
> +       return ret;
> +}
> +

[...]

FYI: I left out all comments related to the clock provider
initialization. I think it makes better sense to review that code,
after you have converted to use the devm_clk_hw_register() and avoid
registering a separate driver for it.

Other than the minor comments, this looks good to me.

Kind regards
Uffe

WARNING: multiple messages have this Message-ID (diff)
From: Ulf Hansson <ulf.hansson@linaro.org>
To: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: Mark Rutland <mark.rutland@arm.com>,
	DTML <devicetree@vger.kernel.org>,
	Jianxin Pan <jianxin.pan@amlogic.com>,
	"linux-mmc@vger.kernel.org" <linux-mmc@vger.kernel.org>,
	Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	yinxin_1989@aliyun.com, Rob Herring <robh+dt@kernel.org>,
	"open list:ARM/Amlogic Meson..."
	<linux-amlogic@lists.infradead.org>,
	Linux ARM <linux-arm-kernel@lists.infradead.org>,
	lnykww@gmail.com
Subject: Re: [PATCH v5 3/3] mmc: host: meson-mx-sdhc: new driver for the Amlogic Meson SDHC host
Date: Mon, 27 Apr 2020 21:19:24 +0200	[thread overview]
Message-ID: <CAPDyKFqsG4kyABmxn__gAbe4fBmuZ=4mdFpRaCL0ih7QZEhwzQ@mail.gmail.com> (raw)
In-Reply-To: <20200328003249.1248978-4-martin.blumenstingl@googlemail.com>

On Sat, 28 Mar 2020 at 01:33, Martin Blumenstingl
<martin.blumenstingl@googlemail.com> wrote:
>
> The SDHC MMC host controller on Amlogic SoCs provides an eMMC and MMC
> card interface with 1/4/8-bit bus width.
> It supports eMMC spec 4.4x/4.5x including HS200 (up to 100MHz clock).
>
> The public S805 datasheet [0] contains a short documentation about the
> registers. Unfortunately it does not describe how to use the registers
> to make the hardware work. Thus this driver is based on reading (and
> understanding) the Amlogic 3.10 GPL kernel code.
>
> Some hardware details are not easy to see. Jianxin Pan was kind enough
> to answer my questions:
> The hardware has built-in busy timeout support. The maximum timeout is
> 30 seconds. This is only documented in Amlogic's internal
> documentation.
>
> The controller only works with very specific clock configurations. The
> details are not part of the public datasheet. In my own words the
> supported configurations are:
> - 399.812kHz:   clkin =  850MHz div = 2126 sd_rx_phase = 63
> - 1MHz:         clkin =  850MHz div = 850  sd_rx_phase = 55
> - 5.986MHz:     clkin =  850MHz div = 142  sd_rx_phase = 24
> - 25MHz:        clkin =  850MHz div = 34   sd_rx_phase = 15
> - 47.222MHz:    clkin =  850MHz div = 18   sd_rx_phase = 11/15 (SDR50/HS)
> - 53.125MHz:    clkin =  850MHz div = 16   sd_rx_phase = (tuning)
> - 70.833MHz:    clkin =  850MHz div = 12   sd_rx_phase = (tuning)
> - 85MHz:        clkin =  850MHz div = 10   sd_rx_phase = (tuning)
> - 94.44MHz:     clkin =  850MHz div = 9    sd_rx_phase = (tuning)
> - 106.25MHz:    clkin =  850MHz div = 8    sd_rx_phase = (tuning)
> - 127.5MHz:     clkin = 1275MHz div = 10   sd_rx_phase = (tuning)
> - 141.667MHz:   clkin =  850MHz div = 6    sd_rx_phase = (tuning)
> - 159.375MHz:   clkin = 1275MHz div = 8    sd_rx_phase = (tuning)
> - 212.5MHz:     clkin = 1275MHz div = 6    sd_rx_phase = (tuning)
> - (sd_tx_phase is always 1, 94.44MHz is not listed in the datasheet
>    but this is what the 3.10 BSP kernel on Odroid-C1 actually uses)
>
> NOTE: CMD23 support is disabled for now because it results in command
> timeouts and thus decreases read performance.
>
> Tested-by: Wei Wang <lnykww@gmail.com>
> Tested-by: Xin Yin <yinxin_1989@aliyun.com>
> Reviewed-by: Xin Yin <yinxin_1989@aliyun.com>
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> ---
>  drivers/mmc/host/Kconfig         |   14 +
>  drivers/mmc/host/Makefile        |    1 +
>  drivers/mmc/host/meson-mx-sdhc.c | 1064 ++++++++++++++++++++++++++++++
>  3 files changed, 1079 insertions(+)
>  create mode 100644 drivers/mmc/host/meson-mx-sdhc.c
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 3a5089f0332c..2c8361aa7eed 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -405,6 +405,20 @@ config MMC_MESON_GX
>
>           If you have a controller with this interface, say Y here.
>
> +config MMC_MESON_MX_SDHC
> +       tristate "Amlogic Meson SDHC Host Controller support"
> +       depends on (ARM && ARCH_MESON) || COMPILE_TEST
> +       depends on OF
> +       imply COMMON_CLK_MESON_MX_SDHC
> +       help
> +         This selects support for the SDHC Host Controller on
> +         Amlogic Meson6, Meson8, Meson8b and Meson8m2 SoCs.
> +         The controller supports the SD/SDIO Spec 3.x and eMMC Spec 4.5x
> +         with 1, 4, and 8 bit bus widths.
> +
> +         If you have a controller with this interface, say Y or M here.
> +         If unsure, say N.
> +
>  config MMC_MESON_MX_SDIO
>         tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
>         depends on ARCH_MESON || COMPILE_TEST
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 21d9089e5eda..9a0c22a8139a 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -68,6 +68,7 @@ obj-$(CONFIG_MMC_VUB300)      += vub300.o
>  obj-$(CONFIG_MMC_USHC)         += ushc.o
>  obj-$(CONFIG_MMC_WMT)          += wmt-sdmmc.o
>  obj-$(CONFIG_MMC_MESON_GX)     += meson-gx-mmc.o
> +obj-$(CONFIG_MMC_MESON_MX_SDHC)        += meson-mx-sdhc.o
>  obj-$(CONFIG_MMC_MESON_MX_SDIO)        += meson-mx-sdio.o
>  obj-$(CONFIG_MMC_MOXART)       += moxart-mmc.o
>  obj-$(CONFIG_MMC_SUNXI)                += sunxi-mmc.o
> diff --git a/drivers/mmc/host/meson-mx-sdhc.c b/drivers/mmc/host/meson-mx-sdhc.c
> new file mode 100644
> index 000000000000..b92400ee488b
> --- /dev/null
> +++ b/drivers/mmc/host/meson-mx-sdhc.c

[...]

> +
> +static void meson_mx_sdhc_wait_cmd_ready(struct mmc_host *mmc)
> +{
> +       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +       u32 stat, esta;
> +       int ret;
> +
> +       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT, stat,
> +                                      !(stat & MESON_SDHC_STAT_CMD_BUSY), 1,
> +                                      100000);

Please use defines for timeout values.

> +       if (ret) {
> +               dev_warn(mmc_dev(mmc),
> +                        "Failed to poll for CMD_BUSY while processing CMD%d\n",
> +                        host->cmd->opcode);
> +               meson_mx_sdhc_hw_reset(mmc);
> +       }
> +
> +       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, esta,
> +                                      !(esta & MESON_SDHC_ESTA_11_13), 1,
> +                                      100000);

Ditto.

> +       if (ret) {
> +               dev_warn(mmc_dev(mmc),
> +                        "Failed to poll for ESTA[13:11] while processing CMD%d\n",
> +                        host->cmd->opcode);
> +               meson_mx_sdhc_hw_reset(mmc);
> +       }
> +}
> +
> +static void meson_mx_sdhc_start_cmd(struct mmc_host *mmc,
> +                                   struct mmc_command *cmd)
> +{
> +       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +       u32 ictl, send;
> +       int pack_len;
> +
> +       host->cmd = cmd;
> +
> +       ictl = MESON_SDHC_ICTL_DATA_TIMEOUT | MESON_SDHC_ICTL_DATA_ERR_CRC |
> +              MESON_SDHC_ICTL_RXFIFO_FULL | MESON_SDHC_ICTL_TXFIFO_EMPTY |
> +              MESON_SDHC_ICTL_RESP_TIMEOUT | MESON_SDHC_ICTL_RESP_ERR_CRC;
> +
> +       send = FIELD_PREP(MESON_SDHC_SEND_CMD_INDEX, cmd->opcode);
> +
> +       if (cmd->data) {
> +               send |= MESON_SDHC_SEND_CMD_HAS_DATA;
> +               send |= FIELD_PREP(MESON_SDHC_SEND_TOTAL_PACK,
> +                                  cmd->data->blocks - 1);
> +
> +               if (cmd->data->blksz < MESON_SDHC_MAX_BLK_SIZE)
> +                       pack_len = cmd->data->blksz;
> +               else
> +                       pack_len = 0;
> +
> +               if (cmd->data->flags & MMC_DATA_WRITE)
> +                       send |= MESON_SDHC_SEND_DATA_DIR;
> +
> +               /*
> +                * If command with no data, just wait response done
> +                * interrupt(int[0]), and if command with data transfer, just
> +                * wait dma done interrupt(int[11]), don't need care about
> +                * dat0 busy or not.
> +                */
> +               if (host->platform->hardware_flush_all_cmds ||
> +                   cmd->data->flags & MMC_DATA_WRITE)
> +                       /* hardware flush: */
> +                       ictl |= MESON_SDHC_ICTL_DMA_DONE;
> +               else
> +                       /* software flush: */
> +                       ictl |= MESON_SDHC_ICTL_DATA_XFER_OK;
> +       } else {
> +               pack_len = 0;
> +
> +               ictl |= MESON_SDHC_ICTL_RESP_OK;
> +       }
> +
> +       if (cmd->opcode == MMC_STOP_TRANSMISSION)
> +               send |= MESON_SDHC_SEND_DATA_STOP;
> +
> +       if (cmd->flags & MMC_RSP_PRESENT)
> +               send |= MESON_SDHC_SEND_CMD_HAS_RESP;
> +
> +       if (cmd->flags & MMC_RSP_136) {
> +               send |= MESON_SDHC_SEND_RESP_LEN;
> +               send |= MESON_SDHC_SEND_RESP_NO_CRC;
> +       }
> +
> +       if (!(cmd->flags & MMC_RSP_CRC))
> +               send |= MESON_SDHC_SEND_RESP_NO_CRC;
> +
> +       if (cmd->flags & MMC_RSP_BUSY)
> +               send |= MESON_SDHC_SEND_R1B;
> +
> +       /* enable the new IRQs and mask all pending ones */
> +       regmap_write(host->regmap, MESON_SDHC_ICTL, ictl);
> +       regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
> +
> +       regmap_write(host->regmap, MESON_SDHC_ARGU, cmd->arg);
> +
> +       regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
> +                          MESON_SDHC_CTRL_PACK_LEN,
> +                          FIELD_PREP(MESON_SDHC_CTRL_PACK_LEN, pack_len));
> +
> +       if (cmd->data)
> +               regmap_write(host->regmap, MESON_SDHC_ADDR,
> +                            sg_dma_address(cmd->data->sg));
> +
> +       meson_mx_sdhc_wait_cmd_ready(mmc);
> +
> +       if (cmd->data)
> +               host->platform->set_pdma(mmc);
> +
> +       if (host->platform->wait_before_send)
> +               host->platform->wait_before_send(mmc);
> +
> +       regmap_write(host->regmap, MESON_SDHC_SEND, send);

Isn't there a configurable timeout to set for the command?

I mean the driver sets mmc->max_busy_timeout to 30s in ->probe(), but
can the timeout be configured to a lower value?

[...]

> +static void meson_mx_sdhc_wait_before_send_meson8(struct mmc_host *mmc)
> +{
> +       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +       u32 val;
> +       int ret;
> +
> +       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, val,
> +                                      val == 0, 1, 200);

Please use defines for timeout values.

> +       if (ret)
> +               dev_warn(mmc_dev(mmc),
> +                        "Failed to wait for ESTA to clear: 0x%08x\n", val);
> +
> +       if (host->cmd->data && host->cmd->data->flags & MMC_DATA_WRITE) {
> +               ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT,
> +                                              val,
> +                                              val & MESON_SDHC_STAT_TXFIFO_CNT,
> +                                              1, 200);

Ditto.

> +               if (ret)
> +                       dev_warn(mmc_dev(mmc),
> +                                "Failed to wait for TX FIFO to fill\n");
> +       }
> +}
> +

[...]

> +
> +static int meson_mx_sdhc_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct meson_mx_sdhc_host *host;
> +       struct mmc_host *mmc;
> +       void __iomem *base;
> +       int ret, irq;
> +
> +       mmc = mmc_alloc_host(sizeof(*host), dev);
> +       if (!mmc)
> +               return -ENOMEM;
> +
> +       ret = devm_add_action_or_reset(dev, (void(*)(void *))mmc_free_host,
> +                                      mmc);
> +       if (ret) {
> +               dev_err(dev, "Failed to register mmc_free_host action\n");
> +               return ret;
> +       }
> +
> +       host = mmc_priv(mmc);
> +       host->mmc = mmc;
> +
> +       platform_set_drvdata(pdev, host);
> +
> +       host->platform = device_get_match_data(dev);
> +       if (!host->platform)
> +               return -EINVAL;
> +
> +       base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(base))
> +               return PTR_ERR(base);
> +
> +       host->regmap = devm_regmap_init_mmio(dev, base,
> +                                            &meson_mx_sdhc_regmap_config);
> +       if (IS_ERR(host->regmap))
> +               return PTR_ERR(host->regmap);
> +
> +       host->pclk = devm_clk_get(dev, "pclk");
> +       if (IS_ERR(host->pclk))
> +               return PTR_ERR(host->pclk);
> +
> +       /* accessing any register requires the module clock to be enabled: */
> +       ret = clk_prepare_enable(host->pclk);
> +       if (ret) {
> +               dev_err(dev, "Failed to enable 'pclk' clock\n");
> +               return ret;
> +       }
> +
> +       meson_mx_sdhc_init_hw(mmc);
> +
> +       ret = meson_mx_sdhc_register_clkc(host);
> +       if (ret)
> +               goto err_disable_pclk;
> +
> +       host->bulk_clks[0].id = "mod_clk";
> +       host->bulk_clks[1].id = "sd_clk";
> +       host->bulk_clks[2].id = "tx_clk";
> +       host->bulk_clks[3].id = "rx_clk";
> +       ret = devm_clk_bulk_get(dev, MESON_SDHC_NUM_BULK_CLKS,
> +                               host->bulk_clks);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       host->sd_clk = host->bulk_clks[1].clk;
> +
> +       /* Get regulators and the supported OCR mask */
> +       ret = mmc_regulator_get_supply(mmc);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       mmc->max_req_size = SZ_128K;
> +       mmc->max_seg_size = mmc->max_req_size;
> +       mmc->max_blk_count = FIELD_GET(MESON_SDHC_SEND_TOTAL_PACK, ~0);
> +       mmc->max_blk_size = MESON_SDHC_MAX_BLK_SIZE;
> +       mmc->max_busy_timeout = 30 * MSEC_PER_SEC;
> +       mmc->f_min = clk_round_rate(host->sd_clk, 1);
> +       mmc->f_max = clk_round_rate(host->sd_clk, ULONG_MAX);
> +       mmc->max_current_180 = 300;
> +       mmc->max_current_330 = 300;
> +       mmc->caps |= MMC_CAP_ERASE | MMC_CAP_HW_RESET;

Should you also set MMC_CAP_WAIT_WHILE_BUSY? It sounded like the
driver supported this.

> +       mmc->ops = &meson_mx_sdhc_ops;
> +
> +       ret = mmc_of_parse(mmc);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       ret = devm_request_threaded_irq(dev, irq, meson_mx_sdhc_irq,
> +                                       meson_mx_sdhc_irq_thread, IRQF_ONESHOT,
> +                                       NULL, host);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       ret = mmc_add_host(mmc);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       return 0;
> +
> +err_clkc_pdev_unregister:
> +       platform_device_unregister(host->clkc_pdev);
> +err_disable_pclk:
> +       clk_disable_unprepare(host->pclk);
> +       return ret;
> +}
> +

[...]

FYI: I left out all comments related to the clock provider
initialization. I think it makes better sense to review that code,
after you have converted to use the devm_clk_hw_register() and avoid
registering a separate driver for it.

Other than the minor comments, this looks good to me.

Kind regards
Uffe

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

WARNING: multiple messages have this Message-ID (diff)
From: Ulf Hansson <ulf.hansson@linaro.org>
To: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: Mark Rutland <mark.rutland@arm.com>,
	DTML <devicetree@vger.kernel.org>,
	Jianxin Pan <jianxin.pan@amlogic.com>,
	"linux-mmc@vger.kernel.org" <linux-mmc@vger.kernel.org>,
	Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	yinxin_1989@aliyun.com, Rob Herring <robh+dt@kernel.org>,
	"open list:ARM/Amlogic Meson..."
	<linux-amlogic@lists.infradead.org>,
	Linux ARM <linux-arm-kernel@lists.infradead.org>,
	lnykww@gmail.com
Subject: Re: [PATCH v5 3/3] mmc: host: meson-mx-sdhc: new driver for the Amlogic Meson SDHC host
Date: Mon, 27 Apr 2020 21:19:24 +0200	[thread overview]
Message-ID: <CAPDyKFqsG4kyABmxn__gAbe4fBmuZ=4mdFpRaCL0ih7QZEhwzQ@mail.gmail.com> (raw)
In-Reply-To: <20200328003249.1248978-4-martin.blumenstingl@googlemail.com>

On Sat, 28 Mar 2020 at 01:33, Martin Blumenstingl
<martin.blumenstingl@googlemail.com> wrote:
>
> The SDHC MMC host controller on Amlogic SoCs provides an eMMC and MMC
> card interface with 1/4/8-bit bus width.
> It supports eMMC spec 4.4x/4.5x including HS200 (up to 100MHz clock).
>
> The public S805 datasheet [0] contains a short documentation about the
> registers. Unfortunately it does not describe how to use the registers
> to make the hardware work. Thus this driver is based on reading (and
> understanding) the Amlogic 3.10 GPL kernel code.
>
> Some hardware details are not easy to see. Jianxin Pan was kind enough
> to answer my questions:
> The hardware has built-in busy timeout support. The maximum timeout is
> 30 seconds. This is only documented in Amlogic's internal
> documentation.
>
> The controller only works with very specific clock configurations. The
> details are not part of the public datasheet. In my own words the
> supported configurations are:
> - 399.812kHz:   clkin =  850MHz div = 2126 sd_rx_phase = 63
> - 1MHz:         clkin =  850MHz div = 850  sd_rx_phase = 55
> - 5.986MHz:     clkin =  850MHz div = 142  sd_rx_phase = 24
> - 25MHz:        clkin =  850MHz div = 34   sd_rx_phase = 15
> - 47.222MHz:    clkin =  850MHz div = 18   sd_rx_phase = 11/15 (SDR50/HS)
> - 53.125MHz:    clkin =  850MHz div = 16   sd_rx_phase = (tuning)
> - 70.833MHz:    clkin =  850MHz div = 12   sd_rx_phase = (tuning)
> - 85MHz:        clkin =  850MHz div = 10   sd_rx_phase = (tuning)
> - 94.44MHz:     clkin =  850MHz div = 9    sd_rx_phase = (tuning)
> - 106.25MHz:    clkin =  850MHz div = 8    sd_rx_phase = (tuning)
> - 127.5MHz:     clkin = 1275MHz div = 10   sd_rx_phase = (tuning)
> - 141.667MHz:   clkin =  850MHz div = 6    sd_rx_phase = (tuning)
> - 159.375MHz:   clkin = 1275MHz div = 8    sd_rx_phase = (tuning)
> - 212.5MHz:     clkin = 1275MHz div = 6    sd_rx_phase = (tuning)
> - (sd_tx_phase is always 1, 94.44MHz is not listed in the datasheet
>    but this is what the 3.10 BSP kernel on Odroid-C1 actually uses)
>
> NOTE: CMD23 support is disabled for now because it results in command
> timeouts and thus decreases read performance.
>
> Tested-by: Wei Wang <lnykww@gmail.com>
> Tested-by: Xin Yin <yinxin_1989@aliyun.com>
> Reviewed-by: Xin Yin <yinxin_1989@aliyun.com>
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> ---
>  drivers/mmc/host/Kconfig         |   14 +
>  drivers/mmc/host/Makefile        |    1 +
>  drivers/mmc/host/meson-mx-sdhc.c | 1064 ++++++++++++++++++++++++++++++
>  3 files changed, 1079 insertions(+)
>  create mode 100644 drivers/mmc/host/meson-mx-sdhc.c
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 3a5089f0332c..2c8361aa7eed 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -405,6 +405,20 @@ config MMC_MESON_GX
>
>           If you have a controller with this interface, say Y here.
>
> +config MMC_MESON_MX_SDHC
> +       tristate "Amlogic Meson SDHC Host Controller support"
> +       depends on (ARM && ARCH_MESON) || COMPILE_TEST
> +       depends on OF
> +       imply COMMON_CLK_MESON_MX_SDHC
> +       help
> +         This selects support for the SDHC Host Controller on
> +         Amlogic Meson6, Meson8, Meson8b and Meson8m2 SoCs.
> +         The controller supports the SD/SDIO Spec 3.x and eMMC Spec 4.5x
> +         with 1, 4, and 8 bit bus widths.
> +
> +         If you have a controller with this interface, say Y or M here.
> +         If unsure, say N.
> +
>  config MMC_MESON_MX_SDIO
>         tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
>         depends on ARCH_MESON || COMPILE_TEST
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 21d9089e5eda..9a0c22a8139a 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -68,6 +68,7 @@ obj-$(CONFIG_MMC_VUB300)      += vub300.o
>  obj-$(CONFIG_MMC_USHC)         += ushc.o
>  obj-$(CONFIG_MMC_WMT)          += wmt-sdmmc.o
>  obj-$(CONFIG_MMC_MESON_GX)     += meson-gx-mmc.o
> +obj-$(CONFIG_MMC_MESON_MX_SDHC)        += meson-mx-sdhc.o
>  obj-$(CONFIG_MMC_MESON_MX_SDIO)        += meson-mx-sdio.o
>  obj-$(CONFIG_MMC_MOXART)       += moxart-mmc.o
>  obj-$(CONFIG_MMC_SUNXI)                += sunxi-mmc.o
> diff --git a/drivers/mmc/host/meson-mx-sdhc.c b/drivers/mmc/host/meson-mx-sdhc.c
> new file mode 100644
> index 000000000000..b92400ee488b
> --- /dev/null
> +++ b/drivers/mmc/host/meson-mx-sdhc.c

[...]

> +
> +static void meson_mx_sdhc_wait_cmd_ready(struct mmc_host *mmc)
> +{
> +       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +       u32 stat, esta;
> +       int ret;
> +
> +       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT, stat,
> +                                      !(stat & MESON_SDHC_STAT_CMD_BUSY), 1,
> +                                      100000);

Please use defines for timeout values.

> +       if (ret) {
> +               dev_warn(mmc_dev(mmc),
> +                        "Failed to poll for CMD_BUSY while processing CMD%d\n",
> +                        host->cmd->opcode);
> +               meson_mx_sdhc_hw_reset(mmc);
> +       }
> +
> +       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, esta,
> +                                      !(esta & MESON_SDHC_ESTA_11_13), 1,
> +                                      100000);

Ditto.

> +       if (ret) {
> +               dev_warn(mmc_dev(mmc),
> +                        "Failed to poll for ESTA[13:11] while processing CMD%d\n",
> +                        host->cmd->opcode);
> +               meson_mx_sdhc_hw_reset(mmc);
> +       }
> +}
> +
> +static void meson_mx_sdhc_start_cmd(struct mmc_host *mmc,
> +                                   struct mmc_command *cmd)
> +{
> +       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +       u32 ictl, send;
> +       int pack_len;
> +
> +       host->cmd = cmd;
> +
> +       ictl = MESON_SDHC_ICTL_DATA_TIMEOUT | MESON_SDHC_ICTL_DATA_ERR_CRC |
> +              MESON_SDHC_ICTL_RXFIFO_FULL | MESON_SDHC_ICTL_TXFIFO_EMPTY |
> +              MESON_SDHC_ICTL_RESP_TIMEOUT | MESON_SDHC_ICTL_RESP_ERR_CRC;
> +
> +       send = FIELD_PREP(MESON_SDHC_SEND_CMD_INDEX, cmd->opcode);
> +
> +       if (cmd->data) {
> +               send |= MESON_SDHC_SEND_CMD_HAS_DATA;
> +               send |= FIELD_PREP(MESON_SDHC_SEND_TOTAL_PACK,
> +                                  cmd->data->blocks - 1);
> +
> +               if (cmd->data->blksz < MESON_SDHC_MAX_BLK_SIZE)
> +                       pack_len = cmd->data->blksz;
> +               else
> +                       pack_len = 0;
> +
> +               if (cmd->data->flags & MMC_DATA_WRITE)
> +                       send |= MESON_SDHC_SEND_DATA_DIR;
> +
> +               /*
> +                * If command with no data, just wait response done
> +                * interrupt(int[0]), and if command with data transfer, just
> +                * wait dma done interrupt(int[11]), don't need care about
> +                * dat0 busy or not.
> +                */
> +               if (host->platform->hardware_flush_all_cmds ||
> +                   cmd->data->flags & MMC_DATA_WRITE)
> +                       /* hardware flush: */
> +                       ictl |= MESON_SDHC_ICTL_DMA_DONE;
> +               else
> +                       /* software flush: */
> +                       ictl |= MESON_SDHC_ICTL_DATA_XFER_OK;
> +       } else {
> +               pack_len = 0;
> +
> +               ictl |= MESON_SDHC_ICTL_RESP_OK;
> +       }
> +
> +       if (cmd->opcode == MMC_STOP_TRANSMISSION)
> +               send |= MESON_SDHC_SEND_DATA_STOP;
> +
> +       if (cmd->flags & MMC_RSP_PRESENT)
> +               send |= MESON_SDHC_SEND_CMD_HAS_RESP;
> +
> +       if (cmd->flags & MMC_RSP_136) {
> +               send |= MESON_SDHC_SEND_RESP_LEN;
> +               send |= MESON_SDHC_SEND_RESP_NO_CRC;
> +       }
> +
> +       if (!(cmd->flags & MMC_RSP_CRC))
> +               send |= MESON_SDHC_SEND_RESP_NO_CRC;
> +
> +       if (cmd->flags & MMC_RSP_BUSY)
> +               send |= MESON_SDHC_SEND_R1B;
> +
> +       /* enable the new IRQs and mask all pending ones */
> +       regmap_write(host->regmap, MESON_SDHC_ICTL, ictl);
> +       regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
> +
> +       regmap_write(host->regmap, MESON_SDHC_ARGU, cmd->arg);
> +
> +       regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
> +                          MESON_SDHC_CTRL_PACK_LEN,
> +                          FIELD_PREP(MESON_SDHC_CTRL_PACK_LEN, pack_len));
> +
> +       if (cmd->data)
> +               regmap_write(host->regmap, MESON_SDHC_ADDR,
> +                            sg_dma_address(cmd->data->sg));
> +
> +       meson_mx_sdhc_wait_cmd_ready(mmc);
> +
> +       if (cmd->data)
> +               host->platform->set_pdma(mmc);
> +
> +       if (host->platform->wait_before_send)
> +               host->platform->wait_before_send(mmc);
> +
> +       regmap_write(host->regmap, MESON_SDHC_SEND, send);

Isn't there a configurable timeout to set for the command?

I mean the driver sets mmc->max_busy_timeout to 30s in ->probe(), but
can the timeout be configured to a lower value?

[...]

> +static void meson_mx_sdhc_wait_before_send_meson8(struct mmc_host *mmc)
> +{
> +       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
> +       u32 val;
> +       int ret;
> +
> +       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, val,
> +                                      val == 0, 1, 200);

Please use defines for timeout values.

> +       if (ret)
> +               dev_warn(mmc_dev(mmc),
> +                        "Failed to wait for ESTA to clear: 0x%08x\n", val);
> +
> +       if (host->cmd->data && host->cmd->data->flags & MMC_DATA_WRITE) {
> +               ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT,
> +                                              val,
> +                                              val & MESON_SDHC_STAT_TXFIFO_CNT,
> +                                              1, 200);

Ditto.

> +               if (ret)
> +                       dev_warn(mmc_dev(mmc),
> +                                "Failed to wait for TX FIFO to fill\n");
> +       }
> +}
> +

[...]

> +
> +static int meson_mx_sdhc_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct meson_mx_sdhc_host *host;
> +       struct mmc_host *mmc;
> +       void __iomem *base;
> +       int ret, irq;
> +
> +       mmc = mmc_alloc_host(sizeof(*host), dev);
> +       if (!mmc)
> +               return -ENOMEM;
> +
> +       ret = devm_add_action_or_reset(dev, (void(*)(void *))mmc_free_host,
> +                                      mmc);
> +       if (ret) {
> +               dev_err(dev, "Failed to register mmc_free_host action\n");
> +               return ret;
> +       }
> +
> +       host = mmc_priv(mmc);
> +       host->mmc = mmc;
> +
> +       platform_set_drvdata(pdev, host);
> +
> +       host->platform = device_get_match_data(dev);
> +       if (!host->platform)
> +               return -EINVAL;
> +
> +       base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(base))
> +               return PTR_ERR(base);
> +
> +       host->regmap = devm_regmap_init_mmio(dev, base,
> +                                            &meson_mx_sdhc_regmap_config);
> +       if (IS_ERR(host->regmap))
> +               return PTR_ERR(host->regmap);
> +
> +       host->pclk = devm_clk_get(dev, "pclk");
> +       if (IS_ERR(host->pclk))
> +               return PTR_ERR(host->pclk);
> +
> +       /* accessing any register requires the module clock to be enabled: */
> +       ret = clk_prepare_enable(host->pclk);
> +       if (ret) {
> +               dev_err(dev, "Failed to enable 'pclk' clock\n");
> +               return ret;
> +       }
> +
> +       meson_mx_sdhc_init_hw(mmc);
> +
> +       ret = meson_mx_sdhc_register_clkc(host);
> +       if (ret)
> +               goto err_disable_pclk;
> +
> +       host->bulk_clks[0].id = "mod_clk";
> +       host->bulk_clks[1].id = "sd_clk";
> +       host->bulk_clks[2].id = "tx_clk";
> +       host->bulk_clks[3].id = "rx_clk";
> +       ret = devm_clk_bulk_get(dev, MESON_SDHC_NUM_BULK_CLKS,
> +                               host->bulk_clks);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       host->sd_clk = host->bulk_clks[1].clk;
> +
> +       /* Get regulators and the supported OCR mask */
> +       ret = mmc_regulator_get_supply(mmc);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       mmc->max_req_size = SZ_128K;
> +       mmc->max_seg_size = mmc->max_req_size;
> +       mmc->max_blk_count = FIELD_GET(MESON_SDHC_SEND_TOTAL_PACK, ~0);
> +       mmc->max_blk_size = MESON_SDHC_MAX_BLK_SIZE;
> +       mmc->max_busy_timeout = 30 * MSEC_PER_SEC;
> +       mmc->f_min = clk_round_rate(host->sd_clk, 1);
> +       mmc->f_max = clk_round_rate(host->sd_clk, ULONG_MAX);
> +       mmc->max_current_180 = 300;
> +       mmc->max_current_330 = 300;
> +       mmc->caps |= MMC_CAP_ERASE | MMC_CAP_HW_RESET;

Should you also set MMC_CAP_WAIT_WHILE_BUSY? It sounded like the
driver supported this.

> +       mmc->ops = &meson_mx_sdhc_ops;
> +
> +       ret = mmc_of_parse(mmc);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       ret = devm_request_threaded_irq(dev, irq, meson_mx_sdhc_irq,
> +                                       meson_mx_sdhc_irq_thread, IRQF_ONESHOT,
> +                                       NULL, host);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       ret = mmc_add_host(mmc);
> +       if (ret)
> +               goto err_clkc_pdev_unregister;
> +
> +       return 0;
> +
> +err_clkc_pdev_unregister:
> +       platform_device_unregister(host->clkc_pdev);
> +err_disable_pclk:
> +       clk_disable_unprepare(host->pclk);
> +       return ret;
> +}
> +

[...]

FYI: I left out all comments related to the clock provider
initialization. I think it makes better sense to review that code,
after you have converted to use the devm_clk_hw_register() and avoid
registering a separate driver for it.

Other than the minor comments, this looks good to me.

Kind regards
Uffe

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

  parent reply	other threads:[~2020-04-27 19:20 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-28  0:32 [PATCH v5 0/3] Amlogic 32-bit Meson SoC SDHC MMC controller driver Martin Blumenstingl
2020-03-28  0:32 ` Martin Blumenstingl
2020-03-28  0:32 ` Martin Blumenstingl
2020-03-28  0:32 ` [PATCH v5 1/3] dt-bindings: mmc: Document the Amlogic Meson SDHC MMC host controller Martin Blumenstingl
2020-03-28  0:32   ` Martin Blumenstingl
2020-03-28  0:32   ` Martin Blumenstingl
2020-03-30 16:28   ` Rob Herring
2020-03-30 16:28     ` Rob Herring
2020-03-30 16:28     ` Rob Herring
2020-03-28  0:32 ` [PATCH v5 2/3] clk: meson: add a driver for the Meson8/8b/8m2 SDHC clock controller Martin Blumenstingl
2020-03-28  0:32   ` Martin Blumenstingl
2020-03-28  0:32   ` Martin Blumenstingl
2020-04-27  8:41   ` Jerome Brunet
2020-04-27  8:41     ` Jerome Brunet
2020-04-27  8:41     ` Jerome Brunet
2020-04-27 16:33     ` Martin Blumenstingl
2020-04-27 16:33       ` Martin Blumenstingl
2020-04-27 16:33       ` Martin Blumenstingl
2020-04-27 16:58       ` Jerome Brunet
2020-04-27 16:58         ` Jerome Brunet
2020-04-27 16:58         ` Jerome Brunet
2020-03-28  0:32 ` [PATCH v5 3/3] mmc: host: meson-mx-sdhc: new driver for the Amlogic Meson SDHC host Martin Blumenstingl
2020-03-28  0:32   ` Martin Blumenstingl
2020-03-28  0:32   ` Martin Blumenstingl
2020-04-22 18:17   ` Anand Moon
2020-04-22 18:17     ` Anand Moon
2020-04-22 18:17     ` Anand Moon
2020-04-27 19:19   ` Ulf Hansson [this message]
2020-04-27 19:19     ` Ulf Hansson
2020-04-27 19:19     ` Ulf Hansson
2020-04-27 19:44     ` Martin Blumenstingl
2020-04-27 19:44       ` Martin Blumenstingl
2020-04-27 19:44       ` Martin Blumenstingl
2020-04-25 20:26 ` [PATCH v5 0/3] Amlogic 32-bit Meson SoC SDHC MMC controller driver Martin Blumenstingl
2020-04-25 20:26   ` Martin Blumenstingl
2020-04-25 20:26   ` Martin Blumenstingl
2020-04-27  6:58   ` Ulf Hansson
2020-04-27  6:58     ` Ulf Hansson
2020-04-27  6:58     ` Ulf Hansson
2020-04-27  8:56 ` Jerome Brunet
2020-04-27  8:56   ` Jerome Brunet
2020-04-27  8:56   ` Jerome Brunet
2020-04-27 16:23   ` Martin Blumenstingl
2020-04-27 16:23     ` Martin Blumenstingl
2020-04-27 16:23     ` Martin Blumenstingl
2020-04-27 16:46     ` Jerome Brunet
2020-04-27 16:46       ` Jerome Brunet
2020-04-27 16:46       ` Jerome Brunet
2020-04-27 18:35       ` Ulf Hansson
2020-04-27 18:35         ` Ulf Hansson
2020-04-27 18:35         ` Ulf Hansson
2020-04-27 19:31         ` Martin Blumenstingl
2020-04-27 19:31           ` Martin Blumenstingl
2020-04-27 19:31           ` Martin Blumenstingl

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAPDyKFqsG4kyABmxn__gAbe4fBmuZ=4mdFpRaCL0ih7QZEhwzQ@mail.gmail.com' \
    --to=ulf.hansson@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=jianxin.pan@amlogic.com \
    --cc=linux-amlogic@lists.infradead.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=lnykww@gmail.com \
    --cc=mark.rutland@arm.com \
    --cc=martin.blumenstingl@googlemail.com \
    --cc=robh+dt@kernel.org \
    --cc=yinxin_1989@aliyun.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.