* [PATCH 0/2] PHY: Add MediaTek PCI-Express Gen4 S-PHY Driver @ 2026-07-16 15:13 ` AngeloGioacchino Del Regno 0 siblings, 0 replies; 10+ messages in thread From: AngeloGioacchino Del Regno @ 2026-07-16 15:13 UTC (permalink / raw) To: chunfeng.yun Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno, linux-arm-kernel, linux-mediatek, linux-phy, devicetree, linux-kernel, kernel This adds a driver for the PCI-Express Gen4 "S-PHY" found in the Genio MT8894, Kompanio MT8196, Dimensity MT6991 SoCs (which are all variants of the same chip). This was successfully tested on MT8894 and MT8196. AngeloGioacchino Del Regno (2): dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY phy: mediatek: Add support for PCI-Express Gen4 S-PHY .../phy/mediatek,mt8196-pcie-sphy.yaml | 91 +++ drivers/phy/mediatek/Kconfig | 9 + drivers/phy/mediatek/Makefile | 1 + drivers/phy/mediatek/phy-mtk-pcie-sphy.c | 560 ++++++++++++++++++ 4 files changed, 661 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml create mode 100644 drivers/phy/mediatek/phy-mtk-pcie-sphy.c -- 2.54.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 0/2] PHY: Add MediaTek PCI-Express Gen4 S-PHY Driver @ 2026-07-16 15:13 ` AngeloGioacchino Del Regno 0 siblings, 0 replies; 10+ messages in thread From: AngeloGioacchino Del Regno @ 2026-07-16 15:13 UTC (permalink / raw) To: chunfeng.yun Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno, linux-arm-kernel, linux-mediatek, linux-phy, devicetree, linux-kernel, kernel This adds a driver for the PCI-Express Gen4 "S-PHY" found in the Genio MT8894, Kompanio MT8196, Dimensity MT6991 SoCs (which are all variants of the same chip). This was successfully tested on MT8894 and MT8196. AngeloGioacchino Del Regno (2): dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY phy: mediatek: Add support for PCI-Express Gen4 S-PHY .../phy/mediatek,mt8196-pcie-sphy.yaml | 91 +++ drivers/phy/mediatek/Kconfig | 9 + drivers/phy/mediatek/Makefile | 1 + drivers/phy/mediatek/phy-mtk-pcie-sphy.c | 560 ++++++++++++++++++ 4 files changed, 661 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml create mode 100644 drivers/phy/mediatek/phy-mtk-pcie-sphy.c -- 2.54.0 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/2] dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY 2026-07-16 15:13 ` AngeloGioacchino Del Regno @ 2026-07-16 15:13 ` AngeloGioacchino Del Regno -1 siblings, 0 replies; 10+ messages in thread From: AngeloGioacchino Del Regno @ 2026-07-16 15:13 UTC (permalink / raw) To: chunfeng.yun Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno, linux-arm-kernel, linux-mediatek, linux-phy, devicetree, linux-kernel, kernel This adds bindings for the PCI-Express Gen4 S-PHY found in newer MediaTek SoCs, such as MT8196 and its variants. In the current "revision 3", depending on the specific port, this S-PHY supports up to two lanes of PCI-Express Gen 4 and both EP and RC modes. It is not clear whether revisions/versions earlier than 3 have ever been shipped in any other MediaTek SoC. Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> --- .../phy/mediatek,mt8196-pcie-sphy.yaml | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml diff --git a/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml b/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml new file mode 100644 index 000000000000..2b61882c402c --- /dev/null +++ b/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/mediatek,mt8196-pcie-sphy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek PCI-Express Gen4 S-PHY + +maintainers: + - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> + +description: + The MediaTek PCI-Express Gen4 S-PHY is capable of data serialization + and de-serialization with 8, 10, 128 or 130 bits (16GT/s) encoding + and decoding, has analog buffers, elastic buffers and mechanisms for + receiver detection. + This PHY supports link rates of 2.5, 5.0, 6.0 and 16.0 GT/s per-lane + in both Root Complex (RC) and End Point (EP) modes, depending on the + chosen port. + The Digital PHY (PHYD) part adheres to the Intel PIPE (PHY Interface + for the PCIe) specification. + +properties: + compatible: + const: mediatek,mt8196-pcie-sphy + + reg: + minItems: 2 + + reg-names: + items: + - const: sif + - const: ckm + + "#phy-cells": + const: 0 + + nvmem-cells: + items: + - description: Internal resistor selection of TX Bias Current + - description: XTP Bias V2V voltage calibration + - description: S-PLL CKTX Impedance Selection (P-MOSFET side) + - description: S-PLL CKTX Impedance Selection (N-MOSFET side) + - description: S-PLL CKTX Intermediate Transition Impedance + - description: RX Front-End Return Loss Continuous Time Linear EQ + - description: RX Front-End Return Loss Variable Gain Amplifier + - description: RX Front-End Return Loss Decision Feedback EQ + - description: RX Impedance Selection + - description: TX Impedance Selection (P-MOSFET side) + - description: TX Impedance Selection (N-MOSFET side) + - description: TX Lane 0 Switch Resistance (RSWn) + - description: TX Lane 1 Switch Resistance (RSWn) + - description: eFuse calibration table version + + nvmem-cell-names: + items: + - const: int-r + - const: xtp-vtrim + - const: cktx-pmos + - const: cktx-nmos + - const: cktx-r-mid + - const: rxfe-lanes-rl-ctle + - const: rxfe-lanes-rl-vga + - const: rxfe-lanes-rl-dfe + - const: rx-lanes-imp + - const: tx-lanes-pmos + - const: tx-lanes-nmos + - const: tx-ln0-rswn + - const: tx-ln1-rswn + - const: cal-version + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@16900000 { + compatible = "mediatek,mt8196-pcie-sphy"; + reg = <0x16900000 0x10000>, <0x16920000 0x10000>; + reg-names = "sif", "ckm"; + power-domains = <&spm 10>; + #phy-cells = <0>; + }; -- 2.54.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 1/2] dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY @ 2026-07-16 15:13 ` AngeloGioacchino Del Regno 0 siblings, 0 replies; 10+ messages in thread From: AngeloGioacchino Del Regno @ 2026-07-16 15:13 UTC (permalink / raw) To: chunfeng.yun Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno, linux-arm-kernel, linux-mediatek, linux-phy, devicetree, linux-kernel, kernel This adds bindings for the PCI-Express Gen4 S-PHY found in newer MediaTek SoCs, such as MT8196 and its variants. In the current "revision 3", depending on the specific port, this S-PHY supports up to two lanes of PCI-Express Gen 4 and both EP and RC modes. It is not clear whether revisions/versions earlier than 3 have ever been shipped in any other MediaTek SoC. Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> --- .../phy/mediatek,mt8196-pcie-sphy.yaml | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml diff --git a/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml b/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml new file mode 100644 index 000000000000..2b61882c402c --- /dev/null +++ b/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/mediatek,mt8196-pcie-sphy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek PCI-Express Gen4 S-PHY + +maintainers: + - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> + +description: + The MediaTek PCI-Express Gen4 S-PHY is capable of data serialization + and de-serialization with 8, 10, 128 or 130 bits (16GT/s) encoding + and decoding, has analog buffers, elastic buffers and mechanisms for + receiver detection. + This PHY supports link rates of 2.5, 5.0, 6.0 and 16.0 GT/s per-lane + in both Root Complex (RC) and End Point (EP) modes, depending on the + chosen port. + The Digital PHY (PHYD) part adheres to the Intel PIPE (PHY Interface + for the PCIe) specification. + +properties: + compatible: + const: mediatek,mt8196-pcie-sphy + + reg: + minItems: 2 + + reg-names: + items: + - const: sif + - const: ckm + + "#phy-cells": + const: 0 + + nvmem-cells: + items: + - description: Internal resistor selection of TX Bias Current + - description: XTP Bias V2V voltage calibration + - description: S-PLL CKTX Impedance Selection (P-MOSFET side) + - description: S-PLL CKTX Impedance Selection (N-MOSFET side) + - description: S-PLL CKTX Intermediate Transition Impedance + - description: RX Front-End Return Loss Continuous Time Linear EQ + - description: RX Front-End Return Loss Variable Gain Amplifier + - description: RX Front-End Return Loss Decision Feedback EQ + - description: RX Impedance Selection + - description: TX Impedance Selection (P-MOSFET side) + - description: TX Impedance Selection (N-MOSFET side) + - description: TX Lane 0 Switch Resistance (RSWn) + - description: TX Lane 1 Switch Resistance (RSWn) + - description: eFuse calibration table version + + nvmem-cell-names: + items: + - const: int-r + - const: xtp-vtrim + - const: cktx-pmos + - const: cktx-nmos + - const: cktx-r-mid + - const: rxfe-lanes-rl-ctle + - const: rxfe-lanes-rl-vga + - const: rxfe-lanes-rl-dfe + - const: rx-lanes-imp + - const: tx-lanes-pmos + - const: tx-lanes-nmos + - const: tx-ln0-rswn + - const: tx-ln1-rswn + - const: cal-version + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@16900000 { + compatible = "mediatek,mt8196-pcie-sphy"; + reg = <0x16900000 0x10000>, <0x16920000 0x10000>; + reg-names = "sif", "ckm"; + power-domains = <&spm 10>; + #phy-cells = <0>; + }; -- 2.54.0 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY 2026-07-16 15:13 ` AngeloGioacchino Del Regno @ 2026-07-16 15:21 ` sashiko-bot -1 siblings, 0 replies; 10+ messages in thread From: sashiko-bot @ 2026-07-16 15:21 UTC (permalink / raw) To: AngeloGioacchino Del Regno Cc: devicetree, olteanv, neil.armstrong, robh, conor+dt, linux-phy, vkoul Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] Missing upper bound (`maxItems`) for the `reg` property array. -- --- Patch [1]: [PATCH 1/2] dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY --- commit 46cb0bc5744a1aaeece570f9d383cc948bbcab79 Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY This commit introduces the device tree binding documentation for the PCI-Express Gen4 S-PHY found in newer MediaTek SoCs like the MT8196. It details the required properties, nvmem cells, and power domains for this hardware. > diff --git a/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml b/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml > new file mode 100644 > index 0000000000000..2b61882c402c2 > --- /dev/null > +++ b/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml [ ... ] > +properties: > + compatible: > + const: mediatek,mt8196-pcie-sphy > + > + reg: > + minItems: 2 > + > + reg-names: > + items: > + - const: sif > + - const: ckm [Severity: Low] Is an upper bound missing for the reg property array here? The schema defines minItems: 2 but omits maxItems. Since reg-names explicitly defines exactly two items, should reg include maxItems: 2 as well to fully bound the array and prevent potential dt_binding_check validation warnings? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260716151333.77254-1-angelogioacchino.delregno@collabora.com?part=1 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY @ 2026-07-16 15:21 ` sashiko-bot 0 siblings, 0 replies; 10+ messages in thread From: sashiko-bot @ 2026-07-16 15:21 UTC (permalink / raw) To: AngeloGioacchino Del Regno Cc: devicetree, olteanv, neil.armstrong, robh, conor+dt, linux-phy, vkoul Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] Missing upper bound (`maxItems`) for the `reg` property array. -- --- Patch [1]: [PATCH 1/2] dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY --- commit 46cb0bc5744a1aaeece570f9d383cc948bbcab79 Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY This commit introduces the device tree binding documentation for the PCI-Express Gen4 S-PHY found in newer MediaTek SoCs like the MT8196. It details the required properties, nvmem cells, and power domains for this hardware. > diff --git a/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml b/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml > new file mode 100644 > index 0000000000000..2b61882c402c2 > --- /dev/null > +++ b/Documentation/devicetree/bindings/phy/mediatek,mt8196-pcie-sphy.yaml [ ... ] > +properties: > + compatible: > + const: mediatek,mt8196-pcie-sphy > + > + reg: > + minItems: 2 > + > + reg-names: > + items: > + - const: sif > + - const: ckm [Severity: Low] Is an upper bound missing for the reg property array here? The schema defines minItems: 2 but omits maxItems. Since reg-names explicitly defines exactly two items, should reg include maxItems: 2 as well to fully bound the array and prevent potential dt_binding_check validation warnings? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260716151333.77254-1-angelogioacchino.delregno@collabora.com?part=1 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/2] phy: mediatek: Add support for PCI-Express Gen4 S-PHY 2026-07-16 15:13 ` AngeloGioacchino Del Regno @ 2026-07-16 15:13 ` AngeloGioacchino Del Regno -1 siblings, 0 replies; 10+ messages in thread From: AngeloGioacchino Del Regno @ 2026-07-16 15:13 UTC (permalink / raw) To: chunfeng.yun Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno, linux-arm-kernel, linux-mediatek, linux-phy, devicetree, linux-kernel, kernel Add support for the PCI-Express Gen4 S-PHY found in the latest MediaTek SoCs, including MT8196, MT8894 and similar. Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> --- drivers/phy/mediatek/Kconfig | 9 + drivers/phy/mediatek/Makefile | 1 + drivers/phy/mediatek/phy-mtk-pcie-sphy.c | 560 +++++++++++++++++++++++ 3 files changed, 570 insertions(+) create mode 100644 drivers/phy/mediatek/phy-mtk-pcie-sphy.c diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index ba6461350951..77236f3084c8 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -13,6 +13,15 @@ config PHY_MTK_PCIE callback for PCIe GEN3 port, it supports software efuse initialization. +config PHY_MTK_PCIE_SPHY + tristate "MediaTek PCIe S-PHY Driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF + select GENERIC_PHY + help + Say 'Y' here to add support for MediaTek PCIe S-PHY driver for + PCI-Express Gen4 controllers as found in MT6991, MT8196 and others. + config PHY_MTK_XFI_TPHY tristate "MediaTek 10GE SerDes XFI T-PHY driver" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index ed0da708759b..7e984c7cfea3 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_PHY_MTK_DP) += phy-mtk-dp.o obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-pcie.o +obj-$(CONFIG_PHY_MTK_PCIE_SPHY) += phy-mtk-pcie-sphy.o obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o diff --git a/drivers/phy/mediatek/phy-mtk-pcie-sphy.c b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c new file mode 100644 index 000000000000..c794505060a8 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 MediaTek Inc. + * Copyright (c) 2026 Collabora Ltd. + * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> + */ + +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/nvmem-consumer.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "phy-mtk-io.h" + +/* PHY System Interface (SIF) registers */ +#define PEXTP_DIG_GLB_TOP 0x20 +# define RG_XTP_BYPASS_PIPE_RST_RC BIT(17) +#define PEXTP_DIG_GLB_CKBG0 0x30 +# define RG_XTP_CKBG_XTAL_STABLE_TIME_SEL GENMASK(25, 16) +#define PEXTP_DIG_GLB_TPLL_CTL0 0x38 +# define RG_XTP_TPLL_SET_STABLE_TIME_SEL GENMASK(7, 2) +# define RG_XTP_TPLL_PWE_ON_STABLE_TIME_SEL GENMASK(9, 8) +#define PEXTP_DIG_GLB_CLKREQ_CTL 0x50 +# define RG_XTP_CKM_EN_L1S0 BIT(13) +# define RG_XTP_CKM_EN_L1S1 BIT(14) +#define PEXTP_DIG_GLB_TPLL_CTL2 0xf4 +# define RG_XTP_TPLL_ISO_EN_STABLE_TIME_SEL GENMASK(13, 12) + +/* PHY System Interface Digital registers */ +#define PEXTP_DIG_LN_TRX_PIPE_IF_17 0x30e8 +# define RG_XTP_LN_RX_LF_CTLE_CSEL_GEN4 GENMASK(14, 12) +#define PEXTP_DIG_LN_RX_F0 0x50f0 +# define RG_XTP_LN_RX_GEN1_CTLE1_CSEL GENMASK(3, 0) +# define RG_XTP_LN_RX_GEN2_CTLE1_CSEL GENMASK(7, 4) +# define RG_XTP_LN_RX_GEN3_CTLE1_CSEL GENMASK(11, 8) +# define RG_XTP_LN_RX_GEN4_CTLE1_CSEL GENMASK(15, 12) +#define PEXTP_DIG_LN_RX2_AEQ_EDGE_0 0x6004 +# define RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN3 GENMASK(21, 16) +# define RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN4 GENMASK(29, 24) +# define AEQ_EGEQ_RATIO_GEN3_TO_22 0x16 +# define AEQ_EGEQ_RATIO_GEN4_TO_22 0x16 + +/* PHY System Interface Analog registers */ +#define PEXTP_ANA_GLB_TPLL1_RSVD 0x902c +# define RG_XTP_GLB_TPLL1_P_PATH_GAIN GENMASK(2, 0) +#define PEXTP_ANA_GLB_BIAS_0 0x9060 +# define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(5, 0) +#define PEXTP_ANA_GLB_BIAS_1 0x90c0 +# define RG_XTP_GLB_BIAS_V2V_VTRIM GENMASK(9, 6) +#define PEXTP_ANA_LN_TRX_0C 0xa00c +# define RG_XTP_LN_TX_RSWN_IMPSEL GENMASK(20, 16) +#define PEXTP_ANA_LN_TRX_34 0xa034 +# define RG_XTP_LN_RX_FE BIT(15) +#define PEXTP_ANA_LN_TRX_6C 0xa06c +# define RG_XTP_LN_RX_AEQ_CTLE_ERR_TYPE GENMASK(14, 13) +# define AEQ_CTLE_SEARCH_ERR_TYPE_H1P5 0 +# define AEQ_CTLE_SEARCH_ERR_TYPE_H1P5_H2P5 1 +# define AEQ_CTLE_SEARCH_ERR_TYPE_P1P5_H2P5_H3P5 2 +#define PEXTP_ANA_LN_TRX_A0 0xa0a0 +# define RG_XTP_LN_TX_IMPSEL_PMOS GENMASK(4, 0) +# define RG_XTP_LN_TX_IMPSEL_NMOS GENMASK(11, 7) +# define RG_XTP_LN_RX_IMPSEL GENMASK(15, 12) +#define PEXTP_ANA_LN_TRX_A8 0xa0a8 +# define RG_XTP_LN_RX_LEQ_RL_CTLE_CAL GENMASK(6, 2) +# define RG_XTP_LN_RX_LEQ_RL_VGA_CAL GENMASK(11, 7) +# define RG_XTP_LN_RX_LEQ_RL_DFE_CAL GENMASK(23, 19) +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4 0xb004 +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8 0xb008 +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_C 0xb00c +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10 0xb010 +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14 0xb014 +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18 0xb018 +# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1 GENMASK(5, 0) +# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0 GENMASK(13, 8) +# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1 GENMASK(21, 16) +#define PEXTP_REG_LANE(x) ((x) * 0x100) + +/* PHY Clock Management (CKM) registers */ +#define XTP_CKM_FORCE_6 0x38 +# define RG_CKM_BIAS_WAIT_PRD_US GENMASK(21, 16) +#define XTP_CKM_REG_SPLL_FBKDIV_5 0xd4 +# define RG_CKM_CKTX_IMPSEL_PMOS GENMASK(19, 16) +# define RG_CKM_CKTX_IMPSEL_NMOS GENMASK(23, 20) +# define RG_CKM_CKTX_IMPSEL_SW GENMASK(27, 24) + +/* Calibration data from eFuses */ +#define MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES 2 +#define MTK_PCIE_SPHY_CALIBRATION_LAST_QUIRK_VER 4 + +/** + * struct mtk_pcie_sphy_imp_sel - Impedance Selection parameters + * @pmos: Impedance selection for P-Channel MOSFET + * @nmos: Impedance selection for N-Channel MOSFET + */ +struct mtk_pcie_sphy_imp_sel { + u8 pmos : 4; + u8 nmos : 4; +}; + +/** + * struct mtk_pcie_sphy_efuse - eFuse calibration data for S-PHY + * @int_r_ctrl: Internal resistor selection of TX Bias Current + * @xtp_vtrim: XTP Bias V2V voltage calibration + * @cktx_impsel: SPLL CKTX Impedance Selection (P and N MOSFET) + * @cktx_r_mid: SPLL CKTX Intermediate Transition Impedance (Rmid) + * @rx_leq_rl_ctle: RX Front-End Return Loss Continuous Time Linear Equalization value + * @rx_leq_rl_vga: RX Front-End Return Loss Variable Gain Amplifier value + * @rx_leq_rl_dfe: RX Front-End Return Loss Decision Feedback Equalization value + * @rx_impsel: RX Impedance Selection + * @tx_impsel: TX Impedance Selection (P and N MOSFET) + * @tx_rswn_impsel: TX RSWn (Switch Resistance) impedance selection + * @supported: eFuse calibration data is supported + */ +struct mtk_pcie_sphy_efuse { + u8 int_r_ctrl; + u8 xtp_vtrim; + struct mtk_pcie_sphy_imp_sel cktx_impsel; + u8 cktx_r_mid; + u8 rx_leq_rl_ctle; + u8 rx_leq_rl_vga; + u8 rx_leq_rl_dfe; + u8 rx_impsel; + struct mtk_pcie_sphy_imp_sel tx_impsel; + u8 tx_rswn_impsel[MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES]; + bool supported; +}; + +/** + * struct mtk_pcie_sphy - PCI-Express S-PHY driver main structure + * @dev: Pointer to device structure + * @phy: Pointer to generic phy structure + * @sif_base: IO mapped register base address of system interface + * @ckm_base: IO mapped register base address of clock management interface + * @num_lanes: Number of lanes + * @calibration: eFuse calibration data for S-PHY + */ +struct mtk_pcie_sphy { + struct device *dev; + struct phy *phy; + void __iomem *sif_base; + void __iomem *ckm_base; + u8 num_lanes; + struct mtk_pcie_sphy_efuse calibration; +}; + +static void mtk_pcie_sphy_apply_calibration(struct mtk_pcie_sphy *pcie_sphy) +{ + struct mtk_pcie_sphy_efuse *cal = &pcie_sphy->calibration; + int i; + + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_BIAS_0, + RG_XTP_GLB_BIAS_INTR_CTRL, cal->int_r_ctrl); + + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_BIAS_1, + RG_XTP_GLB_BIAS_V2V_VTRIM, cal->xtp_vtrim); + + mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5, + RG_CKM_CKTX_IMPSEL_PMOS, cal->cktx_impsel.pmos); + + mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5, + RG_CKM_CKTX_IMPSEL_NMOS, cal->cktx_impsel.nmos); + + mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5, + RG_CKM_CKTX_IMPSEL_SW, cal->cktx_r_mid); + + for (i = 0; i < pcie_sphy->num_lanes; i++) { + void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_0C, + RG_XTP_LN_TX_RSWN_IMPSEL, cal->tx_rswn_impsel[i]); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8, + RG_XTP_LN_RX_LEQ_RL_CTLE_CAL, cal->rx_leq_rl_ctle); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8, + RG_XTP_LN_RX_LEQ_RL_VGA_CAL, cal->rx_leq_rl_vga); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8, + RG_XTP_LN_RX_LEQ_RL_DFE_CAL, cal->rx_leq_rl_dfe); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_RX_IMPSEL, cal->rx_impsel); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_TX_IMPSEL_PMOS, cal->tx_impsel.pmos); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_TX_IMPSEL_NMOS, cal->tx_impsel.nmos); + } +} + +/** + * mtk_pcie_sphy_init() - Initialize the PCI-Express S-PHY + * @phy: the phy to be initialized + * + * The hardware settings will be reset during suspend, it should be + * reinitialized when the consumer calls phy_init() again on resume. + */ +static int mtk_pcie_sphy_init(struct phy *phy) +{ + struct mtk_pcie_sphy *pcie_sphy = phy_get_drvdata(phy); + struct mtk_pcie_sphy_imp_sel tx_impsel; + int i; + + /* Set CKM Bias wait time to 4 microseconds */ + mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_FORCE_6, + RG_CKM_BIAS_WAIT_PRD_US, 4); + + /* TPLL needs 63 ref_ck ticks to stabilize when setting frequency */ + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL0, + RG_XTP_TPLL_SET_STABLE_TIME_SEL, 63); + + /* TPLL needs 3 ref_ck ticks to stabilize when powering on... */ + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL0, + RG_XTP_TPLL_PWE_ON_STABLE_TIME_SEL, 3); + + /* ...and the same goes for setting isolation */ + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL2, + RG_XTP_TPLL_ISO_EN_STABLE_TIME_SEL, 3); + + /* XTAL doesn't need any stabilization time */ + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_CKBG0, + RG_XTP_CKBG_XTAL_STABLE_TIME_SEL, 0); + + /* Keep pextp_ckm enabled when in L1SS_L1S1 state */ + mtk_phy_clear_bits(pcie_sphy->sif_base + PEXTP_DIG_GLB_CLKREQ_CTL, RG_XTP_CKM_EN_L1S1); + + /* Set PIPE to reset TPLL */ + mtk_phy_clear_bits(pcie_sphy->sif_base + PEXTP_DIG_GLB_TOP, RG_XTP_BYPASS_PIPE_RST_RC); + + /* Set TPLL P-Path gain compensation to 1 */ + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_TPLL1_RSVD, + RG_XTP_GLB_TPLL1_P_PATH_GAIN, 1); + + for (i = 0; i < pcie_sphy->num_lanes; i++) { + void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i); + + /* Set RX Lane AEQ CTRL-E Search Error type to h1.5 + h2.5 */ + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_6C, + RG_XTP_LN_RX_AEQ_CTLE_ERR_TYPE, + AEQ_CTLE_SEARCH_ERR_TYPE_H1P5_H2P5); + + mtk_phy_set_bits(sif_lane_base + PEXTP_ANA_LN_TRX_34, RG_XTP_LN_RX_FE); + + /* TRX: Select CTLE1 for RX Lane AutoEQ CTRL-E Setting on Gen4 */ + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TRX_PIPE_IF_17, + RG_XTP_LN_RX_LF_CTLE_CSEL_GEN4, 1); + + /* Set RX Lane AutoEQ CTRL-E for PCI-Express Gen1 to Gen 4 */ + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0, + RG_XTP_LN_RX_GEN1_CTLE1_CSEL, 13); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0, + RG_XTP_LN_RX_GEN2_CTLE1_CSEL, 13); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0, + RG_XTP_LN_RX_GEN3_CTLE1_CSEL, 13); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0, + RG_XTP_LN_RX_GEN4_CTLE1_CSEL, 0); + + /* Set RX Lane AutoEQ's Edge EQ Ratio to 22 * 0.0625 = 1.375 */ + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX2_AEQ_EDGE_0, + RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN3, 22); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX2_AEQ_EDGE_0, + RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN4, 22); + + /* Setup Digital lane TX Link Characteristics Table */ + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 10); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1, 2); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 11); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1, 1); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_C, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 12); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 13); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 1); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 11); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 1); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 10); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 2); + } + + if (pcie_sphy->calibration.supported) { + mtk_pcie_sphy_apply_calibration(pcie_sphy); + + tx_impsel.pmos = pcie_sphy->calibration.tx_impsel.pmos; + tx_impsel.nmos = pcie_sphy->calibration.tx_impsel.nmos; + } else { + /* Set P=10, N=9 to prevent EMI if no calibration present */ + tx_impsel.pmos = 10; + tx_impsel.nmos = 9; + } + + /* Select TX Impedance on N and P MOSFETs */ + for (i = 0; i < pcie_sphy->num_lanes; i++) { + void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_TX_IMPSEL_PMOS, tx_impsel.pmos); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_TX_IMPSEL_NMOS, tx_impsel.nmos); + } + + return 0; +} + +static const struct phy_ops mtk_pcie_sphy_ops = { + .init = mtk_pcie_sphy_init, + .owner = THIS_MODULE, +}; + +static int mtk_pcie_sphy_get_one_cal_para(struct device *dev, const char *name, u8 max_val) +{ + u16 buf; + u8 tmp; + int ret; + + /* + * All of the calibrations are always max 8 bits long, but some may + * be split between two different 8-bits cells: handle this corner + * case by retrying reading as u16. + */ + ret = nvmem_cell_read_u8(dev, name, &tmp); + if (ret == 0) + buf = tmp; + else + ret = nvmem_cell_read_u16(dev, name, &buf); + + if (ret) { + dev_err(dev, "Cannot get calibration data for %s: %d\n", name, ret); + return ret; + }; + + if (buf == 0) { + dev_warn(dev, "No calibration for %s. Using defaults\n", name); + return -ENOENT; + } + + if (buf > max_val) { + dev_err(dev, "Bad value %u retrieved for %s. Returning.\n", buf, name); + return -ERANGE; + }; + + return buf; +} + +static int mtk_pcie_sphy_get_calibration_data(struct mtk_pcie_sphy *pcie_sphy) +{ + struct mtk_pcie_sphy_efuse *cal = &pcie_sphy->calibration; + struct device *dev = pcie_sphy->dev; + u8 version; + int ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "int-r", + FIELD_MAX(RG_XTP_GLB_BIAS_INTR_CTRL)); + if (ret < 0) + goto end; + cal->int_r_ctrl = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "xtp-vtrim", + FIELD_MAX(RG_XTP_GLB_BIAS_V2V_VTRIM)); + if (ret < 0) + goto end; + cal->xtp_vtrim = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-pmos", + FIELD_MAX(RG_CKM_CKTX_IMPSEL_PMOS)); + if (ret < 0) + goto end; + cal->cktx_impsel.pmos = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-nmos", + FIELD_MAX(RG_CKM_CKTX_IMPSEL_NMOS)); + if (ret < 0) + goto end; + cal->cktx_impsel.nmos = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-r-mid", + FIELD_MAX(RG_CKM_CKTX_IMPSEL_SW)); + if (ret < 0) + goto end; + cal->cktx_r_mid = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-ctle", + FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_CTLE_CAL)); + if (ret < 0) + goto end; + cal->rx_leq_rl_ctle = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-vga", + FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_VGA_CAL)); + if (ret < 0) + goto end; + cal->rx_leq_rl_vga = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-dfe", + FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_DFE_CAL)); + if (ret < 0) + goto end; + cal->rx_leq_rl_dfe = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "rx-lanes-imp", + FIELD_MAX(RG_XTP_LN_RX_IMPSEL)); + if (ret < 0) + goto end; + cal->rx_impsel = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-lanes-pmos", + FIELD_MAX(RG_XTP_LN_TX_IMPSEL_PMOS)); + if (ret < 0) + goto end; + cal->tx_impsel.pmos = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-lanes-nmos", + FIELD_MAX(RG_XTP_LN_TX_IMPSEL_NMOS)); + if (ret < 0) + goto end; + cal->tx_impsel.nmos = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln0-rswn", + FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL)); + if (ret < 0) + goto end; + cal->tx_rswn_impsel[0] = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln1-rswn", + FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL)); + if (ret < 0) + goto end; + cal->tx_rswn_impsel[1] = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "cal-version", 15); + if (ret < 0) + goto end; + version = ret; + + /* Quirk for eFuse calibration table versions 0 to 4 */ + if ((version <= MTK_PCIE_SPHY_CALIBRATION_LAST_QUIRK_VER) && + cal->rx_leq_rl_ctle == 10) { + cal->rx_leq_rl_vga = cal->rx_leq_rl_ctle; + cal->rx_leq_rl_dfe = cal->rx_leq_rl_ctle; + } + +end: + if (ret < 0) { + /* + * If any of the calibration values is missing, or if there is + * no calibration at all in the eFuses, this is not a problem, + * as the PHY doesn't require one to actually work. + */ + if (ret == -ENOENT) { + cal->supported = false; + return 0; + } + return ret; + }; + cal->supported = true; + + return 0; +} + +static int mtk_pcie_sphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct mtk_pcie_sphy *pcie_sphy; + u32 num_lanes; + int ret; + + pcie_sphy = devm_kzalloc(dev, sizeof(*pcie_sphy), GFP_KERNEL); + if (!pcie_sphy) + return -ENOMEM; + + pcie_sphy->sif_base = devm_platform_ioremap_resource_byname(pdev, "sif"); + if (IS_ERR(pcie_sphy->sif_base)) + return dev_err_probe(dev, PTR_ERR(pcie_sphy->sif_base), + "Failed to map phy-sif base\n"); + + pcie_sphy->ckm_base = devm_platform_ioremap_resource_byname(pdev, "ckm"); + if (IS_ERR(pcie_sphy->ckm_base)) + return dev_err_probe(dev, PTR_ERR(pcie_sphy->ckm_base), + "Failed to map phy-ckm base\n"); + + pcie_sphy->phy = devm_phy_create(dev, dev->of_node, &mtk_pcie_sphy_ops); + if (IS_ERR(pcie_sphy->phy)) + return dev_err_probe(dev, PTR_ERR(pcie_sphy->phy), + "Failed to create PCIe phy\n"); + + ret = of_property_read_u32(dev->of_node, "num-lanes", &num_lanes); + if (ret) + num_lanes = 1; + else if (num_lanes > 4) + return dev_err_probe(dev, -EINVAL, "Invalid number of lanes.\n"); + + pcie_sphy->num_lanes = num_lanes; + pcie_sphy->dev = dev; + + if (pcie_sphy->num_lanes <= MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES) { + ret = mtk_pcie_sphy_get_calibration_data(pcie_sphy); + if (ret) + return ret; + } + + phy_set_drvdata(pcie_sphy->phy, pcie_sphy); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) + return dev_err_probe(dev, PTR_ERR(provider), + "Could not register PCI-Express S-PHY\n"); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + return 0; +} + +static const struct of_device_id mtk_pcie_sphy_of_match[] = { + { .compatible = "mediatek,mt8196-pcie-sphy" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mtk_pcie_sphy_of_match); + +static struct platform_driver mtk_pcie_sphy_driver = { + .probe = mtk_pcie_sphy_probe, + .driver = { + .name = "mtk-pcie-sphy", + .of_match_table = mtk_pcie_sphy_of_match, + }, +}; +module_platform_driver(mtk_pcie_sphy_driver); + +MODULE_DESCRIPTION("MediaTek PCIe SPHY driver"); +MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>"); +MODULE_LICENSE("GPL"); -- 2.54.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/2] phy: mediatek: Add support for PCI-Express Gen4 S-PHY @ 2026-07-16 15:13 ` AngeloGioacchino Del Regno 0 siblings, 0 replies; 10+ messages in thread From: AngeloGioacchino Del Regno @ 2026-07-16 15:13 UTC (permalink / raw) To: chunfeng.yun Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno, linux-arm-kernel, linux-mediatek, linux-phy, devicetree, linux-kernel, kernel Add support for the PCI-Express Gen4 S-PHY found in the latest MediaTek SoCs, including MT8196, MT8894 and similar. Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> --- drivers/phy/mediatek/Kconfig | 9 + drivers/phy/mediatek/Makefile | 1 + drivers/phy/mediatek/phy-mtk-pcie-sphy.c | 560 +++++++++++++++++++++++ 3 files changed, 570 insertions(+) create mode 100644 drivers/phy/mediatek/phy-mtk-pcie-sphy.c diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index ba6461350951..77236f3084c8 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -13,6 +13,15 @@ config PHY_MTK_PCIE callback for PCIe GEN3 port, it supports software efuse initialization. +config PHY_MTK_PCIE_SPHY + tristate "MediaTek PCIe S-PHY Driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF + select GENERIC_PHY + help + Say 'Y' here to add support for MediaTek PCIe S-PHY driver for + PCI-Express Gen4 controllers as found in MT6991, MT8196 and others. + config PHY_MTK_XFI_TPHY tristate "MediaTek 10GE SerDes XFI T-PHY driver" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index ed0da708759b..7e984c7cfea3 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_PHY_MTK_DP) += phy-mtk-dp.o obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-pcie.o +obj-$(CONFIG_PHY_MTK_PCIE_SPHY) += phy-mtk-pcie-sphy.o obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o diff --git a/drivers/phy/mediatek/phy-mtk-pcie-sphy.c b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c new file mode 100644 index 000000000000..c794505060a8 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 MediaTek Inc. + * Copyright (c) 2026 Collabora Ltd. + * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> + */ + +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/nvmem-consumer.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "phy-mtk-io.h" + +/* PHY System Interface (SIF) registers */ +#define PEXTP_DIG_GLB_TOP 0x20 +# define RG_XTP_BYPASS_PIPE_RST_RC BIT(17) +#define PEXTP_DIG_GLB_CKBG0 0x30 +# define RG_XTP_CKBG_XTAL_STABLE_TIME_SEL GENMASK(25, 16) +#define PEXTP_DIG_GLB_TPLL_CTL0 0x38 +# define RG_XTP_TPLL_SET_STABLE_TIME_SEL GENMASK(7, 2) +# define RG_XTP_TPLL_PWE_ON_STABLE_TIME_SEL GENMASK(9, 8) +#define PEXTP_DIG_GLB_CLKREQ_CTL 0x50 +# define RG_XTP_CKM_EN_L1S0 BIT(13) +# define RG_XTP_CKM_EN_L1S1 BIT(14) +#define PEXTP_DIG_GLB_TPLL_CTL2 0xf4 +# define RG_XTP_TPLL_ISO_EN_STABLE_TIME_SEL GENMASK(13, 12) + +/* PHY System Interface Digital registers */ +#define PEXTP_DIG_LN_TRX_PIPE_IF_17 0x30e8 +# define RG_XTP_LN_RX_LF_CTLE_CSEL_GEN4 GENMASK(14, 12) +#define PEXTP_DIG_LN_RX_F0 0x50f0 +# define RG_XTP_LN_RX_GEN1_CTLE1_CSEL GENMASK(3, 0) +# define RG_XTP_LN_RX_GEN2_CTLE1_CSEL GENMASK(7, 4) +# define RG_XTP_LN_RX_GEN3_CTLE1_CSEL GENMASK(11, 8) +# define RG_XTP_LN_RX_GEN4_CTLE1_CSEL GENMASK(15, 12) +#define PEXTP_DIG_LN_RX2_AEQ_EDGE_0 0x6004 +# define RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN3 GENMASK(21, 16) +# define RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN4 GENMASK(29, 24) +# define AEQ_EGEQ_RATIO_GEN3_TO_22 0x16 +# define AEQ_EGEQ_RATIO_GEN4_TO_22 0x16 + +/* PHY System Interface Analog registers */ +#define PEXTP_ANA_GLB_TPLL1_RSVD 0x902c +# define RG_XTP_GLB_TPLL1_P_PATH_GAIN GENMASK(2, 0) +#define PEXTP_ANA_GLB_BIAS_0 0x9060 +# define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(5, 0) +#define PEXTP_ANA_GLB_BIAS_1 0x90c0 +# define RG_XTP_GLB_BIAS_V2V_VTRIM GENMASK(9, 6) +#define PEXTP_ANA_LN_TRX_0C 0xa00c +# define RG_XTP_LN_TX_RSWN_IMPSEL GENMASK(20, 16) +#define PEXTP_ANA_LN_TRX_34 0xa034 +# define RG_XTP_LN_RX_FE BIT(15) +#define PEXTP_ANA_LN_TRX_6C 0xa06c +# define RG_XTP_LN_RX_AEQ_CTLE_ERR_TYPE GENMASK(14, 13) +# define AEQ_CTLE_SEARCH_ERR_TYPE_H1P5 0 +# define AEQ_CTLE_SEARCH_ERR_TYPE_H1P5_H2P5 1 +# define AEQ_CTLE_SEARCH_ERR_TYPE_P1P5_H2P5_H3P5 2 +#define PEXTP_ANA_LN_TRX_A0 0xa0a0 +# define RG_XTP_LN_TX_IMPSEL_PMOS GENMASK(4, 0) +# define RG_XTP_LN_TX_IMPSEL_NMOS GENMASK(11, 7) +# define RG_XTP_LN_RX_IMPSEL GENMASK(15, 12) +#define PEXTP_ANA_LN_TRX_A8 0xa0a8 +# define RG_XTP_LN_RX_LEQ_RL_CTLE_CAL GENMASK(6, 2) +# define RG_XTP_LN_RX_LEQ_RL_VGA_CAL GENMASK(11, 7) +# define RG_XTP_LN_RX_LEQ_RL_DFE_CAL GENMASK(23, 19) +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4 0xb004 +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8 0xb008 +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_C 0xb00c +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10 0xb010 +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14 0xb014 +#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18 0xb018 +# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1 GENMASK(5, 0) +# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0 GENMASK(13, 8) +# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1 GENMASK(21, 16) +#define PEXTP_REG_LANE(x) ((x) * 0x100) + +/* PHY Clock Management (CKM) registers */ +#define XTP_CKM_FORCE_6 0x38 +# define RG_CKM_BIAS_WAIT_PRD_US GENMASK(21, 16) +#define XTP_CKM_REG_SPLL_FBKDIV_5 0xd4 +# define RG_CKM_CKTX_IMPSEL_PMOS GENMASK(19, 16) +# define RG_CKM_CKTX_IMPSEL_NMOS GENMASK(23, 20) +# define RG_CKM_CKTX_IMPSEL_SW GENMASK(27, 24) + +/* Calibration data from eFuses */ +#define MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES 2 +#define MTK_PCIE_SPHY_CALIBRATION_LAST_QUIRK_VER 4 + +/** + * struct mtk_pcie_sphy_imp_sel - Impedance Selection parameters + * @pmos: Impedance selection for P-Channel MOSFET + * @nmos: Impedance selection for N-Channel MOSFET + */ +struct mtk_pcie_sphy_imp_sel { + u8 pmos : 4; + u8 nmos : 4; +}; + +/** + * struct mtk_pcie_sphy_efuse - eFuse calibration data for S-PHY + * @int_r_ctrl: Internal resistor selection of TX Bias Current + * @xtp_vtrim: XTP Bias V2V voltage calibration + * @cktx_impsel: SPLL CKTX Impedance Selection (P and N MOSFET) + * @cktx_r_mid: SPLL CKTX Intermediate Transition Impedance (Rmid) + * @rx_leq_rl_ctle: RX Front-End Return Loss Continuous Time Linear Equalization value + * @rx_leq_rl_vga: RX Front-End Return Loss Variable Gain Amplifier value + * @rx_leq_rl_dfe: RX Front-End Return Loss Decision Feedback Equalization value + * @rx_impsel: RX Impedance Selection + * @tx_impsel: TX Impedance Selection (P and N MOSFET) + * @tx_rswn_impsel: TX RSWn (Switch Resistance) impedance selection + * @supported: eFuse calibration data is supported + */ +struct mtk_pcie_sphy_efuse { + u8 int_r_ctrl; + u8 xtp_vtrim; + struct mtk_pcie_sphy_imp_sel cktx_impsel; + u8 cktx_r_mid; + u8 rx_leq_rl_ctle; + u8 rx_leq_rl_vga; + u8 rx_leq_rl_dfe; + u8 rx_impsel; + struct mtk_pcie_sphy_imp_sel tx_impsel; + u8 tx_rswn_impsel[MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES]; + bool supported; +}; + +/** + * struct mtk_pcie_sphy - PCI-Express S-PHY driver main structure + * @dev: Pointer to device structure + * @phy: Pointer to generic phy structure + * @sif_base: IO mapped register base address of system interface + * @ckm_base: IO mapped register base address of clock management interface + * @num_lanes: Number of lanes + * @calibration: eFuse calibration data for S-PHY + */ +struct mtk_pcie_sphy { + struct device *dev; + struct phy *phy; + void __iomem *sif_base; + void __iomem *ckm_base; + u8 num_lanes; + struct mtk_pcie_sphy_efuse calibration; +}; + +static void mtk_pcie_sphy_apply_calibration(struct mtk_pcie_sphy *pcie_sphy) +{ + struct mtk_pcie_sphy_efuse *cal = &pcie_sphy->calibration; + int i; + + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_BIAS_0, + RG_XTP_GLB_BIAS_INTR_CTRL, cal->int_r_ctrl); + + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_BIAS_1, + RG_XTP_GLB_BIAS_V2V_VTRIM, cal->xtp_vtrim); + + mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5, + RG_CKM_CKTX_IMPSEL_PMOS, cal->cktx_impsel.pmos); + + mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5, + RG_CKM_CKTX_IMPSEL_NMOS, cal->cktx_impsel.nmos); + + mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5, + RG_CKM_CKTX_IMPSEL_SW, cal->cktx_r_mid); + + for (i = 0; i < pcie_sphy->num_lanes; i++) { + void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_0C, + RG_XTP_LN_TX_RSWN_IMPSEL, cal->tx_rswn_impsel[i]); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8, + RG_XTP_LN_RX_LEQ_RL_CTLE_CAL, cal->rx_leq_rl_ctle); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8, + RG_XTP_LN_RX_LEQ_RL_VGA_CAL, cal->rx_leq_rl_vga); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8, + RG_XTP_LN_RX_LEQ_RL_DFE_CAL, cal->rx_leq_rl_dfe); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_RX_IMPSEL, cal->rx_impsel); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_TX_IMPSEL_PMOS, cal->tx_impsel.pmos); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_TX_IMPSEL_NMOS, cal->tx_impsel.nmos); + } +} + +/** + * mtk_pcie_sphy_init() - Initialize the PCI-Express S-PHY + * @phy: the phy to be initialized + * + * The hardware settings will be reset during suspend, it should be + * reinitialized when the consumer calls phy_init() again on resume. + */ +static int mtk_pcie_sphy_init(struct phy *phy) +{ + struct mtk_pcie_sphy *pcie_sphy = phy_get_drvdata(phy); + struct mtk_pcie_sphy_imp_sel tx_impsel; + int i; + + /* Set CKM Bias wait time to 4 microseconds */ + mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_FORCE_6, + RG_CKM_BIAS_WAIT_PRD_US, 4); + + /* TPLL needs 63 ref_ck ticks to stabilize when setting frequency */ + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL0, + RG_XTP_TPLL_SET_STABLE_TIME_SEL, 63); + + /* TPLL needs 3 ref_ck ticks to stabilize when powering on... */ + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL0, + RG_XTP_TPLL_PWE_ON_STABLE_TIME_SEL, 3); + + /* ...and the same goes for setting isolation */ + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL2, + RG_XTP_TPLL_ISO_EN_STABLE_TIME_SEL, 3); + + /* XTAL doesn't need any stabilization time */ + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_CKBG0, + RG_XTP_CKBG_XTAL_STABLE_TIME_SEL, 0); + + /* Keep pextp_ckm enabled when in L1SS_L1S1 state */ + mtk_phy_clear_bits(pcie_sphy->sif_base + PEXTP_DIG_GLB_CLKREQ_CTL, RG_XTP_CKM_EN_L1S1); + + /* Set PIPE to reset TPLL */ + mtk_phy_clear_bits(pcie_sphy->sif_base + PEXTP_DIG_GLB_TOP, RG_XTP_BYPASS_PIPE_RST_RC); + + /* Set TPLL P-Path gain compensation to 1 */ + mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_TPLL1_RSVD, + RG_XTP_GLB_TPLL1_P_PATH_GAIN, 1); + + for (i = 0; i < pcie_sphy->num_lanes; i++) { + void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i); + + /* Set RX Lane AEQ CTRL-E Search Error type to h1.5 + h2.5 */ + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_6C, + RG_XTP_LN_RX_AEQ_CTLE_ERR_TYPE, + AEQ_CTLE_SEARCH_ERR_TYPE_H1P5_H2P5); + + mtk_phy_set_bits(sif_lane_base + PEXTP_ANA_LN_TRX_34, RG_XTP_LN_RX_FE); + + /* TRX: Select CTLE1 for RX Lane AutoEQ CTRL-E Setting on Gen4 */ + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TRX_PIPE_IF_17, + RG_XTP_LN_RX_LF_CTLE_CSEL_GEN4, 1); + + /* Set RX Lane AutoEQ CTRL-E for PCI-Express Gen1 to Gen 4 */ + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0, + RG_XTP_LN_RX_GEN1_CTLE1_CSEL, 13); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0, + RG_XTP_LN_RX_GEN2_CTLE1_CSEL, 13); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0, + RG_XTP_LN_RX_GEN3_CTLE1_CSEL, 13); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0, + RG_XTP_LN_RX_GEN4_CTLE1_CSEL, 0); + + /* Set RX Lane AutoEQ's Edge EQ Ratio to 22 * 0.0625 = 1.375 */ + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX2_AEQ_EDGE_0, + RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN3, 22); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX2_AEQ_EDGE_0, + RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN4, 22); + + /* Setup Digital lane TX Link Characteristics Table */ + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 10); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1, 2); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 11); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1, 1); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_C, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 12); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 13); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 1); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 11); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 1); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 10); + + mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18, + RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 2); + } + + if (pcie_sphy->calibration.supported) { + mtk_pcie_sphy_apply_calibration(pcie_sphy); + + tx_impsel.pmos = pcie_sphy->calibration.tx_impsel.pmos; + tx_impsel.nmos = pcie_sphy->calibration.tx_impsel.nmos; + } else { + /* Set P=10, N=9 to prevent EMI if no calibration present */ + tx_impsel.pmos = 10; + tx_impsel.nmos = 9; + } + + /* Select TX Impedance on N and P MOSFETs */ + for (i = 0; i < pcie_sphy->num_lanes; i++) { + void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_TX_IMPSEL_PMOS, tx_impsel.pmos); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_TX_IMPSEL_NMOS, tx_impsel.nmos); + } + + return 0; +} + +static const struct phy_ops mtk_pcie_sphy_ops = { + .init = mtk_pcie_sphy_init, + .owner = THIS_MODULE, +}; + +static int mtk_pcie_sphy_get_one_cal_para(struct device *dev, const char *name, u8 max_val) +{ + u16 buf; + u8 tmp; + int ret; + + /* + * All of the calibrations are always max 8 bits long, but some may + * be split between two different 8-bits cells: handle this corner + * case by retrying reading as u16. + */ + ret = nvmem_cell_read_u8(dev, name, &tmp); + if (ret == 0) + buf = tmp; + else + ret = nvmem_cell_read_u16(dev, name, &buf); + + if (ret) { + dev_err(dev, "Cannot get calibration data for %s: %d\n", name, ret); + return ret; + }; + + if (buf == 0) { + dev_warn(dev, "No calibration for %s. Using defaults\n", name); + return -ENOENT; + } + + if (buf > max_val) { + dev_err(dev, "Bad value %u retrieved for %s. Returning.\n", buf, name); + return -ERANGE; + }; + + return buf; +} + +static int mtk_pcie_sphy_get_calibration_data(struct mtk_pcie_sphy *pcie_sphy) +{ + struct mtk_pcie_sphy_efuse *cal = &pcie_sphy->calibration; + struct device *dev = pcie_sphy->dev; + u8 version; + int ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "int-r", + FIELD_MAX(RG_XTP_GLB_BIAS_INTR_CTRL)); + if (ret < 0) + goto end; + cal->int_r_ctrl = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "xtp-vtrim", + FIELD_MAX(RG_XTP_GLB_BIAS_V2V_VTRIM)); + if (ret < 0) + goto end; + cal->xtp_vtrim = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-pmos", + FIELD_MAX(RG_CKM_CKTX_IMPSEL_PMOS)); + if (ret < 0) + goto end; + cal->cktx_impsel.pmos = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-nmos", + FIELD_MAX(RG_CKM_CKTX_IMPSEL_NMOS)); + if (ret < 0) + goto end; + cal->cktx_impsel.nmos = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-r-mid", + FIELD_MAX(RG_CKM_CKTX_IMPSEL_SW)); + if (ret < 0) + goto end; + cal->cktx_r_mid = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-ctle", + FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_CTLE_CAL)); + if (ret < 0) + goto end; + cal->rx_leq_rl_ctle = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-vga", + FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_VGA_CAL)); + if (ret < 0) + goto end; + cal->rx_leq_rl_vga = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-dfe", + FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_DFE_CAL)); + if (ret < 0) + goto end; + cal->rx_leq_rl_dfe = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "rx-lanes-imp", + FIELD_MAX(RG_XTP_LN_RX_IMPSEL)); + if (ret < 0) + goto end; + cal->rx_impsel = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-lanes-pmos", + FIELD_MAX(RG_XTP_LN_TX_IMPSEL_PMOS)); + if (ret < 0) + goto end; + cal->tx_impsel.pmos = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-lanes-nmos", + FIELD_MAX(RG_XTP_LN_TX_IMPSEL_NMOS)); + if (ret < 0) + goto end; + cal->tx_impsel.nmos = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln0-rswn", + FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL)); + if (ret < 0) + goto end; + cal->tx_rswn_impsel[0] = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln1-rswn", + FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL)); + if (ret < 0) + goto end; + cal->tx_rswn_impsel[1] = ret; + + ret = mtk_pcie_sphy_get_one_cal_para(dev, "cal-version", 15); + if (ret < 0) + goto end; + version = ret; + + /* Quirk for eFuse calibration table versions 0 to 4 */ + if ((version <= MTK_PCIE_SPHY_CALIBRATION_LAST_QUIRK_VER) && + cal->rx_leq_rl_ctle == 10) { + cal->rx_leq_rl_vga = cal->rx_leq_rl_ctle; + cal->rx_leq_rl_dfe = cal->rx_leq_rl_ctle; + } + +end: + if (ret < 0) { + /* + * If any of the calibration values is missing, or if there is + * no calibration at all in the eFuses, this is not a problem, + * as the PHY doesn't require one to actually work. + */ + if (ret == -ENOENT) { + cal->supported = false; + return 0; + } + return ret; + }; + cal->supported = true; + + return 0; +} + +static int mtk_pcie_sphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct mtk_pcie_sphy *pcie_sphy; + u32 num_lanes; + int ret; + + pcie_sphy = devm_kzalloc(dev, sizeof(*pcie_sphy), GFP_KERNEL); + if (!pcie_sphy) + return -ENOMEM; + + pcie_sphy->sif_base = devm_platform_ioremap_resource_byname(pdev, "sif"); + if (IS_ERR(pcie_sphy->sif_base)) + return dev_err_probe(dev, PTR_ERR(pcie_sphy->sif_base), + "Failed to map phy-sif base\n"); + + pcie_sphy->ckm_base = devm_platform_ioremap_resource_byname(pdev, "ckm"); + if (IS_ERR(pcie_sphy->ckm_base)) + return dev_err_probe(dev, PTR_ERR(pcie_sphy->ckm_base), + "Failed to map phy-ckm base\n"); + + pcie_sphy->phy = devm_phy_create(dev, dev->of_node, &mtk_pcie_sphy_ops); + if (IS_ERR(pcie_sphy->phy)) + return dev_err_probe(dev, PTR_ERR(pcie_sphy->phy), + "Failed to create PCIe phy\n"); + + ret = of_property_read_u32(dev->of_node, "num-lanes", &num_lanes); + if (ret) + num_lanes = 1; + else if (num_lanes > 4) + return dev_err_probe(dev, -EINVAL, "Invalid number of lanes.\n"); + + pcie_sphy->num_lanes = num_lanes; + pcie_sphy->dev = dev; + + if (pcie_sphy->num_lanes <= MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES) { + ret = mtk_pcie_sphy_get_calibration_data(pcie_sphy); + if (ret) + return ret; + } + + phy_set_drvdata(pcie_sphy->phy, pcie_sphy); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) + return dev_err_probe(dev, PTR_ERR(provider), + "Could not register PCI-Express S-PHY\n"); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + return 0; +} + +static const struct of_device_id mtk_pcie_sphy_of_match[] = { + { .compatible = "mediatek,mt8196-pcie-sphy" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mtk_pcie_sphy_of_match); + +static struct platform_driver mtk_pcie_sphy_driver = { + .probe = mtk_pcie_sphy_probe, + .driver = { + .name = "mtk-pcie-sphy", + .of_match_table = mtk_pcie_sphy_of_match, + }, +}; +module_platform_driver(mtk_pcie_sphy_driver); + +MODULE_DESCRIPTION("MediaTek PCIe SPHY driver"); +MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>"); +MODULE_LICENSE("GPL"); -- 2.54.0 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] phy: mediatek: Add support for PCI-Express Gen4 S-PHY 2026-07-16 15:13 ` AngeloGioacchino Del Regno @ 2026-07-16 15:24 ` sashiko-bot -1 siblings, 0 replies; 10+ messages in thread From: sashiko-bot @ 2026-07-16 15:24 UTC (permalink / raw) To: AngeloGioacchino Del Regno Cc: devicetree, olteanv, linux-phy, neil.armstrong, conor+dt, robh, vkoul Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider: - [High] The 4-bit bitfields in `struct mtk_pcie_sphy_imp_sel` will silently truncate 5-bit TX impedance calibration values. - [High] Probe fails when CONFIG_NVMEM is disabled due to unhandled -EOPNOTSUPP. - [Medium] Unconditional read of lane 1 calibration data needlessly disables calibration entirely for 1-lane PHYs. - [Medium] Valid calibration data containing the value '0' incorrectly aborts the entire calibration process. - [Medium] PHY provider is registered before Runtime PM is enabled, creating a probe race condition. - [Low] Expected missing calibration data and probe deferrals trigger unconditional dev_err() logs. - [Low] Missing `#include <linux/pm_runtime.h>` while using `devm_pm_runtime_enable()`. -- --- Patch [2]: [PATCH 2/2] phy: mediatek: Add support for PCI-Express Gen4 S-PHY --- commit 7cbdba90e32bbdc889bb04306231fd5de6141303 Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> phy: mediatek: Add support for PCI-Express Gen4 S-PHY Add support for the PCI-Express Gen4 S-PHY found in the latest MediaTek SoCs, including MT8196, MT8894 and similar. > diff --git a/drivers/phy/mediatek/phy-mtk-pcie-sphy.c b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c > new file mode 100644 > --- /dev/null > +++ b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c > @@ -0,0 +1,560 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2022 MediaTek Inc. > + * Copyright (c) 2026 Collabora Ltd. > + * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> > + */ > + > +#include <linux/bitfield.h> > +#include <linux/module.h> > +#include <linux/nvmem-consumer.h> > +#include <linux/of.h> > +#include <linux/phy/phy.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> [Severity: Low] Does this need linux/pm_runtime.h included since devm_pm_runtime_enable() is used later in the file? [ ... ] > +/** > + * struct mtk_pcie_sphy_imp_sel - Impedance Selection parameters > + * @pmos: Impedance selection for P-Channel MOSFET > + * @nmos: Impedance selection for N-Channel MOSFET > + */ > +struct mtk_pcie_sphy_imp_sel { > + u8 pmos : 4; > + u8 nmos : 4; > +}; [Severity: High] Can this truncate calibration values? The eFuse data read for tx-lanes-pmos and tx-lanes-nmos uses FIELD_MAX(RG_XTP_LN_TX_IMPSEL_PMOS), which is GENMASK(4, 0), allowing 5-bit values up to 31. Will values greater than 15 be silently truncated here? [ ... ] > +static int mtk_pcie_sphy_get_one_cal_para(struct device *dev, const char *name, u8 max_val) > +{ > + u16 buf; > + u8 tmp; > + int ret; > + > + /* > + * All of the calibrations are always max 8 bits long, but some may > + * be split between two different 8-bits cells: handle this corner > + * case by retrying reading as u16. > + */ > + ret = nvmem_cell_read_u8(dev, name, &tmp); > + if (ret == 0) > + buf = tmp; > + else > + ret = nvmem_cell_read_u16(dev, name, &buf); > + > + if (ret) { > + dev_err(dev, "Cannot get calibration data for %s: %d\n", name, ret); > + return ret; > + }; [Severity: Low] Does this unconditionally log an error for expected missing cells (-ENOENT) or during probe deferral (-EPROBE_DEFER)? > + > + if (buf == 0) { > + dev_warn(dev, "No calibration for %s. Using defaults\n", name); > + return -ENOENT; > + } [Severity: Medium] Is 0 an invalid calibration value? Later code checks if the calibration version is <= 4, which implies version 0 could exist. If a valid cell contains 0, will this mistakenly abort the entire calibration process? [ ... ] > +static int mtk_pcie_sphy_get_calibration_data(struct mtk_pcie_sphy *pcie_sphy) > +{ [ ... ] > + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln0-rswn", > + FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL)); > + if (ret < 0) > + goto end; > + cal->tx_rswn_impsel[0] = ret; > + > + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln1-rswn", > + FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL)); > + if (ret < 0) > + goto end; > + cal->tx_rswn_impsel[1] = ret; [Severity: Medium] Will this break calibration on 1-lane PHYs? If num_lanes is 1, the device tree might naturally omit lane 1 data. Would the read for tx-ln1-rswn then return -ENOENT and cause all valid lane 0 calibration to be discarded? [ ... ] > +end: > + if (ret < 0) { > + /* > + * If any of the calibration values is missing, or if there is > + * no calibration at all in the eFuses, this is not a problem, > + * as the PHY doesn't require one to actually work. > + */ > + if (ret == -ENOENT) { > + cal->supported = false; > + return 0; > + } > + return ret; > + }; [Severity: High] If the kernel is compiled without CONFIG_NVMEM, nvmem_cell_read_u8 returns -EOPNOTSUPP. Does this cause the entire PHY probe to fail on systems without NVMEM, even though calibration is optional? [ ... ] > +static int mtk_pcie_sphy_probe(struct platform_device *pdev) > +{ [ ... ] > + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > + if (IS_ERR(provider)) > + return dev_err_probe(dev, PTR_ERR(provider), > + "Could not register PCI-Express S-PHY\n"); > + > + ret = devm_pm_runtime_enable(dev); > + if (ret) > + return ret; > + > + return 0; > +} [Severity: Medium] Could registering the PHY provider before enabling Runtime PM lead to a race condition? If a consumer driver probes concurrently and immediately calls phy_init(), could it try to interact with the device before devm_pm_runtime_enable() has completed? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260716151333.77254-1-angelogioacchino.delregno@collabora.com?part=2 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] phy: mediatek: Add support for PCI-Express Gen4 S-PHY @ 2026-07-16 15:24 ` sashiko-bot 0 siblings, 0 replies; 10+ messages in thread From: sashiko-bot @ 2026-07-16 15:24 UTC (permalink / raw) To: AngeloGioacchino Del Regno Cc: devicetree, olteanv, linux-phy, neil.armstrong, conor+dt, robh, vkoul Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider: - [High] The 4-bit bitfields in `struct mtk_pcie_sphy_imp_sel` will silently truncate 5-bit TX impedance calibration values. - [High] Probe fails when CONFIG_NVMEM is disabled due to unhandled -EOPNOTSUPP. - [Medium] Unconditional read of lane 1 calibration data needlessly disables calibration entirely for 1-lane PHYs. - [Medium] Valid calibration data containing the value '0' incorrectly aborts the entire calibration process. - [Medium] PHY provider is registered before Runtime PM is enabled, creating a probe race condition. - [Low] Expected missing calibration data and probe deferrals trigger unconditional dev_err() logs. - [Low] Missing `#include <linux/pm_runtime.h>` while using `devm_pm_runtime_enable()`. -- --- Patch [2]: [PATCH 2/2] phy: mediatek: Add support for PCI-Express Gen4 S-PHY --- commit 7cbdba90e32bbdc889bb04306231fd5de6141303 Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> phy: mediatek: Add support for PCI-Express Gen4 S-PHY Add support for the PCI-Express Gen4 S-PHY found in the latest MediaTek SoCs, including MT8196, MT8894 and similar. > diff --git a/drivers/phy/mediatek/phy-mtk-pcie-sphy.c b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c > new file mode 100644 > --- /dev/null > +++ b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c > @@ -0,0 +1,560 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2022 MediaTek Inc. > + * Copyright (c) 2026 Collabora Ltd. > + * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> > + */ > + > +#include <linux/bitfield.h> > +#include <linux/module.h> > +#include <linux/nvmem-consumer.h> > +#include <linux/of.h> > +#include <linux/phy/phy.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> [Severity: Low] Does this need linux/pm_runtime.h included since devm_pm_runtime_enable() is used later in the file? [ ... ] > +/** > + * struct mtk_pcie_sphy_imp_sel - Impedance Selection parameters > + * @pmos: Impedance selection for P-Channel MOSFET > + * @nmos: Impedance selection for N-Channel MOSFET > + */ > +struct mtk_pcie_sphy_imp_sel { > + u8 pmos : 4; > + u8 nmos : 4; > +}; [Severity: High] Can this truncate calibration values? The eFuse data read for tx-lanes-pmos and tx-lanes-nmos uses FIELD_MAX(RG_XTP_LN_TX_IMPSEL_PMOS), which is GENMASK(4, 0), allowing 5-bit values up to 31. Will values greater than 15 be silently truncated here? [ ... ] > +static int mtk_pcie_sphy_get_one_cal_para(struct device *dev, const char *name, u8 max_val) > +{ > + u16 buf; > + u8 tmp; > + int ret; > + > + /* > + * All of the calibrations are always max 8 bits long, but some may > + * be split between two different 8-bits cells: handle this corner > + * case by retrying reading as u16. > + */ > + ret = nvmem_cell_read_u8(dev, name, &tmp); > + if (ret == 0) > + buf = tmp; > + else > + ret = nvmem_cell_read_u16(dev, name, &buf); > + > + if (ret) { > + dev_err(dev, "Cannot get calibration data for %s: %d\n", name, ret); > + return ret; > + }; [Severity: Low] Does this unconditionally log an error for expected missing cells (-ENOENT) or during probe deferral (-EPROBE_DEFER)? > + > + if (buf == 0) { > + dev_warn(dev, "No calibration for %s. Using defaults\n", name); > + return -ENOENT; > + } [Severity: Medium] Is 0 an invalid calibration value? Later code checks if the calibration version is <= 4, which implies version 0 could exist. If a valid cell contains 0, will this mistakenly abort the entire calibration process? [ ... ] > +static int mtk_pcie_sphy_get_calibration_data(struct mtk_pcie_sphy *pcie_sphy) > +{ [ ... ] > + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln0-rswn", > + FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL)); > + if (ret < 0) > + goto end; > + cal->tx_rswn_impsel[0] = ret; > + > + ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln1-rswn", > + FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL)); > + if (ret < 0) > + goto end; > + cal->tx_rswn_impsel[1] = ret; [Severity: Medium] Will this break calibration on 1-lane PHYs? If num_lanes is 1, the device tree might naturally omit lane 1 data. Would the read for tx-ln1-rswn then return -ENOENT and cause all valid lane 0 calibration to be discarded? [ ... ] > +end: > + if (ret < 0) { > + /* > + * If any of the calibration values is missing, or if there is > + * no calibration at all in the eFuses, this is not a problem, > + * as the PHY doesn't require one to actually work. > + */ > + if (ret == -ENOENT) { > + cal->supported = false; > + return 0; > + } > + return ret; > + }; [Severity: High] If the kernel is compiled without CONFIG_NVMEM, nvmem_cell_read_u8 returns -EOPNOTSUPP. Does this cause the entire PHY probe to fail on systems without NVMEM, even though calibration is optional? [ ... ] > +static int mtk_pcie_sphy_probe(struct platform_device *pdev) > +{ [ ... ] > + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > + if (IS_ERR(provider)) > + return dev_err_probe(dev, PTR_ERR(provider), > + "Could not register PCI-Express S-PHY\n"); > + > + ret = devm_pm_runtime_enable(dev); > + if (ret) > + return ret; > + > + return 0; > +} [Severity: Medium] Could registering the PHY provider before enabling Runtime PM lead to a race condition? If a consumer driver probes concurrently and immediately calls phy_init(), could it try to interact with the device before devm_pm_runtime_enable() has completed? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260716151333.77254-1-angelogioacchino.delregno@collabora.com?part=2 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-07-16 15:24 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-16 15:13 [PATCH 0/2] PHY: Add MediaTek PCI-Express Gen4 S-PHY Driver AngeloGioacchino Del Regno 2026-07-16 15:13 ` AngeloGioacchino Del Regno 2026-07-16 15:13 ` [PATCH 1/2] dt-bindings: phy: Document MT8196 MediaTek PCI-Express Gen4 S-PHY AngeloGioacchino Del Regno 2026-07-16 15:13 ` AngeloGioacchino Del Regno 2026-07-16 15:21 ` sashiko-bot 2026-07-16 15:21 ` sashiko-bot 2026-07-16 15:13 ` [PATCH 2/2] phy: mediatek: Add support for " AngeloGioacchino Del Regno 2026-07-16 15:13 ` AngeloGioacchino Del Regno 2026-07-16 15:24 ` sashiko-bot 2026-07-16 15:24 ` sashiko-bot
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.