* Re: [PATCH 2/8] phy: axiado: add Arasan eMMC-PHY for Axiado
@ 2025-12-23 13:22 kernel test robot
0 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2025-12-23 13:22 UTC (permalink / raw)
To: oe-kbuild; +Cc: lkp
::::::
:::::: Manual check reason: "low confidence static check warning: drivers/phy/axiado/phy-axiado-emmc.c:258:1: sparse: sparse: bad integer constant expression"
::::::
BCC: lkp@intel.com
CC: oe-kbuild-all@lists.linux.dev
In-Reply-To: <20251222-axiado-ax3000-add-emmc-host-driver-support-v1-2-5457d0ebcdb4@axiado.com>
References: <20251222-axiado-ax3000-add-emmc-host-driver-support-v1-2-5457d0ebcdb4@axiado.com>
TO: "Tzu-Hao Wei" <twei@axiado.com>
TO: SriNavmani A <srinavmani@axiado.com>
TO: Prasad Bolisetty <pbolisetty@axiado.com>
TO: Vinod Koul <vkoul@kernel.org>
TO: Neil Armstrong <neil.armstrong@linaro.org>
TO: Rob Herring <robh@kernel.org>
TO: Krzysztof Kozlowski <krzk@kernel.org>
TO: Conor Dooley <conor+dt@kernel.org>
TO: Harshit Shah <hshah@axiado.com>
TO: Ulf Hansson <ulf.hansson@linaro.org>
TO: Adrian Hunter <adrian.hunter@intel.com>
TO: Michal Simek <monstr@monstr.eu>
CC: linux-phy@lists.infradead.org
CC: devicetree@vger.kernel.org
CC: linux-arm-kernel@lists.infradead.org
CC: linux-kernel@vger.kernel.org
CC: linux-mmc@vger.kernel.org
CC: "Tzu-Hao Wei" <twei@axiado.com>
Hi Tzu-Hao,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 9448598b22c50c8a5bb77a9103e2d49f134c9578]
url: https://github.com/intel-lab-lkp/linux/commits/Tzu-Hao-Wei/dt-bindings-phy-axiado-ax3000-emmc-phy-add-Axiado-eMMC-PHY-document/20251222-165544
base: 9448598b22c50c8a5bb77a9103e2d49f134c9578
patch link: https://lore.kernel.org/r/20251222-axiado-ax3000-add-emmc-host-driver-support-v1-2-5457d0ebcdb4%40axiado.com
patch subject: [PATCH 2/8] phy: axiado: add Arasan eMMC-PHY for Axiado
:::::: branch date: 28 hours ago
:::::: commit date: 28 hours ago
config: loongarch-randconfig-r133-20251223 (https://download.01.org/0day-ci/archive/20251223/202512232108.SCc2Ad7g-lkp@intel.com/config)
compiler: clang version 19.1.7 (https://github.com/llvm/llvm-project cd708029e0b2869e80abe31ddb175f7c35361f90)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251223/202512232108.SCc2Ad7g-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/r/202512232108.SCc2Ad7g-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> drivers/phy/axiado/phy-axiado-emmc.c:258:1: sparse: sparse: bad integer constant expression
drivers/phy/axiado/phy-axiado-emmc.c:258:1: sparse: sparse: static assertion failed: "MODULE_INFO(description, ...) contains embedded NUL byte"
drivers/phy/axiado/phy-axiado-emmc.c:259:1: sparse: sparse: bad integer constant expression
drivers/phy/axiado/phy-axiado-emmc.c:259:1: sparse: sparse: static assertion failed: "MODULE_INFO(author, ...) contains embedded NUL byte"
drivers/phy/axiado/phy-axiado-emmc.c:260:1: sparse: sparse: bad integer constant expression
drivers/phy/axiado/phy-axiado-emmc.c:260:1: sparse: sparse: static assertion failed: "MODULE_INFO(file, ...) contains embedded NUL byte"
drivers/phy/axiado/phy-axiado-emmc.c:260:1: sparse: sparse: bad integer constant expression
drivers/phy/axiado/phy-axiado-emmc.c:260:1: sparse: sparse: static assertion failed: "MODULE_INFO(license, ...) contains embedded NUL byte"
vim +258 drivers/phy/axiado/phy-axiado-emmc.c
c69e10e2d9b006a SriNavmani A 2025-12-22 257
c69e10e2d9b006a SriNavmani A 2025-12-22 @258 MODULE_DESCRIPTION("AX3000 eMMC PHY Driver");
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 0/8] Add Axiado AX3000 eMMC Host Controller Support
@ 2025-12-22 8:44 Tzu-Hao Wei
2025-12-22 8:45 ` Tzu-Hao Wei
0 siblings, 1 reply; 7+ messages in thread
From: Tzu-Hao Wei @ 2025-12-22 8:44 UTC (permalink / raw)
To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah,
Ulf Hansson, Adrian Hunter, Michal Simek
Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc,
Tzu-Hao Wei
This patch series adds support for the eMMC host controller found on
the Axiado AX3000 SoC. The implementation includes support for an
external Arasan eMMC PHY that enables HS200 mode operation at 200 MHz,
significantly improving performance over the default 25 MHz operation.
Hardware Details:
Arasan eMMC host controller on AX3000 SoC
External Arasan eMMC PHY (different from internal PHY supported
by the standard sdhci-of-arasan driver)
Support for HS200 mode (200 MHz) operation
Integration with existing SDHCI framework
Implementation Overview:
The series adds support for the external eMMC PHY required for HS200
mode. The existing sdhci-of-arasan.c driver supports internal PHY
configurations, but the AX3000 uses an external PHY that requires
different configuration. The PHY driver was implemented based on the
reference code in sdhci-pci-arasan.c as recommended by the Arasan team.
This series includes:
1. Device tree bindings for the eMMC PHY
2. PHY driver implementation for the external Arasan eMMC PHY
3. Integration of PHY support into the sdhci-of-arasan host driver
4. Device tree bindings for the eMMC host controller variant
5. Device tree source files for the SoC and EVK board
6. MAINTAINERS file update
Patch series structure:
[1/8] dt-bindings: phy: axiado,ax3000-emmc-phy: add Axiado eMMC PHY document
[2/8] phy: axiado: add Arasan eMMC-PHY for Axiado
[3/8] arm64: dts: axiado: Add eMMC-PHY node support
[4/8] MAINTAINERS: Update entry for Axiado eMMC PHY
[5/8] dt-bindings: mmc: axiado: Add axiado eMMC variant
[6/8] mmc: host: axiado: add AX3000 eMMC PHY support to sdhci-of-arasan
[7/8] arm64: dts: axiado: add common sdhci host node in dtsi
[8/8] arm64: dts: axiado: enable sdhci host
The driver follows the standard Linux PHY framework and integrates
seamlessly with the existing SDHCI subsystem. The implementation
maintains compatibility with the standard Arasan host controller
driver while adding the necessary support for the external PHY.
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
SriNavmani A (8):
dt-bindings: phy: axiado,ax3000-emmc-phy: add Axiado eMMC PHY document
phy: axiado: add Arasan eMMC-PHY for Axiado
arm64: dts: axiado: Add eMMC-PHY node support
MAINTAINERS: Update entry for Axiado eMMC PHY
dt-bindings: mmc: axiado: Add axiado eMMC variant
mmc: host: axiado: add AX3000 eMMC PHY support to sdhci-of-arasan
arm64: dts: axiado: add common sdhci host node in dtsi
arm64: dts: axiado: enable sdhci host
.../devicetree/bindings/mmc/arasan,sdhci.yaml | 18 ++
.../bindings/phy/axiado,ax3000-emmc-phy.yaml | 46 ++++
MAINTAINERS | 10 +
arch/arm64/boot/dts/axiado/ax3000-evk.dts | 8 +
arch/arm64/boot/dts/axiado/ax3000.dtsi | 20 ++
drivers/mmc/host/sdhci-of-arasan.c | 20 +-
drivers/phy/Kconfig | 1 +
drivers/phy/Makefile | 1 +
drivers/phy/axiado/Kconfig | 15 ++
drivers/phy/axiado/Makefile | 1 +
drivers/phy/axiado/phy-axiado-emmc.c | 260 +++++++++++++++++++++
11 files changed, 393 insertions(+), 7 deletions(-)
---
base-commit: 9448598b22c50c8a5bb77a9103e2d49f134c9578
change-id: 20251222-axiado-ax3000-add-emmc-host-driver-support-2cc84a8f889a
Best regards,
--
Tzu-Hao Wei <twei@axiado.com>
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 2/8] phy: axiado: add Arasan eMMC-PHY for Axiado 2025-12-22 8:44 [PATCH 0/8] Add Axiado AX3000 eMMC Host Controller Support Tzu-Hao Wei @ 2025-12-22 8:45 ` Tzu-Hao Wei 0 siblings, 0 replies; 7+ messages in thread From: Tzu-Hao Wei @ 2025-12-22 8:45 UTC (permalink / raw) To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah, Ulf Hansson, Adrian Hunter, Michal Simek Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc, Tzu-Hao Wei From: SriNavmani A <srinavmani@axiado.com> Add support for the Arasan eMMC PHY found on the AX3000 SoC. This external PHY is required to achieve HS200 mode operation (200 MHz) on the eMMC interface. The existing sdhci-of-arasan.c driver supports internal PHY configurations, but the AX3000 uses an external PHY that requires different configuration. This driver was written based on the reference implementation in sdhci-pci-arasan.c as suggested by the Arasan team to provide the necessary PHY configuration for HS200 high-speed mode support. Signed-off-by: SriNavmani A <srinavmani@axiado.com> Signed-off-by: Tzu-Hao Wei <twei@axiado.com> --- drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 1 + drivers/phy/axiado/Kconfig | 15 ++ drivers/phy/axiado/Makefile | 1 + drivers/phy/axiado/phy-axiado-emmc.c | 260 +++++++++++++++++++++++++++++++++++ 5 files changed, 278 insertions(+) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 678dd0452f0aa0597773433f04d2a9ba77474d2a..b802274ea45a84bd36d7c0b7fb90e368a5c018b4 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -103,6 +103,7 @@ config PHY_NXP_PTN3222 source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" +source "drivers/phy/axiado/Kconfig" source "drivers/phy/broadcom/Kconfig" source "drivers/phy/cadence/Kconfig" source "drivers/phy/freescale/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index bfb27fb5a494283d7fd05dd670ebd1b12df8b1a1..f1b9e4a8673bcde3fdc0fdc06a3deddb5785ced1 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o obj-y += allwinner/ \ amlogic/ \ + axiado/ \ broadcom/ \ cadence/ \ freescale/ \ diff --git a/drivers/phy/axiado/Kconfig b/drivers/phy/axiado/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..824114e6068da327308321b9884552ad33db9efc --- /dev/null +++ b/drivers/phy/axiado/Kconfig @@ -0,0 +1,15 @@ +# +# PHY drivers for Axiado platforms +# + +config PHY_AX3000_EMMC + tristate "Axiado eMMC PHY driver" + select GENERIC_PHY + help + This enables support for the eMMC PHY block found on the + Axiado AX3000 SoCs. The PHY provides the physical layer + interface used by the Arasan SDHCI host controller for emmc + signaling and timing adjustment. + + If you are building a kernel for AX3000 platform with + eMMC storage, say Y or N. diff --git a/drivers/phy/axiado/Makefile b/drivers/phy/axiado/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..1e2b1ba016092eaffdbd7acbd9cdc8577d79b35c --- /dev/null +++ b/drivers/phy/axiado/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PHY_AX3000_EMMC) += phy-axiado-emmc.o diff --git a/drivers/phy/axiado/phy-axiado-emmc.c b/drivers/phy/axiado/phy-axiado-emmc.c new file mode 100644 index 0000000000000000000000000000000000000000..a61a458c9a65915cd576b431bc6a0cf7e8b18add --- /dev/null +++ b/drivers/phy/axiado/phy-axiado-emmc.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Axiado eMMC PHY driver + * + * Copyright (C) 2022-2025 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. + * + * Copyright (C) 2017 Arasan Chip Systems Inc. + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +/* Arasan eMMC 5.1 - 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 + +#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 BIT(12) +#define REN_DAT0 BIT(13) +#define REN_DAT1 BIT(14) +#define REN_DAT2 BIT(15) +#define REN_DAT3 BIT(16) +#define REN_DAT4 BIT(17) +#define REN_DAT5 BIT(18) +#define REN_DAT6 BIT(19) +#define REN_DAT7 BIT(20) +#define REN_CMD_EN (REN_CMD | REN_DAT0 | REN_DAT1 | REN_DAT2 | \ + REN_DAT3 | REN_DAT4 | REN_DAT5 | REN_DAT6 | REN_DAT7) + +/* Pull-UP Enable on CMD Line */ +#define PU_CMD BIT(3) +#define PU_DAT0 BIT(4) +#define PU_DAT1 BIT(5) +#define PU_DAT2 BIT(6) +#define PU_DAT3 BIT(7) +#define PU_DAT4 BIT(8) +#define PU_DAT5 BIT(9) +#define PU_DAT6 BIT(10) +#define PU_DAT7 BIT(11) +#define PU_CMD_EN (PU_CMD | PU_DAT0 | PU_DAT1 | PU_DAT2 | PU_DAT3 | \ + PU_DAT4 | PU_DAT5 | PU_DAT6 | PU_DAT7) + +/* Slection value for the optimum delay from 1-32 output tap lines */ +#define OTAP_DLY 0x02 +/* DLL charge pump current trim default [1000] */ +#define DLL_TRM_ICP 0x08 +/* Select the frequency range of DLL Operation */ +#define FRQ_SEL 0x01 + +#define OTAP_SEL(x) (((x) << 7) | OTAPDLY_EN) +#define DLL_TRM(x) (((x) << 22) | DLL_ENBL) +#define DLL_FRQSEL(x) ((x) << 25) + +#define OTAPDLY_EN BIT(11) + +#define SEL_DLY_RXCLK BIT(18) +#define SEL_DLY_TXCLK BIT(19) + +#define CALDONE_MASK 0x40 +#define DLL_RDY_MASK 0x1 +#define MAX_CLK_BUF0 BIT(20) +#define MAX_CLK_BUF1 BIT(21) +#define MAX_CLK_BUF2 BIT(22) + +#define CLK_MULTIPLIER 0xC008E +#define LOOP_TIMEOUT 3000 +#define TIMEOUT_DELAY 100 + +struct axiado_emmc_phy { + void __iomem *reg_base; +}; + +static void arasan_emmc_phy_write(struct axiado_emmc_phy *ax_phy, u32 offset, u32 data) +{ + writel(data, ax_phy->reg_base + offset); +} + +static int arasan_emmc_phy_read(struct axiado_emmc_phy *ax_phy, u32 offset) +{ + u32 val = readl(ax_phy->reg_base + offset); + + return val; +} + +static int axiado_emmc_phy_init(struct phy *phy) +{ + u32 val; + ktime_t timeout; + + struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | RETB_ENBL | RTRIM_EN); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | PDB_ENBL); + + /* Wait max 3000 ms */ + timeout = ktime_add_ms(ktime_get(), LOOP_TIMEOUT); + + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + if (arasan_emmc_phy_read(ax_phy, STATUS) & CALDONE_MASK) + break; + + if (timedout) { + dev_err(&phy->dev, "CALDONE_MASK bit is not cleared."); + return -ETIMEDOUT; + } + udelay(TIMEOUT_DELAY); + } + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); + + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | REN_CMD_EN | PU_CMD_EN); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_2, val | REN_STRB); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | MAX_CLK_BUF0 | + MAX_CLK_BUF1 | MAX_CLK_BUF2); + + val = arasan_emmc_phy_read(ax_phy, CAP_REG_IN_S1_MSB); + arasan_emmc_phy_write(ax_phy, CAP_REG_IN_S1_MSB, CLK_MULTIPLIER); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | SEL_DLY_RXCLK | + SEL_DLY_TXCLK); + + return 0; +} + +static int axiado_emmc_phy_power_on(struct phy *phy) +{ + struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy); + + u32 val; + ktime_t timeout; + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | RETB_ENBL); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | PDB_ENBL); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_2, val | OTAP_SEL(OTAP_DLY)); + + arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | DLL_TRM(DLL_TRM_ICP)); + + arasan_emmc_phy_write(ax_phy, STATUS, 0x00); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | DLL_FRQSEL(FRQ_SEL)); + + /* Wait max 3000 ms */ + timeout = ktime_add_ms(ktime_get(), LOOP_TIMEOUT); + + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + if (arasan_emmc_phy_read(ax_phy, STATUS) & DLL_RDY_MASK) + break; + + if (timedout) { + dev_err(&phy->dev, "DLL_RDY_MASK bit is not cleared."); + return -ETIMEDOUT; + } + udelay(TIMEOUT_DELAY); + } + return 0; +} + +static const struct phy_ops axiado_emmc_phy_ops = { + .init = axiado_emmc_phy_init, + .power_on = axiado_emmc_phy_power_on, + .owner = THIS_MODULE, +}; + +static const struct of_device_id axiado_emmc_phy_of_match[] = { + { .compatible = "axiado,ax3000-emmc-phy"}, + {} +}; +MODULE_DEVICE_TABLE(of, axiado_emmc_phy_of_match); + +static int axiado_emmc_phy_probe(struct platform_device *pdev) +{ + struct axiado_emmc_phy *ax_phy; + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + const struct of_device_id *id; + struct phy *generic_phy; + struct resource *res; + + if (!dev->of_node) + return -ENODEV; + + ax_phy = devm_kzalloc(dev, sizeof(*ax_phy), GFP_KERNEL); + if (!ax_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + ax_phy->reg_base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(ax_phy->reg_base)) + return PTR_ERR(ax_phy->reg_base); + + id = of_match_node(axiado_emmc_phy_of_match, pdev->dev.of_node); + if (!id) { + dev_err(dev, "failed to get match_node\n"); + return -EINVAL; + } + + generic_phy = devm_phy_create(dev, dev->of_node, &axiado_emmc_phy_ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, ax_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver axiado_emmc_phy_driver = { + .probe = axiado_emmc_phy_probe, + .driver = { + .name = "axiado-emmc-phy", + .of_match_table = axiado_emmc_phy_of_match, + }, +}; +module_platform_driver(axiado_emmc_phy_driver); + +MODULE_DESCRIPTION("AX3000 eMMC PHY Driver"); +MODULE_AUTHOR("Axiado Corporation"); +MODULE_LICENSE("GPL"); -- 2.48.1 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/8] phy: axiado: add Arasan eMMC-PHY for Axiado @ 2025-12-22 8:45 ` Tzu-Hao Wei 0 siblings, 0 replies; 7+ messages in thread From: Tzu-Hao Wei @ 2025-12-22 8:45 UTC (permalink / raw) To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah, Ulf Hansson, Adrian Hunter, Michal Simek Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc, Tzu-Hao Wei From: SriNavmani A <srinavmani@axiado.com> Add support for the Arasan eMMC PHY found on the AX3000 SoC. This external PHY is required to achieve HS200 mode operation (200 MHz) on the eMMC interface. The existing sdhci-of-arasan.c driver supports internal PHY configurations, but the AX3000 uses an external PHY that requires different configuration. This driver was written based on the reference implementation in sdhci-pci-arasan.c as suggested by the Arasan team to provide the necessary PHY configuration for HS200 high-speed mode support. Signed-off-by: SriNavmani A <srinavmani@axiado.com> Signed-off-by: Tzu-Hao Wei <twei@axiado.com> --- drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 1 + drivers/phy/axiado/Kconfig | 15 ++ drivers/phy/axiado/Makefile | 1 + drivers/phy/axiado/phy-axiado-emmc.c | 260 +++++++++++++++++++++++++++++++++++ 5 files changed, 278 insertions(+) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 678dd0452f0aa0597773433f04d2a9ba77474d2a..b802274ea45a84bd36d7c0b7fb90e368a5c018b4 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -103,6 +103,7 @@ config PHY_NXP_PTN3222 source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" +source "drivers/phy/axiado/Kconfig" source "drivers/phy/broadcom/Kconfig" source "drivers/phy/cadence/Kconfig" source "drivers/phy/freescale/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index bfb27fb5a494283d7fd05dd670ebd1b12df8b1a1..f1b9e4a8673bcde3fdc0fdc06a3deddb5785ced1 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o obj-y += allwinner/ \ amlogic/ \ + axiado/ \ broadcom/ \ cadence/ \ freescale/ \ diff --git a/drivers/phy/axiado/Kconfig b/drivers/phy/axiado/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..824114e6068da327308321b9884552ad33db9efc --- /dev/null +++ b/drivers/phy/axiado/Kconfig @@ -0,0 +1,15 @@ +# +# PHY drivers for Axiado platforms +# + +config PHY_AX3000_EMMC + tristate "Axiado eMMC PHY driver" + select GENERIC_PHY + help + This enables support for the eMMC PHY block found on the + Axiado AX3000 SoCs. The PHY provides the physical layer + interface used by the Arasan SDHCI host controller for emmc + signaling and timing adjustment. + + If you are building a kernel for AX3000 platform with + eMMC storage, say Y or N. diff --git a/drivers/phy/axiado/Makefile b/drivers/phy/axiado/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..1e2b1ba016092eaffdbd7acbd9cdc8577d79b35c --- /dev/null +++ b/drivers/phy/axiado/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PHY_AX3000_EMMC) += phy-axiado-emmc.o diff --git a/drivers/phy/axiado/phy-axiado-emmc.c b/drivers/phy/axiado/phy-axiado-emmc.c new file mode 100644 index 0000000000000000000000000000000000000000..a61a458c9a65915cd576b431bc6a0cf7e8b18add --- /dev/null +++ b/drivers/phy/axiado/phy-axiado-emmc.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Axiado eMMC PHY driver + * + * Copyright (C) 2022-2025 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. + * + * Copyright (C) 2017 Arasan Chip Systems Inc. + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +/* Arasan eMMC 5.1 - 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 + +#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 BIT(12) +#define REN_DAT0 BIT(13) +#define REN_DAT1 BIT(14) +#define REN_DAT2 BIT(15) +#define REN_DAT3 BIT(16) +#define REN_DAT4 BIT(17) +#define REN_DAT5 BIT(18) +#define REN_DAT6 BIT(19) +#define REN_DAT7 BIT(20) +#define REN_CMD_EN (REN_CMD | REN_DAT0 | REN_DAT1 | REN_DAT2 | \ + REN_DAT3 | REN_DAT4 | REN_DAT5 | REN_DAT6 | REN_DAT7) + +/* Pull-UP Enable on CMD Line */ +#define PU_CMD BIT(3) +#define PU_DAT0 BIT(4) +#define PU_DAT1 BIT(5) +#define PU_DAT2 BIT(6) +#define PU_DAT3 BIT(7) +#define PU_DAT4 BIT(8) +#define PU_DAT5 BIT(9) +#define PU_DAT6 BIT(10) +#define PU_DAT7 BIT(11) +#define PU_CMD_EN (PU_CMD | PU_DAT0 | PU_DAT1 | PU_DAT2 | PU_DAT3 | \ + PU_DAT4 | PU_DAT5 | PU_DAT6 | PU_DAT7) + +/* Slection value for the optimum delay from 1-32 output tap lines */ +#define OTAP_DLY 0x02 +/* DLL charge pump current trim default [1000] */ +#define DLL_TRM_ICP 0x08 +/* Select the frequency range of DLL Operation */ +#define FRQ_SEL 0x01 + +#define OTAP_SEL(x) (((x) << 7) | OTAPDLY_EN) +#define DLL_TRM(x) (((x) << 22) | DLL_ENBL) +#define DLL_FRQSEL(x) ((x) << 25) + +#define OTAPDLY_EN BIT(11) + +#define SEL_DLY_RXCLK BIT(18) +#define SEL_DLY_TXCLK BIT(19) + +#define CALDONE_MASK 0x40 +#define DLL_RDY_MASK 0x1 +#define MAX_CLK_BUF0 BIT(20) +#define MAX_CLK_BUF1 BIT(21) +#define MAX_CLK_BUF2 BIT(22) + +#define CLK_MULTIPLIER 0xC008E +#define LOOP_TIMEOUT 3000 +#define TIMEOUT_DELAY 100 + +struct axiado_emmc_phy { + void __iomem *reg_base; +}; + +static void arasan_emmc_phy_write(struct axiado_emmc_phy *ax_phy, u32 offset, u32 data) +{ + writel(data, ax_phy->reg_base + offset); +} + +static int arasan_emmc_phy_read(struct axiado_emmc_phy *ax_phy, u32 offset) +{ + u32 val = readl(ax_phy->reg_base + offset); + + return val; +} + +static int axiado_emmc_phy_init(struct phy *phy) +{ + u32 val; + ktime_t timeout; + + struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | RETB_ENBL | RTRIM_EN); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | PDB_ENBL); + + /* Wait max 3000 ms */ + timeout = ktime_add_ms(ktime_get(), LOOP_TIMEOUT); + + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + if (arasan_emmc_phy_read(ax_phy, STATUS) & CALDONE_MASK) + break; + + if (timedout) { + dev_err(&phy->dev, "CALDONE_MASK bit is not cleared."); + return -ETIMEDOUT; + } + udelay(TIMEOUT_DELAY); + } + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); + + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | REN_CMD_EN | PU_CMD_EN); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_2, val | REN_STRB); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | MAX_CLK_BUF0 | + MAX_CLK_BUF1 | MAX_CLK_BUF2); + + val = arasan_emmc_phy_read(ax_phy, CAP_REG_IN_S1_MSB); + arasan_emmc_phy_write(ax_phy, CAP_REG_IN_S1_MSB, CLK_MULTIPLIER); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | SEL_DLY_RXCLK | + SEL_DLY_TXCLK); + + return 0; +} + +static int axiado_emmc_phy_power_on(struct phy *phy) +{ + struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy); + + u32 val; + ktime_t timeout; + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | RETB_ENBL); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | PDB_ENBL); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_2, val | OTAP_SEL(OTAP_DLY)); + + arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | DLL_TRM(DLL_TRM_ICP)); + + arasan_emmc_phy_write(ax_phy, STATUS, 0x00); + + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | DLL_FRQSEL(FRQ_SEL)); + + /* Wait max 3000 ms */ + timeout = ktime_add_ms(ktime_get(), LOOP_TIMEOUT); + + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + if (arasan_emmc_phy_read(ax_phy, STATUS) & DLL_RDY_MASK) + break; + + if (timedout) { + dev_err(&phy->dev, "DLL_RDY_MASK bit is not cleared."); + return -ETIMEDOUT; + } + udelay(TIMEOUT_DELAY); + } + return 0; +} + +static const struct phy_ops axiado_emmc_phy_ops = { + .init = axiado_emmc_phy_init, + .power_on = axiado_emmc_phy_power_on, + .owner = THIS_MODULE, +}; + +static const struct of_device_id axiado_emmc_phy_of_match[] = { + { .compatible = "axiado,ax3000-emmc-phy"}, + {} +}; +MODULE_DEVICE_TABLE(of, axiado_emmc_phy_of_match); + +static int axiado_emmc_phy_probe(struct platform_device *pdev) +{ + struct axiado_emmc_phy *ax_phy; + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + const struct of_device_id *id; + struct phy *generic_phy; + struct resource *res; + + if (!dev->of_node) + return -ENODEV; + + ax_phy = devm_kzalloc(dev, sizeof(*ax_phy), GFP_KERNEL); + if (!ax_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + ax_phy->reg_base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(ax_phy->reg_base)) + return PTR_ERR(ax_phy->reg_base); + + id = of_match_node(axiado_emmc_phy_of_match, pdev->dev.of_node); + if (!id) { + dev_err(dev, "failed to get match_node\n"); + return -EINVAL; + } + + generic_phy = devm_phy_create(dev, dev->of_node, &axiado_emmc_phy_ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, ax_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver axiado_emmc_phy_driver = { + .probe = axiado_emmc_phy_probe, + .driver = { + .name = "axiado-emmc-phy", + .of_match_table = axiado_emmc_phy_of_match, + }, +}; +module_platform_driver(axiado_emmc_phy_driver); + +MODULE_DESCRIPTION("AX3000 eMMC PHY Driver"); +MODULE_AUTHOR("Axiado Corporation"); +MODULE_LICENSE("GPL"); -- 2.48.1 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 2/8] phy: axiado: add Arasan eMMC-PHY for Axiado 2025-12-22 8:45 ` Tzu-Hao Wei @ 2025-12-23 14:32 ` Krzysztof Kozlowski -1 siblings, 0 replies; 7+ messages in thread From: Krzysztof Kozlowski @ 2025-12-23 14:32 UTC (permalink / raw) To: Tzu-Hao Wei Cc: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah, Ulf Hansson, Adrian Hunter, Michal Simek, linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc On Mon, Dec 22, 2025 at 04:45:01PM +0800, Tzu-Hao Wei wrote: > @@ -15,6 +15,7 @@ obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o > obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o Where is maintainers file update in this patch? Why shall we take unmaintained code? > obj-y += allwinner/ \ > amlogic/ \ > + axiado/ \ > broadcom/ \ > cadence/ \ > freescale/ \ > diff --git a/drivers/phy/axiado/Kconfig b/drivers/phy/axiado/Kconfig > new file mode 100644 > index 0000000000000000000000000000000000000000..824114e6068da327308321b9884552ad33db9efc > --- /dev/null > +++ b/drivers/phy/axiado/Kconfig > @@ -0,0 +1,15 @@ > +# > +# PHY drivers for Axiado platforms > +# > + Missing menuconfig or other if-block for groupping this with your ARCH and COMPILE_TEST dependency. Look how other NEW and MAINTAINED platforms did it. > +config PHY_AX3000_EMMC > + tristate "Axiado eMMC PHY driver" > + select GENERIC_PHY > + help > + This enables support for the eMMC PHY block found on the > + Axiado AX3000 SoCs. The PHY provides the physical layer > + interface used by the Arasan SDHCI host controller for emmc > + signaling and timing adjustment. > + > + If you are building a kernel for AX3000 platform with > + eMMC storage, say Y or N. ... > +static void arasan_emmc_phy_write(struct axiado_emmc_phy *ax_phy, u32 offset, u32 data) > +{ > + writel(data, ax_phy->reg_base + offset); > +} > + > +static int arasan_emmc_phy_read(struct axiado_emmc_phy *ax_phy, u32 offset) Useless wrappers. Just use readl/writel directly. You are not making code more readable. > +{ > + u32 val = readl(ax_phy->reg_base + offset); > + > + return val; > +} > + > +static int axiado_emmc_phy_init(struct phy *phy) > +{ > + u32 val; > + ktime_t timeout; > + > + struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | RETB_ENBL | RTRIM_EN); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | PDB_ENBL); > + > + /* Wait max 3000 ms */ > + timeout = ktime_add_ms(ktime_get(), LOOP_TIMEOUT); > + > + while (1) { > + bool timedout = ktime_after(ktime_get(), timeout); > + > + if (arasan_emmc_phy_read(ax_phy, STATUS) & CALDONE_MASK) > + break; > + > + if (timedout) { > + dev_err(&phy->dev, "CALDONE_MASK bit is not cleared."); > + return -ETIMEDOUT; > + } > + udelay(TIMEOUT_DELAY); > + } > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); > + > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | REN_CMD_EN | PU_CMD_EN); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_2, val | REN_STRB); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | MAX_CLK_BUF0 | > + MAX_CLK_BUF1 | MAX_CLK_BUF2); > + > + val = arasan_emmc_phy_read(ax_phy, CAP_REG_IN_S1_MSB); > + arasan_emmc_phy_write(ax_phy, CAP_REG_IN_S1_MSB, CLK_MULTIPLIER); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | SEL_DLY_RXCLK | > + SEL_DLY_TXCLK); > + > + return 0; > +} > + > +static int axiado_emmc_phy_power_on(struct phy *phy) > +{ > + struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy); > + > + u32 val; > + ktime_t timeout; > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | RETB_ENBL); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | PDB_ENBL); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_2, val | OTAP_SEL(OTAP_DLY)); > + > + arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | DLL_TRM(DLL_TRM_ICP)); > + > + arasan_emmc_phy_write(ax_phy, STATUS, 0x00); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | DLL_FRQSEL(FRQ_SEL)); > + > + /* Wait max 3000 ms */ > + timeout = ktime_add_ms(ktime_get(), LOOP_TIMEOUT); > + > + while (1) { You proper read_poll loop. > + bool timedout = ktime_after(ktime_get(), timeout); > + > + if (arasan_emmc_phy_read(ax_phy, STATUS) & DLL_RDY_MASK) > + break; > + > + if (timedout) { > + dev_err(&phy->dev, "DLL_RDY_MASK bit is not cleared."); > + return -ETIMEDOUT; > + } > + udelay(TIMEOUT_DELAY); ... > +static int axiado_emmc_phy_probe(struct platform_device *pdev) > +{ > + struct axiado_emmc_phy *ax_phy; > + struct phy_provider *phy_provider; > + struct device *dev = &pdev->dev; > + const struct of_device_id *id; > + struct phy *generic_phy; > + struct resource *res; > + > + if (!dev->of_node) > + return -ENODEV; > + > + ax_phy = devm_kzalloc(dev, sizeof(*ax_phy), GFP_KERNEL); > + if (!ax_phy) > + return -ENOMEM; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + Use proper wrapper to combine get resource and ioremap. > + ax_phy->reg_base = devm_ioremap_resource(&pdev->dev, res); > + Drop blank line, there's never such. > + if (IS_ERR(ax_phy->reg_base)) > + return PTR_ERR(ax_phy->reg_base); > + > + id = of_match_node(axiado_emmc_phy_of_match, pdev->dev.of_node); > + if (!id) { > + dev_err(dev, "failed to get match_node\n"); What is the point of this? You do not use this match at all, no other devices. How can your device bind and still fail the match? Drop > + return -EINVAL; > + } > + > + generic_phy = devm_phy_create(dev, dev->of_node, &axiado_emmc_phy_ops); > + if (IS_ERR(generic_phy)) { > + dev_err(dev, "failed to create PHY\n"); > + return PTR_ERR(generic_phy); Syntax is - return dev_err_probe. Didn't Axiado receive this feedback before? Are you sure that you have procedures set inside to avoid repeating same mistakes? Best regards, Krzysztof ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/8] phy: axiado: add Arasan eMMC-PHY for Axiado @ 2025-12-23 14:32 ` Krzysztof Kozlowski 0 siblings, 0 replies; 7+ messages in thread From: Krzysztof Kozlowski @ 2025-12-23 14:32 UTC (permalink / raw) To: Tzu-Hao Wei Cc: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah, Ulf Hansson, Adrian Hunter, Michal Simek, linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc On Mon, Dec 22, 2025 at 04:45:01PM +0800, Tzu-Hao Wei wrote: > @@ -15,6 +15,7 @@ obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o > obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o Where is maintainers file update in this patch? Why shall we take unmaintained code? > obj-y += allwinner/ \ > amlogic/ \ > + axiado/ \ > broadcom/ \ > cadence/ \ > freescale/ \ > diff --git a/drivers/phy/axiado/Kconfig b/drivers/phy/axiado/Kconfig > new file mode 100644 > index 0000000000000000000000000000000000000000..824114e6068da327308321b9884552ad33db9efc > --- /dev/null > +++ b/drivers/phy/axiado/Kconfig > @@ -0,0 +1,15 @@ > +# > +# PHY drivers for Axiado platforms > +# > + Missing menuconfig or other if-block for groupping this with your ARCH and COMPILE_TEST dependency. Look how other NEW and MAINTAINED platforms did it. > +config PHY_AX3000_EMMC > + tristate "Axiado eMMC PHY driver" > + select GENERIC_PHY > + help > + This enables support for the eMMC PHY block found on the > + Axiado AX3000 SoCs. The PHY provides the physical layer > + interface used by the Arasan SDHCI host controller for emmc > + signaling and timing adjustment. > + > + If you are building a kernel for AX3000 platform with > + eMMC storage, say Y or N. ... > +static void arasan_emmc_phy_write(struct axiado_emmc_phy *ax_phy, u32 offset, u32 data) > +{ > + writel(data, ax_phy->reg_base + offset); > +} > + > +static int arasan_emmc_phy_read(struct axiado_emmc_phy *ax_phy, u32 offset) Useless wrappers. Just use readl/writel directly. You are not making code more readable. > +{ > + u32 val = readl(ax_phy->reg_base + offset); > + > + return val; > +} > + > +static int axiado_emmc_phy_init(struct phy *phy) > +{ > + u32 val; > + ktime_t timeout; > + > + struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | RETB_ENBL | RTRIM_EN); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | PDB_ENBL); > + > + /* Wait max 3000 ms */ > + timeout = ktime_add_ms(ktime_get(), LOOP_TIMEOUT); > + > + while (1) { > + bool timedout = ktime_after(ktime_get(), timeout); > + > + if (arasan_emmc_phy_read(ax_phy, STATUS) & CALDONE_MASK) > + break; > + > + if (timedout) { > + dev_err(&phy->dev, "CALDONE_MASK bit is not cleared."); > + return -ETIMEDOUT; > + } > + udelay(TIMEOUT_DELAY); > + } > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); > + > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | REN_CMD_EN | PU_CMD_EN); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_2, val | REN_STRB); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | MAX_CLK_BUF0 | > + MAX_CLK_BUF1 | MAX_CLK_BUF2); > + > + val = arasan_emmc_phy_read(ax_phy, CAP_REG_IN_S1_MSB); > + arasan_emmc_phy_write(ax_phy, CAP_REG_IN_S1_MSB, CLK_MULTIPLIER); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | SEL_DLY_RXCLK | > + SEL_DLY_TXCLK); > + > + return 0; > +} > + > +static int axiado_emmc_phy_power_on(struct phy *phy) > +{ > + struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy); > + > + u32 val; > + ktime_t timeout; > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | RETB_ENBL); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | PDB_ENBL); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_2, val | OTAP_SEL(OTAP_DLY)); > + > + arasan_emmc_phy_read(ax_phy, PHY_CTRL_2); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_1); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_1, val | DLL_TRM(DLL_TRM_ICP)); > + > + arasan_emmc_phy_write(ax_phy, STATUS, 0x00); > + > + val = arasan_emmc_phy_read(ax_phy, PHY_CTRL_3); > + arasan_emmc_phy_write(ax_phy, PHY_CTRL_3, val | DLL_FRQSEL(FRQ_SEL)); > + > + /* Wait max 3000 ms */ > + timeout = ktime_add_ms(ktime_get(), LOOP_TIMEOUT); > + > + while (1) { You proper read_poll loop. > + bool timedout = ktime_after(ktime_get(), timeout); > + > + if (arasan_emmc_phy_read(ax_phy, STATUS) & DLL_RDY_MASK) > + break; > + > + if (timedout) { > + dev_err(&phy->dev, "DLL_RDY_MASK bit is not cleared."); > + return -ETIMEDOUT; > + } > + udelay(TIMEOUT_DELAY); ... > +static int axiado_emmc_phy_probe(struct platform_device *pdev) > +{ > + struct axiado_emmc_phy *ax_phy; > + struct phy_provider *phy_provider; > + struct device *dev = &pdev->dev; > + const struct of_device_id *id; > + struct phy *generic_phy; > + struct resource *res; > + > + if (!dev->of_node) > + return -ENODEV; > + > + ax_phy = devm_kzalloc(dev, sizeof(*ax_phy), GFP_KERNEL); > + if (!ax_phy) > + return -ENOMEM; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + Use proper wrapper to combine get resource and ioremap. > + ax_phy->reg_base = devm_ioremap_resource(&pdev->dev, res); > + Drop blank line, there's never such. > + if (IS_ERR(ax_phy->reg_base)) > + return PTR_ERR(ax_phy->reg_base); > + > + id = of_match_node(axiado_emmc_phy_of_match, pdev->dev.of_node); > + if (!id) { > + dev_err(dev, "failed to get match_node\n"); What is the point of this? You do not use this match at all, no other devices. How can your device bind and still fail the match? Drop > + return -EINVAL; > + } > + > + generic_phy = devm_phy_create(dev, dev->of_node, &axiado_emmc_phy_ops); > + if (IS_ERR(generic_phy)) { > + dev_err(dev, "failed to create PHY\n"); > + return PTR_ERR(generic_phy); Syntax is - return dev_err_probe. Didn't Axiado receive this feedback before? Are you sure that you have procedures set inside to avoid repeating same mistakes? Best regards, Krzysztof -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/8] phy: axiado: add Arasan eMMC-PHY for Axiado 2025-12-23 14:32 ` Krzysztof Kozlowski @ 2026-02-05 3:36 ` Tzu-Hao Wei -1 siblings, 0 replies; 7+ messages in thread From: Tzu-Hao Wei @ 2026-02-05 3:36 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah, Ulf Hansson, Adrian Hunter, Michal Simek, linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc On 12/23/2025 10:32 PM, Krzysztof Kozlowski wrote: > CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe. > > > On Mon, Dec 22, 2025 at 04:45:01PM +0800, Tzu-Hao Wei wrote: >> @@ -15,6 +15,7 @@ obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o >> obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o > > > Where is maintainers file update in this patch? Why shall we take > unmaintained code? > It's in this series 4/8. >> obj-y += allwinner/ \ >> amlogic/ \ >> + axiado/ \ >> broadcom/ \ >> cadence/ \ >> freescale/ \ >> diff --git a/drivers/phy/axiado/Kconfig b/drivers/phy/axiado/Kconfig >> new file mode 100644 >> index 0000000000000000000000000000000000000000..824114e6068da327308321b9884552ad33db9efc >> --- /dev/null >> +++ b/drivers/phy/axiado/Kconfig >> @@ -0,0 +1,15 @@ >> +# >> +# PHY drivers for Axiado platforms >> +# >> + > > Missing menuconfig or other if-block for groupping this with your ARCH > and COMPILE_TEST dependency. > Updated in the next version with depends on OF && (ARCH_AXIADO || COMPILE_TEST) > Look how other NEW and MAINTAINED platforms did it. > >> +config PHY_AX3000_EMMC >> + tristate "Axiado eMMC PHY driver" >> + select GENERIC_PHY >> + help >> + This enables support for the eMMC PHY block found on the >> + Axiado AX3000 SoCs. The PHY provides the physical layer >> + interface used by the Arasan SDHCI host controller for emmc >> + signaling and timing adjustment. >> + >> + If you are building a kernel for AX3000 platform with >> + eMMC storage, say Y or N. > > ... > > Clean up the description in the new patch. >> +static void arasan_emmc_phy_write(struct axiado_emmc_phy *ax_phy, u32 offset, u32 data) >> +{ >> + writel(data, ax_phy->reg_base + offset); >> +} >> + >> +static int arasan_emmc_phy_read(struct axiado_emmc_phy *ax_phy, u32 offset) > > Useless wrappers. Just use readl/writel directly. You are not making > code more readable. > Removed wrappers and use readl/writel directly. >> + >> + while (1) { > > You proper read_poll loop. Removed while() and use readl_poll_timeout() >> + bool timedout = ktime_after(ktime_get(), timeout); >> + >> + if (arasan_emmc_phy_read(ax_phy, STATUS) & DLL_RDY_MASK) >> + break; >> + >> + if (timedout) { >> + dev_err(&phy->dev, "DLL_RDY_MASK bit is not cleared."); >> + return -ETIMEDOUT; >> + } >> + udelay(TIMEOUT_DELAY); > > ... > Removed. >> +static int axiado_emmc_phy_probe(struct platform_device *pdev) >> +{ >> + struct axiado_emmc_phy *ax_phy; >> + struct phy_provider *phy_provider; >> + struct device *dev = &pdev->dev; >> + const struct of_device_id *id; >> + struct phy *generic_phy; >> + struct resource *res; >> + >> + if (!dev->of_node) >> + return -ENODEV; >> + >> + ax_phy = devm_kzalloc(dev, sizeof(*ax_phy), GFP_KERNEL); >> + if (!ax_phy) >> + return -ENOMEM; >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + > > Use proper wrapper to combine get resource and ioremap. > Fixed. Calling devm_platform_ioremap_resource(pdev, 0) now. >> + ax_phy->reg_base = devm_ioremap_resource(&pdev->dev, res); >> + > > Drop blank line, there's never such. > Removed >> + if (IS_ERR(ax_phy->reg_base)) >> + return PTR_ERR(ax_phy->reg_base); >> + >> + id = of_match_node(axiado_emmc_phy_of_match, pdev->dev.of_node); >> + if (!id) { >> + dev_err(dev, "failed to get match_node\n"); > > What is the point of this? You do not use this match at all, no other > devices. How can your device bind and still fail the match? > > Drop > Removed. >> + return -EINVAL; >> + } >> + >> + generic_phy = devm_phy_create(dev, dev->of_node, &axiado_emmc_phy_ops); >> + if (IS_ERR(generic_phy)) { >> + dev_err(dev, "failed to create PHY\n"); >> + return PTR_ERR(generic_phy); > > Syntax is - return dev_err_probe. > Fixed. > Didn't Axiado receive this feedback before? Are you sure that you have > procedures set inside to avoid repeating same mistakes? > > Best regards, > Krzysztof > Thanks Krzysztof. I have checked the comments one-by-one and make sure every comments are adopted. Best regards, TH ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/8] phy: axiado: add Arasan eMMC-PHY for Axiado @ 2026-02-05 3:36 ` Tzu-Hao Wei 0 siblings, 0 replies; 7+ messages in thread From: Tzu-Hao Wei @ 2026-02-05 3:36 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah, Ulf Hansson, Adrian Hunter, Michal Simek, linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc On 12/23/2025 10:32 PM, Krzysztof Kozlowski wrote: > CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe. > > > On Mon, Dec 22, 2025 at 04:45:01PM +0800, Tzu-Hao Wei wrote: >> @@ -15,6 +15,7 @@ obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o >> obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o > > > Where is maintainers file update in this patch? Why shall we take > unmaintained code? > It's in this series 4/8. >> obj-y += allwinner/ \ >> amlogic/ \ >> + axiado/ \ >> broadcom/ \ >> cadence/ \ >> freescale/ \ >> diff --git a/drivers/phy/axiado/Kconfig b/drivers/phy/axiado/Kconfig >> new file mode 100644 >> index 0000000000000000000000000000000000000000..824114e6068da327308321b9884552ad33db9efc >> --- /dev/null >> +++ b/drivers/phy/axiado/Kconfig >> @@ -0,0 +1,15 @@ >> +# >> +# PHY drivers for Axiado platforms >> +# >> + > > Missing menuconfig or other if-block for groupping this with your ARCH > and COMPILE_TEST dependency. > Updated in the next version with depends on OF && (ARCH_AXIADO || COMPILE_TEST) > Look how other NEW and MAINTAINED platforms did it. > >> +config PHY_AX3000_EMMC >> + tristate "Axiado eMMC PHY driver" >> + select GENERIC_PHY >> + help >> + This enables support for the eMMC PHY block found on the >> + Axiado AX3000 SoCs. The PHY provides the physical layer >> + interface used by the Arasan SDHCI host controller for emmc >> + signaling and timing adjustment. >> + >> + If you are building a kernel for AX3000 platform with >> + eMMC storage, say Y or N. > > ... > > Clean up the description in the new patch. >> +static void arasan_emmc_phy_write(struct axiado_emmc_phy *ax_phy, u32 offset, u32 data) >> +{ >> + writel(data, ax_phy->reg_base + offset); >> +} >> + >> +static int arasan_emmc_phy_read(struct axiado_emmc_phy *ax_phy, u32 offset) > > Useless wrappers. Just use readl/writel directly. You are not making > code more readable. > Removed wrappers and use readl/writel directly. >> + >> + while (1) { > > You proper read_poll loop. Removed while() and use readl_poll_timeout() >> + bool timedout = ktime_after(ktime_get(), timeout); >> + >> + if (arasan_emmc_phy_read(ax_phy, STATUS) & DLL_RDY_MASK) >> + break; >> + >> + if (timedout) { >> + dev_err(&phy->dev, "DLL_RDY_MASK bit is not cleared."); >> + return -ETIMEDOUT; >> + } >> + udelay(TIMEOUT_DELAY); > > ... > Removed. >> +static int axiado_emmc_phy_probe(struct platform_device *pdev) >> +{ >> + struct axiado_emmc_phy *ax_phy; >> + struct phy_provider *phy_provider; >> + struct device *dev = &pdev->dev; >> + const struct of_device_id *id; >> + struct phy *generic_phy; >> + struct resource *res; >> + >> + if (!dev->of_node) >> + return -ENODEV; >> + >> + ax_phy = devm_kzalloc(dev, sizeof(*ax_phy), GFP_KERNEL); >> + if (!ax_phy) >> + return -ENOMEM; >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + > > Use proper wrapper to combine get resource and ioremap. > Fixed. Calling devm_platform_ioremap_resource(pdev, 0) now. >> + ax_phy->reg_base = devm_ioremap_resource(&pdev->dev, res); >> + > > Drop blank line, there's never such. > Removed >> + if (IS_ERR(ax_phy->reg_base)) >> + return PTR_ERR(ax_phy->reg_base); >> + >> + id = of_match_node(axiado_emmc_phy_of_match, pdev->dev.of_node); >> + if (!id) { >> + dev_err(dev, "failed to get match_node\n"); > > What is the point of this? You do not use this match at all, no other > devices. How can your device bind and still fail the match? > > Drop > Removed. >> + return -EINVAL; >> + } >> + >> + generic_phy = devm_phy_create(dev, dev->of_node, &axiado_emmc_phy_ops); >> + if (IS_ERR(generic_phy)) { >> + dev_err(dev, "failed to create PHY\n"); >> + return PTR_ERR(generic_phy); > > Syntax is - return dev_err_probe. > Fixed. > Didn't Axiado receive this feedback before? Are you sure that you have > procedures set inside to avoid repeating same mistakes? > > Best regards, > Krzysztof > Thanks Krzysztof. I have checked the comments one-by-one and make sure every comments are adopted. Best regards, TH -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-02-05 3:36 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-12-23 13:22 [PATCH 2/8] phy: axiado: add Arasan eMMC-PHY for Axiado kernel test robot -- strict thread matches above, loose matches on Subject: below -- 2025-12-22 8:44 [PATCH 0/8] Add Axiado AX3000 eMMC Host Controller Support Tzu-Hao Wei 2025-12-22 8:45 ` [PATCH 2/8] phy: axiado: add Arasan eMMC-PHY for Axiado Tzu-Hao Wei 2025-12-22 8:45 ` Tzu-Hao Wei 2025-12-23 14:32 ` Krzysztof Kozlowski 2025-12-23 14:32 ` Krzysztof Kozlowski 2026-02-05 3:36 ` Tzu-Hao Wei 2026-02-05 3:36 ` Tzu-Hao Wei
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.