2015-07-29 14:22:42

by Ludovic Desroches

[permalink] [raw]
Subject: [PATCH v2 0/3] introduce driver for the Atmel SDMMC

Hi,

This set of patches introduce a driver for the new Atmel SDMMC device avaible
on SAMA5D2 SoC.

There is also a resend of an old patch which has not been taken. Ulf asked
for some reviews since it could impact all sdhci devices but nobody did it...
This patch is not necessary for patch 2/3. It only fixes a special use case. If
there are objections about it, drop it, I don't want to delay the Atmel SDMMC
driver inclusion only for this patch.

Changes:
- from v1:
- update license

Ludovic Desroches (3):
mmc: sdhci: switch from programmable clock mode to divided one if
needed
mmc: sdhci-of-at91: introduce driver for the Atmel SDMMC
MAINTAINERS: add entry for Atmel sdhci-of-at91 driver

.../devicetree/bindings/mmc/sdhci-atmel.txt | 21 +++
MAINTAINERS | 6 +
drivers/mmc/host/Kconfig | 8 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-of-at91.c | 192 +++++++++++++++++++++
drivers/mmc/host/sdhci.c | 29 +++-
6 files changed, 248 insertions(+), 9 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-atmel.txt
create mode 100644 drivers/mmc/host/sdhci-of-at91.c

--
2.5.0


2015-07-29 14:22:46

by Ludovic Desroches

[permalink] [raw]
Subject: [PATCH v2 1/3] mmc: sdhci: switch from programmable clock mode to divided one if needed

In programmable mode, if the clock frequency is too high, the divider
can be too small to meet the clock frequency requirement especially to
init the SD card. In this case, switch to the divided clock mode.

Signed-off-by: Ludovic Desroches <[email protected]>
---
drivers/mmc/host/sdhci.c | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index bc14452..32cf274 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1152,6 +1152,7 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
int real_div = div, clk_mul = 1;
u16 clk = 0;
unsigned long timeout;
+ bool switch_base_clk = false;

host->mmc->actual_clock = 0;

@@ -1189,15 +1190,25 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
<= clock)
break;
}
- /*
- * Set Programmable Clock Mode in the Clock
- * Control register.
- */
- clk = SDHCI_PROG_CLOCK_MODE;
- real_div = div;
- clk_mul = host->clk_mul;
- div--;
- } else {
+ if ((host->max_clk * host->clk_mul / div) <= clock) {
+ /*
+ * Set Programmable Clock Mode in the Clock
+ * Control register.
+ */
+ clk = SDHCI_PROG_CLOCK_MODE;
+ real_div = div;
+ clk_mul = host->clk_mul;
+ div--;
+ } else {
+ /*
+ * Divisor can be too small to reach clock
+ * speed requirement. Then use the base clock.
+ */
+ switch_base_clk = true;
+ }
+ }
+
+ if (!host->clk_mul || switch_base_clk) {
/* Version 3.00 divisors must be a multiple of 2. */
if (host->max_clk <= clock)
div = 1;
--
2.5.0

2015-07-29 14:22:48

by Ludovic Desroches

[permalink] [raw]
Subject: [PATCH v2 2/3] mmc: sdhci-of-at91: introduce driver for the Atmel SDMMC

Introduce driver for he Atmel SDMMC available on sama5d2. It is a sdhci
compliant controller.

Signed-off-by: Ludovic Desroches <[email protected]>
---
.../devicetree/bindings/mmc/sdhci-atmel.txt | 21 +++
drivers/mmc/host/Kconfig | 8 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-of-at91.c | 192 +++++++++++++++++++++
4 files changed, 222 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-atmel.txt
create mode 100644 drivers/mmc/host/sdhci-of-at91.c

diff --git a/Documentation/devicetree/bindings/mmc/sdhci-atmel.txt b/Documentation/devicetree/bindings/mmc/sdhci-atmel.txt
new file mode 100644
index 0000000..1b662d7
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sdhci-atmel.txt
@@ -0,0 +1,21 @@
+* Atmel SDHCI controller
+
+This file documents the differences between the core properties in
+Documentation/devicetree/bindings/mmc/mmc.txt and the properties used by the
+sdhci-of-at91 driver.
+
+Required properties:
+- compatible: Must be "atmel,sama5d2-sdhci".
+- clocks: Phandlers to the clocks.
+- clock-names: Must be "hclock", "multclk", "baseclk";
+
+
+Example:
+
+sdmmc0: sdio-host@a0000000 {
+ compatible = "atmel,sama5d2-sdhci";
+ reg = <0xa0000000 0x300>;
+ interrupts = <31 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&sdmmc0_hclk>, <&sdmmc0_gclk>, <&main>;
+ clock-names = "hclock", "multclk", "baseclk";
+};
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index fd9a58e..c1c75e8 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -129,6 +129,14 @@ config MMC_SDHCI_OF_ARASAN

If unsure, say N.

+config MMC_SDHCI_OF_AT91
+ tristate "SDHCI OF support for the Atmel SDMMC controller"
+ depends on MMC_SDHCI_PLTFM
+ depends on OF
+ select MMC_SDHCI_IO_ACCESSORS
+ help
+ This selects the Atmel SDMMC driver
+
config MMC_SDHCI_OF_ESDHC
tristate "SDHCI OF support for the Freescale eSDHC controller"
depends on MMC_SDHCI_PLTFM
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e928d61..4f3452a 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o
+obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c
new file mode 100644
index 0000000..7a9f4b1
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-at91.c
@@ -0,0 +1,192 @@
+/*
+ * Atmel SDMMC controller driver.
+ *
+ * Copyright (C) 2015 Atmel,
+ * 2015 Ludovic Desroches <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mmc/host.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "sdhci-pltfm.h"
+
+#define SDMMC_CACR 0x230
+#define SDMMC_CACR_CAPWREN BIT(0)
+#define SDMMC_CACR_KEY (0x46 << 8)
+
+struct sdhci_at91_priv {
+ struct clk *hclock;
+ struct clk *gck;
+ struct clk *mainck;
+};
+
+static const struct sdhci_ops sdhci_at91_sama5d2_ops = {
+ .set_clock = sdhci_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static const struct sdhci_pltfm_data soc_data_sama5d2 = {
+ .ops = &sdhci_at91_sama5d2_ops,
+};
+
+static const struct of_device_id sdhci_at91_dt_match[] = {
+ { .compatible = "atmel,sama5d2-sdhci", .data = &soc_data_sama5d2 },
+ {}
+};
+
+static int sdhci_at91_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ const struct sdhci_pltfm_data *soc_data;
+ struct sdhci_host *host;
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_at91_priv *priv;
+ unsigned int caps0, caps1;
+ unsigned int clk_base, clk_mul;
+ unsigned int gck_rate, real_gck_rate;
+ int ret;
+
+ match = of_match_device(sdhci_at91_dt_match, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+ soc_data = match->data;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "unable to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ priv->mainck = devm_clk_get(&pdev->dev, "baseclk");
+ if (IS_ERR(priv->mainck)) {
+ dev_err(&pdev->dev, "failed to get baseclk\n");
+ return PTR_ERR(priv->mainck);
+ }
+
+ priv->hclock = devm_clk_get(&pdev->dev, "hclock");
+ if (IS_ERR(priv->hclock)) {
+ dev_err(&pdev->dev, "failed to get hclock\n");
+ return PTR_ERR(priv->hclock);
+ }
+
+ priv->gck = devm_clk_get(&pdev->dev, "multclk");
+ if (IS_ERR(priv->gck)) {
+ dev_err(&pdev->dev, "failed to get multclk\n");
+ return PTR_ERR(priv->gck);
+ }
+
+ host = sdhci_pltfm_init(pdev, soc_data, 0);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ /*
+ * The mult clock is provided by as a generated clock by the PMC
+ * controller. In order to set the rate of gck, we have to get the
+ * base clock rate and the clock mult from capabilities.
+ */
+ clk_prepare_enable(priv->hclock);
+ caps0 = readl(host->ioaddr + SDHCI_CAPABILITIES);
+ caps1 = readl(host->ioaddr + SDHCI_CAPABILITIES_1);
+ clk_base = (caps0 & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
+ clk_mul = (caps1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT;
+ gck_rate = clk_base * 1000000 * (clk_mul + 1);
+ ret = clk_set_rate(priv->gck, gck_rate);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to set gck");
+ goto hclock_disable_unprepare;
+ return -EINVAL;
+ }
+ /*
+ * We need to check if we have the requested rate for gck because in
+ * some cases this rate could be not supported. If it happens, the rate
+ * is the closest one gck can provide. We have to update the value
+ * of clk mul.
+ */
+ real_gck_rate = clk_get_rate(priv->gck);
+ if (real_gck_rate != gck_rate) {
+ clk_mul = real_gck_rate / (clk_base * 1000000) - 1;
+ caps1 &= (~SDHCI_CLOCK_MUL_MASK);
+ caps1 |= ((clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK);
+ /* Set capabilities in r/w mode. */
+ writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR);
+ writel(caps1, host->ioaddr + SDHCI_CAPABILITIES_1);
+ /* Set capabilities in ro mode. */
+ writel(0, host->ioaddr + SDMMC_CACR);
+ dev_info(&pdev->dev, "update clk mul to %u as gck rate is %u Hz\n",
+ clk_mul, real_gck_rate);
+ }
+
+ clk_prepare_enable(priv->mainck);
+ clk_prepare_enable(priv->gck);
+
+ pltfm_host = sdhci_priv(host);
+ pltfm_host->priv = priv;
+
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto clocks_disable_unprepare;
+
+ sdhci_get_of_property(pdev);
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto clocks_disable_unprepare;
+
+ return 0;
+
+clocks_disable_unprepare:
+ clk_disable_unprepare(priv->gck);
+ clk_disable_unprepare(priv->mainck);
+hclock_disable_unprepare:
+ clk_disable_unprepare(priv->hclock);
+ sdhci_pltfm_free(pdev);
+ return ret;
+}
+
+static int sdhci_at91_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_at91_priv *priv = pltfm_host->priv;
+
+ sdhci_pltfm_unregister(pdev);
+
+ clk_disable_unprepare(priv->gck);
+ clk_disable_unprepare(priv->hclock);
+ clk_disable_unprepare(priv->mainck);
+
+ return 0;
+}
+
+static struct platform_driver sdhci_at91_driver = {
+ .driver = {
+ .name = "sdhci-at91",
+ .owner = THIS_MODULE,
+ .of_match_table = sdhci_at91_dt_match,
+ .pm = SDHCI_PLTFM_PMOPS,
+ },
+ .probe = sdhci_at91_probe,
+ .remove = sdhci_at91_remove,
+};
+
+module_platform_driver(sdhci_at91_driver);
+
+MODULE_DESCRIPTION("SDHCI driver for at91");
+MODULE_AUTHOR("Ludovic Desroches <[email protected]>");
+MODULE_LICENSE("GPL v2");
--
2.5.0

2015-07-29 14:22:51

by Ludovic Desroches

[permalink] [raw]
Subject: [PATCH v2 3/3] MAINTAINERS: add entry for Atmel sdhci-of-at91 driver

Add an entry for Atmel SDMMC device.

Signed-off-by: Ludovic Desroches <[email protected]>
---
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index e7bdbac..6480ce9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1880,6 +1880,12 @@ L: [email protected]
S: Supported
F: drivers/mtd/nand/atmel_nand*

+ATMEL SDMMC DRIVER
+M: Ludovic Desroches <[email protected]>
+L: [email protected]
+S: Supported
+F: drivers/mmc/host/sdhci-of-at91.c
+
ATMEL SPI DRIVER
M: Nicolas Ferre <[email protected]>
S: Supported
--
2.5.0

2015-08-18 08:33:47

by Ludovic Desroches

[permalink] [raw]
Subject: Re: [PATCH v2 0/3] introduce driver for the Atmel SDMMC

Hi,

On Wed, Jul 29, 2015 at 04:22:45PM +0200, Ludovic Desroches wrote:
> Hi,
>
> This set of patches introduce a driver for the new Atmel SDMMC device avaible
> on SAMA5D2 SoC.
>
> There is also a resend of an old patch which has not been taken. Ulf asked
> for some reviews since it could impact all sdhci devices but nobody did it...
> This patch is not necessary for patch 2/3. It only fixes a special use case. If
> there are objections about it, drop it, I don't want to delay the Atmel SDMMC
> driver inclusion only for this patch.

Ping, any objections about this driver?

>
> Changes:
> - from v1:
> - update license
>
> Ludovic Desroches (3):
> mmc: sdhci: switch from programmable clock mode to divided one if
> needed
> mmc: sdhci-of-at91: introduce driver for the Atmel SDMMC
> MAINTAINERS: add entry for Atmel sdhci-of-at91 driver
>
> .../devicetree/bindings/mmc/sdhci-atmel.txt | 21 +++
> MAINTAINERS | 6 +
> drivers/mmc/host/Kconfig | 8 +
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/sdhci-of-at91.c | 192 +++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 29 +++-
> 6 files changed, 248 insertions(+), 9 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-atmel.txt
> create mode 100644 drivers/mmc/host/sdhci-of-at91.c
>
> --
> 2.5.0
>

2015-08-25 12:07:21

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH v2 0/3] introduce driver for the Atmel SDMMC

On 29 July 2015 at 16:22, Ludovic Desroches <[email protected]> wrote:
> Hi,
>
> This set of patches introduce a driver for the new Atmel SDMMC device avaible
> on SAMA5D2 SoC.
>
> There is also a resend of an old patch which has not been taken. Ulf asked
> for some reviews since it could impact all sdhci devices but nobody did it...
> This patch is not necessary for patch 2/3. It only fixes a special use case. If
> there are objections about it, drop it, I don't want to delay the Atmel SDMMC
> driver inclusion only for this patch.
>
> Changes:
> - from v1:
> - update license
>
> Ludovic Desroches (3):
> mmc: sdhci: switch from programmable clock mode to divided one if
> needed
> mmc: sdhci-of-at91: introduce driver for the Atmel SDMMC
> MAINTAINERS: add entry for Atmel sdhci-of-at91 driver
>
> .../devicetree/bindings/mmc/sdhci-atmel.txt | 21 +++
> MAINTAINERS | 6 +
> drivers/mmc/host/Kconfig | 8 +
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/sdhci-of-at91.c | 192 +++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 29 +++-
> 6 files changed, 248 insertions(+), 9 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-atmel.txt
> create mode 100644 drivers/mmc/host/sdhci-of-at91.c
>
> --
> 2.5.0
>

Thanks, applied all three patches for next!

Kind regards
Uffe