Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v2 1/2] dt-bindings: mmc: hisilicon,hi3660-dw-mshc: Convert to DT schema
From: Ulf Hansson @ 2026-03-26 12:34 UTC (permalink / raw)
  To: Bhargav Joshi
  Cc: devicetree, linux-arm-kernel, xuwei5, robh, krzk+dt, conor+dt,
	zhangfei.gao, linux-mmc, daniel.baluta, simona.toaca, d-gole,
	m-chawdhry, linux-kernel
In-Reply-To: <20260325225439.68161-2-rougueprince47@gmail.com>

On Wed, 25 Mar 2026 at 23:55, Bhargav Joshi <rougueprince47@gmail.com> wrote:
>
> Convert the Hisilicon DesignWare Mobile Storage Host Controller
> (dw-mshc) bindings from text format to DT schema.
>
> As part of this conversion, the binding file is renamed from
> k3-dw-mshc.txt to hisilicon,hi3660-dw-mshc.yaml to align with compatible
> string naming conventions. Examples have been updated to pass schema
> validation.
>
> Note: synopsys-dw-mshc binding specifies clock names as "biu" followed
> by "ciu". However, this Hisilicon binding reverses the order to 'ciu'
> then 'biu' to match both the legacy text binding and in-kernel Hisilicon
> DTS board files.
>
> Signed-off-by: Bhargav Joshi <rougueprince47@gmail.com>
> Acked-by: Zhangfei Gao <zhangfei.gao@linaro.org>

Applied for next, thanks!

Kind regards
Uffe



> ---
> Changes in v2:
>     - Grouped compatible strings into an enum.
>     - Replaced raw numbers with proper flags.
>     - Fixed property order and removed invalid hex values.
>     - Added explanation for clock order change in commit message.
>     - Collected Acked-by tag.
>
>  .../mmc/hisilicon,hi3660-dw-mshc.yaml         | 117 ++++++++++++++++++
>  .../devicetree/bindings/mmc/k3-dw-mshc.txt    |  73 -----------
>  2 files changed, 117 insertions(+), 73 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mmc/hisilicon,hi3660-dw-mshc.yaml
>  delete mode 100644 Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
>
> diff --git a/Documentation/devicetree/bindings/mmc/hisilicon,hi3660-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/hisilicon,hi3660-dw-mshc.yaml
> new file mode 100644
> index 000000000000..296bd776488e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/hisilicon,hi3660-dw-mshc.yaml
> @@ -0,0 +1,117 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mmc/hisilicon,hi3660-dw-mshc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Hisilicon specific extensions to the Synopsys Designware Mobile Storage Host Controller
> +
> +maintainers:
> +  - Zhangfei Gao <zhangfei.gao@linaro.org>
> +
> +description:
> +  The Synopsys designware mobile storage host controller is used to interface
> +  a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
> +  differences between the core Synopsys dw mshc controller properties described
> +  by synopsys-dw-mshc.txt and the properties used by the Hisilicon specific
> +  extensions to the Synopsys Designware Mobile Storage Host Controller.
> +
> +allOf:
> +  - $ref: /schemas/mmc/synopsys-dw-mshc-common.yaml#
> +
> +properties:
> +  compatible:
> +    oneOf:
> +      - enum:
> +          - hisilicon,hi3660-dw-mshc
> +          - hisilicon,hi4511-dw-mshc
> +          - hisilicon,hi6220-dw-mshc
> +      - items:
> +          - const: hisilicon,hi3670-dw-mshc
> +          - const: hisilicon,hi3660-dw-mshc
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    items:
> +      - description: card interface unit clock
> +      - description: bus interface unit clock
> +
> +  clock-names:
> +    items:
> +      - const: ciu
> +      - const: biu
> +
> +  hisilicon,peripheral-syscon:
> +    $ref: /schemas/types.yaml#/definitions/phandle
> +    description: phandle of syscon used to control peripheral.
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - clock-names
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/clock/hi3620-clock.h>
> +    #include <dt-bindings/gpio/gpio.h>
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> +    mmc@fcd03000 {
> +        compatible = "hisilicon,hi4511-dw-mshc";
> +        reg = <0xfcd03000 0x1000>;
> +        interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +        clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
> +        clock-names = "ciu", "biu";
> +        vmmc-supply = <&ldo12>;
> +        fifo-depth = <0x100>;
> +        pinctrl-names = "default";
> +        pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
> +        bus-width = <4>;
> +        disable-wp;
> +        cd-gpios = <&gpio10 3 GPIO_ACTIVE_HIGH>;
> +        cap-mmc-highspeed;
> +        cap-sd-highspeed;
> +    };
> +
> +  - |
> +    #include <dt-bindings/clock/hi6220-clock.h>
> +    #include <dt-bindings/gpio/gpio.h>
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> +    soc {
> +      #address-cells = <2>;
> +      #size-cells = <2>;
> +
> +      mmc@f723e000 {
> +          compatible = "hisilicon,hi6220-dw-mshc";
> +          reg = <0x0 0xf723e000 0x0 0x1000>;
> +          interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
> +          clocks = <&clock_sys HI6220_MMC1_CIUCLK>,
> +                   <&clock_sys HI6220_MMC1_CLK>;
> +          clock-names = "ciu", "biu";
> +          bus-width = <4>;
> +          disable-wp;
> +          cap-sd-highspeed;
> +          sd-uhs-sdr12;
> +          sd-uhs-sdr25;
> +          card-detect-delay = <200>;
> +          hisilicon,peripheral-syscon = <&ao_ctrl>;
> +          cd-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>;
> +          pinctrl-names = "default", "idle";
> +          pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>;
> +          pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>;
> +          vqmmc-supply = <&ldo7>;
> +          vmmc-supply = <&ldo10>;
> +      };
> +    };
> diff --git a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
> deleted file mode 100644
> index 36c4bea675d5..000000000000
> --- a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
> +++ /dev/null
> @@ -1,73 +0,0 @@
> -* Hisilicon specific extensions to the Synopsys Designware Mobile
> -  Storage Host Controller
> -
> -Read synopsys-dw-mshc.txt for more details
> -
> -The Synopsys designware mobile storage host controller is used to interface
> -a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
> -differences between the core Synopsys dw mshc controller properties described
> -by synopsys-dw-mshc.txt and the properties used by the Hisilicon specific
> -extensions to the Synopsys Designware Mobile Storage Host Controller.
> -
> -Required Properties:
> -
> -* compatible: should be one of the following.
> -  - "hisilicon,hi3660-dw-mshc": for controllers with hi3660 specific extensions.
> -  - "hisilicon,hi3670-dw-mshc", "hisilicon,hi3660-dw-mshc": for controllers
> -     with hi3670 specific extensions.
> -  - "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extensions.
> -  - "hisilicon,hi6220-dw-mshc": for controllers with hi6220 specific extensions.
> -
> -Optional Properties:
> -- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral.
> -
> -Example:
> -
> -       /* for Hi3620 */
> -
> -       /* SoC portion */
> -       dwmmc_0: dwmmc0@fcd03000 {
> -               compatible = "hisilicon,hi4511-dw-mshc";
> -               reg = <0xfcd03000 0x1000>;
> -               interrupts = <0 16 4>;
> -               #address-cells = <1>;
> -               #size-cells = <0>;
> -               clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
> -               clock-names = "ciu", "biu";
> -       };
> -
> -       /* Board portion */
> -       dwmmc0@fcd03000 {
> -               vmmc-supply = <&ldo12>;
> -               fifo-depth = <0x100>;
> -               pinctrl-names = "default";
> -               pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
> -               bus-width = <4>;
> -               disable-wp;
> -               cd-gpios = <&gpio10 3 0>;
> -               cap-mmc-highspeed;
> -               cap-sd-highspeed;
> -       };
> -
> -       /* for Hi6220 */
> -
> -       dwmmc_1: dwmmc1@f723e000 {
> -               compatible = "hisilicon,hi6220-dw-mshc";
> -               bus-width = <0x4>;
> -               disable-wp;
> -               cap-sd-highspeed;
> -               sd-uhs-sdr12;
> -               sd-uhs-sdr25;
> -               card-detect-delay = <200>;
> -               hisilicon,peripheral-syscon = <&ao_ctrl>;
> -               reg = <0x0 0xf723e000 0x0 0x1000>;
> -               interrupts = <0x0 0x49 0x4>;
> -               clocks = <&clock_sys HI6220_MMC1_CIUCLK>, <&clock_sys HI6220_MMC1_CLK>;
> -               clock-names = "ciu", "biu";
> -               cd-gpios = <&gpio1 0 1>;
> -               pinctrl-names = "default", "idle";
> -               pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>;
> -               pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>;
> -               vqmmc-supply = <&ldo7>;
> -               vmmc-supply = <&ldo10>;
> -       };
> --
> 2.53.0
>

^ permalink raw reply

* Re: [PATCH] dt-bindings: mmc: sdhci-msm: add IPQ9650 compatible
From: Ulf Hansson @ 2026-03-26 12:34 UTC (permalink / raw)
  To: Kathiravan Thirumoorthy
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, linux-mmc, devicetree, linux-kernel
In-Reply-To: <20260325-ipq9650_sdhc-v1-1-53cf7ba11087@oss.qualcomm.com>

On Wed, 25 Mar 2026 at 12:40, Kathiravan Thirumoorthy
<kathiravan.thirumoorthy@oss.qualcomm.com> wrote:
>
> The IPQ9650 supports eMMC with an SDHCI controller. Add the appropriate
> compatible to the documentation.
>
> Signed-off-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>

Applied for next, thanks!

Kind regards
Uffe


> ---
>  Documentation/devicetree/bindings/mmc/sdhci-msm.yaml | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml
> index fd1d5b04e755..695a95e8f35d 100644
> --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml
> +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml
> @@ -43,6 +43,7 @@ properties:
>                - qcom,ipq5424-sdhci
>                - qcom,ipq6018-sdhci
>                - qcom,ipq9574-sdhci
> +              - qcom,ipq9650-sdhci
>                - qcom,kaanapali-sdhci
>                - qcom,milos-sdhci
>                - qcom,qcm2290-sdhci
>
> ---
> base-commit: 85964cdcad0fac9a0eb7b87a0f9d88cc074b854c
> change-id: 20260325-ipq9650_sdhc-9b75ee9ed5ca
>
> Best regards,
> --
> Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
>

^ permalink raw reply

* Re: [PATCH v5 2/9] dt-bindings: mmc: amlogic: Add compatible for T7 mmc
From: Ulf Hansson @ 2026-03-26 12:33 UTC (permalink / raw)
  To: Ronald Claveau
  Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Johannes Berg,
	van Spriel, linux-arm-kernel, linux-amlogic, devicetree,
	linux-kernel, linux-mmc, linux-wireless, Conor Dooley,
	Xianwei Zhao
In-Reply-To: <20260326-add-emmc-t7-vim4-v5-2-d3f182b48e9d@aliel.fr>

On Thu, 26 Mar 2026 at 11:01, Ronald Claveau <linux-kernel-dev@aliel.fr> wrote:
>
> Add amlogic,t7-mmc compatible string, falling back to amlogic,meson-axg-mmc
> as the T7 MMC controller is compatible with the AXG implementation.
>
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> Acked-by: Rob Herring (Arm) <robh@kernel.org>
> Reviewed-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
> Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>

Applied for next, thanks!

Kind regards
Uffe


> ---
>  Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml
> index 57646575a13f8..976f36de2091c 100644
> --- a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml
> +++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml
> @@ -19,6 +19,10 @@ allOf:
>  properties:
>    compatible:
>      oneOf:
> +      - items:
> +          - enum:
> +              - amlogic,t7-mmc
> +          - const: amlogic,meson-axg-mmc
>        - const: amlogic,meson-axg-mmc
>        - items:
>            - const: amlogic,meson-gx-mmc
>
> --
> 2.49.0
>

^ permalink raw reply

* Re: [PATCH v2 09/12] arm64: dts: imx8mp-sr-som: Correct PAD settings for PMIC_nINT
From: Josua Mayer @ 2026-03-26 12:33 UTC (permalink / raw)
  To: Peng Fan, Laurent Pinchart, Peng Fan (OSS)
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Shawn Guo,
	Daniel Scally, Marco Felsch, Gilles Talis, Viorel Suman,
	S.J. Wang, Jagan Teki, Manoj Sai, matteo.lisi, Ray Chang,
	Richard Hu, Heiko Schocher, Martyn Welch,
	Goran Rađenović, Börge Strümpfel,
	Christoph Niedermaier, Marek Vasut, devicetree@vger.kernel.org,
	imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, kernel@dh-electronics.com
In-Reply-To: <PAXPR04MB845972866AB6A7E85D1570CB8856A@PAXPR04MB8459.eurprd04.prod.outlook.com>

Hi Peng,

Am 26.03.26 um 10:55 schrieb Peng Fan:
>> Subject: Re: [PATCH v2 09/12] arm64: dts: imx8mp-sr-som: Correct
>> PAD settings for PMIC_nINT
>>> --- a/arch/arm64/boot/dts/freescale/imx8mp-sr-som.dtsi
>>> +++ b/arch/arm64/boot/dts/freescale/imx8mp-sr-som.dtsi
>>> @@ -174,7 +174,7 @@ pmic: pmic@25 {
>>>  		pinctrl-0 = <&pmic_pins>;
>>>  		pinctrl-names = "default";
>>>  		interrupt-parent = <&gpio1>;
>>> -		interrupts = <3 GPIO_ACTIVE_LOW>;
>>> +		interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
>> This is a good change, but it should be mentioned in the commit
>> message, or split to a separate patch. Same for other patches in this
>> series where you make the same change.
> Thanks, waiting to see if any board owners would give a test.
> Then I will update in V3.
>
> Thanks,
> Peng.

Looks correct to me.

Reviewed-by: Josua Mayer <josua@solid-run.com>

^ permalink raw reply

* Re: [PATCH net-next v2 3/4] net: phy: air_phy_lib: Factorize BuckPBus register accessors
From: Andrew Lunn @ 2026-03-26 12:30 UTC (permalink / raw)
  To: Louis-Alexis Eyraud
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	AngeloGioacchino Del Regno, Heiner Kallweit, Russell King,
	kevin-kw.huang, macpaul.lin, matthias.bgg, kernel, netdev,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel
In-Reply-To: <20260326-add-airoha-an8801-support-v2-3-1a42d6b6050f@collabora.com>

> @@ -480,8 +287,8 @@ static int en8811h_wait_mcu_ready(struct phy_device *phydev)
>  {
>  	int ret, reg_value;
>  
> -	ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
> -				     EN8811H_FW_CTRL_1_FINISH);
> +	ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
> +					 EN8811H_FW_CTRL_1_FINISH);

Is a rename required? Is the namespace air_buckpbus_ used somewhere
else?

	Andrew

^ permalink raw reply

* Re: [PATCH v8 0/2] iio: proximity: add driver for ST VL53L1X ToF sensor
From: Sirat @ 2026-03-26 12:24 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-iio, devicetree, dlechner, nuno.sa, andy, robh, krzk+dt,
	conor+dt, linux-kernel
In-Reply-To: <20260326082447.5e447606@jic23-huawei>

On Thu, Mar 26, 2026 at 2:25 PM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Thu, 26 Mar 2026 02:19:40 +0600
> Siratul Islam <email@sirat.me> wrote:
>
...
> Applied to the testing branch of iio.git.
>
> Thanks,
>
> J

Thanks for applying the patch. Thanks to you and all the reviewers.
The code it ended up becoming is much superior to what I initially
had.

Best regards
Sirat

^ permalink raw reply

* Re: [PATCH v1 1/2] dt-bindings: i2c: ls2x-i2c: Add clock- related properties
From: Krzysztof Kozlowski @ 2026-03-26 12:23 UTC (permalink / raw)
  To: Hongliang Wang
  Cc: Binbin Zhou, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-i2c, devicetree, loongarch
In-Reply-To: <2d032399-d3fd-92b3-6dd4-28e0c78f199d@loongson.cn>

On 26/03/2026 12:48, Hongliang Wang wrote:
> Hi Krzysztof and Andi,
> 
> Sorry, I didn't express myself clearly. The purpose of defining clock-input and
> clock-div properties is to use them in both acpi and dts. If these properties
> cannot be used in dts, can I first use them in acpi? and second, use clock
> framework in dts.

I already asked you:
1. Stop requesting read receipts on your email
2. Don't top post.

I already told you also why these cannot be in DTS. Respond to technical
arguments, not top-post of what you want.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH v8 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor
From: Sirat @ 2026-03-26 12:20 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: linux-iio, devicetree, jic23, dlechner, nuno.sa, andy, robh,
	krzk+dt, conor+dt, linux-kernel
In-Reply-To: <acUHDGUqg4RdI7fW@ashevche-desk.local>

On Thu, Mar 26, 2026 at 4:14 PM Andy Shevchenko
<andriy.shevchenko@intel.com> wrote:
>
> On Thu, Mar 26, 2026 at 02:19:42AM +0600, Siratul Islam wrote:
> > Add support for the STMicroelectronics VL53L1X Time-of-Flight
> > ranging sensor with I2C interface.
>
> Some ideas for small followup amendments.
>
> ...
>
> > +#define VL53L1X_REG_SOFT_RESET                                               0x0000
> > +#define VL53L1X_REG_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND            0x0008
> > +#define VL53L1X_REG_VHV_CONFIG__INIT                                 0x000B
> > +#define VL53L1X_REG_GPIO_HV_MUX__CTRL                                        0x0030
> > +#define VL53L1X_REG_GPIO__TIO_HV_STATUS                                      0x0031
> > +#define VL53L1X_REG_SYSTEM__INTERRUPT_CONFIG_GPIO                    0x0046
> > +#define VL53L1X_REG_PHASECAL_CONFIG__TIMEOUT_MACROP                  0x004B
> > +#define VL53L1X_REG_RANGE_CONFIG__TIMEOUT_MACROP_A                   0x005E
> > +#define VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_A                     0x0060
> > +#define VL53L1X_REG_RANGE_CONFIG__TIMEOUT_MACROP_B                   0x0061
> > +#define VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_B                     0x0063
> > +#define VL53L1X_REG_RANGE_CONFIG__VALID_PHASE_HIGH                   0x0069
> > +#define VL53L1X_REG_SYSTEM__INTERMEASUREMENT_PERIOD                  0x006C
> > +#define VL53L1X_REG_SD_CONFIG__WOI_SD0                                       0x0078
> > +#define VL53L1X_REG_SD_CONFIG__WOI_SD1                                       0x0079
> > +#define VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD0                     0x007A
> > +#define VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD1                     0x007B
> > +#define VL53L1X_REG_SYSTEM__INTERRUPT_CLEAR                          0x0086
> > +#define VL53L1X_REG_SYSTEM__MODE_START                                       0x0087
> > +#define VL53L1X_REG_RESULT__RANGE_STATUS                             0x0089
> > +#define VL53L1X_REG_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0   0x0096
> > +#define VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL                                0x00DE
> > +#define VL53L1X_REG_FIRMWARE__SYSTEM_STATUS                          0x00E5
> > +#define VL53L1X_REG_IDENTIFICATION__MODEL_ID                         0x010F
> > +#define VL53L1X_REG_DEFAULT_CONFIG                                   0x002D
>
> Keep the list ordered by the value?
>
> ...
>
> > +static int vl53l1x_chip_init(struct vl53l1x_data *data)
> > +{
> > +     struct device *dev = regmap_get_device(data->regmap);
> > +     unsigned int val;
> > +     u16 model_id;
> > +     int ret;
> > +
> > +     if (!data->xshut_reset) {
> > +             ret = regmap_write(data->regmap, VL53L1X_REG_SOFT_RESET, 0x00);
> > +             if (ret)
> > +                     return ret;
> > +             fsleep(100); /* conservative reset pulse, no spec */
> > +
> > +             ret = regmap_write(data->regmap, VL53L1X_REG_SOFT_RESET, 0x01);
> > +             if (ret)
> > +                     return ret;
> > +             fsleep(1000); /* conservative boot wait, no spec */
> > +     }
> > +
> > +     ret = regmap_read_poll_timeout(data->regmap,
> > +                                    VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, val,
> > +                                    val & BIT(0),
> > +                                    1 * USEC_PER_MSEC,
> > +                                    100 * USEC_PER_MSEC);
>
> Use logical split
>
>         ret = regmap_read_poll_timeout(data->regmap,
>                                        VL53L1X_REG_FIRMWARE__SYSTEM_STATUS,
>                                        val, val & BIT(0),
>                                        1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
>
> > +     if (ret)
> > +             return dev_err_probe(dev, ret, "firmware boot timeout\n");
> > +
> > +     ret = vl53l1x_read_u16(data, VL53L1X_REG_IDENTIFICATION__MODEL_ID,
> > +                            &model_id);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (model_id != VL53L1X_MODEL_ID_VAL)
> > +             dev_info(dev, "unknown model id: 0x%04x, continuing\n", model_id);
> > +
> > +     ret = regmap_bulk_write(data->regmap, VL53L1X_REG_DEFAULT_CONFIG,
> > +                             vl53l1x_default_config,
> > +                             sizeof(vl53l1x_default_config));
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = regmap_read(data->regmap, VL53L1X_REG_GPIO_HV_MUX__CTRL, &val);
> > +     if (ret)
> > +             return ret;
> > +     data->gpio_polarity = !!(val & VL53L1X_GPIO_HV_MUX_POLARITY);
> > +
> > +     /* Initial ranging cycle for VHV calibration */
> > +     ret = vl53l1x_start_ranging(data);
> > +     if (ret)
> > +             return ret;
> > +
> > +     /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */
> > +     ret = regmap_read_poll_timeout(data->regmap,
> > +                                    VL53L1X_REG_GPIO__TIO_HV_STATUS, val,
> > +                                    (val & 1) != data->gpio_polarity,
> > +                                    1 * USEC_PER_MSEC,
> > +                                    1000 * USEC_PER_MSEC);
>
> Ditto.
>
>         ret = regmap_read_poll_timeout(data->regmap,
>                                        VL53L1X_REG_GPIO__TIO_HV_STATUS,
>                                        val, (val & 1) != data->gpio_polarity,
>                                        1 * USEC_PER_MSEC, 1 * USEC_PER_SEC);
>
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = vl53l1x_clear_irq(data);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = vl53l1x_stop_ranging(data);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = regmap_write(data->regmap,
> > +                        VL53L1X_REG_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND,
> > +                        VL53L1X_VHV_LOOP_BOUND_TWO);
> > +     if (ret)
> > +             return ret;
> > +
> > +     return regmap_write(data->regmap, VL53L1X_REG_VHV_CONFIG__INIT, 0x00);
> > +}
>
> ...
>
> > +     if (data->irq) {
> > +             reinit_completion(&data->completion);
> > +
> > +             ret = vl53l1x_clear_irq(data);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             if (!wait_for_completion_timeout(&data->completion, HZ))
> > +                     return -ETIMEDOUT;
> > +     } else {
> > +             unsigned int rdy;
> > +
> > +             /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */
> > +             ret = regmap_read_poll_timeout(data->regmap,
> > +                                            VL53L1X_REG_GPIO__TIO_HV_STATUS, rdy,
> > +                                            (rdy & 1) != data->gpio_polarity,
> > +                                            1 * USEC_PER_MSEC,
> > +                                            1000 * USEC_PER_MSEC);
>
> Ditto.
>
>                 ret = regmap_read_poll_timeout(data->regmap,
>                                                VL53L1X_REG_GPIO__TIO_HV_STATUS,
>                                                rdy, (rdy & 1) != data->gpio_polarity,
>                                                1 * USEC_PER_MSEC, 1 * USEC_PER_SEC);
>
> Yes, in this case they are slightly longer than 80 characters. But
> looking at the above this entire call should be a helper, so you can
> reuse it here and above.
>
> > +             if (ret)
> > +                     return ret;
> > +     }
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
>
Hi Andy!

Thanks for the review again. I will send a small separate cleanup
patch for this once it gets to a more stable stage.

Sirat

^ permalink raw reply

* Re: [PATCH 2/2] iio: adc: qcom-pm8xxx-xoadc: add support for reading channel labels
From: Antony Kurniawan Soemardi @ 2026-03-26 12:00 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Jonathan Cameron, David Lechner, Nuno Sá,
	Andy Shevchenko, linux-arm-msm, devicetree, linux-kernel,
	linux-iio, phone-devel
In-Reply-To: <acUIAh_r7mSfIiEB@ashevche-desk.local>

On 3/26/2026 5:18 PM, Andy Shevchenko wrote:
 >> Tested-on: Sony Xperia SP (PM8921)
 >
 > Interesting, never saw this tag before.

Oh, I just realized I misremember Tested-by tag as Tested-on... Let me
know if it's not acceptable.

 >> +	if (!ch) {
 >> +		dev_err(adc->dev, "no such channel %lu\n", chan->address);
 >> +		return -EINVAL;
 >> +	}
 >
 > Isn't it a dead code? Also poisoning dmesg with this recurrent 
message is not
 > good idea to begin with (the user space will have a door to flood it, 
which
 > might be considered as an assistance to hackers to clear immediate 
logs after
 > a successful attack).

Good point about the successful attack hint! I was copying the existing
code from pm8xxx_read_raw. Do you think those checks are unnecessary for
pm8xxx_read_raw as well?

Thanks,
Antony K. S.

^ permalink raw reply

* Re: [PATCH 3/3] net: dsa: microchip: implement KSZ87xx Module 3 low-loss cable errata
From: Andrew Lunn @ 2026-03-26 12:18 UTC (permalink / raw)
  To: Fidelio Lawson
  Cc: Woojung Huh, UNGLinuxDriver, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Marek Vasut, Maxime Chevallier,
	netdev, devicetree, linux-kernel, Fidelio Lawson
In-Reply-To: <20260326-ksz87xx_errata_low_loss_connections-v1-3-79a698f43626@exotec.com>

> +	mutex_lock(&dev->alu_mutex);
> +
> +	ret = ksz_write8(dev, regs[REG_IND_CTRL_0], 0xA0);
> +
> +	if (!ret)
> +		ret = ksz_write8(dev, 0x6F, indir_reg);
> +
> +	if (!ret)
> +		ret = ksz_write8(dev, regs[REG_IND_BYTE], indir_val);
> +
> +	mutex_unlock(&dev->alu_mutex);

What address space are these registers in? Normally workarounds for a
PHY would be in the PHY driver. But that assumes the registers are
accessible from the PHY driver.

	   Andrew

^ permalink raw reply

* Re: [PATCH 0/4] Add i.MX94 remoteproc support and reset vector handling improvements
From: Peng Fan @ 2026-03-26 12:20 UTC (permalink / raw)
  To: Daniel Baluta
  Cc: Mathieu Poirier, Bjorn Andersson, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, linux-remoteproc,
	devicetree, imx, linux-arm-kernel, linux-kernel, Peng Fan
In-Reply-To: <80c7815b-26d9-4876-a44f-3e17b6338dc6@oss.nxp.com>

On Wed, Mar 25, 2026 at 10:41:29AM +0200, Daniel Baluta wrote:
>On 3/23/26 16:32, Mathieu Poirier wrote:
>> On Fri, Mar 20, 2026 at 11:19:06AM +0200, Daniel Baluta wrote:
>>> On 3/12/26 14:36, Peng Fan (OSS) wrote:
>>>> This series adds remoteproc support for the i.MX94 family, including the
>>>> CM70, CM71, and CM33S cores, and introduces a new device‑tree property to
>>>> correctly derive the hardware reset vector for Cortex‑M processors whose
>>>> ELF entry point does not directly correspond to the actual reset address.
>>>>
>>>> Background:
>>>> Cortex‑M processors fetch their initial SP and PC from a fixed reset vector
>>>> table. While ELF images embed the entry point (e_entry), this value is
>>>> not always aligned to the hardware reset address. On platforms such as
>>>> i.MX94 CM33S, masking is required to compute the correct reset vector
>>>> address before programming the SoC reset registers.
>>> What happens if the reset vector is at 0 and the e_entry point is at 0x800...?
>>>
>>> In this case masking will no longer work! Can we implement a generic approach?
>>>
>> I will wait to see an R-B from Daniel before looking at this set.
>>
>> Thanks,
>> Mathieu
>>  
>>
>Hi Mathieu, Peng,
>
>Patchseries mostly looks good to me. The only blocking issue here is how to correctly specify the hardware reset address.
>
>I see two options here:
>
>1) Create a special section in TCM that holds the PC/Stack initial value as concluded here [1]. But this
>
>doesn't work in all the cases 
>
>2) Add a per device data that holds the hardware reset mask that gets applied to entry address read from ELF.
>
>I'm fine going with option 2) and that's because this change is IMX rproc driver specific, it scales well and will be maintained by Peng.

Thanks, I will go with option 2.

Thanks
Peng

>
>thanks,
>
>Daniel.
>
>[1] https://lore.kernel.org/linux-remoteproc/38476dd0-07a6-310f-1fba-2b3021a5b007@kontron.de/
>

^ permalink raw reply

* Re: [PATCH 1/3] dt-bindings: dsa: microchip: add KSZ low-loss cable errata properties
From: Andrew Lunn @ 2026-03-26 12:14 UTC (permalink / raw)
  To: Fidelio Lawson
  Cc: Woojung Huh, UNGLinuxDriver, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Marek Vasut, Maxime Chevallier,
	netdev, devicetree, linux-kernel, Fidelio Lawson
In-Reply-To: <20260326-ksz87xx_errata_low_loss_connections-v1-1-79a698f43626@exotec.com>

On Thu, Mar 26, 2026 at 10:10:21AM +0100, Fidelio Lawson wrote:
> Microchip KSZ87xx switches are affected by the "Module 3: Equalizer fix
> for short cables" erratum described in DS80000687C.
> The embedded PHY receivers are tuned for long, high-loss cables,
> which may cause signal distortion when operated with short or low-loss
> cabling such as CAT5e or CAT6. In these cases,
> the PHY may fail to establish a link due to internal over-amplification.
> 
> Two workarounds are provided by Microchip, each configuring a different
> indirect register value to adjust the PHY equalizer settings.
> 
> This patch introduces two new device tree properties to enable and
> select the appropriate workaround:
> 
>   - microchip,low-loss-errata-enable: boolean enabling the feature
>   - microchip,low-loss-errata: selects workaround 1 or 2 (default: 1)
> 
> These properties allow board designers to opt into the errata fix
> according to the targeted cable characteristics of their platform.

Does the errata give any indication how the two different workarounds
differ? How would a user decided which to use?

I also question if this should be a DT property. The length of the
cables is not a property of the board.

A PHY tunable would better reflect the same board can be used with
different cables, with different lengths/quality.

	  Andrew

^ permalink raw reply

* Re: [PATCH v3 5/9] mfd: mt6397: Add support for MT6392 pmic
From: Lee Jones @ 2026-03-26 12:10 UTC (permalink / raw)
  To: Luca Leonardo Scorcia
  Cc: linux-mediatek, Fabien Parent, Val Packett, Dmitry Torokhov,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sen Chu,
	Sean Wang, Macpaul Lin, Matthias Brugger,
	AngeloGioacchino Del Regno, Linus Walleij, Liam Girdwood,
	Mark Brown, Julien Massot, Gary Bisson, Louis-Alexis Eyraud,
	Chen Zhong, linux-input, devicetree, linux-kernel, linux-pm,
	linux-arm-kernel, linux-gpio
In-Reply-To: <20260317184507.523060-6-l.scorcia@gmail.com>

On Tue, 17 Mar 2026, Luca Leonardo Scorcia wrote:

> From: Fabien Parent <parent.f@gmail.com>
> 
> Update the MT6397 MFD driver to support the MT6392 PMIC.
> 
> Signed-off-by: Fabien Parent <parent.f@gmail.com>
> Signed-off-by: Val Packett <val@packett.cool>
> Signed-off-by: Luca Leonardo Scorcia <l.scorcia@gmail.com>
> ---
>  drivers/mfd/mt6397-core.c            |  46 +++
>  drivers/mfd/mt6397-irq.c             |   8 +
>  include/linux/mfd/mt6392/core.h      |  42 +++
>  include/linux/mfd/mt6392/registers.h | 487 +++++++++++++++++++++++++++
>  include/linux/mfd/mt6397/core.h      |   1 +
>  5 files changed, 584 insertions(+)
>  create mode 100644 include/linux/mfd/mt6392/core.h
>  create mode 100644 include/linux/mfd/mt6392/registers.h
> 
> diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
> index 3e58d0764c7e..c4b86a44c68b 100644
> --- a/drivers/mfd/mt6397-core.c
> +++ b/drivers/mfd/mt6397-core.c
> @@ -18,6 +18,7 @@
>  #include <linux/mfd/mt6357/core.h>
>  #include <linux/mfd/mt6358/core.h>
>  #include <linux/mfd/mt6359/core.h>
> +#include <linux/mfd/mt6392/core.h>
>  #include <linux/mfd/mt6397/core.h>
>  #include <linux/mfd/mt6323/registers.h>
>  #include <linux/mfd/mt6328/registers.h>
> @@ -25,6 +26,7 @@
>  #include <linux/mfd/mt6357/registers.h>
>  #include <linux/mfd/mt6358/registers.h>
>  #include <linux/mfd/mt6359/registers.h>
> +#include <linux/mfd/mt6392/registers.h>
>  #include <linux/mfd/mt6397/registers.h>
>  
>  #define MT6323_RTC_BASE		0x8000
> @@ -39,6 +41,9 @@
>  #define MT6358_RTC_BASE		0x0588
>  #define MT6358_RTC_SIZE		0x3c
>  
> +#define MT6392_RTC_BASE		0x8000
> +#define MT6392_RTC_SIZE		0x3e
> +
>  #define MT6397_RTC_BASE		0xe000
>  #define MT6397_RTC_SIZE		0x3e
>  
> @@ -65,6 +70,11 @@ static const struct resource mt6358_rtc_resources[] = {
>  	DEFINE_RES_IRQ(MT6358_IRQ_RTC),
>  };
>  
> +static const struct resource mt6392_rtc_resources[] = {
> +	DEFINE_RES_MEM(MT6392_RTC_BASE, MT6392_RTC_SIZE),
> +	DEFINE_RES_IRQ(MT6392_IRQ_RTC),
> +};
> +
>  static const struct resource mt6397_rtc_resources[] = {
>  	DEFINE_RES_MEM(MT6397_RTC_BASE, MT6397_RTC_SIZE),
>  	DEFINE_RES_IRQ(MT6397_IRQ_RTC),
> @@ -114,6 +124,11 @@ static const struct resource mt6331_keys_resources[] = {
>  	DEFINE_RES_IRQ_NAMED(MT6331_IRQ_STATUS_HOMEKEY, "homekey"),
>  };
>  
> +static const struct resource mt6392_keys_resources[] = {
> +	DEFINE_RES_IRQ_NAMED(MT6392_IRQ_PWRKEY, "powerkey"),
> +	DEFINE_RES_IRQ_NAMED(MT6392_IRQ_FCHRKEY, "homekey"),
> +};
> +
>  static const struct resource mt6397_keys_resources[] = {
>  	DEFINE_RES_IRQ_NAMED(MT6397_IRQ_PWRKEY, "powerkey"),
>  	DEFINE_RES_IRQ_NAMED(MT6397_IRQ_HOMEKEY, "homekey"),
> @@ -253,6 +268,26 @@ static const struct mfd_cell mt6359_devs[] = {
>  	},
>  };
>  
> +static const struct mfd_cell mt6392_devs[] = {
> +	{
> +		.name = "mt6392-rtc",
> +		.num_resources = ARRAY_SIZE(mt6392_rtc_resources),
> +		.resources = mt6392_rtc_resources,
> +		.of_compatible = "mediatek,mt6392-rtc",
> +	}, {
> +		.name = "mt6392-regulator",
> +		.of_compatible = "mediatek,mt6392-regulator",
> +	}, {
> +		.name = "mt6392-pinctrl",
> +		.of_compatible = "mediatek,mt6392-pinctrl",
> +	}, {
> +		.name = "mt6392-keys",
> +		.num_resources = ARRAY_SIZE(mt6392_keys_resources),
> +		.resources = mt6392_keys_resources,
> +		.of_compatible = "mediatek,mt6392-keys"
> +	},
> +};
> +
>  static const struct mfd_cell mt6397_devs[] = {
>  	{
>  		.name = "mt6397-rtc",
> @@ -335,6 +370,14 @@ static const struct chip_data mt6359_core = {
>  	.irq_init = mt6358_irq_init,
>  };
>  
> +static const struct chip_data mt6392_core = {
> +	.cid_addr = MT6392_CID,
> +	.cid_shift = 0,
> +	.cells = mt6392_devs,

I'm not really sure what came over me when I accepted this 6 years ago,
but I have a _strong_ aversion to MFD data being passed through the OF APIs.

Before this patch lands, please could you refactor this driver to only
pass through an identifier through mt6397_of_match[*].data.  Then, you
can match on that via a switch statement where you can allocate each
device's data structures.

This is how the vast majority of MFD drivers work so there should be
lots of examples to work through to make this trivial.

> +	.cell_size = ARRAY_SIZE(mt6392_devs),
> +	.irq_init = mt6397_irq_init,
> +};

-- 
Lee Jones [李琼斯]

^ permalink raw reply

* Re: [PATCH v3 3/6] arm64: dts: qcom: msm8953-flipkart-rimob: Enable display and GPU
From: Konrad Dybcio @ 2026-03-26 12:07 UTC (permalink / raw)
  To: cristian_ci
  Cc: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
	dri-devel, devicetree, linux-kernel, linux-arm-msm,
	~postmarketos/upstreaming, phone-devel
In-Reply-To: <7wYxWlqdcbL3ANB9n0g4t74x-dwE2yG4CmVw9TwGNmwARxbT5WsdG1fpuIDSA5ab750Gs8OUBwmVa_nZd065guKDJueytb1AmlQ05__IzmU=@protonmail.com>

On 3/24/26 12:18 PM, cristian_ci wrote:
> On Monday, March 23rd, 2026 at 11:52, Konrad Dybcio <konrad.dybcio@oss.qualcomm.com> wrote:
> 
>> On 3/21/26 5:23 PM, Cristian Cozzolino via B4 Relay wrote:
>>> From: Cristian Cozzolino <cristian_ci@protonmail.com>
>>>
>>> Add the description for the display panel found on this phone.
>>> And with this done we can also enable the GPU and set the zap shader
>>> firmware path.
>>>
>>> Signed-off-by: Cristian Cozzolino <cristian_ci@protonmail.com>
>>> ---
>>
>> [...]
>>
>>> +	panel_default: panel-default-state {
>>> +		pins = "gpio61";
>>> +		function = "gpio";
>>> +		drive-strength = <8>;
>>> +		bias-disable;
>>> +		output-high;
>>
>> This says "by default, actively drive the pin not to reset the display
>> panel". Is this actually necessary?
> 
> I've tried to remove panel pinctrl stuff from the panel and the device still boots/works exactly like before. So, have I to submit v4 without pinctrl at all for the panel?

No, the pin config is useful, I'm specifically referencing the output-high
property

Konrad

^ permalink raw reply

* Re: [RFC PATCH 2/3] media: qcom: camss: Add CAMSS Offline Processing Engine driver
From: johannes.goede @ 2026-03-26 12:06 UTC (permalink / raw)
  To: Dmitry Baryshkov, Bryan O'Donoghue
  Cc: Loic Poulain, vladimir.zapolskiy, laurent.pinchart,
	kieran.bingham, robh, krzk+dt, andersson, konradybcio,
	linux-media, linux-arm-msm, devicetree, linux-kernel, mchehab
In-Reply-To: <rj2vb725he4yzfvjifzyxqpbqk5oztfaafp7sg2qes4qz3mlaz@z5dyoqod56d7>

Hi Dmitry,

On 24-Mar-26 22:27, Dmitry Baryshkov wrote:
> On Tue, Mar 24, 2026 at 11:00:21AM +0000, Bryan O'Donoghue wrote:
>> On 23/03/2026 15:31, Loic Poulain wrote:

<snip>

>>> As far as I understand, CDM could also be implemented in a generic way
>>> within CAMSS, since other CAMSS blocks make use of CDM as well.
>>> This is something we should discuss further.
>> My concern is even conservatively if each module adds another 10 ? writes by
>> the time we get to denoising, sharpening, lens shade correction, those
>> writes could easily look more like 100.
>>
>> What user-space should submit is well documented data-structures which then
>> get translated into CDM buffers by the OPE and IFE for the various bits of
>> the pipeline.
> 
> I hope here you have accent on the well-documented (ideally some kind of
> the vendor-independent ABI).

The plan is to use the new extensible generic v4l2 ISP parameters
API for this:

https://docs.kernel.org/6.19/driver-api/media/v4l2-isp.html

What this does is basically divide the parameter buffer (which
is just a mmap-able bunch of bytes) into variable sized packets/
blocks with each block having a small header, with a type field.

And then we can have say CCMv1 type for the CCM on the OPE and
if with some future hardware the format of the CCM (say different
fixpoint format) ever changes we can simply define a new CCMv2
and then the parameter buffer can be filled with different
versions of different parameter blocks depending on the hw.

And on the kernel side there are helpers to parse this, you
simply pass a list of the types the current hw supports
+ per type data-callback functions.

And then your CCMv1 or CCMv2 helper will get called with
the matching parameter-data.

So this way we can easily add new hw support without needing
to change the existing API, we can simply extend the list
of parameter types as needed.

Regards,

Hans


^ permalink raw reply

* [PATCH net-next v2 4/4] net: phy: Introduce Airoha AN8801/R Gigabit Ethernet PHY driver
From: Louis-Alexis Eyraud @ 2026-03-26 12:04 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	AngeloGioacchino Del Regno, Andrew Lunn, Heiner Kallweit,
	Russell King
  Cc: kevin-kw.huang, macpaul.lin, matthias.bgg, kernel, netdev,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Louis-Alexis Eyraud
In-Reply-To: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com>

From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>

Introduce a driver for the Airoha AN8801R Series Gigabit Ethernet
PHY; this currently supports setting up PHY LEDs, 10/100M, 1000M
speeds, and Wake on LAN and PHY interrupts.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
 drivers/net/phy/Kconfig      |    6 +
 drivers/net/phy/Makefile     |    1 +
 drivers/net/phy/air_an8801.c | 1115 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1122 insertions(+)

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index b6b1cde7e51fa1e470bfe210c8764a193449acb5..4dd77ba487763eaf16c2b390feb237e667f76746 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -90,6 +90,12 @@ config AS21XXX_PHY
 	  AS21210PB1 that all register with the PHY ID 0x7500 0x7500
 	  before the firmware is loaded.
 
+config AIR_AN8801_PHY
+	tristate "Airoha AN8801 Gigabit PHY"
+	select AIR_NET_PHYLIB
+	help
+	  Currently supports the Airoha AN8801R PHY.
+
 config AIR_EN8811H_PHY
 	tristate "Airoha EN8811H 2.5 Gigabit PHY"
 	select AIR_NET_PHYLIB
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 7cf1fa9e12cb6073a9e68c0ffa6284b361b80487..de660ae949453d99f8a383ca0c0e3cf3f1a14922 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -29,6 +29,7 @@ obj-y				+= $(sfp-obj-y) $(sfp-obj-m)
 
 obj-$(CONFIG_ADIN_PHY)		+= adin.o
 obj-$(CONFIG_ADIN1100_PHY)	+= adin1100.o
+obj-$(CONFIG_AIR_AN8801_PHY)	+= air_an8801.o
 obj-$(CONFIG_AIR_EN8811H_PHY)   += air_en8811h.o
 obj-$(CONFIG_AIR_NET_PHYLIB)	+= air_phy_lib.o
 obj-$(CONFIG_AMD_PHY)		+= amd.o
diff --git a/drivers/net/phy/air_an8801.c b/drivers/net/phy/air_an8801.c
new file mode 100644
index 0000000000000000000000000000000000000000..53ade9665fe9e08010fe208f760051f1b621a128
--- /dev/null
+++ b/drivers/net/phy/air_an8801.c
@@ -0,0 +1,1115 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for the Airoha AN8801 Gigabit PHY.
+ *
+ * Copyright (C) 2025 Airoha Technology Corp.
+ * Copyright (C) 2025 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/phy.h>
+#include <linux/pm_wakeirq.h>
+
+#include "air_phy_lib.h"
+
+#define AN8801R_PHY_ID			0xc0ff0421
+
+/* MII Registers */
+
+/* MII Registers - Airoha Page 1 */
+#define AN8801_EXT_REG_PHY		0x14
+#define   AN8801_EXT_PHY_STATUS0	GENMASK(1, 0)
+#define   AN8801_EXT_PHY_DOWNSHIFT_CTL	GENMASK(3, 2) /* 2 to 5 1G auto-neg attempts (0..3) */
+#define   AN8801_EXT_PHY_DOWNSHIFT_EN	BIT(4)
+#define   AN8801_EXT_PHY_CTRL0		BIT(5)
+#define   AN8801_EXT_PHY_STATUS1	GENMASK(8, 6)
+#define   AN8801_EXT_PHY_CTRL1		GENMASK(14, 9)
+
+/* MII Registers - Airoha Page 4 */
+#define AN8801_PBUS_ACCESS		BIT(28)
+#define AN8801_PBUS_EPHY_ACCESS		BIT(24)
+#define AN8801_PBUS_CL22_ACCESS		BIT(23)
+
+/* BPBUS Registers */
+#define AN8801_BPBUS_REG_LED_GPIO	0x54
+#define AN8801_BPBUS_REG_LED_ID_SEL	0x58
+#define   LED_ID_GPIO_SEL(led, gpio)	((led) << ((gpio) * 3))
+#define AN8801_BPBUS_REG_GPIO_MODE	0x70
+#define AN8801_BPBUS_REG_PHY_IRQ_GPIO	0x7c
+#define   AN8801_PHY_IRQ_GPIO_NUM_MASK	GENMASK(19, 16)
+#define   AN8801_PHY_IRQ_GPIO_NUM	1
+
+#define AN8801_BPBUS_REG_CKO		0x1a4
+#define AN8801_CKO_OUTPUT_MODE_AUTO	3
+
+#define AN8801_BPBUS_REG_LINK_MODE	0x5054
+#define  AN8801_BPBUS_LINK_MODE_1000	BIT(0)
+
+#define AN8801_BPBUS_REG_BYPASS_PTP	0x21c004
+#define   AN8801_BYP_PTP_SGMII_TO_GPHY	BIT(8)
+#define   AN8801_BYP_PTP_RGMII_TO_GPHY	BIT(0)
+
+#define AN8801_BPBUS_REG_TXDLY_STEP	0x21c024
+#define   RGMII_DELAY_STEP_MASK		GENMASK(2, 0)
+#define   RGMII_DELAY_NO_STEP		0
+#define   RGMII_DELAY_STEP_1		1
+#define   RGMII_DELAY_STEP_2		2
+#define   RGMII_DELAY_STEP_3		3
+#define   RGMII_DELAY_STEP_4		4
+#define   RGMII_DELAY_STEP_5		5
+#define   RGMII_DELAY_STEP_6		6
+#define   RGMII_DELAY_STEP_7		7
+#define   RGMII_TXDELAY_FORCE_MODE	BIT(24)
+
+#define AN8801_RGMII_TXDELAY_DEFAULT	RGMII_DELAY_STEP_4 /* 1.883ns delay */
+
+#define AN8801_BPBUS_REG_RXDLY_STEP	0x21c02c
+#define   RGMII_RXDELAY_ALIGN		BIT(4)
+#define   RGMII_RXDELAY_FORCE_MODE	BIT(24)
+
+#define AN8801_RGMII_RXDELAY_DEFAULT	RGMII_DELAY_NO_STEP /* 1.992ns delay */
+
+#define AN8801_BPBUS_REG_EFIFO_CTL(x)	(0x270004 + (0x100 * (x))) /* 0..2 */
+#define   AN8801_EFIFO_ALL_EN		GENMASK(7, 0)
+#define   AN8801_EFIFO_RX_EN		BIT(0)
+#define   AN8801_EFIFO_TX_EN		BIT(1)
+#define   AN8801_EFIFO_RX_CLK_EN	BIT(2)
+#define   AN8801_EFIFO_TX_CLK_EN	BIT(3)
+#define   AN8801_EFIFO_RX_EEE_EN	BIT(4)
+#define   AN8801_EFIFO_TX_EEE_EN	BIT(5)
+#define   AN8801_EFIFO_RX_ODD_NIBBLE_EN	BIT(6)
+#define   AN8801_EFIFO_TX_ODD_NIBBLE_EN	BIT(7)
+
+#define AN8801_BPBUS_REG_WOL_MAC_16_47	0x285114
+#define AN8801_BPBUS_REG_WOL_MAC_0_15	0x285118
+
+#define AN8801_BPBUS_REG_WAKEUP_CTL1	0x285400
+#define   AN8801_WOL_WAKE_MAGIC_EN	GENMASK(3, 1)
+#define   AN8801_WOL_WAKE_LNKCHG_EN	BIT(4)
+
+#define AN8801_BPBUS_REG_WAKEUP_CTL2	0x285404
+#define   AN8801_WAKE_OUT_TYPE_PULSE	BIT(0) /* Set/Unset: Pulse/Static */
+#define   AN8801_WAKE_OUT_POLARITY_NEG	BIT(1) /* Set/Unset: Negative/Positive */
+#define   AN8801_WAKE_OUT_WIDTH		GENMASK(2, 3)
+#define    AN8801_WAKE_OUT_84MS		0
+#define    AN8801_WAKE_OUT_168MS	1
+#define    AN8801_WAKE_OUT_336MS	2
+#define    AN8801_WAKE_OUT_672MS	3
+#define   AN8801_WAKE_OUT_EN		BIT(4)
+#define   AN8801_PME_WAKEUP_CLR		BIT(8)
+
+#define AN8801_BPBUS_REG_WAKE_IRQ_EN	0x285700
+#define AN8801_BPBUS_REG_WAKE_IRQ_STS	0x285704
+#define   AN8801_IRQ_WAKE_LNKCHG	BIT(0) /* Wake on link change */
+#define   AN8801_IRQ_WAKE_UNIPKT	BIT(1) /* Wake on unicast packet */
+#define   AN8801_IRQ_WAKE_MULPKT	BIT(2) /* Wake on multicast packet */
+#define   AN8801_IRQ_WAKE_BCPKT		BIT(3) /* Wake on broadcast packet */
+#define   AN8801_IRQ_WAKE_MAGICPKT	BIT(4) /* Wake on magic packet */
+#define   AN8801_IRQ_WAKE_ALL		GENMASK(4, 0)
+
+/* MDIO_MMD_VEND1 Registers */
+#define AN8801_PHY_TX_PAIR_DLY_SEL_GBE	0x13
+#define   AN8801_PHY_PAIR_DLY_SEL_A_GBE	GENMASK(14, 12)
+#define   AN8801_PHY_PAIR_DLY_SEL_B_GBE	GENMASK(10, 8)
+#define   AN8801_PHY_PAIR_DLY_SEL_C_GBE	GENMASK(6, 4)
+#define   AN8801_PHY_PAIR_DLY_SEL_D_GBE	GENMASK(2, 0)
+#define AN8801_PHY_RXADC_CTRL		0xd8
+#define   AN8801_PHY_RXADC_SAMP_PHSEL_A	BIT(12)
+#define   AN8801_PHY_RXADC_SAMP_PHSEL_B	BIT(8)
+#define   AN8801_PHY_RXADC_SAMP_PHSEL_C	BIT(4)
+#define   AN8801_PHY_RXADC_SAMP_PHSEL_D	BIT(0)
+#define AN8801_PHY_RXADC_REV_0		0xd9
+#define   AN8801_PHY_RXADC_REV_MASK_A	GENMASK(15, 8)
+#define   AN8801_PHY_RXADC_REV_MASK_B	GENMASK(7, 0)
+#define AN8801_PHY_RXADC_REV_1		0xda
+#define   AN8801_PHY_RXADC_REV_MASK_C	GENMASK(15, 8)
+#define   AN8801_PHY_RXADC_REV_MASK_D	GENMASK(7, 0)
+
+/* MDIO_MMD_VEND2 Registers */
+#define LED_BCR				0x21
+#define   LED_BCR_MODE_MASK		GENMASK(1, 0)
+#define   LED_BCR_TIME_TEST		BIT(2)
+#define   LED_BCR_CLK_EN		BIT(3)
+#define   LED_BCR_EVT_ALL		BIT(4)
+#define   LED_BCR_EXT_CTRL		BIT(15)
+#define   LED_BCR_MODE_DISABLE		0
+#define   LED_BCR_MODE_2LED		1
+#define   LED_BCR_MODE_3LED_1		2
+#define   LED_BCR_MODE_3LED_2		3
+
+#define LED_ON_DUR			0x22
+#define   LED_ON_DUR_MASK		GENMASK(15, 0)
+
+#define LED_BLINK_DUR			0x23
+#define   LED_BLINK_DUR_MASK		GENMASK(15, 0)
+
+#define LED_ON_CTRL(i)			(0x24 + ((i) * 2))
+#define   LED_ON_EVT_MASK		GENMASK(6, 0)
+#define   LED_ON_EVT_LINK_1000M		BIT(0)
+#define   LED_ON_EVT_LINK_100M		BIT(1)
+#define   LED_ON_EVT_LINK_10M		BIT(2)
+#define   LED_ON_EVT_LINK_DN		BIT(3)
+#define   LED_ON_EVT_FDX		BIT(4)
+#define   LED_ON_EVT_HDX		BIT(5)
+#define   LED_ON_EVT_FORCE		BIT(6)
+#define   LED_ON_POL			BIT(14)
+#define   LED_ON_EN			BIT(15)
+
+#define LED_BLINK_CTRL(i)		(0x25 + ((i) * 2))
+#define LED_BLINK_EVT_MASK		GENMASK(9, 0)
+#define LED_BLINK_EVT_1000M_TX		BIT(0)
+#define LED_BLINK_EVT_1000M_RX		BIT(1)
+#define LED_BLINK_EVT_100M_TX		BIT(2)
+#define LED_BLINK_EVT_100M_RX		BIT(3)
+#define LED_BLINK_EVT_10M_TX		BIT(4)
+#define LED_BLINK_EVT_10M_RX		BIT(5)
+#define LED_BLINK_EVT_COLLISION		BIT(6)
+#define LED_BLINK_EVT_RX_CRC_ERR	BIT(7)
+#define LED_BLINK_EVT_RX_IDLE_ERR	BIT(8)
+#define LED_BLINK_EVT_FORCE		BIT(9)
+
+#define AN8801R_NUM_LEDS		3
+#define AN8801_PERIOD_SHIFT		15
+#define AN8801_PERIOD_UNIT		32768 /* (1 << AN8801_PERIOD_SHIFT) */
+#define AN8801_MAX_PERIOD_MS		2147
+
+#define LED_BLINK_DURATION_UNIT		780
+#define LED_BLINK_DURATION(f)		(LED_BLINK_DURATION_UNIT << (f))
+
+#define AN8801_LED_DURATION_UNIT_US	32768
+
+#define AN8801_REG_PHY_INTERNAL0	0x600
+#define AN8801_REG_PHY_INTERNAL1	0x601
+#define   AN8801_PHY_INTFUNC_MASK	GENMASK(15, 0) /* PHY internal functions */
+
+enum an8801r_led_fn {
+	AN8801R_LED_FN_NONE,
+	AN8801R_LED_FN_LINK,
+	AN8801R_LED_FN_ACTIVITY,
+	AN8801R_LED_FN_MAX,
+};
+
+struct an8801r_priv {
+	bool wake_magic_enabled;
+	bool wake_lnkchg_enabled;
+};
+
+static int an8801_buckpbus_reg_rmw(struct phy_device *phydev,
+				   u32 addr, u32 mask, u32 set)
+{
+	return air_phy_buckpbus_reg_modify(phydev,
+					   addr | AN8801_PBUS_ACCESS,
+					   mask, set);
+}
+
+static int an8801_buckpbus_reg_set_bits(struct phy_device *phydev,
+					u32 addr, u32 mask)
+{
+	return air_phy_buckpbus_reg_modify(phydev,
+					   addr | AN8801_PBUS_ACCESS,
+					   mask, mask);
+}
+
+static int an8801_buckpbus_reg_clear_bits(struct phy_device *phydev,
+					  u32 addr, u32 mask)
+{
+	return air_phy_buckpbus_reg_modify(phydev,
+					   addr | AN8801_PBUS_ACCESS,
+					   mask, 0);
+}
+
+static int an8801_buckpbus_reg_write(struct phy_device *phydev, u32 addr,
+				     u32 data)
+{
+	return air_phy_buckpbus_reg_write(phydev,
+					  addr | AN8801_PBUS_ACCESS,
+					  data);
+}
+
+static int an8801_buckpbus_reg_read(struct phy_device *phydev, u32 addr,
+				    u32 *data)
+{
+	return air_phy_buckpbus_reg_read(phydev,
+					 addr | AN8801_PBUS_ACCESS,
+					 data);
+}
+
+static u32 an8801r_led_blink_ms_to_hw(unsigned long req_ms)
+{
+	u32 req_ns, regval;
+
+	if (req_ms > AN8801_MAX_PERIOD_MS)
+		req_ms = AN8801_MAX_PERIOD_MS;
+
+	req_ns = req_ms * 1000000;
+
+	/* Round to the nearest period unit... */
+	regval = req_ns + (AN8801_PERIOD_UNIT / 2);
+
+	/* ...and now divide by the full period */
+	regval >>= AN8801_PERIOD_SHIFT;
+
+	return regval;
+}
+
+static int an8801r_led_blink_set(struct phy_device *phydev, u8 index,
+				 unsigned long *delay_on,
+				 unsigned long *delay_off)
+{
+	u32 hw_delay_on, hw_delay_off;
+	bool blink;
+	int ret;
+
+	if (index >= AN8801R_NUM_LEDS)
+		return -EINVAL;
+
+	if (delay_on && delay_off) {
+		blink = true;
+
+		if (*delay_on == 0 || *delay_off == 0) {
+			*delay_on = 64;
+			*delay_off = 64;
+		}
+
+		hw_delay_on = an8801r_led_blink_ms_to_hw(*delay_on);
+		hw_delay_off = an8801r_led_blink_ms_to_hw(*delay_off);
+	} else {
+		blink = false;
+	}
+
+	if (blink) {
+		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_DUR,
+				    LED_BLINK_DURATION(hw_delay_on));
+		if (ret)
+			goto error;
+
+		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_ON_DUR,
+				    LED_BLINK_DURATION(hw_delay_off) >> 1);
+		if (ret)
+			goto error;
+	}
+
+	ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index),
+			     LED_ON_EN, blink ? LED_ON_EN : 0);
+	if (ret)
+		return ret;
+
+	return 0;
+error:
+	phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index),
+		       LED_ON_EN, 0);
+	return ret;
+}
+
+static int an8801r_led_brightness_set(struct phy_device *phydev, u8 index,
+				      enum led_brightness value)
+{
+	int ret;
+
+	if (index >= AN8801R_NUM_LEDS)
+		return -EINVAL;
+
+	ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index),
+			     LED_ON_EVT_MASK,
+			     (value == LED_OFF) ? 0 : LED_ON_EVT_FORCE);
+	if (ret)
+		return ret;
+
+	return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index),
+			      LED_ON_EN, (value == LED_OFF) ? 0 : LED_ON_EN);
+}
+
+static int an8801r_led_hw_control_get(struct phy_device *phydev, u8 index,
+				      unsigned long *rules)
+{
+	int on, blink;
+
+	on = phy_read_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index));
+	if (on < 0)
+		return on;
+
+	blink = phy_read_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_CTRL(index));
+	if (blink < 0)
+		return blink;
+
+	if (FIELD_GET(LED_ON_EVT_LINK_10M, on))
+		__set_bit(TRIGGER_NETDEV_LINK_10, rules);
+
+	if (FIELD_GET(LED_ON_EVT_LINK_100M, on))
+		__set_bit(TRIGGER_NETDEV_LINK_100, rules);
+
+	if (FIELD_GET(LED_ON_EVT_LINK_1000M, on))
+		__set_bit(TRIGGER_NETDEV_LINK_1000, rules);
+
+	if (FIELD_GET(LED_ON_EVT_LINK_10M, on) &&
+	    FIELD_GET(LED_ON_EVT_LINK_100M, on) &&
+	    FIELD_GET(LED_ON_EVT_LINK_1000M, on))
+		__set_bit(TRIGGER_NETDEV_LINK, rules);
+
+	if (FIELD_GET(LED_BLINK_EVT_10M_RX, blink) ||
+	    FIELD_GET(LED_BLINK_EVT_100M_RX, blink) ||
+	    FIELD_GET(LED_BLINK_EVT_1000M_RX, blink))
+		__set_bit(TRIGGER_NETDEV_RX, rules);
+
+	if (FIELD_GET(LED_BLINK_EVT_10M_TX, blink) ||
+	    FIELD_GET(LED_BLINK_EVT_100M_TX, blink) ||
+	    FIELD_GET(LED_BLINK_EVT_1000M_TX, blink))
+		__set_bit(TRIGGER_NETDEV_TX, rules);
+
+	if (FIELD_GET(LED_BLINK_EVT_RX_CRC_ERR, blink))
+		__set_bit(TRIGGER_NETDEV_RX_ERR, rules);
+
+	return 0;
+}
+
+static int an8801r_led_trig_to_hw(unsigned long rules, u16 *on, u16 *blink)
+{
+	if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
+		*on |= LED_ON_EVT_LINK_10M;
+
+	if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
+		*on |= LED_ON_EVT_LINK_100M;
+
+	if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
+		*on |= LED_ON_EVT_LINK_1000M;
+
+	if (test_bit(TRIGGER_NETDEV_LINK, &rules)) {
+		*on |= LED_ON_EVT_LINK_10M;
+		*on |= LED_ON_EVT_LINK_100M;
+		*on |= LED_ON_EVT_LINK_1000M;
+	}
+
+	if (test_bit(TRIGGER_NETDEV_RX, &rules)) {
+		*blink |= LED_BLINK_EVT_10M_RX;
+		*blink |= LED_BLINK_EVT_100M_RX;
+		*blink |= LED_BLINK_EVT_1000M_RX;
+	}
+
+	if (test_bit(TRIGGER_NETDEV_TX, &rules)) {
+		*blink |= LED_BLINK_EVT_10M_TX;
+		*blink |= LED_BLINK_EVT_100M_TX;
+		*blink |= LED_BLINK_EVT_1000M_TX;
+	}
+
+	if (test_bit(TRIGGER_NETDEV_RX_ERR, &rules))
+		*blink |= LED_BLINK_EVT_RX_CRC_ERR;
+
+	if (rules && !*on && !*blink)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int an8801r_led_hw_is_supported(struct phy_device *phydev, u8 index,
+				       unsigned long rules)
+{
+	u16 on = 0, blink = 0;
+
+	if (index >= AN8801R_NUM_LEDS)
+		return -EINVAL;
+
+	return an8801r_led_trig_to_hw(rules, &on, &blink);
+}
+
+static int an8801r_led_hw_control_set(struct phy_device *phydev, u8 index,
+				      unsigned long rules)
+{
+	u16 on = 0, blink = 0;
+	int ret;
+
+	if (index >= AN8801R_NUM_LEDS)
+		return -EINVAL;
+
+	ret = an8801r_led_trig_to_hw(rules, &on, &blink);
+	if (ret)
+		return ret;
+
+	ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index),
+			     LED_ON_EVT_MASK, on);
+	if (ret)
+		return ret;
+
+	ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_CTRL(index),
+			     LED_BLINK_EVT_MASK, blink);
+
+	if (ret)
+		return ret;
+
+	return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index),
+			      LED_ON_EN, on | blink ? LED_ON_EN : 0);
+}
+
+static int an8801r_led_polarity_set(struct phy_device *phydev, int index,
+				    unsigned long modes)
+{
+	unsigned long mode;
+	bool active_high;
+
+	if (index >= AN8801R_NUM_LEDS)
+		return -EINVAL;
+
+	for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
+		switch (mode) {
+		case PHY_LED_ACTIVE_HIGH:
+			active_high = true;
+			break;
+		case PHY_LED_ACTIVE_LOW:
+			active_high = false;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index),
+			      LED_ON_POL, active_high ? LED_ON_POL : 0);
+}
+
+static int an8801r_led_init(struct phy_device *phydev, u8 *led_cfg)
+{
+	int led_id, ret;
+
+	/* Set LED BCR Enable */
+	ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, LED_BCR,
+			       LED_BCR_EXT_CTRL | LED_BCR_CLK_EN);
+	if (ret)
+		return ret;
+
+	for (led_id = 0; led_id < AN8801R_NUM_LEDS; led_id++) {
+		unsigned long led_trigger = 0;
+		u32 led_gpio = led_id + 1;
+
+		switch (led_cfg[led_id]) {
+		case AN8801R_LED_FN_LINK:
+			led_trigger = BIT(TRIGGER_NETDEV_LINK);
+			break;
+		case AN8801R_LED_FN_ACTIVITY:
+			led_trigger = BIT(TRIGGER_NETDEV_RX) |
+				    BIT(TRIGGER_NETDEV_TX);
+			break;
+		default:
+			led_trigger = 0;
+			break;
+		}
+
+		ret = an8801_buckpbus_reg_set_bits(phydev,
+						   AN8801_BPBUS_REG_LED_GPIO,
+						   BIT(led_gpio));
+		if (ret)
+			return ret;
+
+		ret = an8801_buckpbus_reg_set_bits(phydev,
+						   AN8801_BPBUS_REG_LED_ID_SEL,
+						   LED_ID_GPIO_SEL(led_id,
+								   led_gpio));
+		if (ret)
+			return ret;
+
+		ret = an8801_buckpbus_reg_clear_bits(phydev,
+						     AN8801_BPBUS_REG_GPIO_MODE,
+						     BIT(led_gpio));
+		if (ret)
+			return ret;
+
+		if (!led_trigger)
+			continue;
+
+		ret = an8801r_led_hw_control_set(phydev, led_id, led_trigger);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static int an8801r_reset_wake(struct phy_device *phydev)
+{
+	struct an8801r_priv *priv = phydev->priv;
+	u32 reg_val = 0;
+	int ret;
+
+	/* Enable wakeup clear and disable wake up output */
+	ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL2,
+					AN8801_PME_WAKEUP_CLR |
+					AN8801_WAKE_OUT_POLARITY_NEG);
+	if (ret)
+		return ret;
+
+	/* Clear WAKEUP_CTL1 register before enabling the wakeup events
+	 * again
+	 */
+	ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL1,
+					0);
+	if (ret)
+		return ret;
+
+	if (priv->wake_magic_enabled)
+		reg_val |= AN8801_WOL_WAKE_MAGIC_EN;
+
+	if (priv->wake_lnkchg_enabled)
+		reg_val |= AN8801_WOL_WAKE_LNKCHG_EN;
+
+	ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL1,
+					reg_val);
+	if (ret)
+		return ret;
+
+	/* Disable wake up clear and re-enable wake up output */
+	ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL2,
+					AN8801_WAKE_OUT_POLARITY_NEG |
+					AN8801_WAKE_OUT_EN);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int an8801r_ack_interrupt(struct phy_device *phydev)
+{
+	int ret;
+
+	/* Reset wake status */
+	ret = an8801r_reset_wake(phydev);
+	if (ret)
+		return ret;
+
+	/* Clear the interrupts by writing the reg */
+	ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKE_IRQ_STS,
+					AN8801_IRQ_WAKE_ALL);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int an8801r_config_intr(struct phy_device *phydev)
+{
+	int ret;
+
+	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+		u32 val = FIELD_PREP(AN8801_PHY_IRQ_GPIO_NUM_MASK,
+				     AN8801_PHY_IRQ_GPIO_NUM);
+
+		ret = an8801_buckpbus_reg_write(phydev,
+						AN8801_BPBUS_REG_PHY_IRQ_GPIO,
+						val);
+		if (ret)
+			return ret;
+
+		ret = an8801_buckpbus_reg_set_bits(phydev,
+						   AN8801_BPBUS_REG_WAKE_IRQ_EN,
+						   AN8801_IRQ_WAKE_LNKCHG);
+		if (ret)
+			return ret;
+
+	} else {
+		ret = an8801_buckpbus_reg_write(phydev,
+						AN8801_BPBUS_REG_PHY_IRQ_GPIO,
+						0);
+		if (ret)
+			return ret;
+
+		ret = an8801_buckpbus_reg_clear_bits(phydev,
+						     AN8801_BPBUS_REG_WAKE_IRQ_EN,
+						     AN8801_IRQ_WAKE_LNKCHG);
+		if (ret)
+			return ret;
+	}
+
+	ret = an8801r_ack_interrupt(phydev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static irqreturn_t an8801r_handle_interrupt(struct phy_device *phydev)
+{
+	u32 irq_status = 0;
+	int ret;
+
+	ret = an8801_buckpbus_reg_read(phydev, AN8801_BPBUS_REG_WAKE_IRQ_STS,
+				       &irq_status);
+	if (ret)
+		return ret;
+
+	ret = an8801r_ack_interrupt(phydev);
+	if (ret)
+		return IRQ_NONE;
+
+	if (irq_status & AN8801_IRQ_WAKE_MAGICPKT) {
+		pm_wakeup_event(&phydev->mdio.dev, 0);
+		return IRQ_HANDLED;
+	}
+
+	if (irq_status & AN8801_IRQ_WAKE_LNKCHG) {
+		phy_trigger_machine(phydev);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static void an8801r_get_wol(struct phy_device *phydev,
+			    struct ethtool_wolinfo *wol)
+{
+	u32 reg_val;
+
+	/* If the PHY is not capable of waking the system, then WoL can not
+	 * be supported.
+	 */
+	if (!device_can_wakeup(&phydev->mdio.dev)) {
+		wol->supported = 0;
+		return;
+	}
+
+	wol->supported = WAKE_MAGIC;
+
+	an8801_buckpbus_reg_read(phydev, AN8801_BPBUS_REG_WAKEUP_CTL1,
+				 &reg_val);
+
+	if (reg_val & AN8801_WOL_WAKE_MAGIC_EN)
+		wol->wolopts |= WAKE_MAGIC;
+	else
+		wol->wolopts &= ~WAKE_MAGIC;
+}
+
+static int an8801r_set_wol(struct phy_device *phydev,
+			   struct ethtool_wolinfo *wol)
+{
+	struct net_device *attach_dev = phydev->attached_dev;
+	const unsigned char *macaddr = attach_dev->dev_addr;
+	struct an8801r_priv *priv = phydev->priv;
+	u32 reg_val;
+	int ret;
+
+	if (!device_can_wakeup(&phydev->mdio.dev))
+		return -EOPNOTSUPP;
+
+	if (wol->wolopts & WAKE_MAGIC) {
+		/* MAC bits 16..47 */
+		reg_val = (macaddr[2] << 24) | (macaddr[3] << 16);
+		reg_val |= (macaddr[4] << 8) | (macaddr[5]);
+
+		ret = an8801_buckpbus_reg_write(phydev,
+						AN8801_BPBUS_REG_WOL_MAC_16_47,
+						reg_val);
+		if (ret)
+			return ret;
+
+		/* MAC bits 0..15 */
+		reg_val = (macaddr[0] << 8) | (macaddr[1]);
+
+		ret = an8801_buckpbus_reg_write(phydev,
+						AN8801_BPBUS_REG_WOL_MAC_0_15,
+						reg_val);
+		if (ret)
+			return ret;
+
+		ret = an8801_buckpbus_reg_set_bits(phydev,
+						   AN8801_BPBUS_REG_WAKEUP_CTL1,
+						   AN8801_WOL_WAKE_MAGIC_EN);
+		if (ret)
+			return ret;
+
+		ret = an8801_buckpbus_reg_set_bits(phydev,
+						   AN8801_BPBUS_REG_WAKE_IRQ_EN,
+						   AN8801_IRQ_WAKE_MAGICPKT);
+		if (ret)
+			return ret;
+
+	} else {
+		ret = an8801_buckpbus_reg_clear_bits(phydev,
+						     AN8801_BPBUS_REG_WAKEUP_CTL1,
+						     AN8801_WOL_WAKE_MAGIC_EN);
+		if (ret)
+			return ret;
+
+		ret = an8801_buckpbus_reg_clear_bits(phydev,
+						     AN8801_BPBUS_REG_WAKE_IRQ_EN,
+						     AN8801_IRQ_WAKE_MAGICPKT);
+		if (ret)
+			return ret;
+	}
+
+	priv->wake_magic_enabled = !!(wol->wolopts & WAKE_MAGIC);
+
+	device_set_wakeup_enable(&phydev->mdio.dev,
+				 priv->wake_magic_enabled);
+
+	return 0;
+}
+
+static int an8801r_of_init_leds(struct phy_device *phydev, u8 *led_cfg)
+{
+	struct device *dev = &phydev->mdio.dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *leds;
+	u32 function_enum_idx;
+	int ret;
+
+	if (!np)
+		return 0;
+
+	/* If devicetree is present, leds configuration is required */
+	leds = of_get_child_by_name(np, "leds");
+	if (!leds)
+		return 0;
+
+	for_each_available_child_of_node_scoped(leds, led) {
+		u32 led_idx;
+
+		ret = of_property_read_u32(led, "reg", &led_idx);
+		if (ret)
+			goto out;
+
+		if (led_idx >= AN8801R_NUM_LEDS) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		ret = of_property_read_u32(led, "function-enumerator",
+					   &function_enum_idx);
+		if (ret)
+			function_enum_idx = AN8801R_LED_FN_NONE;
+
+		if (function_enum_idx >= AN8801R_LED_FN_MAX) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		led_cfg[led_idx] = function_enum_idx;
+	}
+out:
+	of_node_put(leds);
+	return ret;
+}
+
+static int an8801r_rgmii_rxdelay(struct phy_device *phydev, bool enable,
+				 u16 delay_steps)
+{
+	u32 reg_val;
+
+	if (delay_steps > RGMII_DELAY_STEP_MASK)
+		return -EINVAL;
+
+	if (enable) {
+		reg_val = delay_steps & RGMII_DELAY_STEP_MASK;
+
+		 /* Set align bit to add extra offset for RX delay */
+		reg_val |= RGMII_RXDELAY_ALIGN;
+
+		 /* Set force mode bit to enable RX delay insertion */
+		reg_val |= RGMII_RXDELAY_FORCE_MODE;
+	} else {
+		reg_val = 0;
+	}
+
+	return an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_RXDLY_STEP,
+					 reg_val);
+}
+
+static int an8801r_rgmii_txdelay(struct phy_device *phydev, bool enable,
+				 u16 delay_steps)
+{
+	u32 reg_val;
+
+	if (delay_steps > RGMII_DELAY_STEP_MASK)
+		return -EINVAL;
+
+	if (enable) {
+		reg_val = delay_steps & RGMII_DELAY_STEP_MASK;
+
+		 /* Set force mode bit to enable TX delay insertion */
+		reg_val |= RGMII_TXDELAY_FORCE_MODE;
+	} else {
+		reg_val = 0;
+	}
+
+	return an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_TXDLY_STEP,
+					 reg_val);
+}
+
+static int an8801r_rgmii_delay_config(struct phy_device *phydev)
+{
+	bool enable_delay;
+	u16 delay_step;
+	int ret;
+
+	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+		enable_delay = true;
+		delay_step = AN8801_RGMII_TXDELAY_DEFAULT;
+	} else {
+		enable_delay = false;
+		delay_step = RGMII_DELAY_NO_STEP;
+	}
+
+	ret = an8801r_rgmii_txdelay(phydev, enable_delay, delay_step);
+	if (ret)
+		return ret;
+
+	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
+		enable_delay = true;
+		delay_step = AN8801_RGMII_RXDELAY_DEFAULT;
+	} else {
+		enable_delay = false;
+		delay_step = RGMII_DELAY_NO_STEP;
+	}
+
+	ret = an8801r_rgmii_rxdelay(phydev, enable_delay, delay_step);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int an8801r_config_init(struct phy_device *phydev)
+{
+	u8 led_default_function[AN8801R_NUM_LEDS] = { 0 };
+	int ret;
+
+	ret = an8801r_of_init_leds(phydev, led_default_function);
+	if (ret)
+		return ret;
+
+	/* Disable Low Power Mode (LPM) */
+	ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AN8801_REG_PHY_INTERNAL0,
+			    FIELD_PREP(AN8801_PHY_INTFUNC_MASK, 0x1e));
+	if (ret)
+		return ret;
+
+	ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AN8801_REG_PHY_INTERNAL1,
+			    FIELD_PREP(AN8801_PHY_INTFUNC_MASK, 0x2));
+	if (ret)
+		return ret;
+
+	/* Set the PHY to perform auto-downshift after 3 auto-negotiation
+	 * attempts
+	 */
+	ret = phy_write_paged(phydev, AIR_PHY_PAGE_EXTENDED_1,
+			      AN8801_EXT_REG_PHY,
+			      FIELD_PREP(AN8801_EXT_PHY_CTRL1, 0x1d) |
+			      FIELD_PREP(AN8801_EXT_PHY_DOWNSHIFT_CTL, 1) |
+			      AN8801_EXT_PHY_DOWNSHIFT_EN);
+	if (ret < 0)
+		return ret;
+
+	ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_BYPASS_PTP,
+					AN8801_BYP_PTP_RGMII_TO_GPHY);
+	if (ret)
+		return ret;
+
+	ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(0),
+					AN8801_EFIFO_RX_EN |
+					AN8801_EFIFO_TX_EN |
+					AN8801_EFIFO_RX_CLK_EN |
+					AN8801_EFIFO_TX_CLK_EN |
+					AN8801_EFIFO_RX_EEE_EN |
+					AN8801_EFIFO_TX_EEE_EN);
+	if (ret)
+		return ret;
+
+	ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(1),
+					AN8801_EFIFO_ALL_EN);
+	if (ret)
+		return ret;
+
+	ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(2),
+					AN8801_EFIFO_ALL_EN);
+	if (ret)
+		return ret;
+
+	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
+			    AN8801_PHY_TX_PAIR_DLY_SEL_GBE,
+			    FIELD_PREP(AN8801_PHY_PAIR_DLY_SEL_A_GBE, 4) |
+			    FIELD_PREP(AN8801_PHY_PAIR_DLY_SEL_C_GBE, 4));
+	if (ret)
+		return ret;
+
+	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8801_PHY_RXADC_CTRL,
+			    AN8801_PHY_RXADC_SAMP_PHSEL_A |
+			    AN8801_PHY_RXADC_SAMP_PHSEL_C);
+	if (ret)
+		return ret;
+
+	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8801_PHY_RXADC_REV_0,
+			    FIELD_PREP(AN8801_PHY_RXADC_REV_MASK_A, 1));
+	if (ret)
+		return ret;
+
+	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8801_PHY_RXADC_REV_1,
+			    FIELD_PREP(AN8801_PHY_RXADC_REV_MASK_C, 1));
+	if (ret)
+		return ret;
+
+	ret = an8801r_rgmii_delay_config(phydev);
+	if (ret)
+		return ret;
+
+	ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_CKO,
+					AN8801_CKO_OUTPUT_MODE_AUTO);
+	if (ret)
+		return ret;
+
+	ret = an8801r_led_init(phydev, led_default_function);
+	if (ret) {
+		phydev_err(phydev, "Cannot initialize LEDs: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int an8801r_read_status(struct phy_device *phydev)
+{
+	int prev_speed, ret;
+	u32 val;
+
+	prev_speed = phydev->speed;
+
+	ret = genphy_read_status(phydev);
+	if (ret)
+		return ret;
+
+	if (phydev->link && prev_speed != phydev->speed) {
+		val = phydev->speed == SPEED_1000 ?
+		      AN8801_BPBUS_LINK_MODE_1000 : 0;
+
+		return an8801_buckpbus_reg_rmw(phydev,
+					       AN8801_BPBUS_REG_LINK_MODE,
+					       AN8801_BPBUS_LINK_MODE_1000,
+					       val);
+	};
+
+	return 0;
+}
+
+static int an8801r_probe(struct phy_device *phydev)
+{
+	struct device *dev = &phydev->mdio.dev;
+	struct an8801r_priv *priv;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->wake_lnkchg_enabled = true;
+
+	phydev->priv = priv;
+
+	/* Mark this PHY as wakeup capable and register the interrupt as a
+	 * wakeup IRQ if the PHY is marked as a wakeup source in devicetree,
+	 * and the interrupt is valid.
+	 */
+	if (of_property_read_bool(dev->of_node, "wakeup-source") &&
+	    phy_interrupt_is_valid(phydev)) {
+		device_set_wakeup_capable(dev, true);
+		devm_pm_set_wake_irq(dev, phydev->irq);
+	}
+
+	return 0;
+}
+
+static int an8801r_suspend(struct phy_device *phydev)
+{
+	struct an8801r_priv *priv = phydev->priv;
+	int ret;
+
+	/* If the PHY may wake up by a wake-on-line event, disable the link
+	 * interrupt to only keep the  WOL magic interrupt enabled
+	 */
+	if (device_may_wakeup(&phydev->mdio.dev)) {
+		priv->wake_lnkchg_enabled = false;
+
+		ret = an8801_buckpbus_reg_clear_bits(phydev,
+						     AN8801_BPBUS_REG_WAKE_IRQ_EN,
+						     AN8801_IRQ_WAKE_LNKCHG);
+		if (ret)
+			return ret;
+
+		/* Reset Wol status */
+		ret = an8801r_reset_wake(phydev);
+		if (ret)
+			return ret;
+	}
+
+	if (!phydev->wol_enabled)
+		return genphy_suspend(phydev);
+
+	return 0;
+}
+
+static int an8801r_resume(struct phy_device *phydev)
+{
+	struct an8801r_priv *priv = phydev->priv;
+	int ret;
+
+	ret = genphy_resume(phydev);
+	if (ret)
+		return ret;
+
+	/* Restore the interrupt enable so phylib can receive link
+	 * state interrupts.
+	 */
+	if (device_may_wakeup(&phydev->mdio.dev)) {
+		priv->wake_lnkchg_enabled = true;
+
+		ret = an8801_buckpbus_reg_set_bits(phydev,
+						   AN8801_BPBUS_REG_WAKEUP_CTL1,
+						   AN8801_WOL_WAKE_LNKCHG_EN);
+		if (ret)
+			return ret;
+
+		ret = an8801_buckpbus_reg_set_bits(phydev,
+						   AN8801_BPBUS_REG_WAKE_IRQ_EN,
+						   AN8801_IRQ_WAKE_LNKCHG);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct phy_driver airoha_driver[] = {
+{
+	PHY_ID_MATCH_MODEL(AN8801R_PHY_ID),
+	.name			= "Airoha AN8801R",
+	.probe			= an8801r_probe,
+	.config_init		= an8801r_config_init,
+	.suspend		= an8801r_suspend,
+	.resume			= an8801r_resume,
+	.config_aneg		= genphy_config_aneg,
+	.read_status		= an8801r_read_status,
+	.config_intr		= an8801r_config_intr,
+	.handle_interrupt	= an8801r_handle_interrupt,
+	.set_wol		= an8801r_set_wol,
+	.get_wol		= an8801r_get_wol,
+	.read_page		= air_phy_read_page,
+	.write_page		= air_phy_write_page,
+	.flags			= PHY_ALWAYS_CALL_SUSPEND,
+	.led_brightness_set	= an8801r_led_brightness_set,
+	.led_blink_set		= an8801r_led_blink_set,
+	.led_hw_is_supported	= an8801r_led_hw_is_supported,
+	.led_hw_control_set	= an8801r_led_hw_control_set,
+	.led_hw_control_get	= an8801r_led_hw_control_get,
+	.led_polarity_set	= an8801r_led_polarity_set,
+} };
+module_phy_driver(airoha_driver);
+
+static struct mdio_device_id __maybe_unused an8801_tbl[] = {
+	{ PHY_ID_MATCH_MODEL(AN8801R_PHY_ID) },
+	{ }
+};
+MODULE_DEVICE_TABLE(mdio, an8801_tbl);
+
+MODULE_DESCRIPTION("Airoha AN8801 PHY driver");
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
+MODULE_LICENSE("GPL");

-- 
2.53.0


^ permalink raw reply related

* [PATCH net-next v2 3/4] net: phy: air_phy_lib: Factorize BuckPBus register accessors
From: Louis-Alexis Eyraud @ 2026-03-26 12:04 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	AngeloGioacchino Del Regno, Andrew Lunn, Heiner Kallweit,
	Russell King
  Cc: kevin-kw.huang, macpaul.lin, matthias.bgg, kernel, netdev,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Louis-Alexis Eyraud
In-Reply-To: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com>

In preparation of Airoha AN8801R PHY support, move the BuckPBus
register accessors and definitions, present in air_en8811h driver,
into the Airoha PHY shared code (air_phy_lib), so they will be usable
by the new driver without duplicating them.
Also, update air_en8811h driver to use the new function names.

Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
 drivers/net/phy/air_en8811h.c | 303 ++++++++----------------------------------
 drivers/net/phy/air_phy_lib.c | 180 +++++++++++++++++++++++++
 drivers/net/phy/air_phy_lib.h |  23 ++++
 3 files changed, 259 insertions(+), 247 deletions(-)

diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c
index be7c3426182a26fe3799b875827750e7772caadd..a42898ae41358fe86072a55528a0ecff0eb5ec19 100644
--- a/drivers/net/phy/air_en8811h.c
+++ b/drivers/net/phy/air_en8811h.c
@@ -42,22 +42,6 @@
 #define   AIR_AUX_CTRL_STATUS_SPEED_1000	0x8
 #define   AIR_AUX_CTRL_STATUS_SPEED_2500	0xc
 
-#define   AIR_PHY_PAGE_STANDARD			0x0000
-#define   AIR_PHY_PAGE_EXTENDED_4		0x0004
-
-/* MII Registers Page 4*/
-#define AIR_BPBUS_MODE			0x10
-#define   AIR_BPBUS_MODE_ADDR_FIXED		0x0000
-#define   AIR_BPBUS_MODE_ADDR_INCR		BIT(15)
-#define AIR_BPBUS_WR_ADDR_HIGH		0x11
-#define AIR_BPBUS_WR_ADDR_LOW		0x12
-#define AIR_BPBUS_WR_DATA_HIGH		0x13
-#define AIR_BPBUS_WR_DATA_LOW		0x14
-#define AIR_BPBUS_RD_ADDR_HIGH		0x15
-#define AIR_BPBUS_RD_ADDR_LOW		0x16
-#define AIR_BPBUS_RD_DATA_HIGH		0x17
-#define AIR_BPBUS_RD_DATA_LOW		0x18
-
 /* Registers on MDIO_MMD_VEND1 */
 #define EN8811H_PHY_FW_STATUS		0x8009
 #define   EN8811H_PHY_READY			0x02
@@ -245,183 +229,6 @@ static const unsigned long en8811h_led_trig = BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
 					      BIT(TRIGGER_NETDEV_RX)          |
 					      BIT(TRIGGER_NETDEV_TX);
 
-static int __air_buckpbus_reg_write(struct phy_device *phydev,
-				    u32 pbus_address, u32 pbus_data)
-{
-	int ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
-	if (ret < 0)
-		return ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
-			  upper_16_bits(pbus_address));
-	if (ret < 0)
-		return ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
-			  lower_16_bits(pbus_address));
-	if (ret < 0)
-		return ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
-			  upper_16_bits(pbus_data));
-	if (ret < 0)
-		return ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
-			  lower_16_bits(pbus_data));
-	if (ret < 0)
-		return ret;
-
-	return 0;
-}
-
-static int air_buckpbus_reg_write(struct phy_device *phydev,
-				  u32 pbus_address, u32 pbus_data)
-{
-	int saved_page;
-	int ret = 0;
-
-	saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
-
-	if (saved_page >= 0) {
-		ret = __air_buckpbus_reg_write(phydev, pbus_address,
-					       pbus_data);
-		if (ret < 0)
-			phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
-				   pbus_address, ret);
-	}
-
-	return phy_restore_page(phydev, saved_page, ret);
-}
-
-static int __air_buckpbus_reg_read(struct phy_device *phydev,
-				   u32 pbus_address, u32 *pbus_data)
-{
-	int pbus_data_low, pbus_data_high;
-	int ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
-	if (ret < 0)
-		return ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
-			  upper_16_bits(pbus_address));
-	if (ret < 0)
-		return ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
-			  lower_16_bits(pbus_address));
-	if (ret < 0)
-		return ret;
-
-	pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
-	if (pbus_data_high < 0)
-		return pbus_data_high;
-
-	pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
-	if (pbus_data_low < 0)
-		return pbus_data_low;
-
-	*pbus_data = pbus_data_low | (pbus_data_high << 16);
-	return 0;
-}
-
-static int air_buckpbus_reg_read(struct phy_device *phydev,
-				 u32 pbus_address, u32 *pbus_data)
-{
-	int saved_page;
-	int ret = 0;
-
-	saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
-
-	if (saved_page >= 0) {
-		ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data);
-		if (ret < 0)
-			phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
-				   pbus_address, ret);
-	}
-
-	return phy_restore_page(phydev, saved_page, ret);
-}
-
-static int __air_buckpbus_reg_modify(struct phy_device *phydev,
-				     u32 pbus_address, u32 mask, u32 set)
-{
-	int pbus_data_low, pbus_data_high;
-	u32 pbus_data_old, pbus_data_new;
-	int ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
-	if (ret < 0)
-		return ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
-			  upper_16_bits(pbus_address));
-	if (ret < 0)
-		return ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
-			  lower_16_bits(pbus_address));
-	if (ret < 0)
-		return ret;
-
-	pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
-	if (pbus_data_high < 0)
-		return pbus_data_high;
-
-	pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
-	if (pbus_data_low < 0)
-		return pbus_data_low;
-
-	pbus_data_old = pbus_data_low | (pbus_data_high << 16);
-	pbus_data_new = (pbus_data_old & ~mask) | set;
-	if (pbus_data_new == pbus_data_old)
-		return 0;
-
-	ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
-			  upper_16_bits(pbus_address));
-	if (ret < 0)
-		return ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
-			  lower_16_bits(pbus_address));
-	if (ret < 0)
-		return ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
-			  upper_16_bits(pbus_data_new));
-	if (ret < 0)
-		return ret;
-
-	ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
-			  lower_16_bits(pbus_data_new));
-	if (ret < 0)
-		return ret;
-
-	return 0;
-}
-
-static int air_buckpbus_reg_modify(struct phy_device *phydev,
-				   u32 pbus_address, u32 mask, u32 set)
-{
-	int saved_page;
-	int ret = 0;
-
-	saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
-
-	if (saved_page >= 0) {
-		ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask,
-						set);
-		if (ret < 0)
-			phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
-				   pbus_address, ret);
-	}
-
-	return phy_restore_page(phydev, saved_page, ret);
-}
-
 static int __air_write_buf(struct phy_device *phydev, u32 address,
 			   const struct firmware *fw)
 {
@@ -480,8 +287,8 @@ static int en8811h_wait_mcu_ready(struct phy_device *phydev)
 {
 	int ret, reg_value;
 
-	ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-				     EN8811H_FW_CTRL_1_FINISH);
+	ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+					 EN8811H_FW_CTRL_1_FINISH);
 	if (ret)
 		return ret;
 
@@ -506,28 +313,29 @@ static int an8811hb_check_crc(struct phy_device *phydev, u32 set1,
 	int ret;
 
 	/* Configure CRC */
-	ret = air_buckpbus_reg_modify(phydev, set1,
-				      AN8811HB_CRC_RD_EN,
-				      AN8811HB_CRC_RD_EN);
+	ret = air_phy_buckpbus_reg_modify(phydev, set1,
+					  AN8811HB_CRC_RD_EN,
+					  AN8811HB_CRC_RD_EN);
 	if (ret < 0)
 		return ret;
-	air_buckpbus_reg_read(phydev, set1, &pbus_value);
+	air_phy_buckpbus_reg_read(phydev, set1, &pbus_value);
 
 	do {
 		msleep(300);
-		air_buckpbus_reg_read(phydev, mon2, &pbus_value);
+		air_phy_buckpbus_reg_read(phydev, mon2, &pbus_value);
 
 		/* We do not know what errors this check is supposed
 		 * catch or what to do about a failure. So print the
 		 * result and continue like the vendor driver does.
 		 */
 		if (pbus_value & AN8811HB_CRC_ST) {
-			air_buckpbus_reg_read(phydev, mon3, &pbus_value);
+			air_phy_buckpbus_reg_read(phydev, mon3, &pbus_value);
 			phydev_dbg(phydev, "CRC Check %s!\n",
 				   pbus_value & AN8811HB_CRC_CHECK_PASS ?
 					"PASS" : "FAIL");
-			return air_buckpbus_reg_modify(phydev, set1,
-						       AN8811HB_CRC_RD_EN, 0);
+			return air_phy_buckpbus_reg_modify(phydev, set1,
+							   AN8811HB_CRC_RD_EN,
+							   0);
 		}
 	} while (--retry);
 
@@ -539,8 +347,8 @@ static void en8811h_print_fw_version(struct phy_device *phydev)
 {
 	struct en8811h_priv *priv = phydev->priv;
 
-	air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
-			      &priv->firmware_version);
+	air_phy_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
+				  &priv->firmware_version);
 	phydev_info(phydev, "MD32 firmware version: %08x\n",
 		    priv->firmware_version);
 }
@@ -565,8 +373,8 @@ static int an8811hb_load_firmware(struct phy_device *phydev)
 {
 	int ret;
 
-	ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-				     EN8811H_FW_CTRL_1_START);
+	ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+					 EN8811H_FW_CTRL_1_START);
 	if (ret < 0)
 		return ret;
 
@@ -607,14 +415,14 @@ static int en8811h_load_firmware(struct phy_device *phydev)
 	if (ret < 0)
 		goto en8811h_load_firmware_rel1;
 
-	ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-				     EN8811H_FW_CTRL_1_START);
+	ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+					 EN8811H_FW_CTRL_1_START);
 	if (ret < 0)
 		goto en8811h_load_firmware_out;
 
-	ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
-				      EN8811H_FW_CTRL_2_LOADING,
-				      EN8811H_FW_CTRL_2_LOADING);
+	ret = air_phy_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
+					  EN8811H_FW_CTRL_2_LOADING,
+					  EN8811H_FW_CTRL_2_LOADING);
 	if (ret < 0)
 		goto en8811h_load_firmware_out;
 
@@ -626,8 +434,8 @@ static int en8811h_load_firmware(struct phy_device *phydev)
 	if (ret < 0)
 		goto en8811h_load_firmware_out;
 
-	ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
-				      EN8811H_FW_CTRL_2_LOADING, 0);
+	ret = air_phy_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
+					  EN8811H_FW_CTRL_2_LOADING, 0);
 	if (ret < 0)
 		goto en8811h_load_firmware_out;
 
@@ -653,8 +461,8 @@ static int en8811h_restart_mcu(struct phy_device *phydev)
 {
 	int ret;
 
-	ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-				     EN8811H_FW_CTRL_1_START);
+	ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+					 EN8811H_FW_CTRL_1_START);
 	if (ret < 0)
 		return ret;
 
@@ -948,7 +756,7 @@ static unsigned long an8811hb_clk_recalc_rate(struct clk_hw *hw,
 	u32 pbus_value;
 	int ret;
 
-	ret = air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value);
+	ret = air_phy_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value);
 	if (ret < 0)
 		return ret;
 
@@ -960,9 +768,9 @@ static int an8811hb_clk_enable(struct clk_hw *hw)
 	struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
 	struct phy_device *phydev = priv->phydev;
 
-	return air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,
-				       AN8811HB_CLK_DRV_CKO_MASK,
-				       AN8811HB_CLK_DRV_CKO_MASK);
+	return air_phy_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,
+					   AN8811HB_CLK_DRV_CKO_MASK,
+					   AN8811HB_CLK_DRV_CKO_MASK);
 }
 
 static void an8811hb_clk_disable(struct clk_hw *hw)
@@ -970,8 +778,8 @@ static void an8811hb_clk_disable(struct clk_hw *hw)
 	struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
 	struct phy_device *phydev = priv->phydev;
 
-	air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,
-				AN8811HB_CLK_DRV_CKO_MASK, 0);
+	air_phy_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,
+				    AN8811HB_CLK_DRV_CKO_MASK, 0);
 }
 
 static int an8811hb_clk_is_enabled(struct clk_hw *hw)
@@ -981,7 +789,7 @@ static int an8811hb_clk_is_enabled(struct clk_hw *hw)
 	u32 pbus_value;
 	int ret;
 
-	ret = air_buckpbus_reg_read(phydev, AN8811HB_CLK_DRV, &pbus_value);
+	ret = air_phy_buckpbus_reg_read(phydev, AN8811HB_CLK_DRV, &pbus_value);
 	if (ret < 0)
 		return ret;
 
@@ -1047,7 +855,7 @@ static unsigned long en8811h_clk_recalc_rate(struct clk_hw *hw,
 	u32 pbus_value;
 	int ret;
 
-	ret = air_buckpbus_reg_read(phydev, EN8811H_HWTRAP1, &pbus_value);
+	ret = air_phy_buckpbus_reg_read(phydev, EN8811H_HWTRAP1, &pbus_value);
 	if (ret < 0)
 		return ret;
 
@@ -1059,9 +867,9 @@ static int en8811h_clk_enable(struct clk_hw *hw)
 	struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
 	struct phy_device *phydev = priv->phydev;
 
-	return air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM,
-				       EN8811H_CLK_CGM_CKO,
-				       EN8811H_CLK_CGM_CKO);
+	return air_phy_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM,
+					   EN8811H_CLK_CGM_CKO,
+					   EN8811H_CLK_CGM_CKO);
 }
 
 static void en8811h_clk_disable(struct clk_hw *hw)
@@ -1069,8 +877,8 @@ static void en8811h_clk_disable(struct clk_hw *hw)
 	struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
 	struct phy_device *phydev = priv->phydev;
 
-	air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM,
-				EN8811H_CLK_CGM_CKO, 0);
+	air_phy_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM,
+				    EN8811H_CLK_CGM_CKO, 0);
 }
 
 static int en8811h_clk_is_enabled(struct clk_hw *hw)
@@ -1080,7 +888,7 @@ static int en8811h_clk_is_enabled(struct clk_hw *hw)
 	u32 pbus_value;
 	int ret;
 
-	ret = air_buckpbus_reg_read(phydev, EN8811H_CLK_CGM, &pbus_value);
+	ret = air_phy_buckpbus_reg_read(phydev, EN8811H_CLK_CGM, &pbus_value);
 	if (ret < 0)
 		return ret;
 
@@ -1191,9 +999,9 @@ static int an8811hb_probe(struct phy_device *phydev)
 		return ret;
 
 	/* Configure led gpio pins as output */
-	ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
-				      AN8811HB_GPIO_OUTPUT_345,
-				      AN8811HB_GPIO_OUTPUT_345);
+	ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
+					  AN8811HB_GPIO_OUTPUT_345,
+					  AN8811HB_GPIO_OUTPUT_345);
 	if (ret < 0)
 		return ret;
 
@@ -1232,9 +1040,9 @@ static int en8811h_probe(struct phy_device *phydev)
 		return ret;
 
 	/* Configure led gpio pins as output */
-	ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT,
-				      EN8811H_GPIO_OUTPUT_345,
-				      EN8811H_GPIO_OUTPUT_345);
+	ret = air_phy_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT,
+					  EN8811H_GPIO_OUTPUT_345,
+					  EN8811H_GPIO_OUTPUT_345);
 	if (ret < 0)
 		return ret;
 
@@ -1254,9 +1062,9 @@ static int an8811hb_config_serdes_polarity(struct phy_device *phydev)
 		return ret;
 	if (pol == PHY_POL_NORMAL)
 		pbus_value |= AN8811HB_RX_POLARITY_NORMAL;
-	ret = air_buckpbus_reg_modify(phydev, AN8811HB_RX_POLARITY,
-				      AN8811HB_RX_POLARITY_NORMAL,
-				      pbus_value);
+	ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_RX_POLARITY,
+					  AN8811HB_RX_POLARITY_NORMAL,
+					  pbus_value);
 	if (ret < 0)
 		return ret;
 
@@ -1267,9 +1075,9 @@ static int an8811hb_config_serdes_polarity(struct phy_device *phydev)
 	pbus_value = 0;
 	if (pol == PHY_POL_NORMAL)
 		pbus_value |= AN8811HB_TX_POLARITY_NORMAL;
-	return air_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY,
-				       AN8811HB_TX_POLARITY_NORMAL,
-				       pbus_value);
+	return air_phy_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY,
+					   AN8811HB_TX_POLARITY_NORMAL,
+					   pbus_value);
 }
 
 static int en8811h_config_serdes_polarity(struct phy_device *phydev)
@@ -1303,9 +1111,10 @@ static int en8811h_config_serdes_polarity(struct phy_device *phydev)
 	if (pol == PHY_POL_NORMAL)
 		pbus_value |= EN8811H_POLARITY_TX_NORMAL;
 
-	return air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
-				       EN8811H_POLARITY_RX_REVERSE |
-				       EN8811H_POLARITY_TX_NORMAL, pbus_value);
+	return air_phy_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
+					   EN8811H_POLARITY_RX_REVERSE |
+					   EN8811H_POLARITY_TX_NORMAL,
+					   pbus_value);
 }
 
 static int an8811hb_config_init(struct phy_device *phydev)
@@ -1457,8 +1266,8 @@ static int en8811h_read_status(struct phy_device *phydev)
 				 val & MDIO_AN_10GBT_STAT_LP2_5G);
 	} else {
 		/* Get link partner 2.5GBASE-T ability from vendor register */
-		ret = air_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA,
-					    &pbus_value);
+		ret = air_phy_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA,
+						&pbus_value);
 		if (ret < 0)
 			return ret;
 		linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
diff --git a/drivers/net/phy/air_phy_lib.c b/drivers/net/phy/air_phy_lib.c
index 04c4719a073f98ef75eabd54c4851f6a16391350..780ce2a17d3a58d5e3105534e9fea71bf3097b36 100644
--- a/drivers/net/phy/air_phy_lib.c
+++ b/drivers/net/phy/air_phy_lib.c
@@ -13,6 +13,186 @@
 
 #define AIR_EXT_PAGE_ACCESS		0x1f
 
+static int __air_buckpbus_reg_read(struct phy_device *phydev,
+				   u32 pbus_address, u32 *pbus_data)
+{
+	int pbus_data_low, pbus_data_high;
+	int ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
+	if (ret < 0)
+		return ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
+			  upper_16_bits(pbus_address));
+	if (ret < 0)
+		return ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
+			  lower_16_bits(pbus_address));
+	if (ret < 0)
+		return ret;
+
+	pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
+	if (pbus_data_high < 0)
+		return pbus_data_high;
+
+	pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
+	if (pbus_data_low < 0)
+		return pbus_data_low;
+
+	*pbus_data = pbus_data_low | (pbus_data_high << 16);
+	return 0;
+}
+
+static int __air_buckpbus_reg_write(struct phy_device *phydev,
+				    u32 pbus_address, u32 pbus_data)
+{
+	int ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
+	if (ret < 0)
+		return ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
+			  upper_16_bits(pbus_address));
+	if (ret < 0)
+		return ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
+			  lower_16_bits(pbus_address));
+	if (ret < 0)
+		return ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
+			  upper_16_bits(pbus_data));
+	if (ret < 0)
+		return ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
+			  lower_16_bits(pbus_data));
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int __air_buckpbus_reg_modify(struct phy_device *phydev,
+				     u32 pbus_address, u32 mask, u32 set)
+{
+	int pbus_data_low, pbus_data_high;
+	u32 pbus_data_old, pbus_data_new;
+	int ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
+	if (ret < 0)
+		return ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
+			  upper_16_bits(pbus_address));
+	if (ret < 0)
+		return ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
+			  lower_16_bits(pbus_address));
+	if (ret < 0)
+		return ret;
+
+	pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
+	if (pbus_data_high < 0)
+		return pbus_data_high;
+
+	pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
+	if (pbus_data_low < 0)
+		return pbus_data_low;
+
+	pbus_data_old = pbus_data_low | (pbus_data_high << 16);
+	pbus_data_new = (pbus_data_old & ~mask) | set;
+	if (pbus_data_new == pbus_data_old)
+		return 0;
+
+	ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
+			  upper_16_bits(pbus_address));
+	if (ret < 0)
+		return ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
+			  lower_16_bits(pbus_address));
+	if (ret < 0)
+		return ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
+			  upper_16_bits(pbus_data_new));
+	if (ret < 0)
+		return ret;
+
+	ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
+			  lower_16_bits(pbus_data_new));
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+int air_phy_buckpbus_reg_read(struct phy_device *phydev, u32 pbus_address,
+			      u32 *pbus_data)
+{
+	int saved_page;
+	int ret = 0;
+
+	saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+
+	if (saved_page >= 0) {
+		ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data);
+		if (ret < 0)
+			phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
+				   pbus_address, ret);
+	}
+
+	return phy_restore_page(phydev, saved_page, ret);
+}
+EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_read);
+
+int air_phy_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address,
+			       u32 pbus_data)
+{
+	int saved_page;
+	int ret = 0;
+
+	saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+
+	if (saved_page >= 0) {
+		ret = __air_buckpbus_reg_write(phydev, pbus_address,
+					       pbus_data);
+		if (ret < 0)
+			phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
+				   pbus_address, ret);
+	}
+
+	return phy_restore_page(phydev, saved_page, ret);
+}
+EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_write);
+
+int air_phy_buckpbus_reg_modify(struct phy_device *phydev, u32 pbus_address,
+				u32 mask, u32 set)
+{
+	int saved_page;
+	int ret = 0;
+
+	saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+
+	if (saved_page >= 0) {
+		ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask,
+						set);
+		if (ret < 0)
+			phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
+				   pbus_address, ret);
+	}
+
+	return phy_restore_page(phydev, saved_page, ret);
+}
+EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_modify);
+
 int air_phy_read_page(struct phy_device *phydev)
 {
 	return __phy_read(phydev, AIR_EXT_PAGE_ACCESS);
diff --git a/drivers/net/phy/air_phy_lib.h b/drivers/net/phy/air_phy_lib.h
index dd501b175a3a38e57744f79571eb1bc4ef46fdf5..23d8e3e318398958f2bba297619f35a86f9871a9 100644
--- a/drivers/net/phy/air_phy_lib.h
+++ b/drivers/net/phy/air_phy_lib.h
@@ -8,6 +8,29 @@
 #ifndef __AIR_PHY_LIB_H
 #define __AIR_PHY_LIB_H
 
+#define AIR_PHY_PAGE_STANDARD		0x0000
+#define AIR_PHY_PAGE_EXTENDED_1		0x0001
+#define AIR_PHY_PAGE_EXTENDED_4		0x0004
+
+/* MII Registers Page 4*/
+#define AIR_BPBUS_MODE			0x10
+#define   AIR_BPBUS_MODE_ADDR_FIXED		0x0000
+#define   AIR_BPBUS_MODE_ADDR_INCR		BIT(15)
+#define AIR_BPBUS_WR_ADDR_HIGH		0x11
+#define AIR_BPBUS_WR_ADDR_LOW		0x12
+#define AIR_BPBUS_WR_DATA_HIGH		0x13
+#define AIR_BPBUS_WR_DATA_LOW		0x14
+#define AIR_BPBUS_RD_ADDR_HIGH		0x15
+#define AIR_BPBUS_RD_ADDR_LOW		0x16
+#define AIR_BPBUS_RD_DATA_HIGH		0x17
+#define AIR_BPBUS_RD_DATA_LOW		0x18
+
+int air_phy_buckpbus_reg_modify(struct phy_device *phydev, u32 pbus_address,
+				u32 mask, u32 set);
+int air_phy_buckpbus_reg_read(struct phy_device *phydev, u32 pbus_address,
+			      u32 *pbus_data);
+int air_phy_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address,
+			       u32 pbus_data);
 int air_phy_read_page(struct phy_device *phydev);
 int air_phy_write_page(struct phy_device *phydev, int page);
 

-- 
2.53.0


^ permalink raw reply related

* [PATCH net-next v2 2/4] net: phy: Add Airoha phy library for shared code
From: Louis-Alexis Eyraud @ 2026-03-26 12:04 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	AngeloGioacchino Del Regno, Andrew Lunn, Heiner Kallweit,
	Russell King
  Cc: kevin-kw.huang, macpaul.lin, matthias.bgg, kernel, netdev,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Louis-Alexis Eyraud
In-Reply-To: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com>

In preparation of Airoha AN8801R PHY support, split out the interface
functions that will be common between the already present air_en8811h
driver and the new one, and put them into a new library named
air_phy_lib.

Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
 drivers/net/phy/Kconfig       |  6 ++++++
 drivers/net/phy/Makefile      |  1 +
 drivers/net/phy/air_en8811h.c | 13 ++-----------
 drivers/net/phy/air_phy_lib.c | 30 ++++++++++++++++++++++++++++++
 drivers/net/phy/air_phy_lib.h | 14 ++++++++++++++
 5 files changed, 53 insertions(+), 11 deletions(-)

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index b5ee338b620d53980fbec9e83ab0de3d96ab4cc9..b6b1cde7e51fa1e470bfe210c8764a193449acb5 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -92,10 +92,16 @@ config AS21XXX_PHY
 
 config AIR_EN8811H_PHY
 	tristate "Airoha EN8811H 2.5 Gigabit PHY"
+	select AIR_NET_PHYLIB
 	select PHY_COMMON_PROPS
 	help
 	  Currently supports the Airoha EN8811H PHY.
 
+config AIR_NET_PHYLIB
+	tristate
+	help
+	  Airoha Ethernet PHY common library
+
 config AMD_PHY
 	tristate "AMD and Altima PHYs"
 	help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 05e4878af27abeae3dfd9ab18fd29f8bf788b2a4..7cf1fa9e12cb6073a9e68c0ffa6284b361b80487 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -30,6 +30,7 @@ obj-y				+= $(sfp-obj-y) $(sfp-obj-m)
 obj-$(CONFIG_ADIN_PHY)		+= adin.o
 obj-$(CONFIG_ADIN1100_PHY)	+= adin1100.o
 obj-$(CONFIG_AIR_EN8811H_PHY)   += air_en8811h.o
+obj-$(CONFIG_AIR_NET_PHYLIB)	+= air_phy_lib.o
 obj-$(CONFIG_AMD_PHY)		+= amd.o
 obj-$(CONFIG_AMCC_QT2025_PHY)	+= qt2025.o
 obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia/
diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c
index 29ae73e65caaa9cdebe2253b5349aa6c7478dc85..be7c3426182a26fe3799b875827750e7772caadd 100644
--- a/drivers/net/phy/air_en8811h.c
+++ b/drivers/net/phy/air_en8811h.c
@@ -21,6 +21,8 @@
 #include <linux/wordpart.h>
 #include <linux/unaligned.h>
 
+#include "air_phy_lib.h"
+
 #define EN8811H_PHY_ID		0x03a2a411
 #define AN8811HB_PHY_ID		0xc0ff04a0
 
@@ -40,7 +42,6 @@
 #define   AIR_AUX_CTRL_STATUS_SPEED_1000	0x8
 #define   AIR_AUX_CTRL_STATUS_SPEED_2500	0xc
 
-#define AIR_EXT_PAGE_ACCESS		0x1f
 #define   AIR_PHY_PAGE_STANDARD			0x0000
 #define   AIR_PHY_PAGE_EXTENDED_4		0x0004
 
@@ -244,16 +245,6 @@ static const unsigned long en8811h_led_trig = BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
 					      BIT(TRIGGER_NETDEV_RX)          |
 					      BIT(TRIGGER_NETDEV_TX);
 
-static int air_phy_read_page(struct phy_device *phydev)
-{
-	return __phy_read(phydev, AIR_EXT_PAGE_ACCESS);
-}
-
-static int air_phy_write_page(struct phy_device *phydev, int page)
-{
-	return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page);
-}
-
 static int __air_buckpbus_reg_write(struct phy_device *phydev,
 				    u32 pbus_address, u32 pbus_data)
 {
diff --git a/drivers/net/phy/air_phy_lib.c b/drivers/net/phy/air_phy_lib.c
new file mode 100644
index 0000000000000000000000000000000000000000..04c4719a073f98ef75eabd54c4851f6a16391350
--- /dev/null
+++ b/drivers/net/phy/air_phy_lib.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Airoha Ethernet PHY common library
+ *
+ * Copyright (C) 2026 Airoha Technology Corp.
+ * Copyright (C) 2026 Collabora Ltd.
+ *                    Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#include <linux/phy.h>
+
+#include "air_phy_lib.h"
+
+#define AIR_EXT_PAGE_ACCESS		0x1f
+
+int air_phy_read_page(struct phy_device *phydev)
+{
+	return __phy_read(phydev, AIR_EXT_PAGE_ACCESS);
+}
+EXPORT_SYMBOL_GPL(air_phy_read_page);
+
+int air_phy_write_page(struct phy_device *phydev, int page)
+{
+	return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page);
+}
+EXPORT_SYMBOL_GPL(air_phy_write_page);
+
+MODULE_DESCRIPTION("Airoha PHY Library");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Louis-Alexis Eyraud");
diff --git a/drivers/net/phy/air_phy_lib.h b/drivers/net/phy/air_phy_lib.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd501b175a3a38e57744f79571eb1bc4ef46fdf5
--- /dev/null
+++ b/drivers/net/phy/air_phy_lib.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2026 Airoha Technology Corp.
+ * Copyright (C) 2026 Collabora Ltd.
+ *                    Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#ifndef __AIR_PHY_LIB_H
+#define __AIR_PHY_LIB_H
+
+int air_phy_read_page(struct phy_device *phydev);
+int air_phy_write_page(struct phy_device *phydev, int page);
+
+#endif /* __AIR_PHY_LIB_H */

-- 
2.53.0


^ permalink raw reply related

* [PATCH net-next v2 1/4] dt-bindings: net: Add support for Airoha AN8801/R GbE PHY
From: Louis-Alexis Eyraud @ 2026-03-26 12:04 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	AngeloGioacchino Del Regno, Andrew Lunn, Heiner Kallweit,
	Russell King
  Cc: kevin-kw.huang, macpaul.lin, matthias.bgg, kernel, netdev,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Louis-Alexis Eyraud
In-Reply-To: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com>

From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>

Add a new binding to support the Airoha AN8801R Series Gigabit
Ethernet PHY.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
 .../devicetree/bindings/net/airoha,an8801.yaml     | 85 ++++++++++++++++++++++
 1 file changed, 85 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/airoha,an8801.yaml b/Documentation/devicetree/bindings/net/airoha,an8801.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..b90b21b5505367309b5df8ece54ea38664f6b50f
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/airoha,an8801.yaml
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/airoha,an8801.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Airoha AN8801R Series PHY
+
+maintainers:
+  - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+description:
+  The Airoha AN8801R is a low power single-port Ethernet PHY Transceiver
+  with Single-port serdes interface for 1000Base-X/RGMII; this chip is
+  compliant with 10Base-T, 100Base-TX and 1000Base-T IEEE 802.3(u,ab)
+  and supports Energy Efficient Ethernet (802.3az), Full Duplex Control
+  Flow (802.3x), auto-negotiation, crossover detect and autocorrection,
+  Wake-on-LAN with Magic Packet, and Jumbo Frame up to 9 Kilobytes.
+  This PHY also supports up to three user-configurable LEDs, which are
+  usually used for LAN Activity, 100M, 1000M indication.
+
+allOf:
+  - $ref: ethernet-phy.yaml#
+
+properties:
+  compatible:
+    enum:
+      - ethernet-phy-idc0ff.0421
+
+  reg:
+    maxItems: 1
+
+  leds: true
+
+  wakeup-source:
+    $ref: /schemas/types.yaml#/definitions/flag
+    description:
+      Enable Wake-on-LAN support
+
+required:
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/leds/common.h>
+
+    mdio {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        ethernet-phy@0 {
+            compatible = "ethernet-phy-idc0ff.0421";
+            reg = <0>;
+
+            leds {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                led@0 {
+                    reg = <0>;
+                    color = <LED_COLOR_ID_GREEN>;
+                    function = LED_FUNCTION_LAN;
+                    default-state = "keep";
+                };
+
+                led@1 {
+                    reg = <1>;
+                    color = <LED_COLOR_ID_GREEN>;
+                    function = LED_FUNCTION_LAN;
+                    function-enumerator = <1>;
+                    default-state = "keep";
+                };
+
+                led@2 {
+                    reg = <2>;
+                    color = <LED_COLOR_ID_YELLOW>;
+                    function = LED_FUNCTION_LAN;
+                    function-enumerator = <2>;
+                    default-state = "keep";
+                };
+            };
+        };
+    };

-- 
2.53.0


^ permalink raw reply related

* [PATCH net-next v2 0/4] Introduce Airoha AN8801R series Gigabit Ethernet PHY driver
From: Louis-Alexis Eyraud @ 2026-03-26 12:04 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	AngeloGioacchino Del Regno, Andrew Lunn, Heiner Kallweit,
	Russell King
  Cc: kevin-kw.huang, macpaul.lin, matthias.bgg, kernel, netdev,
	devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
	Louis-Alexis Eyraud

This series introduces the Airoha AN8801R Gigabit Ethernet PHY initial
support.

The Airoha AN8801R is a low power single-port Ethernet PHY Transceiver
with Single-port serdes interface for 1000Base-X/RGMII.
This chip is compliant with 10Base-T, 100Base-TX and 1000Base-T IEEE
802.3(u,ab) and supports:
  - Energy Efficient Ethernet (802.3az)
  - Full Duplex Control Flow (802.3x)
  - auto-negotiation
  - crossover detect and autocorrection,
  - Wake-on-LAN with Magic Packet
  - Jumbo Frame up to 9 Kilobytes.
This PHY also supports up to three user-configurable LEDs, which are
usually used for LAN Activity, 100M, 1000M indication.

The series provides the devicetree binding and the driver that have been
written by AngeloGioacchino Del Regno, based on downstream
implementation ([1]). The driver allows setting up PHY LEDs, 10/100M,
1000M speeds, and Wake on LAN and PHY interrupts.

Since v2, the series also adds the air_phy_lib library, which goal is to
share common code between air_en8811h and air_an8801 drivers, and its use
in them. The first shared functions are the existing BuckPbus register
accessors and air_phy_read/write_page functions coming from air_en8811h
driver.

The series is based on net-next kernel tree (sha1: d1e59a4697371) and
I have tested it on Mediatek Genio 720-EVK board (that integrates an
Airoha AN8801RIN/A Ethernet PHY) with early board hardware enablement
patches.

[1]: https://gitlab.com/mediatek/aiot/bsp/linux/-/blob/mtk-v6.6/drivers/net/phy/an8801.c

Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
Changes in v2:
- Rebased on net-next (d1e59a4697371)
- Fixed dt-bindings to remove the leds property from the required ones and
  add wakeup-source as valid property
- Added new reviewed-by trailer for dt_bindings
- Added new patches (2 and 3) to create air_phy_lib, to share common code
  between air_en8811h and air_an8801 drivers and use it in air_en8811h.
- Remove custom BuckPBus register accessor functions and definitions from
  air_an8801 and use the ones from air_phy_lib. It also fixes a build
  issue on v1 due to an uninitialized variable used in
  __air_buckpbus_reg_read, that is now removed from driver code
- Added air_an8801_probe function to allocate the newly added private
  data structure and detect if the PHY is wakeup capable and the interrupt
  can be registered as a wakeup IRQ, and perform the needed actions
- Added an8801r_suspend and an8801r_resume functions to perform specific
  actions when WoL is enabled (reset its status, enable/disable the Link
  Changed interrupt) and call the genphy_suspend/resume functions if
  needed
- Modified an8801r_get_wol to return WoL is not supported if the PHY
  device is not wakeup capable
- Modified an8801r_set_wol to return EOPNOTSUPP error code if the PHY
  device is not wakeup capable, and to update the wakeup flag according
  to WoL mode
- Modified an8801r_config_init to remove EEE disabling and replace
  __phy_write use by phy_write_paged
- Reworked an8801r_rgmii_delay_config and its subfunctions to fix a
  double return use in PHY_INTERFACE_MODE_RGMII_ID case, replace the
  magic value use for default TX and RX delay and handle better the
  enable/disable the inserted delays for all RGMII modes
- Merged an8801r_did_interrupt function in an8801r_handle_interrupt
- Modified the an8801r_handle_interrupt processing to process differently
  the Magic Packet (to notify system wakeup) and the Link Changed
  interrupt (to notify PHY state machine)
- Splitted the reset WoL status part from an8801r_ack_interrupt and fix
  an issue that in some random cases made WAKEUP_CTL1 register lose the
  Magic Packet WoL settings
- Modified an8801r_of_init_leds function so it does not return an error
  if the leds configuration is not present in devicetree
- Removed feature field and add PHY_ALWAYS_CALL_SUSPEND flag in
  airoha_driver data structure

- Link to v1: https://lore.kernel.org/r/20260304-add-airoha-an8801-support-v1-0-0ae4ee5a2f9d@collabora.com

---
AngeloGioacchino Del Regno (2):
      dt-bindings: net: Add support for Airoha AN8801/R GbE PHY
      net: phy: Introduce Airoha AN8801/R Gigabit Ethernet PHY driver

Louis-Alexis Eyraud (2):
      net: phy: Add Airoha phy library for shared code
      net: phy: air_phy_lib: Factorize BuckPBus register accessors

 .../devicetree/bindings/net/airoha,an8801.yaml     |   85 ++
 drivers/net/phy/Kconfig                            |   12 +
 drivers/net/phy/Makefile                           |    2 +
 drivers/net/phy/air_an8801.c                       | 1115 ++++++++++++++++++++
 drivers/net/phy/air_en8811h.c                      |  316 +-----
 drivers/net/phy/air_phy_lib.c                      |  210 ++++
 drivers/net/phy/air_phy_lib.h                      |   37 +
 7 files changed, 1519 insertions(+), 258 deletions(-)
---
base-commit: d1e59a46973719e458bec78d00dd767d7a7ba71f
change-id: 20260303-add-airoha-an8801-support-57d544a4afed

Best regards,
-- 
Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>


^ permalink raw reply

* Re: [PATCH v7 4/7] mfd: p3h2x4x: Add driver for NXP P3H2x4x i3c hub and on-die regulator
From: Lee Jones @ 2026-03-26 11:58 UTC (permalink / raw)
  To: Lakshay Piplani
  Cc: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
	conor+dt, devicetree, broonie, Frank.Li, lgirdwood, vikash.bansal,
	priyanka.jain, aman.kumarpandey
In-Reply-To: <20260319112441.3888957-5-lakshay.piplani@nxp.com>

On Thu, 19 Mar 2026, Lakshay Piplani wrote:

> From: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> 
> Add core MFD support for the NXP P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841)
> family of multiport I3C hub devices. These devices connect to a host via
> I3C/I2C/SMBus and expose multiple downstream target ports.
> 
> Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
> Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
> 
> ---
> Changes in v7:
>  - Use new config I3C_OR_I2C
> 
> Changes in v6:
>  - No change
> 
> Changes in v5:
>  - Corrected the ordering in the Makefile and Kconfig for MFD_P3H2X4X
>  - Updated dev_err_probe() for regmap_init failure.
>  - Updated module description
> 
> Changes in v4:
>  - Split the driver into three separate patches(mfd, regulator and I3C hub)
>  - Added support for NXP P3H2x4x MFD functionality
> ---
> ---

One is enough.

>  MAINTAINERS                 |   2 +
>  drivers/mfd/Kconfig         |  13 ++++
>  drivers/mfd/Makefile        |   1 +
>  drivers/mfd/p3h2840.c       | 125 ++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/p3h2840.h |  27 ++++++++
>  5 files changed, 168 insertions(+)
>  create mode 100644 drivers/mfd/p3h2840.c
>  create mode 100644 include/linux/mfd/p3h2840.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c72976dbdf31..4f67fe64c833 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19101,6 +19101,8 @@ L:	linux-kernel@vger.kernel.org
>  L:	linux-i3c-owner@lists.infradead.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
> +F:	drivers/mfd/p3h2840.c
> +F:	include/linux/mfd/p3h2840.h
>  
>  NXP PF5300/PF5301/PF5302 PMIC REGULATOR DEVICE DRIVER
>  M:	Woodrow Douglass <wdouglass@carnegierobotics.com>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 7192c9d1d268..14f4736683a1 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -617,6 +617,19 @@ config MFD_MX25_TSADC
>  	  i.MX25 processors. They consist of a conversion queue for general
>  	  purpose ADC and a queue for Touchscreens.
>  
> +config MFD_P3H2X4X

MFD_NXP_P3H2X4X

> +       tristate "NXP P3H2X4X I3C Hub Device"
> +       depends on I3C_OR_I2C
> +       select MFD_CORE
> +       select REGMAP_I3C
> +       select REGMAP_I2C
> +       help
> +         Enable Support for NXP P3H244x/P3H284x I3C HUB device using I3C/I2C
> +         communication interface.
> +
> +         This driver provides support for I3C hub and regulator, each subdriver
> +         can be enabled independently depending on the required functionality.
> +
>  config MFD_PF1550
>  	tristate "NXP PF1550 PMIC Support"
>  	depends on I2C=y && OF
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index e75e8045c28a..aaadf50fedf4 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -122,6 +122,7 @@ obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
>  obj-$(CONFIG_MFD_MC13XXX_SPI)	+= mc13xxx-spi.o
>  obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
>  
> +obj-$(CONFIG_MFD_P3H2X4X)  	+= p3h2840.o

There is a mismatch in the naming here.

Are we expressing the family or the specific device?

>  obj-$(CONFIG_MFD_PF1550)	+= pf1550.o
>  
>  obj-$(CONFIG_MFD_NCT6694)	+= nct6694.o
> diff --git a/drivers/mfd/p3h2840.c b/drivers/mfd/p3h2840.c
> new file mode 100644
> index 000000000000..65090a09f7c6
> --- /dev/null
> +++ b/drivers/mfd/p3h2840.c
> @@ -0,0 +1,125 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 NXP

Needs updating.

Author(s)?

> + * P3H2x4x i3c hub and regulator device.
> + */

Could this comment be a little more descriptive? Something like:

"Core driver for the NXP P3H2x4x family of I3C hubs."

Drop the regular part.

> +#include <linux/i3c/master.h>
> +#include <linux/i2c.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/p3h2840.h>
> +#include <linux/regmap.h>

Please sort your `#include` directives alphabetically.

> +static const struct mfd_cell p3h2x4x_devs[] = {
> +	{
> +		.name = "p3h2x4x-regulator",
> +	},
> +	{
> +		.name = "p3h2x4x-i3c-hub",
> +	},
> +};

MFD_CELL_NAME()

> +static const struct regmap_config p3h2x4x_regmap_config = {
> +	.reg_bits = P3H2x4x_REG_BITS,
> +	.val_bits = P3H2x4x_VAL_BITS,
> +	.max_register = 0xFF,
> +};
> +
> +static int p3h2x4x_device_probe_i3c(struct i3c_device *i3cdev)
> +{
> +	struct p3h2x4x_dev *p3h2x4x;
> +	int ret;
> +
> +	p3h2x4x = devm_kzalloc(&i3cdev->dev, sizeof(*p3h2x4x), GFP_KERNEL);
> +	if (!p3h2x4x)
> +		return -ENOMEM;
> +
> +	i3cdev_set_drvdata(i3cdev, p3h2x4x);

No point in setting this until after its populated.

> +	p3h2x4x->regmap = devm_regmap_init_i3c(i3cdev, &p3h2x4x_regmap_config);
> +	if (IS_ERR(p3h2x4x->regmap))
> +		return dev_err_probe(&i3cdev->dev, PTR_ERR(p3h2x4x->regmap),
> +				     "Failed to register I3C HUB regmap\n");

This error message appears to be a copy-paste from the I2C probe function.
Perhaps a more generic message like "Failed to initialise regmap" would be
more appropriate for both contexts.

> +
> +	p3h2x4x->is_p3h2x4x_in_i3c = true;

This is superfluous.  Just check `p3h2x4x->i3cdev`.

> +	p3h2x4x->i3cdev = i3cdev;
> +
> +	ret = devm_mfd_add_devices(&i3cdev->dev, PLATFORM_DEVID_NONE,
> +				   p3h2x4x_devs, ARRAY_SIZE(p3h2x4x_devs),
> +				   NULL, 0, NULL);
> +	if (ret)
> +		return dev_err_probe(&i3cdev->dev, ret, "Failed to add sub devices\n");
> +
> +	return 0;
> +}
> +
> +static int p3h2x4x_device_probe_i2c(struct i2c_client *client)
> +{
> +	struct p3h2x4x_dev *p3h2x4x;
> +	int ret;
> +
> +	p3h2x4x = devm_kzalloc(&client->dev, sizeof(*p3h2x4x), GFP_KERNEL);
> +	if (!p3h2x4x)
> +		return -ENOMEM;
> +
> +	i2c_set_clientdata(client, p3h2x4x);

As above.

> +	p3h2x4x->regmap = devm_regmap_init_i2c(client, &p3h2x4x_regmap_config);
> +	if (IS_ERR(p3h2x4x->regmap))
> +		return dev_err_probe(&client->dev, PTR_ERR(p3h2x4x->regmap),
> +				     "Failed to register I3C HUB regmap\n");

I2C

> +
> +	p3h2x4x->is_p3h2x4x_in_i3c = false;

As above.

> +	ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
> +				   p3h2x4x_devs, ARRAY_SIZE(p3h2x4x_devs),
> +				   NULL, 0, NULL);
> +	if (ret)
> +		return dev_err_probe(&client->dev, ret, "Failed to add sub devices\n");
> +
> +	return 0;
> +}

These two probe functions, `p3h2x4x_device_probe_i3c` and
`p3h2x4x_device_probe_i2c`, are almost identical. Could they be refactored
into a single common probe function to reduce code duplication?

The bus-specific wrappers could handle the regmap initialisation and then
call a shared `p3h2x4x_probe(struct device *dev, struct regmap *regmap)`
function.

> +
> +/* p3h2x4x ids (i3c) */
> +static const struct i3c_device_id p3h2x4x_i3c_ids[] = {
> +	I3C_CLASS(I3C_DCR_HUB, NULL),
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(i3c, p3h2x4x_i3c_ids);
> +
> +/* p3h2x4x ids (i2c) */
> +static const struct i2c_device_id p3h2x4x_i2c_id_table[] = {
> +	{ "nxp-i3c-hub" },

This is an MFD core driver, not just a hub driver. Perhaps a more suitable
name would be "p3h2x4x"?

> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(i2c, p3h2x4x_i2c_id_table);
> +
> +static const struct of_device_id  p3h2x4x_i2c_of_match[] = {
> +	{ .compatible = "nxp,p3h2840", },
> +	{ /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, p3h2x4x_i2c_of_match);

This should be adjacent to the struct above.

> +static struct i3c_driver p3h2x4x_i3c = {
> +	.driver = {
> +		.name = "p3h2x4x_i3c_drv",

"_drv" should be removed.

> +	},
> +	.probe = p3h2x4x_device_probe_i3c,
> +	.id_table = p3h2x4x_i3c_ids,
> +};
> +
> +static struct i2c_driver p3h2x4x_i2c = {
> +	.driver = {
> +		.name = "p3h2x4x_i2c_drv",

As above.

> +		.of_match_table = p3h2x4x_i2c_of_match,
> +	},
> +	.probe =  p3h2x4x_device_probe_i2c,
> +	.id_table = p3h2x4x_i2c_id_table,
> +};
> +
> +module_i3c_i2c_driver(p3h2x4x_i3c, &p3h2x4x_i2c);
> +
> +MODULE_AUTHOR("Aman Kumar Pandey <aman.kumarpandey@nxp.com>");
> +MODULE_AUTHOR("Vikash Bansal <vikash.bansal@nxp.com>");

Stick to having one of these and put two in the header comment.

> +MODULE_DESCRIPTION("P3H2x4x I3C HUB multi function driver");

This could be more precise. How about:

"NXP P3H2x4x I3C Hub core driver"

Drop all mentions of MFD.  It's not a real thing.

> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/p3h2840.h b/include/linux/mfd/p3h2840.h
> new file mode 100644
> index 000000000000..cba6fa516d1e
> --- /dev/null
> +++ b/include/linux/mfd/p3h2840.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright 2025 NXP
> + * This header file contain private Reg address and its bit mapping etc.
> + */

The comment is not a complete sentence and states the obvious. Please consider
something more descriptive, for example:

"Register definitions for the NXP P3H2x4x I3C Hub."

> +
> +#ifndef _LINUX_MFD_P3H2840_H
> +#define _LINUX_MFD_P3H2840_H

If the filenames are changed to `p3h2x4x.h` as suggested earlier, this
header guard will need to be updated to `_LINUX_MFD_P3H2X4X_H`.

> +
> +#include <linux/types.h>
> +
> +/* Device Configuration Registers */
> +#define P3H2x4x_DEV_REG_PROTECTION_CODE				0x10
> +#define P3H2x4x_REGISTERS_LOCK_CODE				0x00
> +#define P3H2x4x_REGISTERS_UNLOCK_CODE				0x69
> +#define P3H2x4x_CP1_REGISTERS_UNLOCK_CODE			0x6a
> +
> +/* Reg config for Regmap */
> +#define P3H2x4x_REG_BITS					8
> +#define P3H2x4x_VAL_BITS					8
> +
> +struct p3h2x4x_dev {
> +	struct i3c_device *i3cdev;
> +	struct regmap *regmap;
> +	bool is_p3h2x4x_in_i3c;
> +};

Why are we storing i3c_device, but not i2c_device_id?

-- 
Lee Jones [李琼斯]

^ permalink raw reply

* Re: [PATCH v1 1/2] dt-bindings: i2c: ls2x-i2c: Add clock- related properties
From: Hongliang Wang @ 2026-03-26 11:48 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Binbin Zhou, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-i2c, devicetree, loongarch
In-Reply-To: <900dc1a4-66ab-411f-8a32-4c6cf339e8ec@kernel.org>

Hi Krzysztof and Andi,

Sorry, I didn't express myself clearly. The purpose of defining clock-input and
clock-div properties is to use them in both acpi and dts. If these properties
cannot be used in dts, can I first use them in acpi? and second, use clock
framework in dts.
if these properties can be used in acpi, use clocks property describes the
clock input may be appropriate.

the [PATCH v2] i2c: ls2x: Add clocks and clock-div properties parsing for ACPI
as follows:

 From 40bdf5588594d2b51f50682fb841c6cbe87c4297 Mon Sep 17 00:00:00 2001
From: wanghongliang <wanghongliang@loongson.cn>
Date: Sun, 12 Apr 2026 03:13:09 +0800
Subject: [PATCH v2] i2c: ls2x: Add clocks and clock-div properties parsing for
  ACPI

On 3A/7A/2K3000 platform, the input clock of i2c controller
and the divisor of input clock maybe different, so define
the clocks and clock-div properties in Device (I2Cx) in acpi
parameter passing. clocks describes the input clock of i2c
controller and clock-div describes the clock divisor of the
input clock.

  Device (I2C1)
  {
    Name (_HID, "LOON0004" /* AT Real-Time Clock */)  // _HID: Hardware ID
    Name (_UID, 0x1)  // _UID: Unique ID
    Name (_DSD, Package (0x02)  // _DSD: Device-Specific Data
    {
      ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301")
      Package (0x03)
      {
        Package (0x02)
        {
          "clock-frequency",
          100000
        }
        Package (0x02)
        {
          "clocks",
          100000000
        }
        Package (0x02)
        {
          "clock-div",
          40
        }
      }
    })

The formulas is
Prcescale = (clock-input * 10) / (clock-div * t->bus_freq_hz) - 1

The value of clocks and clock-div on different platforms are
listed below:

1.7A1000/7A2000:
clock-input = 80MHZ
clock-div = 50

2.3A5000/3C5000/3A6000/3C6000:
clock-input = 100MHZ
clock-div = 40

3.2K3000:
clock-input = 100MHZ
clock-div = 55

Signed-off-by: wanghongliang <wanghongliang@loongson.cn>
---
  drivers/i2c/busses/i2c-ls2x.c | 13 ++++++++++++-
  1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-ls2x.c b/drivers/i2c/busses/i2c-ls2x.c
index b475dd27b7af..4d1d461d065a 100644
--- a/drivers/i2c/busses/i2c-ls2x.c
+++ b/drivers/i2c/busses/i2c-ls2x.c
@@ -96,6 +96,7 @@ static irqreturn_t ls2x_i2c_isr(int this_irq, void *dev_id)
  static void ls2x_i2c_adjust_bus_speed(struct ls2x_i2c_priv *priv)
  {
  	u16 val;
+	u32 pclk, div;
  	struct i2c_timings *t = &priv->i2c_t;
  	struct device *dev = priv->adapter.dev.parent;
  	u32 acpi_speed = i2c_acpi_find_bus_speed(dev);
@@ -107,12 +108,22 @@ static void ls2x_i2c_adjust_bus_speed(struct ls2x_i2c_priv *priv)
  	else
  		t->bus_freq_hz = LS2X_I2C_FREQ_STD;
  
+	if (has_acpi_companion(dev)) {
+		if (!device_property_read_u32(dev, "clock-input", &pclk) &&
+		    !device_property_read_u32(dev, "clock-div", &div) &&
+		    div != 0)
+			val = (pclk * 10) / (div * t->bus_freq_hz) - 1;
+		else
+			val = LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1;
+	} else {
+		val = LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1;
+	}
+
  	/*
  	 * According to the chip manual, we can only access the registers as bytes,
  	 * otherwise the high bits will be truncated.
  	 * So set the I2C frequency with a sequential writeb() instead of writew().
  	 */
-	val = LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1;
  	writeb(FIELD_GET(GENMASK(7, 0), val), priv->base + I2C_LS2X_PRER_LO);
  	writeb(FIELD_GET(GENMASK(15, 8), val), priv->base + I2C_LS2X_PRER_HI);
  }
-- 
2.47.2

Best regards,
Hongliang Wang

On 2026/3/26 下午3:49, Krzysztof Kozlowski wrote:
> On 26/03/2026 08:02, Krzysztof Kozlowski wrote:
>> On 26/03/2026 03:12, Hongliang Wang wrote:
>>> Hi Krzysztof,
>>>
>>> Consider the clock framework relies on the device tree, and can only be
>>> used on
>>> Loongson 2K platform with dts parameter mechanism, It cannot be used on
>>> Loongson
>> Don't top post or request read receipts.
>>
>>> 3A+7A platform with the acpi parameter mechanism.
>> And this patch is for ACPI? Then we finish discussion here, because
>> dt-bindings is not for ACPI.
>>
>>> The i2c-ls2x driver is compatible with both Loongson 2K and 3A+7A
>>> platform, parse
>>> the same parameters regardless of dts or acpi parameter passing, So
>>> clock-input
>>> and clock-div attributes are defined to describe input clock of i2c
>>> controller and
>>> divisor of input clock. It can be used on both 2K and 3A+7A platform.
>> And you cannot use them in DTS.
> I need to keep guessing what you want to achieve, because neither your
> message nor commit text was explicit - if you need properties for ACPI
> and you want to be sure that DTS does not have them, then you could
> define them as "foo:false" with a comment why (you always explain WHY
> you are doing things). We don't have such convention so far, but I think
> it will be useful when Rob finishes the ABI checker.
>
> *Otherwise* minimum would be a comment in the driver that these are not
> allowed in DTS.
>
> Best regards,
> Krzysztof


^ permalink raw reply related

* Re: [PATCH v6 2/3] dt-bindings: arm: Add Samsung Galaxy Book4 Edge
From: Krzysztof Kozlowski @ 2026-03-26 11:44 UTC (permalink / raw)
  To: Maxim Storetvedt, andersson, robh, krzk+dt, conor+dt
  Cc: marcus, marijn.suijten, linux-arm-msm, devicetree, linux-kernel,
	abel.vesa, abel.vesa, johan, konradybcio, kirill
In-Reply-To: <20260322160317.424797-3-mstoretv@cern.ch>

On 22/03/2026 17:03, Maxim Storetvedt wrote:
> From: Marcus Glocker <marcus@nazgul.ch>
> 
> Add the Samsung Galaxy Book4 Edge compatibility binding.
> 
> Signed-off-by: Marcus Glocker <marcus@nazgul.ch>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>

Incomplete DCO. Please read submitting patches about certification you
have to make.

Do not attach (thread) your patchsets to some other threads (unrelated
or older versions). This buries them deep in the mailbox and might
interfere with applying entire sets. See also:
https://elixir.bootlin.com/linux/v6.16-rc2/source/Documentation/process/submitting-patches.rst#L830

Please organize the patch documenting the compatible (DT bindings)
before the patch using that compatible.
See also:
https://elixir.bootlin.com/linux/v6.14-rc6/source/Documentation/devicetree/bindings/submitting-patches.rst#L46

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH v6 1/4] dt-bindings: usb: Add binding for Genesys Logic GL3590 hub
From: Swati Agarwal @ 2026-03-26 11:42 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Greg Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Icenowy Zheng, Matthias Kaehlcke, Mike Looijmans,
	Pin-yen Lin, Chaoyi Chen, J . Neuschäfer, Jens Glathe,
	Bjorn Andersson, Konrad Dybcio, linux-usb, devicetree,
	linux-kernel, linux-arm-msm
In-Reply-To: <0e88f413-82c4-4986-9d9c-122e0b0377b1@kernel.org>

On Thu, Mar 26, 2026 at 3:39 PM Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
> On 26/03/2026 11:07, Swati Agarwal wrote:
> > https://lore.kernel.org/all/20260121022449.GA1804139-robh@kernel.org/#t
>
> Exactly this comment is not implemented. v5 repeated it. I repeated now.
Hi Krysztof,
Are you trying to say, i should mark vdd12-supply false wherever
applicable but dont mark it as true and it should be by default
considered as true? Correct me if my understanding is wrong.

Regards,
Swati

^ permalink raw reply

* Re: [PATCH v2 2/2] arm64: dts: qcom: sdm845-shift-axolotl: Enable NFC
From: Konrad Dybcio @ 2026-03-26 11:39 UTC (permalink / raw)
  To: David Heidelberg, Konrad Dybcio, Alexander Martinz
  Cc: Petr Hodina, biemster, netdev, linux-arm-msm, Rob Herring,
	oe-linux-nfc, Krzysztof Kozlowski, devicetree, Conor Dooley,
	linux-kernel, phone-devel, Bjorn Andersson
In-Reply-To: <6638c874-8472-4383-bf83-21c5b632e73e@ixit.cz>

On 3/25/26 9:17 PM, David Heidelberg wrote:
> On 25/03/2026 12:20, Konrad Dybcio wrote:
>> On 3/24/26 7:08 PM, David Heidelberg wrote:
>>>
>>>
>>> On 24/03/2026 14:12, Konrad Dybcio wrote:
>>>> On 3/24/26 12:20 AM, David Heidelberg via B4 Relay wrote:
>>>>> From: David Heidelberg <david@ixit.cz>
>>>>>
>>>>> Definition of the NFC.
>>>>
>>>> "meh" commit message
>>>>
>>>> [...]
>>>>
>>>>> +    nfc_enable_default: nfc-enable-default-state {
>>>>> +        pins = "gpio12", "gpio62";
>>>>> +        function = "gpio";
>>>>> +        drive-strength = <2>;
>>>>> +        bias-pull-up;
> 
>               bias-disable;
>>>>
>>>> Are you sure about pulling up an active-high pin?
>>>
>>> I'm not sure, but downstream does it (and "works for me"). Maybe Alexander would know more details here.
>>
>> Would changing it to bias-disable also "work for you"?
> 
> Yeah, works for me. Should OnePlus 6 do the same?
> 
> Looking at OP6 datasheet, there is no pull-up/down on 12,62,nor IRQ 63.

Generally the internal bias would be used to counteract random noise posing
as signal, to ensure the line is kept in the "inactive" state when not
actively driven

I can see that the driver initially requests both to LOW and then sets it to
high based on the desired mode in nxp_nci_i2c_set_mode(), so pulling up is
perhaps never really desired.

Konrad

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox