All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Ze Huang" <huang.ze@linux.dev>
To: "Inochi Amaoto" <inochiama@gmail.com>,
	"Vinod Koul" <vkoul@kernel.org>,
	"Neil Armstrong" <neil.armstrong@linaro.org>,
	"Rob Herring" <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Yixun Lan" <dlan@kernel.org>, "Kees Cook" <kees@kernel.org>,
	"Gustavo A. R. Silva" <gustavoars@kernel.org>,
	"Paul Walmsley" <pjw@kernel.org>,
	"Palmer Dabbelt" <palmer@dabbelt.com>,
	"Albert Ou" <aou@eecs.berkeley.edu>,
	"Alexandre Ghiti" <alex@ghiti.fr>,
	"Ze Huang" <huang.ze@linux.dev>,
	"Alex Elder" <elder@riscstar.com>
Cc: <linux-phy@lists.infradead.org>, <devicetree@vger.kernel.org>,
	<linux-riscv@lists.infradead.org>, <spacemit@lists.linux.dev>,
	<linux-kernel@vger.kernel.org>, <linux-hardening@vger.kernel.org>,
	"Yixun Lan" <dlan@gentoo.org>,
	"Longbin Li" <looong.bin@gmail.com>
Subject: Re: [PATCH 2/2] phy: spacemit: Add USB3/PCIe comb PHY driver for Spacemit K3
Date: Thu, 30 Apr 2026 15:39:51 +0800	[thread overview]
Message-ID: <DI6BHTW4FFN1.2YLR7O1P8F0Y3@linux.dev> (raw)
In-Reply-To: <20260430022843.1090138-3-inochiama@gmail.com>

On Thu Apr 30, 2026 at 10:28 AM CST, Inochi Amaoto wrote:
> The comb PHY on K3 requires to configure a syscon device for the
> right mux configuration. And it requires calibration before any
> usage.
>
> Add USB3/PCIe comb PHY driver for Spacemit K3.
>
> Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
> ---
>  drivers/phy/spacemit/Kconfig          |  16 ++
>  drivers/phy/spacemit/Makefile         |   2 +
>  drivers/phy/spacemit/phy-k3-combphy.c | 250 ++++++++++++++++
>  drivers/phy/spacemit/phy-k3-common.c  | 398 ++++++++++++++++++++++++++
>  drivers/phy/spacemit/phy-k3-common.h  |  27 ++
>  5 files changed, 693 insertions(+)
>  create mode 100644 drivers/phy/spacemit/phy-k3-combphy.c
>  create mode 100644 drivers/phy/spacemit/phy-k3-common.c
>  create mode 100644 drivers/phy/spacemit/phy-k3-common.h
>
> diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig
> index 50b0005acf66..5fdf18fce499 100644
> --- a/drivers/phy/spacemit/Kconfig
> +++ b/drivers/phy/spacemit/Kconfig
> @@ -23,3 +23,19 @@ config PHY_SPACEMIT_K1_USB2
>  	help
>  	  Enable this to support K1 USB 2.0 PHY driver. This driver takes care of
>  	  enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver.
> +
> +config PHY_SPACEMIT_K3_COMMON_OPS
> +	tristate
> +	select MFD_SYSCON
> +	select GENERIC_PHY
> +
> +config PHY_SPACEMIT_K3_COMBO_PHY
> +	tristate "SpacemiT K3 USB3/PCIe PHY support"
> +	depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF
> +	depends on COMMON_CLK
> +	select PHY_SPACEMIT_K3_COMMON_OPS
> +	help
> +	  Enable this to support K3 USB3/PCIe combo PHY driver. This
> +	  driver takes care of enabling and clock setup and will be used
> +	  by K3 dwc3 driver.
> +	  If unsure, say N.
> diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile
> index a821a21d6142..41be7b0388da 100644
> --- a/drivers/phy/spacemit/Makefile
> +++ b/drivers/phy/spacemit/Makefile
> @@ -1,3 +1,5 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE)		+= phy-k1-pcie.o
>  obj-$(CONFIG_PHY_SPACEMIT_K1_USB2)		+= phy-k1-usb2.o
> +obj-$(CONFIG_PHY_SPACEMIT_K3_COMBO_PHY)		+= phy-k3-combphy.o
> +obj-$(CONFIG_PHY_SPACEMIT_K3_COMMON_OPS)	+= phy-k3-common.o
> diff --git a/drivers/phy/spacemit/phy-k3-combphy.c b/drivers/phy/spacemit/phy-k3-combphy.c
> new file mode 100644
> index 000000000000..66fa6330ad6e
> --- /dev/null
> +++ b/drivers/phy/spacemit/phy-k3-combphy.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * phy-k3-usb3.c - SpacemiT K3 Type-C Orientation Switch Driver
> + *
> + * Copyright (c) 2025 SpacemiT Technology Co. Ltd
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/io.h>

...

> +
> +	phy->apb_spare = syscon_regmap_lookup_by_phandle(node, "spacemit,apb-spare");
> +	if (IS_ERR(phy->apb_spare))
> +		return dev_err_probe(dev, PTR_ERR(phy->apb_spare),
> +				     "Failed to fine APB SPARE syscon");

typo, s/fine/find

> +
> +	apmu = syscon_regmap_lookup_by_phandle_args(node, "spacemit,apmu", 1, &config);
> +	if (IS_ERR(apmu))
> +		return dev_err_probe(dev, PTR_ERR(phy->apb_spare),
> +				     "Failed to fine APMU syscon");

1. typo, s/fine/find
2. PTR_ERR(phy->apb_spare) should be PTR_ERR(apmu)

> +
> +	ret = k3_comb_phy_update_config(apmu, config);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to set lane configuration");
> +
> +	phy->dev = dev;
> +	platform_set_drvdata(pdev, phy);
> +
> +	ret = k3_phy_calibrate(phy->apb_spare);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to calibrate phy");
> +
> +	ret = k3_comb_phy_init_lanes(phy, config);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to init lanes");
> +
> +	provider = devm_of_phy_provider_register(dev, k3_comb_phy_xlate);
> +	if (IS_ERR(provider))
> +		return dev_err_probe(dev, PTR_ERR(provider),
> +				     "Failed to register provider\n");
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id k3_comb_phy_of_match[] = {
> +	{ .compatible = "spacemit,k3-comb-phy" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, k3_comb_phy_of_match);
> +
> +static struct platform_driver k3_comb_phy_driver = {
> +	.probe = k3_comb_phy_probe,
> +	.driver = {
> +		.name = "spacemit,k3-comb-phy",
> +		.of_match_table = k3_comb_phy_of_match,
> +	},
> +};
> +module_platform_driver(k3_comb_phy_driver);
> +
> +MODULE_DESCRIPTION("SpacemiT K3 USB3/PCIe comb PHY driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/phy/spacemit/phy-k3-common.c b/drivers/phy/spacemit/phy-k3-common.c
> new file mode 100644
> index 000000000000..77c4b4073b96
> --- /dev/null
> +++ b/drivers/phy/spacemit/phy-k3-common.c
> @@ -0,0 +1,398 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <linux/bitfield.h>
> +#include <linux/cleanup.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/usb.h>
> +
> +#include <dt-bindings/phy/phy.h>
> +
> +#include "phy-k3-common.h"
> +
> +/* PHY Registers */
> +#define PHY_VERSION			0x0
> +
> +#define PHY_RESET_CFG			0x04
> +
> +#define PHY_RESET_RXBUF_RST		BIT(0)
> +#define PHY_RESET_SOFT_RST_PCS		BIT(1)
> +#define PHY_RESET_SOFT_RST_AHB		BIT(2)
> +#define PHY_RESET_EN_SD_AFTER_LOCK	BIT(6)
> +
> +#define PHY_CLK_CFG			0x08
> +
> +#define PHY_CLK_PLL_READY		BIT(0)
> +#define PHY_CLK_TXCLK_INV		BIT(2)
> +#define PHY_CLK_RXCLK_EN		BIT(3)
> +#define PHY_CLK_TXCLK_EN		BIT(4)
> +#define PHY_CLK_PCLK_EN			BIT(5)
> +#define PHY_CLK_PIPE_PCLK_EN		BIT(6)
> +#define PHY_CLK_REFCLK_FREQ		GENMASK(10, 7)
> +#define PHY_CLK_REFCLK_24M		2
> +#define PHY_CLK_SW_INIT_DONE		BIT(11)
> +#define PHY_CLK_PU_SSC_OUT		BIT(23)
> +
> +#define PHY_MODE_CFG			0x0C
> +
> +#define PHY_MODE_PCIE_INT_EN		BIT(0)
> +#define PHY_MODE_LFPS_TPERIOD		GENMASK(9, 8)
> +#define PHY_MODE_LFPS_TPERIOD_USB	3
> +
> +#define PHY_PU_SEL			0x40
> +
> +#define PHY_PU_CFG_STATUS		BIT(9)
> +#define PHY_PU_OVRD_STATUS		BIT(10)
> +
> +#define PHY_PU_CK_REG			0x54
> +
> +#define PHY_PU_REFCLK_100		BIT(25)
> +
> +#define PHY_PLL_REG1			0x58
> +
> +#define PHY_PLL_FREF_SEL		GENMASK(15, 13)
> +#define PHY_PLL_FREF_24M		0x1
> +#define PHY_PLL_SSC_DEP_SEL		GENMASK(27, 24)
> +#define PHY_PLL_SSC_5000PPM		0xa
> +#define PHY_PLL_SSC_MODE		GENMASK(29, 28)
> +#define PHY_PLL_SSC_MODE_CENTER_SPREAD	0
> +#define PHY_PLL_SSC_MODE_UP_SPREAD	1
> +#define PHY_PLL_SSC_MODE_DOWN_SPREAD	2
> +#define PHY_PLL_SSC_MODE_DOWN_SPREAD1	3
> +
> +#define PHY_PLL_REG2			0x5c
> +
> +#define PHY_PLL_SEL_REF100		BIT(21)
> +
> +/* PHY RX Register Definitions */
> +#define PHY_RX_REG_A			0x60
> +
> +#define PHY_RX_REG0_RLOAD		BIT(4)
> +#define PHY_RX_REG1_RTERM		GENMASK(11, 8)
> +#define PHY_RX_REG1_RC_CALI		GENMASK(15, 12)
> +#define PHY_RX_REG2_CSEL		GENMASK(19, 16)
> +#define PHY_RX_REG2_FORCE_CSEL		BIT(20)
> +#define PHY_RX_REG2_PSEL		GENMASK(23, 21)
> +#define PHY_RX_REG3_I_LOAD		GENMASK(26, 24)
> +#define PHY_RX_REG3_SEL_CBOOST_CODE	BIT(27)
> +#define PHY_RX_REG3_ADJ_BIAS		GENMASK(29, 28)
> +#define PHY_RX_REG3_RDEG1		GENMASK(31, 30)
> +
> +#define PHY_RX_REG_B			0x64
> +
> +#define PHY_RX_REGB_MASK		GENMASK(23, 0)
> +
> +#define PHY_RX_REG4_RDEG2		GENMASK(2, 1)
> +#define PHY_RX_REG4_ENVOS		BIT(4)
> +#define PHY_RX_REG4_RTERM_SEL		BIT(5)
> +#define PHY_RX_REG4_MANUAL_CFG		BIT(7)
> +#define PHY_RX_REG5_RCELL_VCM		GENMASK(11, 8)
> +#define PHY_RX_REG5_RCELL_BIAS		GENMASK(15, 12)
> +#define PHY_RX_REG6_H1_REG		GENMASK(19, 16)
> +#define PHY_RX_REG6_ADAPT_GAIN		GENMASK(21, 20)
> +#define PHY_RX_REG6_BYPASS_ADPT		BIT(22)
> +
> +#define PHY_ADPT_CFG0			0x140
> +#define PHY_ADPT_AFE_RST_OVRD_EN	BIT(1)
> +#define PHY_ADPT_AFE_RST_OVRD_VAL	BIT(4)
> +
> +#define PHY_RXEQ_TIME			0xb4
> +#define PHY_RXEQ_TIME_OVRD_POST_C_SOC	BIT(21)
> +#define PHY_RXEQ_TIME_CFG_AMP_SOC	GENMASK(23, 22)
> +#define PHY_RXEQ_TIME_AMP_SOC_650M	0
> +#define PHY_RXEQ_TIME_AMP_SOC_800M	1
> +#define PHY_RXEQ_TIME_AMP_SOC_870M	2
> +#define PHY_RXEQ_TIME_AMP_SOC_900M	3
> +#define PHY_RXEQ_TIME_OVRD_AMP_SOC	BIT(24)
> +
> +#define PCIE_PU_ADDR_CLK_CFG		0x0008
> +#define PHY_CLK_PLL_READY		BIT(0)
> +#define PCIE_INITAL_TIMER		GENMASK(6, 3)
> +#define CFG_INTERNAL_TIMER_ADJ		GENMASK(10, 7)
> +#define CFG_SW_PHY_INIT_DONE		BIT(11)
> +
> +/* Lane RX/TX configuration (per‑lane, at lane_base) */
> +#define PCIE_RX_REG1			0x050
> +#define PCIE_TX_REG1			0x064
> +
> +#define PCIE_PLL_TIMEOUT		500000
> +#define PCIE_POLL_DELAY			500
> +
> +

...

> +static int k3_pcie_phy_init(struct phy *phy)
> +{
> +	struct k3_lane_group *lg = phy_get_drvdata(phy);
> +	void __iomem *phy_base = lg->base + lg->data->offsets[0];
> +	u32 val;
> +	int ret;
> +	int i;
> +
> +	val = readl(phy_base + PHY_PLL_REG1);
> +	val = u32_replace_bits(val, 0x2, GENMASK(15, 12));
> +	writel(val, phy_base + PHY_PLL_REG1);
> +
> +	val = readl(phy_base + PHY_PLL_REG2);
> +	val = u32_replace_bits(val, 0, BIT(21));
> +	writel(val, phy_base + PHY_PLL_REG2);
> +
> +	for (i = 0; i < lg->data->lanes; i++) {
> +		void __iomem *lane_base = lg->base + lg->data->offsets[i];
> +

> +		val = readl(lane_base + PCIE_RX_REG1);
> +		val = u32_replace_bits(val, 0, 0x3);
> +		writel(val, phy_base + PCIE_RX_REG1);

This looks like a copy-paste bug.

Read from lane_base but write the modified value to phy_base.

> +	}
> +
> +	val = readl(phy_base + PHY_PLL_REG2);
> +	val |= BIT(20);
> +	writel(val, phy_base + PHY_PLL_REG2);
> +

> +	writel(0x00006505, phy_base + PCIE_RX_REG1);

Is it intentional? The loop above configured PCIE_RX_REG1, while the
hard-coded 0x00006505 overwrites what's done for lane0.

> +
> +	/* pll_reg1 of lane0, disable SSC: pll_reg4[3:0] = 0 */
> +	val = readl(phy_base + PHY_PLL_REG1);
> +	val = u32_replace_bits(val, 0, GENMASK(27, 24));
> +	writel(val, phy_base + PHY_PLL_REG1);

A little confusing here, comment says "pll_reg4[3:0] = 0" but the code is
modifying PHY_PLL_REG1[27:24]

> +
> +	for (i = 0; i < lg->data->lanes; i++) {
> +		void __iomem *lane_base = lg->base + lg->data->offsets[i];
> +
> +		/* set cfg_tx_send_dummy_data to be 1'b1 for disable dash data */
> +		val = readl(lane_base + PHY_PU_SEL);
> +		val = u32_replace_bits(val, 1, BIT(13));
> +		writel(val, lane_base + PHY_PU_SEL);
> +
> +		/* disable en_sample_data_after_cdr_locked */
> +		val = readl(lane_base + PHY_RESET_CFG);
> +		val = u32_replace_bits(val, 0, BIT(6));
> +		writel(val, lane_base + PHY_RESET_CFG);
> +
> +		/* Dynamic Lock */
> +		val = readl(lane_base + PHY_MODE_CFG);
> +		val = u32_replace_bits(val, 1, BIT(2));
> +		writel(val, lane_base + PHY_MODE_CFG);
> +
> +		val = FIELD_PREP(GENMASK(7, 0), 0x10) |
> +			FIELD_PREP(GENMASK(15, 8), 0x78) |
> +			FIELD_PREP(GENMASK(23, 16), 0x98) |
> +			FIELD_PREP(GENMASK(31, 24), 0xdf);
> +		writel(val, lane_base + PHY_RX_REG_A);
> +
> +		val = readl(lane_base + PHY_RX_REG_B);
> +		val &= ~PHY_RX_REGB_MASK;
> +		val |= FIELD_PREP(GENMASK(7, 0), 0xb4) |
> +			FIELD_PREP(GENMASK(15, 8), 0x88) |
> +			FIELD_PREP(GENMASK(23, 16), 0x28);
> +		writel(val, lane_base + PHY_RX_REG_B);

Can we define macros for these values? Just like you did for
PHY_CLK_CFG.

> +
> +		/* Set init done */
> +		val = readl(lane_base + PCIE_PU_ADDR_CLK_CFG);
> +		val = u32_replace_bits(val, 1, CFG_SW_PHY_INIT_DONE);
> +		writel(val, lane_base + PCIE_PU_ADDR_CLK_CFG);
> +	}
> +
> +	ret = readl_poll_timeout(phy_base + PCIE_PU_ADDR_CLK_CFG, val,
> +				 (val & PHY_CLK_PLL_READY), PCIE_POLL_DELAY,
> +				 PCIE_PLL_TIMEOUT);
> +	if (ret) {
> +		dev_err(&lg->phy->dev, "PHY PLL lock timeout\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}

WARNING: multiple messages have this Message-ID (diff)
From: "Ze Huang" <huang.ze@linux.dev>
To: "Inochi Amaoto" <inochiama@gmail.com>,
	"Vinod Koul" <vkoul@kernel.org>,
	"Neil Armstrong" <neil.armstrong@linaro.org>,
	"Rob Herring" <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Yixun Lan" <dlan@kernel.org>, "Kees Cook" <kees@kernel.org>,
	"Gustavo A. R. Silva" <gustavoars@kernel.org>,
	"Paul Walmsley" <pjw@kernel.org>,
	"Palmer Dabbelt" <palmer@dabbelt.com>,
	"Albert Ou" <aou@eecs.berkeley.edu>,
	"Alexandre Ghiti" <alex@ghiti.fr>,
	"Ze Huang" <huang.ze@linux.dev>,
	"Alex Elder" <elder@riscstar.com>
Cc: <linux-phy@lists.infradead.org>, <devicetree@vger.kernel.org>,
	<linux-riscv@lists.infradead.org>, <spacemit@lists.linux.dev>,
	<linux-kernel@vger.kernel.org>, <linux-hardening@vger.kernel.org>,
	"Yixun Lan" <dlan@gentoo.org>,
	"Longbin Li" <looong.bin@gmail.com>
Subject: Re: [PATCH 2/2] phy: spacemit: Add USB3/PCIe comb PHY driver for Spacemit K3
Date: Thu, 30 Apr 2026 15:39:51 +0800	[thread overview]
Message-ID: <DI6BHTW4FFN1.2YLR7O1P8F0Y3@linux.dev> (raw)
In-Reply-To: <20260430022843.1090138-3-inochiama@gmail.com>

On Thu Apr 30, 2026 at 10:28 AM CST, Inochi Amaoto wrote:
> The comb PHY on K3 requires to configure a syscon device for the
> right mux configuration. And it requires calibration before any
> usage.
>
> Add USB3/PCIe comb PHY driver for Spacemit K3.
>
> Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
> ---
>  drivers/phy/spacemit/Kconfig          |  16 ++
>  drivers/phy/spacemit/Makefile         |   2 +
>  drivers/phy/spacemit/phy-k3-combphy.c | 250 ++++++++++++++++
>  drivers/phy/spacemit/phy-k3-common.c  | 398 ++++++++++++++++++++++++++
>  drivers/phy/spacemit/phy-k3-common.h  |  27 ++
>  5 files changed, 693 insertions(+)
>  create mode 100644 drivers/phy/spacemit/phy-k3-combphy.c
>  create mode 100644 drivers/phy/spacemit/phy-k3-common.c
>  create mode 100644 drivers/phy/spacemit/phy-k3-common.h
>
> diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig
> index 50b0005acf66..5fdf18fce499 100644
> --- a/drivers/phy/spacemit/Kconfig
> +++ b/drivers/phy/spacemit/Kconfig
> @@ -23,3 +23,19 @@ config PHY_SPACEMIT_K1_USB2
>  	help
>  	  Enable this to support K1 USB 2.0 PHY driver. This driver takes care of
>  	  enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver.
> +
> +config PHY_SPACEMIT_K3_COMMON_OPS
> +	tristate
> +	select MFD_SYSCON
> +	select GENERIC_PHY
> +
> +config PHY_SPACEMIT_K3_COMBO_PHY
> +	tristate "SpacemiT K3 USB3/PCIe PHY support"
> +	depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF
> +	depends on COMMON_CLK
> +	select PHY_SPACEMIT_K3_COMMON_OPS
> +	help
> +	  Enable this to support K3 USB3/PCIe combo PHY driver. This
> +	  driver takes care of enabling and clock setup and will be used
> +	  by K3 dwc3 driver.
> +	  If unsure, say N.
> diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile
> index a821a21d6142..41be7b0388da 100644
> --- a/drivers/phy/spacemit/Makefile
> +++ b/drivers/phy/spacemit/Makefile
> @@ -1,3 +1,5 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE)		+= phy-k1-pcie.o
>  obj-$(CONFIG_PHY_SPACEMIT_K1_USB2)		+= phy-k1-usb2.o
> +obj-$(CONFIG_PHY_SPACEMIT_K3_COMBO_PHY)		+= phy-k3-combphy.o
> +obj-$(CONFIG_PHY_SPACEMIT_K3_COMMON_OPS)	+= phy-k3-common.o
> diff --git a/drivers/phy/spacemit/phy-k3-combphy.c b/drivers/phy/spacemit/phy-k3-combphy.c
> new file mode 100644
> index 000000000000..66fa6330ad6e
> --- /dev/null
> +++ b/drivers/phy/spacemit/phy-k3-combphy.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * phy-k3-usb3.c - SpacemiT K3 Type-C Orientation Switch Driver
> + *
> + * Copyright (c) 2025 SpacemiT Technology Co. Ltd
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/io.h>

...

> +
> +	phy->apb_spare = syscon_regmap_lookup_by_phandle(node, "spacemit,apb-spare");
> +	if (IS_ERR(phy->apb_spare))
> +		return dev_err_probe(dev, PTR_ERR(phy->apb_spare),
> +				     "Failed to fine APB SPARE syscon");

typo, s/fine/find

> +
> +	apmu = syscon_regmap_lookup_by_phandle_args(node, "spacemit,apmu", 1, &config);
> +	if (IS_ERR(apmu))
> +		return dev_err_probe(dev, PTR_ERR(phy->apb_spare),
> +				     "Failed to fine APMU syscon");

1. typo, s/fine/find
2. PTR_ERR(phy->apb_spare) should be PTR_ERR(apmu)

> +
> +	ret = k3_comb_phy_update_config(apmu, config);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to set lane configuration");
> +
> +	phy->dev = dev;
> +	platform_set_drvdata(pdev, phy);
> +
> +	ret = k3_phy_calibrate(phy->apb_spare);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to calibrate phy");
> +
> +	ret = k3_comb_phy_init_lanes(phy, config);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to init lanes");
> +
> +	provider = devm_of_phy_provider_register(dev, k3_comb_phy_xlate);
> +	if (IS_ERR(provider))
> +		return dev_err_probe(dev, PTR_ERR(provider),
> +				     "Failed to register provider\n");
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id k3_comb_phy_of_match[] = {
> +	{ .compatible = "spacemit,k3-comb-phy" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, k3_comb_phy_of_match);
> +
> +static struct platform_driver k3_comb_phy_driver = {
> +	.probe = k3_comb_phy_probe,
> +	.driver = {
> +		.name = "spacemit,k3-comb-phy",
> +		.of_match_table = k3_comb_phy_of_match,
> +	},
> +};
> +module_platform_driver(k3_comb_phy_driver);
> +
> +MODULE_DESCRIPTION("SpacemiT K3 USB3/PCIe comb PHY driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/phy/spacemit/phy-k3-common.c b/drivers/phy/spacemit/phy-k3-common.c
> new file mode 100644
> index 000000000000..77c4b4073b96
> --- /dev/null
> +++ b/drivers/phy/spacemit/phy-k3-common.c
> @@ -0,0 +1,398 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <linux/bitfield.h>
> +#include <linux/cleanup.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/usb.h>
> +
> +#include <dt-bindings/phy/phy.h>
> +
> +#include "phy-k3-common.h"
> +
> +/* PHY Registers */
> +#define PHY_VERSION			0x0
> +
> +#define PHY_RESET_CFG			0x04
> +
> +#define PHY_RESET_RXBUF_RST		BIT(0)
> +#define PHY_RESET_SOFT_RST_PCS		BIT(1)
> +#define PHY_RESET_SOFT_RST_AHB		BIT(2)
> +#define PHY_RESET_EN_SD_AFTER_LOCK	BIT(6)
> +
> +#define PHY_CLK_CFG			0x08
> +
> +#define PHY_CLK_PLL_READY		BIT(0)
> +#define PHY_CLK_TXCLK_INV		BIT(2)
> +#define PHY_CLK_RXCLK_EN		BIT(3)
> +#define PHY_CLK_TXCLK_EN		BIT(4)
> +#define PHY_CLK_PCLK_EN			BIT(5)
> +#define PHY_CLK_PIPE_PCLK_EN		BIT(6)
> +#define PHY_CLK_REFCLK_FREQ		GENMASK(10, 7)
> +#define PHY_CLK_REFCLK_24M		2
> +#define PHY_CLK_SW_INIT_DONE		BIT(11)
> +#define PHY_CLK_PU_SSC_OUT		BIT(23)
> +
> +#define PHY_MODE_CFG			0x0C
> +
> +#define PHY_MODE_PCIE_INT_EN		BIT(0)
> +#define PHY_MODE_LFPS_TPERIOD		GENMASK(9, 8)
> +#define PHY_MODE_LFPS_TPERIOD_USB	3
> +
> +#define PHY_PU_SEL			0x40
> +
> +#define PHY_PU_CFG_STATUS		BIT(9)
> +#define PHY_PU_OVRD_STATUS		BIT(10)
> +
> +#define PHY_PU_CK_REG			0x54
> +
> +#define PHY_PU_REFCLK_100		BIT(25)
> +
> +#define PHY_PLL_REG1			0x58
> +
> +#define PHY_PLL_FREF_SEL		GENMASK(15, 13)
> +#define PHY_PLL_FREF_24M		0x1
> +#define PHY_PLL_SSC_DEP_SEL		GENMASK(27, 24)
> +#define PHY_PLL_SSC_5000PPM		0xa
> +#define PHY_PLL_SSC_MODE		GENMASK(29, 28)
> +#define PHY_PLL_SSC_MODE_CENTER_SPREAD	0
> +#define PHY_PLL_SSC_MODE_UP_SPREAD	1
> +#define PHY_PLL_SSC_MODE_DOWN_SPREAD	2
> +#define PHY_PLL_SSC_MODE_DOWN_SPREAD1	3
> +
> +#define PHY_PLL_REG2			0x5c
> +
> +#define PHY_PLL_SEL_REF100		BIT(21)
> +
> +/* PHY RX Register Definitions */
> +#define PHY_RX_REG_A			0x60
> +
> +#define PHY_RX_REG0_RLOAD		BIT(4)
> +#define PHY_RX_REG1_RTERM		GENMASK(11, 8)
> +#define PHY_RX_REG1_RC_CALI		GENMASK(15, 12)
> +#define PHY_RX_REG2_CSEL		GENMASK(19, 16)
> +#define PHY_RX_REG2_FORCE_CSEL		BIT(20)
> +#define PHY_RX_REG2_PSEL		GENMASK(23, 21)
> +#define PHY_RX_REG3_I_LOAD		GENMASK(26, 24)
> +#define PHY_RX_REG3_SEL_CBOOST_CODE	BIT(27)
> +#define PHY_RX_REG3_ADJ_BIAS		GENMASK(29, 28)
> +#define PHY_RX_REG3_RDEG1		GENMASK(31, 30)
> +
> +#define PHY_RX_REG_B			0x64
> +
> +#define PHY_RX_REGB_MASK		GENMASK(23, 0)
> +
> +#define PHY_RX_REG4_RDEG2		GENMASK(2, 1)
> +#define PHY_RX_REG4_ENVOS		BIT(4)
> +#define PHY_RX_REG4_RTERM_SEL		BIT(5)
> +#define PHY_RX_REG4_MANUAL_CFG		BIT(7)
> +#define PHY_RX_REG5_RCELL_VCM		GENMASK(11, 8)
> +#define PHY_RX_REG5_RCELL_BIAS		GENMASK(15, 12)
> +#define PHY_RX_REG6_H1_REG		GENMASK(19, 16)
> +#define PHY_RX_REG6_ADAPT_GAIN		GENMASK(21, 20)
> +#define PHY_RX_REG6_BYPASS_ADPT		BIT(22)
> +
> +#define PHY_ADPT_CFG0			0x140
> +#define PHY_ADPT_AFE_RST_OVRD_EN	BIT(1)
> +#define PHY_ADPT_AFE_RST_OVRD_VAL	BIT(4)
> +
> +#define PHY_RXEQ_TIME			0xb4
> +#define PHY_RXEQ_TIME_OVRD_POST_C_SOC	BIT(21)
> +#define PHY_RXEQ_TIME_CFG_AMP_SOC	GENMASK(23, 22)
> +#define PHY_RXEQ_TIME_AMP_SOC_650M	0
> +#define PHY_RXEQ_TIME_AMP_SOC_800M	1
> +#define PHY_RXEQ_TIME_AMP_SOC_870M	2
> +#define PHY_RXEQ_TIME_AMP_SOC_900M	3
> +#define PHY_RXEQ_TIME_OVRD_AMP_SOC	BIT(24)
> +
> +#define PCIE_PU_ADDR_CLK_CFG		0x0008
> +#define PHY_CLK_PLL_READY		BIT(0)
> +#define PCIE_INITAL_TIMER		GENMASK(6, 3)
> +#define CFG_INTERNAL_TIMER_ADJ		GENMASK(10, 7)
> +#define CFG_SW_PHY_INIT_DONE		BIT(11)
> +
> +/* Lane RX/TX configuration (per‑lane, at lane_base) */
> +#define PCIE_RX_REG1			0x050
> +#define PCIE_TX_REG1			0x064
> +
> +#define PCIE_PLL_TIMEOUT		500000
> +#define PCIE_POLL_DELAY			500
> +
> +

...

> +static int k3_pcie_phy_init(struct phy *phy)
> +{
> +	struct k3_lane_group *lg = phy_get_drvdata(phy);
> +	void __iomem *phy_base = lg->base + lg->data->offsets[0];
> +	u32 val;
> +	int ret;
> +	int i;
> +
> +	val = readl(phy_base + PHY_PLL_REG1);
> +	val = u32_replace_bits(val, 0x2, GENMASK(15, 12));
> +	writel(val, phy_base + PHY_PLL_REG1);
> +
> +	val = readl(phy_base + PHY_PLL_REG2);
> +	val = u32_replace_bits(val, 0, BIT(21));
> +	writel(val, phy_base + PHY_PLL_REG2);
> +
> +	for (i = 0; i < lg->data->lanes; i++) {
> +		void __iomem *lane_base = lg->base + lg->data->offsets[i];
> +

> +		val = readl(lane_base + PCIE_RX_REG1);
> +		val = u32_replace_bits(val, 0, 0x3);
> +		writel(val, phy_base + PCIE_RX_REG1);

This looks like a copy-paste bug.

Read from lane_base but write the modified value to phy_base.

> +	}
> +
> +	val = readl(phy_base + PHY_PLL_REG2);
> +	val |= BIT(20);
> +	writel(val, phy_base + PHY_PLL_REG2);
> +

> +	writel(0x00006505, phy_base + PCIE_RX_REG1);

Is it intentional? The loop above configured PCIE_RX_REG1, while the
hard-coded 0x00006505 overwrites what's done for lane0.

> +
> +	/* pll_reg1 of lane0, disable SSC: pll_reg4[3:0] = 0 */
> +	val = readl(phy_base + PHY_PLL_REG1);
> +	val = u32_replace_bits(val, 0, GENMASK(27, 24));
> +	writel(val, phy_base + PHY_PLL_REG1);

A little confusing here, comment says "pll_reg4[3:0] = 0" but the code is
modifying PHY_PLL_REG1[27:24]

> +
> +	for (i = 0; i < lg->data->lanes; i++) {
> +		void __iomem *lane_base = lg->base + lg->data->offsets[i];
> +
> +		/* set cfg_tx_send_dummy_data to be 1'b1 for disable dash data */
> +		val = readl(lane_base + PHY_PU_SEL);
> +		val = u32_replace_bits(val, 1, BIT(13));
> +		writel(val, lane_base + PHY_PU_SEL);
> +
> +		/* disable en_sample_data_after_cdr_locked */
> +		val = readl(lane_base + PHY_RESET_CFG);
> +		val = u32_replace_bits(val, 0, BIT(6));
> +		writel(val, lane_base + PHY_RESET_CFG);
> +
> +		/* Dynamic Lock */
> +		val = readl(lane_base + PHY_MODE_CFG);
> +		val = u32_replace_bits(val, 1, BIT(2));
> +		writel(val, lane_base + PHY_MODE_CFG);
> +
> +		val = FIELD_PREP(GENMASK(7, 0), 0x10) |
> +			FIELD_PREP(GENMASK(15, 8), 0x78) |
> +			FIELD_PREP(GENMASK(23, 16), 0x98) |
> +			FIELD_PREP(GENMASK(31, 24), 0xdf);
> +		writel(val, lane_base + PHY_RX_REG_A);
> +
> +		val = readl(lane_base + PHY_RX_REG_B);
> +		val &= ~PHY_RX_REGB_MASK;
> +		val |= FIELD_PREP(GENMASK(7, 0), 0xb4) |
> +			FIELD_PREP(GENMASK(15, 8), 0x88) |
> +			FIELD_PREP(GENMASK(23, 16), 0x28);
> +		writel(val, lane_base + PHY_RX_REG_B);

Can we define macros for these values? Just like you did for
PHY_CLK_CFG.

> +
> +		/* Set init done */
> +		val = readl(lane_base + PCIE_PU_ADDR_CLK_CFG);
> +		val = u32_replace_bits(val, 1, CFG_SW_PHY_INIT_DONE);
> +		writel(val, lane_base + PCIE_PU_ADDR_CLK_CFG);
> +	}
> +
> +	ret = readl_poll_timeout(phy_base + PCIE_PU_ADDR_CLK_CFG, val,
> +				 (val & PHY_CLK_PLL_READY), PCIE_POLL_DELAY,
> +				 PCIE_PLL_TIMEOUT);
> +	if (ret) {
> +		dev_err(&lg->phy->dev, "PHY PLL lock timeout\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

WARNING: multiple messages have this Message-ID (diff)
From: "Ze Huang" <huang.ze@linux.dev>
To: "Inochi Amaoto" <inochiama@gmail.com>,
	"Vinod Koul" <vkoul@kernel.org>,
	"Neil Armstrong" <neil.armstrong@linaro.org>,
	"Rob Herring" <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Yixun Lan" <dlan@kernel.org>, "Kees Cook" <kees@kernel.org>,
	"Gustavo A. R. Silva" <gustavoars@kernel.org>,
	"Paul Walmsley" <pjw@kernel.org>,
	"Palmer Dabbelt" <palmer@dabbelt.com>,
	"Albert Ou" <aou@eecs.berkeley.edu>,
	"Alexandre Ghiti" <alex@ghiti.fr>,
	"Ze Huang" <huang.ze@linux.dev>,
	"Alex Elder" <elder@riscstar.com>
Cc: <linux-phy@lists.infradead.org>, <devicetree@vger.kernel.org>,
	<linux-riscv@lists.infradead.org>, <spacemit@lists.linux.dev>,
	<linux-kernel@vger.kernel.org>, <linux-hardening@vger.kernel.org>,
	"Yixun Lan" <dlan@gentoo.org>,
	"Longbin Li" <looong.bin@gmail.com>
Subject: Re: [PATCH 2/2] phy: spacemit: Add USB3/PCIe comb PHY driver for Spacemit K3
Date: Thu, 30 Apr 2026 15:39:51 +0800	[thread overview]
Message-ID: <DI6BHTW4FFN1.2YLR7O1P8F0Y3@linux.dev> (raw)
In-Reply-To: <20260430022843.1090138-3-inochiama@gmail.com>

On Thu Apr 30, 2026 at 10:28 AM CST, Inochi Amaoto wrote:
> The comb PHY on K3 requires to configure a syscon device for the
> right mux configuration. And it requires calibration before any
> usage.
>
> Add USB3/PCIe comb PHY driver for Spacemit K3.
>
> Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
> ---
>  drivers/phy/spacemit/Kconfig          |  16 ++
>  drivers/phy/spacemit/Makefile         |   2 +
>  drivers/phy/spacemit/phy-k3-combphy.c | 250 ++++++++++++++++
>  drivers/phy/spacemit/phy-k3-common.c  | 398 ++++++++++++++++++++++++++
>  drivers/phy/spacemit/phy-k3-common.h  |  27 ++
>  5 files changed, 693 insertions(+)
>  create mode 100644 drivers/phy/spacemit/phy-k3-combphy.c
>  create mode 100644 drivers/phy/spacemit/phy-k3-common.c
>  create mode 100644 drivers/phy/spacemit/phy-k3-common.h
>
> diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig
> index 50b0005acf66..5fdf18fce499 100644
> --- a/drivers/phy/spacemit/Kconfig
> +++ b/drivers/phy/spacemit/Kconfig
> @@ -23,3 +23,19 @@ config PHY_SPACEMIT_K1_USB2
>  	help
>  	  Enable this to support K1 USB 2.0 PHY driver. This driver takes care of
>  	  enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver.
> +
> +config PHY_SPACEMIT_K3_COMMON_OPS
> +	tristate
> +	select MFD_SYSCON
> +	select GENERIC_PHY
> +
> +config PHY_SPACEMIT_K3_COMBO_PHY
> +	tristate "SpacemiT K3 USB3/PCIe PHY support"
> +	depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF
> +	depends on COMMON_CLK
> +	select PHY_SPACEMIT_K3_COMMON_OPS
> +	help
> +	  Enable this to support K3 USB3/PCIe combo PHY driver. This
> +	  driver takes care of enabling and clock setup and will be used
> +	  by K3 dwc3 driver.
> +	  If unsure, say N.
> diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile
> index a821a21d6142..41be7b0388da 100644
> --- a/drivers/phy/spacemit/Makefile
> +++ b/drivers/phy/spacemit/Makefile
> @@ -1,3 +1,5 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE)		+= phy-k1-pcie.o
>  obj-$(CONFIG_PHY_SPACEMIT_K1_USB2)		+= phy-k1-usb2.o
> +obj-$(CONFIG_PHY_SPACEMIT_K3_COMBO_PHY)		+= phy-k3-combphy.o
> +obj-$(CONFIG_PHY_SPACEMIT_K3_COMMON_OPS)	+= phy-k3-common.o
> diff --git a/drivers/phy/spacemit/phy-k3-combphy.c b/drivers/phy/spacemit/phy-k3-combphy.c
> new file mode 100644
> index 000000000000..66fa6330ad6e
> --- /dev/null
> +++ b/drivers/phy/spacemit/phy-k3-combphy.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * phy-k3-usb3.c - SpacemiT K3 Type-C Orientation Switch Driver
> + *
> + * Copyright (c) 2025 SpacemiT Technology Co. Ltd
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/io.h>

...

> +
> +	phy->apb_spare = syscon_regmap_lookup_by_phandle(node, "spacemit,apb-spare");
> +	if (IS_ERR(phy->apb_spare))
> +		return dev_err_probe(dev, PTR_ERR(phy->apb_spare),
> +				     "Failed to fine APB SPARE syscon");

typo, s/fine/find

> +
> +	apmu = syscon_regmap_lookup_by_phandle_args(node, "spacemit,apmu", 1, &config);
> +	if (IS_ERR(apmu))
> +		return dev_err_probe(dev, PTR_ERR(phy->apb_spare),
> +				     "Failed to fine APMU syscon");

1. typo, s/fine/find
2. PTR_ERR(phy->apb_spare) should be PTR_ERR(apmu)

> +
> +	ret = k3_comb_phy_update_config(apmu, config);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to set lane configuration");
> +
> +	phy->dev = dev;
> +	platform_set_drvdata(pdev, phy);
> +
> +	ret = k3_phy_calibrate(phy->apb_spare);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to calibrate phy");
> +
> +	ret = k3_comb_phy_init_lanes(phy, config);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to init lanes");
> +
> +	provider = devm_of_phy_provider_register(dev, k3_comb_phy_xlate);
> +	if (IS_ERR(provider))
> +		return dev_err_probe(dev, PTR_ERR(provider),
> +				     "Failed to register provider\n");
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id k3_comb_phy_of_match[] = {
> +	{ .compatible = "spacemit,k3-comb-phy" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, k3_comb_phy_of_match);
> +
> +static struct platform_driver k3_comb_phy_driver = {
> +	.probe = k3_comb_phy_probe,
> +	.driver = {
> +		.name = "spacemit,k3-comb-phy",
> +		.of_match_table = k3_comb_phy_of_match,
> +	},
> +};
> +module_platform_driver(k3_comb_phy_driver);
> +
> +MODULE_DESCRIPTION("SpacemiT K3 USB3/PCIe comb PHY driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/phy/spacemit/phy-k3-common.c b/drivers/phy/spacemit/phy-k3-common.c
> new file mode 100644
> index 000000000000..77c4b4073b96
> --- /dev/null
> +++ b/drivers/phy/spacemit/phy-k3-common.c
> @@ -0,0 +1,398 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <linux/bitfield.h>
> +#include <linux/cleanup.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/usb.h>
> +
> +#include <dt-bindings/phy/phy.h>
> +
> +#include "phy-k3-common.h"
> +
> +/* PHY Registers */
> +#define PHY_VERSION			0x0
> +
> +#define PHY_RESET_CFG			0x04
> +
> +#define PHY_RESET_RXBUF_RST		BIT(0)
> +#define PHY_RESET_SOFT_RST_PCS		BIT(1)
> +#define PHY_RESET_SOFT_RST_AHB		BIT(2)
> +#define PHY_RESET_EN_SD_AFTER_LOCK	BIT(6)
> +
> +#define PHY_CLK_CFG			0x08
> +
> +#define PHY_CLK_PLL_READY		BIT(0)
> +#define PHY_CLK_TXCLK_INV		BIT(2)
> +#define PHY_CLK_RXCLK_EN		BIT(3)
> +#define PHY_CLK_TXCLK_EN		BIT(4)
> +#define PHY_CLK_PCLK_EN			BIT(5)
> +#define PHY_CLK_PIPE_PCLK_EN		BIT(6)
> +#define PHY_CLK_REFCLK_FREQ		GENMASK(10, 7)
> +#define PHY_CLK_REFCLK_24M		2
> +#define PHY_CLK_SW_INIT_DONE		BIT(11)
> +#define PHY_CLK_PU_SSC_OUT		BIT(23)
> +
> +#define PHY_MODE_CFG			0x0C
> +
> +#define PHY_MODE_PCIE_INT_EN		BIT(0)
> +#define PHY_MODE_LFPS_TPERIOD		GENMASK(9, 8)
> +#define PHY_MODE_LFPS_TPERIOD_USB	3
> +
> +#define PHY_PU_SEL			0x40
> +
> +#define PHY_PU_CFG_STATUS		BIT(9)
> +#define PHY_PU_OVRD_STATUS		BIT(10)
> +
> +#define PHY_PU_CK_REG			0x54
> +
> +#define PHY_PU_REFCLK_100		BIT(25)
> +
> +#define PHY_PLL_REG1			0x58
> +
> +#define PHY_PLL_FREF_SEL		GENMASK(15, 13)
> +#define PHY_PLL_FREF_24M		0x1
> +#define PHY_PLL_SSC_DEP_SEL		GENMASK(27, 24)
> +#define PHY_PLL_SSC_5000PPM		0xa
> +#define PHY_PLL_SSC_MODE		GENMASK(29, 28)
> +#define PHY_PLL_SSC_MODE_CENTER_SPREAD	0
> +#define PHY_PLL_SSC_MODE_UP_SPREAD	1
> +#define PHY_PLL_SSC_MODE_DOWN_SPREAD	2
> +#define PHY_PLL_SSC_MODE_DOWN_SPREAD1	3
> +
> +#define PHY_PLL_REG2			0x5c
> +
> +#define PHY_PLL_SEL_REF100		BIT(21)
> +
> +/* PHY RX Register Definitions */
> +#define PHY_RX_REG_A			0x60
> +
> +#define PHY_RX_REG0_RLOAD		BIT(4)
> +#define PHY_RX_REG1_RTERM		GENMASK(11, 8)
> +#define PHY_RX_REG1_RC_CALI		GENMASK(15, 12)
> +#define PHY_RX_REG2_CSEL		GENMASK(19, 16)
> +#define PHY_RX_REG2_FORCE_CSEL		BIT(20)
> +#define PHY_RX_REG2_PSEL		GENMASK(23, 21)
> +#define PHY_RX_REG3_I_LOAD		GENMASK(26, 24)
> +#define PHY_RX_REG3_SEL_CBOOST_CODE	BIT(27)
> +#define PHY_RX_REG3_ADJ_BIAS		GENMASK(29, 28)
> +#define PHY_RX_REG3_RDEG1		GENMASK(31, 30)
> +
> +#define PHY_RX_REG_B			0x64
> +
> +#define PHY_RX_REGB_MASK		GENMASK(23, 0)
> +
> +#define PHY_RX_REG4_RDEG2		GENMASK(2, 1)
> +#define PHY_RX_REG4_ENVOS		BIT(4)
> +#define PHY_RX_REG4_RTERM_SEL		BIT(5)
> +#define PHY_RX_REG4_MANUAL_CFG		BIT(7)
> +#define PHY_RX_REG5_RCELL_VCM		GENMASK(11, 8)
> +#define PHY_RX_REG5_RCELL_BIAS		GENMASK(15, 12)
> +#define PHY_RX_REG6_H1_REG		GENMASK(19, 16)
> +#define PHY_RX_REG6_ADAPT_GAIN		GENMASK(21, 20)
> +#define PHY_RX_REG6_BYPASS_ADPT		BIT(22)
> +
> +#define PHY_ADPT_CFG0			0x140
> +#define PHY_ADPT_AFE_RST_OVRD_EN	BIT(1)
> +#define PHY_ADPT_AFE_RST_OVRD_VAL	BIT(4)
> +
> +#define PHY_RXEQ_TIME			0xb4
> +#define PHY_RXEQ_TIME_OVRD_POST_C_SOC	BIT(21)
> +#define PHY_RXEQ_TIME_CFG_AMP_SOC	GENMASK(23, 22)
> +#define PHY_RXEQ_TIME_AMP_SOC_650M	0
> +#define PHY_RXEQ_TIME_AMP_SOC_800M	1
> +#define PHY_RXEQ_TIME_AMP_SOC_870M	2
> +#define PHY_RXEQ_TIME_AMP_SOC_900M	3
> +#define PHY_RXEQ_TIME_OVRD_AMP_SOC	BIT(24)
> +
> +#define PCIE_PU_ADDR_CLK_CFG		0x0008
> +#define PHY_CLK_PLL_READY		BIT(0)
> +#define PCIE_INITAL_TIMER		GENMASK(6, 3)
> +#define CFG_INTERNAL_TIMER_ADJ		GENMASK(10, 7)
> +#define CFG_SW_PHY_INIT_DONE		BIT(11)
> +
> +/* Lane RX/TX configuration (per‑lane, at lane_base) */
> +#define PCIE_RX_REG1			0x050
> +#define PCIE_TX_REG1			0x064
> +
> +#define PCIE_PLL_TIMEOUT		500000
> +#define PCIE_POLL_DELAY			500
> +
> +

...

> +static int k3_pcie_phy_init(struct phy *phy)
> +{
> +	struct k3_lane_group *lg = phy_get_drvdata(phy);
> +	void __iomem *phy_base = lg->base + lg->data->offsets[0];
> +	u32 val;
> +	int ret;
> +	int i;
> +
> +	val = readl(phy_base + PHY_PLL_REG1);
> +	val = u32_replace_bits(val, 0x2, GENMASK(15, 12));
> +	writel(val, phy_base + PHY_PLL_REG1);
> +
> +	val = readl(phy_base + PHY_PLL_REG2);
> +	val = u32_replace_bits(val, 0, BIT(21));
> +	writel(val, phy_base + PHY_PLL_REG2);
> +
> +	for (i = 0; i < lg->data->lanes; i++) {
> +		void __iomem *lane_base = lg->base + lg->data->offsets[i];
> +

> +		val = readl(lane_base + PCIE_RX_REG1);
> +		val = u32_replace_bits(val, 0, 0x3);
> +		writel(val, phy_base + PCIE_RX_REG1);

This looks like a copy-paste bug.

Read from lane_base but write the modified value to phy_base.

> +	}
> +
> +	val = readl(phy_base + PHY_PLL_REG2);
> +	val |= BIT(20);
> +	writel(val, phy_base + PHY_PLL_REG2);
> +

> +	writel(0x00006505, phy_base + PCIE_RX_REG1);

Is it intentional? The loop above configured PCIE_RX_REG1, while the
hard-coded 0x00006505 overwrites what's done for lane0.

> +
> +	/* pll_reg1 of lane0, disable SSC: pll_reg4[3:0] = 0 */
> +	val = readl(phy_base + PHY_PLL_REG1);
> +	val = u32_replace_bits(val, 0, GENMASK(27, 24));
> +	writel(val, phy_base + PHY_PLL_REG1);

A little confusing here, comment says "pll_reg4[3:0] = 0" but the code is
modifying PHY_PLL_REG1[27:24]

> +
> +	for (i = 0; i < lg->data->lanes; i++) {
> +		void __iomem *lane_base = lg->base + lg->data->offsets[i];
> +
> +		/* set cfg_tx_send_dummy_data to be 1'b1 for disable dash data */
> +		val = readl(lane_base + PHY_PU_SEL);
> +		val = u32_replace_bits(val, 1, BIT(13));
> +		writel(val, lane_base + PHY_PU_SEL);
> +
> +		/* disable en_sample_data_after_cdr_locked */
> +		val = readl(lane_base + PHY_RESET_CFG);
> +		val = u32_replace_bits(val, 0, BIT(6));
> +		writel(val, lane_base + PHY_RESET_CFG);
> +
> +		/* Dynamic Lock */
> +		val = readl(lane_base + PHY_MODE_CFG);
> +		val = u32_replace_bits(val, 1, BIT(2));
> +		writel(val, lane_base + PHY_MODE_CFG);
> +
> +		val = FIELD_PREP(GENMASK(7, 0), 0x10) |
> +			FIELD_PREP(GENMASK(15, 8), 0x78) |
> +			FIELD_PREP(GENMASK(23, 16), 0x98) |
> +			FIELD_PREP(GENMASK(31, 24), 0xdf);
> +		writel(val, lane_base + PHY_RX_REG_A);
> +
> +		val = readl(lane_base + PHY_RX_REG_B);
> +		val &= ~PHY_RX_REGB_MASK;
> +		val |= FIELD_PREP(GENMASK(7, 0), 0xb4) |
> +			FIELD_PREP(GENMASK(15, 8), 0x88) |
> +			FIELD_PREP(GENMASK(23, 16), 0x28);
> +		writel(val, lane_base + PHY_RX_REG_B);

Can we define macros for these values? Just like you did for
PHY_CLK_CFG.

> +
> +		/* Set init done */
> +		val = readl(lane_base + PCIE_PU_ADDR_CLK_CFG);
> +		val = u32_replace_bits(val, 1, CFG_SW_PHY_INIT_DONE);
> +		writel(val, lane_base + PCIE_PU_ADDR_CLK_CFG);
> +	}
> +
> +	ret = readl_poll_timeout(phy_base + PCIE_PU_ADDR_CLK_CFG, val,
> +				 (val & PHY_CLK_PLL_READY), PCIE_POLL_DELAY,
> +				 PCIE_PLL_TIMEOUT);
> +	if (ret) {
> +		dev_err(&lg->phy->dev, "PHY PLL lock timeout\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

  reply	other threads:[~2026-04-30  7:40 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-30  2:28 [PATCH 0/2] riscv: spacemit: Add K3 PCIe/USB comb phy support Inochi Amaoto
2026-04-30  2:28 ` Inochi Amaoto
2026-04-30  2:28 ` Inochi Amaoto
2026-04-30  2:28 ` [PATCH 1/2] dt-bindings: phy: Add Spacemit K3 USB3/PCIe " Inochi Amaoto
2026-04-30  2:28   ` Inochi Amaoto
2026-04-30  2:28   ` Inochi Amaoto
2026-05-04  9:54   ` Krzysztof Kozlowski
2026-05-04  9:54     ` Krzysztof Kozlowski
2026-05-04 10:20     ` Inochi Amaoto
2026-05-04 10:20       ` Inochi Amaoto
2026-05-04 10:20       ` Inochi Amaoto
2026-04-30  2:28 ` [PATCH 2/2] phy: spacemit: Add USB3/PCIe comb PHY driver for Spacemit K3 Inochi Amaoto
2026-04-30  2:28   ` Inochi Amaoto
2026-04-30  2:28   ` Inochi Amaoto
2026-04-30  7:39   ` Ze Huang [this message]
2026-04-30  7:39     ` Ze Huang
2026-04-30  7:39     ` Ze Huang
2026-05-01  4:05     ` Inochi Amaoto
2026-05-01  4:05       ` Inochi Amaoto
2026-05-01  4:05       ` Inochi Amaoto
2026-05-10 10:44 ` [PATCH 0/2] riscv: spacemit: Add K3 PCIe/USB comb phy support Vinod Koul
2026-05-10 10:44   ` Vinod Koul
2026-05-10 10:44   ` Vinod Koul

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DI6BHTW4FFN1.2YLR7O1P8F0Y3@linux.dev \
    --to=huang.ze@linux.dev \
    --cc=alex@ghiti.fr \
    --cc=aou@eecs.berkeley.edu \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dlan@gentoo.org \
    --cc=dlan@kernel.org \
    --cc=elder@riscstar.com \
    --cc=gustavoars@kernel.org \
    --cc=inochiama@gmail.com \
    --cc=kees@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-phy@lists.infradead.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=looong.bin@gmail.com \
    --cc=neil.armstrong@linaro.org \
    --cc=palmer@dabbelt.com \
    --cc=pjw@kernel.org \
    --cc=robh@kernel.org \
    --cc=spacemit@lists.linux.dev \
    --cc=vkoul@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.