diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2005-12-09 14:34:57 -0500 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-12-12 15:27:20 -0500 |
commit | 91c86df5a8a44157b456bf1e91fc6d878582e68c (patch) | |
tree | 74f9e3f1c9b78ede4350d3ad59914dd75def74e0 | |
parent | fb17358fe31e01baf902a9fd1fce0e29e3493517 (diff) |
[PATCH] sky2: phy processing in workqueue rather than tasklet
Do phy processing in a work queue rather than a tasklet.
This means we can let bottom halves run.
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
-rw-r--r-- | drivers/net/sky2.c | 58 | ||||
-rw-r--r-- | drivers/net/sky2.h | 6 |
2 files changed, 33 insertions, 31 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 0ab376845e39..2cb6406c9635 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #include <linux/tcp.h> | 43 | #include <linux/tcp.h> |
44 | #include <linux/in.h> | 44 | #include <linux/in.h> |
45 | #include <linux/delay.h> | 45 | #include <linux/delay.h> |
46 | #include <linux/workqueue.h> | ||
46 | #include <linux/if_vlan.h> | 47 | #include <linux/if_vlan.h> |
47 | #include <linux/mii.h> | 48 | #include <linux/mii.h> |
48 | 49 | ||
@@ -535,9 +536,9 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) | |||
535 | 536 | ||
536 | sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC)); | 537 | sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC)); |
537 | 538 | ||
538 | spin_lock_bh(&hw->phy_lock); | 539 | down(&sky2->phy_sema); |
539 | sky2_phy_init(hw, port); | 540 | sky2_phy_init(hw, port); |
540 | spin_unlock_bh(&hw->phy_lock); | 541 | up(&sky2->phy_sema); |
541 | 542 | ||
542 | /* MIB clear */ | 543 | /* MIB clear */ |
543 | reg = gma_read16(hw, port, GM_PHY_ADDR); | 544 | reg = gma_read16(hw, port, GM_PHY_ADDR); |
@@ -839,9 +840,11 @@ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
839 | /* fallthru */ | 840 | /* fallthru */ |
840 | case SIOCGMIIREG: { | 841 | case SIOCGMIIREG: { |
841 | u16 val = 0; | 842 | u16 val = 0; |
842 | spin_lock_bh(&hw->phy_lock); | 843 | |
844 | down(&sky2->phy_sema); | ||
843 | err = __gm_phy_read(hw, sky2->port, data->reg_num & 0x1f, &val); | 845 | err = __gm_phy_read(hw, sky2->port, data->reg_num & 0x1f, &val); |
844 | spin_unlock_bh(&hw->phy_lock); | 846 | up(&sky2->phy_sema); |
847 | |||
845 | data->val_out = val; | 848 | data->val_out = val; |
846 | break; | 849 | break; |
847 | } | 850 | } |
@@ -850,10 +853,10 @@ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
850 | if (!capable(CAP_NET_ADMIN)) | 853 | if (!capable(CAP_NET_ADMIN)) |
851 | return -EPERM; | 854 | return -EPERM; |
852 | 855 | ||
853 | spin_lock_bh(&hw->phy_lock); | 856 | down(&sky2->phy_sema); |
854 | err = gm_phy_write(hw, sky2->port, data->reg_num & 0x1f, | 857 | err = gm_phy_write(hw, sky2->port, data->reg_num & 0x1f, |
855 | data->val_in); | 858 | data->val_in); |
856 | spin_unlock_bh(&hw->phy_lock); | 859 | up(&sky2->phy_sema); |
857 | break; | 860 | break; |
858 | } | 861 | } |
859 | return err; | 862 | return err; |
@@ -1308,6 +1311,7 @@ static int sky2_down(struct net_device *dev) | |||
1308 | sky2_write32(hw, B0_IMSK, hw->intr_mask); | 1311 | sky2_write32(hw, B0_IMSK, hw->intr_mask); |
1309 | local_irq_enable(); | 1312 | local_irq_enable(); |
1310 | 1313 | ||
1314 | flush_scheduled_work(); | ||
1311 | 1315 | ||
1312 | sky2_phy_reset(hw, port); | 1316 | sky2_phy_reset(hw, port); |
1313 | 1317 | ||
@@ -1520,17 +1524,17 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) | |||
1520 | } | 1524 | } |
1521 | 1525 | ||
1522 | /* | 1526 | /* |
1523 | * Interrupt from PHY are handled in tasklet (soft irq) | 1527 | * Interrupt from PHY are handled outside of interrupt context |
1524 | * because accessing phy registers requires spin wait which might | 1528 | * because accessing phy registers requires spin wait which might |
1525 | * cause excess interrupt latency. | 1529 | * cause excess interrupt latency. |
1526 | */ | 1530 | */ |
1527 | static void sky2_phy_task(unsigned long data) | 1531 | static void sky2_phy_task(void *arg) |
1528 | { | 1532 | { |
1529 | struct sky2_port *sky2 = (struct sky2_port *)data; | 1533 | struct sky2_port *sky2 = arg; |
1530 | struct sky2_hw *hw = sky2->hw; | 1534 | struct sky2_hw *hw = sky2->hw; |
1531 | u16 istatus, phystat; | 1535 | u16 istatus, phystat; |
1532 | 1536 | ||
1533 | spin_lock(&hw->phy_lock); | 1537 | down(&sky2->phy_sema); |
1534 | istatus = gm_phy_read(hw, sky2->port, PHY_MARV_INT_STAT); | 1538 | istatus = gm_phy_read(hw, sky2->port, PHY_MARV_INT_STAT); |
1535 | phystat = gm_phy_read(hw, sky2->port, PHY_MARV_PHY_STAT); | 1539 | phystat = gm_phy_read(hw, sky2->port, PHY_MARV_PHY_STAT); |
1536 | 1540 | ||
@@ -1558,7 +1562,7 @@ static void sky2_phy_task(unsigned long data) | |||
1558 | sky2_link_down(sky2); | 1562 | sky2_link_down(sky2); |
1559 | } | 1563 | } |
1560 | out: | 1564 | out: |
1561 | spin_unlock(&hw->phy_lock); | 1565 | up(&sky2->phy_sema); |
1562 | 1566 | ||
1563 | local_irq_disable(); | 1567 | local_irq_disable(); |
1564 | hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2; | 1568 | hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2; |
@@ -1960,7 +1964,7 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port) | |||
1960 | 1964 | ||
1961 | hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); | 1965 | hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); |
1962 | sky2_write32(hw, B0_IMSK, hw->intr_mask); | 1966 | sky2_write32(hw, B0_IMSK, hw->intr_mask); |
1963 | tasklet_schedule(&sky2->phy_task); | 1967 | schedule_work(&sky2->phy_task); |
1964 | } | 1968 | } |
1965 | 1969 | ||
1966 | static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) | 1970 | static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) |
@@ -2150,10 +2154,8 @@ static int sky2_reset(struct sky2_hw *hw) | |||
2150 | 2154 | ||
2151 | sky2_write32(hw, B0_HWE_IMSK, Y2_HWE_ALL_MASK); | 2155 | sky2_write32(hw, B0_HWE_IMSK, Y2_HWE_ALL_MASK); |
2152 | 2156 | ||
2153 | spin_lock_bh(&hw->phy_lock); | ||
2154 | for (i = 0; i < hw->ports; i++) | 2157 | for (i = 0; i < hw->ports; i++) |
2155 | sky2_phy_reset(hw, i); | 2158 | sky2_phy_reset(hw, i); |
2156 | spin_unlock_bh(&hw->phy_lock); | ||
2157 | 2159 | ||
2158 | memset(hw->st_le, 0, STATUS_LE_BYTES); | 2160 | memset(hw->st_le, 0, STATUS_LE_BYTES); |
2159 | hw->st_idx = 0; | 2161 | hw->st_idx = 0; |
@@ -2386,10 +2388,10 @@ static int sky2_nway_reset(struct net_device *dev) | |||
2386 | 2388 | ||
2387 | netif_stop_queue(dev); | 2389 | netif_stop_queue(dev); |
2388 | 2390 | ||
2389 | spin_lock_irq(&hw->phy_lock); | 2391 | down(&sky2->phy_sema); |
2390 | sky2_phy_reset(hw, sky2->port); | 2392 | sky2_phy_reset(hw, sky2->port); |
2391 | sky2_phy_init(hw, sky2->port); | 2393 | sky2_phy_init(hw, sky2->port); |
2392 | spin_unlock_irq(&hw->phy_lock); | 2394 | up(&sky2->phy_sema); |
2393 | 2395 | ||
2394 | return 0; | 2396 | return 0; |
2395 | } | 2397 | } |
@@ -2528,11 +2530,10 @@ static void sky2_set_multicast(struct net_device *dev) | |||
2528 | /* Can have one global because blinking is controlled by | 2530 | /* Can have one global because blinking is controlled by |
2529 | * ethtool and that is always under RTNL mutex | 2531 | * ethtool and that is always under RTNL mutex |
2530 | */ | 2532 | */ |
2531 | static inline void sky2_led(struct sky2_hw *hw, unsigned port, int on) | 2533 | static void sky2_led(struct sky2_hw *hw, unsigned port, int on) |
2532 | { | 2534 | { |
2533 | u16 pg; | 2535 | u16 pg; |
2534 | 2536 | ||
2535 | spin_lock_bh(&hw->phy_lock); | ||
2536 | switch (hw->chip_id) { | 2537 | switch (hw->chip_id) { |
2537 | case CHIP_ID_YUKON_XL: | 2538 | case CHIP_ID_YUKON_XL: |
2538 | pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); | 2539 | pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); |
@@ -2562,7 +2563,6 @@ static inline void sky2_led(struct sky2_hw *hw, unsigned port, int on) | |||
2562 | PHY_M_LED_MO_RX(MO_LED_OFF)); | 2563 | PHY_M_LED_MO_RX(MO_LED_OFF)); |
2563 | 2564 | ||
2564 | } | 2565 | } |
2565 | spin_unlock_bh(&hw->phy_lock); | ||
2566 | } | 2566 | } |
2567 | 2567 | ||
2568 | /* blink LED's for finding board */ | 2568 | /* blink LED's for finding board */ |
@@ -2573,6 +2573,7 @@ static int sky2_phys_id(struct net_device *dev, u32 data) | |||
2573 | unsigned port = sky2->port; | 2573 | unsigned port = sky2->port; |
2574 | u16 ledctrl, ledover = 0; | 2574 | u16 ledctrl, ledover = 0; |
2575 | long ms; | 2575 | long ms; |
2576 | int interrupted; | ||
2576 | int onoff = 1; | 2577 | int onoff = 1; |
2577 | 2578 | ||
2578 | if (!data || data > (u32) (MAX_SCHEDULE_TIMEOUT / HZ)) | 2579 | if (!data || data > (u32) (MAX_SCHEDULE_TIMEOUT / HZ)) |
@@ -2581,7 +2582,7 @@ static int sky2_phys_id(struct net_device *dev, u32 data) | |||
2581 | ms = data * 1000; | 2582 | ms = data * 1000; |
2582 | 2583 | ||
2583 | /* save initial values */ | 2584 | /* save initial values */ |
2584 | spin_lock_bh(&hw->phy_lock); | 2585 | down(&sky2->phy_sema); |
2585 | if (hw->chip_id == CHIP_ID_YUKON_XL) { | 2586 | if (hw->chip_id == CHIP_ID_YUKON_XL) { |
2586 | u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); | 2587 | u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); |
2587 | gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); | 2588 | gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); |
@@ -2591,19 +2592,20 @@ static int sky2_phys_id(struct net_device *dev, u32 data) | |||
2591 | ledctrl = gm_phy_read(hw, port, PHY_MARV_LED_CTRL); | 2592 | ledctrl = gm_phy_read(hw, port, PHY_MARV_LED_CTRL); |
2592 | ledover = gm_phy_read(hw, port, PHY_MARV_LED_OVER); | 2593 | ledover = gm_phy_read(hw, port, PHY_MARV_LED_OVER); |
2593 | } | 2594 | } |
2594 | spin_unlock_bh(&hw->phy_lock); | ||
2595 | 2595 | ||
2596 | while (ms > 0) { | 2596 | interrupted = 0; |
2597 | while (!interrupted && ms > 0) { | ||
2597 | sky2_led(hw, port, onoff); | 2598 | sky2_led(hw, port, onoff); |
2598 | onoff = !onoff; | 2599 | onoff = !onoff; |
2599 | 2600 | ||
2600 | if (msleep_interruptible(250)) | 2601 | up(&sky2->phy_sema); |
2601 | break; /* interrupted */ | 2602 | interrupted = msleep_interruptible(250); |
2603 | down(&sky2->phy_sema); | ||
2604 | |||
2602 | ms -= 250; | 2605 | ms -= 250; |
2603 | } | 2606 | } |
2604 | 2607 | ||
2605 | /* resume regularly scheduled programming */ | 2608 | /* resume regularly scheduled programming */ |
2606 | spin_lock_bh(&hw->phy_lock); | ||
2607 | if (hw->chip_id == CHIP_ID_YUKON_XL) { | 2609 | if (hw->chip_id == CHIP_ID_YUKON_XL) { |
2608 | u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); | 2610 | u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); |
2609 | gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); | 2611 | gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); |
@@ -2613,7 +2615,7 @@ static int sky2_phys_id(struct net_device *dev, u32 data) | |||
2613 | gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); | 2615 | gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); |
2614 | gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover); | 2616 | gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover); |
2615 | } | 2617 | } |
2616 | spin_unlock_bh(&hw->phy_lock); | 2618 | up(&sky2->phy_sema); |
2617 | 2619 | ||
2618 | return 0; | 2620 | return 0; |
2619 | } | 2621 | } |
@@ -2917,7 +2919,8 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, | |||
2917 | sky2->speed = -1; | 2919 | sky2->speed = -1; |
2918 | sky2->advertising = sky2_supported_modes(hw); | 2920 | sky2->advertising = sky2_supported_modes(hw); |
2919 | sky2->rx_csum = 1; | 2921 | sky2->rx_csum = 1; |
2920 | tasklet_init(&sky2->phy_task, sky2_phy_task, (unsigned long)sky2); | 2922 | INIT_WORK(&sky2->phy_task, sky2_phy_task, sky2); |
2923 | init_MUTEX(&sky2->phy_sema); | ||
2921 | sky2->tx_pending = TX_DEF_PENDING; | 2924 | sky2->tx_pending = TX_DEF_PENDING; |
2922 | sky2->rx_pending = is_ec_a1(hw) ? 8 : RX_DEF_PENDING; | 2925 | sky2->rx_pending = is_ec_a1(hw) ? 8 : RX_DEF_PENDING; |
2923 | 2926 | ||
@@ -3027,7 +3030,6 @@ static int __devinit sky2_probe(struct pci_dev *pdev, | |||
3027 | 3030 | ||
3028 | memset(hw, 0, sizeof(*hw)); | 3031 | memset(hw, 0, sizeof(*hw)); |
3029 | hw->pdev = pdev; | 3032 | hw->pdev = pdev; |
3030 | spin_lock_init(&hw->phy_lock); | ||
3031 | 3033 | ||
3032 | hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); | 3034 | hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); |
3033 | if (!hw->regs) { | 3035 | if (!hw->regs) { |
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index 7943dd42ac94..29ebca099f99 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h | |||
@@ -1823,8 +1823,10 @@ struct sky2_port { | |||
1823 | u8 rx_csum; | 1823 | u8 rx_csum; |
1824 | u8 wol; | 1824 | u8 wol; |
1825 | 1825 | ||
1826 | struct tasklet_struct phy_task; | ||
1827 | struct net_device_stats net_stats; | 1826 | struct net_device_stats net_stats; |
1827 | |||
1828 | struct work_struct phy_task; | ||
1829 | struct semaphore phy_sema; | ||
1828 | }; | 1830 | }; |
1829 | 1831 | ||
1830 | struct sky2_hw { | 1832 | struct sky2_hw { |
@@ -1842,8 +1844,6 @@ struct sky2_hw { | |||
1842 | struct sky2_status_le *st_le; | 1844 | struct sky2_status_le *st_le; |
1843 | u32 st_idx; | 1845 | u32 st_idx; |
1844 | dma_addr_t st_dma; | 1846 | dma_addr_t st_dma; |
1845 | |||
1846 | spinlock_t phy_lock; | ||
1847 | }; | 1847 | }; |
1848 | 1848 | ||
1849 | /* Register accessor for memory mapped device */ | 1849 | /* Register accessor for memory mapped device */ |