diff options
| author | Tom Rini <[email protected]> | 2026-03-13 09:00:54 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-03-13 10:52:03 -0600 |
| commit | 6dc75d440dbdd3e2ac24ae5bb0ed51123bee8c33 (patch) | |
| tree | 854cbd24bc2ee6852e0efed6b029c755c7c26a3e /drivers/net/phy | |
| parent | 9f1059bc735ec2a8d78c1f3e4fcab1fa3f428317 (diff) | |
| parent | a5fcbd5a83553b3803df28422410c9fd22adaec6 (diff) | |
Merge tag 'net-20260312' of https://source.denx.de/u-boot/custodians/u-boot-net into next
Pull request net-20260312.
net:
- Move network PHY under NETDEVICES
- s/DM_CLK/CLK/ in HIFEMAC_{ETH,MDIO}
- Add support for Airoha AN8811HB PHY
- airoha: PCS and MDIO support for Airoha AN7581 SoC
net-lwip:
- Fix issue when TFTP blocksize is >8192
- Adjust PBUF_POOL_SIZE/IP_REASS_MAX_PBUFS for better performance and
resource usage.
- Enable mii command for NET_LWIP
Diffstat (limited to 'drivers/net/phy')
| -rw-r--r-- | drivers/net/phy/airoha/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/net/phy/airoha/air_en8811.c | 689 |
2 files changed, 647 insertions, 44 deletions
diff --git a/drivers/net/phy/airoha/Kconfig b/drivers/net/phy/airoha/Kconfig index 999564e4848..fcace9a24ac 100644 --- a/drivers/net/phy/airoha/Kconfig +++ b/drivers/net/phy/airoha/Kconfig @@ -8,4 +8,4 @@ config PHY_AIROHA_EN8811 select FW_LOADER help AIROHA EN8811H supported. - + AIROHA AN8811HB supported. diff --git a/drivers/net/phy/airoha/air_en8811.c b/drivers/net/phy/airoha/air_en8811.c index 1a628ede82b..0b974472732 100644 --- a/drivers/net/phy/airoha/air_en8811.c +++ b/drivers/net/phy/airoha/air_en8811.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Driver for the Airoha EN8811H 2.5 Gigabit PHY. + * Driver for the Airoha EN8811H and AN8811HB 2.5 Gigabit PHY. * * Limitations of the EN8811H: * - Only full duplex supported @@ -8,9 +8,8 @@ * * Source originated from linux air_en8811h.c * - * Copyright (C) 2025 Airoha Technology Corp. + * Copyright (C) 2025, 2026 Airoha Technology Corp. */ - #include <phy.h> #include <errno.h> #include <log.h> @@ -20,27 +19,15 @@ #include <asm/unaligned.h> #include <linux/iopoll.h> #include <linux/bitops.h> +#include <linux/bitfield.h> #include <linux/compat.h> #include <dm/device_compat.h> #include <u-boot/crc.h> -#define EN8811H_PHY_ID 0x03a2a411 - -#define AIR_FW_ADDR_DM 0x00000000 -#define AIR_FW_ADDR_DSP 0x00100000 - -#define EN8811H_MD32_DM_SIZE 0x4000 -#define EN8811H_MD32_DSP_SIZE 0x20000 - -#define EN8811H_FW_CTRL_1 0x0f0018 -#define EN8811H_FW_CTRL_1_START 0x0 -#define EN8811H_FW_CTRL_1_FINISH 0x1 -#define EN8811H_FW_CTRL_2 0x800000 -#define EN8811H_FW_CTRL_2_LOADING BIT(11) - /* MII Registers */ #define AIR_AUX_CTRL_STATUS 0x1d #define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) +#define AIR_AUX_CTRL_STATUS_SPEED_10 0x0 #define AIR_AUX_CTRL_STATUS_SPEED_100 0x4 #define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 #define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc @@ -49,6 +36,7 @@ #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 #define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 @@ -63,8 +51,16 @@ #define AIR_BPBUS_RD_DATA_LOW 0x18 /* Registers on MDIO_MMD_VEND1 */ -#define EN8811H_PHY_FW_STATUS 0x8009 -#define EN8811H_PHY_READY 0x02 +#define AIR_PHY_MCU_CMD_1 0x800c +#define AIR_PHY_MCU_CMD_1_MODE1 0x0 +#define AIR_PHY_MCU_CMD_2 0x800d +#define AIR_PHY_MCU_CMD_2_MODE1 0x0 +#define AIR_PHY_MCU_CMD_3 0x800e +#define AIR_PHY_MCU_CMD_3_MODE1 0x1101 +#define AIR_PHY_MCU_CMD_3_DOCMD 0x1100 +#define AIR_PHY_MCU_CMD_4 0x800f +#define AIR_PHY_MCU_CMD_4_MODE1 0x0002 +#define AIR_PHY_MCU_CMD_4_INTCLR 0x00e4 /* Registers on MDIO_MMD_VEND2 */ #define AIR_PHY_LED_BCR 0x021 @@ -77,7 +73,7 @@ #define AIR_PHY_LED_DUR_BLINK 0x023 -#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2)) +#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2)) #define AIR_PHY_LED_ON_MASK (GENMASK(6, 0) | BIT(8)) #define AIR_PHY_LED_ON_LINK1000 BIT(0) #define AIR_PHY_LED_ON_LINK100 BIT(1) @@ -90,7 +86,7 @@ #define AIR_PHY_LED_ON_POLARITY BIT(14) #define AIR_PHY_LED_ON_ENABLE BIT(15) -#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2)) +#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2)) #define AIR_PHY_LED_BLINK_1000TX BIT(0) #define AIR_PHY_LED_BLINK_1000RX BIT(1) #define AIR_PHY_LED_BLINK_100TX BIT(2) @@ -104,21 +100,101 @@ #define AIR_PHY_LED_BLINK_2500TX BIT(10) #define AIR_PHY_LED_BLINK_2500RX BIT(11) +/* Registers on BUCKPBUS */ +#define AIR_PHY_CONTROL 0x3a9c +#define AIR_PHY_CONTROL_SURGE_5R BIT(3) +#define AIR_PHY_CONTROL_INTERNAL BIT(11) + +/* Led definitions */ +#define EN8811H_LED_COUNT 3 + +/* Firmware registers */ +#define AIR_FW_ADDR_DM 0x00000000 +#define AIR_FW_ADDR_DSP 0x00100000 +#define EN8811H_FW_CTRL_1 0x0f0018 +#define EN8811H_FW_CTRL_1_START 0x0 +#define EN8811H_FW_CTRL_1_FINISH 0x1 +#define EN8811H_FW_CTRL_2 0x800000 +#define EN8811H_FW_CTRL_2_LOADING BIT(11) +#define EN8811H_PHY_FW_STATUS 0x8009 +#define EN8811H_PHY_READY 0x02 +#define AIR_PHY_FW_STATUS 0x8009 +#define AIR_PHY_READY 0x02 + +#define AIR_PHY_FW_CTRL_1 0x0f0018 +#define AIR_PHY_FW_CTRL_1_START 0x0 +#define AIR_PHY_FW_CTRL_1_FINISH 0x1 + +/* EN8811H */ +#define EN8811H_PHY_ID 0x03a2a411 +#define EN8811H_MD32_DM_SIZE 0x4000 +#define EN8811H_MD32_DSP_SIZE 0x20000 #define EN8811H_FW_VERSION 0x3b3c #define EN8811H_POLARITY 0xca0f8 #define EN8811H_POLARITY_TX_NORMAL BIT(0) #define EN8811H_POLARITY_RX_REVERSE BIT(1) - #define EN8811H_CLK_CGM 0xcf958 #define EN8811H_CLK_CGM_CKO BIT(26) #define EN8811H_HWTRAP1 0xcf914 #define EN8811H_HWTRAP1_CKO BIT(12) -#define clear_bit(bit, bitmap) __clear_bit(bit, bitmap) - -/* Led definitions */ -#define EN8811H_LED_COUNT 3 +/* AN8811HB */ +#define AN8811HB_PHY_ID 0xc0ff04a0 +#define AIR_MD32_DM_SIZE 0x8000 +#define AIR_MD32_DSP_SIZE 0x20000 +#define AIR_PHY_MD32FW_VERSION 0x3b3c + +#define AN8811HB_GPIO_OUTPUT 0x5cf8b8 +#define AN8811HB_GPIO_OUTPUT_MASK GENMASK(15, 0) +#define AN8811HB_GPIO_OUTPUT_345 (BIT(3) | BIT(4) | BIT(5)) +#define AN8811HB_GPIO_OUTPUT_0115 (BIT(0) | BIT(1) | BIT(15)) +#define AN8811HB_GPIO_SEL_1 0x5cf8bc +#define AN8811HB_GPIO_SEL_1_0_MASK GENMASK(2, 0) +#define AN8811HB_GPIO_SEL_1_1_MASK GENMASK(6, 4) +#define AN8811HB_GPIO_SEL_1_0 FIELD_PREP(AN8811HB_GPIO_SEL_1_0_MASK, 1) +#define AN8811HB_GPIO_SEL_1_1 FIELD_PREP(AN8811HB_GPIO_SEL_1_1_MASK, 0) +#define AN8811HB_GPIO_SEL_2 0x5cf8c0 +#define AN8811HB_GPIO_SEL_2_15_MASK GENMASK(30, 28) +#define AN8811HB_GPIO_SEL_2_15 FIELD_PREP(AN8811HB_GPIO_SEL_2_15_MASK, 2) + +#define AN8811HB_CRC_PM_SET1 0xf020c +#define AN8811HB_CRC_PM_MON2 0xf0218 +#define AN8811HB_CRC_PM_MON3 0xf021c +#define AN8811HB_CRC_DM_SET1 0xf0224 +#define AN8811HB_CRC_DM_MON2 0xf0230 +#define AN8811HB_CRC_DM_MON3 0xf0234 +#define AN8811HB_CRC_RD_EN BIT(0) +#define AN8811HB_CRC_ST (BIT(0) | BIT(1)) +#define AN8811HB_CRC_CHECK_PASS BIT(0) + +#define AN8811HB_TX_POLARITY 0x5ce004 +#define AN8811HB_TX_POLARITY_NORMAL BIT(7) +#define AN8811HB_RX_POLARITY 0x5ce61c +#define AN8811HB_RX_POLARITY_NORMAL BIT(7) + +#define AN8811HB_HWTRAP1 0x5cf910 +#define AN8811HB_HWTRAP2 0x5cf914 +#define AN8811HB_HWTRAP2_CKO BIT(28) +#define AN8811HB_HWTRAP2_PKG (BIT(12) | BIT(13) | BIT(14)) +#define AN8811HB_PRO_ID 0x5cf920 +#define AN8811HB_PRO_ID_VERSION GENMASK(3, 0) + +#define AN8811HB_CLK_DRV 0x5cf9e4 +#define AN8811HB_CLK_DRV_CKO_MASK GENMASK(14, 12) +#define AN8811HB_CLK_DRV_CKOPWD BIT(12) +#define AN8811HB_CLK_DRV_CKO_LDPWD BIT(13) +#define AN8811HB_CLK_DRV_CKO_LPPWD BIT(14) + +#define AN8811HB_MCU_SW_RST 0x5cf9f8 +#define AN8811HB_MCU_SW_RST_HOLD BIT(16) +#define AN8811HB_MCU_SW_RST_RUN (BIT(16) | BIT(0)) +#define AN8811HB_MCU_SW_START 0x5cf9fc +#define AN8811HB_MCU_SW_START_EN BIT(16) + +#define clear_bit(bit, bitmap) __clear_bit(bit, bitmap) + +#define SCRIPT_NAME(name) #name "_load_firmware" struct led { unsigned long rules; @@ -191,11 +267,48 @@ enum air_led_trigger_netdev_modes { #define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS) struct en8811h_priv { - int firmware_version; + u32 firmware_version; bool mcu_needs_restart; struct led led[EN8811H_LED_COUNT]; + u32 pro_id; + u32 pkg_sel; + u32 mem_size; + const char *script_name; }; +static int air_pbus_reg_write(struct phy_device *phydev, + u32 pbus_reg, u32 pbus_data) +{ + int pbus_addr = (phydev->addr) + 8; + struct mii_dev *bus = phydev->bus; + int ret; + + ret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE, + AIR_EXT_PAGE_ACCESS, + (pbus_reg >> 16)); + if (ret < 0) + return ret; + + ret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE, + AIR_PBUS_MODE_ADDR_HIGH, + ((pbus_reg & GENMASK(15, 6)) >> 6)); + if (ret < 0) + return ret; + + ret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE, + ((pbus_reg & GENMASK(5, 2)) >> 2), + (pbus_data & GENMASK(15, 0))); + if (ret < 0) + return ret; + + ret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE, 0x10, + ((pbus_data & GENMASK(31, 16)) >> 16)); + if (ret < 0) + return ret; + + return ret; +} + static int air_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address, u32 pbus_data) { @@ -359,8 +472,8 @@ restore_page: static int air_write_buf(struct phy_device *phydev, unsigned long address, unsigned long array_size, const unsigned char *buffer) { - unsigned int offset; int ret, saved_page; + u32 offset; u16 val; saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); @@ -419,18 +532,144 @@ static int en8811h_wait_mcu_ready(struct phy_device *phydev) return ret; } -int en8811h_read_fw(void **fw, size_t *fwsize) +static int an8811hb_check_crc(struct phy_device *phydev, + u32 set1, u32 mon2, u32 mon3) +{ + int ret, retry = 10; + u32 pbus_value; + + /* Configure CRC */ + ret = air_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); + if (ret < 0) + return ret; + + debug("%d: reg 0x%x val 0x%x!\n", __LINE__, set1, pbus_value); + + do { + mdelay(300); + + ret = air_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); + if (ret < 0) + return ret; + + debug("%d: reg 0x%x val 0x%x!\n", __LINE__, mon3, + pbus_value); + + if (pbus_value & AN8811HB_CRC_CHECK_PASS) + debug("CRC Check PASS!\n"); + else + dev_err(phydev->dev, "CRC Check FAIL!(0x%lx)\n", + pbus_value & AN8811HB_CRC_CHECK_PASS); + + break; + } + + if (!retry) { + dev_err(phydev->dev, + "CRC Check is not ready.(Status %u)\n", + pbus_value); + return -ENODEV; + } + } while (--retry); + + ret = air_buckpbus_reg_modify(phydev, set1, AN8811HB_CRC_RD_EN, 0); + if (ret < 0) + return ret; + + ret = air_buckpbus_reg_read(phydev, set1, &pbus_value); + if (ret < 0) + return ret; + + debug("%d: reg 0x%x val 0x%x!\n", __LINE__, set1, pbus_value); + + return ret; +} + +static int an8811hb_mcu_assert(struct phy_device *phydev) +{ + int ret; + + ret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_RST, + AN8811HB_MCU_SW_RST_HOLD); + if (ret < 0) + return ret; + + ret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_START, 0); + if (ret < 0) + return ret; + + debug("MCU asserted\n"); + mdelay(50); + + return ret; +} + +static int an8811hb_mcu_deassert(struct phy_device *phydev) +{ + int ret; + + ret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_START, + AN8811HB_MCU_SW_START_EN); + if (ret < 0) + return ret; + + ret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_RST, + AN8811HB_MCU_SW_RST_RUN); + if (ret < 0) + return ret; + + debug("MCU deasserted\n"); + mdelay(50); + + return ret; +} + +static int an8811hb_surge_protect_cfg(struct phy_device *phydev) +{ + ofnode node = phy_get_ofnode(phydev); + int ret = 0; + + if (!ofnode_read_bool(node, "airoha,surge-5r")) { + debug("Surge Protection mode - 0R\n"); + return ret; + } + + ret = air_buckpbus_reg_modify(phydev, AIR_PHY_CONTROL, + AIR_PHY_CONTROL_SURGE_5R, + AIR_PHY_CONTROL_SURGE_5R); + if (ret < 0) + return ret; + + debug("Surge Protection mode - 5R\n"); + + return ret; +} + +static int en8811h_read_fw(void **fw, size_t *fwsize, struct en8811h_priv *priv) { + const char *script_name = priv->script_name; + u32 mem_size = priv->mem_size; void *buffer; int ret; - buffer = malloc(EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE); + buffer = malloc(mem_size); if (!buffer) return -ENOMEM; - ret = request_firmware_into_buf_via_script(buffer, - EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE, - "en8811h_load_firmware", fwsize); + ret = request_firmware_into_buf_via_script(buffer, mem_size, + script_name, fwsize); if (ret) { free(buffer); return ret; @@ -450,7 +689,10 @@ static int en8811h_load_firmware(struct phy_device *phydev) void *buffer; int ret; - ret = en8811h_read_fw(&buffer, &fw_size); + priv->script_name = SCRIPT_NAME(en8811h); + priv->mem_size = EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE; + + ret = en8811h_read_fw(&buffer, &fw_size, priv); if (ret < 0) { dev_err(phydev->dev, "Failed to get firmware data\n"); return -EINVAL; @@ -496,9 +738,12 @@ static int en8811h_load_firmware(struct phy_device *phydev) goto en8811h_load_firmware_out; ret = en8811h_wait_mcu_ready(phydev); + if (ret < 0) + goto en8811h_load_firmware_out; air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, &priv->firmware_version); + dev_info(phydev->dev, "MD32 firmware version: %08x\n", priv->firmware_version); @@ -510,6 +755,130 @@ en8811h_load_firmware_out: return ret; } +static int an8811hb_load_firmware(struct phy_device *phydev) +{ + struct en8811h_priv *priv = phydev->priv; + int ret, retry = 10; + size_t fw_size; + void *buffer; + u32 reg_val; + + ret = an8811hb_mcu_assert(phydev); + if (ret < 0) + return ret; + + ret = an8811hb_mcu_deassert(phydev); + if (ret < 0) + return ret; + + priv->script_name = SCRIPT_NAME(an8811hb); + priv->mem_size = AIR_MD32_DM_SIZE + AIR_MD32_DSP_SIZE; + + ret = en8811h_read_fw(&buffer, &fw_size, priv); + 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); + if (ret < 0) + goto an8811hb_load_firmware_out; + + ret = air_write_buf(phydev, AIR_FW_ADDR_DM, AIR_MD32_DM_SIZE, + (unsigned char *)buffer); + if (ret < 0) + goto an8811hb_load_firmware_out; + + ret = an8811hb_check_crc(phydev, AN8811HB_CRC_DM_SET1, + AN8811HB_CRC_DM_MON2, AN8811HB_CRC_DM_MON3); + if (ret < 0) + goto an8811hb_load_firmware_out; + + ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, AIR_MD32_DSP_SIZE, + (unsigned char *)buffer + AIR_MD32_DM_SIZE); + if (ret < 0) + goto an8811hb_load_firmware_out; + + ret = an8811hb_check_crc(phydev, AN8811HB_CRC_PM_SET1, + AN8811HB_CRC_PM_MON2, AN8811HB_CRC_PM_MON3); + 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); + if (ret < 0) + goto an8811hb_load_firmware_out; + + ret = an8811hb_surge_protect_cfg(phydev); + if (ret < 0) { + dev_err(phydev->dev, "an8811hb_surge_protect_cfg fail. (ret=%d)\n", ret); + goto an8811hb_load_firmware_out; + } + + do { + mdelay(300); + + ret = air_buckpbus_reg_read(phydev, AIR_PHY_FW_CTRL_1, ®_val); + if (ret < 0) + goto an8811hb_load_firmware_out; + + if (reg_val == AIR_PHY_FW_CTRL_1_FINISH) + break; + + 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); + if (ret < 0) + goto an8811hb_load_firmware_out; + + } while (--retry); + + ret = en8811h_wait_mcu_ready(phydev); + if (ret < 0) + goto an8811hb_load_firmware_out; + + air_buckpbus_reg_read(phydev, AIR_PHY_MD32FW_VERSION, + &priv->firmware_version); + + debug("MD32 firmware version: %08x\n", priv->firmware_version); + +an8811hb_load_firmware_out: + free(buffer); + if (ret < 0) + dev_err(phydev->dev, "Firmware loading failed: %d\n", ret); + + return ret; +} + +int an8811hb_cko_cfg(struct phy_device *phydev) +{ + ofnode node = phy_get_ofnode(phydev); + u32 pbus_value; + 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); + if (ret < 0) + return ret; + + debug("CKO Output mode - Disabled\n"); + } else { + ret = air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value); + if (ret < 0) + return ret; + + debug("CKO Output %dMHz - Enabled\n", + (pbus_value & AN8811HB_HWTRAP2_CKO) ? 50 : 25); + } + + return ret; +} + static int en8811h_restart_mcu(struct phy_device *phydev) { int ret; @@ -613,13 +982,30 @@ static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol) return 0; } +/** + * air_leds_init - Initialize and configure LEDs for a phy device. + * + * @phydev: Pointer to the phy_device structure. + * @num: Number of LEDs to initialize. + * @dur: Duration for LED blink in milliseconds. It sets the duration + * for both the ON and OFF periods (OFF period will be half of `dur`). + * @mode: LED operation mode. Supported modes are: + * - AIR_LED_MODE_DISABLE: Disables LED control. + * - AIR_LED_MODE_USER_DEFINE: Enables user-defined LED control. + * + * Initializes and configures LEDs on a phy device with a specified blink duration + * and mode. Supports disabling or enabling user-defined control. + * Return: + * On success, returns 0. On error, it returns a negative value that denotes + * the error code. + */ + static int air_leds_init(struct phy_device *phydev, int num, u16 dur, int mode) { struct en8811h_priv *priv = phydev->priv; int ret, i; - ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK, - dur); + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK, dur); if (ret < 0) return ret; @@ -707,10 +1093,121 @@ static int en8811h_config(struct phy_device *phydev) pbus_value |= EN8811H_POLARITY_TX_NORMAL; ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, EN8811H_POLARITY_RX_REVERSE | - EN8811H_POLARITY_TX_NORMAL, pbus_value); + EN8811H_POLARITY_TX_NORMAL, + pbus_value); + if (ret < 0) + return ret; + + ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, + AIR_LED_MODE_USER_DEFINE); + if (ret < 0) { + dev_err(phydev->dev, "Failed to disable leds: %d\n", ret); + return ret; + } + + return 0; +} + +static int an8811hb_config(struct phy_device *phydev) +{ + struct en8811h_priv *priv = phydev->priv; + u32 pbus_value = 0; + ofnode node; + int ret = 0; + + node = phy_get_ofnode(phydev); + if (!ofnode_valid(node)) + return 0; + + /* If restart happened in .probe(), no need to restart now */ + if (priv->mcu_needs_restart) { + ret = an8811hb_mcu_assert(phydev); + if (ret < 0) + return ret; + + ret = an8811hb_mcu_deassert(phydev); + if (ret < 0) + return ret; + + ret = en8811h_restart_mcu(phydev); + if (ret < 0) + return ret; + } else { + ret = an8811hb_load_firmware(phydev); + if (ret) { + dev_err(phydev->dev, "Load firmware fail.\n"); + return ret; + } + /* Next calls to .config() mcu needs to restart */ + priv->mcu_needs_restart = true; + } + + ret = air_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); + if (ret < 0) + return ret; + priv->pkg_sel = (pbus_value & AN8811HB_HWTRAP2_PKG) >> 12; + debug("%s(%d) Version: E%d\n", + priv->pkg_sel ? "AN8811HBCN" : "AN8811HBN", priv->pkg_sel, + priv->pro_id); + + /* Serdes polarity */ + pbus_value = 0; + if (ofnode_read_bool(node, "airoha,pnswap-rx")) + pbus_value &= ~AN8811HB_RX_POLARITY_NORMAL; + else + 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); + if (ret < 0) + return ret; + + pbus_value = 0; + if (ofnode_read_bool(node, "airoha,pnswap-tx")) + pbus_value &= ~AN8811HB_TX_POLARITY_NORMAL; + else + 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); 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); + 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); + 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); + if (ret < 0) + return ret; + } else { + ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT, + AN8811HB_GPIO_OUTPUT_345, + AN8811HB_GPIO_OUTPUT_345); + if (ret < 0) + return ret; + } + ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, AIR_LED_MODE_USER_DEFINE); if (ret < 0) { @@ -718,9 +1215,91 @@ static int en8811h_config(struct phy_device *phydev) return ret; } + /* Co-Clock Output */ + ret = an8811hb_cko_cfg(phydev); + if (ret) + return ret; + + printf("AN8811HB initialize OK !\n"); + + return 0; +} + +static int an8811hb_update_duplex(struct phy_device *phydev) +{ + int lpa; + + if (phydev->autoneg == AUTONEG_ENABLE) { + lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA); + if (lpa < 0) + return lpa; + + switch (phydev->speed) { + case SPEED_2500: + case SPEED_1000: + phydev->duplex = DUPLEX_FULL; + break; + case SPEED_100: + phydev->duplex = (lpa & LPA_100FULL) ? DUPLEX_FULL : + DUPLEX_HALF; + break; + case SPEED_10: + phydev->duplex = (lpa & LPA_10FULL) ? DUPLEX_FULL : + DUPLEX_HALF; + break; + } + } else { + int bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + + if (phydev->speed == SPEED_2500) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : + DUPLEX_HALF; + } + return 0; } +static int an8811hb_parse_status(struct phy_device *phydev) +{ + int ret = 0, reg_value; + + reg_value = phy_read(phydev, MDIO_DEVAD_NONE, AIR_AUX_CTRL_STATUS); + if (reg_value < 0) + return reg_value; + + switch (reg_value & AIR_AUX_CTRL_STATUS_SPEED_MASK) { + case AIR_AUX_CTRL_STATUS_SPEED_2500: + phydev->speed = SPEED_2500; + break; + case AIR_AUX_CTRL_STATUS_SPEED_1000: + phydev->speed = SPEED_1000; + break; + case AIR_AUX_CTRL_STATUS_SPEED_100: + phydev->speed = SPEED_100; + break; + case AIR_AUX_CTRL_STATUS_SPEED_10: + phydev->speed = SPEED_10; + break; + default: + dev_err(phydev->dev, + "Auto-neg error, defaulting to 2500M/FD\n"); + phydev->speed = SPEED_2500; + phydev->duplex = DUPLEX_FULL; + return 0; + } + + /* Update duplex mode based on speed and negotiation status */ + ret = an8811hb_update_duplex(phydev); + if (ret < 0) + return ret; + + debug("Speed: %d, %s duplex\n", phydev->speed, + (phydev->duplex) ? "full" : "half"); + return ret; +} + static int en8811h_parse_status(struct phy_device *phydev) { int ret = 0, reg_value; @@ -742,7 +1321,8 @@ static int en8811h_parse_status(struct phy_device *phydev) phydev->speed = SPEED_100; break; default: - dev_err(phydev->dev, "Auto-neg error, defaulting to 2500M/FD\n"); + dev_err(phydev->dev, + "Auto-neg error, defaulting to 2500M/FD\n"); phydev->speed = SPEED_2500; break; } @@ -752,24 +1332,35 @@ static int en8811h_parse_status(struct phy_device *phydev) static int en8811h_startup(struct phy_device *phydev) { + u32 phy_id = phydev->phy_id; int ret = 0; ret = genphy_update_link(phydev); if (ret) return ret; - return en8811h_parse_status(phydev); + if (phy_id == EN8811H_PHY_ID) + ret = en8811h_parse_status(phydev); + else if (phy_id == AN8811HB_PHY_ID) + ret = an8811hb_parse_status(phydev); + + return ret; } static int en8811h_probe(struct phy_device *phydev) { struct en8811h_priv *priv; + int phy_id; priv = malloc(sizeof(*priv)); if (!priv) return -ENOMEM; memset(priv, 0, sizeof(*priv)); + debug("%s driver is probed.\n", phydev->drv->name); + get_phy_id(phydev->bus, phydev->addr, MDIO_DEVAD_NONE, &phy_id); + debug("phy id is 0x%x.\n", phy_id); + priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0; priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1; priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2; @@ -782,12 +1373,12 @@ static int en8811h_probe(struct phy_device *phydev) return 0; } -static int en8811h_read_page(struct phy_device *phydev) +static int air_phy_read_page(struct phy_device *phydev) { return phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS); } -static int en8811h_write_page(struct phy_device *phydev, int page) +static int air_phy_write_page(struct phy_device *phydev, int page) { return phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page); } @@ -798,8 +1389,20 @@ U_BOOT_PHY_DRIVER(en8811h) = { .mask = 0x0ffffff0, .config = &en8811h_config, .probe = &en8811h_probe, - .read_page = &en8811h_read_page, - .write_page = &en8811h_write_page, + .read_page = &air_phy_read_page, + .write_page = &air_phy_write_page, + .startup = &en8811h_startup, + .shutdown = &genphy_shutdown, +}; + +U_BOOT_PHY_DRIVER(an8811hb) = { + .name = "Airoha AN8811HB", + .uid = AN8811HB_PHY_ID, + .mask = 0x0ffffff0, + .config = &an8811hb_config, + .probe = &en8811h_probe, + .read_page = &air_phy_read_page, + .write_page = &air_phy_write_page, .startup = &en8811h_startup, .shutdown = &genphy_shutdown, }; |
