lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <valmrbddij2dn4fjxefr46zud2u6eco2isyaa62sd66d27foyl@4hrhafqftgb5>
Date: Thu, 14 Aug 2025 07:42:48 +0800
From: Inochi Amaoto <inochiama@...il.com>
To: Alex Elder <elder@...cstar.com>, lpieralisi@...nel.org, 
	kwilczynski@...nel.org, mani@...nel.org, robh@...nel.org, bhelgaas@...gle.com, 
	krzk+dt@...nel.org, conor+dt@...nel.org, vkoul@...nel.org, kishon@...nel.org
Cc: dlan@...too.org, paul.walmsley@...ive.com, palmer@...belt.com, 
	aou@...s.berkeley.edu, alex@...ti.fr, p.zabel@...gutronix.de, tglx@...utronix.de, 
	johan+linaro@...nel.org, thippeswamy.havalige@....com, namcao@...utronix.de, 
	mayank.rana@....qualcomm.com, shradha.t@...sung.com, inochiama@...il.com, 
	quic_schintav@...cinc.com, fan.ni@...sung.com, devicetree@...r.kernel.org, 
	linux-phy@...ts.infradead.org, linux-pci@...r.kernel.org, spacemit@...ts.linux.dev, 
	linux-riscv@...ts.infradead.org, linux-kernel@...r.kernel.org, 
	Junzhong Pan <panjunzhong@...ux.spacemit.com>
Subject: Re: [PATCH 4/6] phy: spacemit: introduce PCIe/combo PHY

On Wed, Aug 13, 2025 at 01:46:58PM -0500, Alex Elder wrote:
> Introduce a driver that supports three PHYs found on the SpacemiT
> K1 SoC.  The first PHY is a combo PHY that can be configured for
> use for either USB 3 or PCIe.  The other two PHYs support PCIe
> only.
> 
> All three PHYs must be programmed with an 8 bit receiver termination
> value, which must be determined dynamically; only the combo PHY is
> able to determine this value.  The combo PHY performs a special
> calibration step at probe time to discover this, and that value is
> used to program each PHY that operates in PCIe mode.  The combo
> PHY must therefore be probed--first--if either of the PCIe-only
> PHYs will be used.
> 
> During normal operation, the USB or PCIe driver using the PHY must
> ensure clocks and resets are set up properly.  However clocks are
> enabled and resets are de-asserted temporarily by this driver to
> perform the calibration step on the combo PHY.
> 
> Tested-by: Junzhong Pan <panjunzhong@...ux.spacemit.com>
> Signed-off-by: Alex Elder <elder@...cstar.com>
> ---
>  drivers/phy/Kconfig                |  11 +
>  drivers/phy/Makefile               |   1 +
>  drivers/phy/phy-spacemit-k1-pcie.c | 639 +++++++++++++++++++++++++++++
>  3 files changed, 651 insertions(+)
>  create mode 100644 drivers/phy/phy-spacemit-k1-pcie.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 58c911e1b2d20..0fa343203f289 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -101,6 +101,17 @@ config PHY_NXP_PTN3222
>  	  schemes. It supports all three USB 2.0 data rates: Low Speed, Full
>  	  Speed and High Speed.
>  
> +config PHY_SPACEMIT_K1_PCIE
> +	tristate "PCIe and combo PHY driver for the SpacemiT K1 SoC"
> +	depends on ARCH_SPACEMIT || COMPILE_TEST
> +	depends on HAS_IOMEM
> +	depends on OF
> +	select GENERIC_PHY
> +	default ARCH_SPACEMIT
> +	help
> +	  Enable support for the PCIe and USB 3 combo PHY and two
> +	  PCIe-only PHYs used in the SpacemiT K1 SoC.
> +
>  source "drivers/phy/allwinner/Kconfig"
>  source "drivers/phy/amlogic/Kconfig"
>  source "drivers/phy/broadcom/Kconfig"
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index c670a8dac4680..20f0078e543c7 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -13,6 +13,7 @@ obj-$(CONFIG_PHY_SNPS_EUSB2)		+= phy-snps-eusb2.o
>  obj-$(CONFIG_USB_LGM_PHY)		+= phy-lgm-usb.o
>  obj-$(CONFIG_PHY_AIROHA_PCIE)		+= phy-airoha-pcie.o
>  obj-$(CONFIG_PHY_NXP_PTN3222)		+= phy-nxp-ptn3222.o
> +obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE)	+= phy-spacemit-k1-pcie.o
>  obj-y					+= allwinner/	\
>  					   amlogic/	\
>  					   broadcom/	\
> diff --git a/drivers/phy/phy-spacemit-k1-pcie.c b/drivers/phy/phy-spacemit-k1-pcie.c
> new file mode 100644
> index 0000000000000..32dce53170fbb
> --- /dev/null
> +++ b/drivers/phy/phy-spacemit-k1-pcie.c
> @@ -0,0 +1,639 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * SpacemiT K1 PCIe and PCIe/USB 3 combo PHY driver
> + *
> + * Copyright (C) 2025 by RISCstar Solutions Corporation.  All rights reserved.
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include <dt-bindings/phy/phy.h>
> +
> +/*
> + * Three PCIe ports are supported in the SpacemiT K1 SoC, and this driver
> + * supports their PHYs.
> + *
> + * The PHY for PCIe port A is different from the PHYs for ports B and C:
> + * - It has one lane, while ports B and C have two
> + * - It is a combo PHY can be used for PCIe or USB 3
> + * - It can automatically calibrate PCIe TX and RX termination settings
> + *
> + * The PHY functionality for PCIe ports B and C is identical:
> + * - They have two PCIe lanes (but can be restricted to 1 via Device Tree)
> + * - They are used for PCIe only
> + * - They are configured using TX and RX values computed for port A
> + *
> + * A given board is designed to use the combo PHY for either PCIe or USB 3.
> + * Whether the combo PHY is configured for PCIe or USB 3 is specified in
> + * Device Tree using a phandle plus an argument.  The argument indicates
> + * the type (either PHY_TYPE_PCIE or PHY_TYPE_USB3).
> + *
> + * Each PHY depends on clocks and resets provided by the controller
> + * hardware (PCIe or USB) it is associated with.  The controller drivers
> + * are required to enable any clocks and de-assert any resets that affect
> + * PHY operation.
> + *
> + * PCIe PHYs must be programmed with RX and TX calibration values.  The
> + * combo PHY is the only one that can determine these values.  They are
> + * determined by temporarily enabling the combo PHY in PCIe mode at probe
> + * time (if necessary).  This calibration only needs to be done once, and
> + * when it has completed the TX and RX values are saved.
> + *
> + * To allow the combo PHY to be enabled for calibration, the resets and
> + * clocks it uses in PCIe mode must be supplied.
> + */
> +
> +struct k1_pcie_phy {
> +	struct device *dev;		/* PHY provider device */
> +	struct phy *phy;
> +	void __iomem *regs;
> +	u32 pcie_lanes;			/* Max unless limited by DT */
> +	/* The remaining fields are only used for the combo PHY */
> +	u32 type;			/* PHY_TYPE_PCIE or PHY_TYPE_USB3 */
> +	struct regmap *pmu;
> +};
> +
> +#define CALIBRATION_TIMEOUT		500000	/* microseconds */
> +#define PLL_TIMEOUT			500000	/* microseconds */
> +#define POLL_DELAY			500	/* microseconds */
> +
> +/* Selecting the combo PHY operating mode requires PMU regmap access */
> +#define SYSCON_PMU			"spacemit,syscon-pmu"
> +
> +/* PMU space, for selecting between PCIe and USB3 mode on the combo PHY */
> +
> +#define PMUA_USB_PHY_CTRL0			0x0110
> +#define COMBO_PHY_SEL			BIT(3)	/* 0: PCIe; 1: USB3 */
> +
> +#define PCIE_CLK_RES_CTRL			0x03cc
> +#define PCIE_APP_HOLD_PHY_RST		BIT(30)
> +
> +/* PHY register space */
> +
> +/* Offset between lane 0 and lane 1 registers when there are two */
> +#define PHY_LANE_OFFSET				0x0400
> +
> +#define PCIE_PU_ADDR_CLK_CFG			0x0008
> +#define PLL_READY			BIT(0)		/* read-only */
> +#define CFG_RXCLK_EN			BIT(3)
> +#define CFG_TXCLK_EN			BIT(4)
> +#define CFG_PCLK_EN			BIT(5)
> +#define CFG_PIPE_PCLK_EN		BIT(6)
> +#define CFG_INTERNAL_TIMER_ADJ		GENMASK(10, 7)
> +#define TIMER_ADJ_USB		0x2
> +#define TIMER_ADJ_PCIE		0x6
> +#define CFG_SW_PHY_INIT_DONE		BIT(11)	/* We set after PLL config */
> +
> +#define PCIE_RC_DONE_STATUS			0x0018
> +#define CFG_FORCE_RCV_RETRY		BIT(10)
> +
> +#define PCIE_RC_CAL_REG2			0x0020
> +#define RC_CAL_TOGGLE			BIT(22)
> +#define CLKSEL				GENMASK(31, 29)
> +#define CLKSEL_24M		0x3
> +
> +#define PCIE_PU_PLL_1				0x0048
> +#define REF_100_WSSC			BIT(12)	/* 1: input is 100MHz, SSC */
> +#define FREF_SEL			GENMASK(15, 13)
> +#define FREF_24M		0x1
> +#define SSC_DEP_SEL			GENMASK(19, 16)
> +#define SSC_DEP_NONE		0x0
> +#define SSC_DEP_5000PPM		0xa
> +#define SSC_MODE			GENMASK(21, 20)
> +#define SSC_MODE_DOWN_SPREAD	0x3
> +#define SSC_OFFSET			GENMASK(23, 22)
> +#define SSC_OFFSET_0_PPM	0x0
> +
> +#define PCIE_PU_PLL_2				0x004c
> +#define GEN_REF100			BIT(4)	/* 1: generate 100MHz clk */
> +
> +#define PCIE_RX_REG1				0x0050
> +#define EN_RTERM			BIT(3)
> +#define AFE_RTERM_REG			GENMASK(11, 8)
> +
> +#define PCIE_RX_REG2				0x0054
> +#define RX_RTERM_SEL			BIT(5)	/* 0: use AFE_RTERM_REG value */
> +
> +#define PCIE_LTSSM_DIS_ENTRY			0x005c
> +#define CFG_REFCLK_MODE			GENMASK(9, 8)
> +#define RFCLK_MODE_DRIVER	0x1
> +#define OVRD_REFCLK_MODE		BIT(10)	/* 1: use CFG_RFCLK_MODE */
> +
> +#define PCIE_TX_REG1				0x0064
> +#define TX_RTERM_REG			GENMASK(15, 12)
> +#define TX_RTERM_SEL			BIT(25)	/* 1: use TX_RTERM_REG */
> +
> +#define USB3_TEST_CTRL				0x0068
> +
> +#define PCIE_RCAL_RESULT			0x0084	/* Port A PHY only */
> +#define RTERM_VALUE_RX			GENMASK(3, 0)
> +#define RTERM_VALUE_TX			GENMASK(7, 4)
> +#define R_TUNE_DONE			BIT(10)
> +
> +static u32 k1_phy_rterm = ~0;     /* Invalid initial value */
> +
> +/* Save the RX and TX receiver termination values */
> +static void k1_phy_rterm_set(u32 val)
> +{
> +	k1_phy_rterm = val & (RTERM_VALUE_RX | RTERM_VALUE_TX);
> +}
> +
> +static bool k1_phy_rterm_valid(void)
> +{
> +	/* Valid if no bits outside those we care about are set */
> +	return !(k1_phy_rterm & ~(RTERM_VALUE_RX | RTERM_VALUE_TX));
> +}
> +
> +static u32 k1_phy_rterm_rx(void)
> +{
> +	return FIELD_GET(RTERM_VALUE_RX, k1_phy_rterm);
> +}
> +
> +static u32 k1_phy_rterm_tx(void)
> +{
> +	return FIELD_GET(RTERM_VALUE_TX, k1_phy_rterm);
> +}
> +
> +/* Only the combo PHY has a PMU pointer defined */
> +static bool k1_phy_port_a(struct k1_pcie_phy *k1_phy)
> +{
> +	return !!k1_phy->pmu;
> +}
> +
> +/*
> + * Select PCIe or USB 3 mode for the combo PHY.  Return 1 if the bit
> + * was changed, 0 if it was not, or a negative error value otherwise.
> + */
> +static int k1_combo_phy_sel(struct k1_pcie_phy *k1_phy, bool usb3)
> +{
> +	int ret;
> +
> +	ret = regmap_test_bits(k1_phy->pmu, PMUA_USB_PHY_CTRL0, COMBO_PHY_SEL);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* If it's already in the desired state, we're done */
> +	if (!!ret == usb3)
> +		return 0;
> +
> +	/* Change the bit */
> +	ret = regmap_assign_bits(k1_phy->pmu, PMUA_USB_PHY_CTRL0,
> +				 COMBO_PHY_SEL, usb3);
> +
> +	return ret < 0 ? ret : 1;
> +}
> +

> +static void k1_pcie_phy_init_pll(struct k1_pcie_phy *k1_phy,
> +				 void __iomem *regs, bool pcie)
> +{
> +	void __iomem *virt;
> +	u32 timer_adj;
> +	u32 ssc_dep;
> +	u32 val;
> +
> +	if (pcie) {
> +		timer_adj = TIMER_ADJ_PCIE;
> +		ssc_dep = SSC_DEP_NONE;
> +	} else {
> +		timer_adj = TIMER_ADJ_USB;
> +		ssc_dep = SSC_DEP_5000PPM;
> +	}
> +
> +	/*
> +	 * Disable 100 MHz input reference with spread-spectrum
> +	 * clocking and select the 24 MHz clock input frequency
> +	 */
> +	virt = k1_phy->regs + PCIE_PU_PLL_1;
> +	val = readl(virt);
> +	val &= ~REF_100_WSSC;
> +
> +	val &= ~FREF_SEL;
> +	val |= FIELD_PREP(FREF_SEL, FREF_24M);
> +
> +	val &= ~SSC_DEP_SEL;
> +	val |= FIELD_PREP(SSC_DEP_SEL, ssc_dep);
> +
> +	val &= ~SSC_MODE;
> +	val |= FIELD_PREP(SSC_MODE, SSC_MODE_DOWN_SPREAD);
> +
> +	val &= ~SSC_OFFSET;
> +	val |= FIELD_PREP(SSC_OFFSET, SSC_OFFSET_0_PPM);
> +	writel(val, virt);
> +
> +	if (pcie) {
> +		virt = regs + PCIE_PU_PLL_2;
> +		val = readl(virt);
> +		val |= GEN_REF100;	/* Enable 100 MHz PLL output clock */
> +		writel(val, virt);
> +	}
> +
> +	/* Enable clocks and mark PLL initialization done */
> +	virt = regs + PCIE_PU_ADDR_CLK_CFG;
> +	val = readl(virt);
> +	val |= CFG_RXCLK_EN;
> +	val |= CFG_TXCLK_EN;
> +	val |= CFG_PCLK_EN;
> +	val |= CFG_PIPE_PCLK_EN;
> +
> +	val &= ~CFG_INTERNAL_TIMER_ADJ;
> +	val |= FIELD_PREP(CFG_INTERNAL_TIMER_ADJ, timer_adj);
> +
> +	val |= CFG_SW_PHY_INIT_DONE;
> +	writel(val, virt);
> +}
> +
> +static int k1_pcie_pll_lock(struct k1_pcie_phy *k1_phy, bool pcie)
> +{
> +	u32 val = pcie ? CFG_FORCE_RCV_RETRY : 0;
> +	void __iomem *virt;
> +
> +	writel(val, k1_phy->regs + PCIE_RC_DONE_STATUS);
> +
> +	/*
> +	 * Wait for indication the PHY PLL is locked.  Lanes for ports
> +	 * B and C share a PLL, so it's enough to sample just lane 0.
> +	 */
> +	virt = k1_phy->regs + PCIE_PU_ADDR_CLK_CFG;	/* Lane 0 */
> +
> +	return readl_poll_timeout(virt, val, val & PLL_READY,
> +				  POLL_DELAY, PLL_TIMEOUT);
> +}
> +

Can we use standard clk_ops and clk_mux to normalize this process?

Regards,
Inochi

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ