2018-10-04 11:14:29

by Faiz Abbas

[permalink] [raw]
Subject: [PATCH 0/6] Add Support for MMC/SD in TI's AM65x SOCs

The following patches add driver support for MMC/SD in TI's
AM65x SOCs. There are two controller instances. Both are compatible
with eMMC5.1 Host Controller Standard Specification and SD Host
Controller Standard Specification 4.10.

DT Support patches will be posted separately.

Faiz Abbas (6):
dt-bindings: phy: am654-mmc-phy: Document new phy bindings
phy: am654-mmc-phy: Add Support for MMC PHY on AM654 Devices
dt-bindings: mmc: sdhci-of-arasan: Add new compatible for AM654 MMC
PHY
mmc: sdhci-of-arasan: Add a single data structure to incorporate pdata
and soc_ctl_map
mmc: sdhci-of-arasan: Add Support for AM654 MMC and PHY
arm64: defconfig: Enable MMC PHY for AM65xx

.../devicetree/bindings/mmc/arasan,sdhci.txt | 1 +
.../devicetree/bindings/phy/am654-mmc-phy.txt | 42 +++
arch/arm64/configs/defconfig | 1 +
drivers/mmc/host/sdhci-of-arasan.c | 93 +++++-
drivers/phy/ti/Kconfig | 7 +
drivers/phy/ti/Makefile | 1 +
drivers/phy/ti/phy-am654-mmc.c | 291 ++++++++++++++++++
7 files changed, 421 insertions(+), 15 deletions(-)
create mode 100644 Documentation/devicetree/bindings/phy/am654-mmc-phy.txt
create mode 100644 drivers/phy/ti/phy-am654-mmc.c

--
2.18.0



2018-10-04 11:13:20

by Faiz Abbas

[permalink] [raw]
Subject: [PATCH 3/6] dt-bindings: mmc: sdhci-of-arasan: Add new compatible for AM654 MMC PHY

Add a new compatible to use the host controller driver with the
MMC PHY on TI's AM654 SOCs

Signed-off-by: Faiz Abbas <[email protected]>
Signed-off-by: Sekhar Nori <[email protected]>
---
Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
index f6ddba31cb73..e2effe17f05e 100644
--- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
+++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
@@ -15,6 +15,7 @@ Required Properties:
- "arasan,sdhci-5.1": generic Arasan SDHCI 5.1 PHY
- "rockchip,rk3399-sdhci-5.1", "arasan,sdhci-5.1": rk3399 eMMC PHY
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
+ - "ti,am654-sdhci-5.1", "arasan,sdhci-5.1": TI AM654 MMC PHY
- reg: From mmc bindings: Register location and length.
- clocks: From clock bindings: Handles to clock inputs.
- clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
--
2.18.0


2018-10-04 11:13:22

by Faiz Abbas

[permalink] [raw]
Subject: [PATCH 4/6] mmc: sdhci-of-arasan: Add a single data structure to incorporate pdata and soc_ctl_map

Currently, the driver passes platform data as a global structure
and uses the .data of of_device_id to pass the soc_ctl_map. To
make the implementation more flexible add a single data structure
that incorporates both of the above and pass it in the .data of
of_device_id.

Signed-off-by: Faiz Abbas <[email protected]>
Signed-off-by: Sekhar Nori <[email protected]>
---
drivers/mmc/host/sdhci-of-arasan.c | 47 ++++++++++++++++++++----------
1 file changed, 32 insertions(+), 15 deletions(-)

diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index b806b24a3e5f..c9e3e050ccc8 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -107,6 +107,11 @@ struct sdhci_arasan_data {
#define SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE BIT(1)
};

+struct sdhci_arasan_of_data {
+ const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
+ const struct sdhci_pltfm_data *pdata;
+};
+
static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = {
.baseclkfreq = { .reg = 0xf000, .width = 8, .shift = 8 },
.clockmultiplier = { .reg = 0xf02c, .width = 8, .shift = 0},
@@ -307,6 +312,10 @@ static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
SDHCI_QUIRK2_STOP_WITH_TC,
};

+static struct sdhci_arasan_of_data sdhci_arasan_data = {
+ .pdata = &sdhci_arasan_pdata,
+};
+
static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
{
int cmd_error = 0;
@@ -363,6 +372,11 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
};

+static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = {
+ .soc_ctl_map = &rk3399_soc_ctl_map,
+ .pdata = &sdhci_arasan_cqe_pdata,
+};
+
#ifdef CONFIG_PM_SLEEP
/**
* sdhci_arasan_suspend - Suspend method for the driver
@@ -462,14 +476,21 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
/* SoC-specific compatible strings w/ soc_ctl_map */
{
.compatible = "rockchip,rk3399-sdhci-5.1",
- .data = &rk3399_soc_ctl_map,
+ .data = &sdhci_arasan_rk3399_data,
},
-
/* Generic compatible below here */
- { .compatible = "arasan,sdhci-8.9a" },
- { .compatible = "arasan,sdhci-5.1" },
- { .compatible = "arasan,sdhci-4.9a" },
-
+ {
+ .compatible = "arasan,sdhci-8.9a",
+ .data = &sdhci_arasan_data,
+ },
+ {
+ .compatible = "arasan,sdhci-5.1",
+ .data = &sdhci_arasan_data,
+ },
+ {
+ .compatible = "arasan,sdhci-4.9a",
+ .data = &sdhci_arasan_data,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
@@ -707,14 +728,11 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_arasan_data *sdhci_arasan;
struct device_node *np = pdev->dev.of_node;
- const struct sdhci_pltfm_data *pdata;
-
- if (of_device_is_compatible(pdev->dev.of_node, "arasan,sdhci-5.1"))
- pdata = &sdhci_arasan_cqe_pdata;
- else
- pdata = &sdhci_arasan_pdata;
+ const struct sdhci_arasan_of_data *data;

- host = sdhci_pltfm_init(pdev, pdata, sizeof(*sdhci_arasan));
+ match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
+ data = match->data;
+ host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*sdhci_arasan));

if (IS_ERR(host))
return PTR_ERR(host);
@@ -723,8 +741,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
sdhci_arasan->host = host;

- match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
- sdhci_arasan->soc_ctl_map = match->data;
+ sdhci_arasan->soc_ctl_map = data->soc_ctl_map;

node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
if (node) {
--
2.18.0


2018-10-04 11:13:26

by Faiz Abbas

[permalink] [raw]
Subject: [PATCH 5/6] mmc: sdhci-of-arasan: Add Support for AM654 MMC and PHY

The current arasan sdhci PHY configuration isn't compatible
with the PHY on TI's AM654 devices.

Therefore, add a new compatible, AM654 specific quirks
and a new AM654 specific set_clock function which
configures the PHY in a sane way.

Signed-off-by: Faiz Abbas <[email protected]>
Signed-off-by: Sekhar Nori <[email protected]>
---
drivers/mmc/host/sdhci-of-arasan.c | 46 ++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)

diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index c9e3e050ccc8..142c4b802f31 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -231,6 +231,25 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
}
}

+static void sdhci_arasan_am654_set_clock(struct sdhci_host *host,
+ unsigned int clock)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+
+ if (sdhci_arasan->is_phy_on) {
+ phy_power_off(sdhci_arasan->phy);
+ sdhci_arasan->is_phy_on = false;
+ }
+
+ sdhci_set_clock(host, clock);
+
+ if (clock > PHY_CLK_TOO_SLOW_HZ) {
+ phy_power_on(sdhci_arasan->phy);
+ sdhci_arasan->is_phy_on = true;
+ }
+}
+
static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc,
struct mmc_ios *ios)
{
@@ -316,6 +335,29 @@ static struct sdhci_arasan_of_data sdhci_arasan_data = {
.pdata = &sdhci_arasan_pdata,
};

+static const struct sdhci_ops sdhci_arasan_am654_ops = {
+ .set_clock = sdhci_arasan_am654_set_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_arasan_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static const struct sdhci_pltfm_data sdhci_arasan_am654_pdata = {
+ .ops = &sdhci_arasan_am654_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
+ SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
+ SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
+};
+
+static const struct sdhci_arasan_of_data sdhci_arasan_am654_data = {
+ .pdata = &sdhci_arasan_am654_pdata,
+};
+
static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
{
int cmd_error = 0;
@@ -478,6 +520,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
.compatible = "rockchip,rk3399-sdhci-5.1",
.data = &sdhci_arasan_rk3399_data,
},
+ {
+ .compatible = "ti,am654-sdhci-5.1",
+ .data = &sdhci_arasan_am654_data,
+ },
/* Generic compatible below here */
{
.compatible = "arasan,sdhci-8.9a",
--
2.18.0


2018-10-04 11:13:27

by Faiz Abbas

[permalink] [raw]
Subject: [PATCH 6/6] arm64: defconfig: Enable MMC PHY for AM65xx

Enable the MMC phy implemented in the AM65xx SOC.
This phy is required for the sdhci host controller
driving the MMC ports.

Signed-off-by: Faiz Abbas <[email protected]>
---
arch/arm64/configs/defconfig | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index a51967154caa..d897ec2ba390 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -659,6 +659,7 @@ CONFIG_PHY_ROCKCHIP_TYPEC=y
CONFIG_PHY_TEGRA_XUSB=y
CONFIG_PHY_UNIPHIER_USB3=y
CONFIG_PHY_UNIPHIER_USB2=y
+CONFIG_PHY_AM654_MMC=y
CONFIG_HISI_PMU=y
CONFIG_QCOM_L2_PMU=y
CONFIG_QCOM_L3_PMU=y
--
2.18.0


2018-10-04 11:14:25

by Faiz Abbas

[permalink] [raw]
Subject: [PATCH 1/6] dt-bindings: phy: am654-mmc-phy: Document new phy bindings

Add information to document bindings for the MMC PHY
on TI's AM654 devices.

Signed-off-by: Faiz Abbas <[email protected]>
Signed-off-by: Sekhar Nori <[email protected]>
---
.../devicetree/bindings/phy/am654-mmc-phy.txt | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/am654-mmc-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/am654-mmc-phy.txt b/Documentation/devicetree/bindings/phy/am654-mmc-phy.txt
new file mode 100644
index 000000000000..766921612758
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/am654-mmc-phy.txt
@@ -0,0 +1,42 @@
+TI AM654 MMC PHY
+-------------------
+
+Required properties:
+ - compatible: ti,am654-emmc-phy
+ - #phy-cells: must be 0
+ - reg: PHY registers address offset and size
+ - clocks: must be phandle of the clock provider which is the host
+ controller
+ - clock-names: must be "mmcclk"
+ - ti,otap-del-sel: Output Tap Delay select.
+ - ti,trm-icp: DLL trim select.
+ - ti,driver-strength-ohm: driver strength in ohms.
+ Valid values are 33, 40, 50, 66, and 100 ohms.
+
+Example:
+ mmc_phy1: mmc_phy@10100 {
+ compatible = "ti,am654-mmc-phy";
+ reg = <0x10100 0x34>;
+ clocks = <&sdhci1>;
+ clock-names = "mmcclk";
+ #phy-cells = <0>;
+ ti,otap-del-sel = <0x2>;
+ ti,trm-icp = <0x8>;
+ status = "disabled";
+ };
+
+Host controller node:
+
+ sdhci1: sdhci@0 {
+ compatible = "ti,am654-sdhci-5.1";
+ reg = <0x0 0x1000>;
+ power-domains = <&k3_pds 48>;
+ clocks = <&k3_clks 48 0>, <&k3_clks 48 1>;
+ clock-names = "clk_ahb", "clk_xin";
+ interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
+ clock-output-names = "mmc_cardclck";
+ #clock-cells = <0>;
+ phys=<&mmc_phy1>;
+ phy-names = "phy_arasan";
+ status = "disabled";
+ };
--
2.18.0


2018-10-04 11:15:52

by Faiz Abbas

[permalink] [raw]
Subject: [PATCH 2/6] phy: am654-mmc-phy: Add Support for MMC PHY on AM654 Devices

Add driver support for the MMC physical layer present
on TI's AM654 devices.

Signed-off-by: Faiz Abbas <[email protected]>
Signed-off-by: Sekhar Nori <[email protected]>
---
drivers/phy/ti/Kconfig | 7 +
drivers/phy/ti/Makefile | 1 +
drivers/phy/ti/phy-am654-mmc.c | 291 +++++++++++++++++++++++++++++++++
3 files changed, 299 insertions(+)
create mode 100644 drivers/phy/ti/phy-am654-mmc.c

diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig
index 20503562666c..ea5fe4db01c8 100644
--- a/drivers/phy/ti/Kconfig
+++ b/drivers/phy/ti/Kconfig
@@ -76,3 +76,10 @@ config TWL4030_USB
family chips (including the TWL5030 and TPS659x0 devices).
This transceiver supports high and full speed devices plus,
in host mode, low speed.
+
+config PHY_AM654_MMC
+ bool "TI AM654 MMC PHY Support"
+ select GENERIC_PHY
+ help
+ This option enables support for the Physical layer for MMC host
+ controllers present on TI AM654 SOCs.
diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile
index 9f361756eaf2..5b2db2d164a5 100644
--- a/drivers/phy/ti/Makefile
+++ b/drivers/phy/ti/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
+obj-$(CONFIG_PHY_AM654_MMC) += phy-am654-mmc.o
diff --git a/drivers/phy/ti/phy-am654-mmc.c b/drivers/phy/ti/phy-am654-mmc.c
new file mode 100644
index 000000000000..91255947fb67
--- /dev/null
+++ b/drivers/phy/ti/phy-am654-mmc.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * phy-am654-mmc.c - MMC PHY driver for TI's AM654 SOCs
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/regmap.h>
+
+/* MMC PHY Registers */
+#define PHYCTRL_CTRL1_REG 0x00
+#define PHYCTRL_CTRL2_REG 0x04
+#define PHYCTRL_CTRL3_REG 0x08
+#define PHYCTRL_CTRL4_REG 0x0C
+#define PHYCTRL_CTRL5_REG 0x10
+#define PHYCTRL_CTRL6_REG 0x14
+#define PHYCTRL_STAT1_REG 0x30
+#define PHYCTRL_STAT2_REG 0x34
+
+#define IOMUX_ENABLE_SHIFT 31
+#define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT)
+#define OTAPDLYENA_SHIFT 20
+#define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT)
+#define OTAPDLYSEL_SHIFT 12
+#define OTAPDLYSEL_MASK GENMASK(15, 12)
+#define STRBSEL_SHIFT 24
+#define STRBSEL_MASK GENMASK(27, 24)
+#define SEL50_SHIFT 8
+#define SEL50_MASK BIT(SEL50_SHIFT)
+#define SEL100_SHIFT 9
+#define SEL100_MASK BIT(SEL100_SHIFT)
+#define DLL_TRIM_ICP_SHIFT 4
+#define DLL_TRIM_ICP_MASK GENMASK(7, 4)
+#define DR_TY_SHIFT 20
+#define DR_TY_MASK GENMASK(22, 20)
+#define ENDLL_SHIFT 1
+#define ENDLL_MASK BIT(ENDLL_SHIFT)
+#define DLLRDY_SHIFT 0
+#define DLLRDY_MASK BIT(DLLRDY_SHIFT)
+#define PDB_SHIFT 0
+#define PDB_MASK BIT(PDB_SHIFT)
+#define CALDONE_SHIFT 1
+#define CALDONE_MASK BIT(CALDONE_SHIFT)
+
+#define DRIVER_STRENGTH_50_OHM 0x0
+#define DRIVER_STRENGTH_33_OHM 0x1
+#define DRIVER_STRENGTH_66_OHM 0x2
+#define DRIVER_STRENGTH_100_OHM 0x3
+#define DRIVER_STRENGTH_40_OHM 0x4
+
+static struct regmap_config am654_mmc_phy_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+};
+
+struct am654_mmc_phy {
+ struct regmap *reg_base;
+ struct clk *mmcclk;
+ int otap_del_sel;
+ int trm_icp;
+ int drv_strength;
+};
+
+static int am654_mmc_phy_init(struct phy *phy)
+{
+ struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
+ int ret;
+ u32 val;
+
+ /* Reset registers to default value */
+ regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL1_REG, 0x10000);
+ regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL4_REG, 0x0);
+ regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL5_REG, 0x0);
+
+ /* Calibrate IO lines */
+ regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
+ PDB_MASK, PDB_MASK);
+ ret = regmap_read_poll_timeout(mmc_phy->reg_base, PHYCTRL_STAT1_REG,
+ val, val & CALDONE_MASK, 1, 20);
+ if (ret)
+ return ret;
+
+ /* Enable pins by setting the IO mux to 0 */
+ regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
+ IOMUX_ENABLE_MASK, 0);
+
+ mmc_phy->mmcclk = clk_get(&phy->dev, "mmcclk");
+ if (IS_ERR(mmc_phy->mmcclk)) {
+ dev_err(&phy->dev, "Error getting mmcclk");
+ return PTR_ERR(mmc_phy->mmcclk);
+ }
+
+ return 0;
+}
+
+static int am654_mmc_phy_exit(struct phy *phy)
+{
+ struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
+
+ clk_put(mmc_phy->mmcclk);
+
+ return 0;
+}
+
+static int am654_mmc_phy_power_on(struct phy *phy)
+{
+ struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
+ u32 mask, val;
+ int sel50, sel100;
+ int rate;
+
+ /* Setup DLL Output TAP delay */
+ mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+ val = (1 << OTAPDLYENA_SHIFT) |
+ (mmc_phy->otap_del_sel << OTAPDLYSEL_SHIFT);
+ regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL4_REG,
+ mask, val);
+
+ rate = clk_get_rate(mmc_phy->mmcclk);
+ switch (rate) {
+ case 200000000:
+ sel50 = 0;
+ sel100 = 0;
+ break;
+ case 100000000:
+ sel50 = 0;
+ sel100 = 1;
+ break;
+ default:
+ sel50 = 1;
+ sel100 = 0;
+ }
+
+ /* Configure PHY DLL frequency */
+ mask = SEL50_MASK | SEL100_MASK;
+ val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
+ regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL5_REG,
+ mask, val);
+
+ /* Configure DLL TRIM */
+ mask = DLL_TRIM_ICP_MASK;
+ val = mmc_phy->trm_icp << DLL_TRIM_ICP_SHIFT;
+
+ /* Configure DLL driver strength */
+ mask |= DR_TY_MASK;
+ val |= mmc_phy->drv_strength << DR_TY_SHIFT;
+ regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG, mask, val);
+
+ /* Enable DLL */
+ regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
+ ENDLL_MASK, 0x1 << ENDLL_SHIFT);
+
+ /*
+ * Poll for DLL ready. Use a one second timeout.
+ * Works in all experiments done so far
+ */
+ return regmap_read_poll_timeout(mmc_phy->reg_base, PHYCTRL_STAT1_REG,
+ val, val & DLLRDY_MASK, 1000, 1000000);
+
+}
+
+static int am654_mmc_phy_power_off(struct phy *phy)
+{
+ struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
+
+ /* Disable DLL */
+ regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
+ ENDLL_MASK, 0);
+
+ /* Reset registers to default value except PDB */
+ regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
+ 0x10000 | PDB_MASK);
+ regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL4_REG, 0x0);
+ regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL5_REG, 0x0);
+
+ return 0;
+}
+
+static const struct phy_ops ops = {
+ .init = am654_mmc_phy_init,
+ .exit = am654_mmc_phy_exit,
+ .power_on = am654_mmc_phy_power_on,
+ .power_off = am654_mmc_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int am654_mmc_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct am654_mmc_phy *mmc_phy;
+ struct phy *generic_phy;
+ struct resource *res;
+ void __iomem *base;
+ struct regmap *map;
+ int drv_strength;
+ int err;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ map = devm_regmap_init_mmio(dev, base, &am654_mmc_phy_regmap_config);
+ if (IS_ERR(map)) {
+ dev_err(dev, "could not initialize regmap\n");
+ return PTR_ERR(map);
+ }
+
+ mmc_phy = devm_kzalloc(dev, sizeof(struct am654_mmc_phy), GFP_KERNEL);
+ if (!mmc_phy)
+ return -ENOMEM;
+
+ mmc_phy->reg_base = map;
+ err = of_property_read_u32(np, "ti,otap-del-sel",
+ &mmc_phy->otap_del_sel);
+ if (err)
+ return err;
+
+ err = of_property_read_u32(np, "ti,trm-icp",
+ &mmc_phy->trm_icp);
+ if (err)
+ return err;
+
+ err = of_property_read_u32(np, "ti,driver-strength-ohm", &drv_strength);
+ if (err)
+ return err;
+
+ switch (drv_strength) {
+ case 50:
+ mmc_phy->drv_strength = DRIVER_STRENGTH_50_OHM;
+ break;
+ case 33:
+ mmc_phy->drv_strength = DRIVER_STRENGTH_33_OHM;
+ break;
+ case 66:
+ mmc_phy->drv_strength = DRIVER_STRENGTH_66_OHM;
+ break;
+ case 100:
+ mmc_phy->drv_strength = DRIVER_STRENGTH_100_OHM;
+ break;
+ case 40:
+ mmc_phy->drv_strength = DRIVER_STRENGTH_40_OHM;
+ break;
+ default:
+ dev_err(dev, "Invalid driver strength\n");
+ return -EINVAL;
+ }
+
+ generic_phy = devm_phy_create(dev, dev->of_node, &ops);
+ if (IS_ERR(generic_phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(generic_phy);
+ }
+
+ phy_set_drvdata(generic_phy, mmc_phy);
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id am654_mmc_phy_dt_ids[] = {
+ { .compatible = "ti,am654-mmc-phy" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, am654_mmc_phy_dt_ids);
+
+static struct platform_driver am654_mmc_phy_driver = {
+ .probe = am654_mmc_phy_probe,
+ .driver = {
+ .name = "am654-mmc-phy",
+ .of_match_table = am654_mmc_phy_dt_ids,
+ },
+};
+
+module_platform_driver(am654_mmc_phy_driver);
+
+MODULE_AUTHOR("Faiz Abbas <[email protected]>");
+MODULE_DESCRIPTION("TI AM654 MMC PHY driver");
+MODULE_LICENSE("GPL v2");
--
2.18.0


2018-10-05 15:59:06

by Andrew Davis

[permalink] [raw]
Subject: Re: [PATCH 1/6] dt-bindings: phy: am654-mmc-phy: Document new phy bindings

On 10/04/2018 06:14 AM, Faiz Abbas wrote:
> Add information to document bindings for the MMC PHY
> on TI's AM654 devices.
>
> Signed-off-by: Faiz Abbas <[email protected]>
> Signed-off-by: Sekhar Nori <[email protected]>
> ---
> .../devicetree/bindings/phy/am654-mmc-phy.txt | 42 +++++++++++++++++++
> 1 file changed, 42 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/am654-mmc-phy.txt
>
> diff --git a/Documentation/devicetree/bindings/phy/am654-mmc-phy.txt b/Documentation/devicetree/bindings/phy/am654-mmc-phy.txt
> new file mode 100644
> index 000000000000..766921612758
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/am654-mmc-phy.txt


Name ti,am654-mmc-phy.txt might be more consistent name.


> @@ -0,0 +1,42 @@
> +TI AM654 MMC PHY
> +-------------------
> +
> +Required properties:
> + - compatible: ti,am654-emmc-phy
> + - #phy-cells: must be 0
> + - reg: PHY registers address offset and size
> + - clocks: must be phandle of the clock provider which is the host
> + controller
> + - clock-names: must be "mmcclk"
> + - ti,otap-del-sel: Output Tap Delay select.
> + - ti,trm-icp: DLL trim select.
> + - ti,driver-strength-ohm: driver strength in ohms.
> + Valid values are 33, 40, 50, 66, and 100 ohms.
> +
> +Example:
> + mmc_phy1: mmc_phy@10100 {
> + compatible = "ti,am654-mmc-phy";
> + reg = <0x10100 0x34>;
> + clocks = <&sdhci1>;
> + clock-names = "mmcclk";
> + #phy-cells = <0>;
> + ti,otap-del-sel = <0x2>;
> + ti,trm-icp = <0x8>;
> + status = "disabled";


No need for disabled in the example case. Also not sure the host
controller example below needs to be here, there are already examples
for that binding in mmc/arasan,sdhci.txt.

Andrew


> + };
> +
> +Host controller node:
> +
> + sdhci1: sdhci@0 {
> + compatible = "ti,am654-sdhci-5.1";
> + reg = <0x0 0x1000>;
> + power-domains = <&k3_pds 48>;
> + clocks = <&k3_clks 48 0>, <&k3_clks 48 1>;
> + clock-names = "clk_ahb", "clk_xin";
> + interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
> + clock-output-names = "mmc_cardclck";
> + #clock-cells = <0>;
> + phys=<&mmc_phy1>;
> + phy-names = "phy_arasan";
> + status = "disabled";
> + };
>

2018-10-08 11:31:16

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH 1/6] dt-bindings: phy: am654-mmc-phy: Document new phy bindings

[...]

> +Host controller node:
> +
> + sdhci1: sdhci@0 {
> + compatible = "ti,am654-sdhci-5.1";
> + reg = <0x0 0x1000>;
> + power-domains = <&k3_pds 48>;
> + clocks = <&k3_clks 48 0>, <&k3_clks 48 1>;
> + clock-names = "clk_ahb", "clk_xin";
> + interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
> + clock-output-names = "mmc_cardclck";

mmc_cardclk

> + #clock-cells = <0>;
> + phys=<&mmc_phy1>;
> + phy-names = "phy_arasan";
> + status = "disabled";
> + };
> --
> 2.18.0
>

Besides the nitpick and the comments from Andrew, this looks good to me.

Kind regards
Uffe

2018-10-08 11:33:32

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH 2/6] phy: am654-mmc-phy: Add Support for MMC PHY on AM654 Devices

On 4 October 2018 at 13:14, Faiz Abbas <[email protected]> wrote:
> Add driver support for the MMC physical layer present
> on TI's AM654 devices.
>
> Signed-off-by: Faiz Abbas <[email protected]>
> Signed-off-by: Sekhar Nori <[email protected]>

I assume Kishon would like to pick up this through his tree? If not,
please tell and I can do it, with his ack.

Reviewed-by: Ulf Hansson <[email protected]>

Kind regards
Uffe

> ---
> drivers/phy/ti/Kconfig | 7 +
> drivers/phy/ti/Makefile | 1 +
> drivers/phy/ti/phy-am654-mmc.c | 291 +++++++++++++++++++++++++++++++++
> 3 files changed, 299 insertions(+)
> create mode 100644 drivers/phy/ti/phy-am654-mmc.c
>
> diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig
> index 20503562666c..ea5fe4db01c8 100644
> --- a/drivers/phy/ti/Kconfig
> +++ b/drivers/phy/ti/Kconfig
> @@ -76,3 +76,10 @@ config TWL4030_USB
> family chips (including the TWL5030 and TPS659x0 devices).
> This transceiver supports high and full speed devices plus,
> in host mode, low speed.
> +
> +config PHY_AM654_MMC
> + bool "TI AM654 MMC PHY Support"
> + select GENERIC_PHY
> + help
> + This option enables support for the Physical layer for MMC host
> + controllers present on TI AM654 SOCs.
> diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile
> index 9f361756eaf2..5b2db2d164a5 100644
> --- a/drivers/phy/ti/Makefile
> +++ b/drivers/phy/ti/Makefile
> @@ -6,3 +6,4 @@ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
> obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
> obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
> obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
> +obj-$(CONFIG_PHY_AM654_MMC) += phy-am654-mmc.o
> diff --git a/drivers/phy/ti/phy-am654-mmc.c b/drivers/phy/ti/phy-am654-mmc.c
> new file mode 100644
> index 000000000000..91255947fb67
> --- /dev/null
> +++ b/drivers/phy/ti/phy-am654-mmc.c
> @@ -0,0 +1,291 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * phy-am654-mmc.c - MMC PHY driver for TI's AM654 SOCs
> + *
> + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/printk.h>
> +#include <linux/regmap.h>
> +
> +/* MMC PHY Registers */
> +#define PHYCTRL_CTRL1_REG 0x00
> +#define PHYCTRL_CTRL2_REG 0x04
> +#define PHYCTRL_CTRL3_REG 0x08
> +#define PHYCTRL_CTRL4_REG 0x0C
> +#define PHYCTRL_CTRL5_REG 0x10
> +#define PHYCTRL_CTRL6_REG 0x14
> +#define PHYCTRL_STAT1_REG 0x30
> +#define PHYCTRL_STAT2_REG 0x34
> +
> +#define IOMUX_ENABLE_SHIFT 31
> +#define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT)
> +#define OTAPDLYENA_SHIFT 20
> +#define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT)
> +#define OTAPDLYSEL_SHIFT 12
> +#define OTAPDLYSEL_MASK GENMASK(15, 12)
> +#define STRBSEL_SHIFT 24
> +#define STRBSEL_MASK GENMASK(27, 24)
> +#define SEL50_SHIFT 8
> +#define SEL50_MASK BIT(SEL50_SHIFT)
> +#define SEL100_SHIFT 9
> +#define SEL100_MASK BIT(SEL100_SHIFT)
> +#define DLL_TRIM_ICP_SHIFT 4
> +#define DLL_TRIM_ICP_MASK GENMASK(7, 4)
> +#define DR_TY_SHIFT 20
> +#define DR_TY_MASK GENMASK(22, 20)
> +#define ENDLL_SHIFT 1
> +#define ENDLL_MASK BIT(ENDLL_SHIFT)
> +#define DLLRDY_SHIFT 0
> +#define DLLRDY_MASK BIT(DLLRDY_SHIFT)
> +#define PDB_SHIFT 0
> +#define PDB_MASK BIT(PDB_SHIFT)
> +#define CALDONE_SHIFT 1
> +#define CALDONE_MASK BIT(CALDONE_SHIFT)
> +
> +#define DRIVER_STRENGTH_50_OHM 0x0
> +#define DRIVER_STRENGTH_33_OHM 0x1
> +#define DRIVER_STRENGTH_66_OHM 0x2
> +#define DRIVER_STRENGTH_100_OHM 0x3
> +#define DRIVER_STRENGTH_40_OHM 0x4
> +
> +static struct regmap_config am654_mmc_phy_regmap_config = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .fast_io = true,
> +};
> +
> +struct am654_mmc_phy {
> + struct regmap *reg_base;
> + struct clk *mmcclk;
> + int otap_del_sel;
> + int trm_icp;
> + int drv_strength;
> +};
> +
> +static int am654_mmc_phy_init(struct phy *phy)
> +{
> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
> + int ret;
> + u32 val;
> +
> + /* Reset registers to default value */
> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL1_REG, 0x10000);
> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL4_REG, 0x0);
> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL5_REG, 0x0);
> +
> + /* Calibrate IO lines */
> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
> + PDB_MASK, PDB_MASK);
> + ret = regmap_read_poll_timeout(mmc_phy->reg_base, PHYCTRL_STAT1_REG,
> + val, val & CALDONE_MASK, 1, 20);
> + if (ret)
> + return ret;
> +
> + /* Enable pins by setting the IO mux to 0 */
> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
> + IOMUX_ENABLE_MASK, 0);
> +
> + mmc_phy->mmcclk = clk_get(&phy->dev, "mmcclk");
> + if (IS_ERR(mmc_phy->mmcclk)) {
> + dev_err(&phy->dev, "Error getting mmcclk");
> + return PTR_ERR(mmc_phy->mmcclk);
> + }
> +
> + return 0;
> +}
> +
> +static int am654_mmc_phy_exit(struct phy *phy)
> +{
> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
> +
> + clk_put(mmc_phy->mmcclk);
> +
> + return 0;
> +}
> +
> +static int am654_mmc_phy_power_on(struct phy *phy)
> +{
> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
> + u32 mask, val;
> + int sel50, sel100;
> + int rate;
> +
> + /* Setup DLL Output TAP delay */
> + mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
> + val = (1 << OTAPDLYENA_SHIFT) |
> + (mmc_phy->otap_del_sel << OTAPDLYSEL_SHIFT);
> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL4_REG,
> + mask, val);
> +
> + rate = clk_get_rate(mmc_phy->mmcclk);
> + switch (rate) {
> + case 200000000:
> + sel50 = 0;
> + sel100 = 0;
> + break;
> + case 100000000:
> + sel50 = 0;
> + sel100 = 1;
> + break;
> + default:
> + sel50 = 1;
> + sel100 = 0;
> + }
> +
> + /* Configure PHY DLL frequency */
> + mask = SEL50_MASK | SEL100_MASK;
> + val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL5_REG,
> + mask, val);
> +
> + /* Configure DLL TRIM */
> + mask = DLL_TRIM_ICP_MASK;
> + val = mmc_phy->trm_icp << DLL_TRIM_ICP_SHIFT;
> +
> + /* Configure DLL driver strength */
> + mask |= DR_TY_MASK;
> + val |= mmc_phy->drv_strength << DR_TY_SHIFT;
> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG, mask, val);
> +
> + /* Enable DLL */
> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
> + ENDLL_MASK, 0x1 << ENDLL_SHIFT);
> +
> + /*
> + * Poll for DLL ready. Use a one second timeout.
> + * Works in all experiments done so far
> + */
> + return regmap_read_poll_timeout(mmc_phy->reg_base, PHYCTRL_STAT1_REG,
> + val, val & DLLRDY_MASK, 1000, 1000000);
> +
> +}
> +
> +static int am654_mmc_phy_power_off(struct phy *phy)
> +{
> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
> +
> + /* Disable DLL */
> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
> + ENDLL_MASK, 0);
> +
> + /* Reset registers to default value except PDB */
> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
> + 0x10000 | PDB_MASK);
> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL4_REG, 0x0);
> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL5_REG, 0x0);
> +
> + return 0;
> +}
> +
> +static const struct phy_ops ops = {
> + .init = am654_mmc_phy_init,
> + .exit = am654_mmc_phy_exit,
> + .power_on = am654_mmc_phy_power_on,
> + .power_off = am654_mmc_phy_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static int am654_mmc_phy_probe(struct platform_device *pdev)
> +{
> + struct phy_provider *phy_provider;
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + struct am654_mmc_phy *mmc_phy;
> + struct phy *generic_phy;
> + struct resource *res;
> + void __iomem *base;
> + struct regmap *map;
> + int drv_strength;
> + int err;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + map = devm_regmap_init_mmio(dev, base, &am654_mmc_phy_regmap_config);
> + if (IS_ERR(map)) {
> + dev_err(dev, "could not initialize regmap\n");
> + return PTR_ERR(map);
> + }
> +
> + mmc_phy = devm_kzalloc(dev, sizeof(struct am654_mmc_phy), GFP_KERNEL);
> + if (!mmc_phy)
> + return -ENOMEM;
> +
> + mmc_phy->reg_base = map;
> + err = of_property_read_u32(np, "ti,otap-del-sel",
> + &mmc_phy->otap_del_sel);
> + if (err)
> + return err;
> +
> + err = of_property_read_u32(np, "ti,trm-icp",
> + &mmc_phy->trm_icp);
> + if (err)
> + return err;
> +
> + err = of_property_read_u32(np, "ti,driver-strength-ohm", &drv_strength);
> + if (err)
> + return err;
> +
> + switch (drv_strength) {
> + case 50:
> + mmc_phy->drv_strength = DRIVER_STRENGTH_50_OHM;
> + break;
> + case 33:
> + mmc_phy->drv_strength = DRIVER_STRENGTH_33_OHM;
> + break;
> + case 66:
> + mmc_phy->drv_strength = DRIVER_STRENGTH_66_OHM;
> + break;
> + case 100:
> + mmc_phy->drv_strength = DRIVER_STRENGTH_100_OHM;
> + break;
> + case 40:
> + mmc_phy->drv_strength = DRIVER_STRENGTH_40_OHM;
> + break;
> + default:
> + dev_err(dev, "Invalid driver strength\n");
> + return -EINVAL;
> + }
> +
> + generic_phy = devm_phy_create(dev, dev->of_node, &ops);
> + if (IS_ERR(generic_phy)) {
> + dev_err(dev, "failed to create PHY\n");
> + return PTR_ERR(generic_phy);
> + }
> +
> + phy_set_drvdata(generic_phy, mmc_phy);
> + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> +
> + return PTR_ERR_OR_ZERO(phy_provider);
> +}
> +
> +static const struct of_device_id am654_mmc_phy_dt_ids[] = {
> + { .compatible = "ti,am654-mmc-phy" },
> + {}
> +};
> +
> +MODULE_DEVICE_TABLE(of, am654_mmc_phy_dt_ids);
> +
> +static struct platform_driver am654_mmc_phy_driver = {
> + .probe = am654_mmc_phy_probe,
> + .driver = {
> + .name = "am654-mmc-phy",
> + .of_match_table = am654_mmc_phy_dt_ids,
> + },
> +};
> +
> +module_platform_driver(am654_mmc_phy_driver);
> +
> +MODULE_AUTHOR("Faiz Abbas <[email protected]>");
> +MODULE_DESCRIPTION("TI AM654 MMC PHY driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.18.0
>

2018-10-09 05:19:27

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH 2/6] phy: am654-mmc-phy: Add Support for MMC PHY on AM654 Devices

Hi Uffe,

On Monday 08 October 2018 05:02 PM, Ulf Hansson wrote:
> On 4 October 2018 at 13:14, Faiz Abbas <[email protected]> wrote:
>> Add driver support for the MMC physical layer present
>> on TI's AM654 devices.
>>
>> Signed-off-by: Faiz Abbas <[email protected]>
>> Signed-off-by: Sekhar Nori <[email protected]>
>
> I assume Kishon would like to pick up this through his tree? If not,
> please tell and I can do it, with his ack.

yes, I'll pick this in my tree.

>
> Reviewed-by: Ulf Hansson <[email protected]>

Thanks
Kishon

>
> Kind regards
> Uffe
>
>> ---
>> drivers/phy/ti/Kconfig | 7 +
>> drivers/phy/ti/Makefile | 1 +
>> drivers/phy/ti/phy-am654-mmc.c | 291 +++++++++++++++++++++++++++++++++
>> 3 files changed, 299 insertions(+)
>> create mode 100644 drivers/phy/ti/phy-am654-mmc.c
>>
>> diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig
>> index 20503562666c..ea5fe4db01c8 100644
>> --- a/drivers/phy/ti/Kconfig
>> +++ b/drivers/phy/ti/Kconfig
>> @@ -76,3 +76,10 @@ config TWL4030_USB
>> family chips (including the TWL5030 and TPS659x0 devices).
>> This transceiver supports high and full speed devices plus,
>> in host mode, low speed.
>> +
>> +config PHY_AM654_MMC
>> + bool "TI AM654 MMC PHY Support"
>> + select GENERIC_PHY
>> + help
>> + This option enables support for the Physical layer for MMC host
>> + controllers present on TI AM654 SOCs.
>> diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile
>> index 9f361756eaf2..5b2db2d164a5 100644
>> --- a/drivers/phy/ti/Makefile
>> +++ b/drivers/phy/ti/Makefile
>> @@ -6,3 +6,4 @@ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
>> obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
>> obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
>> obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
>> +obj-$(CONFIG_PHY_AM654_MMC) += phy-am654-mmc.o
>> diff --git a/drivers/phy/ti/phy-am654-mmc.c b/drivers/phy/ti/phy-am654-mmc.c
>> new file mode 100644
>> index 000000000000..91255947fb67
>> --- /dev/null
>> +++ b/drivers/phy/ti/phy-am654-mmc.c
>> @@ -0,0 +1,291 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * phy-am654-mmc.c - MMC PHY driver for TI's AM654 SOCs
>> + *
>> + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
>> + *
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/printk.h>
>> +#include <linux/regmap.h>
>> +
>> +/* MMC PHY Registers */
>> +#define PHYCTRL_CTRL1_REG 0x00
>> +#define PHYCTRL_CTRL2_REG 0x04
>> +#define PHYCTRL_CTRL3_REG 0x08
>> +#define PHYCTRL_CTRL4_REG 0x0C
>> +#define PHYCTRL_CTRL5_REG 0x10
>> +#define PHYCTRL_CTRL6_REG 0x14
>> +#define PHYCTRL_STAT1_REG 0x30
>> +#define PHYCTRL_STAT2_REG 0x34
>> +
>> +#define IOMUX_ENABLE_SHIFT 31
>> +#define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT)
>> +#define OTAPDLYENA_SHIFT 20
>> +#define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT)
>> +#define OTAPDLYSEL_SHIFT 12
>> +#define OTAPDLYSEL_MASK GENMASK(15, 12)
>> +#define STRBSEL_SHIFT 24
>> +#define STRBSEL_MASK GENMASK(27, 24)
>> +#define SEL50_SHIFT 8
>> +#define SEL50_MASK BIT(SEL50_SHIFT)
>> +#define SEL100_SHIFT 9
>> +#define SEL100_MASK BIT(SEL100_SHIFT)
>> +#define DLL_TRIM_ICP_SHIFT 4
>> +#define DLL_TRIM_ICP_MASK GENMASK(7, 4)
>> +#define DR_TY_SHIFT 20
>> +#define DR_TY_MASK GENMASK(22, 20)
>> +#define ENDLL_SHIFT 1
>> +#define ENDLL_MASK BIT(ENDLL_SHIFT)
>> +#define DLLRDY_SHIFT 0
>> +#define DLLRDY_MASK BIT(DLLRDY_SHIFT)
>> +#define PDB_SHIFT 0
>> +#define PDB_MASK BIT(PDB_SHIFT)
>> +#define CALDONE_SHIFT 1
>> +#define CALDONE_MASK BIT(CALDONE_SHIFT)
>> +
>> +#define DRIVER_STRENGTH_50_OHM 0x0
>> +#define DRIVER_STRENGTH_33_OHM 0x1
>> +#define DRIVER_STRENGTH_66_OHM 0x2
>> +#define DRIVER_STRENGTH_100_OHM 0x3
>> +#define DRIVER_STRENGTH_40_OHM 0x4
>> +
>> +static struct regmap_config am654_mmc_phy_regmap_config = {
>> + .reg_bits = 32,
>> + .val_bits = 32,
>> + .reg_stride = 4,
>> + .fast_io = true,
>> +};
>> +
>> +struct am654_mmc_phy {
>> + struct regmap *reg_base;
>> + struct clk *mmcclk;
>> + int otap_del_sel;
>> + int trm_icp;
>> + int drv_strength;
>> +};
>> +
>> +static int am654_mmc_phy_init(struct phy *phy)
>> +{
>> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
>> + int ret;
>> + u32 val;
>> +
>> + /* Reset registers to default value */
>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL1_REG, 0x10000);
>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL4_REG, 0x0);
>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL5_REG, 0x0);
>> +
>> + /* Calibrate IO lines */
>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
>> + PDB_MASK, PDB_MASK);
>> + ret = regmap_read_poll_timeout(mmc_phy->reg_base, PHYCTRL_STAT1_REG,
>> + val, val & CALDONE_MASK, 1, 20);
>> + if (ret)
>> + return ret;
>> +
>> + /* Enable pins by setting the IO mux to 0 */
>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
>> + IOMUX_ENABLE_MASK, 0);
>> +
>> + mmc_phy->mmcclk = clk_get(&phy->dev, "mmcclk");
>> + if (IS_ERR(mmc_phy->mmcclk)) {
>> + dev_err(&phy->dev, "Error getting mmcclk");
>> + return PTR_ERR(mmc_phy->mmcclk);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int am654_mmc_phy_exit(struct phy *phy)
>> +{
>> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
>> +
>> + clk_put(mmc_phy->mmcclk);
>> +
>> + return 0;
>> +}
>> +
>> +static int am654_mmc_phy_power_on(struct phy *phy)
>> +{
>> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
>> + u32 mask, val;
>> + int sel50, sel100;
>> + int rate;
>> +
>> + /* Setup DLL Output TAP delay */
>> + mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
>> + val = (1 << OTAPDLYENA_SHIFT) |
>> + (mmc_phy->otap_del_sel << OTAPDLYSEL_SHIFT);
>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL4_REG,
>> + mask, val);
>> +
>> + rate = clk_get_rate(mmc_phy->mmcclk);
>> + switch (rate) {
>> + case 200000000:
>> + sel50 = 0;
>> + sel100 = 0;
>> + break;
>> + case 100000000:
>> + sel50 = 0;
>> + sel100 = 1;
>> + break;
>> + default:
>> + sel50 = 1;
>> + sel100 = 0;
>> + }
>> +
>> + /* Configure PHY DLL frequency */
>> + mask = SEL50_MASK | SEL100_MASK;
>> + val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL5_REG,
>> + mask, val);
>> +
>> + /* Configure DLL TRIM */
>> + mask = DLL_TRIM_ICP_MASK;
>> + val = mmc_phy->trm_icp << DLL_TRIM_ICP_SHIFT;
>> +
>> + /* Configure DLL driver strength */
>> + mask |= DR_TY_MASK;
>> + val |= mmc_phy->drv_strength << DR_TY_SHIFT;
>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG, mask, val);
>> +
>> + /* Enable DLL */
>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
>> + ENDLL_MASK, 0x1 << ENDLL_SHIFT);
>> +
>> + /*
>> + * Poll for DLL ready. Use a one second timeout.
>> + * Works in all experiments done so far
>> + */
>> + return regmap_read_poll_timeout(mmc_phy->reg_base, PHYCTRL_STAT1_REG,
>> + val, val & DLLRDY_MASK, 1000, 1000000);
>> +
>> +}
>> +
>> +static int am654_mmc_phy_power_off(struct phy *phy)
>> +{
>> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
>> +
>> + /* Disable DLL */
>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
>> + ENDLL_MASK, 0);
>> +
>> + /* Reset registers to default value except PDB */
>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
>> + 0x10000 | PDB_MASK);
>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL4_REG, 0x0);
>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL5_REG, 0x0);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct phy_ops ops = {
>> + .init = am654_mmc_phy_init,
>> + .exit = am654_mmc_phy_exit,
>> + .power_on = am654_mmc_phy_power_on,
>> + .power_off = am654_mmc_phy_power_off,
>> + .owner = THIS_MODULE,
>> +};
>> +
>> +static int am654_mmc_phy_probe(struct platform_device *pdev)
>> +{
>> + struct phy_provider *phy_provider;
>> + struct device *dev = &pdev->dev;
>> + struct device_node *np = dev->of_node;
>> + struct am654_mmc_phy *mmc_phy;
>> + struct phy *generic_phy;
>> + struct resource *res;
>> + void __iomem *base;
>> + struct regmap *map;
>> + int drv_strength;
>> + int err;
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + base = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(base))
>> + return PTR_ERR(base);
>> +
>> + map = devm_regmap_init_mmio(dev, base, &am654_mmc_phy_regmap_config);
>> + if (IS_ERR(map)) {
>> + dev_err(dev, "could not initialize regmap\n");
>> + return PTR_ERR(map);
>> + }
>> +
>> + mmc_phy = devm_kzalloc(dev, sizeof(struct am654_mmc_phy), GFP_KERNEL);
>> + if (!mmc_phy)
>> + return -ENOMEM;
>> +
>> + mmc_phy->reg_base = map;
>> + err = of_property_read_u32(np, "ti,otap-del-sel",
>> + &mmc_phy->otap_del_sel);
>> + if (err)
>> + return err;
>> +
>> + err = of_property_read_u32(np, "ti,trm-icp",
>> + &mmc_phy->trm_icp);
>> + if (err)
>> + return err;
>> +
>> + err = of_property_read_u32(np, "ti,driver-strength-ohm", &drv_strength);
>> + if (err)
>> + return err;
>> +
>> + switch (drv_strength) {
>> + case 50:
>> + mmc_phy->drv_strength = DRIVER_STRENGTH_50_OHM;
>> + break;
>> + case 33:
>> + mmc_phy->drv_strength = DRIVER_STRENGTH_33_OHM;
>> + break;
>> + case 66:
>> + mmc_phy->drv_strength = DRIVER_STRENGTH_66_OHM;
>> + break;
>> + case 100:
>> + mmc_phy->drv_strength = DRIVER_STRENGTH_100_OHM;
>> + break;
>> + case 40:
>> + mmc_phy->drv_strength = DRIVER_STRENGTH_40_OHM;
>> + break;
>> + default:
>> + dev_err(dev, "Invalid driver strength\n");
>> + return -EINVAL;
>> + }
>> +
>> + generic_phy = devm_phy_create(dev, dev->of_node, &ops);
>> + if (IS_ERR(generic_phy)) {
>> + dev_err(dev, "failed to create PHY\n");
>> + return PTR_ERR(generic_phy);
>> + }
>> +
>> + phy_set_drvdata(generic_phy, mmc_phy);
>> + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
>> +
>> + return PTR_ERR_OR_ZERO(phy_provider);
>> +}
>> +
>> +static const struct of_device_id am654_mmc_phy_dt_ids[] = {
>> + { .compatible = "ti,am654-mmc-phy" },
>> + {}
>> +};
>> +
>> +MODULE_DEVICE_TABLE(of, am654_mmc_phy_dt_ids);
>> +
>> +static struct platform_driver am654_mmc_phy_driver = {
>> + .probe = am654_mmc_phy_probe,
>> + .driver = {
>> + .name = "am654-mmc-phy",
>> + .of_match_table = am654_mmc_phy_dt_ids,
>> + },
>> +};
>> +
>> +module_platform_driver(am654_mmc_phy_driver);
>> +
>> +MODULE_AUTHOR("Faiz Abbas <[email protected]>");
>> +MODULE_DESCRIPTION("TI AM654 MMC PHY driver");
>> +MODULE_LICENSE("GPL v2");
>> --
>> 2.18.0
>>

2018-10-09 06:33:11

by Faiz Abbas

[permalink] [raw]
Subject: Re: [PATCH 1/6] dt-bindings: phy: am654-mmc-phy: Document new phy bindings

Hi Andrew,

On Friday 05 October 2018 09:28 PM, Andrew F. Davis wrote:
> On 10/04/2018 06:14 AM, Faiz Abbas wrote:
>> Add information to document bindings for the MMC PHY
>> on TI's AM654 devices.
>>
>> Signed-off-by: Faiz Abbas <[email protected]>
>> Signed-off-by: Sekhar Nori <[email protected]>
>> ---
>> .../devicetree/bindings/phy/am654-mmc-phy.txt | 42 +++++++++++++++++++
>> 1 file changed, 42 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/phy/am654-mmc-phy.txt
>>
>> diff --git a/Documentation/devicetree/bindings/phy/am654-mmc-phy.txt b/Documentation/devicetree/bindings/phy/am654-mmc-phy.txt
>> new file mode 100644
>> index 000000000000..766921612758
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/am654-mmc-phy.txt
>
>
> Name ti,am654-mmc-phy.txt might be more consistent name.

Ok.

>
>
>> @@ -0,0 +1,42 @@
>> +TI AM654 MMC PHY
>> +-------------------
>> +
>> +Required properties:
>> + - compatible: ti,am654-emmc-phy
>> + - #phy-cells: must be 0
>> + - reg: PHY registers address offset and size
>> + - clocks: must be phandle of the clock provider which is the host
>> + controller
>> + - clock-names: must be "mmcclk"
>> + - ti,otap-del-sel: Output Tap Delay select.
>> + - ti,trm-icp: DLL trim select.
>> + - ti,driver-strength-ohm: driver strength in ohms.
>> + Valid values are 33, 40, 50, 66, and 100 ohms.
>> +
>> +Example:
>> + mmc_phy1: mmc_phy@10100 {
>> + compatible = "ti,am654-mmc-phy";
>> + reg = <0x10100 0x34>;
>> + clocks = <&sdhci1>;
>> + clock-names = "mmcclk";
>> + #phy-cells = <0>;
>> + ti,otap-del-sel = <0x2>;
>> + ti,trm-icp = <0x8>;
>> + status = "disabled";
>
>
> No need for disabled in the example case.

Ok.

> Also not sure the host
> controller example below needs to be here, there are already examples
> for that binding in mmc/arasan,sdhci.txt.
>

I had added it because it is a clock source in clocks=<&sdhci1> so it
should have #clock-cells and clock-output-names. I suppose I can just
reference it here.

Thanks,
Faiz

2018-10-09 07:31:42

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH 3/6] dt-bindings: mmc: sdhci-of-arasan: Add new compatible for AM654 MMC PHY

On 4 October 2018 at 13:14, Faiz Abbas <[email protected]> wrote:
> Add a new compatible to use the host controller driver with the
> MMC PHY on TI's AM654 SOCs
>
> Signed-off-by: Faiz Abbas <[email protected]>
> Signed-off-by: Sekhar Nori <[email protected]>

Applied for next, thanks!

Kind regards
Uffe

> ---
> Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
> index f6ddba31cb73..e2effe17f05e 100644
> --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
> +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
> @@ -15,6 +15,7 @@ Required Properties:
> - "arasan,sdhci-5.1": generic Arasan SDHCI 5.1 PHY
> - "rockchip,rk3399-sdhci-5.1", "arasan,sdhci-5.1": rk3399 eMMC PHY
> For this device it is strongly suggested to include arasan,soc-ctl-syscon.
> + - "ti,am654-sdhci-5.1", "arasan,sdhci-5.1": TI AM654 MMC PHY
> - reg: From mmc bindings: Register location and length.
> - clocks: From clock bindings: Handles to clock inputs.
> - clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
> --
> 2.18.0
>

2018-10-09 07:31:50

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH 4/6] mmc: sdhci-of-arasan: Add a single data structure to incorporate pdata and soc_ctl_map

On 4 October 2018 at 13:14, Faiz Abbas <[email protected]> wrote:
> Currently, the driver passes platform data as a global structure
> and uses the .data of of_device_id to pass the soc_ctl_map. To
> make the implementation more flexible add a single data structure
> that incorporates both of the above and pass it in the .data of
> of_device_id.
>
> Signed-off-by: Faiz Abbas <[email protected]>
> Signed-off-by: Sekhar Nori <[email protected]>

Applied for next, thanks!

Kind regards
Uffe

> ---
> drivers/mmc/host/sdhci-of-arasan.c | 47 ++++++++++++++++++++----------
> 1 file changed, 32 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
> index b806b24a3e5f..c9e3e050ccc8 100644
> --- a/drivers/mmc/host/sdhci-of-arasan.c
> +++ b/drivers/mmc/host/sdhci-of-arasan.c
> @@ -107,6 +107,11 @@ struct sdhci_arasan_data {
> #define SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE BIT(1)
> };
>
> +struct sdhci_arasan_of_data {
> + const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
> + const struct sdhci_pltfm_data *pdata;
> +};
> +
> static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = {
> .baseclkfreq = { .reg = 0xf000, .width = 8, .shift = 8 },
> .clockmultiplier = { .reg = 0xf02c, .width = 8, .shift = 0},
> @@ -307,6 +312,10 @@ static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
> SDHCI_QUIRK2_STOP_WITH_TC,
> };
>
> +static struct sdhci_arasan_of_data sdhci_arasan_data = {
> + .pdata = &sdhci_arasan_pdata,
> +};
> +
> static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
> {
> int cmd_error = 0;
> @@ -363,6 +372,11 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
> SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
> };
>
> +static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = {
> + .soc_ctl_map = &rk3399_soc_ctl_map,
> + .pdata = &sdhci_arasan_cqe_pdata,
> +};
> +
> #ifdef CONFIG_PM_SLEEP
> /**
> * sdhci_arasan_suspend - Suspend method for the driver
> @@ -462,14 +476,21 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
> /* SoC-specific compatible strings w/ soc_ctl_map */
> {
> .compatible = "rockchip,rk3399-sdhci-5.1",
> - .data = &rk3399_soc_ctl_map,
> + .data = &sdhci_arasan_rk3399_data,
> },
> -
> /* Generic compatible below here */
> - { .compatible = "arasan,sdhci-8.9a" },
> - { .compatible = "arasan,sdhci-5.1" },
> - { .compatible = "arasan,sdhci-4.9a" },
> -
> + {
> + .compatible = "arasan,sdhci-8.9a",
> + .data = &sdhci_arasan_data,
> + },
> + {
> + .compatible = "arasan,sdhci-5.1",
> + .data = &sdhci_arasan_data,
> + },
> + {
> + .compatible = "arasan,sdhci-4.9a",
> + .data = &sdhci_arasan_data,
> + },
> { /* sentinel */ }
> };
> MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
> @@ -707,14 +728,11 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
> struct sdhci_pltfm_host *pltfm_host;
> struct sdhci_arasan_data *sdhci_arasan;
> struct device_node *np = pdev->dev.of_node;
> - const struct sdhci_pltfm_data *pdata;
> -
> - if (of_device_is_compatible(pdev->dev.of_node, "arasan,sdhci-5.1"))
> - pdata = &sdhci_arasan_cqe_pdata;
> - else
> - pdata = &sdhci_arasan_pdata;
> + const struct sdhci_arasan_of_data *data;
>
> - host = sdhci_pltfm_init(pdev, pdata, sizeof(*sdhci_arasan));
> + match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
> + data = match->data;
> + host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*sdhci_arasan));
>
> if (IS_ERR(host))
> return PTR_ERR(host);
> @@ -723,8 +741,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
> sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
> sdhci_arasan->host = host;
>
> - match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
> - sdhci_arasan->soc_ctl_map = match->data;
> + sdhci_arasan->soc_ctl_map = data->soc_ctl_map;
>
> node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
> if (node) {
> --
> 2.18.0
>

2018-10-09 07:31:57

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH 5/6] mmc: sdhci-of-arasan: Add Support for AM654 MMC and PHY

On 4 October 2018 at 13:14, Faiz Abbas <[email protected]> wrote:
> The current arasan sdhci PHY configuration isn't compatible
> with the PHY on TI's AM654 devices.
>
> Therefore, add a new compatible, AM654 specific quirks
> and a new AM654 specific set_clock function which
> configures the PHY in a sane way.
>
> Signed-off-by: Faiz Abbas <[email protected]>
> Signed-off-by: Sekhar Nori <[email protected]>

Applied for next, thanks!

Kind regards
Uffe

> ---
> drivers/mmc/host/sdhci-of-arasan.c | 46 ++++++++++++++++++++++++++++++
> 1 file changed, 46 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
> index c9e3e050ccc8..142c4b802f31 100644
> --- a/drivers/mmc/host/sdhci-of-arasan.c
> +++ b/drivers/mmc/host/sdhci-of-arasan.c
> @@ -231,6 +231,25 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
> }
> }
>
> +static void sdhci_arasan_am654_set_clock(struct sdhci_host *host,
> + unsigned int clock)
> +{
> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> + struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
> +
> + if (sdhci_arasan->is_phy_on) {
> + phy_power_off(sdhci_arasan->phy);
> + sdhci_arasan->is_phy_on = false;
> + }
> +
> + sdhci_set_clock(host, clock);
> +
> + if (clock > PHY_CLK_TOO_SLOW_HZ) {
> + phy_power_on(sdhci_arasan->phy);
> + sdhci_arasan->is_phy_on = true;
> + }
> +}
> +
> static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc,
> struct mmc_ios *ios)
> {
> @@ -316,6 +335,29 @@ static struct sdhci_arasan_of_data sdhci_arasan_data = {
> .pdata = &sdhci_arasan_pdata,
> };
>
> +static const struct sdhci_ops sdhci_arasan_am654_ops = {
> + .set_clock = sdhci_arasan_am654_set_clock,
> + .get_max_clock = sdhci_pltfm_clk_get_max_clock,
> + .get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
> + .set_bus_width = sdhci_set_bus_width,
> + .reset = sdhci_arasan_reset,
> + .set_uhs_signaling = sdhci_set_uhs_signaling,
> +};
> +
> +static const struct sdhci_pltfm_data sdhci_arasan_am654_pdata = {
> + .ops = &sdhci_arasan_am654_ops,
> + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
> + SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
> + SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
> + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
> + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
> + SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
> +};
> +
> +static const struct sdhci_arasan_of_data sdhci_arasan_am654_data = {
> + .pdata = &sdhci_arasan_am654_pdata,
> +};
> +
> static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
> {
> int cmd_error = 0;
> @@ -478,6 +520,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
> .compatible = "rockchip,rk3399-sdhci-5.1",
> .data = &sdhci_arasan_rk3399_data,
> },
> + {
> + .compatible = "ti,am654-sdhci-5.1",
> + .data = &sdhci_arasan_am654_data,
> + },
> /* Generic compatible below here */
> {
> .compatible = "arasan,sdhci-8.9a",
> --
> 2.18.0
>

2018-10-09 07:33:38

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH 2/6] phy: am654-mmc-phy: Add Support for MMC PHY on AM654 Devices

On 9 October 2018 at 07:18, Kishon Vijay Abraham I <[email protected]> wrote:
> Hi Uffe,
>
> On Monday 08 October 2018 05:02 PM, Ulf Hansson wrote:
>> On 4 October 2018 at 13:14, Faiz Abbas <[email protected]> wrote:
>>> Add driver support for the MMC physical layer present
>>> on TI's AM654 devices.
>>>
>>> Signed-off-by: Faiz Abbas <[email protected]>
>>> Signed-off-by: Sekhar Nori <[email protected]>
>>
>> I assume Kishon would like to pick up this through his tree? If not,
>> please tell and I can do it, with his ack.
>
> yes, I'll pick this in my tree.
>

So I have picked patch3, 4 and 5. The rest I leave for you to pick up then.

Kind regards
Uffe

>>
>> Reviewed-by: Ulf Hansson <[email protected]>
>
> Thanks
> Kishon
>
>>
>> Kind regards
>> Uffe
>>
>>> ---
>>> drivers/phy/ti/Kconfig | 7 +
>>> drivers/phy/ti/Makefile | 1 +
>>> drivers/phy/ti/phy-am654-mmc.c | 291 +++++++++++++++++++++++++++++++++
>>> 3 files changed, 299 insertions(+)
>>> create mode 100644 drivers/phy/ti/phy-am654-mmc.c
>>>
>>> diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig
>>> index 20503562666c..ea5fe4db01c8 100644
>>> --- a/drivers/phy/ti/Kconfig
>>> +++ b/drivers/phy/ti/Kconfig
>>> @@ -76,3 +76,10 @@ config TWL4030_USB
>>> family chips (including the TWL5030 and TPS659x0 devices).
>>> This transceiver supports high and full speed devices plus,
>>> in host mode, low speed.
>>> +
>>> +config PHY_AM654_MMC
>>> + bool "TI AM654 MMC PHY Support"
>>> + select GENERIC_PHY
>>> + help
>>> + This option enables support for the Physical layer for MMC host
>>> + controllers present on TI AM654 SOCs.
>>> diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile
>>> index 9f361756eaf2..5b2db2d164a5 100644
>>> --- a/drivers/phy/ti/Makefile
>>> +++ b/drivers/phy/ti/Makefile
>>> @@ -6,3 +6,4 @@ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
>>> obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
>>> obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
>>> obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
>>> +obj-$(CONFIG_PHY_AM654_MMC) += phy-am654-mmc.o
>>> diff --git a/drivers/phy/ti/phy-am654-mmc.c b/drivers/phy/ti/phy-am654-mmc.c
>>> new file mode 100644
>>> index 000000000000..91255947fb67
>>> --- /dev/null
>>> +++ b/drivers/phy/ti/phy-am654-mmc.c
>>> @@ -0,0 +1,291 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * phy-am654-mmc.c - MMC PHY driver for TI's AM654 SOCs
>>> + *
>>> + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
>>> + *
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/phy/phy.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/printk.h>
>>> +#include <linux/regmap.h>
>>> +
>>> +/* MMC PHY Registers */
>>> +#define PHYCTRL_CTRL1_REG 0x00
>>> +#define PHYCTRL_CTRL2_REG 0x04
>>> +#define PHYCTRL_CTRL3_REG 0x08
>>> +#define PHYCTRL_CTRL4_REG 0x0C
>>> +#define PHYCTRL_CTRL5_REG 0x10
>>> +#define PHYCTRL_CTRL6_REG 0x14
>>> +#define PHYCTRL_STAT1_REG 0x30
>>> +#define PHYCTRL_STAT2_REG 0x34
>>> +
>>> +#define IOMUX_ENABLE_SHIFT 31
>>> +#define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT)
>>> +#define OTAPDLYENA_SHIFT 20
>>> +#define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT)
>>> +#define OTAPDLYSEL_SHIFT 12
>>> +#define OTAPDLYSEL_MASK GENMASK(15, 12)
>>> +#define STRBSEL_SHIFT 24
>>> +#define STRBSEL_MASK GENMASK(27, 24)
>>> +#define SEL50_SHIFT 8
>>> +#define SEL50_MASK BIT(SEL50_SHIFT)
>>> +#define SEL100_SHIFT 9
>>> +#define SEL100_MASK BIT(SEL100_SHIFT)
>>> +#define DLL_TRIM_ICP_SHIFT 4
>>> +#define DLL_TRIM_ICP_MASK GENMASK(7, 4)
>>> +#define DR_TY_SHIFT 20
>>> +#define DR_TY_MASK GENMASK(22, 20)
>>> +#define ENDLL_SHIFT 1
>>> +#define ENDLL_MASK BIT(ENDLL_SHIFT)
>>> +#define DLLRDY_SHIFT 0
>>> +#define DLLRDY_MASK BIT(DLLRDY_SHIFT)
>>> +#define PDB_SHIFT 0
>>> +#define PDB_MASK BIT(PDB_SHIFT)
>>> +#define CALDONE_SHIFT 1
>>> +#define CALDONE_MASK BIT(CALDONE_SHIFT)
>>> +
>>> +#define DRIVER_STRENGTH_50_OHM 0x0
>>> +#define DRIVER_STRENGTH_33_OHM 0x1
>>> +#define DRIVER_STRENGTH_66_OHM 0x2
>>> +#define DRIVER_STRENGTH_100_OHM 0x3
>>> +#define DRIVER_STRENGTH_40_OHM 0x4
>>> +
>>> +static struct regmap_config am654_mmc_phy_regmap_config = {
>>> + .reg_bits = 32,
>>> + .val_bits = 32,
>>> + .reg_stride = 4,
>>> + .fast_io = true,
>>> +};
>>> +
>>> +struct am654_mmc_phy {
>>> + struct regmap *reg_base;
>>> + struct clk *mmcclk;
>>> + int otap_del_sel;
>>> + int trm_icp;
>>> + int drv_strength;
>>> +};
>>> +
>>> +static int am654_mmc_phy_init(struct phy *phy)
>>> +{
>>> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
>>> + int ret;
>>> + u32 val;
>>> +
>>> + /* Reset registers to default value */
>>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL1_REG, 0x10000);
>>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL4_REG, 0x0);
>>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL5_REG, 0x0);
>>> +
>>> + /* Calibrate IO lines */
>>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
>>> + PDB_MASK, PDB_MASK);
>>> + ret = regmap_read_poll_timeout(mmc_phy->reg_base, PHYCTRL_STAT1_REG,
>>> + val, val & CALDONE_MASK, 1, 20);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + /* Enable pins by setting the IO mux to 0 */
>>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
>>> + IOMUX_ENABLE_MASK, 0);
>>> +
>>> + mmc_phy->mmcclk = clk_get(&phy->dev, "mmcclk");
>>> + if (IS_ERR(mmc_phy->mmcclk)) {
>>> + dev_err(&phy->dev, "Error getting mmcclk");
>>> + return PTR_ERR(mmc_phy->mmcclk);
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int am654_mmc_phy_exit(struct phy *phy)
>>> +{
>>> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
>>> +
>>> + clk_put(mmc_phy->mmcclk);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int am654_mmc_phy_power_on(struct phy *phy)
>>> +{
>>> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
>>> + u32 mask, val;
>>> + int sel50, sel100;
>>> + int rate;
>>> +
>>> + /* Setup DLL Output TAP delay */
>>> + mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
>>> + val = (1 << OTAPDLYENA_SHIFT) |
>>> + (mmc_phy->otap_del_sel << OTAPDLYSEL_SHIFT);
>>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL4_REG,
>>> + mask, val);
>>> +
>>> + rate = clk_get_rate(mmc_phy->mmcclk);
>>> + switch (rate) {
>>> + case 200000000:
>>> + sel50 = 0;
>>> + sel100 = 0;
>>> + break;
>>> + case 100000000:
>>> + sel50 = 0;
>>> + sel100 = 1;
>>> + break;
>>> + default:
>>> + sel50 = 1;
>>> + sel100 = 0;
>>> + }
>>> +
>>> + /* Configure PHY DLL frequency */
>>> + mask = SEL50_MASK | SEL100_MASK;
>>> + val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
>>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL5_REG,
>>> + mask, val);
>>> +
>>> + /* Configure DLL TRIM */
>>> + mask = DLL_TRIM_ICP_MASK;
>>> + val = mmc_phy->trm_icp << DLL_TRIM_ICP_SHIFT;
>>> +
>>> + /* Configure DLL driver strength */
>>> + mask |= DR_TY_MASK;
>>> + val |= mmc_phy->drv_strength << DR_TY_SHIFT;
>>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG, mask, val);
>>> +
>>> + /* Enable DLL */
>>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
>>> + ENDLL_MASK, 0x1 << ENDLL_SHIFT);
>>> +
>>> + /*
>>> + * Poll for DLL ready. Use a one second timeout.
>>> + * Works in all experiments done so far
>>> + */
>>> + return regmap_read_poll_timeout(mmc_phy->reg_base, PHYCTRL_STAT1_REG,
>>> + val, val & DLLRDY_MASK, 1000, 1000000);
>>> +
>>> +}
>>> +
>>> +static int am654_mmc_phy_power_off(struct phy *phy)
>>> +{
>>> + struct am654_mmc_phy *mmc_phy = phy_get_drvdata(phy);
>>> +
>>> + /* Disable DLL */
>>> + regmap_update_bits(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
>>> + ENDLL_MASK, 0);
>>> +
>>> + /* Reset registers to default value except PDB */
>>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL1_REG,
>>> + 0x10000 | PDB_MASK);
>>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL4_REG, 0x0);
>>> + regmap_write(mmc_phy->reg_base, PHYCTRL_CTRL5_REG, 0x0);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct phy_ops ops = {
>>> + .init = am654_mmc_phy_init,
>>> + .exit = am654_mmc_phy_exit,
>>> + .power_on = am654_mmc_phy_power_on,
>>> + .power_off = am654_mmc_phy_power_off,
>>> + .owner = THIS_MODULE,
>>> +};
>>> +
>>> +static int am654_mmc_phy_probe(struct platform_device *pdev)
>>> +{
>>> + struct phy_provider *phy_provider;
>>> + struct device *dev = &pdev->dev;
>>> + struct device_node *np = dev->of_node;
>>> + struct am654_mmc_phy *mmc_phy;
>>> + struct phy *generic_phy;
>>> + struct resource *res;
>>> + void __iomem *base;
>>> + struct regmap *map;
>>> + int drv_strength;
>>> + int err;
>>> +
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + base = devm_ioremap_resource(&pdev->dev, res);
>>> + if (IS_ERR(base))
>>> + return PTR_ERR(base);
>>> +
>>> + map = devm_regmap_init_mmio(dev, base, &am654_mmc_phy_regmap_config);
>>> + if (IS_ERR(map)) {
>>> + dev_err(dev, "could not initialize regmap\n");
>>> + return PTR_ERR(map);
>>> + }
>>> +
>>> + mmc_phy = devm_kzalloc(dev, sizeof(struct am654_mmc_phy), GFP_KERNEL);
>>> + if (!mmc_phy)
>>> + return -ENOMEM;
>>> +
>>> + mmc_phy->reg_base = map;
>>> + err = of_property_read_u32(np, "ti,otap-del-sel",
>>> + &mmc_phy->otap_del_sel);
>>> + if (err)
>>> + return err;
>>> +
>>> + err = of_property_read_u32(np, "ti,trm-icp",
>>> + &mmc_phy->trm_icp);
>>> + if (err)
>>> + return err;
>>> +
>>> + err = of_property_read_u32(np, "ti,driver-strength-ohm", &drv_strength);
>>> + if (err)
>>> + return err;
>>> +
>>> + switch (drv_strength) {
>>> + case 50:
>>> + mmc_phy->drv_strength = DRIVER_STRENGTH_50_OHM;
>>> + break;
>>> + case 33:
>>> + mmc_phy->drv_strength = DRIVER_STRENGTH_33_OHM;
>>> + break;
>>> + case 66:
>>> + mmc_phy->drv_strength = DRIVER_STRENGTH_66_OHM;
>>> + break;
>>> + case 100:
>>> + mmc_phy->drv_strength = DRIVER_STRENGTH_100_OHM;
>>> + break;
>>> + case 40:
>>> + mmc_phy->drv_strength = DRIVER_STRENGTH_40_OHM;
>>> + break;
>>> + default:
>>> + dev_err(dev, "Invalid driver strength\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + generic_phy = devm_phy_create(dev, dev->of_node, &ops);
>>> + if (IS_ERR(generic_phy)) {
>>> + dev_err(dev, "failed to create PHY\n");
>>> + return PTR_ERR(generic_phy);
>>> + }
>>> +
>>> + phy_set_drvdata(generic_phy, mmc_phy);
>>> + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
>>> +
>>> + return PTR_ERR_OR_ZERO(phy_provider);
>>> +}
>>> +
>>> +static const struct of_device_id am654_mmc_phy_dt_ids[] = {
>>> + { .compatible = "ti,am654-mmc-phy" },
>>> + {}
>>> +};
>>> +
>>> +MODULE_DEVICE_TABLE(of, am654_mmc_phy_dt_ids);
>>> +
>>> +static struct platform_driver am654_mmc_phy_driver = {
>>> + .probe = am654_mmc_phy_probe,
>>> + .driver = {
>>> + .name = "am654-mmc-phy",
>>> + .of_match_table = am654_mmc_phy_dt_ids,
>>> + },
>>> +};
>>> +
>>> +module_platform_driver(am654_mmc_phy_driver);
>>> +
>>> +MODULE_AUTHOR("Faiz Abbas <[email protected]>");
>>> +MODULE_DESCRIPTION("TI AM654 MMC PHY driver");
>>> +MODULE_LICENSE("GPL v2");
>>> --
>>> 2.18.0
>>>

2018-10-09 07:33:57

by Faiz Abbas

[permalink] [raw]
Subject: Re: [PATCH 5/6] mmc: sdhci-of-arasan: Add Support for AM654 MMC and PHY

Hi Uffe,

On Tuesday 09 October 2018 01:00 PM, Ulf Hansson wrote:
> On 4 October 2018 at 13:14, Faiz Abbas <[email protected]> wrote:
>> The current arasan sdhci PHY configuration isn't compatible
>> with the PHY on TI's AM654 devices.
>>
>> Therefore, add a new compatible, AM654 specific quirks
>> and a new AM654 specific set_clock function which
>> configures the PHY in a sane way.
>>
>> Signed-off-by: Faiz Abbas <[email protected]>
>> Signed-off-by: Sekhar Nori <[email protected]>
>
> Applied for next, thanks!
>

Do I need to repost these in the v2 ?

Thanks,
Faiz

2018-10-09 07:37:39

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH 5/6] mmc: sdhci-of-arasan: Add Support for AM654 MMC and PHY

On 9 October 2018 at 09:35, Faiz Abbas <[email protected]> wrote:
> Hi Uffe,
>
> On Tuesday 09 October 2018 01:00 PM, Ulf Hansson wrote:
>> On 4 October 2018 at 13:14, Faiz Abbas <[email protected]> wrote:
>>> The current arasan sdhci PHY configuration isn't compatible
>>> with the PHY on TI's AM654 devices.
>>>
>>> Therefore, add a new compatible, AM654 specific quirks
>>> and a new AM654 specific set_clock function which
>>> configures the PHY in a sane way.
>>>
>>> Signed-off-by: Faiz Abbas <[email protected]>
>>> Signed-off-by: Sekhar Nori <[email protected]>
>>
>> Applied for next, thanks!
>>
>
> Do I need to repost these in the v2 ?

No. It's okay if you drop them from the series.

Kind regards
Uffe