Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: dsa: microchip: Refactor MDIO handling for side MDIO access

Add support for accessing PHYs via a side MDIO interface in LAN937x
switches. The existing code already supports accessing PHYs via main
management interfaces, which can be SPI, I2C, or MDIO, depending on the
chip variant. This patch enables using a side MDIO bus, where SPI is
used for the main switch configuration and MDIO for managing the
integrated PHYs. On LAN937x, this is optional, allowing them to operate
in both configurations: SPI only, or SPI + MDIO. Typically, the SPI
interface is used for switch configuration, while MDIO handles PHY
management.

Additionally, update interrupt controller code to support non-linear
port to PHY address mapping, enabling correct interrupt handling for
configurations where PHY addresses do not directly correspond to port
indexes. This change ensures that the interrupt mechanism properly
aligns with the new, flexible PHY address mappings introduced by side
MDIO support.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20241106075942.1636998-4-o.rempel@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Oleksij Rempel and committed by
Jakub Kicinski
9afaf0ee 698b20a6

+224 -10
+165 -10
drivers/net/dsa/microchip/ksz_common.c
··· 2236 2236 return dev->dev_ops->w_phy(dev, addr, regnum, val); 2237 2237 } 2238 2238 2239 + /** 2240 + * ksz_parent_mdio_read - Read data from a PHY register on the parent MDIO bus. 2241 + * @bus: MDIO bus structure. 2242 + * @addr: PHY address on the parent MDIO bus. 2243 + * @regnum: Register number to read. 2244 + * 2245 + * This function provides a direct read operation on the parent MDIO bus for 2246 + * accessing PHY registers. By bypassing SPI or I2C, it uses the parent MDIO bus 2247 + * to retrieve data from the PHY registers at the specified address and register 2248 + * number. 2249 + * 2250 + * Return: Value of the PHY register, or a negative error code on failure. 2251 + */ 2252 + static int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum) 2253 + { 2254 + struct ksz_device *dev = bus->priv; 2255 + 2256 + return mdiobus_read_nested(dev->parent_mdio_bus, addr, regnum); 2257 + } 2258 + 2259 + /** 2260 + * ksz_parent_mdio_write - Write data to a PHY register on the parent MDIO bus. 2261 + * @bus: MDIO bus structure. 2262 + * @addr: PHY address on the parent MDIO bus. 2263 + * @regnum: Register number to write to. 2264 + * @val: Value to write to the PHY register. 2265 + * 2266 + * This function provides a direct write operation on the parent MDIO bus for 2267 + * accessing PHY registers. Bypassing SPI or I2C, it uses the parent MDIO bus 2268 + * to modify the PHY register values at the specified address. 2269 + * 2270 + * Return: 0 on success, or a negative error code on failure. 2271 + */ 2272 + static int ksz_parent_mdio_write(struct mii_bus *bus, int addr, int regnum, 2273 + u16 val) 2274 + { 2275 + struct ksz_device *dev = bus->priv; 2276 + 2277 + return mdiobus_write_nested(dev->parent_mdio_bus, addr, regnum, val); 2278 + } 2279 + 2280 + /** 2281 + * ksz_phy_addr_to_port - Map a PHY address to the corresponding switch port. 2282 + * @dev: Pointer to device structure. 2283 + * @addr: PHY address to map to a port. 2284 + * 2285 + * This function finds the corresponding switch port for a given PHY address by 2286 + * iterating over all user ports on the device. It checks if a port's PHY 2287 + * address in `phy_addr_map` matches the specified address and if the port 2288 + * contains an internal PHY. If a match is found, the index of the port is 2289 + * returned. 2290 + * 2291 + * Return: Port index on success, or -EINVAL if no matching port is found. 2292 + */ 2293 + static int ksz_phy_addr_to_port(struct ksz_device *dev, int addr) 2294 + { 2295 + struct dsa_switch *ds = dev->ds; 2296 + struct dsa_port *dp; 2297 + 2298 + dsa_switch_for_each_user_port(dp, ds) { 2299 + if (dev->info->internal_phy[dp->index] && 2300 + dev->phy_addr_map[dp->index] == addr) 2301 + return dp->index; 2302 + } 2303 + 2304 + return -EINVAL; 2305 + } 2306 + 2307 + /** 2308 + * ksz_irq_phy_setup - Configure IRQs for PHYs in the KSZ device. 2309 + * @dev: Pointer to the KSZ device structure. 2310 + * 2311 + * Sets up IRQs for each active PHY connected to the KSZ switch by mapping the 2312 + * appropriate IRQs for each PHY and assigning them to the `user_mii_bus` in 2313 + * the DSA switch structure. Each IRQ is mapped based on the port's IRQ domain. 2314 + * 2315 + * Return: 0 on success, or a negative error code on failure. 2316 + */ 2239 2317 static int ksz_irq_phy_setup(struct ksz_device *dev) 2240 2318 { 2241 2319 struct dsa_switch *ds = dev->ds; 2242 - int phy; 2320 + int phy, port; 2243 2321 int irq; 2244 2322 int ret; 2245 2323 2246 - for (phy = 0; phy < KSZ_MAX_NUM_PORTS; phy++) { 2324 + for (phy = 0; phy < PHY_MAX_ADDR; phy++) { 2247 2325 if (BIT(phy) & ds->phys_mii_mask) { 2248 - irq = irq_find_mapping(dev->ports[phy].pirq.domain, 2326 + port = ksz_phy_addr_to_port(dev, phy); 2327 + if (port < 0) { 2328 + ret = port; 2329 + goto out; 2330 + } 2331 + 2332 + irq = irq_find_mapping(dev->ports[port].pirq.domain, 2249 2333 PORT_SRC_PHY_INT); 2250 2334 if (irq < 0) { 2251 2335 ret = irq; ··· 2347 2263 return ret; 2348 2264 } 2349 2265 2266 + /** 2267 + * ksz_irq_phy_free - Release IRQ mappings for PHYs in the KSZ device. 2268 + * @dev: Pointer to the KSZ device structure. 2269 + * 2270 + * Releases any IRQ mappings previously assigned to active PHYs in the KSZ 2271 + * switch by disposing of each mapped IRQ in the `user_mii_bus` structure. 2272 + */ 2350 2273 static void ksz_irq_phy_free(struct ksz_device *dev) 2351 2274 { 2352 2275 struct dsa_switch *ds = dev->ds; 2353 2276 int phy; 2354 2277 2355 - for (phy = 0; phy < KSZ_MAX_NUM_PORTS; phy++) 2278 + for (phy = 0; phy < PHY_MAX_ADDR; phy++) 2356 2279 if (BIT(phy) & ds->phys_mii_mask) 2357 2280 irq_dispose_mapping(ds->user_mii_bus->irq[phy]); 2358 2281 } 2359 2282 2283 + /** 2284 + * ksz_mdio_register - Register and configure the MDIO bus for the KSZ device. 2285 + * @dev: Pointer to the KSZ device structure. 2286 + * 2287 + * This function sets up and registers an MDIO bus for the KSZ switch device, 2288 + * allowing access to its internal PHYs. If the device supports side MDIO, 2289 + * the function will configure the external MDIO controller specified by the 2290 + * "mdio-parent-bus" device tree property to directly manage internal PHYs. 2291 + * Otherwise, SPI or I2C access is set up for PHY access. 2292 + * 2293 + * Return: 0 on success, or a negative error code on failure. 2294 + */ 2360 2295 static int ksz_mdio_register(struct ksz_device *dev) 2361 2296 { 2297 + struct device_node *parent_bus_node; 2298 + struct mii_bus *parent_bus = NULL; 2362 2299 struct dsa_switch *ds = dev->ds; 2363 2300 struct device_node *mdio_np; 2364 2301 struct mii_bus *bus; 2365 - int ret; 2302 + struct dsa_port *dp; 2303 + int ret, i; 2366 2304 2367 2305 mdio_np = of_get_child_by_name(dev->dev->of_node, "mdio"); 2368 2306 if (!mdio_np) 2369 2307 return 0; 2308 + 2309 + parent_bus_node = of_parse_phandle(mdio_np, "mdio-parent-bus", 0); 2310 + if (parent_bus_node && !dev->info->phy_side_mdio_supported) { 2311 + dev_err(dev->dev, "Side MDIO bus is not supported for this HW, ignoring 'mdio-parent-bus' property.\n"); 2312 + ret = -EINVAL; 2313 + 2314 + goto put_mdio_node; 2315 + } else if (parent_bus_node) { 2316 + parent_bus = of_mdio_find_bus(parent_bus_node); 2317 + if (!parent_bus) { 2318 + ret = -EPROBE_DEFER; 2319 + 2320 + goto put_mdio_node; 2321 + } 2322 + 2323 + dev->parent_mdio_bus = parent_bus; 2324 + } 2370 2325 2371 2326 bus = devm_mdiobus_alloc(ds->dev); 2372 2327 if (!bus) { ··· 2413 2290 return -ENOMEM; 2414 2291 } 2415 2292 2293 + if (dev->dev_ops->mdio_bus_preinit) { 2294 + ret = dev->dev_ops->mdio_bus_preinit(dev, !!parent_bus); 2295 + if (ret) 2296 + goto put_mdio_node; 2297 + } 2298 + 2299 + if (dev->dev_ops->create_phy_addr_map) { 2300 + ret = dev->dev_ops->create_phy_addr_map(dev, !!parent_bus); 2301 + if (ret) 2302 + goto put_mdio_node; 2303 + } else { 2304 + for (i = 0; i < dev->info->port_cnt; i++) 2305 + dev->phy_addr_map[i] = i; 2306 + } 2307 + 2416 2308 bus->priv = dev; 2417 - bus->read = ksz_sw_mdio_read; 2418 - bus->write = ksz_sw_mdio_write; 2419 - bus->name = "ksz user smi"; 2420 - snprintf(bus->id, MII_BUS_ID_SIZE, "SMI-%d", ds->index); 2309 + if (parent_bus) { 2310 + bus->read = ksz_parent_mdio_read; 2311 + bus->write = ksz_parent_mdio_write; 2312 + bus->name = "KSZ side MDIO"; 2313 + snprintf(bus->id, MII_BUS_ID_SIZE, "ksz-side-mdio-%d", 2314 + ds->index); 2315 + } else { 2316 + bus->read = ksz_sw_mdio_read; 2317 + bus->write = ksz_sw_mdio_write; 2318 + bus->name = "ksz user smi"; 2319 + snprintf(bus->id, MII_BUS_ID_SIZE, "SMI-%d", ds->index); 2320 + } 2321 + 2322 + dsa_switch_for_each_user_port(dp, dev->ds) { 2323 + if (dev->info->internal_phy[dp->index] && 2324 + dev->phy_addr_map[dp->index] < PHY_MAX_ADDR) 2325 + bus->phy_mask |= BIT(dev->phy_addr_map[dp->index]); 2326 + } 2327 + 2328 + ds->phys_mii_mask = bus->phy_mask; 2421 2329 bus->parent = ds->dev; 2422 - bus->phy_mask = ~ds->phys_mii_mask; 2423 2330 2424 2331 ds->user_mii_bus = bus; 2425 2332 ··· 2469 2316 ksz_irq_phy_free(dev); 2470 2317 } 2471 2318 2319 + put_mdio_node: 2472 2320 of_node_put(mdio_np); 2321 + of_node_put(parent_bus_node); 2473 2322 2474 2323 return ret; 2475 2324 }
+59
drivers/net/dsa/microchip/ksz_common.h
··· 65 65 u8 num_tx_queues; 66 66 u8 num_ipms; /* number of Internal Priority Maps */ 67 67 bool tc_cbs_supported; 68 + 69 + /** 70 + * @phy_side_mdio_supported: Indicates if the chip supports an additional 71 + * side MDIO channel for accessing integrated PHYs. 72 + */ 73 + bool phy_side_mdio_supported; 68 74 const struct ksz_dev_ops *ops; 69 75 const struct phylink_mac_ops *phylink_mac_ops; 70 76 bool phy_errata_9477; ··· 197 191 struct ksz_switch_macaddr *switch_macaddr; 198 192 struct net_device *hsr_dev; /* HSR */ 199 193 u8 hsr_ports; 194 + 195 + /** 196 + * @phy_addr_map: Array mapping switch ports to their corresponding PHY 197 + * addresses. 198 + */ 199 + u8 phy_addr_map[KSZ_MAX_NUM_PORTS]; 200 + 201 + /** 202 + * @parent_mdio_bus: Pointer to the external MDIO bus controller. 203 + * 204 + * This points to an external MDIO bus controller that is used to access 205 + * the PHYs integrated within the switch. Unlike an integrated MDIO 206 + * bus, this external controller provides a direct path for managing 207 + * the switch’s internal PHYs, bypassing the main SPI interface. 208 + */ 209 + struct mii_bus *parent_mdio_bus; 200 210 }; 201 211 202 212 /* List of supported models */ ··· 348 326 void (*port_cleanup)(struct ksz_device *dev, int port); 349 327 void (*port_setup)(struct ksz_device *dev, int port, bool cpu_port); 350 328 int (*set_ageing_time)(struct ksz_device *dev, unsigned int msecs); 329 + 330 + /** 331 + * @mdio_bus_preinit: Function pointer to pre-initialize the MDIO bus 332 + * for accessing PHYs. 333 + * @dev: Pointer to device structure. 334 + * @side_mdio: Boolean indicating if the PHYs are accessed over a side 335 + * MDIO bus. 336 + * 337 + * This function pointer is used to configure the MDIO bus for PHY 338 + * access before initiating regular PHY operations. It enables either 339 + * SPI/I2C or side MDIO access modes by unlocking necessary registers 340 + * and setting up access permissions for the selected mode. 341 + * 342 + * Return: 343 + * - 0 on success. 344 + * - Negative error code on failure. 345 + */ 346 + int (*mdio_bus_preinit)(struct ksz_device *dev, bool side_mdio); 347 + 348 + /** 349 + * @create_phy_addr_map: Function pointer to create a port-to-PHY 350 + * address map. 351 + * @dev: Pointer to device structure. 352 + * @side_mdio: Boolean indicating if the PHYs are accessed over a side 353 + * MDIO bus. 354 + * 355 + * This function pointer is responsible for mapping switch ports to PHY 356 + * addresses according to the configured access mode (SPI or side MDIO) 357 + * and the device’s strap configuration. The mapping setup may vary 358 + * depending on the chip variant and configuration. Ensures the correct 359 + * address mapping for PHY communication. 360 + * 361 + * Return: 362 + * - 0 on success. 363 + * - Negative error code on failure (e.g., invalid configuration). 364 + */ 365 + int (*create_phy_addr_map)(struct ksz_device *dev, bool side_mdio); 351 366 int (*r_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 *val); 352 367 int (*w_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 val); 353 368 void (*r_mib_cnt)(struct ksz_device *dev, int port, u16 addr,