aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ucc_geth.c
diff options
context:
space:
mode:
authorAnton Vorontsov <avorontsov@ru.mvista.com>2009-03-27 19:00:03 -0400
committerDavid S. Miller <davem@davemloft.net>2009-03-27 19:00:03 -0400
commit79675900cbf2c4e67e95f94983ec4ee800b83739 (patch)
treec0e4357fd3409fb89c215573736ab82e81d4c7f4 /drivers/net/ucc_geth.c
parentcc0be3227df9146968311308a9d19db1469ce1db (diff)
ucc_geth: Fix three oopses in PHY {de,}initialization code
When there are no free snums, UCC ethernet should gracefully fail, but currently it oopses this way: # ifconfig eth0 up fill_init_enet_entries: Can not get SNUM. ucc_geth_startup: Can not fill p_init_enet_param_shadow. eth0: Cannot configure net device, aborting. Unable to handle kernel paging request for data at address 0x00000190 Faulting instruction address: 0xc0294c88 Oops: Kernel access of bad area, sig: 11 [#1] [...] NIP [c0294c88] mutex_lock+0x0/0x1c LR [c01b6be8] phy_stop+0x20/0x70 Call Trace: [efb25da0] [efb2eb60] 0xefb2eb60 (unreliable) [efb25db0] [c01b2058] ucc_geth_stop+0x2c/0x8c [efb25dd0] [c01b4194] ucc_geth_open+0x48/0x27c [efb25df0] [c020eec0] dev_open+0xc0/0x118 [...] This is because the ucc_geth_stop() routine assumes that ugeth->phydev is always initialized by the ucc_geth_open(), while it is not in case of errors. If we add a check to the ucc_geth_stop(), then another oops pops up: Unable to handle kernel paging request for data at address 0x00000004 Faulting instruction address: 0xc01b46a4 Oops: Kernel access of bad area, sig: 11 [#1] [...] NIP [c01b46a4] adjust_link+0x20/0x1b4 LR [c01b770c] phy_state_machine+0xdc/0x44c Call Trace: [ef83bf10] [c021b388] linkwatch_schedule_work+0x74/0xf8 (unreliable) [ef83bf40] [c01b770c] phy_state_machine+0xdc/0x44c [ef83bf60] [c004c13c] run_workqueue+0xb8/0x148 [ef83bf90] [c004c870] worker_thread+0x70/0xd0 [ef83bfd0] [c00505fc] kthread+0x48/0x84 [ef83bff0] [c000f464] kernel_thread+0x4c/0x68 [...] That one happens because ucc_geth_stop() does not call phy_disconnect() and so phylib state machine is running without any idea that a MAC has just died. Also, when device tree specifies fixed-link, and CONFIG_FIXED_PHY is disabled, we'll get this oops: 0:01 not found eth2: Could not attach to PHY eth2: Cannot initialize PHY, aborting. Unable to handle kernel paging request for data at address 0x00000190 Faulting instruction address: 0xc02967d0 Oops: Kernel access of bad area, sig: 11 [#1] [...] NIP [c02967d0] mutex_lock+0x0/0x1c LR [c01b6bcc] phy_stop+0x20/0x70 Call Trace: [ef82be50] [efb6bb60] 0xefb6bb60 (unreliable) [ef82be60] [c01b2058] ucc_geth_stop+0x2c/0x8c [ef82be80] [c01b4194] ucc_geth_open+0x48/0x27c [ef82bea0] [c0210a04] dev_open+0xc0/0x118 [ef82bec0] [c020f85c] dev_change_flags+0x84/0x1ac [ef82bee0] [c037b768] ic_open_devs+0x168/0x2bc [ef82bf20] [c037ca98] ip_auto_config+0x90/0x28c [ef82bf60] [c0001b9c] do_one_initcall+0x34/0x1a0 [ef82bfd0] [c035e240] do_initcalls+0x38/0x58 [ef82bfe0] [c035e2c4] kernel_init+0x30/0x90 [ef82bff0] [c000f464] kernel_thread+0x4c/0x68 [...] And again, ucc_geth_stop() assumes that ugeth->phydev is there, while it isn't. This patch fixes all three oopses simply by rearranging some code: - In ucc_geth_open(): move init_phy() call to the beginning, so that we only call ucc_geth_stop() with a PHY attached; - Move phy_disconnect() call from ucc_geth_close() to ucc_geth_stop(), so that we'll always disconnect the PHY. Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ucc_geth.c')
-rw-r--r--drivers/net/ucc_geth.c21
1 files changed, 11 insertions, 10 deletions
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index a110326dce6f..86a479f61c0c 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -2009,6 +2009,9 @@ static void ucc_geth_stop(struct ucc_geth_private *ugeth)
2009 /* Disable Rx and Tx */ 2009 /* Disable Rx and Tx */
2010 clrbits32(&ug_regs->maccfg1, MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX); 2010 clrbits32(&ug_regs->maccfg1, MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX);
2011 2011
2012 phy_disconnect(ugeth->phydev);
2013 ugeth->phydev = NULL;
2014
2012 ucc_geth_memclean(ugeth); 2015 ucc_geth_memclean(ugeth);
2013} 2016}
2014 2017
@@ -3345,6 +3348,14 @@ static int ucc_geth_open(struct net_device *dev)
3345 return -EINVAL; 3348 return -EINVAL;
3346 } 3349 }
3347 3350
3351 err = init_phy(dev);
3352 if (err) {
3353 if (netif_msg_ifup(ugeth))
3354 ugeth_err("%s: Cannot initialize PHY, aborting.",
3355 dev->name);
3356 return err;
3357 }
3358
3348 err = ucc_struct_init(ugeth); 3359 err = ucc_struct_init(ugeth);
3349 if (err) { 3360 if (err) {
3350 if (netif_msg_ifup(ugeth)) 3361 if (netif_msg_ifup(ugeth))
@@ -3381,13 +3392,6 @@ static int ucc_geth_open(struct net_device *dev)
3381 &ugeth->ug_regs->macstnaddr1, 3392 &ugeth->ug_regs->macstnaddr1,
3382 &ugeth->ug_regs->macstnaddr2); 3393 &ugeth->ug_regs->macstnaddr2);
3383 3394
3384 err = init_phy(dev);
3385 if (err) {
3386 if (netif_msg_ifup(ugeth))
3387 ugeth_err("%s: Cannot initialize PHY, aborting.", dev->name);
3388 goto out_err;
3389 }
3390
3391 phy_start(ugeth->phydev); 3395 phy_start(ugeth->phydev);
3392 3396
3393 err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX); 3397 err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
@@ -3430,9 +3434,6 @@ static int ucc_geth_close(struct net_device *dev)
3430 3434
3431 free_irq(ugeth->ug_info->uf_info.irq, ugeth->dev); 3435 free_irq(ugeth->ug_info->uf_info.irq, ugeth->dev);
3432 3436
3433 phy_disconnect(ugeth->phydev);
3434 ugeth->phydev = NULL;
3435
3436 netif_stop_queue(dev); 3437 netif_stop_queue(dev);
3437 3438
3438 return 0; 3439 return 0;