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
