diff options
Diffstat (limited to 'drivers/net/phy')
| -rw-r--r-- | drivers/net/phy/Kconfig | 22 | ||||
| -rw-r--r-- | drivers/net/phy/airoha/Kconfig | 13 | ||||
| -rw-r--r-- | drivers/net/phy/airoha/Makefile | 2 | ||||
| -rw-r--r-- | drivers/net/phy/airoha/air_an8801.c | 594 | ||||
| -rw-r--r-- | drivers/net/phy/airoha/air_en8811.c | 303 | ||||
| -rw-r--r-- | drivers/net/phy/airoha/air_phy_lib.c | 216 | ||||
| -rw-r--r-- | drivers/net/phy/airoha/air_phy_lib.h | 39 | ||||
| -rw-r--r-- | drivers/net/phy/dp83867.c | 18 | ||||
| -rw-r--r-- | drivers/net/phy/nxp-c45-tja11xx.c | 2 | ||||
| -rw-r--r-- | drivers/net/phy/phy.c | 29 |
10 files changed, 988 insertions, 250 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 0025c895f12..ee438524490 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -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. diff --git a/drivers/net/phy/airoha/Kconfig b/drivers/net/phy/airoha/Kconfig index 4139df343ad..2d58d674200 100644 --- a/drivers/net/phy/airoha/Kconfig +++ b/drivers/net/phy/airoha/Kconfig @@ -2,12 +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 32f06dd6dfa..7a07be2e956 100644 --- a/drivers/net/phy/airoha/air_en8811.c +++ b/drivers/net/phy/airoha/air_en8811.c @@ -25,6 +25,8 @@ #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 #define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) @@ -33,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 @@ -310,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) { @@ -540,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; @@ -554,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; @@ -585,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; @@ -647,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; @@ -707,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; @@ -728,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; @@ -742,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); @@ -779,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; @@ -804,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; @@ -818,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, ®_val); + ret = air_phy_buckpbus_reg_read(phydev, AIR_PHY_FW_CTRL_1, ®_val); if (ret < 0) goto an8811hb_load_firmware_out; @@ -828,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; @@ -839,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); @@ -859,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; @@ -888,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, @@ -1083,9 +921,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 en8811h_config(struct phy_device *phydev) @@ -1170,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; @@ -1191,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; @@ -1203,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; } @@ -1401,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/dp83867.c b/drivers/net/phy/dp83867.c index ebed61de133..34c871857e1 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -126,6 +126,22 @@ struct dp83867_private { bool sgmii_ref_clk_en; }; +static int dp83867_phy_extread(struct phy_device *phydev, + int addr, int devad, int reg) +{ + if (devad != DP83867_DEVADDR) + return -EINVAL; + return phy_read_mmd(phydev, devad, addr); +}; + +static int dp83867_phy_extwrite(struct phy_device *phydev, int addr, + int devad, int reg, u16 val) +{ + if (devad != DP83867_DEVADDR) + return -EINVAL; + return phy_write_mmd(phydev, devad, addr, (u32)val); +}; + static int dp83867_config_port_mirroring(struct phy_device *phydev) { struct dp83867_private *dp83867 = @@ -410,4 +426,6 @@ U_BOOT_PHY_DRIVER(dp83867) = { .config = &dp83867_config, .startup = &genphy_startup, .shutdown = &genphy_shutdown, + .readext = dp83867_phy_extread, + .writeext = dp83867_phy_extwrite, }; 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; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index d7e0c4fe02d..f2e9b2a9820 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -20,6 +20,7 @@ #include <asm-generic/gpio.h> #include <dm/device_compat.h> #include <dm/of_extra.h> +#include <dm/uclass-internal.h> #include <linux/bitops.h> #include <linux/delay.h> #include <linux/err.h> @@ -921,6 +922,27 @@ static struct phy_device *phy_connect_fixed(struct mii_dev *bus, } #endif +#ifdef CONFIG_DM_ETH_PHY +static struct phy_device *phy_connect_dm_bound(struct mii_dev *bus, + struct udevice *dev, + uint mask) +{ + struct udevice *dm_phy_dev; + struct phy_device *phydev; + + if (!uclass_find_device_by_phandle(UCLASS_ETH_PHY, dev, + "phy-handle", &dm_phy_dev)) { + phydev = phy_find_by_mask(bus, mask); + if (phydev) + phydev->node = dev_ofnode(dm_phy_dev); + + return phydev; + } + + return NULL; +} +#endif + struct phy_device *phy_connect(struct mii_dev *bus, int addr, struct udevice *dev, phy_interface_t interface) @@ -928,8 +950,13 @@ struct phy_device *phy_connect(struct mii_dev *bus, int addr, struct phy_device *phydev = NULL; uint mask = (addr >= 0) ? (1 << addr) : 0xffffffff; +#ifdef CONFIG_DM_ETH_PHY + phydev = phy_connect_dm_bound(bus, dev, mask); +#endif + #ifdef CONFIG_PHY_FIXED - phydev = phy_connect_fixed(bus, dev); + if (!phydev) + phydev = phy_connect_fixed(bus, dev); #endif #ifdef CONFIG_PHY_NCSI |
