diff options
-rw-r--r-- | drivers/net/dsa/microchip/ksz_common.c | 7 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/lan937x.h | 2 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/lan937x_main.c | 226 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/lan937x_reg.h | 4 |
4 files changed, 223 insertions, 16 deletions
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 26a4a8b49dc8..19fb090c09ab 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -411,6 +411,8 @@ static const struct ksz_dev_ops lan937x_dev_ops = { .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table, .port_setup = lan937x_port_setup, .set_ageing_time = lan937x_set_ageing_time, + .mdio_bus_preinit = lan937x_mdio_bus_preinit, + .create_phy_addr_map = lan937x_create_phy_addr_map, .r_phy = lan937x_r_phy, .w_phy = lan937x_w_phy, .r_mib_cnt = ksz9477_r_mib_cnt, @@ -1762,6 +1764,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 8, .num_ipms = 8, .tc_cbs_supported = true, + .phy_side_mdio_supported = true, .ops = &lan937x_dev_ops, .phylink_mac_ops = &lan937x_phylink_mac_ops, .mib_names = ksz9477_mib_names, @@ -1790,6 +1793,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 8, .num_ipms = 8, .tc_cbs_supported = true, + .phy_side_mdio_supported = true, .ops = &lan937x_dev_ops, .phylink_mac_ops = &lan937x_phylink_mac_ops, .mib_names = ksz9477_mib_names, @@ -1818,6 +1822,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 8, .num_ipms = 8, .tc_cbs_supported = true, + .phy_side_mdio_supported = true, .ops = &lan937x_dev_ops, .phylink_mac_ops = &lan937x_phylink_mac_ops, .mib_names = ksz9477_mib_names, @@ -1850,6 +1855,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 8, .num_ipms = 8, .tc_cbs_supported = true, + .phy_side_mdio_supported = true, .ops = &lan937x_dev_ops, .phylink_mac_ops = &lan937x_phylink_mac_ops, .mib_names = ksz9477_mib_names, @@ -1882,6 +1888,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 8, .num_ipms = 8, .tc_cbs_supported = true, + .phy_side_mdio_supported = true, .ops = &lan937x_dev_ops, .phylink_mac_ops = &lan937x_phylink_mac_ops, .mib_names = ksz9477_mib_names, diff --git a/drivers/net/dsa/microchip/lan937x.h b/drivers/net/dsa/microchip/lan937x.h index 3388d91dbc44..df13ebbd356f 100644 --- a/drivers/net/dsa/microchip/lan937x.h +++ b/drivers/net/dsa/microchip/lan937x.h @@ -13,6 +13,8 @@ void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port); void lan937x_config_cpu_port(struct dsa_switch *ds); int lan937x_switch_init(struct ksz_device *dev); void lan937x_switch_exit(struct ksz_device *dev); +int lan937x_mdio_bus_preinit(struct ksz_device *dev, bool side_mdio); +int lan937x_create_phy_addr_map(struct ksz_device *dev, bool side_mdio); int lan937x_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data); int lan937x_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val); int lan937x_change_mtu(struct ksz_device *dev, int port, int new_mtu); diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c index 824d9309a3d3..b7652efd632e 100644 --- a/drivers/net/dsa/microchip/lan937x_main.c +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -18,6 +18,87 @@ #include "ksz9477.h" #include "lan937x.h" +/* marker for ports without built-in PHY */ +#define LAN937X_NO_PHY U8_MAX + +/* + * lan9370_phy_addr - Mapping of LAN9370 switch ports to PHY addresses. + * + * Each entry corresponds to a specific port on the LAN9370 switch, + * where ports 1-4 are connected to integrated 100BASE-T1 PHYs, and + * Port 5 is connected to an RGMII interface without a PHY. The values + * are based on the documentation (DS00003108E, section 3.3). + */ +static const u8 lan9370_phy_addr[] = { + [0] = 2, /* Port 1, T1 AFE0 */ + [1] = 3, /* Port 2, T1 AFE1 */ + [2] = 5, /* Port 3, T1 AFE3 */ + [3] = 6, /* Port 4, T1 AFE4 */ + [4] = LAN937X_NO_PHY, /* Port 5, RGMII 2 */ +}; + +/* + * lan9371_phy_addr - Mapping of LAN9371 switch ports to PHY addresses. + * + * The values are based on the documentation (DS00003109E, section 3.3). + */ +static const u8 lan9371_phy_addr[] = { + [0] = 2, /* Port 1, T1 AFE0 */ + [1] = 3, /* Port 2, T1 AFE1 */ + [2] = 5, /* Port 3, T1 AFE3 */ + [3] = 8, /* Port 4, TX PHY */ + [4] = LAN937X_NO_PHY, /* Port 5, RGMII 2 */ + [5] = LAN937X_NO_PHY, /* Port 6, RGMII 1 */ +}; + +/* + * lan9372_phy_addr - Mapping of LAN9372 switch ports to PHY addresses. + * + * The values are based on the documentation (DS00003110F, section 3.3). + */ +static const u8 lan9372_phy_addr[] = { + [0] = 2, /* Port 1, T1 AFE0 */ + [1] = 3, /* Port 2, T1 AFE1 */ + [2] = 5, /* Port 3, T1 AFE3 */ + [3] = 8, /* Port 4, TX PHY */ + [4] = LAN937X_NO_PHY, /* Port 5, RGMII 2 */ + [5] = LAN937X_NO_PHY, /* Port 6, RGMII 1 */ + [6] = 6, /* Port 7, T1 AFE4 */ + [7] = 4, /* Port 8, T1 AFE2 */ +}; + +/* + * lan9373_phy_addr - Mapping of LAN9373 switch ports to PHY addresses. + * + * The values are based on the documentation (DS00003110F, section 3.3). + */ +static const u8 lan9373_phy_addr[] = { + [0] = 2, /* Port 1, T1 AFE0 */ + [1] = 3, /* Port 2, T1 AFE1 */ + [2] = 5, /* Port 3, T1 AFE3 */ + [3] = LAN937X_NO_PHY, /* Port 4, SGMII */ + [4] = LAN937X_NO_PHY, /* Port 5, RGMII 2 */ + [5] = LAN937X_NO_PHY, /* Port 6, RGMII 1 */ + [6] = 6, /* Port 7, T1 AFE4 */ + [7] = 4, /* Port 8, T1 AFE2 */ +}; + +/* + * lan9374_phy_addr - Mapping of LAN9374 switch ports to PHY addresses. + * + * The values are based on the documentation (DS00003110F, section 3.3). + */ +static const u8 lan9374_phy_addr[] = { + [0] = 2, /* Port 1, T1 AFE0 */ + [1] = 3, /* Port 2, T1 AFE1 */ + [2] = 5, /* Port 3, T1 AFE3 */ + [3] = 7, /* Port 4, T1 AFE5 */ + [4] = LAN937X_NO_PHY, /* Port 5, RGMII 2 */ + [5] = LAN937X_NO_PHY, /* Port 6, RGMII 1 */ + [6] = 6, /* Port 7, T1 AFE4 */ + [7] = 4, /* Port 8, T1 AFE2 */ +}; + static int lan937x_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) { return regmap_update_bits(ksz_regmap_8(dev), addr, bits, set ? bits : 0); @@ -30,24 +111,144 @@ static int lan937x_port_cfg(struct ksz_device *dev, int port, int offset, bits, set ? bits : 0); } -static int lan937x_enable_spi_indirect_access(struct ksz_device *dev) +/** + * lan937x_create_phy_addr_map - Create port-to-PHY address map for MDIO bus. + * @dev: Pointer to device structure. + * @side_mdio: Boolean indicating if the PHYs are accessed over a side MDIO bus. + * + * This function sets up the PHY address mapping for the LAN937x switches, + * which support two access modes for internal PHYs: + * 1. **SPI Access**: A straightforward one-to-one port-to-PHY address + * mapping is applied. + * 2. **MDIO Access**: The PHY address mapping varies based on chip variant + * and strap configuration. An offset is calculated based on strap settings + * to ensure correct PHY addresses are assigned. The offset calculation logic + * is based on Microchip's Article Number 000015828, available at: + * https://microchip.my.site.com/s/article/LAN9374-Virtual-PHY-PHY-Address-Mapping + * + * The function first checks if side MDIO access is disabled, in which case a + * simple direct mapping (port number = PHY address) is applied. If side MDIO + * access is enabled, it reads the strap configuration to determine the correct + * offset for PHY addresses. + * + * The appropriate mapping table is selected based on the chip ID, and the + * `phy_addr_map` is populated with the correct addresses for each port. Any + * port with no PHY is assigned a `LAN937X_NO_PHY` marker. + * + * Return: 0 on success, error code on failure. + */ +int lan937x_create_phy_addr_map(struct ksz_device *dev, bool side_mdio) +{ + static const u8 *phy_addr_map; + u32 strap_val; + u8 offset = 0; + size_t size; + int ret, i; + + if (!side_mdio) { + /* simple direct mapping */ + for (i = 0; i < dev->info->port_cnt; i++) + dev->phy_addr_map[i] = i; + + return 0; + } + + ret = ksz_read32(dev, REG_SW_CFG_STRAP_VAL, &strap_val); + if (ret < 0) + return ret; + + if (!(strap_val & SW_CASCADE_ID_CFG) && !(strap_val & SW_VPHY_ADD_CFG)) + offset = 0; + else if (!(strap_val & SW_CASCADE_ID_CFG) && (strap_val & SW_VPHY_ADD_CFG)) + offset = 7; + else if ((strap_val & SW_CASCADE_ID_CFG) && !(strap_val & SW_VPHY_ADD_CFG)) + offset = 15; + else + offset = 22; + + switch (dev->info->chip_id) { + case LAN9370_CHIP_ID: + phy_addr_map = lan9370_phy_addr; + size = ARRAY_SIZE(lan9370_phy_addr); + break; + case LAN9371_CHIP_ID: + phy_addr_map = lan9371_phy_addr; + size = ARRAY_SIZE(lan9371_phy_addr); + break; + case LAN9372_CHIP_ID: + phy_addr_map = lan9372_phy_addr; + size = ARRAY_SIZE(lan9372_phy_addr); + break; + case LAN9373_CHIP_ID: + phy_addr_map = lan9373_phy_addr; + size = ARRAY_SIZE(lan9373_phy_addr); + break; + case LAN9374_CHIP_ID: + phy_addr_map = lan9374_phy_addr; + size = ARRAY_SIZE(lan9374_phy_addr); + break; + default: + return -EINVAL; + } + + if (size < dev->info->port_cnt) + return -EINVAL; + + for (i = 0; i < dev->info->port_cnt; i++) { + if (phy_addr_map[i] == LAN937X_NO_PHY) + dev->phy_addr_map[i] = phy_addr_map[i]; + else + dev->phy_addr_map[i] = phy_addr_map[i] + offset; + } + + return 0; +} + +/** + * lan937x_mdio_bus_preinit - Pre-initialize MDIO bus for accessing PHYs. + * @dev: Pointer to device structure. + * @side_mdio: Boolean indicating if the PHYs are accessed over a side MDIO bus. + * + * This function configures the LAN937x switch for PHY access either through + * SPI or the side MDIO bus, unlocking the necessary registers for each access + * mode. + * + * Operation Modes: + * 1. **SPI Access**: Enables SPI indirect access to address clock domain + * crossing issues when SPI is used for PHY access. + * 2. **MDIO Access**: Grants access to internal PHYs over the side MDIO bus, + * required when using the MDIO bus for PHY management. + * + * Return: 0 on success, error code on failure. + */ +int lan937x_mdio_bus_preinit(struct ksz_device *dev, bool side_mdio) { u16 data16; int ret; - /* Enable Phy access through SPI */ + /* Unlock access to the PHYs, needed for SPI and side MDIO access */ ret = lan937x_cfg(dev, REG_GLOBAL_CTRL_0, SW_PHY_REG_BLOCK, false); if (ret < 0) - return ret; + goto print_error; - ret = ksz_read16(dev, REG_VPHY_SPECIAL_CTRL__2, &data16); - if (ret < 0) - return ret; + if (side_mdio) + /* Allow access to internal PHYs over MDIO bus */ + data16 = VPHY_MDIO_INTERNAL_ENABLE; + else + /* Enable SPI indirect access to address clock domain crossing + * issue + */ + data16 = VPHY_SPI_INDIRECT_ENABLE; - /* Allow SPI access */ - data16 |= VPHY_SPI_INDIRECT_ENABLE; + ret = ksz_rmw16(dev, REG_VPHY_SPECIAL_CTRL__2, + VPHY_SPI_INDIRECT_ENABLE | VPHY_MDIO_INTERNAL_ENABLE, + data16); + +print_error: + if (ret < 0) + dev_err(dev->dev, "failed to preinit the MDIO bus\n"); - return ksz_write16(dev, REG_VPHY_SPECIAL_CTRL__2, data16); + return ret; } static int lan937x_vphy_ind_addr_wr(struct ksz_device *dev, int addr, int reg) @@ -363,13 +564,6 @@ int lan937x_setup(struct dsa_switch *ds) struct ksz_device *dev = ds->priv; int ret; - /* enable Indirect Access from SPI to the VPHY registers */ - ret = lan937x_enable_spi_indirect_access(dev); - if (ret < 0) { - dev_err(dev->dev, "failed to enable spi indirect access"); - return ret; - } - /* The VLAN aware is a global setting. Mixed vlan * filterings are not supported. */ diff --git a/drivers/net/dsa/microchip/lan937x_reg.h b/drivers/net/dsa/microchip/lan937x_reg.h index 2f22a9d01de3..4ec93e421da4 100644 --- a/drivers/net/dsa/microchip/lan937x_reg.h +++ b/drivers/net/dsa/microchip/lan937x_reg.h @@ -37,6 +37,10 @@ #define SW_CLK125_ENB BIT(1) #define SW_CLK25_ENB BIT(0) +#define REG_SW_CFG_STRAP_VAL 0x0200 +#define SW_CASCADE_ID_CFG BIT(15) +#define SW_VPHY_ADD_CFG BIT(0) + /* 2 - PHY Control */ #define REG_SW_CFG_STRAP_OVR 0x0214 #define SW_VPHY_DISABLE BIT(31) |