* [PATCH 0/5] Add intial support to DW MMC host on ZTE SoC
From: Jun Nie @ 2016-10-24 9:11 UTC (permalink / raw)
To: shawn.guo, xie.baoyou
Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
Add intial support to DW MMC host on ZTE SoC. It include platform
specific wrapper driver and workarounds for fifo quirk.
Patches are prepared based on latest dw mmc runtime change:
https://github.com/jh80chung/dw-mmc.git for-ulf
Jun Nie (5):
mmc: dt-bindings: add ZTE MMC bindings
mmc: zx: Initial support for ZX mmc controller
Documentation: synopsys-dw-mshc: add binding for fifo quirks
mmc: dw: Add fifo address override property
mmc: dw: Add fifo watermark quirk
.../devicetree/bindings/mmc/synopsys-dw-mshc.txt | 13 ++
.../devicetree/bindings/mmc/zx-dw-mshc.txt | 34 +++
drivers/mmc/host/Kconfig | 9 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/dw_mmc-zx.c | 230 +++++++++++++++++++++
drivers/mmc/host/dw_mmc-zx.h | 23 +++
drivers/mmc/host/dw_mmc.c | 17 +-
include/linux/mmc/dw_mmc.h | 3 +
8 files changed, 328 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt
create mode 100644 drivers/mmc/host/dw_mmc-zx.c
create mode 100644 drivers/mmc/host/dw_mmc-zx.h
--
1.9.1
^ permalink raw reply
* [PATCH 1/5] mmc: dt-bindings: add ZTE MMC bindings
From: Jun Nie @ 2016-10-24 9:11 UTC (permalink / raw)
To: shawn.guo, xie.baoyou
Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
In-Reply-To: <1477300316-1420-1-git-send-email-jun.nie@linaro.org>
Document the device-tree binding of ZTE MMC host
Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
.../devicetree/bindings/mmc/zx-dw-mshc.txt | 34 ++++++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt
diff --git a/Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt
new file mode 100644
index 0000000..8ad506e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt
@@ -0,0 +1,34 @@
+* ZTE specific extensions to the Synopsys Designware Mobile Storage
+ Host Controller
+
+The Synopsys designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core Synopsys dw mshc controller properties described
+by synopsys-dw-mshc.txt and the properties used by the ZTE specific
+extensions to the Synopsys Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be
+ - "zx,dw-mshc": for ZX SoCs
+
+Example:
+
+ mmc1: mmc@1110000 {
+ compatible = "zte,dw-mshc";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x01110000 0x1000>;
+ interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+ fifo-depth = <32>;
+ fifo-addr-override = <0x200>;
+ bus-width = <4>;
+ clock-frequency = <50000000>;
+ clocks = <&topcrm SD0_AHB>, <&topcrm SD0_WCLK>;
+ clock-names = "biu", "ciu";
+ num-slots = <1>;
+ max-frequency = <50000000>;
+ cap-sdio-irq;
+ cap-sd-highspeed;
+ status = "disabled";
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH 2/5] mmc: zx: Initial support for ZX mmc controller
From: Jun Nie @ 2016-10-24 9:11 UTC (permalink / raw)
To: shawn.guo, xie.baoyou
Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
In-Reply-To: <1477300316-1420-1-git-send-email-jun.nie@linaro.org>
This platform driver adds initial support for the DW host controller
found on ZTE SoCs.
It has been tested on ZX296718 EVB board currently. More support on
timing tuning will be added when hardware is available.
Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
drivers/mmc/host/Kconfig | 9 ++
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/dw_mmc-zx.c | 230 +++++++++++++++++++++++++++++++++++++++++++
drivers/mmc/host/dw_mmc-zx.h | 23 +++++
4 files changed, 263 insertions(+)
create mode 100644 drivers/mmc/host/dw_mmc-zx.c
create mode 100644 drivers/mmc/host/dw_mmc-zx.h
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5274f50..2b3202c 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -662,6 +662,15 @@ config MMC_DW_ROCKCHIP
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on RK3066, RK3188 and RK3288 SoC's.
+config MMC_DW_ZX
+ tristate "ZTE specific extensions for Synopsys DW Memory Card Interface"
+ depends on MMC_DW
+ select MMC_DW_PLTFM
+ help
+ This selects support for ZTE SoC specific extensions to the
+ Synopsys DesignWare Memory Card Interface driver. Select this option
+ for platforms based on ZX296718 SoC's.
+
config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support"
depends on HAS_DMA
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e2bdaaf..9766143 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
+obj-$(CONFIG_MMC_DW_ZX) += dw_mmc-zx.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o
diff --git a/drivers/mmc/host/dw_mmc-zx.c b/drivers/mmc/host/dw_mmc-zx.c
new file mode 100644
index 0000000..d0e4ef2
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-zx.c
@@ -0,0 +1,230 @@
+/*
+ * ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver
+ *
+ * Copyright (C) 2016, Linaro Ltd.
+ * Copyright (C) 2016, ZTE Corp.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+#include "dw_mmc-zx.h"
+
+#define ZX_DLL_LOCKED BIT(2)
+
+struct dw_mci_zx_priv_data {
+ struct regmap *sysc_base;
+};
+
+static int dw_mci_zx_emmc_set_delay(struct dw_mci *host, unsigned int delay,
+ unsigned int clk_flag)
+{
+ struct dw_mci_zx_priv_data *priv = host->priv;
+ struct regmap *sysc_base = priv->sysc_base;
+ unsigned int clksel;
+ unsigned int loop = 1000;
+ int ret;
+
+ ret = regmap_write(sysc_base, LB_AON_EMMC_CFG_REG0,
+ PARA_DLL_START_POINT(4) | PARA_DLL_LOCK_NUM(4));
+ if (ret)
+ return ret;
+
+ ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG1, &clksel);
+ if (ret)
+ return ret;
+
+ if (clk_flag) {
+ clksel &= ~(CLK_SAMP_DELAY(0x7F));
+ clksel |= (delay << 8);
+ } else {
+ clksel &= ~(READ_DQS_DELAY(0x7F));
+ clksel |= delay;
+ }
+
+ regmap_write(sysc_base, LB_AON_EMMC_CFG_REG1, clksel);
+ regmap_write(sysc_base, LB_AON_EMMC_CFG_REG0,
+ PARA_DLL_START_POINT(4) | PARA_DLL_LOCK_NUM(4) |
+ DLL_REG_SET);
+
+ do {
+ ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG2, &clksel);
+ if (ret)
+ return ret;
+
+ } while (--loop && !(clksel & ZX_DLL_LOCKED));
+
+ if (!loop) {
+ dev_err(host->dev, "Error: %s dll lock fail\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int dw_mci_zx_emmc_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+{
+ struct dw_mci *host = slot->host;
+ struct mmc_host *mmc = slot->mmc;
+ int len, start = 0, end = 0, delay, best = 0;
+ int ret = 0;
+
+ for (delay = 1 ; delay < 128; delay++) {
+ ret = dw_mci_zx_emmc_set_delay(host, delay, 1);
+ if (ret)
+ return ret;
+
+ if (mmc_send_tuning(mmc, opcode, NULL)) {
+ if (start >= 0) {
+ end = delay - 1;
+ /* check and update longest good range */
+ if ((end - start) > len) {
+ best = (start + end) >> 1;
+ len = end - start;
+ }
+ }
+ start = -1;
+ end = 0;
+ continue;
+ }
+ if (start < 0)
+ start = delay;
+ }
+
+ if (start >= 0) {
+ end = delay - 1;
+ if ((end - start) > len) {
+ best = (start + end) >> 1;
+ len = end - start;
+ }
+ }
+ if (best < 0)
+ return -EIO;
+
+ dev_info(host->dev, "%s best range: start %d end %d\n", __func__,
+ start, end);
+ dw_mci_zx_emmc_set_delay(host, best, 1);
+ return 0;
+}
+
+static int dw_mci_zx_prepare_hs400_tuning(struct dw_mci *host,
+ struct mmc_ios *ios)
+{
+ int ret;
+
+ /* config phase shift 90 */
+ ret = dw_mci_zx_emmc_set_delay(host, 32, 0);
+ if (ret < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int dw_mci_zx_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+{
+ struct dw_mci *host = slot->host;
+
+ if (host->verid == 0x290a) /* emmc */
+ return dw_mci_zx_emmc_execute_tuning(slot, opcode);
+
+ return 0;
+}
+
+static int dw_mci_zx_parse_dt(struct dw_mci *host)
+{
+ struct device_node *np = host->dev->of_node;
+ struct device_node *node;
+ struct dw_mci_zx_priv_data *priv;
+ struct regmap *sysc_base;
+ int ret;
+
+ node = of_parse_phandle(np, "zte,aon-syscon", 0);
+ if (node) {
+ sysc_base = syscon_node_to_regmap(node);
+ of_node_put(node);
+
+ if (IS_ERR(sysc_base)) {
+ ret = PTR_ERR(sysc_base);
+ if (ret != -EPROBE_DEFER)
+ dev_err(host->dev, "Can't get syscon: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->sysc_base = sysc_base;
+ host->priv = priv;
+
+ return 0;
+}
+
+static unsigned long zx_dwmmc_caps[3] = {
+ MMC_CAP_HW_RESET,
+ 0,
+ 0,
+};
+
+static const struct dw_mci_drv_data zx_drv_data = {
+ .caps = zx_dwmmc_caps,
+ .execute_tuning = dw_mci_zx_execute_tuning,
+ .prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning,
+ .parse_dt = dw_mci_zx_parse_dt,
+};
+
+static const struct of_device_id dw_mci_zx_match[] = {
+ { .compatible = "zte,dw-mshc", .data = &zx_drv_data},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_zx_match);
+
+static int dw_mci_zx_probe(struct platform_device *pdev)
+{
+ const struct dw_mci_drv_data *drv_data;
+ const struct of_device_id *match;
+
+ match = of_match_node(dw_mci_zx_match, pdev->dev.of_node);
+ drv_data = match->data;
+
+ return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
+ dw_mci_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver dw_mci_zx_pltfm_driver = {
+ .probe = dw_mci_zx_probe,
+ .remove = dw_mci_pltfm_remove,
+ .driver = {
+ .name = "dwmmc_zx",
+ .of_match_table = dw_mci_zx_match,
+ .pm = &dw_mci_zx_dev_pm_ops,
+ },
+};
+
+module_platform_driver(dw_mci_zx_pltfm_driver);
+
+MODULE_DESCRIPTION("ZTE emmc/sd driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc-zx.h b/drivers/mmc/host/dw_mmc-zx.h
new file mode 100644
index 0000000..b1aac52
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-zx.h
@@ -0,0 +1,23 @@
+#ifndef _DW_MMC_ZX_H_
+#define _DW_MMC_ZX_H_
+
+/* dll reg offset*/
+#define LB_AON_EMMC_CFG_REG0 0x1B0
+#define LB_AON_EMMC_CFG_REG1 0x1B4
+#define LB_AON_EMMC_CFG_REG2 0x1B8
+
+/* LB_AON_EMMC_CFG_REG0 register defines */
+#define PARA_DLL_START_POINT(x) (((x) & 0xFF) << 0)
+#define DLL_REG_SET BIT(8)
+#define PARA_DLL_LOCK_NUM(x) (((x) & 7) << 16)
+#define PARA_PHASE_DET_SEL(x) (((x) & 7) << 20)
+#define PARA_DLL_BYPASS_MODE BIT(23)
+#define PARA_HALF_CLK_MODE BIT(24)
+
+/* LB_AON_EMMC_CFG_REG1 register defines */
+#define READ_DQS_DELAY(x) (((x) & 0x7F) << 0)
+#define READ_DQS_BYPASS_MODE BIT(7)
+#define CLK_SAMP_DELAY(x) (((x) & 0x7F) << 8)
+#define CLK_SAMP_BYPASS_MODE BIT(15)
+
+#endif /* _DW_MMC_ZX_H_ */
--
1.9.1
^ permalink raw reply related
* [PATCH 3/5] Documentation: synopsys-dw-mshc: add binding for fifo quirks
From: Jun Nie @ 2016-10-24 9:11 UTC (permalink / raw)
To: shawn.guo, xie.baoyou
Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
In-Reply-To: <1477300316-1420-1-git-send-email-jun.nie@linaro.org>
Add fifo-addr-override property and fifo-watermark-quirk property to
synopsys-dw-mshc bindings. It is intended to provide workarounds to
support more SoCs that break current assumption.
See Documentation/devicetree/bindings/reset/reset.txt for details.
Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
index 4e00e85..eb64921 100644
--- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
@@ -76,6 +76,17 @@ Optional properties:
* broken-cd: as documented in mmc core bindings.
+* fifo-addr-override: Override fifo address with value provided by DT. The FIFO
+ reg offset of version 0x210A break current assumption that 0x100 (version < 0x240A)
+ and 0x200(version >= 0x240A) in some implementation. So this property serves as
+ workaround.
+
+* fifo-watermark-quirk: Data done irq is expected if data length is less than
+ watermark in PIO mode. But fifo watermark is requested to be aligned with data
+ length in some SoC so that TX/RX irq can be generated with data done irq. Add the
+ watermark quirk to mark this requirement and force fifo watermark setting
+ accordingly.
+
* vmmc-supply: The phandle to the regulator to use for vmmc. If this is
specified we'll defer probe until we can find this regulator.
@@ -103,6 +114,8 @@ board specific portions as listed below.
interrupts = <0 75 0>;
#address-cells = <1>;
#size-cells = <0>;
+ fifo-addr-override = <0x200>;
+ fifo-watermark-quirk;
};
[board specific internal DMA resources]
--
1.9.1
^ permalink raw reply related
* [PATCH 4/5] mmc: dw: Add fifo address override property
From: Jun Nie @ 2016-10-24 9:11 UTC (permalink / raw)
To: shawn.guo, xie.baoyou
Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
In-Reply-To: <1477300316-1420-1-git-send-email-jun.nie@linaro.org>
The FIFO address of version 0x210A conflict with assumption that
0x100(version < 0x240A) and 0x200(version >= 0x240A) in some dw mmc
implementation. So this property serves as a workaround to set fifo
address with value provided by DT node.
Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
drivers/mmc/host/dw_mmc.c | 6 ++++++
include/linux/mmc/dw_mmc.h | 2 ++
2 files changed, 8 insertions(+)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 1c9ee36..9dcd6da 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -2955,6 +2955,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
+ of_property_read_u32(np, "fifo-addr-override",
+ &host->fifo_addr_override);
+
if (!of_property_read_u32(np, "clock-frequency", &clock_frequency))
pdata->bus_hz = clock_frequency;
@@ -3163,6 +3166,9 @@ int dw_mci_probe(struct dw_mci *host)
else
host->fifo_reg = host->regs + DATA_240A_OFFSET;
+ if (host->fifo_addr_override)
+ host->fifo_reg = host->regs + host->fifo_addr_override;
+
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
host->irq_flags, "dw-mci", host);
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index f5af2bd..4866ef5 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -107,6 +107,7 @@ struct dw_mci_dma_slave {
* @ciu_clk: Pointer to card interface unit clock instance.
* @slot: Slots sharing this MMC controller.
* @fifo_depth: depth of FIFO.
+ * @fifo_addr_override: override fifo reg offset with this value.
* @data_shift: log2 of FIFO item size.
* @part_buf_start: Start index in part_buf.
* @part_buf_count: Bytes of partial data in part_buf.
@@ -154,6 +155,7 @@ struct dw_mci {
spinlock_t irq_lock;
void __iomem *regs;
void __iomem *fifo_reg;
+ u32 fifo_addr_override;
struct scatterlist *sg;
struct sg_mapping_iter sg_miter;
--
1.9.1
^ permalink raw reply related
* [PATCH 5/5] mmc: dw: Add fifo watermark quirk
From: Jun Nie @ 2016-10-24 9:11 UTC (permalink / raw)
To: shawn.guo, xie.baoyou
Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
In-Reply-To: <1477300316-1420-1-git-send-email-jun.nie@linaro.org>
Data done irq is expected if data length is less than
watermark in PIO mode. But fifo watermark is requested
to be aligned with data length in some SoC so that TX/RX
irq can be generated with data done irq. Add the
watermark quirk to mark this requirement and force
fifo watermark setting accordingly.
Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
drivers/mmc/host/dw_mmc.c | 11 +++++++++--
include/linux/mmc/dw_mmc.h | 1 +
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 9dcd6da..d746c4a 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1111,11 +1111,15 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
mci_writel(host, CTRL, temp);
/*
- * Use the initial fifoth_val for PIO mode.
+ * Use the initial fifoth_val for PIO mode. If wm_quirk
+ * is set, we set watermark same as data size.
* If next issued data may be transfered by DMA mode,
* prev_blksz should be invalidated.
*/
- mci_writel(host, FIFOTH, host->fifoth_val);
+ if (host->wm_quirk)
+ dw_mci_adjust_fifoth(host, data);
+ else
+ mci_writel(host, FIFOTH, host->fifoth_val);
host->prev_blksz = 0;
} else {
/*
@@ -2958,6 +2962,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
of_property_read_u32(np, "fifo-addr-override",
&host->fifo_addr_override);
+ if (of_get_property(np, "fifo-watermark-quirk", NULL))
+ host->wm_quirk = true;
+
if (!of_property_read_u32(np, "clock-frequency", &clock_frequency))
pdata->bus_hz = clock_frequency;
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index 4866ef5..e7cc584 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -156,6 +156,7 @@ struct dw_mci {
void __iomem *regs;
void __iomem *fifo_reg;
u32 fifo_addr_override;
+ u32 wm_quirk;
struct scatterlist *sg;
struct sg_mapping_iter sg_miter;
--
1.9.1
^ permalink raw reply related
* Re: [PATCH v3 2/3] mmc: tmio-mmc: add support for 32bit data port
From: Geert Uytterhoeven @ 2016-10-24 11:06 UTC (permalink / raw)
To: Wolfram Sang
Cc: Chris Brandt, Ulf Hansson, Wolfram Sang, Sergei Shtylyov,
Simon Horman, linux-mmc@vger.kernel.org,
linux-renesas-soc@vger.kernel.org
In-Reply-To: <20161021215639.GB1450@katana>
Hi Wolfram,
On Fri, Oct 21, 2016 at 11:56 PM, Wolfram Sang <wsa@the-dreams.de> wrote:
>> For the VERSION register, the low byte is the version of the IP, but
>> the upper byte is a number that the design group that made the part
>
> I know. It is just that I haven't seen this one "in the wild" so far.
I think you can also see it on your tamed Genmai ;-)
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH v3 2/3] mmc: tmio-mmc: add support for 32bit data port
From: Wolfram Sang @ 2016-10-24 11:11 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Chris Brandt, Ulf Hansson, Wolfram Sang, Sergei Shtylyov,
Simon Horman, linux-mmc@vger.kernel.org,
linux-renesas-soc@vger.kernel.org
In-Reply-To: <CAMuHMdU3q5PTXumWgizK1RG1nK6X6nCq1ySZ6z0DCH0uy+ZB2Q@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 518 bytes --]
On Mon, Oct 24, 2016 at 01:06:38PM +0200, Geert Uytterhoeven wrote:
> Hi Wolfram,
>
> On Fri, Oct 21, 2016 at 11:56 PM, Wolfram Sang <wsa@the-dreams.de> wrote:
> >> For the VERSION register, the low byte is the version of the IP, but
> >> the upper byte is a number that the design group that made the part
> >
> > I know. It is just that I haven't seen this one "in the wild" so far.
>
> I think you can also see it on your tamed Genmai ;-)
I intentionally did not write "couldn't have seen this" ;)
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH] mmc: dw_mmc-pltfm: fix the potential NULL pointer dereference
From: Shawn Lin @ 2016-10-24 12:06 UTC (permalink / raw)
To: Jaehoon Chung; +Cc: linux-mmc, shawn.lin, ulf.hansson, stable, gregkh
In-Reply-To: <20161021105757.22712-1-jh80.chung@samsung.com>
On 2016/10/21 18:57, Jaehoon Chung wrote:
> platform_get_resource can be returned the NULL pointer.
> Then regs->start should be referred to NULL Pointer.
> devm_ioremap_resource() checks whether res is NULL or not.
>
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> ---
> drivers/mmc/host/dw_mmc-pltfm.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
Looks good,
Reviewed-by: Shawn Lin <shawn.lin@rock-chips.com>
> diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
> index b486fef..1236d49b 100644
> --- a/drivers/mmc/host/dw_mmc-pltfm.c
> +++ b/drivers/mmc/host/dw_mmc-pltfm.c
> @@ -47,12 +47,13 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
> host->pdata = pdev->dev.platform_data;
>
> regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> - /* Get registers' physical base address */
> - host->phy_regs = regs->start;
> host->regs = devm_ioremap_resource(&pdev->dev, regs);
> if (IS_ERR(host->regs))
> return PTR_ERR(host->regs);
>
> + /* Get registers' physical base address */
> + host->phy_regs = regs->start;
> +
> platform_set_drvdata(pdev, host);
> return dw_mci_probe(host);
> }
>
--
Best Regards
Shawn Lin
^ permalink raw reply
* Re: [PATCH 3/5] Documentation: synopsys-dw-mshc: add binding for fifo quirks
From: Shawn Lin @ 2016-10-24 12:19 UTC (permalink / raw)
To: Jun Nie, shawn.guo, xie.baoyou
Cc: shawn.lin, ulf.hansson, jh80.chung, jason.liu, linux-mmc
In-Reply-To: <1477300316-1420-4-git-send-email-jun.nie@linaro.org>
On 2016/10/24 17:11, Jun Nie wrote:
> Add fifo-addr-override property and fifo-watermark-quirk property to
> synopsys-dw-mshc bindings. It is intended to provide workarounds to
> support more SoCs that break current assumption.
>
> See Documentation/devicetree/bindings/reset/reset.txt for details.
>
> Signed-off-by: Jun Nie <jun.nie@linaro.org>
> ---
> Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
> index 4e00e85..eb64921 100644
> --- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
> +++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
> @@ -76,6 +76,17 @@ Optional properties:
>
> * broken-cd: as documented in mmc core bindings.
>
> +* fifo-addr-override: Override fifo address with value provided by DT. The FIFO
> + reg offset of version 0x210A break current assumption that 0x100 (version < 0x240A)
> + and 0x200(version >= 0x240A) in some implementation. So this property serves as
> + workaround.
Can we hardcode this to the code by checking the host version?
> +
> +* fifo-watermark-quirk: Data done irq is expected if data length is less than
> + watermark in PIO mode. But fifo watermark is requested to be aligned with data
> + length in some SoC so that TX/RX irq can be generated with data done irq. Add the
> + watermark quirk to mark this requirement and force fifo watermark setting
> + accordingly.
I would like to know if this limitation is *really* related to some
Socs or the version of 210A dw_mmc?
> +
> * vmmc-supply: The phandle to the regulator to use for vmmc. If this is
> specified we'll defer probe until we can find this regulator.
>
> @@ -103,6 +114,8 @@ board specific portions as listed below.
> interrupts = <0 75 0>;
> #address-cells = <1>;
> #size-cells = <0>;
> + fifo-addr-override = <0x200>;
> + fifo-watermark-quirk;
> };
>
> [board specific internal DMA resources]
>
--
Best Regards
Shawn Lin
^ permalink raw reply
* Re: [PATCH 2/5] mmc: zx: Initial support for ZX mmc controller
From: Shawn Lin @ 2016-10-24 12:29 UTC (permalink / raw)
To: Jun Nie, shawn.guo, xie.baoyou
Cc: shawn.lin, ulf.hansson, jh80.chung, jason.liu, linux-mmc
In-Reply-To: <1477300316-1420-3-git-send-email-jun.nie@linaro.org>
在 2016/10/24 17:11, Jun Nie 写道:
> This platform driver adds initial support for the DW host controller
> found on ZTE SoCs.
>
> It has been tested on ZX296718 EVB board currently. More support on
> timing tuning will be added when hardware is available.
>
> Signed-off-by: Jun Nie <jun.nie@linaro.org>
> ---
> drivers/mmc/host/Kconfig | 9 ++
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/dw_mmc-zx.c | 230 +++++++++++++++++++++++++++++++++++++++++++
> drivers/mmc/host/dw_mmc-zx.h | 23 +++++
> 4 files changed, 263 insertions(+)
> create mode 100644 drivers/mmc/host/dw_mmc-zx.c
> create mode 100644 drivers/mmc/host/dw_mmc-zx.h
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 5274f50..2b3202c 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -662,6 +662,15 @@ config MMC_DW_ROCKCHIP
> Synopsys DesignWare Memory Card Interface driver. Select this option
> for platforms based on RK3066, RK3188 and RK3288 SoC's.
>
> +config MMC_DW_ZX
> + tristate "ZTE specific extensions for Synopsys DW Memory Card Interface"
> + depends on MMC_DW
> + select MMC_DW_PLTFM
> + help
> + This selects support for ZTE SoC specific extensions to the
> + Synopsys DesignWare Memory Card Interface driver. Select this option
> + for platforms based on ZX296718 SoC's.
> +
> config MMC_SH_MMCIF
> tristate "SuperH Internal MMCIF support"
> depends on HAS_DMA
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index e2bdaaf..9766143 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -48,6 +48,7 @@ obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
> obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
> obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
> obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
> +obj-$(CONFIG_MMC_DW_ZX) += dw_mmc-zx.o
> obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
> obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
> obj-$(CONFIG_MMC_VUB300) += vub300.o
> diff --git a/drivers/mmc/host/dw_mmc-zx.c b/drivers/mmc/host/dw_mmc-zx.c
> new file mode 100644
> index 0000000..d0e4ef2
> --- /dev/null
> +++ b/drivers/mmc/host/dw_mmc-zx.c
> @@ -0,0 +1,230 @@
> +/*
> + * ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver
> + *
> + * Copyright (C) 2016, Linaro Ltd.
> + * Copyright (C) 2016, ZTE Corp.
> + *
> + * 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; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/mmc/dw_mmc.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
#include <linux/platform_device.h>
to keep alphabetical order
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include "dw_mmc.h"
> +#include "dw_mmc-pltfm.h"
> +#include "dw_mmc-zx.h"
> +
> +#define ZX_DLL_LOCKED BIT(2)
> +
> +struct dw_mci_zx_priv_data {
> + struct regmap *sysc_base;
> +};
> +
> +static int dw_mci_zx_emmc_set_delay(struct dw_mci *host, unsigned int delay,
> + unsigned int clk_flag)
I don't know if this is expected/legit way to control the delay/phase
by adding code here. It looks more general to me that we should use
clk_{set,get}_phase API.
> +{
> + struct dw_mci_zx_priv_data *priv = host->priv;
> + struct regmap *sysc_base = priv->sysc_base;
> + unsigned int clksel;
> + unsigned int loop = 1000;
> + int ret;
> +
> + ret = regmap_write(sysc_base, LB_AON_EMMC_CFG_REG0,
> + PARA_DLL_START_POINT(4) | PARA_DLL_LOCK_NUM(4));
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG1, &clksel);
> + if (ret)
> + return ret;
> +
> + if (clk_flag) {
> + clksel &= ~(CLK_SAMP_DELAY(0x7F));
> + clksel |= (delay << 8);
> + } else {
> + clksel &= ~(READ_DQS_DELAY(0x7F));
> + clksel |= delay;
> + }
> +
> + regmap_write(sysc_base, LB_AON_EMMC_CFG_REG1, clksel);
> + regmap_write(sysc_base, LB_AON_EMMC_CFG_REG0,
> + PARA_DLL_START_POINT(4) | PARA_DLL_LOCK_NUM(4) |
> + DLL_REG_SET);
> +
> + do {
> + ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG2, &clksel);
> + if (ret)
> + return ret;
> +
> + } while (--loop && !(clksel & ZX_DLL_LOCKED));
> +
> + if (!loop) {
> + dev_err(host->dev, "Error: %s dll lock fail\n", __func__);
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int dw_mci_zx_emmc_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
> +{
> + struct dw_mci *host = slot->host;
> + struct mmc_host *mmc = slot->mmc;
> + int len, start = 0, end = 0, delay, best = 0;
> + int ret = 0;
> +
> + for (delay = 1 ; delay < 128; delay++) {
> + ret = dw_mci_zx_emmc_set_delay(host, delay, 1);
> + if (ret)
> + return ret;
> +
> + if (mmc_send_tuning(mmc, opcode, NULL)) {
> + if (start >= 0) {
> + end = delay - 1;
> + /* check and update longest good range */
> + if ((end - start) > len) {
> + best = (start + end) >> 1;
> + len = end - start;
> + }
> + }
> + start = -1;
> + end = 0;
> + continue;
> + }
> + if (start < 0)
> + start = delay;
> + }
> +
> + if (start >= 0) {
> + end = delay - 1;
> + if ((end - start) > len) {
> + best = (start + end) >> 1;
> + len = end - start;
> + }
> + }
> + if (best < 0)
> + return -EIO;
> +
> + dev_info(host->dev, "%s best range: start %d end %d\n", __func__,
> + start, end);
> + dw_mci_zx_emmc_set_delay(host, best, 1);
> + return 0;
> +}
> +
> +static int dw_mci_zx_prepare_hs400_tuning(struct dw_mci *host,
> + struct mmc_ios *ios)
> +{
> + int ret;
> +
> + /* config phase shift 90 */
> + ret = dw_mci_zx_emmc_set_delay(host, 32, 0);
> + if (ret < 0)
> + return -EIO;
> +
> + return 0;
> +}
> +
> +static int dw_mci_zx_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
> +{
> + struct dw_mci *host = slot->host;
> +
> + if (host->verid == 0x290a) /* emmc */
> + return dw_mci_zx_emmc_execute_tuning(slot, opcode);
> +
> + return 0;
> +}
> +
> +static int dw_mci_zx_parse_dt(struct dw_mci *host)
> +{
> + struct device_node *np = host->dev->of_node;
> + struct device_node *node;
> + struct dw_mci_zx_priv_data *priv;
> + struct regmap *sysc_base;
> + int ret;
> +
> + node = of_parse_phandle(np, "zte,aon-syscon", 0);
> + if (node) {
> + sysc_base = syscon_node_to_regmap(node);
> + of_node_put(node);
> +
> + if (IS_ERR(sysc_base)) {
> + ret = PTR_ERR(sysc_base);
> + if (ret != -EPROBE_DEFER)
> + dev_err(host->dev, "Can't get syscon: %d\n",
> + ret);
> + return ret;
> + }
> + }
> +
> + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> + priv->sysc_base = sysc_base;
> + host->priv = priv;
> +
> + return 0;
> +}
> +
> +static unsigned long zx_dwmmc_caps[3] = {
> + MMC_CAP_HW_RESET,
> + 0,
> + 0,
> +};
> +
just a thought, maybe you could add the cap-mmc-hw-reset for your
emmc node on the DT as
1) you could save some bits here
2) Moreover, it's related to the board design since it isn't
guaranteed that the hw_rest pin won't be used for other purpose..
> +static const struct dw_mci_drv_data zx_drv_data = {
> + .caps = zx_dwmmc_caps,
> + .execute_tuning = dw_mci_zx_execute_tuning,
> + .prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning,
> + .parse_dt = dw_mci_zx_parse_dt,
> +};
> +
> +static const struct of_device_id dw_mci_zx_match[] = {
> + { .compatible = "zte,dw-mshc", .data = &zx_drv_data},
> +};
> +MODULE_DEVICE_TABLE(of, dw_mci_zx_match);
> +
> +static int dw_mci_zx_probe(struct platform_device *pdev)
> +{
> + const struct dw_mci_drv_data *drv_data;
> + const struct of_device_id *match;
> +
> + match = of_match_node(dw_mci_zx_match, pdev->dev.of_node);
> + drv_data = match->data;
> +
> + return dw_mci_pltfm_register(pdev, drv_data);
> +}
> +
> +static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> + pm_runtime_force_resume)
> + SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
> + dw_mci_runtime_resume,
> + NULL)
> +};
> +
> +static struct platform_driver dw_mci_zx_pltfm_driver = {
> + .probe = dw_mci_zx_probe,
> + .remove = dw_mci_pltfm_remove,
> + .driver = {
> + .name = "dwmmc_zx",
> + .of_match_table = dw_mci_zx_match,
> + .pm = &dw_mci_zx_dev_pm_ops,
> + },
> +};
> +
> +module_platform_driver(dw_mci_zx_pltfm_driver);
> +
> +MODULE_DESCRIPTION("ZTE emmc/sd driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/mmc/host/dw_mmc-zx.h b/drivers/mmc/host/dw_mmc-zx.h
> new file mode 100644
> index 0000000..b1aac52
> --- /dev/null
> +++ b/drivers/mmc/host/dw_mmc-zx.h
> @@ -0,0 +1,23 @@
> +#ifndef _DW_MMC_ZX_H_
> +#define _DW_MMC_ZX_H_
> +
> +/* dll reg offset*/
> +#define LB_AON_EMMC_CFG_REG0 0x1B0
> +#define LB_AON_EMMC_CFG_REG1 0x1B4
> +#define LB_AON_EMMC_CFG_REG2 0x1B8
> +
> +/* LB_AON_EMMC_CFG_REG0 register defines */
> +#define PARA_DLL_START_POINT(x) (((x) & 0xFF) << 0)
> +#define DLL_REG_SET BIT(8)
> +#define PARA_DLL_LOCK_NUM(x) (((x) & 7) << 16)
> +#define PARA_PHASE_DET_SEL(x) (((x) & 7) << 20)
> +#define PARA_DLL_BYPASS_MODE BIT(23)
> +#define PARA_HALF_CLK_MODE BIT(24)
> +
> +/* LB_AON_EMMC_CFG_REG1 register defines */
> +#define READ_DQS_DELAY(x) (((x) & 0x7F) << 0)
> +#define READ_DQS_BYPASS_MODE BIT(7)
> +#define CLK_SAMP_DELAY(x) (((x) & 0x7F) << 8)
> +#define CLK_SAMP_BYPASS_MODE BIT(15)
> +
> +#endif /* _DW_MMC_ZX_H_ */
>
--
Best Regards
Shawn Lin
^ permalink raw reply
* RE: [PATCH v3 2/3] mmc: tmio-mmc: add support for 32bit data port
From: Chris Brandt @ 2016-10-24 12:37 UTC (permalink / raw)
To: Wolfram Sang
Cc: Ulf Hansson, Wolfram Sang, Sergei Shtylyov, Geert Uytterhoeven,
Simon Horman, linux-mmc@vger.kernel.org,
linux-renesas-soc@vger.kernel.org
In-Reply-To: <20161021215639.GB1450@katana>
> > > This SWAP register exists on R-Car as well. Out of curiosity, what
> > > is the register value of 0xE804E0E4?
> >
> > 0xE804E0E4 = 0x0001
> >
> > So...something is there!
>
> I am quite convinced there is this BUSWIDTH register. If you are
> interested, try setting this to 0 and see if it works without the 32 bit
> register patches.
Well, it looks like I can't write that register location to 0.
It always keeps a value of 0x0001. So, it must be read-only.
It was a good idea though.
Still, I might try to find out what is there at that location out of curiosity.
Without my the 32-bit write patch, I get a lot of these messages:
sh_mobile_sdhi e804e800.sd: timeout waiting for SD bus idle
Chris
^ permalink raw reply
* [PATCH 1/3] mmc: mmci: clean up header defines
From: Linus Walleij @ 2016-10-24 14:21 UTC (permalink / raw)
To: linux-mmc, linux-arm-kernel, Srinivas Kandagatla
Cc: Russell King, Ulf Hansson, Linus Walleij
There was some confusion in the CPSM (Command Path State Machine)
and DPSM (Data Path State Machine) regarding the naming of the
registers, clarify the meaning of this acronym so the naming is
understandable, and consistently use BIT() to define these fields.
Include new definitions for a few bits found in a patch from
Srinivas Kandagatla.
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
drivers/mmc/host/mmci.c | 2 +-
drivers/mmc/host/mmci.h | 69 +++++++++++++++++++++++++++----------------------
2 files changed, 39 insertions(+), 32 deletions(-)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index df990bb8c873..79b135752d3d 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -210,7 +210,7 @@ static struct variant_data variant_qcom = {
MCI_QCOM_CLK_SELECT_IN_FBCLK,
.clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8,
.datactrl_mask_ddrmode = MCI_QCOM_CLK_SELECT_IN_DDR_MODE,
- .data_cmd_enable = MCI_QCOM_CSPM_DATCMD,
+ .data_cmd_enable = MCI_CPSM_QCOM_DATCMD,
.blksz_datactrl4 = true,
.datalength_bits = 24,
.pwrreg_powerup = MCI_PWR_UP,
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index a1f5e4f49e2a..8952285196cd 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -51,25 +51,27 @@
#define MCI_QCOM_CLK_SELECT_IN_DDR_MODE (BIT(14) | BIT(15))
#define MMCIARGUMENT 0x008
-#define MMCICOMMAND 0x00c
-#define MCI_CPSM_RESPONSE (1 << 6)
-#define MCI_CPSM_LONGRSP (1 << 7)
-#define MCI_CPSM_INTERRUPT (1 << 8)
-#define MCI_CPSM_PENDING (1 << 9)
-#define MCI_CPSM_ENABLE (1 << 10)
-/* Argument flag extenstions in the ST Micro versions */
-#define MCI_ST_SDIO_SUSP (1 << 11)
-#define MCI_ST_ENCMD_COMPL (1 << 12)
-#define MCI_ST_NIEN (1 << 13)
-#define MCI_ST_CE_ATACMD (1 << 14)
-/* Modified on Qualcomm Integrations */
-#define MCI_QCOM_CSPM_DATCMD BIT(12)
-#define MCI_QCOM_CSPM_MCIABORT BIT(13)
-#define MCI_QCOM_CSPM_CCSENABLE BIT(14)
-#define MCI_QCOM_CSPM_CCSDISABLE BIT(15)
-#define MCI_QCOM_CSPM_AUTO_CMD19 BIT(16)
-#define MCI_QCOM_CSPM_AUTO_CMD21 BIT(21)
+/* The command register controls the Command Path State Machine (CPSM) */
+#define MMCICOMMAND 0x00c
+#define MCI_CPSM_RESPONSE BIT(6)
+#define MCI_CPSM_LONGRSP BIT(7)
+#define MCI_CPSM_INTERRUPT BIT(8)
+#define MCI_CPSM_PENDING BIT(9)
+#define MCI_CPSM_ENABLE BIT(10)
+/* Command register flag extenstions in the ST Micro versions */
+#define MCI_CPSM_ST_SDIO_SUSP BIT(11)
+#define MCI_CPSM_ST_ENCMD_COMPL BIT(12)
+#define MCI_CPSM_ST_NIEN BIT(13)
+#define MCI_CPSM_ST_CE_ATACMD BIT(14)
+/* Command register flag extensions in the Qualcomm versions */
+#define MCI_CPSM_QCOM_PROGENA BIT(11)
+#define MCI_CPSM_QCOM_DATCMD BIT(12)
+#define MCI_CPSM_QCOM_MCIABORT BIT(13)
+#define MCI_CPSM_QCOM_CCSENABLE BIT(14)
+#define MCI_CPSM_QCOM_CCSDISABLE BIT(15)
+#define MCI_CPSM_QCOM_AUTO_CMD19 BIT(16)
+#define MCI_CPSM_QCOM_AUTO_CMD21 BIT(21)
#define MMCIRESPCMD 0x010
#define MMCIRESPONSE0 0x014
@@ -78,22 +80,27 @@
#define MMCIRESPONSE3 0x020
#define MMCIDATATIMER 0x024
#define MMCIDATALENGTH 0x028
+
+/* The data control register controls the Data Path State Machine (DPSM) */
#define MMCIDATACTRL 0x02c
-#define MCI_DPSM_ENABLE (1 << 0)
-#define MCI_DPSM_DIRECTION (1 << 1)
-#define MCI_DPSM_MODE (1 << 2)
-#define MCI_DPSM_DMAENABLE (1 << 3)
-#define MCI_DPSM_BLOCKSIZE (1 << 4)
+#define MCI_DPSM_ENABLE BIT(0)
+#define MCI_DPSM_DIRECTION BIT(1)
+#define MCI_DPSM_MODE BIT(2)
+#define MCI_DPSM_DMAENABLE BIT(3)
+#define MCI_DPSM_BLOCKSIZE BIT(4)
/* Control register extensions in the ST Micro U300 and Ux500 versions */
-#define MCI_ST_DPSM_RWSTART (1 << 8)
-#define MCI_ST_DPSM_RWSTOP (1 << 9)
-#define MCI_ST_DPSM_RWMOD (1 << 10)
-#define MCI_ST_DPSM_SDIOEN (1 << 11)
+#define MCI_ST_DPSM_RWSTART BIT(8)
+#define MCI_ST_DPSM_RWSTOP BIT(9)
+#define MCI_ST_DPSM_RWMOD BIT(10)
+#define MCI_ST_DPSM_SDIOEN BIT(11)
/* Control register extensions in the ST Micro Ux500 versions */
-#define MCI_ST_DPSM_DMAREQCTL (1 << 12)
-#define MCI_ST_DPSM_DBOOTMODEEN (1 << 13)
-#define MCI_ST_DPSM_BUSYMODE (1 << 14)
-#define MCI_ST_DPSM_DDRMODE (1 << 15)
+#define MCI_ST_DPSM_DMAREQCTL BIT(12)
+#define MCI_ST_DPSM_DBOOTMODEEN BIT(13)
+#define MCI_ST_DPSM_BUSYMODE BIT(14)
+#define MCI_ST_DPSM_DDRMODE BIT(15)
+/* Control register extensions in the Qualcomm versions */
+#define MCI_QCOM_DPSM_DATA_PEND BIT(17)
+#define MCI_QCOM_DPSM_RX_DATA_PEND BIT(20)
#define MMCIDATACNT 0x030
#define MMCISTATUS 0x034
--
2.7.4
^ permalink raw reply related
* [PATCH 2/3] mmc: mmci: refactor ST Micro busy detection
From: Linus Walleij @ 2016-10-24 14:21 UTC (permalink / raw)
To: linux-mmc, linux-arm-kernel, Srinivas Kandagatla
Cc: Russell King, Ulf Hansson, Linus Walleij
In-Reply-To: <1477318886-24851-1-git-send-email-linus.walleij@linaro.org>
The ST Micro-specific busy detection was made after the assumption
that only this variant supports busy detection. So when doing busy
detection, the host immediately tries to use some ST-specific
register bits.
Since the qualcomm variant also supports some busy detection
schemes, encapsulate the variant flags better in the variant struct
and prepare to add more variants by just providing some bitmasks
to the logic.
Put the entire busy detection logic within an if()-clause in the
mmci_cmd_irq() function so the code is only executed when busy
detection is enabled, and so that it is kept in (almost) one
place, and add comments describing what is going on so the
code can be understood.
Tested on the Ux500 by introducing some prints in the busy
detection path and noticing how the IRQ is enabled, used and
disabled successfully.
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
drivers/mmc/host/mmci.c | 113 +++++++++++++++++++++++++++++++++++-------------
drivers/mmc/host/mmci.h | 2 +-
2 files changed, 85 insertions(+), 30 deletions(-)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 79b135752d3d..06e19d8359e0 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -71,7 +71,12 @@ static unsigned int fmax = 515633;
* @f_max: maximum clk frequency supported by the controller.
* @signal_direction: input/out direction of bus signals can be indicated
* @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
- * @busy_detect: true if busy detection on dat0 is supported
+ * @busy_detect: true if the variant supports busy detection on DAT0.
+ * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM
+ * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register
+ * indicating that the card is busy
+ * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for
+ * getting busy end detection interrupts
* @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
* @explicit_mclk_control: enable explicit mclk control in driver.
* @qcom_fifo: enables qcom specific fifo pio read logic.
@@ -98,6 +103,9 @@ struct variant_data {
bool signal_direction;
bool pwrreg_clkgate;
bool busy_detect;
+ u32 busy_dpsm_flag;
+ u32 busy_detect_flag;
+ u32 busy_detect_mask;
bool pwrreg_nopower;
bool explicit_mclk_control;
bool qcom_fifo;
@@ -178,6 +186,9 @@ static struct variant_data variant_ux500 = {
.signal_direction = true,
.pwrreg_clkgate = true,
.busy_detect = true,
+ .busy_dpsm_flag = MCI_ST_DPSM_BUSYMODE,
+ .busy_detect_flag = MCI_ST_CARDBUSY,
+ .busy_detect_mask = MCI_ST_BUSYENDMASK,
.pwrreg_nopower = true,
};
@@ -199,6 +210,9 @@ static struct variant_data variant_ux500v2 = {
.signal_direction = true,
.pwrreg_clkgate = true,
.busy_detect = true,
+ .busy_dpsm_flag = MCI_ST_DPSM_BUSYMODE,
+ .busy_detect_flag = MCI_ST_CARDBUSY,
+ .busy_detect_mask = MCI_ST_BUSYENDMASK,
.pwrreg_nopower = true,
};
@@ -220,6 +234,7 @@ static struct variant_data variant_qcom = {
.qcom_dml = true,
};
+/* Busy detection for the ST Micro variant */
static int mmci_card_busy(struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
@@ -227,7 +242,7 @@ static int mmci_card_busy(struct mmc_host *mmc)
int busy = 0;
spin_lock_irqsave(&host->lock, flags);
- if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY)
+ if (readl(host->base + MMCISTATUS) & host->variant->busy_detect_flag)
busy = 1;
spin_unlock_irqrestore(&host->lock, flags);
@@ -294,8 +309,8 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
*/
static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
{
- /* Keep ST Micro busy mode if enabled */
- datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE;
+ /* Keep busy mode in DPSM if enabled */
+ datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag;
if (host->datactrl_reg != datactrl) {
host->datactrl_reg = datactrl;
@@ -973,37 +988,66 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
unsigned int status)
{
void __iomem *base = host->base;
- bool sbc, busy_resp;
+ bool sbc;
if (!cmd)
return;
sbc = (cmd == host->mrq->sbc);
- busy_resp = host->variant->busy_detect && (cmd->flags & MMC_RSP_BUSY);
- if (!((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
- MCI_CMDSENT|MCI_CMDRESPEND)))
+ /*
+ * We need to be one of these interrupts to be considered worth
+ * handling. Note that we tag on any latent IRQs postponed
+ * due to waiting for busy status.
+ */
+ if (!((status|host->busy_status) &
+ (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND)))
return;
- /* Check if we need to wait for busy completion. */
- if (host->busy_status && (status & MCI_ST_CARDBUSY))
- return;
+ /*
+ * ST Micro variant: handle busy detection.
+ */
+ if (host->variant->busy_detect) {
+ bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY);
- /* Enable busy completion if needed and supported. */
- if (!host->busy_status && busy_resp &&
- !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
- (readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) {
- writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND,
- base + MMCIMASK0);
- host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND);
- return;
- }
+ /* We are busy with a command, return */
+ if (host->busy_status &&
+ (status & host->variant->busy_detect_flag))
+ return;
+
+ /*
+ * We were not busy, but we now got a busy response on
+ * something that was not an error, and we double-check
+ * that the special busy status bit is still set before
+ * proceeding.
+ */
+ if (!host->busy_status && busy_resp &&
+ !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
+ (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {
+ /* Unmask the busy IRQ */
+ writel(readl(base + MMCIMASK0) |
+ host->variant->busy_detect_mask,
+ base + MMCIMASK0);
+ /*
+ * Now cache the last response status code (until
+ * the busy bit goes low), and return.
+ */
+ host->busy_status =
+ status & (MCI_CMDSENT|MCI_CMDRESPEND);
+ return;
+ }
- /* At busy completion, mask the IRQ and complete the request. */
- if (host->busy_status) {
- writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND,
- base + MMCIMASK0);
- host->busy_status = 0;
+ /*
+ * At this point we are not busy with a command, we have
+ * not recieved a new busy request, mask the busy IRQ and
+ * fall through to process the IRQ.
+ */
+ if (host->busy_status) {
+ writel(readl(base + MMCIMASK0) &
+ ~host->variant->busy_detect_mask,
+ base + MMCIMASK0);
+ host->busy_status = 0;
+ }
}
host->cmd = NULL;
@@ -1257,9 +1301,11 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
mmci_data_irq(host, host->data, status);
}
- /* Don't poll for busy completion in irq context. */
- if (host->busy_status)
- status &= ~MCI_ST_CARDBUSY;
+ /*
+ * Don't poll for busy completion in irq context.
+ */
+ if (host->variant->busy_detect && host->busy_status)
+ status &= ~host->variant->busy_detect_flag;
ret = 1;
} while (status);
@@ -1612,9 +1658,18 @@ static int mmci_probe(struct amba_device *dev,
/* We support these capabilities. */
mmc->caps |= MMC_CAP_CMD23;
+ /*
+ * Enable busy detection.
+ */
if (variant->busy_detect) {
mmci_ops.card_busy = mmci_card_busy;
- mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
+ /*
+ * Not all variants have a flag to enable busy detection
+ * in the DPSM, but if they do, set it here.
+ */
+ if (variant->busy_dpsm_flag)
+ mmci_write_datactrlreg(host,
+ host->variant->busy_dpsm_flag);
mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
mmc->max_busy_timeout = 0;
}
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 8952285196cd..38f6f1365ec4 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -174,7 +174,7 @@
/* Extended status bits for the ST Micro variants */
#define MCI_ST_SDIOITMASK (1 << 22)
#define MCI_ST_CEATAENDMASK (1 << 23)
-#define MCI_ST_BUSYEND (1 << 24)
+#define MCI_ST_BUSYENDMASK (1 << 24)
#define MMCIMASK1 0x040
#define MMCIFIFOCNT 0x048
--
2.7.4
^ permalink raw reply related
* [PATCH 3/3] RFC: mmc: mmci: add qcom specific program end support
From: Linus Walleij @ 2016-10-24 14:21 UTC (permalink / raw)
To: linux-mmc, linux-arm-kernel, Srinivas Kandagatla
Cc: Russell King, Ulf Hansson, Linus Walleij
In-Reply-To: <1477318886-24851-1-git-send-email-linus.walleij@linaro.org>
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
This patch adds support to programend interrupt which is very
specific to QCOM integration. This interrupt is use as busy signal
when a command forces the card to enter into programming state
like CMD6 writing to ext_csd registers.
Hopefully, this also fixes the __mmc_switch timeout issue reproted
with latest versions of the eMMC used on DB600c board.
This patch is based on a WIP patch from Srinivas Kandagatla and
augmented by Linus Walleij for another approach.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
Srinivas: please test to see if this fixes your problems. Sadly
it does *NOT* solve my APQ8060 issues, but it would be nice if
the common code path works for the busy detection on your
DB600c.
---
drivers/mmc/host/mmci.c | 21 +++++++++++++++++++--
drivers/mmc/host/mmci.h | 6 ++++++
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 06e19d8359e0..c0380713df34 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -72,7 +72,10 @@ static unsigned int fmax = 515633;
* @signal_direction: input/out direction of bus signals can be indicated
* @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
* @busy_detect: true if the variant supports busy detection on DAT0.
- * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM
+ * @busy_cpsm_flag: bitmask enabling busy detection in the CPSM (command
+ * path state machine)
+ * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM (data path
+ * state machine)
* @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register
* indicating that the card is busy
* @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for
@@ -103,6 +106,7 @@ struct variant_data {
bool signal_direction;
bool pwrreg_clkgate;
bool busy_detect;
+ u32 busy_cpsm_flag;
u32 busy_dpsm_flag;
u32 busy_detect_flag;
u32 busy_detect_mask;
@@ -229,6 +233,10 @@ static struct variant_data variant_qcom = {
.datalength_bits = 24,
.pwrreg_powerup = MCI_PWR_UP,
.f_max = 208000000,
+ .busy_detect = true,
+ .busy_cpsm_flag = MCI_CPSM_QCOM_PROGENA,
+ .busy_detect_flag = MCI_QCOM_PROGDONE,
+ .busy_detect_mask = MCI_QCOM_PROGDONEMASK,
.explicit_mclk_control = true,
.qcom_fifo = true,
.qcom_dml = true,
@@ -903,6 +911,15 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
if (/*interrupt*/0)
c |= MCI_CPSM_INTERRUPT;
+ /*
+ * Enable the program end interrupt for specific commands
+ * used for busy detection.
+ */
+ if (host->variant->busy_detect &&
+ (cmd->flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
+ c |= host->variant->busy_cpsm_flag;
+ }
+
if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
c |= host->variant->data_cmd_enable;
@@ -1005,7 +1022,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
return;
/*
- * ST Micro variant: handle busy detection.
+ * ST Micro and Qualcomm variants: handle busy detection.
*/
if (host->variant->busy_detect) {
bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 38f6f1365ec4..99abf02f560e 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -130,6 +130,8 @@
#define MCI_ST_SDIOIT (1 << 22)
#define MCI_ST_CEATAEND (1 << 23)
#define MCI_ST_CARDBUSY (1 << 24)
+/* Extended status bits for the QCOM variants */
+#define MCI_QCOM_PROGDONE (1 << 23)
#define MMCICLEAR 0x038
#define MCI_CMDCRCFAILCLR (1 << 0)
@@ -147,6 +149,8 @@
#define MCI_ST_SDIOITC (1 << 22)
#define MCI_ST_CEATAENDC (1 << 23)
#define MCI_ST_BUSYENDC (1 << 24)
+/* Extended status bits for the QCOM variants */
+#define MCI_QCOM_PROGDONECLR (1 << 23)
#define MMCIMASK0 0x03c
#define MCI_CMDCRCFAILMASK (1 << 0)
@@ -175,6 +179,8 @@
#define MCI_ST_SDIOITMASK (1 << 22)
#define MCI_ST_CEATAENDMASK (1 << 23)
#define MCI_ST_BUSYENDMASK (1 << 24)
+/* Extended status bits for the Qualcomm variants */
+#define MCI_QCOM_PROGDONEMASK (1 << 23)
#define MMCIMASK1 0x040
#define MMCIFIFOCNT 0x048
--
2.7.4
^ permalink raw reply related
* Re: Regression after "do not use CMD13 to get status after speed mode switch"
From: Linus Walleij @ 2016-10-24 14:23 UTC (permalink / raw)
To: Srinivas Kandagatla
Cc: linux-mmc@vger.kernel.org, Ulf Hansson, Chaotian Jing,
linux-arm-msm@vger.kernel.org, Bjorn Andersson, Stephen Boyd,
Andy Gross
In-Reply-To: <bb6c1f05-d81c-4284-5831-c9d49bf038f3@linaro.org>
On Thu, Oct 20, 2016 at 5:17 PM, Srinivas Kandagatla
<srinivas.kandagatla@linaro.org> wrote:
> I remember hitting timeout issue with new version of eMMC on SD600c-eval
> board, this patch/hack fixed it for me.
>
> https://git.linaro.org/people/srinivas.kandagatla/linux.git/commitdiff/e42d7427b9ce77174eef079be0e04e22bdb8fd07?hp=3e4bf0b78405694fd1ab4371c73e80932be21e3a
This doesn't work for me, sadly. I reworked the patch to use the same
codeflow as the Ux500/ST variant and posted my patches, but still no
luck (hope it helps though, and just wanted to illustrate how I think the
final patch should look).
Are you sure the MMCI derivates on the APQ8060 and MSM8660 has this
extra "PROGDONE" bit? (I guess you have the SoC reference manual for
these, I don't...)
> Also one more important thing was to do with regulator mode too.
> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=93b8e6fda11757a20df21f3fc18e12789841f173
Since my rootfs does come up after the 20 minute timeout I do not thing
power surges are the cause. This problem seems SD600c-specific.
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH 1/3] mmc: mmci: clean up header defines
From: Robin Murphy @ 2016-10-24 15:33 UTC (permalink / raw)
To: Linus Walleij, linux-mmc, linux-arm-kernel, Srinivas Kandagatla
Cc: Russell King, Ulf Hansson
In-Reply-To: <1477318886-24851-1-git-send-email-linus.walleij@linaro.org>
Hi Linus,
One tiny nit which stands out from a cleanup perspective...
On 24/10/16 15:21, Linus Walleij wrote:
> There was some confusion in the CPSM (Command Path State Machine)
> and DPSM (Data Path State Machine) regarding the naming of the
> registers, clarify the meaning of this acronym so the naming is
> understandable, and consistently use BIT() to define these fields.
>
> Include new definitions for a few bits found in a patch from
> Srinivas Kandagatla.
>
> Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> drivers/mmc/host/mmci.c | 2 +-
> drivers/mmc/host/mmci.h | 69 +++++++++++++++++++++++++++----------------------
> 2 files changed, 39 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index df990bb8c873..79b135752d3d 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -210,7 +210,7 @@ static struct variant_data variant_qcom = {
> MCI_QCOM_CLK_SELECT_IN_FBCLK,
> .clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8,
> .datactrl_mask_ddrmode = MCI_QCOM_CLK_SELECT_IN_DDR_MODE,
> - .data_cmd_enable = MCI_QCOM_CSPM_DATCMD,
> + .data_cmd_enable = MCI_CPSM_QCOM_DATCMD,
> .blksz_datactrl4 = true,
> .datalength_bits = 24,
> .pwrreg_powerup = MCI_PWR_UP,
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index a1f5e4f49e2a..8952285196cd 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -51,25 +51,27 @@
> #define MCI_QCOM_CLK_SELECT_IN_DDR_MODE (BIT(14) | BIT(15))
>
> #define MMCIARGUMENT 0x008
> -#define MMCICOMMAND 0x00c
> -#define MCI_CPSM_RESPONSE (1 << 6)
> -#define MCI_CPSM_LONGRSP (1 << 7)
> -#define MCI_CPSM_INTERRUPT (1 << 8)
> -#define MCI_CPSM_PENDING (1 << 9)
> -#define MCI_CPSM_ENABLE (1 << 10)
> -/* Argument flag extenstions in the ST Micro versions */
> -#define MCI_ST_SDIO_SUSP (1 << 11)
> -#define MCI_ST_ENCMD_COMPL (1 << 12)
> -#define MCI_ST_NIEN (1 << 13)
> -#define MCI_ST_CE_ATACMD (1 << 14)
>
> -/* Modified on Qualcomm Integrations */
> -#define MCI_QCOM_CSPM_DATCMD BIT(12)
> -#define MCI_QCOM_CSPM_MCIABORT BIT(13)
> -#define MCI_QCOM_CSPM_CCSENABLE BIT(14)
> -#define MCI_QCOM_CSPM_CCSDISABLE BIT(15)
> -#define MCI_QCOM_CSPM_AUTO_CMD19 BIT(16)
> -#define MCI_QCOM_CSPM_AUTO_CMD21 BIT(21)
> +/* The command register controls the Command Path State Machine (CPSM) */
> +#define MMCICOMMAND 0x00c
> +#define MCI_CPSM_RESPONSE BIT(6)
> +#define MCI_CPSM_LONGRSP BIT(7)
> +#define MCI_CPSM_INTERRUPT BIT(8)
> +#define MCI_CPSM_PENDING BIT(9)
> +#define MCI_CPSM_ENABLE BIT(10)
> +/* Command register flag extenstions in the ST Micro versions */
> +#define MCI_CPSM_ST_SDIO_SUSP BIT(11)
> +#define MCI_CPSM_ST_ENCMD_COMPL BIT(12)
> +#define MCI_CPSM_ST_NIEN BIT(13)
> +#define MCI_CPSM_ST_CE_ATACMD BIT(14)
> +/* Command register flag extensions in the Qualcomm versions */
> +#define MCI_CPSM_QCOM_PROGENA BIT(11)
> +#define MCI_CPSM_QCOM_DATCMD BIT(12)
> +#define MCI_CPSM_QCOM_MCIABORT BIT(13)
> +#define MCI_CPSM_QCOM_CCSENABLE BIT(14)
> +#define MCI_CPSM_QCOM_CCSDISABLE BIT(15)
> +#define MCI_CPSM_QCOM_AUTO_CMD19 BIT(16)
> +#define MCI_CPSM_QCOM_AUTO_CMD21 BIT(21)
These definitions take the format of MCI_register_{vendor_}field...
> #define MMCIRESPCMD 0x010
> #define MMCIRESPONSE0 0x014
> @@ -78,22 +80,27 @@
> #define MMCIRESPONSE3 0x020
> #define MMCIDATATIMER 0x024
> #define MMCIDATALENGTH 0x028
> +
> +/* The data control register controls the Data Path State Machine (DPSM) */
> #define MMCIDATACTRL 0x02c
> -#define MCI_DPSM_ENABLE (1 << 0)
> -#define MCI_DPSM_DIRECTION (1 << 1)
> -#define MCI_DPSM_MODE (1 << 2)
> -#define MCI_DPSM_DMAENABLE (1 << 3)
> -#define MCI_DPSM_BLOCKSIZE (1 << 4)
> +#define MCI_DPSM_ENABLE BIT(0)
> +#define MCI_DPSM_DIRECTION BIT(1)
> +#define MCI_DPSM_MODE BIT(2)
> +#define MCI_DPSM_DMAENABLE BIT(3)
> +#define MCI_DPSM_BLOCKSIZE BIT(4)
> /* Control register extensions in the ST Micro U300 and Ux500 versions */
> -#define MCI_ST_DPSM_RWSTART (1 << 8)
> -#define MCI_ST_DPSM_RWSTOP (1 << 9)
> -#define MCI_ST_DPSM_RWMOD (1 << 10)
> -#define MCI_ST_DPSM_SDIOEN (1 << 11)
> +#define MCI_ST_DPSM_RWSTART BIT(8)
> +#define MCI_ST_DPSM_RWSTOP BIT(9)
> +#define MCI_ST_DPSM_RWMOD BIT(10)
> +#define MCI_ST_DPSM_SDIOEN BIT(11)
> /* Control register extensions in the ST Micro Ux500 versions */
> -#define MCI_ST_DPSM_DMAREQCTL (1 << 12)
> -#define MCI_ST_DPSM_DBOOTMODEEN (1 << 13)
> -#define MCI_ST_DPSM_BUSYMODE (1 << 14)
> -#define MCI_ST_DPSM_DDRMODE (1 << 15)
> +#define MCI_ST_DPSM_DMAREQCTL BIT(12)
> +#define MCI_ST_DPSM_DBOOTMODEEN BIT(13)
> +#define MCI_ST_DPSM_BUSYMODE BIT(14)
> +#define MCI_ST_DPSM_DDRMODE BIT(15)
> +/* Control register extensions in the Qualcomm versions */
> +#define MCI_QCOM_DPSM_DATA_PEND BIT(17)
> +#define MCI_QCOM_DPSM_RX_DATA_PEND BIT(20)
...but these retain the previous MCI_{vendor_}register_field format - it
seems like a needless inconsistency, but if there is some good reason
then fair enough (I only have the vaguest familiarity with this IP/driver).
Robin.
>
> #define MMCIDATACNT 0x030
> #define MMCISTATUS 0x034
>
^ permalink raw reply
* Re: [RFC v2 2/2] mmc: sdhci: Ignore capability register when it comes to speeds and use DT binding instead when sdhci-cap-speed-modes-broken is set.
From: Zach Brown @ 2016-10-24 15:48 UTC (permalink / raw)
To: Adrian Hunter
Cc: ulf.hansson, robh+dt, mark.rutland, linux-mmc, devicetree,
linux-kernel
In-Reply-To: <d4547457-036a-dbb7-ddb8-b839e04949b7@intel.com>
On Mon, Oct 24, 2016 at 10:34:46AM +0300, Adrian Hunter wrote:
> On 22/10/16 00:35, Zach Brown wrote:
> > When the sdhci-cap-speed-modes-broken DT property is set, the driver
> > will ignore the bits of the capability registers that correspond to
> > speed modes.
> >
> > Signed-off-by: Zach Brown <zach.brown@ni.com>
> > ---
> > drivers/mmc/host/sdhci.c | 10 ++++++++++
> > 1 file changed, 10 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 1e25b01..59c62d3 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -22,6 +22,7 @@
> > #include <linux/scatterlist.h>
> > #include <linux/regulator/consumer.h>
> > #include <linux/pm_runtime.h>
> > +#include <linux/of.h>
> >
> > #include <linux/leds.h>
> >
> > @@ -3013,10 +3014,19 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
> >
> > host->caps = caps ? *caps : sdhci_readl(host, SDHCI_CAPABILITIES);
> >
> > + if (of_property_read_bool(mmc_dev(host->mmc)->of_node,
> > + "sdhci-cap-speed-modes-broken"))
>
> It rather begs the question: if you are going to do something sdhci
> specific, why not just read the whole of the caps register from DT?
>
Throwing out the whole of the caps register seems like overkill. Also
there are some things set by the caps that are not available in the DT.
For example, SDHCI_CAN_64BIT is set by the cap register and is used in
sdhci_setup_host to set host->flags SDHCI_USE_64_BIT_DMA.
^ permalink raw reply
* [RFC v3 1/2] mmc: sdhci: Add device tree property sdhci-cap-speed-modes-broken
From: Zach Brown @ 2016-10-24 19:04 UTC (permalink / raw)
To: ulf.hansson
Cc: adrian.hunter, robh+dt, mark.rutland, linux-mmc, devicetree,
linux-kernel, zach.brown
In-Reply-To: <1477335850-14945-1-git-send-email-zach.brown@ni.com>
On some systems the sdhci capabilty registers are incorrect for one
reason or another.
The sdhci-cap-speed-modes-broken property will let the driver know that
the sdhci capability registers should not be relied on for speed modes.
Instead the driver should check the mmc generic DT bindings.
Signed-off-by: Zach Brown <zach.brown@ni.com>
---
Documentation/devicetree/bindings/mmc/mmc.txt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
index 8a37782..671d6c0 100644
--- a/Documentation/devicetree/bindings/mmc/mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/mmc.txt
@@ -52,6 +52,9 @@ Optional properties:
- no-sdio: controller is limited to send sdio cmd during initialization
- no-sd: controller is limited to send sd cmd during initialization
- no-mmc: controller is limited to send mmc cmd during initialization
+- sdhci-cap-speed-modes-broken: One or more of the bits in the sdhci
+ capabilities registers representing speed modes are incorrect. All the bits
+ representing speed modes should be ignored.
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
polarity properties, we have to fix the meaning of the "normal" and "inverted"
--
2.7.4
^ permalink raw reply related
* [RFC v3 2/2] mmc: sdhci: Ignore capability register when it comes to speeds and use DT binding instead when sdhci-cap-speed-modes-broken is set.
From: Zach Brown @ 2016-10-24 19:04 UTC (permalink / raw)
To: ulf.hansson
Cc: adrian.hunter, robh+dt, mark.rutland, linux-mmc, devicetree,
linux-kernel, zach.brown
In-Reply-To: <1477335850-14945-1-git-send-email-zach.brown@ni.com>
When the sdhci-cap-speed-modes-broken DT property is set, the driver
will ignore the bits of the capability registers that correspond to
speed modes.
Signed-off-by: Zach Brown <zach.brown@ni.com>
---
drivers/mmc/host/sdhci.c | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 1e25b01..a17c47e 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -22,6 +22,7 @@
#include <linux/scatterlist.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
#include <linux/leds.h>
@@ -2991,6 +2992,7 @@ static int sdhci_set_dma_mask(struct sdhci_host *host)
void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
{
u16 v;
+ unsigned int speed_cap_broken = false;
if (host->read_caps)
return;
@@ -3005,18 +3007,37 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
sdhci_do_reset(host, SDHCI_RESET_ALL);
+ if (of_property_read_bool(mmc_dev(host->mmc)->of_node,
+ "sdhci-cap-speed-modes-broken"))
+ speed_cap_broken = true;
+
v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION);
host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
return;
- host->caps = caps ? *caps : sdhci_readl(host, SDHCI_CAPABILITIES);
+ if (caps)
+ host->caps = *caps;
+ else {
+ host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+ if (speed_cap_broken)
+ host->caps &= ~SDHCI_CAN_DO_HISPD;
+ }
if (host->version < SDHCI_SPEC_300)
return;
- host->caps1 = caps1 ? *caps1 : sdhci_readl(host, SDHCI_CAPABILITIES_1);
+ if (caps1)
+ host->caps1 = *caps1;
+ else {
+ host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
+ if (speed_cap_broken)
+ host->caps1 &= ~(SDHCI_SUPPORT_SDR50 |
+ SDHCI_SUPPORT_SDR104 |
+ SDHCI_SUPPORT_DDR50 |
+ SDHCI_SUPPORT_HS400);
+ }
}
EXPORT_SYMBOL_GPL(__sdhci_read_caps);
--
2.7.4
^ permalink raw reply related
* [RFC v3 0/2] mmc: sdhci: Ignore capability register when it comes to speeds and use DT binding instead when sdhci-cap-speed-modes-broken is set.
From: Zach Brown @ 2016-10-24 19:04 UTC (permalink / raw)
To: ulf.hansson
Cc: adrian.hunter, robh+dt, mark.rutland, linux-mmc, devicetree,
linux-kernel, zach.brown
The first patch add documentation about a new devicetree property
sdhci-cap-speed-modes-broken.
The second patch makes the sdhci use the DT binding instead of the caps
register for determining which speed modes are supported by the controller.
This RFC is an alternative to another patch[1] set I sent up.
[1]
https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1251944.html
v2:
* Removed separate OF parsing function, relying on mmc_of_parse to be called
before read_caps
* Moved check of sdhci-cap-speed-modes-broken into sdhci_read_caps.
* Added SDHCI_SUPPORT_HS400 to list of bits to clear in sdhci cap1, not
sure that's all of the them now, but I think so.
v3:
* Read "sdhci-cap-speed-modes-broken" only once.
* If caps are provided to sdhci_read_caps the speed bits are not overwritten.
Zach Brown (2):
mmc: sdhci: Add device tree property sdhci-cap-speed-modes-broken
mmc: sdhci: Ignore capability register when it comes to speeds and use
DT binding instead when sdhci-cap-speed-modes-broken is set.
Documentation/devicetree/bindings/mmc/mmc.txt | 3 +++
drivers/mmc/host/sdhci.c | 25 +++++++++++++++++++++++--
2 files changed, 26 insertions(+), 2 deletions(-)
--
2.7.4
^ permalink raw reply
* Re: [PATCH 3/5] Documentation: synopsys-dw-mshc: add binding for fifo quirks
From: Jaehoon Chung @ 2016-10-25 2:16 UTC (permalink / raw)
To: Shawn Lin, Jun Nie, shawn.guo, xie.baoyou
Cc: ulf.hansson, jason.liu, linux-mmc
In-Reply-To: <02e88f2e-d87b-c999-a148-65cfe5cf84d0@rock-chips.com>
On 10/24/2016 09:19 PM, Shawn Lin wrote:
> On 2016/10/24 17:11, Jun Nie wrote:
>> Add fifo-addr-override property and fifo-watermark-quirk property to
>> synopsys-dw-mshc bindings. It is intended to provide workarounds to
>> support more SoCs that break current assumption.
>>
>> See Documentation/devicetree/bindings/reset/reset.txt for details.
>>
>> Signed-off-by: Jun Nie <jun.nie@linaro.org>
>> ---
>> Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 13 +++++++++++++
>> 1 file changed, 13 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
>> index 4e00e85..eb64921 100644
>> --- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
>> +++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
>> @@ -76,6 +76,17 @@ Optional properties:
>>
>> * broken-cd: as documented in mmc core bindings.
>>
>> +* fifo-addr-override: Override fifo address with value provided by DT. The FIFO
>> + reg offset of version 0x210A break current assumption that 0x100 (version < 0x240A)
>> + and 0x200(version >= 0x240A) in some implementation. So this property serves as
>> + workaround.
>
> Can we hardcode this to the code by checking the host version?
I think it should be not workaround..According to TRM, Address is equal or greater than 0x100.
It means address can be 0x200, right?
If you needs to overwrite the DATA register offset for your target, just can add the property for this.
>
>> +
>> +* fifo-watermark-quirk: Data done irq is expected if data length is less than
>> + watermark in PIO mode. But fifo watermark is requested to be aligned with data
>> + length in some SoC so that TX/RX irq can be generated with data done irq. Add the
>> + watermark quirk to mark this requirement and force fifo watermark setting
>> + accordingly.
>
> I would like to know if this limitation is *really* related to some
> Socs or the version of 210A dw_mmc?
>
>
>> +
>> * vmmc-supply: The phandle to the regulator to use for vmmc. If this is
>> specified we'll defer probe until we can find this regulator.
>>
>> @@ -103,6 +114,8 @@ board specific portions as listed below.
>> interrupts = <0 75 0>;
>> #address-cells = <1>;
>> #size-cells = <0>;
>> + fifo-addr-override = <0x200>;
>> + fifo-watermark-quirk;
>> };
>>
>> [board specific internal DMA resources]
>>
>
>
^ permalink raw reply
* Re: [PATCH 3/5] Documentation: synopsys-dw-mshc: add binding for fifo quirks
From: Shawn Lin @ 2016-10-25 4:17 UTC (permalink / raw)
To: Jaehoon Chung, Jun Nie, shawn.guo, xie.baoyou
Cc: shawn.lin, ulf.hansson, jason.liu, linux-mmc
In-Reply-To: <39dbfa10-03fc-0f8b-c947-0f8da5dae60b@samsung.com>
On 2016/10/25 10:16, Jaehoon Chung wrote:
> On 10/24/2016 09:19 PM, Shawn Lin wrote:
>> On 2016/10/24 17:11, Jun Nie wrote:
>>> Add fifo-addr-override property and fifo-watermark-quirk property to
>>> synopsys-dw-mshc bindings. It is intended to provide workarounds to
>>> support more SoCs that break current assumption.
>>>
>>> See Documentation/devicetree/bindings/reset/reset.txt for details.
>>>
>>> Signed-off-by: Jun Nie <jun.nie@linaro.org>
>>> ---
>>> Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 13 +++++++++++++
>>> 1 file changed, 13 insertions(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
>>> index 4e00e85..eb64921 100644
>>> --- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
>>> +++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
>>> @@ -76,6 +76,17 @@ Optional properties:
>>>
>>> * broken-cd: as documented in mmc core bindings.
>>>
>>> +* fifo-addr-override: Override fifo address with value provided by DT. The FIFO
>>> + reg offset of version 0x210A break current assumption that 0x100 (version < 0x240A)
>>> + and 0x200(version >= 0x240A) in some implementation. So this property serves as
>>> + workaround.
>>
>> Can we hardcode this to the code by checking the host version?
>
> I think it should be not workaround..According to TRM, Address is equal or greater than 0x100.
> It means address can be 0x200, right?
> If you needs to overwrite the DATA register offset for your target, just can add the property for this.
>
I can't follow yours here as I don't have 210A TRM. Do you mean the TRM
for 210A say: "Address is equal or greater than 0x100" ?
>>
>>> +
>>> +* fifo-watermark-quirk: Data done irq is expected if data length is less than
>>> + watermark in PIO mode. But fifo watermark is requested to be aligned with data
>>> + length in some SoC so that TX/RX irq can be generated with data done irq. Add the
>>> + watermark quirk to mark this requirement and force fifo watermark setting
>>> + accordingly.
>>
>> I would like to know if this limitation is *really* related to some
>> Socs or the version of 210A dw_mmc?
>>
>>
>>> +
>>> * vmmc-supply: The phandle to the regulator to use for vmmc. If this is
>>> specified we'll defer probe until we can find this regulator.
>>>
>>> @@ -103,6 +114,8 @@ board specific portions as listed below.
>>> interrupts = <0 75 0>;
>>> #address-cells = <1>;
>>> #size-cells = <0>;
>>> + fifo-addr-override = <0x200>;
>>> + fifo-watermark-quirk;
>>> };
>>>
>>> [board specific internal DMA resources]
>>>
>>
>>
>
>
>
>
--
Best Regards
Shawn Lin
^ permalink raw reply
* Re: [PATCH 3/5] Documentation: synopsys-dw-mshc: add binding for fifo quirks
From: Jaehoon Chung @ 2016-10-25 8:09 UTC (permalink / raw)
To: Shawn Lin, Jun Nie, shawn.guo, xie.baoyou
Cc: ulf.hansson, jason.liu, linux-mmc
In-Reply-To: <c7fea328-45a9-77cd-39a5-37681ea2bc7a@rock-chips.com>
On 10/25/2016 01:17 PM, Shawn Lin wrote:
> On 2016/10/25 10:16, Jaehoon Chung wrote:
>> On 10/24/2016 09:19 PM, Shawn Lin wrote:
>>> On 2016/10/24 17:11, Jun Nie wrote:
>>>> Add fifo-addr-override property and fifo-watermark-quirk property to
>>>> synopsys-dw-mshc bindings. It is intended to provide workarounds to
>>>> support more SoCs that break current assumption.
>>>>
>>>> See Documentation/devicetree/bindings/reset/reset.txt for details.
>>>>
>>>> Signed-off-by: Jun Nie <jun.nie@linaro.org>
>>>> ---
>>>> Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 13 +++++++++++++
>>>> 1 file changed, 13 insertions(+)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
>>>> index 4e00e85..eb64921 100644
>>>> --- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
>>>> +++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
>>>> @@ -76,6 +76,17 @@ Optional properties:
>>>>
>>>> * broken-cd: as documented in mmc core bindings.
>>>>
>>>> +* fifo-addr-override: Override fifo address with value provided by DT. The FIFO
>>>> + reg offset of version 0x210A break current assumption that 0x100 (version < 0x240A)
>>>> + and 0x200(version >= 0x240A) in some implementation. So this property serves as
>>>> + workaround.
>>>
>>> Can we hardcode this to the code by checking the host version?
>>
>> I think it should be not workaround..According to TRM, Address is equal or greater than 0x100.
>> It means address can be 0x200, right?
>> If you needs to overwrite the DATA register offset for your target, just can add the property for this.
>>
>
> I can't follow yours here as I don't have 210A TRM. Do you mean the TRM
> for 210A say: "Address is equal or greater than 0x100" ?
Upper version than IP 2.40a is used the offset 0x100 as other purpose.
For preventing to use wrong register. we put to check host->verid.
Yes, can set the address to any value 0x100 or greater.
If some soc needs to use 0x200, why not?
But it's not true that FIFO reg offset of version 2.10a break.
I have checked 2.10a TRM..It's DATA offset >= 0x100.
Best Regards,
Jaehoon Chung
>
>>>
>>>> +
>>>> +* fifo-watermark-quirk: Data done irq is expected if data length is less than
>>>> + watermark in PIO mode. But fifo watermark is requested to be aligned with data
>>>> + length in some SoC so that TX/RX irq can be generated with data done irq. Add the
>>>> + watermark quirk to mark this requirement and force fifo watermark setting
>>>> + accordingly.
>>>
>>> I would like to know if this limitation is *really* related to some
>>> Socs or the version of 210A dw_mmc?
>>>
>>>
>>>> +
>>>> * vmmc-supply: The phandle to the regulator to use for vmmc. If this is
>>>> specified we'll defer probe until we can find this regulator.
>>>>
>>>> @@ -103,6 +114,8 @@ board specific portions as listed below.
>>>> interrupts = <0 75 0>;
>>>> #address-cells = <1>;
>>>> #size-cells = <0>;
>>>> + fifo-addr-override = <0x200>;
>>>> + fifo-watermark-quirk;
>>>> };
>>>>
>>>> [board specific internal DMA resources]
>>>>
>>>
>>>
>>
>>
>>
>>
>
>
^ permalink raw reply
* Re: [PATCH 2/5] mmc: zx: Initial support for ZX mmc controller
From: Jun Nie @ 2016-10-25 8:29 UTC (permalink / raw)
To: Shawn Lin
Cc: Shawn Guo, xie.baoyou, Ulf Hansson, Jaehoon Chung, Jason Liu,
linux-mmc
In-Reply-To: <0c1d8185-618b-8e5d-e072-af305cd2ff78@rock-chips.com>
2016-10-24 20:29 GMT+08:00 Shawn Lin <shawn.lin@rock-chips.com>:
> 在 2016/10/24 17:11, Jun Nie 写道:
>>
>> This platform driver adds initial support for the DW host controller
>> found on ZTE SoCs.
>>
>> It has been tested on ZX296718 EVB board currently. More support on
>> timing tuning will be added when hardware is available.
>>
>> Signed-off-by: Jun Nie <jun.nie@linaro.org>
>> ---
>> drivers/mmc/host/Kconfig | 9 ++
>> drivers/mmc/host/Makefile | 1 +
>> drivers/mmc/host/dw_mmc-zx.c | 230
>> +++++++++++++++++++++++++++++++++++++++++++
>> drivers/mmc/host/dw_mmc-zx.h | 23 +++++
>> 4 files changed, 263 insertions(+)
>> create mode 100644 drivers/mmc/host/dw_mmc-zx.c
>> create mode 100644 drivers/mmc/host/dw_mmc-zx.h
>>
>> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
>> index 5274f50..2b3202c 100644
>> --- a/drivers/mmc/host/Kconfig
>> +++ b/drivers/mmc/host/Kconfig
>> @@ -662,6 +662,15 @@ config MMC_DW_ROCKCHIP
>> Synopsys DesignWare Memory Card Interface driver. Select this
>> option
>> for platforms based on RK3066, RK3188 and RK3288 SoC's.
>>
>> +config MMC_DW_ZX
>> + tristate "ZTE specific extensions for Synopsys DW Memory Card
>> Interface"
>> + depends on MMC_DW
>> + select MMC_DW_PLTFM
>> + help
>> + This selects support for ZTE SoC specific extensions to the
>> + Synopsys DesignWare Memory Card Interface driver. Select this
>> option
>> + for platforms based on ZX296718 SoC's.
>> +
>> config MMC_SH_MMCIF
>> tristate "SuperH Internal MMCIF support"
>> depends on HAS_DMA
>> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
>> index e2bdaaf..9766143 100644
>> --- a/drivers/mmc/host/Makefile
>> +++ b/drivers/mmc/host/Makefile
>> @@ -48,6 +48,7 @@ obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
>> obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
>> obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
>> obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
>> +obj-$(CONFIG_MMC_DW_ZX) += dw_mmc-zx.o
>> obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
>> obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
>> obj-$(CONFIG_MMC_VUB300) += vub300.o
>> diff --git a/drivers/mmc/host/dw_mmc-zx.c b/drivers/mmc/host/dw_mmc-zx.c
>> new file mode 100644
>> index 0000000..d0e4ef2
>> --- /dev/null
>> +++ b/drivers/mmc/host/dw_mmc-zx.c
>> @@ -0,0 +1,230 @@
>> +/*
>> + * ZX Specific Extensions for Synopsys DW Multimedia Card Interface
>> driver
>> + *
>> + * Copyright (C) 2016, Linaro Ltd.
>> + * Copyright (C) 2016, ZTE Corp.
>> + *
>> + * 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; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/platform_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/mmc/dw_mmc.h>
>> +#include <linux/mmc/host.h>
>> +#include <linux/mmc/mmc.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>
>
> #include <linux/platform_device.h>
> to keep alphabetical order
Will do.
>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include "dw_mmc.h"
>> +#include "dw_mmc-pltfm.h"
>> +#include "dw_mmc-zx.h"
>> +
>> +#define ZX_DLL_LOCKED BIT(2)
>> +
>> +struct dw_mci_zx_priv_data {
>> + struct regmap *sysc_base;
>> +};
>> +
>> +static int dw_mci_zx_emmc_set_delay(struct dw_mci *host, unsigned int
>> delay,
>> + unsigned int clk_flag)
>
>
> I don't know if this is expected/legit way to control the delay/phase
> by adding code here. It looks more general to me that we should use
> clk_{set,get}_phase API.
The phase control register reside in a system controller, not a
standard clock controller. The register control data sampling phase
and bypass mode etc, but no clk rate/mux. So it will take more code
and ugly logic to expose a clk_* API based on such register.
>
>
>> +{
>> + struct dw_mci_zx_priv_data *priv = host->priv;
>> + struct regmap *sysc_base = priv->sysc_base;
>> + unsigned int clksel;
>> + unsigned int loop = 1000;
>> + int ret;
>> +
>> + ret = regmap_write(sysc_base, LB_AON_EMMC_CFG_REG0,
>> + PARA_DLL_START_POINT(4) |
>> PARA_DLL_LOCK_NUM(4));
>> + if (ret)
>> + return ret;
>> +
>> + ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG1, &clksel);
>> + if (ret)
>> + return ret;
>> +
>> + if (clk_flag) {
>> + clksel &= ~(CLK_SAMP_DELAY(0x7F));
>> + clksel |= (delay << 8);
>> + } else {
>> + clksel &= ~(READ_DQS_DELAY(0x7F));
>> + clksel |= delay;
>> + }
>> +
>> + regmap_write(sysc_base, LB_AON_EMMC_CFG_REG1, clksel);
>> + regmap_write(sysc_base, LB_AON_EMMC_CFG_REG0,
>> + PARA_DLL_START_POINT(4) | PARA_DLL_LOCK_NUM(4) |
>> + DLL_REG_SET);
>> +
>> + do {
>> + ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG2,
>> &clksel);
>> + if (ret)
>> + return ret;
>> +
>> + } while (--loop && !(clksel & ZX_DLL_LOCKED));
>> +
>> + if (!loop) {
>> + dev_err(host->dev, "Error: %s dll lock fail\n", __func__);
>> + return -EIO;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int dw_mci_zx_emmc_execute_tuning(struct dw_mci_slot *slot, u32
>> opcode)
>> +{
>> + struct dw_mci *host = slot->host;
>> + struct mmc_host *mmc = slot->mmc;
>> + int len, start = 0, end = 0, delay, best = 0;
>> + int ret = 0;
>> +
>> + for (delay = 1 ; delay < 128; delay++) {
>> + ret = dw_mci_zx_emmc_set_delay(host, delay, 1);
>> + if (ret)
>> + return ret;
>> +
>> + if (mmc_send_tuning(mmc, opcode, NULL)) {
>> + if (start >= 0) {
>> + end = delay - 1;
>> + /* check and update longest good range */
>> + if ((end - start) > len) {
>> + best = (start + end) >> 1;
>> + len = end - start;
>> + }
>> + }
>> + start = -1;
>> + end = 0;
>> + continue;
>> + }
>> + if (start < 0)
>> + start = delay;
>> + }
>> +
>> + if (start >= 0) {
>> + end = delay - 1;
>> + if ((end - start) > len) {
>> + best = (start + end) >> 1;
>> + len = end - start;
>> + }
>> + }
>> + if (best < 0)
>> + return -EIO;
>> +
>> + dev_info(host->dev, "%s best range: start %d end %d\n", __func__,
>> + start, end);
>> + dw_mci_zx_emmc_set_delay(host, best, 1);
>> + return 0;
>> +}
>> +
>> +static int dw_mci_zx_prepare_hs400_tuning(struct dw_mci *host,
>> + struct mmc_ios *ios)
>> +{
>> + int ret;
>> +
>> + /* config phase shift 90 */
>> + ret = dw_mci_zx_emmc_set_delay(host, 32, 0);
>> + if (ret < 0)
>> + return -EIO;
>> +
>> + return 0;
>> +}
>> +
>> +static int dw_mci_zx_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
>> +{
>> + struct dw_mci *host = slot->host;
>> +
>> + if (host->verid == 0x290a) /* emmc */
>> + return dw_mci_zx_emmc_execute_tuning(slot, opcode);
>> +
>> + return 0;
>> +}
>> +
>> +static int dw_mci_zx_parse_dt(struct dw_mci *host)
>> +{
>> + struct device_node *np = host->dev->of_node;
>> + struct device_node *node;
>> + struct dw_mci_zx_priv_data *priv;
>> + struct regmap *sysc_base;
>> + int ret;
>> +
>> + node = of_parse_phandle(np, "zte,aon-syscon", 0);
>> + if (node) {
>> + sysc_base = syscon_node_to_regmap(node);
>> + of_node_put(node);
>> +
>> + if (IS_ERR(sysc_base)) {
>> + ret = PTR_ERR(sysc_base);
>> + if (ret != -EPROBE_DEFER)
>> + dev_err(host->dev, "Can't get syscon:
>> %d\n",
>> + ret);
>> + return ret;
>> + }
>> + }
>> +
>> + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
>> + if (!priv)
>> + return -ENOMEM;
>> + priv->sysc_base = sysc_base;
>> + host->priv = priv;
>> +
>> + return 0;
>> +}
>> +
>> +static unsigned long zx_dwmmc_caps[3] = {
>> + MMC_CAP_HW_RESET,
>> + 0,
>> + 0,
>> +};
>> +
>
>
> just a thought, maybe you could add the cap-mmc-hw-reset for your
> emmc node on the DT as
>
> 1) you could save some bits here
> 2) Moreover, it's related to the board design since it isn't
> guaranteed that the hw_rest pin won't be used for other purpose..
>
You are right.
>
>> +static const struct dw_mci_drv_data zx_drv_data = {
>> + .caps = zx_dwmmc_caps,
>> + .execute_tuning = dw_mci_zx_execute_tuning,
>> + .prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning,
>> + .parse_dt = dw_mci_zx_parse_dt,
>> +};
>> +
>> +static const struct of_device_id dw_mci_zx_match[] = {
>> + { .compatible = "zte,dw-mshc", .data = &zx_drv_data},
>> +};
>> +MODULE_DEVICE_TABLE(of, dw_mci_zx_match);
>> +
>> +static int dw_mci_zx_probe(struct platform_device *pdev)
>> +{
>> + const struct dw_mci_drv_data *drv_data;
>> + const struct of_device_id *match;
>> +
>> + match = of_match_node(dw_mci_zx_match, pdev->dev.of_node);
>> + drv_data = match->data;
>> +
>> + return dw_mci_pltfm_register(pdev, drv_data);
>> +}
>> +
>> +static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = {
>> + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
>> + pm_runtime_force_resume)
>> + SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
>> + dw_mci_runtime_resume,
>> + NULL)
>> +};
>> +
>> +static struct platform_driver dw_mci_zx_pltfm_driver = {
>> + .probe = dw_mci_zx_probe,
>> + .remove = dw_mci_pltfm_remove,
>> + .driver = {
>> + .name = "dwmmc_zx",
>> + .of_match_table = dw_mci_zx_match,
>> + .pm = &dw_mci_zx_dev_pm_ops,
>> + },
>> +};
>> +
>> +module_platform_driver(dw_mci_zx_pltfm_driver);
>> +
>> +MODULE_DESCRIPTION("ZTE emmc/sd driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/mmc/host/dw_mmc-zx.h b/drivers/mmc/host/dw_mmc-zx.h
>> new file mode 100644
>> index 0000000..b1aac52
>> --- /dev/null
>> +++ b/drivers/mmc/host/dw_mmc-zx.h
>> @@ -0,0 +1,23 @@
>> +#ifndef _DW_MMC_ZX_H_
>> +#define _DW_MMC_ZX_H_
>> +
>> +/* dll reg offset*/
>> +#define LB_AON_EMMC_CFG_REG0 0x1B0
>> +#define LB_AON_EMMC_CFG_REG1 0x1B4
>> +#define LB_AON_EMMC_CFG_REG2 0x1B8
>> +
>> +/* LB_AON_EMMC_CFG_REG0 register defines */
>> +#define PARA_DLL_START_POINT(x) (((x) & 0xFF) << 0)
>> +#define DLL_REG_SET BIT(8)
>> +#define PARA_DLL_LOCK_NUM(x) (((x) & 7) << 16)
>> +#define PARA_PHASE_DET_SEL(x) (((x) & 7) << 20)
>> +#define PARA_DLL_BYPASS_MODE BIT(23)
>> +#define PARA_HALF_CLK_MODE BIT(24)
>> +
>> +/* LB_AON_EMMC_CFG_REG1 register defines */
>> +#define READ_DQS_DELAY(x) (((x) & 0x7F) << 0)
>> +#define READ_DQS_BYPASS_MODE BIT(7)
>> +#define CLK_SAMP_DELAY(x) (((x) & 0x7F) << 8)
>> +#define CLK_SAMP_BYPASS_MODE BIT(15)
>> +
>> +#endif /* _DW_MMC_ZX_H_ */
>>
>
>
> --
> Best Regards
> Shawn Lin
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox