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

Merge branch 'rework-genet-mdioclocking'

Florian Fainelli says:

====================
Rework GENET MDIO controller clocking

This patch series reworks the way that we manage the GENET MDIO
controller clocks around I/O accesses. During testing with a fully
modular build where bcmgenet, mdio-bcm-unimac, and the Broadcom PHY
driver (broadcom) are all loaded as modules, with no particular care
being taken to order them to mimize deferred probing the following bus
error was obtained:

[ 4.344831] printk: console [ttyS0] enabled
[ 4.351102] 840d000.serial: ttyS1 at MMIO 0x840d000 (irq = 29, base_baud = 5062500) is a Broadcom BCM7271 UART
[ 4.363110] 840e000.serial: ttyS2 at MMIO 0x840e000 (irq = 30, base_baud = 5062500) is a Broadcom BCM7271 UART
[ 4.387392] iproc-rng200 8402000.rng: hwrng registered
[ 4.398012] Consider using thermal netlink events interface
[ 4.403717] brcmstb_thermal a581500.thermal: registered AVS TMON of-sensor driver
[ 4.440085] bcmgenet 8f00000.ethernet: GENET 5.0 EPHY: 0x0000
[ 4.482526] unimac-mdio unimac-mdio.0: Broadcom UniMAC MDIO bus
[ 4.514019] bridge: filtering via arp/ip/ip6tables is no longer available by default. Update your scripts to load br_netfilter if you need this.
[ 4.551304] SError Interrupt on CPU2, code 0x00000000bf000002 -- SError
[ 4.551324] CPU: 2 PID: 8 Comm: kworker/u8:0 Not tainted 6.1.53-0.1pre-g5a26d98e908c #2
[ 4.551330] Hardware name: BCM972180HB_V20 (DT)
[ 4.551336] Workqueue: events_unbound deferred_probe_work_func
[ 4.551363] pstate: 00000005 (nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 4.551368] pc : el1_abort+0x2c/0x58
[ 4.551376] lr : el1_abort+0x20/0x58
[ 4.551379] sp : ffffffc00a383960
[ 4.551380] x29: ffffffc00a383960 x28: ffffff80029fd780 x27: 0000000000000000
[ 4.551385] x26: 0000000000000000 x25: ffffff8002839005 x24: ffffffc00a1f9bd0
[ 4.551390] x23: 0000000040000005 x22: ffffffc000a48084 x21: ffffffc00a3dde14
[ 4.551394] x20: 0000000096000210 x19: ffffffc00a3839a0 x18: 0000000000000579
[ 4.551399] x17: 0000000000000000 x16: 0000000100000000 x15: ffffffc00a3838c0
[ 4.551403] x14: 000000000000000a x13: 6e69622f7273752f x12: 3a6e6962732f7273
[ 4.551408] x11: 752f3a6e69622f3a x10: 6e6962732f3d4854 x9 : ffffffc0086466a8
[ 4.551412] x8 : ffffff80049ee100 x7 : ffffff8003231938 x6 : 0000000000000000
[ 4.551416] x5 : 0000002200000000 x4 : ffffffc00a3839a0 x3 : 0000002000000000
[ 4.551420] x2 : 0000000000000025 x1 : 0000000096000210 x0 : 0000000000000000
[ 4.551429] Kernel panic - not syncing: Asynchronous SError Interrupt
[ 4.551432] CPU: 2 PID: 8 Comm: kworker/u8:0 Not tainted 6.1.53-0.1pre-g5a26d98e908c #2
[ 4.551435] Hardware name: BCM972180HB_V20 (DT)
[ 4.551437] Workqueue: events_unbound deferred_probe_work_func
[ 4.551443] Call trace:
[ 4.551445] dump_backtrace+0xe4/0x124
[ 4.551452] show_stack+0x1c/0x28
[ 4.551455] dump_stack_lvl+0x60/0x78
[ 4.551462] dump_stack+0x14/0x2c
[ 4.551467] panic+0x134/0x304
[ 4.551472] nmi_panic+0x50/0x70
[ 4.551480] arm64_serror_panic+0x70/0x7c
[ 4.551484] do_serror+0x2c/0x5c
[ 4.551487] el1h_64_error_handler+0x2c/0x40
[ 4.551491] el1h_64_error+0x64/0x68
[ 4.551496] el1_abort+0x2c/0x58
[ 4.551499] el1h_64_sync_handler+0x8c/0xb4
[ 4.551502] el1h_64_sync+0x64/0x68
[ 4.551505] unimac_mdio_readl.isra.0+0x4/0xc [mdio_bcm_unimac]
[ 4.551519] __mdiobus_read+0x2c/0x88
[ 4.551526] mdiobus_read+0x40/0x60
[ 4.551530] phy_read+0x18/0x20
[ 4.551534] bcm_phy_config_intr+0x20/0x84
[ 4.551537] phy_disable_interrupts+0x2c/0x3c
[ 4.551543] phy_probe+0x80/0x1b0
[ 4.551545] really_probe+0x1b8/0x390
[ 4.551550] __driver_probe_device+0x134/0x14c
[ 4.551554] driver_probe_device+0x40/0xf8
[ 4.551559] __device_attach_driver+0x108/0x11c
[ 4.551563] bus_for_each_drv+0xa4/0xcc
[ 4.551567] __device_attach+0xdc/0x190
[ 4.551571] device_initial_probe+0x18/0x20
[ 4.551575] bus_probe_device+0x34/0x94
[ 4.551579] deferred_probe_work_func+0xd4/0xe8
[ 4.551583] process_one_work+0x1ac/0x25c
[ 4.551590] worker_thread+0x1f4/0x260
[ 4.551595] kthread+0xc0/0xd0
[ 4.551600] ret_from_fork+0x10/0x20
[ 4.551608] SMP: stopping secondary CPUs
[ 4.551617] Kernel Offset: disabled
[ 4.551619] CPU features: 0x00000,00c00080,0000420b
[ 4.551622] Memory Limit: none
[ 4.833838] ---[ end Kernel panic - not syncing: Asynchronous SError Interrupt ]---

The issue here is that we managed to probe the GENET controller, the
mdio-bcm-unimac MDIO controller, but the PHY was still being held in a
probe deferral state because it depended upon a GPIO controller provider
not loaded yet. As soon as that provider is loaded however, the PHY
continues to probe, tries to disable the interrupts, and this causes a
MDIO transaction. That MDIO transaction requires I/O register accesses
within the GENET's larger block, and since its clocks are turned off,
the CPU gets a bus error signaled as a System Error.

The patch series takes the simplest approach of keeping the clocks
enabled just for the duration of the I/O accesses. This is also
beneficial to other drivers like bcmasp2 which make use of the same MDIO
controller driver.

Changes in v2:

- added missing ret assignment in the if (IS_ERR(priv->clk)) branch

- added Jacob's R-by tags

- corrected the commit ID being reverted in patch #3
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+57 -45
+4 -2
drivers/net/ethernet/broadcom/genet/bcmmii.c
··· 476 476 ppd.wait_func = bcmgenet_mii_wait; 477 477 ppd.wait_func_data = priv; 478 478 ppd.bus_name = "bcmgenet MII bus"; 479 + /* Pass a reference to our "main" clock which is used for MDIO 480 + * transfers 481 + */ 482 + ppd.clk = priv->clk; 479 483 480 484 /* Unimac MDIO bus controller starts at UniMAC offset + MDIO_CMD 481 485 * and is 2 * 32-bits word long, 8 bytes total. ··· 678 674 if (of_phy_is_fixed_link(dn)) 679 675 of_phy_deregister_fixed_link(dn); 680 676 of_node_put(priv->phy_dn); 681 - clk_prepare_enable(priv->clk); 682 677 platform_device_unregister(priv->mii_pdev); 683 - clk_disable_unprepare(priv->clk); 684 678 }
+50 -43
drivers/net/mdio/mdio-bcm-unimac.c
··· 94 94 int ret; 95 95 u32 cmd; 96 96 97 + ret = clk_prepare_enable(priv->clk); 98 + if (ret) 99 + return ret; 100 + 97 101 /* Prepare the read operation */ 98 102 cmd = MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT); 99 103 unimac_mdio_writel(priv, cmd, MDIO_CMD); ··· 107 103 108 104 ret = priv->wait_func(priv->wait_func_data); 109 105 if (ret) 110 - return ret; 106 + goto out; 111 107 112 108 cmd = unimac_mdio_readl(priv, MDIO_CMD); 113 109 ··· 116 112 * that condition here and ignore the MDIO controller read failure 117 113 * indication. 118 114 */ 119 - if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (cmd & MDIO_READ_FAIL)) 120 - return -EIO; 115 + if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (cmd & MDIO_READ_FAIL)) { 116 + ret = -EIO; 117 + goto out; 118 + } 121 119 122 - return cmd & 0xffff; 120 + ret = cmd & 0xffff; 121 + out: 122 + clk_disable_unprepare(priv->clk); 123 + return ret; 123 124 } 124 125 125 126 static int unimac_mdio_write(struct mii_bus *bus, int phy_id, ··· 132 123 { 133 124 struct unimac_mdio_priv *priv = bus->priv; 134 125 u32 cmd; 126 + int ret; 127 + 128 + ret = clk_prepare_enable(priv->clk); 129 + if (ret) 130 + return ret; 135 131 136 132 /* Prepare the write operation */ 137 133 cmd = MDIO_WR | (phy_id << MDIO_PMD_SHIFT) | ··· 145 131 146 132 unimac_mdio_start(priv); 147 133 148 - return priv->wait_func(priv->wait_func_data); 134 + ret = priv->wait_func(priv->wait_func_data); 135 + clk_disable_unprepare(priv->clk); 136 + 137 + return ret; 149 138 } 150 139 151 140 /* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with ··· 195 178 return 0; 196 179 } 197 180 198 - static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv) 181 + static int unimac_mdio_clk_set(struct unimac_mdio_priv *priv) 199 182 { 200 183 unsigned long rate; 201 184 u32 reg, div; 185 + int ret; 202 186 203 187 /* Keep the hardware default values */ 204 188 if (!priv->clk_freq) 205 - return; 189 + return 0; 190 + 191 + ret = clk_prepare_enable(priv->clk); 192 + if (ret) 193 + return ret; 206 194 207 195 if (!priv->clk) 208 196 rate = 250000000; ··· 217 195 div = (rate / (2 * priv->clk_freq)) - 1; 218 196 if (div & ~MDIO_CLK_DIV_MASK) { 219 197 pr_warn("Incorrect MDIO clock frequency, ignoring\n"); 220 - return; 198 + ret = 0; 199 + goto out; 221 200 } 222 201 223 202 /* The MDIO clock is the reference clock (typically 250Mhz) divided by ··· 228 205 reg &= ~(MDIO_CLK_DIV_MASK << MDIO_CLK_DIV_SHIFT); 229 206 reg |= div << MDIO_CLK_DIV_SHIFT; 230 207 unimac_mdio_writel(priv, reg, MDIO_CFG); 208 + out: 209 + clk_disable_unprepare(priv->clk); 210 + return ret; 231 211 } 232 212 233 213 static int unimac_mdio_probe(struct platform_device *pdev) ··· 261 235 return -ENOMEM; 262 236 } 263 237 264 - priv->clk = devm_clk_get_optional(&pdev->dev, NULL); 265 - if (IS_ERR(priv->clk)) 266 - return PTR_ERR(priv->clk); 267 - 268 - ret = clk_prepare_enable(priv->clk); 269 - if (ret) 270 - return ret; 271 - 272 238 if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq)) 273 239 priv->clk_freq = 0; 274 240 275 - unimac_mdio_clk_set(priv); 276 - 277 241 priv->mii_bus = mdiobus_alloc(); 278 - if (!priv->mii_bus) { 279 - ret = -ENOMEM; 280 - goto out_clk_disable; 281 - } 242 + if (!priv->mii_bus) 243 + return -ENOMEM; 282 244 283 245 bus = priv->mii_bus; 284 246 bus->priv = priv; ··· 275 261 priv->wait_func = pdata->wait_func; 276 262 priv->wait_func_data = pdata->wait_func_data; 277 263 bus->phy_mask = ~pdata->phy_mask; 264 + priv->clk = pdata->clk; 278 265 } else { 279 266 bus->name = "unimac MII bus"; 280 267 priv->wait_func_data = priv; 281 268 priv->wait_func = unimac_mdio_poll; 269 + priv->clk = devm_clk_get_optional(&pdev->dev, NULL); 282 270 } 271 + 272 + if (IS_ERR(priv->clk)) { 273 + ret = PTR_ERR(priv->clk); 274 + goto out_mdio_free; 275 + } 276 + 283 277 bus->parent = &pdev->dev; 284 278 bus->read = unimac_mdio_read; 285 279 bus->write = unimac_mdio_write; 286 280 bus->reset = unimac_mdio_reset; 287 281 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); 282 + 283 + ret = unimac_mdio_clk_set(priv); 284 + if (ret) 285 + goto out_mdio_free; 288 286 289 287 ret = of_mdiobus_register(bus, np); 290 288 if (ret) { ··· 312 286 313 287 out_mdio_free: 314 288 mdiobus_free(bus); 315 - out_clk_disable: 316 - clk_disable_unprepare(priv->clk); 317 289 return ret; 318 290 } 319 291 ··· 321 297 322 298 mdiobus_unregister(priv->mii_bus); 323 299 mdiobus_free(priv->mii_bus); 324 - clk_disable_unprepare(priv->clk); 325 - } 326 - 327 - static int __maybe_unused unimac_mdio_suspend(struct device *d) 328 - { 329 - struct unimac_mdio_priv *priv = dev_get_drvdata(d); 330 - 331 - clk_disable_unprepare(priv->clk); 332 - 333 - return 0; 334 300 } 335 301 336 302 static int __maybe_unused unimac_mdio_resume(struct device *d) 337 303 { 338 304 struct unimac_mdio_priv *priv = dev_get_drvdata(d); 339 - int ret; 340 305 341 - ret = clk_prepare_enable(priv->clk); 342 - if (ret) 343 - return ret; 344 - 345 - unimac_mdio_clk_set(priv); 346 - 347 - return 0; 306 + return unimac_mdio_clk_set(priv); 348 307 } 349 308 350 309 static SIMPLE_DEV_PM_OPS(unimac_mdio_pm_ops, 351 - unimac_mdio_suspend, unimac_mdio_resume); 310 + NULL, unimac_mdio_resume); 352 311 353 312 static const struct of_device_id unimac_mdio_ids[] = { 354 313 { .compatible = "brcm,asp-v2.1-mdio", },
+3
include/linux/platform_data/mdio-bcm-unimac.h
··· 1 1 #ifndef __MDIO_BCM_UNIMAC_PDATA_H 2 2 #define __MDIO_BCM_UNIMAC_PDATA_H 3 3 4 + struct clk; 5 + 4 6 struct unimac_mdio_pdata { 5 7 u32 phy_mask; 6 8 int (*wait_func)(void *data); 7 9 void *wait_func_data; 8 10 const char *bus_name; 11 + struct clk *clk; 9 12 }; 10 13 11 14 #define UNIMAC_MDIO_DRV_NAME "unimac-mdio"