summaryrefslogtreecommitdiff
path: root/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/Kconfig26
-rw-r--r--drivers/net/phy/adin.c13
-rw-r--r--drivers/net/phy/airoha/Kconfig14
-rw-r--r--drivers/net/phy/airoha/Makefile2
-rw-r--r--drivers/net/phy/airoha/air_an8801.c594
-rw-r--r--drivers/net/phy/airoha/air_en8811.c357
-rw-r--r--drivers/net/phy/airoha/air_phy_lib.c216
-rw-r--r--drivers/net/phy/airoha/air_phy_lib.h39
-rw-r--r--drivers/net/phy/aquantia.c2
-rw-r--r--drivers/net/phy/dp83867.c16
-rw-r--r--drivers/net/phy/mscc.c11
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx.c2
12 files changed, 1015 insertions, 277 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 5d2277a4602..ee438524490 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -7,7 +7,7 @@ config MV88E6352_SWITCH
menuconfig PHYLIB
bool "Ethernet PHY (physical media interface) support"
- depends on NET || NET_LWIP
+ depends on NET
help
Enable Ethernet PHY (physical media interface) support.
@@ -81,7 +81,7 @@ config PHYLIB_10G
config PHY_ADIN
bool "Analog Devices Industrial Ethernet PHYs"
help
- Add support for configuring RGMII on Analog Devices ADIN PHYs.
+ Add support for configuring RGMII on Analog Devices ADIN PHYs.
menuconfig PHY_AQUANTIA
bool "Aquantia Ethernet PHYs support"
@@ -126,9 +126,9 @@ config SYS_CORTINA_NO_FW_UPLOAD
bool "Cortina firmware loading support"
depends on PHY_CORTINA
help
- Cortina phy has provision to store phy firmware in attached dedicated
- EEPROM. And boards designed with such EEPROM does not require firmware
- upload.
+ Cortina phy has provision to store phy firmware in attached dedicated
+ EEPROM. And boards designed with such EEPROM does not require firmware
+ upload.
choice
prompt "Location of the Cortina firmware"
@@ -167,7 +167,7 @@ config PHY_CORTINA_ACCESS
default y
depends on CORTINA_NI_ENET
help
- Cortina Access Ethernet PHYs init process
+ Cortina Access Ethernet PHYs init process
config PHY_DAVICOM
bool "Davicom Ethernet PHYs support"
@@ -233,7 +233,7 @@ config PHY_MICREL_KSZ8XXX
endif # PHY_MICREL
config PHY_MOTORCOMM
- tristate "Motorcomm PHYs"
+ bool "Motorcomm PHYs"
help
Enables support for Motorcomm network PHYs.
Currently supports the YT8511 and YT8531 Gigabit Ethernet PHYs.
@@ -246,7 +246,7 @@ config PHY_NATSEMI
bool "National Semiconductor Ethernet PHYs support"
config PHY_NXP_C45_TJA11XX
- tristate "NXP C45 TJA11XX PHYs"
+ bool "NXP C45 TJA11XX PHYs"
select DEVRES
help
Enable support for NXP C45 TJA11XX PHYs.
@@ -317,13 +317,13 @@ config PHY_TERANETICS
config PHY_TI
bool "Texas Instruments Ethernet PHYs support"
- ---help---
+ help
Adds PHY registration support for TI PHYs.
config PHY_TI_DP83867
select PHY_TI
bool "Texas Instruments Ethernet DP83867 PHY support"
- ---help---
+ help
Adds support for the TI DP83867 1Gbit PHY.
config SPL_PHY_TI_DP83867
@@ -333,13 +333,13 @@ config SPL_PHY_TI_DP83867
config PHY_TI_DP83869
select PHY_TI
bool "Texas Instruments Ethernet DP83869 PHY support"
- ---help---
+ help
Adds support for the TI DP83869 1Gbit PHY.
config PHY_TI_GENERIC
select PHY_TI
bool "Texas Instruments Generic Ethernet PHYs support"
- ---help---
+ help
Adds support for Generic TI PHYs that don't need special handling but
the PHY name is associated with a PHY ID.
@@ -381,7 +381,7 @@ config PHY_FIXED
config PHY_NCSI
bool "NC-SI based PHY"
- depends on NET
+ depends on NET_LEGACY
endif #PHYLIB
diff --git a/drivers/net/phy/adin.c b/drivers/net/phy/adin.c
index ce448810ff6..4d42e56dada 100644
--- a/drivers/net/phy/adin.c
+++ b/drivers/net/phy/adin.c
@@ -10,6 +10,7 @@
#include <linux/bitops.h>
#include <linux/bitfield.h>
+#define PHY_ID_ADIN1200 0x0283bc20
#define PHY_ID_ADIN1300 0x0283bc30
#define ADIN1300_EXT_REG_PTR 0x10
#define ADIN1300_EXT_REG_DATA 0x11
@@ -263,6 +264,18 @@ static int adin1300_config(struct phy_device *phydev)
return genphy_config(phydev);
}
+U_BOOT_PHY_DRIVER(ADIN1200) = {
+ .name = "ADIN1200",
+ .uid = PHY_ID_ADIN1200,
+ .mask = 0xffffffff,
+ .features = PHY_BASIC_FEATURES,
+ .config = adin1300_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+ .readext = adin_extread,
+ .writeext = adin_extwrite,
+};
+
U_BOOT_PHY_DRIVER(ADIN1300) = {
.name = "ADIN1300",
.uid = PHY_ID_ADIN1300,
diff --git a/drivers/net/phy/airoha/Kconfig b/drivers/net/phy/airoha/Kconfig
index da8747939e3..2d58d674200 100644
--- a/drivers/net/phy/airoha/Kconfig
+++ b/drivers/net/phy/airoha/Kconfig
@@ -2,11 +2,25 @@
menuconfig PHY_AIROHA
bool "Airoha Ethernet PHYs support"
+config PHY_AIROHA_AN8801
+ bool "Airoha Ethernet AN8801 support"
+ depends on PHY_AIROHA
+ select PHY_AIROHA_PHYLIB
+ help
+ Currently support AIROHA AN8801 1G PHY.
+
config PHY_AIROHA_EN8811
bool "Airoha Ethernet EN8811H support"
depends on PHY_AIROHA
depends on SUPPORTS_FW_LOADER
select FW_LOADER
+ select PHY_AIROHA_PHYLIB
+ select PHY_COMMON_PROPS
help
AIROHA EN8811H supported.
AIROHA AN8811HB supported.
+
+config PHY_AIROHA_PHYLIB
+ bool
+ help
+ Airoha Ethernet PHY common library
diff --git a/drivers/net/phy/airoha/Makefile b/drivers/net/phy/airoha/Makefile
index 84d23b19ab0..25e44004cfd 100644
--- a/drivers/net/phy/airoha/Makefile
+++ b/drivers/net/phy/airoha/Makefile
@@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PHY_AIROHA_AN8801) += air_an8801.o
obj-$(CONFIG_PHY_AIROHA_EN8811) += air_en8811.o
+obj-$(CONFIG_PHY_AIROHA_PHYLIB) += air_phy_lib.o
diff --git a/drivers/net/phy/airoha/air_an8801.c b/drivers/net/phy/airoha/air_an8801.c
new file mode 100644
index 00000000000..9d9958fc665
--- /dev/null
+++ b/drivers/net/phy/airoha/air_an8801.c
@@ -0,0 +1,594 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * air_an8801.c - PHY driver for Airoha AN8801.
+ * Copyright (c) 2026 Airoha Technology Corp.
+ * Copyright (C) 2026 BayLibre, SAS.
+ * Author: Kevin-KW Huang <[email protected]>
+ * Sita Huang <[email protected]>
+ * Julien Stephan <[email protected]>
+ */
+
+#include <malloc.h>
+#include <phy.h>
+#include <dm/device_compat.h>
+
+#include "air_phy_lib.h"
+
+#define AN8801R_PHY_ID1 0xc0ff
+#define AN8801R_PHY_ID2 0x0421
+#define AN8801R_PHY_ID ((u32)((AN8801R_PHY_ID1 << 16) | AN8801R_PHY_ID2))
+
+#define AN8801R_MAX_LED_SIZE 3
+
+/* MII Registers - Airoha Page 4 */
+#define AN8801_PBUS_ACCESS BIT(28)
+
+/* 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_LINK_MODE 0x5054
+#define AN8801_BPBUS_LINK_MODE_1000 BIT(0)
+
+#define AN8801_BPBUS_REG_BYPASS_PTP 0x21c004
+#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 AIR_RGMII_DELAY_NOSTEP 0
+#define AIR_RGMII_DELAY_STEP_1 1
+#define AIR_RGMII_DELAY_STEP_2 2
+#define AIR_RGMII_DELAY_STEP_3 3
+#define AIR_RGMII_DELAY_STEP_4 4
+#define AIR_RGMII_DELAY_STEP_5 5
+#define AIR_RGMII_DELAY_STEP_6 6
+#define AIR_RGMII_DELAY_STEP_7 7
+#define RGMII_TXDELAY_FORCE_MODE BIT(24)
+
+#define AN8801_BPBUS_REG_RXDLY_STEP 0x21c02c
+#define RGMII_RXDELAY_ALIGN BIT(4)
+#define RGMII_RXDELAY_FORCE_MODE BIT(24)
+
+#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_HWRST_DE_GLITCH 0xc8
+#define AN8801_DE_GLITCH_EN BIT(2)
+#define AN8801_11_CYCLE_XTAL_PERIOD_DE_GLITCH GENMASK(1, 0)
+
+#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) (0x024 + ((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) (0x025 + ((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_FORCE BIT(9)
+
+#define UNIT_LED_BLINK_DURATION 780
+#define LED_BLINK_DURATION(f) (UNIT_LED_BLINK_DURATION << (f))
+
+/* Link on(1G/100M/10M), no activity */
+#define AIR_LED0_ON \
+ (LED_ON_EVT_LINK_1000M | LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M)
+#define AIR_LED0_BLINK 0x0
+/* No link on, activity(1G/100M/10M TX/RX) */
+#define AIR_LED1_ON 0x0
+#define AIR_LED1_BLINK \
+ (LED_BLINK_EVT_1000M_TX | LED_BLINK_EVT_1000M_RX | \
+ LED_BLINK_EVT_100M_TX | LED_BLINK_EVT_100M_RX | \
+ LED_BLINK_EVT_10M_TX | LED_BLINK_EVT_10M_RX)
+/* Link on(100M/10M), activity(100M/10M TX/RX) */
+#define AIR_LED2_ON \
+ (LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M)
+#define AIR_LED2_BLINK \
+ (LED_BLINK_EVT_100M_TX | LED_BLINK_EVT_100M_RX | \
+ LED_BLINK_EVT_10M_TX | LED_BLINK_EVT_10M_RX)
+
+#define INVALID_DATA GENMASK(31, 0)
+
+#define AN8801_REG_PHY_INTERNAL0 0x600
+#define AN8801_REG_PHY_INTERNAL1 0x601
+
+#define AN8801_LED_ENABLE 1
+
+enum air_led_gpio_pin {
+ AIR_LED_GPIO1 = 1,
+ AIR_LED_GPIO2,
+ AIR_LED_GPIO3
+};
+
+enum air_led {
+ AIR_LED0 = 0,
+ AIR_LED1,
+ AIR_LED2,
+ AIR_LED3
+};
+
+enum air_led_blink_dut {
+ AIR_LED_BLINK_DUR_32M = 0,
+ AIR_LED_BLINK_DUR_64M,
+ AIR_LED_BLINK_DUR_128M,
+ AIR_LED_BLINK_DUR_256M,
+ AIR_LED_BLINK_DUR_512M,
+ AIR_LED_BLINK_DUR_1024M,
+ AIR_LED_BLINK_DUR_LAST
+};
+
+enum air_led_polarity {
+ AIR_ACTIVE_LOW = 0,
+ AIR_ACTIVE_HIGH,
+};
+
+enum air_led_mode {
+ AIR_LED_MODE_DISABLE = 0,
+ AIR_LED_MODE_USER_DEFINE,
+ AIR_LED_MODE_LAST
+};
+
+struct air_led_cfg {
+ u16 led_en;
+ u16 gpio;
+ u16 led_polarity;
+ u16 led_on_cfg;
+ u16 led_blk_cfg;
+};
+
+struct an8801r_priv {
+ struct air_led_cfg led_cfg[AN8801R_MAX_LED_SIZE];
+ u32 led_blink_cfg;
+ u8 rxdelay_force;
+ u8 txdelay_force;
+ u16 rxdelay_step;
+ u8 rxdelay_align;
+ u16 txdelay_step;
+};
+
+#define phydev_cfg(phy) ((struct an8801r_priv *)(phy)->priv)
+
+/*
+ * GPIO1 <-> LED0,
+ * GPIO2 <-> LED1,
+ * GPIO3 <-> LED2,
+ */
+static const struct an8801r_priv an8801r_priv_defaults = {
+ .led_cfg = {
+ /* LED Enable, GPIO, LED Polarity, LED ON, LED Blink */
+ {AN8801_LED_ENABLE, AIR_LED_GPIO1, AIR_ACTIVE_LOW, AIR_LED0_ON, AIR_LED0_BLINK},
+ {AN8801_LED_ENABLE, AIR_LED_GPIO2, AIR_ACTIVE_HIGH, AIR_LED1_ON, AIR_LED1_BLINK},
+ {AN8801_LED_ENABLE, AIR_LED_GPIO3, AIR_ACTIVE_HIGH, AIR_LED2_ON, AIR_LED2_BLINK},
+ },
+ .led_blink_cfg = AIR_LED_BLINK_DUR_64M,
+ .rxdelay_force = false,
+ .txdelay_force = false,
+ .rxdelay_step = AIR_RGMII_DELAY_NOSTEP,
+ .rxdelay_align = false,
+ .txdelay_step = AIR_RGMII_DELAY_NOSTEP,
+};
+
+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 an8801r_led_set_usr_def(struct phy_device *phydev, u8 entity,
+ u16 polar, u16 on_evt, u16 blk_evt)
+{
+ int ret;
+
+ if (polar == AIR_ACTIVE_HIGH)
+ on_evt |= LED_ON_POL;
+ else
+ on_evt &= ~LED_ON_POL;
+
+ on_evt |= LED_ON_EN;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(entity), on_evt);
+ if (ret)
+ return ret;
+
+ return phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_CTRL(entity), blk_evt);
+}
+
+static int an8801r_led_set_blink(struct phy_device *phydev, u16 blink)
+{
+ int ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_DUR,
+ LED_BLINK_DURATION(blink));
+ if (ret)
+ return ret;
+
+ return phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_ON_DUR,
+ LED_BLINK_DURATION(blink) / 2);
+}
+
+static int an8801r_led_set_mode(struct phy_device *phydev, u8 mode)
+{
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, LED_BCR);
+ if (ret < 0)
+ return ret;
+
+ switch (mode) {
+ case AIR_LED_MODE_DISABLE:
+ ret &= ~LED_BCR_EXT_CTRL;
+ ret &= ~LED_BCR_MODE_MASK;
+ ret |= LED_BCR_MODE_DISABLE;
+ break;
+ case AIR_LED_MODE_USER_DEFINE:
+ ret |= LED_BCR_EXT_CTRL | LED_BCR_CLK_EN;
+ break;
+ }
+ return phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_BCR, ret);
+}
+
+static int an8801r_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
+{
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(entity));
+ if (ret < 0)
+ return ret;
+
+ if (state)
+ ret |= LED_ON_EN;
+ else
+ ret &= ~LED_ON_EN;
+
+ return phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(entity), ret);
+}
+
+static int an8801r_led_init(struct phy_device *phydev)
+{
+ struct an8801r_priv *priv = phydev_cfg(phydev);
+ struct air_led_cfg *led_cfg = priv->led_cfg;
+ u16 led_blink_cfg = priv->led_blink_cfg;
+ int ret, led_id;
+
+ ret = an8801r_led_set_blink(phydev, led_blink_cfg);
+ if (ret)
+ return ret;
+
+ ret = an8801r_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
+ if (ret) {
+ dev_err(phydev->dev, "AN8801R: Fail to set LED mode, ret %d!\n", ret);
+ return ret;
+ }
+
+ for (led_id = AIR_LED0; led_id < AN8801R_MAX_LED_SIZE; led_id++) {
+ ret = an8801r_led_set_state(phydev, led_id, led_cfg[led_id].led_en);
+ if (ret) {
+ dev_err(phydev->dev, "AN8801R: Fail to set LED%d state, ret %d!\n",
+ led_id, ret);
+ return ret;
+ }
+
+ if (!led_cfg[led_id].led_en)
+ continue;
+
+ ret = an8801_buckpbus_reg_set_bits(phydev, AN8801_BPBUS_REG_LED_GPIO,
+ BIT(led_cfg[led_id].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_cfg[led_id].gpio));
+ if (ret)
+ return ret;
+
+ ret = an8801_buckpbus_reg_clear_bits(phydev, AN8801_BPBUS_REG_GPIO_MODE,
+ BIT(led_cfg[led_id].gpio));
+ if (ret)
+ return ret;
+
+ ret = an8801r_led_set_usr_def(phydev, led_id,
+ led_cfg[led_id].led_polarity,
+ led_cfg[led_id].led_on_cfg,
+ led_cfg[led_id].led_blk_cfg);
+ if (ret) {
+ dev_err(phydev->dev, "AN8801R: Fail to set LED%d, ret %d!\n",
+ led_id, ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int an8801r_of_init(struct phy_device *phydev)
+{
+ struct an8801r_priv *priv = phydev_cfg(phydev);
+ ofnode node = phy_get_ofnode(phydev);
+ u32 val = 0;
+ int ret;
+
+ if (!ofnode_valid(node))
+ return -EINVAL;
+
+ if (ofnode_has_property(node, "airoha,rxclk-delay")) {
+ ret = ofnode_read_u32(node, "airoha,rxclk-delay", &val);
+ if (ret) {
+ dev_err(phydev->dev, "airoha,rxclk-delay value is invalid.\n");
+ return ret;
+ }
+ if (val > AIR_RGMII_DELAY_STEP_7) {
+ dev_err(phydev->dev, "airoha,rxclk-delay value %u out of range.\n", val);
+ return -EINVAL;
+ }
+ priv->rxdelay_force = true;
+ priv->rxdelay_step = val;
+ priv->rxdelay_align = ofnode_read_bool(node,
+ "airoha,rxclk-delay-align");
+ }
+
+ if (ofnode_has_property(node, "airoha,txclk-delay")) {
+ ret = ofnode_read_u32(node, "airoha,txclk-delay", &val);
+ if (ret) {
+ dev_err(phydev->dev, "airoha,txclk-delay value is invalid.\n");
+ return ret;
+ }
+ if (val > AIR_RGMII_DELAY_STEP_7) {
+ dev_err(phydev->dev, "airoha,txclk-delay value %u out of range.\n", val);
+ return -EINVAL;
+ }
+ priv->txdelay_force = true;
+ priv->txdelay_step = val;
+ }
+ return 0;
+}
+
+static int an8801r_rgmii_rxdelay(struct phy_device *phydev, u16 delay, u8 align)
+{
+ u32 reg_val = delay & RGMII_DELAY_STEP_MASK;
+ int ret;
+
+ if (align) {
+ reg_val |= RGMII_RXDELAY_ALIGN;
+ debug("AN8801R: Rxdelay align\n");
+ }
+ reg_val |= RGMII_RXDELAY_FORCE_MODE;
+ ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_RXDLY_STEP, reg_val);
+ if (ret)
+ return ret;
+
+ debug("AN8801R: Force rxdelay = %d(0x%x)\n", delay, reg_val);
+ return 0;
+}
+
+static int an8801r_rgmii_txdelay(struct phy_device *phydev, u16 delay)
+{
+ u32 reg_val = delay & RGMII_DELAY_STEP_MASK;
+ int ret;
+
+ reg_val |= RGMII_TXDELAY_FORCE_MODE;
+ ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_TXDLY_STEP, reg_val);
+ if (ret)
+ return ret;
+
+ debug("AN8801R: Force txdelay = %d(0x%x)\n", delay, reg_val);
+ return 0;
+}
+
+static int an8801r_rgmii_delay_config(struct phy_device *phydev)
+{
+ struct an8801r_priv *priv = phydev_cfg(phydev);
+ int ret;
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ return an8801r_rgmii_txdelay(phydev, AIR_RGMII_DELAY_STEP_4);
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ return an8801r_rgmii_rxdelay(phydev, AIR_RGMII_DELAY_NOSTEP, true);
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ ret = an8801r_rgmii_txdelay(phydev, AIR_RGMII_DELAY_STEP_4);
+ if (ret)
+ return ret;
+ return an8801r_rgmii_rxdelay(phydev, AIR_RGMII_DELAY_NOSTEP, true);
+ case PHY_INTERFACE_MODE_RGMII:
+ default:
+ if (priv->rxdelay_force) {
+ ret = an8801r_rgmii_rxdelay(phydev, priv->rxdelay_step,
+ priv->rxdelay_align);
+ if (ret)
+ return ret;
+ }
+ if (priv->txdelay_force)
+ return an8801r_rgmii_txdelay(phydev, priv->txdelay_step);
+ return 0;
+ }
+}
+
+static int an8801r_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = an8801r_of_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_HWRST_DE_GLITCH,
+ AN8801_DE_GLITCH_EN |
+ AN8801_11_CYCLE_XTAL_PERIOD_DE_GLITCH);
+ if (ret)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AN8801_REG_PHY_INTERNAL0, 0x1e);
+ if (ret)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AN8801_REG_PHY_INTERNAL1, 0x02);
+ if (ret)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
+ if (ret)
+ 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 = an8801r_rgmii_delay_config(phydev);
+ if (ret)
+ return ret;
+
+ ret = an8801r_led_init(phydev);
+ if (ret) {
+ dev_err(phydev->dev, "AN8801R: LED initialize fail, ret %d!\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int an8801r_phy_probe(struct phy_device *phydev)
+{
+ struct an8801r_priv *priv;
+ u32 phy_id;
+ int ret;
+
+ ret = get_phy_id(phydev->bus, phydev->addr, MDIO_DEVAD_NONE, &phy_id);
+ if (ret)
+ return ret;
+
+ if (phy_id != AN8801R_PHY_ID) {
+ dev_err(phydev->dev,
+ "AN8801R can't be detected (id=0x%08x).\n", phy_id);
+ return -ENODEV;
+ }
+
+ priv = malloc(sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+
+ *priv = an8801r_priv_defaults;
+
+ phydev->priv = priv;
+
+ return 0;
+}
+
+static int an8801r_read_status(struct phy_device *phydev)
+{
+ u32 data;
+
+ if (!phydev->link)
+ return 0;
+
+ debug("AN8801R: SPEED %d\n", phydev->speed);
+ data = 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, data);
+}
+
+static int an8801r_startup(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_startup(phydev);
+ if (ret)
+ return ret;
+
+ return an8801r_read_status(phydev);
+}
+
+U_BOOT_PHY_DRIVER(an8801r) = {
+ .name = "Airoha AN8801R",
+ .uid = AN8801R_PHY_ID,
+ .mask = 0x0ffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .probe = &an8801r_phy_probe,
+ .config = &an8801r_config_init,
+ .read_page = &air_phy_read_page,
+ .write_page = &air_phy_write_page,
+ .startup = &an8801r_startup,
+ .shutdown = &genphy_shutdown,
+};
diff --git a/drivers/net/phy/airoha/air_en8811.c b/drivers/net/phy/airoha/air_en8811.c
index 0b974472732..7a07be2e956 100644
--- a/drivers/net/phy/airoha/air_en8811.c
+++ b/drivers/net/phy/airoha/air_en8811.c
@@ -23,6 +23,9 @@
#include <linux/compat.h>
#include <dm/device_compat.h>
#include <u-boot/crc.h>
+#include <linux/phy/phy-common-props.h>
+
+#include "air_phy_lib.h"
/* MII Registers */
#define AIR_AUX_CTRL_STATUS 0x1d
@@ -32,10 +35,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
-
#define AIR_PBUS_MODE_ADDR_HIGH 0x1c
/* MII Registers Page 4 */
#define AIR_BPBUS_MODE 0x10
@@ -309,166 +308,6 @@ static int air_pbus_reg_write(struct phy_device *phydev,
return ret;
}
-static int air_buckpbus_reg_write(struct phy_device *phydev,
- u32 pbus_address, u32 pbus_data)
-{
- int ret, saved_page;
-
- saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
- if (saved_page < 0)
- return saved_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
- AIR_BPBUS_MODE_ADDR_FIXED);
- if (ret < 0)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
- upper_16_bits(pbus_address));
- if (ret < 0)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
- lower_16_bits(pbus_address));
- if (ret < 0)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
- upper_16_bits(pbus_data));
- if (ret < 0)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
- lower_16_bits(pbus_data));
- if (ret < 0)
- goto restore_page;
-
-restore_page:
- if (ret < 0)
- dev_err(phydev->dev, "%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 = 0, saved_page;
-
- saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
- if (saved_page < 0)
- return saved_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
- AIR_BPBUS_MODE_ADDR_FIXED);
- if (ret < 0)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
- upper_16_bits(pbus_address));
- if (ret < 0)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
- lower_16_bits(pbus_address));
- if (ret < 0)
- goto restore_page;
-
- pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);
- if (pbus_data_high < 0) {
- ret = pbus_data_high;
- goto restore_page;
- }
-
- pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);
- if (pbus_data_low < 0) {
- ret = pbus_data_low;
- goto restore_page;
- }
-
- *pbus_data = pbus_data_low | (pbus_data_high << 16);
-
-restore_page:
- if (ret < 0)
- dev_err(phydev->dev, "%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 = 0, saved_page;
-
- saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
- if (saved_page < 0)
- return saved_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
- AIR_BPBUS_MODE_ADDR_FIXED);
- if (ret < 0)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
- upper_16_bits(pbus_address));
- if (ret < 0)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
- lower_16_bits(pbus_address));
- if (ret < 0)
- goto restore_page;
-
- pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);
- if (pbus_data_high < 0) {
- ret = pbus_data_high;
- goto restore_page;
- }
-
- pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);
- if (pbus_data_low < 0) {
- ret = pbus_data_low;
- goto restore_page;
- }
-
- 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)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
- upper_16_bits(pbus_address));
- if (ret < 0)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
- lower_16_bits(pbus_address));
- if (ret < 0)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
- upper_16_bits(pbus_data_new));
- if (ret < 0)
- goto restore_page;
-
- ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
- lower_16_bits(pbus_data_new));
- if (ret < 0)
- goto restore_page;
-
-restore_page:
- if (ret < 0)
- dev_err(phydev->dev, "%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, unsigned long address,
unsigned long array_size, const unsigned char *buffer)
{
@@ -539,12 +378,12 @@ static int an8811hb_check_crc(struct phy_device *phydev,
u32 pbus_value;
/* 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;
- ret = air_buckpbus_reg_read(phydev, set1, &pbus_value);
+ ret = air_phy_buckpbus_reg_read(phydev, set1, &pbus_value);
if (ret < 0)
return ret;
@@ -553,14 +392,14 @@ static int an8811hb_check_crc(struct phy_device *phydev,
do {
mdelay(300);
- ret = air_buckpbus_reg_read(phydev, mon2, &pbus_value);
+ ret = air_phy_buckpbus_reg_read(phydev, mon2, &pbus_value);
if (ret < 0)
return ret;
debug("%d: reg 0x%x val 0x%x!\n", __LINE__, mon2, pbus_value);
if (pbus_value & AN8811HB_CRC_ST) {
- ret = air_buckpbus_reg_read(phydev, mon3, &pbus_value);
+ ret = air_phy_buckpbus_reg_read(phydev, mon3, &pbus_value);
if (ret < 0)
return ret;
@@ -584,11 +423,11 @@ static int an8811hb_check_crc(struct phy_device *phydev,
}
} while (--retry);
- ret = air_buckpbus_reg_modify(phydev, set1, AN8811HB_CRC_RD_EN, 0);
+ ret = air_phy_buckpbus_reg_modify(phydev, set1, AN8811HB_CRC_RD_EN, 0);
if (ret < 0)
return ret;
- ret = air_buckpbus_reg_read(phydev, set1, &pbus_value);
+ ret = air_phy_buckpbus_reg_read(phydev, set1, &pbus_value);
if (ret < 0)
return ret;
@@ -646,9 +485,9 @@ static int an8811hb_surge_protect_cfg(struct phy_device *phydev)
return ret;
}
- ret = air_buckpbus_reg_modify(phydev, AIR_PHY_CONTROL,
- AIR_PHY_CONTROL_SURGE_5R,
- AIR_PHY_CONTROL_SURGE_5R);
+ ret = air_phy_buckpbus_reg_modify(phydev, AIR_PHY_CONTROL,
+ AIR_PHY_CONTROL_SURGE_5R,
+ AIR_PHY_CONTROL_SURGE_5R);
if (ret < 0)
return ret;
@@ -706,14 +545,14 @@ static int en8811h_load_firmware(struct phy_device *phydev)
goto en8811h_load_firmware_out;
}
- 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;
@@ -727,13 +566,13 @@ 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;
- 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 < 0)
goto en8811h_load_firmware_out;
@@ -741,8 +580,8 @@ static int en8811h_load_firmware(struct phy_device *phydev)
if (ret < 0)
goto en8811h_load_firmware_out;
- air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
- &priv->firmware_version);
+ air_phy_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
+ &priv->firmware_version);
dev_info(phydev->dev, "MD32 firmware version: %08x\n",
priv->firmware_version);
@@ -778,8 +617,8 @@ static int an8811hb_load_firmware(struct phy_device *phydev)
if (ret < 0)
goto an8811hb_load_firmware_out;
- ret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
- AIR_PHY_FW_CTRL_1_START);
+ ret = air_phy_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
+ AIR_PHY_FW_CTRL_1_START);
if (ret < 0)
goto an8811hb_load_firmware_out;
@@ -803,8 +642,8 @@ static int an8811hb_load_firmware(struct phy_device *phydev)
if (ret < 0)
goto an8811hb_load_firmware_out;
- ret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
- AIR_PHY_FW_CTRL_1_FINISH);
+ ret = air_phy_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
+ AIR_PHY_FW_CTRL_1_FINISH);
if (ret < 0)
goto an8811hb_load_firmware_out;
@@ -817,7 +656,7 @@ static int an8811hb_load_firmware(struct phy_device *phydev)
do {
mdelay(300);
- ret = air_buckpbus_reg_read(phydev, AIR_PHY_FW_CTRL_1, &reg_val);
+ ret = air_phy_buckpbus_reg_read(phydev, AIR_PHY_FW_CTRL_1, &reg_val);
if (ret < 0)
goto an8811hb_load_firmware_out;
@@ -827,8 +666,8 @@ static int an8811hb_load_firmware(struct phy_device *phydev)
debug("%d: reg 0x%x val 0x%x!\n", __LINE__, AIR_PHY_FW_CTRL_1,
reg_val);
- ret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
- AIR_PHY_FW_CTRL_1_FINISH);
+ ret = air_phy_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
+ AIR_PHY_FW_CTRL_1_FINISH);
if (ret < 0)
goto an8811hb_load_firmware_out;
@@ -838,8 +677,8 @@ static int an8811hb_load_firmware(struct phy_device *phydev)
if (ret < 0)
goto an8811hb_load_firmware_out;
- air_buckpbus_reg_read(phydev, AIR_PHY_MD32FW_VERSION,
- &priv->firmware_version);
+ air_phy_buckpbus_reg_read(phydev, AIR_PHY_MD32FW_VERSION,
+ &priv->firmware_version);
debug("MD32 firmware version: %08x\n", priv->firmware_version);
@@ -858,17 +697,17 @@ int an8811hb_cko_cfg(struct phy_device *phydev)
int ret = 0;
if (!ofnode_read_bool(node, "airoha,phy-output-clock")) {
- ret = air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,
- AN8811HB_CLK_DRV_CKO_MASK,
- AN8811HB_CLK_DRV_CKOPWD |
- AN8811HB_CLK_DRV_CKO_LDPWD |
- AN8811HB_CLK_DRV_CKO_LPPWD);
+ ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,
+ AN8811HB_CLK_DRV_CKO_MASK,
+ AN8811HB_CLK_DRV_CKOPWD |
+ AN8811HB_CLK_DRV_CKO_LDPWD |
+ AN8811HB_CLK_DRV_CKO_LPPWD);
if (ret < 0)
return ret;
debug("CKO Output mode - Disabled\n");
} else {
- 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;
@@ -887,13 +726,13 @@ static int en8811h_restart_mcu(struct phy_device *phydev)
if (ret < 0)
return 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;
- return air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
- EN8811H_FW_CTRL_1_FINISH);
+ return air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+ EN8811H_FW_CTRL_1_FINISH);
}
static int air_led_hw_control_set(struct phy_device *phydev, u8 index,
@@ -1046,11 +885,51 @@ static int air_leds_init(struct phy_device *phydev, int num, u16 dur, int mode)
return 0;
}
-static int en8811h_config(struct phy_device *phydev)
+static int en8811h_config_serdes_polarity(struct phy_device *phydev)
{
- struct en8811h_priv *priv = phydev->priv;
ofnode node = phy_get_ofnode(phydev);
+ unsigned int pol, default_pol;
u32 pbus_value = 0;
+ int ret;
+
+ if (!ofnode_valid(node))
+ return 0;
+
+ default_pol = PHY_POL_NORMAL;
+ if (ofnode_read_bool(node, "airoha,pnswap-rx"))
+ default_pol = PHY_POL_INVERT;
+
+ ret = phy_get_rx_polarity(node,
+ phy_string_for_interface(phydev->interface),
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
+ default_pol, &pol);
+ if (ret)
+ return ret;
+ if (pol == PHY_POL_INVERT)
+ pbus_value |= EN8811H_POLARITY_RX_REVERSE;
+
+ default_pol = PHY_POL_NORMAL;
+ if (ofnode_read_bool(node, "airoha,pnswap-tx"))
+ default_pol = PHY_POL_INVERT;
+
+ ret = phy_get_tx_polarity(node,
+ phy_string_for_interface(phydev->interface),
+ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT),
+ default_pol, &pol);
+ if (ret)
+ return ret;
+ if (pol == PHY_POL_NORMAL)
+ pbus_value |= EN8811H_POLARITY_TX_NORMAL;
+
+ return air_phy_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
+ EN8811H_POLARITY_RX_REVERSE |
+ EN8811H_POLARITY_TX_NORMAL,
+ pbus_value);
+}
+
+static int en8811h_config(struct phy_device *phydev)
+{
+ struct en8811h_priv *priv = phydev->priv;
int ret = 0;
/* If restart happened in .probe(), no need to restart now */
@@ -1081,20 +960,8 @@ static int en8811h_config(struct phy_device *phydev)
if (ret < 0)
return ret;
- /* Serdes polarity */
- pbus_value = 0;
- if (ofnode_read_bool(node, "airoha,pnswap-rx"))
- pbus_value |= EN8811H_POLARITY_RX_REVERSE;
- else
- pbus_value &= ~EN8811H_POLARITY_RX_REVERSE;
- if (ofnode_read_bool(node, "airoha,pnswap-tx"))
- pbus_value &= ~EN8811H_POLARITY_TX_NORMAL;
- else
- pbus_value |= EN8811H_POLARITY_TX_NORMAL;
- ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
- EN8811H_POLARITY_RX_REVERSE |
- EN8811H_POLARITY_TX_NORMAL,
- pbus_value);
+ /* Configure Serdes polarity from device tree */
+ ret = en8811h_config_serdes_polarity(phydev);
if (ret < 0)
return ret;
@@ -1142,12 +1009,12 @@ static int an8811hb_config(struct phy_device *phydev)
priv->mcu_needs_restart = true;
}
- ret = air_buckpbus_reg_read(phydev, AN8811HB_PRO_ID, &pbus_value);
+ ret = air_phy_buckpbus_reg_read(phydev, AN8811HB_PRO_ID, &pbus_value);
if (ret < 0)
return ret;
priv->pro_id = (pbus_value & AN8811HB_PRO_ID_VERSION) + 1;
- 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;
priv->pkg_sel = (pbus_value & AN8811HB_HWTRAP2_PKG) >> 12;
@@ -1163,8 +1030,8 @@ static int an8811hb_config(struct phy_device *phydev)
pbus_value |= AN8811HB_RX_POLARITY_NORMAL;
debug("1 pbus_value 0x%x\n", pbus_value);
- 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;
@@ -1175,35 +1042,35 @@ static int an8811hb_config(struct phy_device *phydev)
pbus_value |= AN8811HB_TX_POLARITY_NORMAL;
debug("2 pbus_value 0x%x\n", pbus_value);
- ret = air_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY,
- AN8811HB_TX_POLARITY_NORMAL, pbus_value);
+ ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY,
+ AN8811HB_TX_POLARITY_NORMAL, pbus_value);
if (ret < 0)
return ret;
/* Configure led gpio pins as output */
if (priv->pkg_sel) {
- ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
- AN8811HB_GPIO_OUTPUT_MASK,
- AN8811HB_GPIO_OUTPUT_0115);
+ ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
+ AN8811HB_GPIO_OUTPUT_MASK,
+ AN8811HB_GPIO_OUTPUT_0115);
if (ret < 0)
return ret;
- ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_1,
- AN8811HB_GPIO_SEL_1_0_MASK |
- AN8811HB_GPIO_SEL_1_1_MASK,
- AN8811HB_GPIO_SEL_1_0 |
- AN8811HB_GPIO_SEL_1_1);
+ ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_1,
+ AN8811HB_GPIO_SEL_1_0_MASK |
+ AN8811HB_GPIO_SEL_1_1_MASK,
+ AN8811HB_GPIO_SEL_1_0 |
+ AN8811HB_GPIO_SEL_1_1);
if (ret < 0)
return ret;
- ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_2,
- AN8811HB_GPIO_SEL_2_15_MASK,
- AN8811HB_GPIO_SEL_2_15);
+ ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_2,
+ AN8811HB_GPIO_SEL_2_15_MASK,
+ AN8811HB_GPIO_SEL_2_15);
if (ret < 0)
return ret;
} else {
- 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;
}
@@ -1373,16 +1240,6 @@ static int en8811h_probe(struct phy_device *phydev)
return 0;
}
-static int air_phy_read_page(struct phy_device *phydev)
-{
- return phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS);
-}
-
-static int air_phy_write_page(struct phy_device *phydev, int page)
-{
- return phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page);
-}
-
U_BOOT_PHY_DRIVER(en8811h) = {
.name = "Airoha EN8811H",
.uid = EN8811H_PHY_ID,
diff --git a/drivers/net/phy/airoha/air_phy_lib.c b/drivers/net/phy/airoha/air_phy_lib.c
new file mode 100644
index 00000000000..61c3bf82822
--- /dev/null
+++ b/drivers/net/phy/airoha/air_phy_lib.c
@@ -0,0 +1,216 @@
+// 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 <[email protected]>
+ *
+ * Adapated from https://lore.kernel.org/all/20260326-add-airoha-an8801-support-v2-2-1a42d6b6050f@collabora.com/
+ */
+
+#include <dm/device_compat.h>
+#include <linux/compat.h>
+#include <phy.h>
+
+#include "air_phy_lib.h"
+
+#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, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
+ AIR_BPBUS_MODE_ADDR_FIXED);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
+ upper_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
+ lower_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE,
+ AIR_BPBUS_RD_DATA_HIGH);
+ if (pbus_data_high < 0)
+ return pbus_data_high;
+
+ pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE,
+ 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, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
+ AIR_BPBUS_MODE_ADDR_FIXED);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
+ upper_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
+ lower_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
+ upper_16_bits(pbus_data));
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 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, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
+ AIR_BPBUS_MODE_ADDR_FIXED);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
+ upper_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
+ lower_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE,
+ AIR_BPBUS_RD_DATA_HIGH);
+ if (pbus_data_high < 0)
+ return pbus_data_high;
+
+ pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE,
+ 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, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
+ upper_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
+ lower_16_bits(pbus_address));
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
+ upper_16_bits(pbus_data_new));
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 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)
+ dev_err(phydev->dev, "%s 0x%08x failed: %d\n", __func__,
+ pbus_address, ret);
+ }
+
+ return phy_restore_page(phydev, saved_page, ret);
+}
+
+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)
+ dev_err(phydev->dev, "%s 0x%08x failed: %d\n", __func__,
+ pbus_address, ret);
+ }
+
+ return phy_restore_page(phydev, saved_page, ret);
+}
+
+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)
+ dev_err(phydev->dev, "%s 0x%08x failed: %d\n", __func__,
+ pbus_address, ret);
+ }
+
+ return phy_restore_page(phydev, saved_page, ret);
+}
+
+int air_phy_read_page(struct phy_device *phydev)
+{
+ return phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS);
+}
+
+int air_phy_write_page(struct phy_device *phydev, int page)
+{
+ return phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page);
+}
+
+MODULE_DESCRIPTION("Airoha PHY Library");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Louis-Alexis Eyraud");
diff --git a/drivers/net/phy/airoha/air_phy_lib.h b/drivers/net/phy/airoha/air_phy_lib.h
new file mode 100644
index 00000000000..845d2f7cfb4
--- /dev/null
+++ b/drivers/net/phy/airoha/air_phy_lib.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2026 Airoha Technology Corp.
+ * Copyright (C) 2026 Collabora Ltd.
+ * Louis-Alexis Eyraud <[email protected]>
+ */
+
+#ifndef __AIR_PHY_LIB_H
+#define __AIR_PHY_LIB_H
+
+#define AIR_EXT_PAGE_ACCESS 0x1f
+
+#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);
+
+#endif /* __AIR_PHY_LIB_H */
diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c
index 10b87dfb8ab..83da3e1cc77 100644
--- a/drivers/net/phy/aquantia.c
+++ b/drivers/net/phy/aquantia.c
@@ -400,7 +400,7 @@ int aquantia_config(struct phy_device *phydev)
int interface = phydev->interface;
u32 val, id, rstatus, fault;
u32 reg_val1 = 0;
- int num_retries = 5;
+ int num_retries = 200;
int usx_an = 0;
/*
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index 7ce03b59b6a..ebed61de133 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -203,32 +203,24 @@ static int dp83867_of_init(struct phy_device *phydev)
"Should be 'rgmii-id' to use internal delays\n");
}
- /* RX delay *must* be specified if internal delay of RX is used. */
+ dp83867->rx_id_delay = DP83867_RGMIIDCTL_2_00_NS;
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
ret = ofnode_read_u32(node, "ti,rx-internal-delay",
&dp83867->rx_id_delay);
- if (ret) {
- pr_debug("ti,rx-internal-delay must be specified\n");
- return ret;
- }
- if (dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) {
+ if (!ret && dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) {
pr_debug("ti,rx-internal-delay value of %u out of range\n",
dp83867->rx_id_delay);
return -EINVAL;
}
}
- /* TX delay *must* be specified if internal delay of RX is used. */
+ dp83867->tx_id_delay = DP83867_RGMIIDCTL_2_00_NS;
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
ret = ofnode_read_u32(node, "ti,tx-internal-delay",
&dp83867->tx_id_delay);
- if (ret) {
- debug("ti,tx-internal-delay must be specified\n");
- return ret;
- }
- if (dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) {
+ if (!ret && dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) {
pr_debug("ti,tx-internal-delay value of %u out of range\n",
dp83867->tx_id_delay);
return -EINVAL;
diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c
index a65e81dff0c..d96970949bc 100644
--- a/drivers/net/phy/mscc.c
+++ b/drivers/net/phy/mscc.c
@@ -23,6 +23,7 @@
#define PHY_ID_VSC8502 0x00070630
#define PHY_ID_VSC8540 0x00070760
#define PHY_ID_VSC8541 0x00070770
+#define PHY_ID_VSC8572 0x000704d0
#define PHY_ID_VSC8574 0x000704a0
#define PHY_ID_VSC8584 0x000707c0
@@ -1612,6 +1613,16 @@ U_BOOT_PHY_DRIVER(vsc8541) = {
.shutdown = &genphy_shutdown,
};
+U_BOOT_PHY_DRIVER(vsc8572) = {
+ .name = "Microsemi VSC8572",
+ .uid = PHY_ID_VSC8572,
+ .mask = 0x000ffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8574_config,
+ .startup = &mscc_startup,
+ .shutdown = &genphy_shutdown,
+};
+
U_BOOT_PHY_DRIVER(vsc8574) = {
.name = "Microsemi VSC8574",
.uid = PHY_ID_VSC8574,
diff --git a/drivers/net/phy/nxp-c45-tja11xx.c b/drivers/net/phy/nxp-c45-tja11xx.c
index a1e4c3d053b..9814ac498ed 100644
--- a/drivers/net/phy/nxp-c45-tja11xx.c
+++ b/drivers/net/phy/nxp-c45-tja11xx.c
@@ -343,7 +343,7 @@ static int nxp_c45_probe(struct phy_device *phydev)
{
struct nxp_c45_phy *priv;
- priv = devm_kzalloc(phydev->priv, sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;