[PATCH 1/4] phy: Add Axiado eMMC PHY driver

Siu Ming Tong <[email protected]>
Newsgroups org.u-boot-project.lists.u-boot
Message-ID <[email protected]>
Add a PHY driver (PHY_AXIADO_EMMC) for the eMMC PHY found on the Axiado
AX3005 SoC. The PHY provides signal conditioning and DLL functionality
required for high-speed eMMC operation with data rates beyond 52MHz.

The driver is based on the Arasan PHY (sdhci-pci-arasan.c) and programs
the calibration, DLL and clock-buffer settings through a memory-mapped
regmap.

Signed-off-by: Siu Ming Tong <[email protected]>
---
 MAINTAINERS                   |   1 +
 drivers/phy/Kconfig           |   9 ++
 drivers/phy/Makefile          |   1 +
 drivers/phy/phy-axiado-emmc.c | 208 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 219 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index e53cb0a485c..eece4e5ebd0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -232,6 +232,7 @@ F:	arch/arm/dts/ax3005*
 F:	arch/arm/mach-axiado/
 F:	board/axiado/scm3005/
 F:	configs/ax3005_scm3005_defconfig
+F:	drivers/phy/phy-axiado-emmc.c
 F:	include/configs/ax3005-scm3005.h
 
 ARM BROADCOM BCM283X / BCM27XX
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 89d84df96ae..16eaa67fd02 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -81,6 +81,15 @@ config APPLE_ATCPHY
 	  This is a dummy driver since the PHY is initialized
 	  sufficiently by previous stage firmware.
 
+config PHY_AXIADO_EMMC
+	bool "Axiado eMMC PHY driver support"
+	depends on PHY
+	imply REGMAP
+	help
+	  Enable this to support the Axiado eMMC PHY found in the Ax3005 SoC.
+	  This PHY provides signal conditioning and DLL functionality for
+	  high-speed eMMC operations with data rates beyond 52MHz.
+
 config BCM6318_USBH_PHY
 	bool "BCM6318 USBH PHY support"
 	depends on PHY && ARCH_BMIPS
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index e46c362878d..7ad0bd59407 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o
 obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o
 obj-$(CONFIG_MT7620_USB_PHY) += mt7620-usb-phy.o
 obj-$(CONFIG_MT76X8_USB_PHY) += mt76x8-usb-phy.o
+obj-$(CONFIG_PHY_AXIADO_EMMC) += phy-axiado-emmc.o
 obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o
 obj-$(CONFIG_PHY_EXYNOS_USBDRD) += phy-exynos-usbdrd.o
 obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
diff --git a/drivers/phy/phy-axiado-emmc.c b/drivers/phy/phy-axiado-emmc.c
new file mode 100644
index 00000000000..a151557a0ec
--- /dev/null
+++ b/drivers/phy/phy-axiado-emmc.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Axiado eMMC PHY driver
+ *
+ * Copyright (C) 2017 Arasan Chip Systems Inc.
+ * Copyright (C) 2022-2026 Axiado Corporation (or its affiliates).
+ *
+ * Based on Arasan Driver (sdhci-pci-arasan.c)
+ * sdhci-pci-arasan.c - Driver for Arasan PCI Controller with integrated phy.
+ */
+
+#include <dm.h>
+#include <generic-phy.h>
+#include <regmap.h>
+#include <time.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+
+/* Axiado eMMC PHY configuration registers */
+#define CAP_REG_IN_S1_LSB 0x00
+#define CAP_REG_IN_S1_MSB 0x04
+#define PHY_CTRL_1 0x38
+#define PHY_CTRL_2 0x3C
+#define PHY_CTRL_3 0x40
+#define STATUS 0x50
+
+/* PHY Control 1 Register bits */
+#define DLL_ENBL BIT(26)
+#define RTRIM_EN BIT(21)
+#define PDB_ENBL BIT(23)
+#define RETB_ENBL BIT(1)
+
+#define REN_STRB BIT(27)
+#define REN_CMD_EN GENMASK(20, 12)
+
+/* Pull-UP Enable on CMD Line */
+#define PU_CMD_EN GENMASK(11, 3)
+
+/* PHY Control 2 Register bits */
+#define OTAPDLY_EN BIT(11)
+
+/* PHY Control 3 Register bits */
+#define SEL_DLY_RXCLK BIT(18)
+#define SEL_DLY_TXCLK BIT(19)
+
+/* Status Register bits */
+#define CALDONE_MASK 0x40
+#define DLL_RDY_MASK 0x1
+
+/* Clock buffer bits */
+#define MAX_CLK_BUF0 BIT(20)
+#define MAX_CLK_BUF1 BIT(21)
+#define MAX_CLK_BUF2 BIT(22)
+
+/* Other constants */
+#define CLK_MULTIPLIER 0xC008E
+#define LOOP_TIMEOUT 3000
+#define TIMEOUT_DELAY 100
+
+struct axiado_emmc_phy {
+	struct regmap *regmap;
+};
+
+static int axiado_emmc_phy_init(struct phy *phy)
+{
+	u32 val;
+	unsigned long timeout;
+	struct axiado_emmc_phy *ax_phy = dev_get_priv(phy->dev);
+
+	regmap_read(ax_phy->regmap, PHY_CTRL_1, &val);
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_1, RETB_ENBL | RTRIM_EN,
+			   RETB_ENBL | RTRIM_EN);
+
+	regmap_read(ax_phy->regmap, PHY_CTRL_3, &val);
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_3, PDB_ENBL, PDB_ENBL);
+
+	/* Wait max 3000 ms */
+	timeout = get_timer(0);
+
+	while (1) {
+		regmap_read(ax_phy->regmap, STATUS, &val);
+		if (val & CALDONE_MASK)
+			break;
+
+		if (get_timer(timeout) > LOOP_TIMEOUT) {
+			dev_err(phy->dev,
+				"eMMC-PHY: CALDONE_MASK bit not cleared.\n");
+			return -ETIMEDOUT;
+		}
+		udelay(TIMEOUT_DELAY);
+	}
+
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_1, REN_CMD_EN | PU_CMD_EN,
+			   REN_CMD_EN | PU_CMD_EN);
+
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_2, REN_STRB, REN_STRB);
+
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_3,
+			   MAX_CLK_BUF0 | MAX_CLK_BUF1 | MAX_CLK_BUF2,
+			   MAX_CLK_BUF0 | MAX_CLK_BUF1 | MAX_CLK_BUF2);
+
+	regmap_write(ax_phy->regmap, CAP_REG_IN_S1_MSB, CLK_MULTIPLIER);
+
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_3,
+			   SEL_DLY_RXCLK | SEL_DLY_TXCLK,
+			   SEL_DLY_RXCLK | SEL_DLY_TXCLK);
+
+	return 0;
+}
+
+static int axiado_emmc_phy_power_on(struct phy *phy)
+{
+	struct axiado_emmc_phy *ax_phy = dev_get_priv(phy->dev);
+	u32 val;
+	unsigned long timeout;
+
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_1, RETB_ENBL, RETB_ENBL);
+
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_3, PDB_ENBL, PDB_ENBL);
+
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_2, OTAPDLY_EN | (0xF << 7),
+			   OTAPDLY_EN | ((0x2) << 7));
+
+	/* Read back to ensure write is completed */
+	regmap_read(ax_phy->regmap, PHY_CTRL_2, &val);
+
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_1, DLL_ENBL | (0xF << 22),
+			   DLL_ENBL | ((0x8) << 22));
+
+	regmap_write(ax_phy->regmap, STATUS, 0x00);
+
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_3, (0x1 << 25),
+			   (0x1 << 25));
+
+	/* Wait max 3000 ms */
+	timeout = get_timer(0);
+
+	while (1) {
+		regmap_read(ax_phy->regmap, STATUS, &val);
+		if (val & DLL_RDY_MASK)
+			break;
+
+		if (get_timer(timeout) > LOOP_TIMEOUT) {
+			dev_err(phy->dev,
+				"eMMC-PHY: DLL_RDY_MASK bit not cleared.\n");
+			return -ETIMEDOUT;
+		}
+		udelay(TIMEOUT_DELAY);
+	}
+	return 0;
+}
+
+static int axiado_emmc_phy_power_off(struct phy *phy)
+{
+	struct axiado_emmc_phy *ax_phy = dev_get_priv(phy->dev);
+
+	/* Disable DLL */
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_1, DLL_ENBL, 0);
+
+	return 0;
+}
+
+static int axiado_emmc_phy_exit(struct phy *phy)
+{
+	struct axiado_emmc_phy *ax_phy = dev_get_priv(phy->dev);
+
+	/* Disable all PHY features */
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_1,
+			   DLL_ENBL | RETB_ENBL | RTRIM_EN, 0);
+	regmap_update_bits(ax_phy->regmap, PHY_CTRL_3, PDB_ENBL, 0);
+
+	return 0;
+}
+
+static const struct phy_ops axiado_emmc_phy_ops = {
+	.init = axiado_emmc_phy_init,
+	.power_on = axiado_emmc_phy_power_on,
+	.power_off = axiado_emmc_phy_power_off,
+	.exit = axiado_emmc_phy_exit,
+};
+
+static int axiado_emmc_phy_probe(struct udevice *dev)
+{
+	struct axiado_emmc_phy *ax_phy = dev_get_priv(dev);
+	int ret;
+
+	ret = regmap_init_mem(dev_ofnode(dev), &ax_phy->regmap);
+	if (ret) {
+		dev_err(dev, "Failed to init regmap: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct udevice_id axiado_emmc_phy_ids[] = {
+	{ .compatible = "axiado,ax3005-emmc-phy" },
+	{}
+};
+
+U_BOOT_DRIVER(axiado_emmc_phy) = {
+	.name = "axiado_emmc_phy",
+	.id = UCLASS_PHY,
+	.of_match = axiado_emmc_phy_ids,
+	.ops = &axiado_emmc_phy_ops,
+	.probe = axiado_emmc_phy_probe,
+	.priv_auto = sizeof(struct axiado_emmc_phy),
+};

-- 
2.34.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.