2015-02-10 00:07:25

by Scott Branden

[permalink] [raw]
Subject: [PATCH 0/4] Add support for IPROC SDHCI controller

This series of patchsets contains the IPROC SDHCI driver used
in a series of Broadcom SoCs
Quirks are also added to support this controller.

Corneliu Doban (1):
mmc: sdhci: do not set AUTO_CMD12 for multi-block CMD53

Scott Branden (3):
mmc: sdhci: add quirk for ACMD23 broken
mmc: sdhci-iproc: add IPROC SDHCI driver
mmc: sdhci-iproc: add device tree bindings

.../devicetree/bindings/mmc/brcm,sdhci-iproc.txt | 23 ++
drivers/mmc/host/Kconfig | 14 ++
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-iproc.c | 241 +++++++++++++++++++++
drivers/mmc/host/sdhci.c | 7 +-
include/linux/mmc/sdhci.h | 2 +
6 files changed, 286 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt
create mode 100644 drivers/mmc/host/sdhci-iproc.c

--
2.2.2


2015-02-10 00:07:43

by Scott Branden

[permalink] [raw]
Subject: [PATCH 1/4] mmc: sdhci: add quirk for ACMD23 broken

Add quirk to handle broken auto-CMD23.
Some controllers do not respond after the first auto-CMD23 is issued.

This allows CMD23 to still work (mandatory for the faster UHS-I mode)
rather than disabling CMD23 entirely via SDHCI_QUIRK2_HOST_NO_CMD23.

Signed-off by: Corneliu Doban <[email protected]>
Signed-off-by: Scott Branden <[email protected]>

Signed-off-by: Scott Branden <[email protected]>
---
drivers/mmc/host/sdhci.c | 3 ++-
include/linux/mmc/sdhci.h | 2 ++
2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index f1a488e..fcf78ab 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -3085,7 +3085,8 @@ int sdhci_add_host(struct sdhci_host *host)
/* Auto-CMD23 stuff only works in ADMA or PIO. */
if ((host->version >= SDHCI_SPEC_300) &&
((host->flags & SDHCI_USE_ADMA) ||
- !(host->flags & SDHCI_USE_SDMA))) {
+ !(host->flags & SDHCI_USE_SDMA)) &&
+ !(host->quirks2 & SDHCI_QUIRK2_ACMD23_BROKEN)) {
host->flags |= SDHCI_AUTO_CMD23;
DBG("%s: Auto-CMD23 available\n", mmc_hostname(mmc));
} else {
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index f767a0d..a29e9bb 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -106,6 +106,8 @@ struct sdhci_host {
#define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10)
/* Capability register bit-63 indicates HS400 support */
#define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11)
+/* Controller broken with using ACMD23 */
+#define SDHCI_QUIRK2_ACMD23_BROKEN (1<<12)

int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
--
2.2.2

2015-02-10 00:10:35

by Scott Branden

[permalink] [raw]
Subject: [PATCH 2/4] mmc: sdhci: do not set AUTO_CMD12 for multi-block CMD53

From: Corneliu Doban <[email protected]>

For CMD53 in block mode, the host does not need to stop the transfer,
as it stops when the block count (present in CMD53) is reached.

Signed-off-by: Corneliu Doban <[email protected]>
Signed-off-by: Scott Branden <[email protected]>
---
drivers/mmc/host/sdhci.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index fcf78ab..f35d413 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -28,6 +28,7 @@
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
#include <linux/mmc/slot-gpio.h>

#include "sdhci.h"
@@ -934,7 +935,8 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
* If we are sending CMD23, CMD12 never gets sent
* on successful completion (so no Auto-CMD12).
*/
- if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12))
+ if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) &&
+ (cmd->opcode != SD_IO_RW_EXTENDED))
mode |= SDHCI_TRNS_AUTO_CMD12;
else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
mode |= SDHCI_TRNS_AUTO_CMD23;
--
2.2.2

2015-02-10 00:07:23

by Scott Branden

[permalink] [raw]
Subject: [PATCH 3/4] mmc: sdhci-iproc: add IPROC SDHCI driver

Add IPROC SDHCI driver for IPROC family of Broadcom devices.

Acked-by: Ray Jui <[email protected]>
Signed-off-by: Corneliu Doban <[email protected]>
Signed-off-by: Scott Branden <[email protected]>
---
drivers/mmc/host/Kconfig | 14 +++
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-iproc.c | 241 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 256 insertions(+)
create mode 100644 drivers/mmc/host/sdhci-iproc.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 2d6fbdd..d6a2ff1 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -292,6 +292,20 @@ config MMC_SDHCI_BCM2835

If unsure, say N.

+config MMC_SDHCI_IPROC
+ tristate "SDHCI platform support for the iProc SD/MMC Controller"
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
+ depends on MMC_SDHCI_PLTFM
+ default ARCH_BCM_IPROC
+ select MMC_SDHCI_IO_ACCESSORS
+ help
+ This selects the iProc SD/MMC controller.
+
+ If you have an IPROC platform with SD or MMC devices,
+ say Y or M here.
+
+ If unsure, say N.
+
config MMC_MOXART
tristate "MOXART SD/MMC Host Controller support"
depends on ARCH_MOXART && MMC
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index f7b0a77..32f24bd 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -70,6 +70,7 @@ 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
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
+obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o

diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
new file mode 100644
index 0000000..4139d34
--- /dev/null
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * iProc SDHCI platform driver
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mmc/host.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include "sdhci-pltfm.h"
+
+struct sdhci_iproc_data {
+ const struct sdhci_pltfm_data *pdata;
+ u32 caps;
+ u32 caps1;
+};
+
+struct sdhci_iproc_host {
+ const struct sdhci_iproc_data *data;
+ u32 shadow_cmd;
+ u32 shadow_blk;
+};
+
+#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
+
+static inline u32 sdhci_iproc_readl(struct sdhci_host *host, int reg)
+{
+ u32 val = readl(host->ioaddr + reg);
+
+ pr_debug("%s: readl [0x%02x] 0x%08x\n",
+ mmc_hostname(host->mmc), reg, val);
+ return val;
+}
+
+static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg)
+{
+ u32 val = sdhci_iproc_readl(host, (reg & ~3));
+ u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
+ return word;
+}
+
+static u8 sdhci_iproc_readb(struct sdhci_host *host, int reg)
+{
+ u32 val = sdhci_iproc_readl(host, (reg & ~3));
+ u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
+ return byte;
+}
+
+static inline void sdhci_iproc_writel(struct sdhci_host *host, u32 val, int reg)
+{
+ pr_debug("%s: writel [0x%02x] 0x%08x\n",
+ mmc_hostname(host->mmc), reg, val);
+
+ writel(val, host->ioaddr + reg);
+
+ if (host->clock <= 400000) {
+ /* Round up to micro-second four SD clock delay */
+ if (host->clock)
+ udelay((4 * 1000000 + host->clock - 1) / host->clock);
+ else
+ udelay(10);
+ }
+}
+
+/*
+ * The Arasan has a bugette whereby it may lose the content of successive
+ * writes to the same register that are within two SD-card clock cycles of
+ * each other (a clock domain crossing problem). The data
+ * register does not have this problem, which is just as well - otherwise we'd
+ * have to nobble the DMA engine too.
+ *
+ * This wouldn't be a problem with the code except that we can only write the
+ * controller with 32-bit writes. So two different 16-bit registers are
+ * written back to back creates the problem.
+ *
+ * In reality, this only happens when SDHCI_BLOCK_SIZE and SDHCI_BLOCK_COUNT
+ * are written followed by SDHCI_TRANSFER_MODE and SDHCI_COMMAND.
+ * The BLOCK_SIZE and BLOCK_COUNT are meaningless until a command issued so
+ * the work around can be further optimized. We can keep shadow values of
+ * BLOCK_SIZE, BLOCK_COUNT, and TRANSFER_MODE until a COMMAND is issued.
+ * Then, write the BLOCK_SIZE+BLOCK_COUNT in a single 32-bit write followed
+ * by the TRANSFER+COMMAND in another 32-bit write.
+ */
+static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_iproc_host *iproc_host = pltfm_host->priv;
+ u32 word_shift = REG_OFFSET_IN_BITS(reg);
+ u32 mask = 0xffff << word_shift;
+ u32 oldval, newval;
+
+ if (reg == SDHCI_COMMAND) {
+ /* Write the block now as we are issuing a command */
+ if (iproc_host->shadow_blk != 0) {
+ sdhci_iproc_writel(host, iproc_host->shadow_blk,
+ SDHCI_BLOCK_SIZE);
+ iproc_host->shadow_blk = 0;
+ }
+ oldval = iproc_host->shadow_cmd;
+ } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
+ /* Block size and count are stored in shadow reg */
+ oldval = iproc_host->shadow_blk;
+ } else {
+ /* Read reg, all other registers are not shadowed */
+ oldval = sdhci_iproc_readl(host, (reg & ~3));
+ }
+ newval = (oldval & ~mask) | (val << word_shift);
+
+ if (reg == SDHCI_TRANSFER_MODE) {
+ /* Save the transfer mode until the command is issued */
+ iproc_host->shadow_cmd = newval;
+ } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
+ /* Save the block info until the command is issued */
+ iproc_host->shadow_blk = newval;
+ } else {
+ /* Command or other regular 32-bit write */
+ sdhci_iproc_writel(host, newval, reg & ~3);
+ }
+}
+
+static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+ u32 oldval = sdhci_iproc_readl(host, (reg & ~3));
+ u32 byte_shift = REG_OFFSET_IN_BITS(reg);
+ u32 mask = 0xff << byte_shift;
+ u32 newval = (oldval & ~mask) | (val << byte_shift);
+
+ sdhci_iproc_writel(host, newval, reg & ~3);
+}
+
+static const struct sdhci_ops sdhci_iproc_ops = {
+ .read_l = sdhci_iproc_readl,
+ .read_w = sdhci_iproc_readw,
+ .read_b = sdhci_iproc_readb,
+ .write_l = sdhci_iproc_writel,
+ .write_w = sdhci_iproc_writew,
+ .write_b = sdhci_iproc_writeb,
+ .set_clock = sdhci_set_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = {
+ .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
+ .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN,
+ .ops = &sdhci_iproc_ops,
+};
+
+static const struct sdhci_iproc_data iproc_data = {
+ .pdata = &sdhci_iproc_pltfm_data,
+ .caps = 0x05E90000,
+ .caps1 = 0x00000064,
+};
+
+static const struct of_device_id sdhci_iproc_of_match[] = {
+ { .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match);
+
+static int sdhci_iproc_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ const struct sdhci_iproc_data *iproc_data;
+ struct sdhci_host *host;
+ struct sdhci_iproc_host *iproc_host;
+ struct sdhci_pltfm_host *pltfm_host;
+ int ret;
+
+ match = of_match_device(sdhci_iproc_of_match, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+ iproc_data = match->data;
+
+ host = sdhci_pltfm_init(pdev, iproc_data->pdata, sizeof(*iproc_host));
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ pltfm_host = sdhci_priv(host);
+ iproc_host = sdhci_pltfm_priv(pltfm_host);
+
+ iproc_host->data = iproc_data;
+
+ mmc_of_parse(host->mmc);
+ sdhci_get_of_property(pdev);
+
+ /* Enable EMMC 1/8V DDR capable */
+ host->mmc->caps |= MMC_CAP_1_8V_DDR;
+
+ pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pltfm_host->clk)) {
+ ret = PTR_ERR(pltfm_host->clk);
+ goto err;
+ }
+
+ if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) {
+ host->caps = iproc_host->data->caps;
+ host->caps1 = iproc_host->data->caps1;
+ }
+
+ return sdhci_add_host(host);
+
+err:
+ sdhci_pltfm_free(pdev);
+ return ret;
+}
+
+static int sdhci_iproc_remove(struct platform_device *pdev)
+{
+ return sdhci_pltfm_unregister(pdev);
+}
+
+static struct platform_driver sdhci_iproc_driver = {
+ .driver = {
+ .name = "sdhci-iproc",
+ .of_match_table = sdhci_iproc_of_match,
+ .pm = SDHCI_PLTFM_PMOPS,
+ },
+ .probe = sdhci_iproc_probe,
+ .remove = sdhci_iproc_remove,
+};
+module_platform_driver(sdhci_iproc_driver);
+
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("IPROC SDHCI driver");
+MODULE_LICENSE("GPL v2");
--
2.2.2

2015-02-10 00:10:03

by Scott Branden

[permalink] [raw]
Subject: [PATCH 4/4] mmc: sdhci-iproc: add device tree bindings

Add device tree binding documentation for IPROC SDHCI driver.

Acked-by: Ray Jui <[email protected]>
Signed-off-by: Corneliu Doban <[email protected]>
Signed-off-by: Scott Branden <[email protected]>
---
.../devicetree/bindings/mmc/brcm,sdhci-iproc.txt | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt

diff --git a/Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt b/Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt
new file mode 100644
index 0000000..72cc9cc
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt
@@ -0,0 +1,23 @@
+Broadcom IPROC SDHCI controller
+
+This file documents differences between the core properties described
+by mmc.txt and the properties that represent the IPROC SDHCI controller.
+
+Required properties:
+- compatible : Should be "brcm,sdhci-iproc-cygnus".
+- clocks : The clock feeding the SDHCI controller.
+
+Optional properties:
+ - sdhci,auto-cmd12: specifies that controller should use auto CMD12.
+
+Example:
+
+sdhci0: sdhci@0x18041000 {
+ compatible = "brcm,sdhci-iproc-cygnus";
+ reg = <0x18041000 0x100>;
+ interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&lcpll0_clks BCM_CYGNUS_LCPLL0_SDIO_CLK>;
+ bus-width = <4>;
+ sdhci,auto-cmd12;
+ no-1-8-v;
+};
--
2.2.2

2015-02-26 17:28:24

by Scott Branden

[permalink] [raw]
Subject: Re: [PATCH 0/4] Add support for IPROC SDHCI controller

Hi Chris,

I have not heard any more feedback on this patchset. Is it in your
queue to review or merge into your tree?

Thanks,
Scott

On 15-02-09 04:06 PM, Scott Branden wrote:
> This series of patchsets contains the IPROC SDHCI driver used
> in a series of Broadcom SoCs
> Quirks are also added to support this controller.
>
> Corneliu Doban (1):
> mmc: sdhci: do not set AUTO_CMD12 for multi-block CMD53
>
> Scott Branden (3):
> mmc: sdhci: add quirk for ACMD23 broken
> mmc: sdhci-iproc: add IPROC SDHCI driver
> mmc: sdhci-iproc: add device tree bindings
>
> .../devicetree/bindings/mmc/brcm,sdhci-iproc.txt | 23 ++
> drivers/mmc/host/Kconfig | 14 ++
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/sdhci-iproc.c | 241 +++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 7 +-
> include/linux/mmc/sdhci.h | 2 +
> 6 files changed, 286 insertions(+), 2 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt
> create mode 100644 drivers/mmc/host/sdhci-iproc.c
>