* Re: [PATCH 1/2] dt-bindings: phy: spacemit: add regulator support to K1 USB2 PHY
From: Yixun Lan @ 2026-02-07 11:55 UTC (permalink / raw)
To: Chukun Pan
Cc: Yixun Lan, Vinod Koul, Ze Huang, Rob Herring, Mark Brown,
Conor Dooley, Liam Girdwood, Krzysztof Kozlowski, Neil Armstrong,
linux-riscv, linux-phy, linux-kernel, devicetree, spacemit
In-Reply-To: <20260206100009.873182-1-amadeus@jmu.edu.cn>
Hi Chukun,
On 18:00 Fri 06 Feb , Chukun Pan wrote:
> Add an optional phy-supply property to describe the regulator
> supplying for USB VBUS.
>
No, the phy itself doesn't require regulator supply
We previously had a discussion about this, and I personally agree it
should be handled at USB port level, see Ze's comment
https://lore.kernel.org/all/aWJAT3n_KcND8bOz@monica.localdomain/
> Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
> ---
> Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
> index 43eaca90d88c..74a1cd5bcdbe 100644
> --- a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
> +++ b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
> @@ -19,6 +19,10 @@ properties:
> clocks:
> maxItems: 1
>
> + phy-supply:
> + description:
> + Phandle to a regulator that provides power to VBUS.
> +
> "#phy-cells":
> const: 0
>
> --
> 2.34.1
>
--
Yixun Lan (dlan)
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: phy: spacemit: add regulator support to K1 USB2 PHY
From: Krzysztof Kozlowski @ 2026-02-07 10:49 UTC (permalink / raw)
To: Chukun Pan
Cc: Yixun Lan, Vinod Koul, Ze Huang, Rob Herring, Mark Brown,
Conor Dooley, Liam Girdwood, Krzysztof Kozlowski, Neil Armstrong,
linux-riscv, linux-phy, linux-kernel, devicetree, spacemit
In-Reply-To: <20260207-dancing-finch-of-chemistry-f98cf2@quoll>
On 07/02/2026 11:48, Krzysztof Kozlowski wrote:
> On Fri, Feb 06, 2026 at 06:00:08PM +0800, Chukun Pan wrote:
>> Add an optional phy-supply property to describe the regulator
>> supplying for USB VBUS.
>
> Why wasn't it there before? USB did not have VBUS?
>
> Explanation is so incomplete I suspect you are patching broken things,
> so as well this could be completely different hardware (e.g. there is no
> regulator for this block but e.g. connector).
>
>
>>
>> Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
>> ---
>> Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml | 4 ++++
>> 1 file changed, 4 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
>> index 43eaca90d88c..74a1cd5bcdbe 100644
>> --- a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
>> +++ b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
>> @@ -19,6 +19,10 @@ properties:
>> clocks:
>> maxItems: 1
>>
>> + phy-supply:
>> + description:
>> + Phandle to a regulator that provides power to VBUS.
>
> Drop redundant part. This cannot be anything else than phandle and
> regulator.
>
> "VBUS power supply" for example. But anyway, I don't have certainty that
> this is correct hardware representation. It's your task to provide that.
Plus, if this was a vbus regulator it would not be called "phy"... Even
more confusing.
Best regards,
Krzysztof
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: phy: spacemit: add regulator support to K1 USB2 PHY
From: Krzysztof Kozlowski @ 2026-02-07 10:48 UTC (permalink / raw)
To: Chukun Pan
Cc: Yixun Lan, Vinod Koul, Ze Huang, Rob Herring, Mark Brown,
Conor Dooley, Liam Girdwood, Krzysztof Kozlowski, Neil Armstrong,
linux-riscv, linux-phy, linux-kernel, devicetree, spacemit
In-Reply-To: <20260206100009.873182-1-amadeus@jmu.edu.cn>
On Fri, Feb 06, 2026 at 06:00:08PM +0800, Chukun Pan wrote:
> Add an optional phy-supply property to describe the regulator
> supplying for USB VBUS.
Why wasn't it there before? USB did not have VBUS?
Explanation is so incomplete I suspect you are patching broken things,
so as well this could be completely different hardware (e.g. there is no
regulator for this block but e.g. connector).
>
> Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
> ---
> Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
> index 43eaca90d88c..74a1cd5bcdbe 100644
> --- a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
> +++ b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
> @@ -19,6 +19,10 @@ properties:
> clocks:
> maxItems: 1
>
> + phy-supply:
> + description:
> + Phandle to a regulator that provides power to VBUS.
Drop redundant part. This cannot be anything else than phandle and
regulator.
"VBUS power supply" for example. But anyway, I don't have certainty that
this is correct hardware representation. It's your task to provide that.
Best regards,
Krzysztof
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 1/3] phy: qcom: edp: Correct and clean up eDP/DP combo PHY configuration values
From: Dmitry Baryshkov @ 2026-02-07 10:22 UTC (permalink / raw)
To: Yongxing Mou
Cc: Vinod Koul, Neil Armstrong, linux-arm-msm, linux-phy,
linux-kernel
In-Reply-To: <20260205-edp_phy-v1-1-231882bbf3f1@oss.qualcomm.com>
On Thu, Feb 05, 2026 at 05:20:53PM +0800, Yongxing Mou wrote:
> According to the current HPG settings, most eDP/DP combo PHYs can reuse the
> same configuration values.
> DP mode:
> -sa8775p/sc7280/sc8280xp/x1e80100
> -glymur
> eDP mode(low vdiff):
> -glymur/sa8775p/sc8280xp/x1e80100
> -sc7280
> The current driver still keeps multiple versions of these tables and
> doesn't fully support every combo PHY mode. This patch removes the
See Documentation/process/submitting-patches.rst, "This patch".
> redundant configs and keeps only the sets we actually use, matching the
> platforms listed above.
Should it be combined with the 3rd patch? There you sort out all the
tables, it makes more sense to review all programming together.
>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/phy/qualcomm/phy-qcom-edp.c | 41 +++++++++----------------------------
> 1 file changed, 10 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c
> index 13feab99feec..10cbb7d9a8a0 100644
> --- a/drivers/phy/qualcomm/phy-qcom-edp.c
> +++ b/drivers/phy/qualcomm/phy-qcom-edp.c
> @@ -116,17 +116,17 @@ struct qcom_edp {
> };
>
> static const u8 dp_swing_hbr_rbr[4][4] = {
> - { 0x08, 0x0f, 0x16, 0x1f },
> + { 0x07, 0x0f, 0x16, 0x1f },
> { 0x11, 0x1e, 0x1f, 0xff },
> { 0x16, 0x1f, 0xff, 0xff },
> { 0x1f, 0xff, 0xff, 0xff }
> };
>
> static const u8 dp_pre_emp_hbr_rbr[4][4] = {
> - { 0x00, 0x0d, 0x14, 0x1a },
> + { 0x00, 0x0e, 0x15, 0x1a },
> { 0x00, 0x0e, 0x15, 0xff },
> { 0x00, 0x0e, 0xff, 0xff },
> - { 0x03, 0xff, 0xff, 0xff }
> + { 0x04, 0xff, 0xff, 0xff }
> };
>
> static const u8 dp_swing_hbr2_hbr3[4][4] = {
> @@ -158,7 +158,7 @@ static const u8 edp_swing_hbr_rbr[4][4] = {
> };
>
> static const u8 edp_pre_emp_hbr_rbr[4][4] = {
> - { 0x05, 0x12, 0x17, 0x1d },
> + { 0x05, 0x11, 0x17, 0x1d },
> { 0x05, 0x11, 0x18, 0xff },
> { 0x06, 0x11, 0xff, 0xff },
> { 0x00, 0xff, 0xff, 0xff }
> @@ -172,10 +172,10 @@ static const u8 edp_swing_hbr2_hbr3[4][4] = {
> };
>
> static const u8 edp_pre_emp_hbr2_hbr3[4][4] = {
> - { 0x08, 0x11, 0x17, 0x1b },
> - { 0x00, 0x0c, 0x13, 0xff },
> - { 0x05, 0x10, 0xff, 0xff },
> - { 0x00, 0xff, 0xff, 0xff }
> + { 0x0c, 0x15, 0x19, 0x1e },
> + { 0x0b, 0x15, 0x19, 0xff },
> + { 0x0e, 0x14, 0xff, 0xff },
> + { 0x0d, 0xff, 0xff, 0xff }
> };
>
> static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg = {
> @@ -193,27 +193,6 @@ static const u8 edp_phy_vco_div_cfg_v4[4] = {
> 0x01, 0x01, 0x02, 0x00,
> };
>
> -static const u8 edp_pre_emp_hbr_rbr_v5[4][4] = {
> - { 0x05, 0x11, 0x17, 0x1d },
> - { 0x05, 0x11, 0x18, 0xff },
> - { 0x06, 0x11, 0xff, 0xff },
> - { 0x00, 0xff, 0xff, 0xff }
> -};
> -
> -static const u8 edp_pre_emp_hbr2_hbr3_v5[4][4] = {
> - { 0x0c, 0x15, 0x19, 0x1e },
> - { 0x0b, 0x15, 0x19, 0xff },
> - { 0x0e, 0x14, 0xff, 0xff },
> - { 0x0d, 0xff, 0xff, 0xff }
> -};
> -
> -static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg_v5 = {
> - .swing_hbr_rbr = &edp_swing_hbr_rbr,
> - .swing_hbr3_hbr2 = &edp_swing_hbr2_hbr3,
> - .pre_emphasis_hbr_rbr = &edp_pre_emp_hbr_rbr_v5,
> - .pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3_v5,
> -};
> -
> static const u8 edp_phy_aux_cfg_v5[DP_AUX_CFG_SIZE] = {
> 0x00, 0x13, 0xa4, 0x00, 0x0a, 0x26, 0x0a, 0x03, 0x37, 0x03, 0x02, 0x02, 0x00,
> };
> @@ -564,7 +543,7 @@ static const struct qcom_edp_phy_cfg sa8775p_dp_phy_cfg = {
> .is_edp = false,
> .aux_cfg = edp_phy_aux_cfg_v5,
> .vco_div_cfg = edp_phy_vco_div_cfg_v4,
> - .swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg_v5,
> + .swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> .ver_ops = &qcom_edp_phy_ops_v4,
> };
>
> @@ -944,7 +923,7 @@ static const struct phy_ver_ops qcom_edp_phy_ops_v8 = {
> static struct qcom_edp_phy_cfg glymur_phy_cfg = {
> .aux_cfg = edp_phy_aux_cfg_v8,
> .vco_div_cfg = edp_phy_vco_div_cfg_v8,
> - .swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg_v5,
> + .swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> .ver_ops = &qcom_edp_phy_ops_v8,
> };
>
>
> --
> 2.43.0
>
--
With best wishes
Dmitry
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 3/3] phy: qcom: edp: Add eDP phy mode switch support
From: Dmitry Baryshkov @ 2026-02-07 10:20 UTC (permalink / raw)
To: Yongxing Mou
Cc: Vinod Koul, Neil Armstrong, linux-arm-msm, linux-phy,
linux-kernel
In-Reply-To: <20260205-edp_phy-v1-3-231882bbf3f1@oss.qualcomm.com>
On Thu, Feb 05, 2026 at 05:20:55PM +0800, Yongxing Mou wrote:
> Add DP/eDP switch support by splitting the PHY swing/pre-emphasis tables
> into separate DP and eDP configurations. This allows the driver to select
> the correct table based on the is_edp flag.
>
> Add a dedicated table for the SC7280/glymur platforms, as they are not
> compatible with the others.
Again, describe what is not working / broken now. Is it something that
we were supposedly supporting or not? Fixes, etc.
>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/phy/qualcomm/phy-qcom-edp.c | 72 ++++++++++++++++++++++++++++---------
> 1 file changed, 56 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c
> index 388226dbad7f..85caa869a8c0 100644
> --- a/drivers/phy/qualcomm/phy-qcom-edp.c
> +++ b/drivers/phy/qualcomm/phy-qcom-edp.c
> @@ -88,7 +88,8 @@ struct qcom_edp_phy_cfg {
> bool is_edp;
> const u8 *aux_cfg;
> const u8 *vco_div_cfg;
> - const struct qcom_edp_swing_pre_emph_cfg *swing_pre_emph_cfg;
> + const struct qcom_edp_swing_pre_emph_cfg *dp_swing_pre_emph_cfg;
> + const struct qcom_edp_swing_pre_emph_cfg *edp_swing_pre_emph_cfg;
> const struct phy_ver_ops *ver_ops;
> };
>
> @@ -151,6 +152,20 @@ static const struct qcom_edp_swing_pre_emph_cfg dp_phy_swing_pre_emph_cfg = {
> .pre_emphasis_hbr3_hbr2 = &dp_pre_emp_hbr2_hbr3,
> };
>
> +static const u8 dp_pre_emp_hbr_rbr_v8[4][4] = {
> + { 0x00, 0x0e, 0x15, 0x1a },
> + { 0x00, 0x0e, 0x15, 0xff },
> + { 0x00, 0x0e, 0xff, 0xff },
> + { 0x00, 0xff, 0xff, 0xff }
> +};
> +
> +static const struct qcom_edp_swing_pre_emph_cfg dp_phy_swing_pre_emph_cfg_v8 = {
> + .swing_hbr_rbr = &dp_swing_hbr_rbr,
> + .swing_hbr3_hbr2 = &dp_swing_hbr2_hbr3,
> + .pre_emphasis_hbr_rbr = &dp_pre_emp_hbr_rbr_v8,
> + .pre_emphasis_hbr3_hbr2 = &dp_pre_emp_hbr2_hbr3,
> +};
> +
> static const u8 edp_swing_hbr_rbr[4][4] = {
> { 0x07, 0x0f, 0x16, 0x1f },
> { 0x0d, 0x16, 0x1e, 0xff },
> @@ -186,6 +201,27 @@ static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg = {
> .pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3,
> };
>
> +static const u8 edp_swing_hbr2_hbr3_v3[4][4] = {
> + { 0x0b, 0x11, 0x16, 0x1b },
> + { 0x0b, 0x19, 0x1f, 0xff },
> + { 0x18, 0x1f, 0xff, 0xff },
> + { 0x1f, 0xff, 0xff, 0xff }
> +};
> +
> +static const u8 edp_pre_emp_hbr2_hbr3_v3[4][4] = {
> + { 0x0c, 0x15, 0x19, 0x1e },
> + { 0x09, 0x14, 0x19, 0xff },
> + { 0x0f, 0x14, 0xff, 0xff },
> + { 0x0d, 0xff, 0xff, 0xff }
> +};
> +
> +static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg_v3 = {
> + .swing_hbr_rbr = &edp_swing_hbr_rbr,
> + .swing_hbr3_hbr2 = &edp_swing_hbr2_hbr3_v3,
> + .pre_emphasis_hbr_rbr = &edp_pre_emp_hbr_rbr,
> + .pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3_v3,
> +};
> +
> static const u8 edp_phy_aux_cfg_v4[DP_AUX_CFG_SIZE] = {
> 0x00, 0x13, 0x24, 0x00, 0x0a, 0x26, 0x0a, 0x03, 0x37, 0x03, 0x02, 0x02, 0x00,
> };
> @@ -242,12 +278,7 @@ static int qcom_edp_phy_init(struct phy *phy)
> DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
> edp->edp + DP_PHY_PD_CTL);
>
> - /*
> - * TODO: Re-work the conditions around setting the cfg8 value
> - * when more information becomes available about why this is
> - * even needed.
> - */
> - if (edp->cfg->swing_pre_emph_cfg && !edp->is_edp)
> + if (!edp->is_edp)
> aux_cfg[8] = 0xb7;
>
> writel(0xfc, edp->edp + DP_PHY_MODE);
> @@ -271,7 +302,7 @@ static int qcom_edp_phy_init(struct phy *phy)
>
> static int qcom_edp_set_voltages(struct qcom_edp *edp, const struct phy_configure_opts_dp *dp_opts)
> {
> - const struct qcom_edp_swing_pre_emph_cfg *cfg = edp->cfg->swing_pre_emph_cfg;
> + const struct qcom_edp_swing_pre_emph_cfg *cfg;
> unsigned int v_level = 0;
> unsigned int p_level = 0;
> int ret;
> @@ -279,12 +310,14 @@ static int qcom_edp_set_voltages(struct qcom_edp *edp, const struct phy_configur
> u8 emph;
> int i;
>
> + if (edp->is_edp)
> + cfg = edp->cfg->edp_swing_pre_emph_cfg;
> + else
> + cfg = edp->cfg->dp_swing_pre_emph_cfg;
> +
> if (!cfg)
> return 0;
Is it not redundant now?
>
> - if (edp->is_edp)
> - cfg = &edp_phy_swing_pre_emph_cfg;
> -
> for (i = 0; i < dp_opts->lanes; i++) {
> v_level = max(v_level, dp_opts->voltage[i]);
> p_level = max(p_level, dp_opts->pre[i]);
> @@ -591,20 +624,24 @@ static const struct qcom_edp_phy_cfg sa8775p_dp_phy_cfg = {
> .is_edp = false,
> .aux_cfg = edp_phy_aux_cfg_v5,
> .vco_div_cfg = edp_phy_vco_div_cfg_v4,
> - .swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> + .dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> + .edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> .ver_ops = &qcom_edp_phy_ops_v4,
> };
>
> static const struct qcom_edp_phy_cfg sc7280_dp_phy_cfg = {
> .aux_cfg = edp_phy_aux_cfg_v4,
> .vco_div_cfg = edp_phy_vco_div_cfg_v4,
> + .dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> + .edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg_v3,
> .ver_ops = &qcom_edp_phy_ops_v3,
> };
>
> static const struct qcom_edp_phy_cfg sc8280xp_dp_phy_cfg = {
> .aux_cfg = edp_phy_aux_cfg_v4,
> .vco_div_cfg = edp_phy_vco_div_cfg_v4,
> - .swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> + .dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> + .edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> .ver_ops = &qcom_edp_phy_ops_v4,
> };
>
> @@ -612,7 +649,8 @@ static const struct qcom_edp_phy_cfg sc8280xp_edp_phy_cfg = {
> .is_edp = true,
> .aux_cfg = edp_phy_aux_cfg_v4,
> .vco_div_cfg = edp_phy_vco_div_cfg_v4,
> - .swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> + .dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> + .edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> .ver_ops = &qcom_edp_phy_ops_v4,
> };
>
> @@ -811,7 +849,8 @@ static const struct phy_ver_ops qcom_edp_phy_ops_v6 = {
> static struct qcom_edp_phy_cfg x1e80100_phy_cfg = {
> .aux_cfg = edp_phy_aux_cfg_v4,
> .vco_div_cfg = edp_phy_vco_div_cfg_v4,
> - .swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> + .dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
> + .edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> .ver_ops = &qcom_edp_phy_ops_v6,
> };
>
> @@ -991,7 +1030,8 @@ static const struct phy_ver_ops qcom_edp_phy_ops_v8 = {
> static struct qcom_edp_phy_cfg glymur_phy_cfg = {
> .aux_cfg = edp_phy_aux_cfg_v8,
> .vco_div_cfg = edp_phy_vco_div_cfg_v8,
> - .swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> + .dp_swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg_v8,
> + .edp_swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
> .ver_ops = &qcom_edp_phy_ops_v8,
> };
>
>
> --
> 2.43.0
>
--
With best wishes
Dmitry
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 2/3] phy: qcom: edp: Add per-version LDO configuration callback
From: Dmitry Baryshkov @ 2026-02-07 10:17 UTC (permalink / raw)
To: Yongxing Mou
Cc: Vinod Koul, Neil Armstrong, linux-arm-msm, linux-phy,
linux-kernel
In-Reply-To: <20260205-edp_phy-v1-2-231882bbf3f1@oss.qualcomm.com>
On Thu, Feb 05, 2026 at 05:20:54PM +0800, Yongxing Mou wrote:
> Introduce the com_ldo_config callback to support per‑PHY LDO
> configuration.
Missing the why part. Is the driver programming incorrect values, or is
it an optimisation? In the former case it needs Fixes, maybe cc:stable,
maybe Reported-by, etc.
>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/phy/qualcomm/phy-qcom-edp.c | 86 ++++++++++++++++++++++++++++++++-----
> 1 file changed, 76 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c
> index 10cbb7d9a8a0..388226dbad7f 100644
> --- a/drivers/phy/qualcomm/phy-qcom-edp.c
> +++ b/drivers/phy/qualcomm/phy-qcom-edp.c
> @@ -81,6 +81,7 @@ struct phy_ver_ops {
> int (*com_clk_fwd_cfg)(const struct qcom_edp *edp);
> int (*com_configure_pll)(const struct qcom_edp *edp);
> int (*com_configure_ssc)(const struct qcom_edp *edp);
> + int (*com_ldo_config)(const struct qcom_edp *edp);
> };
>
> struct qcom_edp_phy_cfg {
> @@ -273,7 +274,7 @@ static int qcom_edp_set_voltages(struct qcom_edp *edp, const struct phy_configur
> const struct qcom_edp_swing_pre_emph_cfg *cfg = edp->cfg->swing_pre_emph_cfg;
> unsigned int v_level = 0;
> unsigned int p_level = 0;
> - u8 ldo_config;
> + int ret;
> u8 swing;
> u8 emph;
> int i;
> @@ -300,13 +301,13 @@ static int qcom_edp_set_voltages(struct qcom_edp *edp, const struct phy_configur
> if (swing == 0xff || emph == 0xff)
> return -EINVAL;
>
> - ldo_config = edp->is_edp ? 0x0 : 0x1;
> + ret = edp->cfg->ver_ops->com_ldo_config(edp);
> + if (ret)
> + return ret;
>
> - writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
> writel(swing, edp->tx0 + TXn_TX_DRV_LVL);
> writel(emph, edp->tx0 + TXn_TX_EMP_POST1_LVL);
>
> - writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);
> writel(swing, edp->tx1 + TXn_TX_DRV_LVL);
> writel(emph, edp->tx1 + TXn_TX_EMP_POST1_LVL);
>
> @@ -530,6 +531,52 @@ static int qcom_edp_com_configure_pll_v4(const struct qcom_edp *edp)
> return 0;
> }
>
> +static int qcom_edp_ldo_config_v3(const struct qcom_edp *edp)
> +{
> + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
> + u32 ldo_config;
> +
> + if (!edp->is_edp)
> + ldo_config = 0x0;
> + else if (dp_opts->link_rate <= 2700)
> + ldo_config = 0x81;
> + else
> + ldo_config = 0x41;
> +
> + writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
> + writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);
> +
> + return 0;
> +}
> +
> +static int qcom_edp_ldo_config_v4(const struct qcom_edp *edp)
> +{
> + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
> + u32 ldo_config;
> +
> + if (!edp->is_edp)
> + ldo_config = 0x0;
> + else if (dp_opts->link_rate <= 2700)
> + ldo_config = 0xC1;
Lowercase hex
> + else
> + ldo_config = 0x81;
> +
> + writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
> + writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);
> +
> + return 0;
> +}
> +
> +static const struct phy_ver_ops qcom_edp_phy_ops_v3 = {
> + .com_power_on = qcom_edp_phy_power_on_v4,
> + .com_resetsm_cntrl = qcom_edp_phy_com_resetsm_cntrl_v4,
> + .com_bias_en_clkbuflr = qcom_edp_com_bias_en_clkbuflr_v4,
> + .com_clk_fwd_cfg = qcom_edp_com_clk_fwd_cfg_v4,
> + .com_configure_pll = qcom_edp_com_configure_pll_v4,
> + .com_configure_ssc = qcom_edp_com_configure_ssc_v4,
> + .com_ldo_config = qcom_edp_ldo_config_v3,
> +};
> +
> static const struct phy_ver_ops qcom_edp_phy_ops_v4 = {
> .com_power_on = qcom_edp_phy_power_on_v4,
> .com_resetsm_cntrl = qcom_edp_phy_com_resetsm_cntrl_v4,
> @@ -537,6 +584,7 @@ static const struct phy_ver_ops qcom_edp_phy_ops_v4 = {
> .com_clk_fwd_cfg = qcom_edp_com_clk_fwd_cfg_v4,
> .com_configure_pll = qcom_edp_com_configure_pll_v4,
> .com_configure_ssc = qcom_edp_com_configure_ssc_v4,
> + .com_ldo_config = qcom_edp_ldo_config_v4,
> };
>
> static const struct qcom_edp_phy_cfg sa8775p_dp_phy_cfg = {
> @@ -550,7 +598,7 @@ static const struct qcom_edp_phy_cfg sa8775p_dp_phy_cfg = {
> static const struct qcom_edp_phy_cfg sc7280_dp_phy_cfg = {
> .aux_cfg = edp_phy_aux_cfg_v4,
> .vco_div_cfg = edp_phy_vco_div_cfg_v4,
> - .ver_ops = &qcom_edp_phy_ops_v4,
> + .ver_ops = &qcom_edp_phy_ops_v3,
This looks like an extra change. Is it intentional in this patch? If so,
mention it in the commit message.
> };
>
> static const struct qcom_edp_phy_cfg sc8280xp_dp_phy_cfg = {
--
With best wishes
Dmitry
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [bug report] phy: apple: Add Apple Type-C PHY
From: Sven Peter @ 2026-02-06 21:48 UTC (permalink / raw)
To: Janne Grunau, Dan Carpenter
Cc: Neal Gompa, Neil Armstrong, asahi, linux-arm-kernel, linux-phy,
linux-kernel
In-Reply-To: <20260206214743.GA194367@robin.jannau.net>
On 06.02.26 22:47, Janne Grunau wrote:
> On Fri, Feb 06, 2026 at 04:40:47PM +0300, Dan Carpenter wrote:
>> [ Smatch checking is paused while we raise funding. #SadFace
>> https://lore.kernel.org/all/aTaiGSbWZ9DJaGo7@stanley.mountain/ -dan ]
>
> This is unfortunate, there have been useful bug reports.
>
>> Commit 8e98ca1e74db ("phy: apple: Add Apple Type-C PHY") from Dec 14,
>> 2025 (linux-next), leads to the following Smatch static checker
>> warning:
>>
>> drivers/phy/apple/atc.c:2209 atcphy_map_resources()
>> warn: 'resources[i]->addr' isn't an ERR_PTR
>>
>> drivers/phy/apple/atc.c
>> 2191 static int atcphy_map_resources(struct platform_device *pdev, struct apple_atcphy *atcphy)
>> 2192 {
>> 2193 struct {
>> 2194 const char *name;
>> 2195 void __iomem **addr;
>> 2196 struct resource **res;
>> 2197 } resources[] = {
>> 2198 { "core", &atcphy->regs.core, &atcphy->res.core },
>> 2199 { "lpdptx", &atcphy->regs.lpdptx, NULL },
>> 2200 { "axi2af", &atcphy->regs.axi2af, &atcphy->res.axi2af },
>> 2201 { "usb2phy", &atcphy->regs.usb2phy, NULL },
>> 2202 { "pipehandler", &atcphy->regs.pipehandler, NULL },
>> 2203 };
>> 2204 struct resource *res;
>> 2205
>> 2206 for (int i = 0; i < ARRAY_SIZE(resources); i++) {
>> 2207 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resources[i].name);
>> 2208 *resources[i].addr = devm_ioremap_resource(&pdev->dev, res);
>> --> 2209 if (IS_ERR(resources[i].addr))
>>
>> This is checking the wrong variable. The * is missing.
>> if (IS_ERR(*resources[i].addr)) {
>
> This issue was identified by testing and is fixed in next by commit
> 7d55b44e2be1 ("phy: apple: atc: Actually check return value of
> devm_apple_tunable_parse").
>
> https://lore.kernel.org/all/20260104-atcphy-tunable-fix-v2-1-84e5c2a57aaa@kernel.org/
I think I actually messed this up *twice*! Once for the tunables and
once again for the resources here :(
Sven
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [bug report] phy: apple: Add Apple Type-C PHY
From: Janne Grunau @ 2026-02-06 21:47 UTC (permalink / raw)
To: Dan Carpenter
Cc: Sven Peter, Neal Gompa, Neil Armstrong, asahi, linux-arm-kernel,
linux-phy, linux-kernel
In-Reply-To: <aYXvX1bYOXtYCgfC@stanley.mountain>
On Fri, Feb 06, 2026 at 04:40:47PM +0300, Dan Carpenter wrote:
> [ Smatch checking is paused while we raise funding. #SadFace
> https://lore.kernel.org/all/aTaiGSbWZ9DJaGo7@stanley.mountain/ -dan ]
This is unfortunate, there have been useful bug reports.
> Commit 8e98ca1e74db ("phy: apple: Add Apple Type-C PHY") from Dec 14,
> 2025 (linux-next), leads to the following Smatch static checker
> warning:
>
> drivers/phy/apple/atc.c:2209 atcphy_map_resources()
> warn: 'resources[i]->addr' isn't an ERR_PTR
>
> drivers/phy/apple/atc.c
> 2191 static int atcphy_map_resources(struct platform_device *pdev, struct apple_atcphy *atcphy)
> 2192 {
> 2193 struct {
> 2194 const char *name;
> 2195 void __iomem **addr;
> 2196 struct resource **res;
> 2197 } resources[] = {
> 2198 { "core", &atcphy->regs.core, &atcphy->res.core },
> 2199 { "lpdptx", &atcphy->regs.lpdptx, NULL },
> 2200 { "axi2af", &atcphy->regs.axi2af, &atcphy->res.axi2af },
> 2201 { "usb2phy", &atcphy->regs.usb2phy, NULL },
> 2202 { "pipehandler", &atcphy->regs.pipehandler, NULL },
> 2203 };
> 2204 struct resource *res;
> 2205
> 2206 for (int i = 0; i < ARRAY_SIZE(resources); i++) {
> 2207 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resources[i].name);
> 2208 *resources[i].addr = devm_ioremap_resource(&pdev->dev, res);
> --> 2209 if (IS_ERR(resources[i].addr))
>
> This is checking the wrong variable. The * is missing.
> if (IS_ERR(*resources[i].addr)) {
This issue was identified by testing and is fixed in next by commit
7d55b44e2be1 ("phy: apple: atc: Actually check return value of
devm_apple_tunable_parse").
https://lore.kernel.org/all/20260104-atcphy-tunable-fix-v2-1-84e5c2a57aaa@kernel.org/
Thanks for the report
Janne
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [bug report] phy: apple: Add Apple Type-C PHY
From: Dan Carpenter @ 2026-02-06 13:40 UTC (permalink / raw)
To: Sven Peter
Cc: Neal Gompa, Neil Armstrong, asahi, linux-arm-kernel, linux-phy,
linux-kernel
In-Reply-To: <caa37f28-a2e8-4e0a-a9ce-a365ce805e4b@stanley.mountain>
[ Smatch checking is paused while we raise funding. #SadFace
https://lore.kernel.org/all/aTaiGSbWZ9DJaGo7@stanley.mountain/ -dan ]
Hello Sven Peter,
Commit 8e98ca1e74db ("phy: apple: Add Apple Type-C PHY") from Dec 14,
2025 (linux-next), leads to the following Smatch static checker
warning:
drivers/phy/apple/atc.c:2209 atcphy_map_resources()
warn: 'resources[i]->addr' isn't an ERR_PTR
drivers/phy/apple/atc.c
2191 static int atcphy_map_resources(struct platform_device *pdev, struct apple_atcphy *atcphy)
2192 {
2193 struct {
2194 const char *name;
2195 void __iomem **addr;
2196 struct resource **res;
2197 } resources[] = {
2198 { "core", &atcphy->regs.core, &atcphy->res.core },
2199 { "lpdptx", &atcphy->regs.lpdptx, NULL },
2200 { "axi2af", &atcphy->regs.axi2af, &atcphy->res.axi2af },
2201 { "usb2phy", &atcphy->regs.usb2phy, NULL },
2202 { "pipehandler", &atcphy->regs.pipehandler, NULL },
2203 };
2204 struct resource *res;
2205
2206 for (int i = 0; i < ARRAY_SIZE(resources); i++) {
2207 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resources[i].name);
2208 *resources[i].addr = devm_ioremap_resource(&pdev->dev, res);
--> 2209 if (IS_ERR(resources[i].addr))
This is checking the wrong variable. The * is missing.
if (IS_ERR(*resources[i].addr)) {
2210 return dev_err_probe(atcphy->dev, PTR_ERR(resources[i].addr),
2211 "Unable to map %s regs", resources[i].name);
2212
2213 if (resources[i].res)
2214 *resources[i].res = res;
2215 }
2216
2217 return 0;
2218 }
regards,
dan carpenter
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [bug report] phy: qcom: qmp-usbc: Add QCS615 USB/DP PHY config and DP mode support
From: Dan Carpenter @ 2026-02-06 13:39 UTC (permalink / raw)
To: Xiangxu Yin; +Cc: Neil Armstrong, linux-arm-msm, linux-phy, linux-kernel
In-Reply-To: <caa37f28-a2e8-4e0a-a9ce-a365ce805e4b@stanley.mountain>
[ Smatch checking is paused while we raise funding. #SadFace
https://lore.kernel.org/all/aTaiGSbWZ9DJaGo7@stanley.mountain/ -dan ]
Hello Xiangxu Yin,
Commit 81791c45c8e0 ("phy: qcom: qmp-usbc: Add QCS615 USB/DP PHY
config and DP mode support") from Dec 15, 2025 (linux-next), leads to
the following Smatch static checker warning:
drivers/phy/qualcomm/phy-qcom-qmp-usbc.c:803 qmp_v2_configure_dp_swing()
index hardmax out of bounds '(*cfg->swing_tbl)[v_level]' size=4 max='4' rl='0-4'
drivers/phy/qualcomm/phy-qcom-qmp-usbc.c
777 static int qmp_v2_configure_dp_swing(struct qmp_usbc *qmp)
778 {
779 const struct qmp_phy_cfg *cfg = qmp->cfg;
780 const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
781 void __iomem *tx = qmp->dp_tx;
782 void __iomem *tx2 = qmp->dp_tx2;
783 unsigned int v_level = 0, p_level = 0;
784 u8 voltage_swing_cfg, pre_emphasis_cfg;
785 int i;
786
787 if (dp_opts->lanes > 4) {
788 dev_err(qmp->dev, "Invalid lane_num(%d)\n", dp_opts->lanes);
789 return -EINVAL;
790 }
791
792 for (i = 0; i < dp_opts->lanes; i++) {
793 v_level = max(v_level, dp_opts->voltage[i]);
794 p_level = max(p_level, dp_opts->pre[i]);
795 }
796
797 if (v_level > 4 || p_level > 4) {
These should be >= 4 instead of >.
798 dev_err(qmp->dev, "Invalid v(%d) | p(%d) level)\n",
799 v_level, p_level);
800 return -EINVAL;
801 }
802
--> 803 voltage_swing_cfg = (*cfg->swing_tbl)[v_level][p_level];
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is a 4x4 array.
804 pre_emphasis_cfg = (*cfg->pre_emphasis_tbl)[v_level][p_level];
805
806 voltage_swing_cfg |= DP_PHY_TXn_TX_DRV_LVL_MUX_EN;
807 pre_emphasis_cfg |= DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN;
808
809 if (voltage_swing_cfg == 0xff && pre_emphasis_cfg == 0xff)
810 return -EINVAL;
811
812 writel(voltage_swing_cfg, tx + QSERDES_V2_TX_TX_DRV_LVL);
813 writel(pre_emphasis_cfg, tx + QSERDES_V2_TX_TX_EMP_POST1_LVL);
814 writel(voltage_swing_cfg, tx2 + QSERDES_V2_TX_TX_DRV_LVL);
815 writel(pre_emphasis_cfg, tx2 + QSERDES_V2_TX_TX_EMP_POST1_LVL);
816
817 return 0;
818 }
regards,
dan carpenter
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 3/3] phy: qcom: edp: Add eDP phy mode switch support
From: Konrad Dybcio @ 2026-02-06 11:02 UTC (permalink / raw)
To: Yongxing Mou, Vinod Koul, Neil Armstrong
Cc: linux-arm-msm, linux-phy, linux-kernel
In-Reply-To: <20260205-edp_phy-v1-3-231882bbf3f1@oss.qualcomm.com>
On 2/5/26 10:20 AM, Yongxing Mou wrote:
> Add DP/eDP switch support by splitting the PHY swing/pre-emphasis tables
> into separate DP and eDP configurations. This allows the driver to select
> the correct table based on the is_edp flag.
>
> Add a dedicated table for the SC7280/glymur platforms, as they are not
> compatible with the others.
>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
[...]
> +static const u8 edp_swing_hbr2_hbr3_v3[4][4] = {
> + { 0x0b, 0x11, 0x16, 0x1b },
> + { 0x0b, 0x19, 0x1f, 0xff },
> + { 0x18, 0x1f, 0xff, 0xff },
> + { 0x1f, 0xff, 0xff, 0xff }
> +};
> +
> +static const u8 edp_pre_emp_hbr2_hbr3_v3[4][4] = {
> + { 0x0c, 0x15, 0x19, 0x1e },
> + { 0x09, 0x14, 0x19, 0xff },
> + { 0x0f, 0x14, 0xff, 0xff },
> + { 0x0d, 0xff, 0xff, 0xff }
> +};
This is not quite in line with docs for kodiak. Now, if you have
better/newer sequences than the HPG, I'm not objecting, but please
cross-check
the rest of this patch I think looks fine
Konrad
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 2/3] phy: qcom: edp: Add per-version LDO configuration callback
From: Konrad Dybcio @ 2026-02-06 10:52 UTC (permalink / raw)
To: Yongxing Mou, Vinod Koul, Neil Armstrong, Dmitry Baryshkov
Cc: linux-arm-msm, linux-phy, linux-kernel
In-Reply-To: <20260205-edp_phy-v1-2-231882bbf3f1@oss.qualcomm.com>
On 2/5/26 10:20 AM, Yongxing Mou wrote:
> Introduce the com_ldo_config callback to support per‑PHY LDO
> configuration.
>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
[...]
> +static int qcom_edp_ldo_config_v4(const struct qcom_edp *edp)
> +{
> + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
> + u32 ldo_config;
> +
> + if (!edp->is_edp)
> + ldo_config = 0x0;
> + else if (dp_opts->link_rate <= 2700)
> + ldo_config = 0xC1;
lowercase hex, please
> + else
> + ldo_config = 0x81;
> +
> + writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
> + writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);
tx1 should be dp_ops->lanes ? 2 : ldo_config : 0x00, in all cases,
I believe
Konrad
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 1/3] phy: qcom: edp: Correct and clean up eDP/DP combo PHY configuration values
From: Konrad Dybcio @ 2026-02-06 10:47 UTC (permalink / raw)
To: Yongxing Mou, Vinod Koul, Neil Armstrong
Cc: linux-arm-msm, linux-phy, linux-kernel
In-Reply-To: <20260205-edp_phy-v1-1-231882bbf3f1@oss.qualcomm.com>
On 2/5/26 10:20 AM, Yongxing Mou wrote:
> According to the current HPG settings, most eDP/DP combo PHYs can reuse the
> same configuration values.
Even across the various process nodes?
> DP mode:
> -sa8775p/sc7280/sc8280xp/x1e80100
> -glymur
> eDP mode(low vdiff):
> -glymur/sa8775p/sc8280xp/x1e80100
> -sc7280
> The current driver still keeps multiple versions of these tables and
> doesn't fully support every combo PHY mode. This patch removes the
> redundant configs and keeps only the sets we actually use, matching the
> platforms listed above.
I see that e.g. eDP Low-Vdiff swing setting for RBR is:
hamoa kodiak
arr[0][1] 0x11 0x12
It may be that this changed later during tuning but it's not reflected
in the docs for kodiak
Konrad
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH 2/2] phy: spacemit: add regulator support to K1 USB2 PHY
From: Chukun Pan @ 2026-02-06 10:00 UTC (permalink / raw)
To: Yixun Lan, Vinod Koul, Ze Huang
Cc: Rob Herring, Chukun Pan, Mark Brown, Conor Dooley, Liam Girdwood,
Krzysztof Kozlowski, Neil Armstrong, linux-riscv, linux-phy,
linux-kernel, devicetree, spacemit
In-Reply-To: <20260206100009.873182-1-amadeus@jmu.edu.cn>
USB VBUS may have a regulator supply. Add optional phy-supply
support to avoid the need for the regulator to be always-on.
Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
---
drivers/phy/spacemit/phy-k1-usb2.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/drivers/phy/spacemit/phy-k1-usb2.c b/drivers/phy/spacemit/phy-k1-usb2.c
index 342061380012..c163a29dabf9 100644
--- a/drivers/phy/spacemit/phy-k1-usb2.c
+++ b/drivers/phy/spacemit/phy-k1-usb2.c
@@ -75,6 +75,7 @@ struct spacemit_usb2phy {
struct phy *phy;
struct clk *clk;
struct regmap *regmap_base;
+ struct regulator *regulator;
};
static const struct regmap_config phy_regmap_config = {
@@ -98,6 +99,12 @@ static int spacemit_usb2phy_init(struct phy *phy)
return ret;
}
+ if (sphy->regulator) {
+ ret = regulator_enable(sphy->regulator);
+ if (ret)
+ return ret;
+ }
+
/*
* make sure the usb controller is not under reset process before
* any configuration
@@ -139,6 +146,9 @@ static int spacemit_usb2phy_exit(struct phy *phy)
clk_disable(sphy->clk);
+ if (sphy->regulator)
+ return regulator_disable(sphy->regulator);
+
return 0;
}
@@ -171,6 +181,16 @@ static int spacemit_usb2phy_probe(struct platform_device *pdev)
if (IS_ERR(sphy->regmap_base))
return dev_err_probe(dev, PTR_ERR(sphy->regmap_base), "Failed to init regmap\n");
+ sphy->regulator = devm_regulator_get_optional(dev, "phy");
+ if (IS_ERR(sphy->regulator)) {
+ int ret = PTR_ERR(sphy->regulator);
+
+ if (ret != -ENODEV)
+ return dev_err_probe(dev, ret, "Failed to get regulator\n");
+
+ sphy->regulator = NULL;
+ }
+
sphy->phy = devm_phy_create(dev, NULL, &spacemit_usb2phy_ops);
if (IS_ERR(sphy->phy))
return dev_err_probe(dev, PTR_ERR(sphy->phy), "Failed to create phy\n");
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: phy: spacemit: add regulator support to K1 USB2 PHY
From: Chukun Pan @ 2026-02-06 10:00 UTC (permalink / raw)
To: Yixun Lan, Vinod Koul, Ze Huang
Cc: Rob Herring, Chukun Pan, Mark Brown, Conor Dooley, Liam Girdwood,
Krzysztof Kozlowski, Neil Armstrong, linux-riscv, linux-phy,
linux-kernel, devicetree, spacemit
Add an optional phy-supply property to describe the regulator
supplying for USB VBUS.
Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
---
Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
index 43eaca90d88c..74a1cd5bcdbe 100644
--- a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml
@@ -19,6 +19,10 @@ properties:
clocks:
maxItems: 1
+ phy-supply:
+ description:
+ Phandle to a regulator that provides power to VBUS.
+
"#phy-cells":
const: 0
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v6 2/8] phy: sort Kconfig and Makefile
From: Luca Ceresoli @ 2026-02-06 8:39 UTC (permalink / raw)
To: Théo Lebrun, Vladimir Kondratiev, Grégory Clement,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vinod Koul,
Kishon Vijay Abraham I, Michael Turquette, Stephen Boyd,
Philipp Zabel, Thomas Bogendoerfer, Neil Armstrong
Cc: linux-mips, devicetree, linux-kernel, linux-phy, linux-clk,
Benoît Monin, Tawfik Bayouk, Thomas Petazzoni
In-Reply-To: <20260127-macb-phy-v6-2-cdd840588188@bootlin.com>
On Tue Jan 27, 2026 at 6:09 PM CET, Théo Lebrun wrote:
> Neither Kconfig nor Makefile are sorted; reorder them.
>
> $ diff -U100 <(grep ^config drivers/phy/Kconfig) \
> <(grep ^config drivers/phy/Kconfig | sort)
>
> $ diff -U100 <(grep ^obj-\\$ drivers/phy/Makefile) \
> <(grep ^obj-\\$ drivers/phy/Makefile | sort)
>
> Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v3 2/2] mmc: sdhci-of-arasan: add support on Axiado AX3000 SoC
From: Tzu-Hao Wei @ 2026-02-06 8:23 UTC (permalink / raw)
To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
Adrian Hunter, Michal Simek
Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc,
openbmc, Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-host-driver-support-v3-0-ef83b09325be@axiado.com>
From: SriNavmani A <srinavmani@axiado.com>
Axiado AX3000 SoC eMMC controller is based on Arasan eMMC 5.1 host
controller IP.
Signed-off-by: SriNavmani A <srinavmani@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
drivers/mmc/host/sdhci-of-arasan.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index ab7f0ffe7b4f007a58eb0a26868b08b0b02b40f3..caf97238a58b487312d6cc2b7a868913ace60f22 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -1512,6 +1512,17 @@ static struct sdhci_arasan_of_data intel_keembay_sdio_data = {
.clk_ops = &arasan_clk_ops,
};
+static const struct sdhci_pltfm_data sdhci_arasan_axiado_pdata = {
+ .ops = &sdhci_arasan_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_CQE,
+};
+
+static struct sdhci_arasan_of_data sdhci_arasan_axiado_data = {
+ .pdata = &sdhci_arasan_axiado_pdata,
+ .clk_ops = &arasan_clk_ops,
+};
+
static const struct of_device_id sdhci_arasan_of_match[] = {
/* SoC-specific compatible strings w/ soc_ctl_map */
{
@@ -1538,6 +1549,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
.compatible = "intel,keembay-sdhci-5.1-sdio",
.data = &intel_keembay_sdio_data,
},
+ {
+ .compatible = "axiado,ax3000-sdhci-5.1-emmc",
+ .data = &sdhci_arasan_axiado_data,
+ },
/* Generic compatible below here */
{
.compatible = "arasan,sdhci-8.9a",
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v3 1/2] dt-bindings: mmc: arasan,sdhci: Add Axiado AX3000 SoC
From: Tzu-Hao Wei @ 2026-02-06 8:23 UTC (permalink / raw)
To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
Adrian Hunter, Michal Simek
Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc,
openbmc, Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-host-driver-support-v3-0-ef83b09325be@axiado.com>
From: SriNavmani A <srinavmani@axiado.com>
Add compatible strings for Axiado AX3000 SoC eMMC controller which
is based on Arasan eMMC controller.
Signed-off-by: SriNavmani A <srinavmani@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml
index 8e79de97b242a698a2c555b0b94e2975b1761710..17dfe220503a6722b9ab446cb6e1aeed56099caf 100644
--- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml
+++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml
@@ -106,6 +106,9 @@ properties:
description:
For this device it is strongly suggested to include
arasan,soc-ctl-syscon.
+ - items:
+ - const: axiado,ax3000-sdhci-5.1-emmc # Axiado AX3000 eMMC controller
+ - const: arasan,sdhci-5.1
reg:
maxItems: 1
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v3 0/2] Add Axiado AX3000 eMMC Host Controller Support
From: Tzu-Hao Wei @ 2026-02-06 8:23 UTC (permalink / raw)
To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
Adrian Hunter, Michal Simek
Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, linux-mmc,
openbmc, Tzu-Hao Wei
Axiado AX3000 SoC eMMC controller is based on Arasan eMMC controller.
This series includes:
1. Add bindings for AX3000 SoC eMMC controller
2. Add arasan sdhci support for eMMC in Axiado AX3000
For platform device tree change, we will send a different series of
patches.
It has been verified on AX3000 platform.
---
Changes in v3:
- Enable SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN quirk
- Remove dependencies in b4
- Link to v2: https://lore.kernel.org/r/20260109-axiado-ax3000-add-emmc-host-driver-support-v2-0-934f1a61f7c0@axiado.com
Changes in v2:
- Keep host controller changes in this patchset
- Use pdata instead of mix compatible string
- Fix coding style
- Link to v1: https://lore.kernel.org/r/20251222-axiado-ax3000-add-emmc-host-driver-support-v1-0-5457d0ebcdb4@axiado.com
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
SriNavmani A (2):
dt-bindings: mmc: arasan,sdhci: Add Axiado AX3000 SoC
mmc: sdhci-of-arasan: add support on Axiado AX3000 SoC
Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml | 3 +++
drivers/mmc/host/sdhci-of-arasan.c | 15 +++++++++++++++
2 files changed, 18 insertions(+)
---
base-commit: 63804fed149a6750ffd28610c5c1c98cce6bd377
change-id: 20251222-axiado-ax3000-add-emmc-host-driver-support-2cc84a8f889a
Best regards,
--
Tzu-Hao Wei <twei@axiado.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v2 4/4] arm64: dts: axiado: Add eMMC PHY node
From: Tzu-Hao Wei @ 2026-02-06 8:22 UTC (permalink / raw)
To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, openbmc,
Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-phy-driver-support-v2-0-a2f59e97a92d@axiado.com>
From: SriNavmani A <srinavmani@axiado.com>
Add the eMMC PHY device tree node to the AX3000 SoC DTSI.
AX3000 has one eMMC PHY interface.
Signed-off-by: SriNavmani A <srinavmani@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
arch/arm64/boot/dts/axiado/ax3000.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm64/boot/dts/axiado/ax3000.dtsi b/arch/arm64/boot/dts/axiado/ax3000.dtsi
index 792f52e0c7dd42cbc54b0eb47e25b0fbf1a706b8..ccc8088bd8258cfb666268b14a3b0716a9ca69f4 100644
--- a/arch/arm64/boot/dts/axiado/ax3000.dtsi
+++ b/arch/arm64/boot/dts/axiado/ax3000.dtsi
@@ -507,6 +507,13 @@ uart3: serial@80520800 {
clocks = <&refclk &refclk>;
status = "disabled";
};
+
+ emmc_phy: phy@80801c00 {
+ compatible = "axiado,ax3000-emmc-phy";
+ reg = <0x0 0x80801c00 0x0 0x1000>;
+ #phy-cells = <0>;
+ status = "disabled";
+ };
};
timer {
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 3/4] MAINTAINERS: Add Axiado AX3000 eMMC PHY driver
From: Tzu-Hao Wei @ 2026-02-06 8:22 UTC (permalink / raw)
To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, openbmc,
Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-phy-driver-support-v2-0-a2f59e97a92d@axiado.com>
Add SriNavmani, Prasad and me as maintainers for Axiado AX3000 eMMC PHY
driver
Acked-by: Prasad Bolisetty <pbolisetty@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
MAINTAINERS | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 67db88b04537b431c927b73624993233eef43e3f..c33b0aa94de81c89b674e44d4813c4e3b95b7b2d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4254,6 +4254,16 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/hwmon/adi,axi-fan-control.yaml
F: drivers/hwmon/axi-fan-control.c
+AXIADO EMMC PHY DRIVER
+M: SriNavmani A <srinavmani@axiado.com>
+M: Tzu-Hao Wei <twei@axiado.com>
+M: Prasad Bolisetty <pbolisetty@axiado.com>
+L: linux-phy@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml
+F: drivers/phy/axiado/Kconfig
+F: drivers/phy/axiado/phy-axiado-emmc.c
+
AXI SPI ENGINE
M: Michael Hennerich <michael.hennerich@analog.com>
M: Nuno Sá <nuno.sa@analog.com>
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 2/4] phy: axiado: add Axiado eMMC PHY driver
From: Tzu-Hao Wei @ 2026-02-06 8:22 UTC (permalink / raw)
To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, openbmc,
Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-phy-driver-support-v2-0-a2f59e97a92d@axiado.com>
From: SriNavmani A <srinavmani@axiado.com>
It provides the required configurations for Axiado eMMC PHY driver for
HS200 mode.
Signed-off-by: SriNavmani A <srinavmani@axiado.com>
Co-developed-by: Prasad Bolisetty <pbolisetty@axiado.com>
Signed-off-by: Prasad Bolisetty <pbolisetty@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
drivers/phy/Kconfig | 1 +
drivers/phy/Makefile | 1 +
drivers/phy/axiado/Kconfig | 11 ++
drivers/phy/axiado/Makefile | 1 +
drivers/phy/axiado/phy-axiado-emmc.c | 221 +++++++++++++++++++++++++++++++++++
5 files changed, 235 insertions(+)
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 678dd0452f0aa0597773433f04d2a9ba77474d2a..b802274ea45a84bd36d7c0b7fb90e368a5c018b4 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -103,6 +103,7 @@ config PHY_NXP_PTN3222
source "drivers/phy/allwinner/Kconfig"
source "drivers/phy/amlogic/Kconfig"
+source "drivers/phy/axiado/Kconfig"
source "drivers/phy/broadcom/Kconfig"
source "drivers/phy/cadence/Kconfig"
source "drivers/phy/freescale/Kconfig"
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index bfb27fb5a494283d7fd05dd670ebd1b12df8b1a1..f1b9e4a8673bcde3fdc0fdc06a3deddb5785ced1 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o
obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o
obj-y += allwinner/ \
amlogic/ \
+ axiado/ \
broadcom/ \
cadence/ \
freescale/ \
diff --git a/drivers/phy/axiado/Kconfig b/drivers/phy/axiado/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..d159e0345345987c7f48dcd12d3237997735d2b5
--- /dev/null
+++ b/drivers/phy/axiado/Kconfig
@@ -0,0 +1,11 @@
+#
+# PHY drivers for Axiado platforms
+#
+
+config PHY_AX3000_EMMC
+ tristate "Axiado eMMC PHY driver"
+ depends on OF && (ARCH_AXIADO || COMPILE_TEST)
+ select GENERIC_PHY
+ help
+ Enables this to support for the AX3000 EMMC PHY driver.
+ If unsure, say N.
diff --git a/drivers/phy/axiado/Makefile b/drivers/phy/axiado/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..1e2b1ba016092eaffdbd7acbd9cdc8577d79b35c
--- /dev/null
+++ b/drivers/phy/axiado/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_PHY_AX3000_EMMC) += phy-axiado-emmc.o
diff --git a/drivers/phy/axiado/phy-axiado-emmc.c b/drivers/phy/axiado/phy-axiado-emmc.c
new file mode 100644
index 0000000000000000000000000000000000000000..28d2a30c3b35ee7dba917487959e226941e8ea4b
--- /dev/null
+++ b/drivers/phy/axiado/phy-axiado-emmc.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Axiado eMMC PHY driver
+ *
+ * Copyright (C) 2017 Arasan Chip Systems Inc.
+ * Copyright (C) 2022-2025 Axiado Corporation (or its affiliates).
+ *
+ * Based on Arasan Driver (sdhci-pci-arasan.c)
+ * sdhci-pci-arasan.c - Driver for Arasan PCI Controller with integrated phy.
+ */
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+/* Arasan eMMC 5.1 - PHY configuration registers */
+#define CAP_REG_IN_S1_LSB 0x00
+#define CAP_REG_IN_S1_MSB 0x04
+#define PHY_CTRL_1 0x38
+#define PHY_CTRL_2 0x3C
+#define PHY_CTRL_3 0x40
+#define STATUS 0x50
+
+#define DLL_ENBL BIT(26)
+#define RTRIM_EN BIT(21)
+#define PDB_ENBL BIT(23)
+#define RETB_ENBL BIT(1)
+
+#define REN_STRB BIT(27)
+#define REN_CMD BIT(12)
+#define REN_DAT0 BIT(13)
+#define REN_DAT1 BIT(14)
+#define REN_DAT2 BIT(15)
+#define REN_DAT3 BIT(16)
+#define REN_DAT4 BIT(17)
+#define REN_DAT5 BIT(18)
+#define REN_DAT6 BIT(19)
+#define REN_DAT7 BIT(20)
+#define REN_CMD_EN (REN_CMD | REN_DAT0 | REN_DAT1 | REN_DAT2 | \
+ REN_DAT3 | REN_DAT4 | REN_DAT5 | REN_DAT6 | REN_DAT7)
+
+/* Pull-UP Enable on CMD Line */
+#define PU_CMD BIT(3)
+#define PU_DAT0 BIT(4)
+#define PU_DAT1 BIT(5)
+#define PU_DAT2 BIT(6)
+#define PU_DAT3 BIT(7)
+#define PU_DAT4 BIT(8)
+#define PU_DAT5 BIT(9)
+#define PU_DAT6 BIT(10)
+#define PU_DAT7 BIT(11)
+#define PU_CMD_EN (PU_CMD | PU_DAT0 | PU_DAT1 | PU_DAT2 | PU_DAT3 | \
+ PU_DAT4 | PU_DAT5 | PU_DAT6 | PU_DAT7)
+
+/* Selection value for the optimum delay from 1-32 output tap lines */
+#define OTAP_DLY 0x02
+/* DLL charge pump current trim default [1000] */
+#define DLL_TRM_ICP 0x08
+/* Select the frequency range of DLL Operation */
+#define FRQ_SEL 0x01
+
+#define OTAP_SEL_MASK GENMASK(10, 7)
+#define DLL_TRM_MASK GENMASK(25, 22)
+#define DLL_FRQSEL_MASK GENMASK(27, 25)
+
+#define OTAP_SEL(x) (FIELD_PREP(OTAP_SEL_MASK, x) | OTAPDLY_EN)
+#define DLL_TRM(x) (FIELD_PREP(DLL_TRM_MASK, x) | DLL_ENBL)
+#define DLL_FRQSEL(x) FIELD_PREP(DLL_FRQSEL_MASK, x)
+
+#define OTAPDLY_EN BIT(11)
+
+#define SEL_DLY_RXCLK BIT(18)
+#define SEL_DLY_TXCLK BIT(19)
+
+#define CALDONE_MASK 0x40
+#define DLL_RDY_MASK 0x1
+#define MAX_CLK_BUF0 BIT(20)
+#define MAX_CLK_BUF1 BIT(21)
+#define MAX_CLK_BUF2 BIT(22)
+
+#define CLK_MULTIPLIER 0xC008E
+#define POLL_TIMEOUT_MS 3000
+#define POLL_DELAY_US 100
+
+struct axiado_emmc_phy {
+ void __iomem *reg_base;
+ struct device *dev;
+};
+
+static int axiado_emmc_phy_init(struct phy *phy)
+{
+ struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy);
+ struct device *dev = ax_phy->dev;
+ u32 val;
+ int ret;
+
+ val = readl(ax_phy->reg_base + PHY_CTRL_1);
+ writel(val | RETB_ENBL | RTRIM_EN, ax_phy->reg_base + PHY_CTRL_1);
+
+ val = readl(ax_phy->reg_base + PHY_CTRL_3);
+ writel(val | PDB_ENBL, ax_phy->reg_base + PHY_CTRL_3);
+
+ ret = readl_poll_timeout(ax_phy->reg_base + STATUS, val,
+ val & CALDONE_MASK, POLL_DELAY_US,
+ POLL_TIMEOUT_MS * 1000);
+ if (ret) {
+ dev_err(dev, "PHY calibration timeout\n");
+ return ret;
+ }
+
+ val = readl(ax_phy->reg_base + PHY_CTRL_1);
+ writel(val | REN_CMD_EN | PU_CMD_EN, ax_phy->reg_base + PHY_CTRL_1);
+
+ val = readl(ax_phy->reg_base + PHY_CTRL_2);
+ writel(val | REN_STRB, ax_phy->reg_base + PHY_CTRL_2);
+
+ val = readl(ax_phy->reg_base + PHY_CTRL_3);
+ writel(val | MAX_CLK_BUF0 | MAX_CLK_BUF1 | MAX_CLK_BUF2,
+ ax_phy->reg_base + PHY_CTRL_3);
+
+ writel(CLK_MULTIPLIER, ax_phy->reg_base + CAP_REG_IN_S1_MSB);
+
+ val = readl(ax_phy->reg_base + PHY_CTRL_3);
+ writel(val | SEL_DLY_RXCLK | SEL_DLY_TXCLK,
+ ax_phy->reg_base + PHY_CTRL_3);
+
+ return 0;
+}
+
+static int axiado_emmc_phy_power_on(struct phy *phy)
+{
+ struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy);
+ struct device *dev = ax_phy->dev;
+ u32 val;
+ int ret;
+
+ val = readl(ax_phy->reg_base + PHY_CTRL_1);
+ writel(val | RETB_ENBL, ax_phy->reg_base + PHY_CTRL_1);
+
+ val = readl(ax_phy->reg_base + PHY_CTRL_3);
+ writel(val | PDB_ENBL, ax_phy->reg_base + PHY_CTRL_3);
+
+ val = readl(ax_phy->reg_base + PHY_CTRL_2);
+ writel(val | OTAP_SEL(OTAP_DLY), ax_phy->reg_base + PHY_CTRL_2);
+
+ val = readl(ax_phy->reg_base + PHY_CTRL_1);
+ writel(val | DLL_TRM(DLL_TRM_ICP), ax_phy->reg_base + PHY_CTRL_1);
+
+ val = readl(ax_phy->reg_base + PHY_CTRL_3);
+ writel(val | DLL_FRQSEL(FRQ_SEL), ax_phy->reg_base + PHY_CTRL_3);
+
+ ret = read_poll_timeout(readl, val, val & DLL_RDY_MASK, POLL_DELAY_US,
+ POLL_TIMEOUT_MS * 1000, false,
+ ax_phy->reg_base + STATUS);
+ if (ret) {
+ dev_err(dev, "DLL ready timeout\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct phy_ops axiado_emmc_phy_ops = {
+ .init = axiado_emmc_phy_init,
+ .power_on = axiado_emmc_phy_power_on,
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id axiado_emmc_phy_of_match[] = {
+ { .compatible = "axiado,ax3000-emmc-phy" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, axiado_emmc_phy_of_match);
+
+static int axiado_emmc_phy_probe(struct platform_device *pdev)
+{
+ struct axiado_emmc_phy *ax_phy;
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct phy *generic_phy;
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ ax_phy = devm_kzalloc(dev, sizeof(*ax_phy), GFP_KERNEL);
+ if (!ax_phy)
+ return -ENOMEM;
+
+ ax_phy->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ax_phy->reg_base))
+ return PTR_ERR(ax_phy->reg_base);
+
+ ax_phy->dev = dev;
+
+ generic_phy = devm_phy_create(dev, dev->of_node, &axiado_emmc_phy_ops);
+ if (IS_ERR(generic_phy))
+ return dev_err_probe(dev, PTR_ERR(generic_phy),
+ "failed to create PHY\n");
+
+ phy_set_drvdata(generic_phy, ax_phy);
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver axiado_emmc_phy_driver = {
+ .probe = axiado_emmc_phy_probe,
+ .driver = {
+ .name = "axiado-emmc-phy",
+ .of_match_table = axiado_emmc_phy_of_match,
+ },
+};
+module_platform_driver(axiado_emmc_phy_driver);
+
+MODULE_DESCRIPTION("AX3000 eMMC PHY Driver");
+MODULE_AUTHOR("Axiado Corporation");
+MODULE_LICENSE("GPL");
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 1/4] dt-bindings: phy: axiado,ax3000-emmc-phy: add Axiado eMMC PHY
From: Tzu-Hao Wei @ 2026-02-06 8:22 UTC (permalink / raw)
To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, openbmc,
Tzu-Hao Wei
In-Reply-To: <20260206-axiado-ax3000-add-emmc-phy-driver-support-v2-0-a2f59e97a92d@axiado.com>
From: SriNavmani A <srinavmani@axiado.com>
Axiado AX3000 SoC contains Arasan PHY which provides the interface to the
HS200 eMMC host controller.
Signed-off-by: SriNavmani A <srinavmani@axiado.com>
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
.../bindings/phy/axiado,ax3000-emmc-phy.yaml | 37 ++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml b/Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..61700b80e93f7185e16ca9eab0922fe6bb29fe86
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/axiado,ax3000-emmc-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Axiado AX3000 Arasan eMMC PHY
+
+maintainers:
+ - SriNavmani A <srinavmani@axiado.com>
+ - Tzu-Hao Wei <twei@axiado.com>
+ - Prasad Bolisetty <pbolisetty@axiado.com>
+
+properties:
+ compatible:
+ const: axiado,ax3000-emmc-phy
+
+ reg:
+ maxItems: 1
+
+ "#phy-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - "#phy-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ phy@80801c00 {
+ compatible = "axiado,ax3000-emmc-phy";
+ reg = <0x80801c00 0x1000>;
+ #phy-cells = <0>;
+ };
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 0/4] Add eMMC PHY support for Axiado AX3000 SoC
From: Tzu-Hao Wei @ 2026-02-06 8:22 UTC (permalink / raw)
To: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-phy, devicetree, linux-arm-kernel, linux-kernel, openbmc,
Tzu-Hao Wei
Axiado AX3000 SoC contains Arasan PHY which provides the interface to the
HS200 eMMC controller.
This series includes:
1. Add bindings for Axiado AX3000 eMMC PHY
2. Add Axiado AX3000 eMMC phy driver
3. Update MAINTAINERS for the new driver
4. Update Axiado AX3000 device tree
Changes in v2:
- Fix dt-binding format
- Fix compilation error in m68k
- Use readl_poll_timeout instead of read_poll_timeout
- Link to v1: https://lore.kernel.org/r/20260109-axiado-ax3000-add-emmc-phy-driver-support-v1-0-dd43459dbfea@axiado.com
Changes: (The previous version was mixed with Host driver, so I separate
the PHY driver as a new thread)
- Fix property order in required section to match properties section
- Fixed example to use lowercase hex and proper node naming
- Removed wrapper functions, use readl/writel directly
- Replaced manual polling loops with read_poll_timeout macro
- Used devm_platform_ioremap_resource instead of separate calls
- Removed unnecessary of_match_node check
- Used dev_err_probe for error reporting
- Added proper Kconfig dependencies (ARCH_AXIADO || COMPILE_TEST)
- Fixed various coding style issues
- Link to previous patches: https://lore.kernel.org/all/20251222-axiado-ax3000-add-emmc-host-driver-support-v1-0-5457d0ebcdb4@axiado.com/
Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
---
SriNavmani A (3):
dt-bindings: phy: axiado,ax3000-emmc-phy: add Axiado eMMC PHY
phy: axiado: add Axiado eMMC PHY driver
arm64: dts: axiado: Add eMMC PHY node
Tzu-Hao Wei (1):
MAINTAINERS: Add Axiado AX3000 eMMC PHY driver
.../bindings/phy/axiado,ax3000-emmc-phy.yaml | 37 ++++
MAINTAINERS | 10 +
arch/arm64/boot/dts/axiado/ax3000.dtsi | 7 +
drivers/phy/Kconfig | 1 +
drivers/phy/Makefile | 1 +
drivers/phy/axiado/Kconfig | 11 +
drivers/phy/axiado/Makefile | 1 +
drivers/phy/axiado/phy-axiado-emmc.c | 221 +++++++++++++++++++++
8 files changed, 289 insertions(+)
---
base-commit: 63804fed149a6750ffd28610c5c1c98cce6bd377
change-id: 20260108-axiado-ax3000-add-emmc-phy-driver-support-d61aead8f622
Best regards,
--
Tzu-Hao Wei <twei@axiado.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 3/4 v2] phy: s32g: Add serdes xpcs subsystem
From: Vincent Guittot @ 2026-02-05 17:02 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: vkoul, neil.armstrong, krzk+dt, conor+dt, ciprianmarian.costea,
s32, p.zabel, ghennadi.procopciuc, Ionut.Vicovan, linux-phy,
devicetree, linux-kernel, linux-arm-kernel, netdev, horms,
Frank.li
In-Reply-To: <aYNl3yQXNa7SkpH9@shell.armlinux.org.uk>
On Wed, 4 Feb 2026 at 16:29, Russell King (Oracle)
<linux@armlinux.org.uk> wrote:
>
> Sorry, I don't have time to finish this review, nor cut down the context
> as I normally would do... I'm being bothered on company Slack, which now
> has the really bloody annoying feature of popping up a window rather than
> using KDE's notification subsystem, and that steals keyboard focus away
> from whatever one is trying to do at the time.
>
> On Tue, Feb 03, 2026 at 05:19:16PM +0100, Vincent Guittot wrote:
> > +static bool s32g_xpcs_poll_timeout(struct s32g_xpcs *xpcs, xpcs_poll_func_t func,
> > + ktime_t timeout)
> > +{
> > + ktime_t cur = ktime_get();
> > +
> > + return func(xpcs) || ktime_after(cur, timeout);
> > +}
> > +
> > +static int s32g_xpcs_wait(struct s32g_xpcs *xpcs, xpcs_poll_func_t func)
> > +{
> > + ktime_t timeout = ktime_add_ms(ktime_get(), XPCS_TIMEOUT_MS);
> > +
> > + spin_until_cond(s32g_xpcs_poll_timeout(xpcs, func, timeout));
> > + if (!func(xpcs))
> > + return -ETIMEDOUT;
>
> XPCS_TIMEOUT_MS is 300ms. spin_until_cond() spins until the condition is
> true. Do you need to tie up this CPU for up to 300ms? That seems
> excessive. What is the reason that read_poll_timeout() or similar
> couldn't be used?
This needs additional tests because some instabilities have been
reported when using read_poll_timeout in some places
>
> The advantage of read_poll_timeout() is that it will correctly handle
> the timeout vs condition being satisfied witout need for special code.
>
> > +
> > + return 0;
> > +}
> > +
> > +static int s32g_xpcs_wait_bits(struct s32g_xpcs *xpcs, unsigned int reg,
> > + unsigned int mask, unsigned int bits)
> > +{
> > + ktime_t cur;
> > + ktime_t timeout = ktime_add_ms(ktime_get(), XPCS_TIMEOUT_MS);
> > +
> > + spin_until_cond((cur = ktime_get(),
> > + (s32g_xpcs_read(xpcs, reg) & mask) == bits ||
> > + ktime_after(cur, timeout)));
> > + if ((s32g_xpcs_read(xpcs, reg) & mask) != bits)
> > + return -ETIMEDOUT;
>
> Same here:
>
> return read_poll_timeout(s32g_xpcs_read, val, (val & mask) == bits,
> 0, XPCS_TIMEOUT_MS, false,
> xpcs, reg);
>
> > +
> > + return 0;
> > +}
> > +
> > +static unsigned int s32g_xpcs_digital_status(struct s32g_xpcs *xpcs)
> > +{
> > + return s32g_xpcs_read(xpcs, VR_MII_DIG_STS);
> > +}
> > +
> > +static int s32g_xpcs_wait_power_good_state(struct s32g_xpcs *xpcs)
> > +{
> > + unsigned int val;
> > +
> > + return read_poll_timeout(s32g_xpcs_digital_status, val,
> > + FIELD_GET(PSEQ_STATE_MASK, val) == POWER_GOOD_STATE,
> > + 0,
> > + XPCS_TIMEOUT_MS, false, xpcs);
>
> This could be:
> return read_poll_timeout(s32g_xpcs_read, val,
> FIELD_GET(PSEQ_STATE_MASK, val) == POWER_GOOD_STATE,
> 0, XPCS_TIMEOUT_MS, false,
> xpcs, VR_MII_DIG_STS);
>
> eliminating the need for s32g_xpcs_digital_status().
fair enough
>
> > +}
> > +
> > +void s32g_xpcs_vreset(struct s32g_xpcs *xpcs)
> > +{
> > + /* Step 19 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, VR_RST, VR_RST);
> > +}
> > +
> > +static bool s32g_xpcs_is_not_in_reset(struct s32g_xpcs *xpcs)
> > +{
> > + unsigned int val;
> > +
> > + val = s32g_xpcs_read(xpcs, VR_MII_DIG_CTRL1);
> > +
> > + return !(val & VR_RST);
> > +}
> > +
> > +int s32g_xpcs_wait_vreset(struct s32g_xpcs *xpcs)
> > +{
> > + int ret;
> > +
> > + /* Step 20 */
> > + ret = s32g_xpcs_wait(xpcs, s32g_xpcs_is_not_in_reset);
> > + if (ret)
> > + dev_err(xpcs->dev, "XPCS%d is in reset\n", xpcs->id);
> > +
> > + return ret;
> > +}
> > +
> > +int s32g_xpcs_reset_rx(struct s32g_xpcs *xpcs)
> > +{
> > + int ret = 0;
> > +
> > + ret = s32g_xpcs_wait_power_good_state(xpcs);
> > + if (ret) {
> > + dev_err(xpcs->dev, "Failed to enter in PGOOD state after vendor reset\n");
> > + return ret;
> > + }
> > +
> > + /* Step 21 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL1,
> > + RX_RST_0, RX_RST_0);
> > +
> > + /* Step 22 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL1,
> > + RX_RST_0, 0);
> > +
> > + /* Step 23 */
> > + /* Wait until SR_MII_STS[LINK_STS] = 1 */
> > +
> > + return ret;
> > +}
> > +
> > +static int s32g_xpcs_ref_clk_sel(struct s32g_xpcs *xpcs,
> > + enum s32g_xpcs_pll ref_pll)
> > +{
> > + switch (ref_pll) {
> > + case XPCS_PLLA:
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLL_CMN_CTRL,
> > + MPLLB_SEL_0, 0);
> > + xpcs->ref = XPCS_PLLA;
> > + break;
> > + case XPCS_PLLB:
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLL_CMN_CTRL,
> > + MPLLB_SEL_0, MPLLB_SEL_0);
> > + xpcs->ref = XPCS_PLLB;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void s32g_xpcs_electrical_configure(struct s32g_xpcs *xpcs)
> > +{
> > + /* Step 2 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_EQ_CTRL0,
> > + TX_EQ_MAIN_MASK, FIELD_PREP(TX_EQ_MAIN_MASK, 0xC));
>
> Prefer hex numbers to be lower case.
ok
>
> > +
> > + /* Step 3 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_CONSUMER_10G_TX_TERM_CTRL,
> > + TX0_TERM_MASK, FIELD_PREP(TX0_TERM_MASK, 0x4));
> > +}
> > +
> > +static int s32g_xpcs_vco_cfg(struct s32g_xpcs *xpcs, enum s32g_xpcs_pll vco_pll)
> > +{
> > + unsigned int vco_ld = 0;
> > + unsigned int vco_ref = 0;
> > + unsigned int rx_baud = 0;
> > + unsigned int tx_baud = 0;
> > +
> > + switch (vco_pll) {
> > + case XPCS_PLLA:
> > + if (xpcs->mhz125) {
> > + vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1360);
> > + vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 17);
> > + } else {
> > + vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1350);
> > + vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 27);
> > + }
> > +
> > + rx_baud = FIELD_PREP(RX0_RATE_MASK, RX0_BAUD_DIV_8);
> > + tx_baud = FIELD_PREP(TX0_RATE_MASK, TX0_BAUD_DIV_4);
> > + break;
> > + case XPCS_PLLB:
> > + if (xpcs->mhz125) {
> > + vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1350);
> > + vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 27);
> > + } else {
> > + vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1344);
> > + vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 43);
> > + }
> > +
> > + rx_baud = FIELD_PREP(RX0_RATE_MASK, RX0_BAUD_DIV_2);
> > + tx_baud = FIELD_PREP(TX0_RATE_MASK, TX0_BAUD_DIV_1);
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_VCO_CAL_LD0,
> > + VCO_LD_VAL_0_MASK, vco_ld);
> > +
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_VCO_CAL_REF0,
> > + VCO_REF_LD_0_MASK, vco_ref);
> > +
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_RATE_CTRL,
> > + TX0_RATE_MASK, tx_baud);
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_RATE_CTRL,
> > + RX0_RATE_MASK, rx_baud);
> > +
> > + if (vco_pll == XPCS_PLLB) {
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_CDR_CTRL,
> > + VCO_LOW_FREQ_0, VCO_LOW_FREQ_0);
> > + } else {
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_CDR_CTRL,
> > + VCO_LOW_FREQ_0, 0);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int s32g_xpcs_init_mplla(struct s32g_xpcs *xpcs)
> > +{
> > + unsigned int val;
> > +
> > + if (!xpcs)
> > + return -EINVAL;
> > +
> > + /* Step 7 */
> > + val = 0;
> > + if (xpcs->ext_clk)
> > + val |= REF_USE_PAD;
> > +
> > + if (xpcs->mhz125) {
> > + val |= REF_MPLLA_DIV2;
> > + val |= REF_CLK_DIV2;
> > + val |= FIELD_PREP(REF_RANGE_MASK, RANGE_52_78_MHZ);
> > + } else {
> > + val |= FIELD_PREP(REF_RANGE_MASK, RANGE_26_53_MHZ);
> > + }
> > +
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_REF_CLK_CTRL,
> > + REF_MPLLA_DIV2 | REF_USE_PAD | REF_RANGE_MASK |
> > + REF_CLK_DIV2, val);
> > +
> > + /* Step 8 */
> > + if (xpcs->mhz125)
> > + val = FIELD_PREP(MLLA_MULTIPLIER_MASK, 80);
> > + else
> > + val = FIELD_PREP(MLLA_MULTIPLIER_MASK, 25);
> > +
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLA_CTRL0,
> > + MPLLA_CAL_DISABLE | MLLA_MULTIPLIER_MASK,
> > + val);
> > +
> > + /* Step 9 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLA_CTRL1,
> > + MPLLA_FRACN_CTRL_MASK, 0);
> > +
> > + /* Step 10 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLA_CTRL2,
> > + MPLLA_TX_CLK_DIV_MASK | MPLLA_DIV10_CLK_EN,
> > + FIELD_PREP(MPLLA_TX_CLK_DIV_MASK, 1) | MPLLA_DIV10_CLK_EN);
> > +
> > + /* Step 11 */
> > + if (xpcs->mhz125)
> > + val = FIELD_PREP(MPLLA_BANDWIDTH_MASK, 43);
> > + else
> > + val = FIELD_PREP(MPLLA_BANDWIDTH_MASK, 357);
> > +
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLA_CTRL3,
> > + MPLLA_BANDWIDTH_MASK, val);
> > +
> > + return 0;
> > +}
> > +
> > +static int s32g_xpcs_init_mpllb(struct s32g_xpcs *xpcs)
> > +{
> > + unsigned int val;
> > +
> > + if (!xpcs)
> > + return -EINVAL;
> > +
> > + /* Step 7 */
> > + val = 0;
> > + if (xpcs->ext_clk)
> > + val |= REF_USE_PAD;
> > +
> > + if (xpcs->mhz125) {
> > + val |= REF_MPLLB_DIV2;
> > + val |= REF_CLK_DIV2;
> > + val |= FIELD_PREP(REF_RANGE_MASK, RANGE_52_78_MHZ);
> > + } else {
> > + val |= FIELD_PREP(REF_RANGE_MASK, RANGE_26_53_MHZ);
> > + }
> > +
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_REF_CLK_CTRL,
> > + REF_MPLLB_DIV2 | REF_USE_PAD | REF_RANGE_MASK |
> > + REF_CLK_DIV2, val);
> > +
> > + /* Step 8 */
> > + if (xpcs->mhz125)
> > + val = 125 << MLLB_MULTIPLIER_OFF;
> > + else
> > + val = 39 << MLLB_MULTIPLIER_OFF;
> > +
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLB_CTRL0,
> > + MPLLB_CAL_DISABLE | MLLB_MULTIPLIER_MASK,
> > + val);
> > +
> > + /* Step 9 */
> > + if (xpcs->mhz125)
> > + val = FIELD_PREP(MPLLB_FRACN_CTRL_MASK, 0);
> > + else
> > + val = FIELD_PREP(MPLLB_FRACN_CTRL_MASK, 1044);
> > +
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLB_CTRL1,
> > + MPLLB_FRACN_CTRL_MASK, val);
> > +
> > + /* Step 10 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLB_CTRL2,
> > + MPLLB_TX_CLK_DIV_MASK | MPLLB_DIV10_CLK_EN,
> > + FIELD_PREP(MPLLB_TX_CLK_DIV_MASK, 5) | MPLLB_DIV10_CLK_EN);
> > +
> > + /* Step 11 */
> > + if (xpcs->mhz125)
> > + val = FIELD_PREP(MPLLB_BANDWIDTH_MASK, 68);
> > + else
> > + val = FIELD_PREP(MPLLB_BANDWIDTH_MASK, 102);
> > +
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLB_CTRL3,
> > + MPLLB_BANDWIDTH_MASK, val);
> > +
> > + return 0;
> > +}
> > +
> > +static void s32g_serdes_pma_high_freq_recovery(struct s32g_xpcs *xpcs)
> > +{
> > + /* PCS signal protection, PLL railout recovery */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_DBG_CTRL, SUPPRESS_LOS_DET | RX_DT_EN_CTL,
> > + SUPPRESS_LOS_DET | RX_DT_EN_CTL);
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MISC_CTRL0,
> > + PLL_CTRL, PLL_CTRL);
> > +}
> > +
> > +static void s32g_serdes_pma_configure_tx_eq_post(struct s32g_xpcs *xpcs)
> > +{
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_EQ_CTRL1,
> > + TX_EQ_OVR_RIDE, TX_EQ_OVR_RIDE);
> > +}
> > +
> > +static int s32g_serdes_bifurcation_pll_transit(struct s32g_xpcs *xpcs,
> > + enum s32g_xpcs_pll target_pll)
> > +{
> > + int ret = 0;
> > + struct device *dev = xpcs->dev;
> > +
> > + /* Configure XPCS speed and VCO */
> > + if (target_pll == XPCS_PLLA) {
> > + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, EN_2_5G_MODE, 0);
> > + s32g_xpcs_vco_cfg(xpcs, XPCS_PLLA);
> > + } else {
> > + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1,
> > + EN_2_5G_MODE, EN_2_5G_MODE);
> > + s32g_xpcs_vco_cfg(xpcs, XPCS_PLLB);
> > + }
>
> I am really not happy with this driver being
> PHY_INTERFACE_MODE_SGMII-only but supporting running that at 2.5Gbps.
> In the kernel, PHY_INTERFACE_MODE_SGMII is strictly _Cisco_ SGMII only,
> which means the version of it clocked at 1.25GHz, not 3.125GHz.
>
> OCSGMII or whatever random name you call it tends to be only supported
> without inband AN, and we have pushed everyone to adopt
> PHY_INTERFACE_MODE_2500BASEX for that. Please do the same.
>
> Should this SerDes be connected to a SFP cage, it will need to support
> dynamically switching between Cisco SGMII and 2500BASE-X.
okay, this needs to be checked with SoC Team
>
> > +
> > + /* Signal that clock are not available */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL1,
> > + TX_CLK_RDY_0, 0);
> > +
> > + /* Select PLL reference */
> > + if (target_pll == XPCS_PLLA)
> > + s32g_xpcs_ref_clk_sel(xpcs, XPCS_PLLA);
> > + else
> > + s32g_xpcs_ref_clk_sel(xpcs, XPCS_PLLB);
> > +
> > + /* Initiate transmitter TX reconfiguration request */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL2,
> > + TX_REQ_0, TX_REQ_0);
> > +
> > + /* Wait for transmitter to reconfigure */
> > + ret = s32g_xpcs_wait_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL2,
> > + TX_REQ_0, 0);
> > + if (ret) {
> > + dev_err(dev, "Switch to TX_REQ_0 failed\n");
> > + return ret;
> > + }
> > +
> > + /* Initiate transmitter RX reconfiguration request */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL2,
> > + RX_REQ_0, RX_REQ_0);
> > +
> > + /* Wait for receiver to reconfigure */
> > + ret = s32g_xpcs_wait_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL2,
> > + RX_REQ_0, 0);
> > + if (ret) {
> > + dev_err(dev, "Switch to RX_REQ_0 failed\n");
> > + return ret;
> > + }
> > +
> > + /* Signal that clock are available */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL1,
> > + TX_CLK_RDY_0, TX_CLK_RDY_0);
> > +
> > + /* Flush internal logic */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, INIT, INIT);
> > +
> > + /* Wait for init */
> > + ret = s32g_xpcs_wait_bits(xpcs, VR_MII_DIG_CTRL1, INIT, 0);
> > + if (ret) {
> > + dev_err(dev, "XPCS INIT failed\n");
> > + return ret;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +/*
> > + * phylink_pcs_ops
> > + */
> > +
> > +static unsigned int s32cc_phylink_pcs_inband_caps(struct phylink_pcs *pcs,
> > + phy_interface_t interface)
> > +{
> > + switch (interface) {
> > + case PHY_INTERFACE_MODE_SGMII:
> > + return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
> > +
> > + default:
> > + return 0;
> > + }
> > +}
> > +
> > +static int s32cc_phylink_pcs_config(struct phylink_pcs *pcs,
> > + unsigned int neg_mode,
> > + phy_interface_t interface,
> > + const unsigned long *advertising,
> > + bool permit_pause_to_mac)
> > +{
> > + struct s32g_xpcs *xpcs = phylink_pcs_to_s32g_xpcs(pcs);
> > +
> > + /* Step 1: Disable SGMII AN */
> > + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0);
> > +
> > + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> > + MII_AN_INTR_EN,
> > + 0);
> > +
> > + if (!(neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED))
> > + return 0;
> > +
> > + /* Step 2 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> > + PCS_MODE_MASK,
> > + FIELD_PREP(PCS_MODE_MASK, PCS_MODE_SGMII));
> > +
> > + /* Step 3 */
> > + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL,
> > + SS13 | SS6,
> > + SS6);
> > +
> > + /* Step 4 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> > + MII_CTRL,
> > + 0);
> > + /* Step 5 and 8 */
> > + if (xpcs->pcie_shared == PCIE_XPCS_2G5) {
> > + s32g_xpcs_write(xpcs, VR_MII_LINK_TIMER_CTRL, 0x2faf);
> > + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1,
> > + MAC_AUTO_SW, MAC_AUTO_SW);
> > + } else {
> > + s32g_xpcs_write(xpcs, VR_MII_LINK_TIMER_CTRL, 0x7a1);
> > + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, MAC_AUTO_SW, 0);
> > + }
> > +
> > + /* Step 6 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1,
> > + CL37_TMR_OVRRIDE, CL37_TMR_OVRRIDE);
> > +
> > + /* Step 7 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> > + MII_AN_INTR_EN,
> > + MII_AN_INTR_EN);
> > +
> > + /* Step 9: Enable SGMII AN */
> > + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, AN_ENABLE);
> > +
> > + return 0;
> > +}
> > +
> > +static void s32cc_phylink_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
> > + struct phylink_link_state *state)
> > +{
> > + struct s32g_xpcs *xpcs = phylink_pcs_to_s32g_xpcs(pcs);
> > + bool ss6, ss13, an_enabled;
> > + struct device *dev = xpcs->dev;
> > + unsigned int val, ss;
> > +
> > + an_enabled = (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED);
> > +
> > + if (an_enabled) {
> > + state->link = 0;
> > + val = s32g_xpcs_read(xpcs, VR_MII_AN_INTR_STS);
> > +
> > + /* Interrupt is raised with each SGMII AN that is in cases
> > + * Link down - Every SGMII link timer expire
> > + * Link up - Once before link goes up
> > + * So either linkup or raised interrupt mean AN was completed
> > + */
> > + if ((val & CL37_ANCMPLT_INTR) || (val & CL37_ANSGM_STS_LINK)) {
> > + state->an_complete = 1;
> > + if (val & CL37_ANSGM_STS_LINK)
> > + state->link = 1;
> > + else
> > + return;
> > + if (val & CL37_ANSGM_STS_DUPLEX)
> > + state->duplex = DUPLEX_FULL;
> > + else
> > + state->duplex = DUPLEX_HALF;
> > + ss = FIELD_GET(CL37_ANSGM_STS_SPEED_MASK, val);
> > + } else {
> > + return;
> > + }
> > +
> > + } else {
> > + val = s32g_xpcs_read(xpcs, SR_MII_STS);
> > + state->link = !!(val & LINK_STS);
> > + state->an_complete = 0;
> > + state->pause = MLO_PAUSE_NONE;
> > +
> > + val = s32g_xpcs_read(xpcs, SR_MII_CTRL);
> > + if (val & DUPLEX_MODE)
> > + state->duplex = DUPLEX_FULL;
> > + else
> > + state->duplex = DUPLEX_HALF;
> > +
> > + /*
> > + * Build similar value as CL37_ANSGM_STS_SPEED with
> > + * SS6 and SS13 of SR_MII_CTRL:
> > + * - 0 for 10 Mbps
> > + * - 1 for 100 Mbps
> > + * - 2 for 1000 Mbps
> > + */
> > + ss6 = !!(val & SS6);
> > + ss13 = !!(val & SS13);
> > + ss = ss6 << 1 | ss13;
> > + }
> > +
> > + switch (ss) {
> > + case CL37_ANSGM_10MBPS:
> > + state->speed = SPEED_10;
> > + break;
> > + case CL37_ANSGM_100MBPS:
> > + state->speed = SPEED_100;
> > + break;
> > + case CL37_ANSGM_1000MBPS:
> > + state->speed = SPEED_1000;
> > + break;
> > + default:
> > + dev_err(dev, "Failed to interpret the value of SR_MII_CTRL\n");
> > + break;
> > + }
> > +
> > + val = s32g_xpcs_read(xpcs, VR_MII_DIG_CTRL1);
> > + if ((val & EN_2_5G_MODE) && state->speed == SPEED_1000)
> > + state->speed = SPEED_2500;
> > +
> > + /* Cover SGMII AN inability to distigunish between 1G and 2.5G */
> > + if ((val & EN_2_5G_MODE) &&
> > + state->speed != SPEED_2500 && an_enabled) {
> > + dev_err(dev, "Speed not supported in SGMII AN mode\n");
> > + }
> > +}
> > +
> > +static void s32cc_phylink_pcs_link_up(struct phylink_pcs *pcs,
> > + unsigned int neg_mode,
> > + phy_interface_t interface, int speed,
> > + int duplex)
> > +{
> > + struct s32g_xpcs *xpcs = phylink_pcs_to_s32g_xpcs(pcs);
> > + struct device *dev = xpcs->dev;
> > + bool an_enabled = (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED);
> > + unsigned int val;
> > + int ret;
> > +
> > + dev_dbg(dev, "xpcs_%d: speed=%u duplex=%d an=%d\n", xpcs->id,
> > + speed, duplex, an_enabled);
> > +
> > + if (an_enabled)
> > + return;
> > +
> > + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0);
> > + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, MII_AN_INTR_EN, 0);
> > + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, MII_CTRL, 0);
>
> Haven't you already disabled AN in .pcs_config() ? This method doesn't
> change the AN enable state, the only time that happens is when
> .pcs_config() will be called. All other cases of passing neg_mode are
> merely informational.
Fair enough, this is probably not needed anymore with the changes in .pcs_config
>
> > +
> > + if (duplex == DUPLEX_FULL)
> > + val = DUPLEX_MODE;
> > + else
> > + val = 0;
> > +
> > + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, val);
> > +
> > + switch (speed) {
> > + case SPEED_10:
> > + val = 0;
> > + break;
> > + case SPEED_100:
> > + val = SS13;
> > + break;
> > + case SPEED_1000:
> > + val = SS6;
> > + break;
> > + case SPEED_2500:
> > + val = SS6;
> > + break;
> > + default:
> > + dev_err(dev, "Speed not supported\n");
> > + break;
> > + }
> > +
> > + if (speed == SPEED_2500) {
> > + ret = s32g_serdes_bifurcation_pll_transit(xpcs, XPCS_PLLB);
> > + if (ret)
> > + dev_err(dev, "Switch to PLLB failed\n");
> > + } else {
> > + ret = s32g_serdes_bifurcation_pll_transit(xpcs, XPCS_PLLA);
> > + if (ret)
> > + dev_err(dev, "Switch to PLLA failed\n");
> > + }
>
> This is a protocol transition, and isn't something that can be handled
> here. Cisco SGMII (PHY_INTERFACE_MODE_SGMII) does not support 2500Mbps
> and phylink will not allow it.
>
> See my comments for s32g_serdes_bifurcation_pll_transit().
>
> > +
> > + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, SS6 | SS13, val);
> > +}
> > +
> > +static const struct phylink_pcs_ops s32cc_phylink_pcs_ops = {
> > + .pcs_inband_caps = s32cc_phylink_pcs_inband_caps,
> > + .pcs_get_state = s32cc_phylink_pcs_get_state,
> > + .pcs_config = s32cc_phylink_pcs_config,
> > + .pcs_link_up = s32cc_phylink_pcs_link_up,
> > +};
> > +
> > +/*
> > + * Serdes functions for initializing/configuring/releasing the xpcs
> > + */
> > +
> > +int s32g_xpcs_pre_pcie_2g5(struct s32g_xpcs *xpcs)
> > +{
> > + int ret;
> > +
> > + /* Enable voltage boost */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL1, VBOOST_EN_0,
> > + VBOOST_EN_0);
> > +
> > + /* TX rate baud */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_RATE_CTRL, 0x7, 0x0U);
> > +
> > + /* Rx rate baud/2 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_RATE_CTRL, 0x3U, 0x1U);
> > +
> > + /* Set low-frequency operating band */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_CDR_CTRL, CDR_SSC_EN_0,
> > + VCO_LOW_FREQ_0);
> > +
> > + ret = s32g_serdes_bifurcation_pll_transit(xpcs, XPCS_PLLB);
> > + if (ret)
> > + dev_err(xpcs->dev, "Switch to PLLB failed\n");
> > +
> > + return ret;
> > +}
> > +
> > +int s32g_xpcs_init_plls(struct s32g_xpcs *xpcs)
> > +{
> > + int ret;
> > + struct device *dev = xpcs->dev;
> > +
> > + if (!xpcs->ext_clk) {
> > + /* Step 1 */
> > + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, BYP_PWRUP, BYP_PWRUP);
> > + } else if (xpcs->pcie_shared == NOT_SHARED) {
> > + ret = s32g_xpcs_wait_power_good_state(xpcs);
> > + if (ret)
> > + return ret;
> > + } else if (xpcs->pcie_shared == PCIE_XPCS_2G5) {
> > + ret = s32g_xpcs_wait_power_good_state(xpcs);
> > + if (ret)
> > + return ret;
> > + /* Configure equalization */
> > + s32g_serdes_pma_configure_tx_eq_post(xpcs);
> > + s32g_xpcs_electrical_configure(xpcs);
> > +
> > + /* Enable receiver recover */
> > + s32g_serdes_pma_high_freq_recovery(xpcs);
> > + return 0;
> > + }
> > +
> > + s32g_xpcs_electrical_configure(xpcs);
> > +
> > + s32g_xpcs_ref_clk_sel(xpcs, XPCS_PLLA);
> > + ret = s32g_xpcs_init_mplla(xpcs);
> > + if (ret) {
> > + dev_err(dev, "Failed to initialize PLLA\n");
> > + return ret;
> > + }
> > + ret = s32g_xpcs_init_mpllb(xpcs);
> > + if (ret) {
> > + dev_err(dev, "Failed to initialize PLLB\n");
> > + return ret;
> > + }
> > + s32g_xpcs_vco_cfg(xpcs, XPCS_PLLA);
> > +
> > + /* Step 18 */
> > + if (!xpcs->ext_clk)
> > + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, BYP_PWRUP, 0);
> > +
> > + /* Will be cleared by Step 19 Vreset */
> > + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0);
> > + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, DUPLEX_MODE);
> > +
> > + return ret;
> > +}
> > +
> > +void s32g_xpcs_disable_an(struct s32g_xpcs *xpcs)
> > +{
> > + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, DUPLEX_MODE);
> > + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0);
> > +}
>
> Sorry, but why? You should never override phylink's requests.
Serdes sometimes needs to be reset and the AN is enabled by default
after the reset and even before pcs_config has been called. This is
called during the inti of serdes after an ip reset.
That being said this might not be needed anymore after the change in
pcs_config that disables AN
>
> > +
> > +int s32g_xpcs_init(struct s32g_xpcs *xpcs, struct device *dev,
> > + unsigned char id, void __iomem *base, bool ext_clk,
> > + unsigned long rate, enum s32g_xpcs_shared pcie_shared)
> > +{
> > + struct regmap_config conf;
> > +
> > + if (rate != (125 * HZ_PER_MHZ) && rate != (100 * HZ_PER_MHZ)) {
> > + dev_err(dev, "XPCS cannot operate @%lu HZ\n", rate);
> > + return -EINVAL;
> > + }
> > +
> > + xpcs->base = base;
> > + xpcs->ext_clk = ext_clk;
> > + xpcs->id = id;
> > + xpcs->dev = dev;
> > + xpcs->pcie_shared = pcie_shared;
> > +
> > + if (rate == (125 * HZ_PER_MHZ))
> > + xpcs->mhz125 = true;
> > + else
> > + xpcs->mhz125 = false;
> > +
> > + conf = s32g_xpcs_regmap_config;
> > +
> > + if (!id)
> > + conf.name = "xpcs0";
> > + else
> > + conf.name = "xpcs1";
> > +
> > + xpcs->regmap = devm_regmap_init(dev, NULL, xpcs, &conf);
> > + if (IS_ERR(xpcs->regmap))
> > + return dev_err_probe(dev, PTR_ERR(xpcs->regmap),
> > + "Failed to init register amp\n");
> > +
> > + /* Phylink PCS */
> > + xpcs->pcs.ops = &s32cc_phylink_pcs_ops;
> > + xpcs->pcs.poll = true;
> > + __set_bit(PHY_INTERFACE_MODE_SGMII, xpcs->pcs.supported_interfaces);
> > +
> > + return 0;
> > +}
> > diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
> > index 45184a3cdd69..bb7f59897faf 100644
> > --- a/drivers/phy/freescale/Kconfig
> > +++ b/drivers/phy/freescale/Kconfig
> > @@ -66,6 +66,7 @@ config PHY_S32G_SERDES
> > tristate "NXP S32G SERDES support"
> > depends on ARCH_S32 || COMPILE_TEST
> > select GENERIC_PHY
> > + select REGMAP
> > help
> > This option enables support for S23G SerDes PHY used for
> > PCIe & Ethernet
> > diff --git a/drivers/phy/freescale/phy-nxp-s32g-serdes.c b/drivers/phy/freescale/phy-nxp-s32g-serdes.c
> > index 321a80c02be5..f2f7eb5aa327 100644
> > --- a/drivers/phy/freescale/phy-nxp-s32g-serdes.c
> > +++ b/drivers/phy/freescale/phy-nxp-s32g-serdes.c
> > @@ -12,12 +12,14 @@
> > #include <linux/module.h>
> > #include <linux/of_platform.h>
> > #include <linux/of_address.h>
> > +#include <linux/pcs/pcs-nxp-s32g-xpcs.h>
> > #include <linux/phy/phy.h>
> > #include <linux/platform_device.h>
> > #include <linux/processor.h>
> > #include <linux/reset.h>
> > #include <linux/units.h>
> >
> > +#define S32G_SERDES_XPCS_MAX 2
> > #define S32G_SERDES_MODE_MAX 5
> >
> > #define EXTERNAL_CLK_NAME "ext"
> > @@ -32,6 +34,52 @@
> > #define S32G_PCIE_PHY_MPLLA_CTRL 0x10
> > #define MPLL_STATE BIT(30)
> >
> > +#define S32G_PCIE_PHY_MPLLB_CTRL 0x14
> > +#define MPLLB_SSC_EN BIT(1)
> > +
> > +#define S32G_PCIE_PHY_EXT_CTRL_SEL 0x18
> > +#define EXT_PHY_CTRL_SEL BIT(0)
> > +
> > +#define S32G_PCIE_PHY_EXT_BS_CTRL 0x1C
> > +#define EXT_BS_TX_LOWSWING BIT(6)
> > +#define EXT_BS_RX_BIGSWING BIT(5)
> > +#define EXT_BS_RX_LEVEL_MASK GENMASK(4, 0)
> > +
> > +#define S32G_PCIE_PHY_REF_CLK_CTRL 0x20
> > +#define EXT_REF_RANGE_MASK GENMASK(5, 3)
> > +#define REF_CLK_DIV2_EN BIT(2)
> > +#define REF_CLK_MPLLB_DIV2_EN BIT(1)
> > +
> > +#define S32G_PCIE_PHY_EXT_MPLLA_CTRL_1 0x30
> > +#define EXT_MPLLA_BANDWIDTH_MASK GENMASK(15, 0)
> > +
> > +#define S32G_PCIE_PHY_EXT_MPLLB_CTRL_1 0x40
> > +#define EXT_MPLLB_DIV_MULTIPLIER_MASK GENMASK(31, 24)
> > +#define EXT_MPLLB_DIV_CLK_EN BIT(19)
> > +#define EXT_MPLLB_DIV8_CLK_EN BIT(18)
> > +#define EXT_MPLLB_DIV10_CLK_EN BIT(16)
> > +#define EXT_MPLLB_BANDWIDTH_MASK GENMASK(15, 0)
> > +
> > +#define S32G_PCIE_PHY_EXT_MPLLB_CTRL_2 0x44
> > +#define EXT_MPLLB_FRACN_CTRL_MASK GENMASK(22, 12)
> > +#define MPLLB_MULTIPLIER_MASK GENMASK(8, 0)
> > +
> > +#define S32G_PCIE_PHY_EXT_MPLLB_CTRL_3 0x48
> > +#define EXT_MPLLB_WORD_DIV2_EN BIT(31)
> > +#define EXT_MPLLB_TX_CLK_DIV_MASK GENMASK(30, 28)
> > +
> > +#define S32G_PCIE_PHY_EXT_MISC_CTRL_1 0xA0
> > +#define EXT_RX_LOS_THRESHOLD_MASK GENMASK(7, 1)
> > +#define EXT_RX_VREF_CTRL_MASK GENMASK(28, 24)
> > +
> > +#define S32G_PCIE_PHY_EXT_MISC_CTRL_2 0xA4
> > +#define EXT_TX_VBOOST_LVL_MASK GENMASK(18, 16)
> > +#define EXT_TX_TERM_CTRL_MASK GENMASK(26, 24)
> > +
> > +#define S32G_PCIE_PHY_XPCS1_RX_OVRD_CTRL 0xD0
> > +#define XPCS1_RX_VCO_LD_VAL_MASK GENMASK(28, 16)
> > +#define XPCS1_RX_REF_LD_VAL_MASK GENMASK(14, 8)
> > +
> > #define S32G_SS_RW_REG_0 0xF0
> > #define SUBMODE_MASK GENMASK(3, 0)
> > #define CLKEN_MASK BIT(23)
> > @@ -44,6 +92,9 @@
> >
> > #define S32G_PHY_REG_DATA 0x4
> >
> > +#define S32G_PHY_RST_CTRL 0x8
> > +#define WARM_RST BIT(1)
> > +
> > #define RAWLANE0_DIG_PCS_XF_RX_EQ_DELTA_IQ_OVRD_IN 0x3019
> > #define RAWLANE1_DIG_PCS_XF_RX_EQ_DELTA_IQ_OVRD_IN 0x3119
> >
> > @@ -76,16 +127,33 @@ struct s32g_pcie_ctrl {
> > bool powered_on;
> > };
> >
> > +struct s32g_xpcs_ctrl {
> > + struct s32g_xpcs *phys[2];
> > + void __iomem *base0, *base1;
> > +};
> > +
> > struct s32g_serdes {
> > struct s32g_serdes_ctrl ctrl;
> > struct s32g_pcie_ctrl pcie;
> > + struct s32g_xpcs_ctrl xpcs;
> > struct device *dev;
> > + u8 lanes_status;
> > };
> >
> > /* PCIe phy subsystem */
> >
> > #define S32G_SERDES_PCIE_FREQ (100 * HZ_PER_MHZ)
> >
> > +static void s32g_pcie_phy_reset(struct s32g_serdes *serdes)
> > +{
> > + u32 val;
> > +
> > + val = readl(serdes->pcie.phy_base + S32G_PHY_RST_CTRL);
> > + writel(val | WARM_RST, serdes->pcie.phy_base + S32G_PHY_RST_CTRL);
> > + usleep_range(1000, 1100);
> > + writel(val, serdes->pcie.phy_base + S32G_PHY_RST_CTRL);
> > +}
> > +
> > static int s32g_pcie_check_clk(struct s32g_serdes *serdes)
> > {
> > struct s32g_serdes_ctrl *sctrl = &serdes->ctrl;
> > @@ -277,6 +345,192 @@ static struct phy *s32g_serdes_phy_xlate(struct device *dev,
> > return phy;
> > }
> >
> > +/* XPCS subsystem */
> > +
> > +static int s32g_serdes_xpcs_init(struct s32g_serdes *serdes, int id)
> > +{
> > + struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
> > + struct s32g_xpcs_ctrl *xpcs = &serdes->xpcs;
> > + enum s32g_xpcs_shared shared = NOT_SHARED;
> > + unsigned long rate = ctrl->ref_clk_rate;
> > + struct device *dev = serdes->dev;
> > + void __iomem *base;
> > +
> > + if (!id)
> > + base = xpcs->base0;
> > + else
> > + base = xpcs->base1;
> > +
> > + if (ctrl->ss_mode == 1 || ctrl->ss_mode == 2)
> > + shared = PCIE_XPCS_1G;
> > + else if (ctrl->ss_mode == 5)
> > + shared = PCIE_XPCS_2G5;
> > +
> > + return s32g_xpcs_init(xpcs->phys[id], dev, id, base,
> > + ctrl->ext_clk, rate, shared);
> > +}
> > +
> > +static void s32g_serdes_prepare_pma_mode5(struct s32g_serdes *serdes)
> > +{
> > + u32 val;
> > + /* Configure TX_VBOOST_LVL and TX_TERM_CTRL */
> > + val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_2);
> > + val &= ~(EXT_TX_VBOOST_LVL_MASK | EXT_TX_TERM_CTRL_MASK);
> > + val |= FIELD_PREP(EXT_TX_VBOOST_LVL_MASK, 0x3) |
> > + FIELD_PREP(EXT_TX_TERM_CTRL_MASK, 0x4);
> > + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_2);
> > +
> > + /* Enable phy external control */
> > + val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_CTRL_SEL);
> > + val |= EXT_PHY_CTRL_SEL;
> > + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_CTRL_SEL);
> > +
> > + /* Configure ref range, disable PLLB/ref div2 */
> > + val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_REF_CLK_CTRL);
> > + val &= ~(REF_CLK_DIV2_EN | REF_CLK_MPLLB_DIV2_EN | EXT_REF_RANGE_MASK);
> > + val |= FIELD_PREP(EXT_REF_RANGE_MASK, 0x3);
> > + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_REF_CLK_CTRL);
> > +
> > + /* Configure multiplier */
> > + val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_2);
> > + val &= ~(MPLLB_MULTIPLIER_MASK | EXT_MPLLB_FRACN_CTRL_MASK | BIT(24) | BIT(28));
> > + val |= FIELD_PREP(MPLLB_MULTIPLIER_MASK, 0x27U) |
> > + FIELD_PREP(EXT_MPLLB_FRACN_CTRL_MASK, 0x414);
> > + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_2);
> > +
> > + val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_MPLLB_CTRL);
> > + val &= ~MPLLB_SSC_EN;
> > + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_MPLLB_CTRL);
> > +
> > + /* Configure tx lane division, disable word clock div2*/
> > + val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_3);
> > + val &= ~(EXT_MPLLB_WORD_DIV2_EN | EXT_MPLLB_TX_CLK_DIV_MASK);
> > + val |= FIELD_PREP(EXT_MPLLB_TX_CLK_DIV_MASK, 0x5);
> > + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_3);
> > +
> > + /* Configure bandwidth for filtering and div10*/
> > + val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_1);
> > + val &= ~(EXT_MPLLB_BANDWIDTH_MASK | EXT_MPLLB_DIV_CLK_EN |
> > + EXT_MPLLB_DIV8_CLK_EN | EXT_MPLLB_DIV_MULTIPLIER_MASK);
> > + val |= FIELD_PREP(EXT_MPLLB_BANDWIDTH_MASK, 0x5f) | EXT_MPLLB_DIV10_CLK_EN;
> > + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_1);
> > +
> > + val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLA_CTRL_1);
> > + val &= ~(EXT_MPLLA_BANDWIDTH_MASK);
> > + val |= FIELD_PREP(EXT_MPLLA_BANDWIDTH_MASK, 0xc5);
> > + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLA_CTRL_1);
> > +
> > + /* Configure VCO */
> > + val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_XPCS1_RX_OVRD_CTRL);
> > + val &= ~(XPCS1_RX_VCO_LD_VAL_MASK | XPCS1_RX_REF_LD_VAL_MASK);
> > + val |= FIELD_PREP(XPCS1_RX_VCO_LD_VAL_MASK, 0x540) |
> > + FIELD_PREP(XPCS1_RX_REF_LD_VAL_MASK, 0x2b);
> > + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_XPCS1_RX_OVRD_CTRL);
> > +
> > + /* Boundary scan control */
> > + val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_BS_CTRL);
> > + val &= ~(EXT_BS_RX_LEVEL_MASK | EXT_BS_TX_LOWSWING);
> > + val |= FIELD_PREP(EXT_BS_RX_LEVEL_MASK, 0xB) | EXT_BS_RX_BIGSWING;
> > + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_BS_CTRL);
> > +
> > + /* Rx loss threshold */
> > + val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_1);
> > + val &= ~(EXT_RX_LOS_THRESHOLD_MASK | EXT_RX_VREF_CTRL_MASK);
> > + val |= FIELD_PREP(EXT_RX_LOS_THRESHOLD_MASK, 0x3U) |
> > + FIELD_PREP(EXT_RX_VREF_CTRL_MASK, 0x11U);
> > + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_1);
> > +}
> > +
> > +static int s32g_serdes_enable_mode5(struct s32g_serdes *serdes, struct s32g_xpcs *xpcs)
> > +{
> > + int ret;
> > +
> > + s32g_serdes_prepare_pma_mode5(serdes);
> > +
> > + ret = s32g_xpcs_pre_pcie_2g5(xpcs);
> > + if (ret) {
> > + dev_err(serdes->dev,
> > + "Failed to prepare SerDes for PCIE & XPCS @ 2G5 mode\n");
> > + return ret;
> > + }
> > +
> > + s32g_pcie_phy_reset(serdes);
> > +
> > + return 0;
> > +}
> > +
> > +static int s32g_serdes_init_clks(struct s32g_serdes *serdes)
> > +{
> > + struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
> > + struct s32g_xpcs_ctrl *xpcs = &serdes->xpcs;
> > + struct s32g_xpcs *order[2];
> > + size_t i;
> > + int ret;
> > +
> > + switch (ctrl->ss_mode) {
> > + case 0:
> > + return 0;
> > + case 1:
> > + order[0] = xpcs->phys[0];
> > + order[1] = NULL;
> > + break;
> > + case 2:
> > + case 5:
> > + order[0] = xpcs->phys[1];
> > + order[1] = NULL;
> > + break;
> > + case 3:
> > + order[0] = xpcs->phys[1];
> > + order[1] = xpcs->phys[0];
> > + break;
> > + case 4:
> > + order[0] = xpcs->phys[0];
> > + order[1] = xpcs->phys[1];
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + for (i = 0; i < ARRAY_SIZE(order); i++) {
> > + if (!order[i])
> > + continue;
> > +
> > + ret = s32g_xpcs_init_plls(order[i]);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + for (i = 0; i < ARRAY_SIZE(order); i++) {
> > + if (!order[i])
> > + continue;
> > +
> > + if (ctrl->ss_mode == 5) {
> > + ret = s32g_serdes_enable_mode5(serdes, order[i]);
> > + if (ret)
> > + return ret;
> > + } else {
> > + s32g_xpcs_vreset(order[i]);
> > + }
> > + }
> > +
> > + for (i = 0; i < ARRAY_SIZE(order); i++) {
> > + if (!order[i])
> > + continue;
> > +
> > + ret = s32g_xpcs_wait_vreset(order[i]);
> > + if (ret)
> > + return ret;
> > +
> > + ret = s32g_xpcs_reset_rx(order[i]);
> > + if (ret)
> > + return ret;
> > +
> > + s32g_xpcs_disable_an(order[i]);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > /* Serdes subsystem */
> >
> > static int s32g_serdes_assert_reset(struct s32g_serdes *serdes)
> > @@ -331,6 +585,10 @@ static int s32g_serdes_init(struct s32g_serdes *serdes)
> > return ret;
> > }
> >
> > + /*
> > + * We have a tight timing for the init sequence and any delay linked to
> > + * printk as an example can fail the init after reset
> > + */
> > ret = s32g_serdes_assert_reset(serdes);
> > if (ret)
> > goto disable_clks;
> > @@ -363,7 +621,13 @@ static int s32g_serdes_init(struct s32g_serdes *serdes)
> > dev_info(serdes->dev, "Using mode %d for SerDes subsystem\n",
> > ctrl->ss_mode);
> >
> > - return 0;
> > + ret = s32g_serdes_init_clks(serdes);
> > + if (ret) {
> > + dev_err(serdes->dev, "XPCS init failed\n");
> > + goto disable_clks;
> > + }
> > +
> > + return ret;
> >
> > disable_clks:
> > clk_bulk_disable_unprepare(serdes->ctrl.nclks,
> > @@ -449,12 +713,32 @@ static int s32g_serdes_get_pcie_resources(struct platform_device *pdev, struct s
> > return 0;
> > }
> >
> > +static int s32g_serdes_get_xpcs_resources(struct platform_device *pdev, struct s32g_serdes *serdes)
> > +{
> > + struct s32g_xpcs_ctrl *xpcs = &serdes->xpcs;
> > + struct device *dev = &pdev->dev;
> > +
> > + xpcs->base0 = devm_platform_ioremap_resource_byname(pdev, "xpcs0");
> > + if (IS_ERR(xpcs->base0)) {
> > + dev_err(dev, "Failed to map 'xpcs0'\n");
> > + return PTR_ERR(xpcs->base0);
> > + }
> > +
> > + xpcs->base1 = devm_platform_ioremap_resource_byname(pdev, "xpcs1");
> > + if (IS_ERR(xpcs->base1)) {
> > + dev_err(dev, "Failed to map 'xpcs1'\n");
> > + return PTR_ERR(xpcs->base1);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > static int s32g2_serdes_create_phy(struct s32g_serdes *serdes, struct device_node *child_node)
> > {
> > struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
> > struct phy_provider *phy_provider;
> > struct device *dev = serdes->dev;
> > - int ss_mode = ctrl->ss_mode;
> > + int ret, ss_mode = ctrl->ss_mode;
> > struct phy *phy;
> >
> > if (of_device_is_compatible(child_node, "nxp,s32g2-serdes-pcie-phy")) {
> > @@ -476,6 +760,37 @@ static int s32g2_serdes_create_phy(struct s32g_serdes *serdes, struct device_nod
> > if (IS_ERR(phy_provider))
> > return PTR_ERR(phy_provider);
> >
> > + } else if (of_device_is_compatible(child_node, "nxp,s32g2-serdes-xpcs")) {
> > + struct s32g_xpcs_ctrl *xpcs_ctrl = &serdes->xpcs;
> > + struct s32g_xpcs *xpcs;
> > + int port;
> > +
> > + /* no Ethernet phy lane */
> > + if (ss_mode == 0)
> > + return 0;
> > +
> > + /* Get XPCS port number connected to the lane */
> > + if (of_property_read_u32(child_node, "reg", &port))
> > + return -EINVAL;
> > +
> > + /* XPCS1 is not used */
> > + if (ss_mode == 1 && port == 1)
> > + return -EINVAL;
> > +
> > + /* XPCS0 is not used */
> > + if (ss_mode == 2 && port == 0)
> > + return -EINVAL;
> > +
> > + xpcs = devm_kmalloc(dev, sizeof(*xpcs), GFP_KERNEL);
> > + if (!xpcs)
> > + return -ENOMEM;
> > +
> > + xpcs_ctrl->phys[port] = xpcs;
> > +
> > + ret = s32g_serdes_xpcs_init(serdes, port);
> > + if (ret)
> > + return ret;
> > +
> > } else {
> > dev_warn(dev, "Skipping unknown child node %pOFn\n", child_node);
> > }
> > @@ -517,6 +832,10 @@ static int s32g_serdes_probe(struct platform_device *pdev)
> > if (ret)
> > return ret;
> >
> > + ret = s32g_serdes_get_xpcs_resources(pdev, serdes);
> > + if (ret)
> > + return ret;
> > +
> > ret = s32g_serdes_parse_lanes(dev, serdes);
> > if (ret)
> > return ret;
> > @@ -555,6 +874,57 @@ static int __maybe_unused s32g_serdes_resume(struct device *device)
> > return ret;
> > }
> >
> > +struct phylink_pcs *s32g_serdes_pcs_create(struct device *dev, struct device_node *np)
> > +{
> > + struct platform_device *pdev;
> > + struct device_node *pcs_np;
> > + struct s32g_serdes *serdes;
> > + u32 port;
> > +
> > + if (of_property_read_u32(np, "reg", &port))
> > + return ERR_PTR(-EINVAL);
> > +
> > + if (port >= S32G_SERDES_XPCS_MAX)
> > + return ERR_PTR(-EINVAL);
> > +
> > + /* The PCS pdev is attached to the parent node */
> > + pcs_np = of_get_parent(np);
> > + if (!pcs_np)
> > + return ERR_PTR(-ENODEV);
> > +
> > + if (!of_device_is_available(pcs_np)) {
> > + of_node_put(pcs_np);
> > + return ERR_PTR(-ENODEV);
> > + }
> > +
> > + pdev = of_find_device_by_node(pcs_np);
> > + of_node_put(pcs_np);
> > + if (!pdev)
> > + return ERR_PTR(-EPROBE_DEFER);
> > +
> > + serdes = platform_get_drvdata(pdev);
> > + if (!serdes) {
> > + put_device(&pdev->dev);
> > + return ERR_PTR(-EPROBE_DEFER);
> > + }
> > +
> > + if (!serdes->xpcs.phys[port]) {
> > + put_device(&pdev->dev);
> > + return ERR_PTR(-EPROBE_DEFER);
> > + }
> > +
> > + return &serdes->xpcs.phys[port]->pcs;
> > +}
> > +EXPORT_SYMBOL(s32g_serdes_pcs_create);
> > +
> > +void s32g_serdes_pcs_destroy(struct phylink_pcs *pcs)
> > +{
> > + struct s32g_xpcs *xpcs = phylink_pcs_to_s32g_xpcs(pcs);
> > +
> > + put_device(xpcs->dev);
> > +}
> > +EXPORT_SYMBOL(s32g_serdes_pcs_destroy);
> > +
> > static const struct of_device_id s32g_serdes_match[] = {
> > {
> > .compatible = "nxp,s32g2-serdes",
> > diff --git a/include/linux/pcs/pcs-nxp-s32g-xpcs.h b/include/linux/pcs/pcs-nxp-s32g-xpcs.h
> > new file mode 100644
> > index 000000000000..96a0049b93a6
> > --- /dev/null
> > +++ b/include/linux/pcs/pcs-nxp-s32g-xpcs.h
> > @@ -0,0 +1,50 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/**
> > + * Copyright 2021-2026 NXP
> > + */
> > +#ifndef PCS_NXP_S32G_XPCS_H
> > +#define PCS_NXP_S32G_XPCS_H
> > +
> > +#include <linux/phylink.h>
> > +
> > +enum s32g_xpcs_shared {
> > + NOT_SHARED,
> > + PCIE_XPCS_1G,
> > + PCIE_XPCS_2G5,
> > +};
> > +
> > +enum s32g_xpcs_pll {
> > + XPCS_PLLA, /* Slow PLL */
> > + XPCS_PLLB, /* Fast PLL */
> > +};
> > +
> > +struct s32g_xpcs {
> > + void __iomem *base;
> > + struct device *dev;
> > + unsigned char id;
> > + struct regmap *regmap;
> > + enum s32g_xpcs_pll ref;
> > + bool ext_clk;
> > + bool mhz125;
> > + enum s32g_xpcs_shared pcie_shared;
> > + struct phylink_pcs pcs;
> > +};
> > +
> > +#define phylink_pcs_to_s32g_xpcs(pl_pcs) \
> > + container_of((pl_pcs), struct s32g_xpcs, pcs)
> > +
> > +int s32g_xpcs_init(struct s32g_xpcs *xpcs, struct device *dev,
> > + unsigned char id, void __iomem *base, bool ext_clk,
> > + unsigned long rate, enum s32g_xpcs_shared pcie_shared);
> > +int s32g_xpcs_init_plls(struct s32g_xpcs *xpcs);
> > +int s32g_xpcs_pre_pcie_2g5(struct s32g_xpcs *xpcs);
> > +void s32g_xpcs_vreset(struct s32g_xpcs *xpcs);
> > +int s32g_xpcs_wait_vreset(struct s32g_xpcs *xpcs);
> > +int s32g_xpcs_reset_rx(struct s32g_xpcs *xpcs);
> > +void s32g_xpcs_disable_an(struct s32g_xpcs *xpcs);
> > +
> > +struct phylink_pcs *s32g_serdes_pcs_create(struct device *dev, struct device_node *np);
> > +void s32g_serdes_pcs_destroy(struct phylink_pcs *pcs);
> > +
> > +#endif /* PCS_NXP_S32G_XPCS_H */
> > +
> > --
> > 2.43.0
> >
> >
>
>
> --
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox