[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>
This commit is contained in:
parent
fb17358fe3
commit
91c86df5a8
@ -43,6 +43,7 @@
|
|||||||
#include <linux/tcp.h>
|
#include <linux/tcp.h>
|
||||||
#include <linux/in.h>
|
#include <linux/in.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
#include <linux/if_vlan.h>
|
#include <linux/if_vlan.h>
|
||||||
#include <linux/mii.h>
|
#include <linux/mii.h>
|
||||||
|
|
||||||
@ -535,9 +536,9 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
|
|||||||
|
|
||||||
sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC));
|
sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC));
|
||||||
|
|
||||||
spin_lock_bh(&hw->phy_lock);
|
down(&sky2->phy_sema);
|
||||||
sky2_phy_init(hw, port);
|
sky2_phy_init(hw, port);
|
||||||
spin_unlock_bh(&hw->phy_lock);
|
up(&sky2->phy_sema);
|
||||||
|
|
||||||
/* MIB clear */
|
/* MIB clear */
|
||||||
reg = gma_read16(hw, port, GM_PHY_ADDR);
|
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)
|
|||||||
/* fallthru */
|
/* fallthru */
|
||||||
case SIOCGMIIREG: {
|
case SIOCGMIIREG: {
|
||||||
u16 val = 0;
|
u16 val = 0;
|
||||||
spin_lock_bh(&hw->phy_lock);
|
|
||||||
|
down(&sky2->phy_sema);
|
||||||
err = __gm_phy_read(hw, sky2->port, data->reg_num & 0x1f, &val);
|
err = __gm_phy_read(hw, sky2->port, data->reg_num & 0x1f, &val);
|
||||||
spin_unlock_bh(&hw->phy_lock);
|
up(&sky2->phy_sema);
|
||||||
|
|
||||||
data->val_out = val;
|
data->val_out = val;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -850,10 +853,10 @@ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|||||||
if (!capable(CAP_NET_ADMIN))
|
if (!capable(CAP_NET_ADMIN))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
spin_lock_bh(&hw->phy_lock);
|
down(&sky2->phy_sema);
|
||||||
err = gm_phy_write(hw, sky2->port, data->reg_num & 0x1f,
|
err = gm_phy_write(hw, sky2->port, data->reg_num & 0x1f,
|
||||||
data->val_in);
|
data->val_in);
|
||||||
spin_unlock_bh(&hw->phy_lock);
|
up(&sky2->phy_sema);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
@ -1308,6 +1311,7 @@ static int sky2_down(struct net_device *dev)
|
|||||||
sky2_write32(hw, B0_IMSK, hw->intr_mask);
|
sky2_write32(hw, B0_IMSK, hw->intr_mask);
|
||||||
local_irq_enable();
|
local_irq_enable();
|
||||||
|
|
||||||
|
flush_scheduled_work();
|
||||||
|
|
||||||
sky2_phy_reset(hw, port);
|
sky2_phy_reset(hw, port);
|
||||||
|
|
||||||
@ -1520,17 +1524,17 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Interrupt from PHY are handled in tasklet (soft irq)
|
* Interrupt from PHY are handled outside of interrupt context
|
||||||
* because accessing phy registers requires spin wait which might
|
* because accessing phy registers requires spin wait which might
|
||||||
* cause excess interrupt latency.
|
* cause excess interrupt latency.
|
||||||
*/
|
*/
|
||||||
static void sky2_phy_task(unsigned long data)
|
static void sky2_phy_task(void *arg)
|
||||||
{
|
{
|
||||||
struct sky2_port *sky2 = (struct sky2_port *)data;
|
struct sky2_port *sky2 = arg;
|
||||||
struct sky2_hw *hw = sky2->hw;
|
struct sky2_hw *hw = sky2->hw;
|
||||||
u16 istatus, phystat;
|
u16 istatus, phystat;
|
||||||
|
|
||||||
spin_lock(&hw->phy_lock);
|
down(&sky2->phy_sema);
|
||||||
istatus = gm_phy_read(hw, sky2->port, PHY_MARV_INT_STAT);
|
istatus = gm_phy_read(hw, sky2->port, PHY_MARV_INT_STAT);
|
||||||
phystat = gm_phy_read(hw, sky2->port, PHY_MARV_PHY_STAT);
|
phystat = gm_phy_read(hw, sky2->port, PHY_MARV_PHY_STAT);
|
||||||
|
|
||||||
@ -1558,7 +1562,7 @@ static void sky2_phy_task(unsigned long data)
|
|||||||
sky2_link_down(sky2);
|
sky2_link_down(sky2);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
spin_unlock(&hw->phy_lock);
|
up(&sky2->phy_sema);
|
||||||
|
|
||||||
local_irq_disable();
|
local_irq_disable();
|
||||||
hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2;
|
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)
|
|||||||
|
|
||||||
hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2);
|
hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2);
|
||||||
sky2_write32(hw, B0_IMSK, hw->intr_mask);
|
sky2_write32(hw, B0_IMSK, hw->intr_mask);
|
||||||
tasklet_schedule(&sky2->phy_task);
|
schedule_work(&sky2->phy_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs)
|
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)
|
|||||||
|
|
||||||
sky2_write32(hw, B0_HWE_IMSK, Y2_HWE_ALL_MASK);
|
sky2_write32(hw, B0_HWE_IMSK, Y2_HWE_ALL_MASK);
|
||||||
|
|
||||||
spin_lock_bh(&hw->phy_lock);
|
|
||||||
for (i = 0; i < hw->ports; i++)
|
for (i = 0; i < hw->ports; i++)
|
||||||
sky2_phy_reset(hw, i);
|
sky2_phy_reset(hw, i);
|
||||||
spin_unlock_bh(&hw->phy_lock);
|
|
||||||
|
|
||||||
memset(hw->st_le, 0, STATUS_LE_BYTES);
|
memset(hw->st_le, 0, STATUS_LE_BYTES);
|
||||||
hw->st_idx = 0;
|
hw->st_idx = 0;
|
||||||
@ -2386,10 +2388,10 @@ static int sky2_nway_reset(struct net_device *dev)
|
|||||||
|
|
||||||
netif_stop_queue(dev);
|
netif_stop_queue(dev);
|
||||||
|
|
||||||
spin_lock_irq(&hw->phy_lock);
|
down(&sky2->phy_sema);
|
||||||
sky2_phy_reset(hw, sky2->port);
|
sky2_phy_reset(hw, sky2->port);
|
||||||
sky2_phy_init(hw, sky2->port);
|
sky2_phy_init(hw, sky2->port);
|
||||||
spin_unlock_irq(&hw->phy_lock);
|
up(&sky2->phy_sema);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2528,11 +2530,10 @@ static void sky2_set_multicast(struct net_device *dev)
|
|||||||
/* Can have one global because blinking is controlled by
|
/* Can have one global because blinking is controlled by
|
||||||
* ethtool and that is always under RTNL mutex
|
* ethtool and that is always under RTNL mutex
|
||||||
*/
|
*/
|
||||||
static inline void sky2_led(struct sky2_hw *hw, unsigned port, int on)
|
static void sky2_led(struct sky2_hw *hw, unsigned port, int on)
|
||||||
{
|
{
|
||||||
u16 pg;
|
u16 pg;
|
||||||
|
|
||||||
spin_lock_bh(&hw->phy_lock);
|
|
||||||
switch (hw->chip_id) {
|
switch (hw->chip_id) {
|
||||||
case CHIP_ID_YUKON_XL:
|
case CHIP_ID_YUKON_XL:
|
||||||
pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
|
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)
|
|||||||
PHY_M_LED_MO_RX(MO_LED_OFF));
|
PHY_M_LED_MO_RX(MO_LED_OFF));
|
||||||
|
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&hw->phy_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* blink LED's for finding board */
|
/* blink LED's for finding board */
|
||||||
@ -2573,6 +2573,7 @@ static int sky2_phys_id(struct net_device *dev, u32 data)
|
|||||||
unsigned port = sky2->port;
|
unsigned port = sky2->port;
|
||||||
u16 ledctrl, ledover = 0;
|
u16 ledctrl, ledover = 0;
|
||||||
long ms;
|
long ms;
|
||||||
|
int interrupted;
|
||||||
int onoff = 1;
|
int onoff = 1;
|
||||||
|
|
||||||
if (!data || data > (u32) (MAX_SCHEDULE_TIMEOUT / HZ))
|
if (!data || data > (u32) (MAX_SCHEDULE_TIMEOUT / HZ))
|
||||||
@ -2581,7 +2582,7 @@ static int sky2_phys_id(struct net_device *dev, u32 data)
|
|||||||
ms = data * 1000;
|
ms = data * 1000;
|
||||||
|
|
||||||
/* save initial values */
|
/* save initial values */
|
||||||
spin_lock_bh(&hw->phy_lock);
|
down(&sky2->phy_sema);
|
||||||
if (hw->chip_id == CHIP_ID_YUKON_XL) {
|
if (hw->chip_id == CHIP_ID_YUKON_XL) {
|
||||||
u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
|
u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
|
||||||
gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
|
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)
|
|||||||
ledctrl = gm_phy_read(hw, port, PHY_MARV_LED_CTRL);
|
ledctrl = gm_phy_read(hw, port, PHY_MARV_LED_CTRL);
|
||||||
ledover = gm_phy_read(hw, port, PHY_MARV_LED_OVER);
|
ledover = gm_phy_read(hw, port, PHY_MARV_LED_OVER);
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&hw->phy_lock);
|
|
||||||
|
|
||||||
while (ms > 0) {
|
interrupted = 0;
|
||||||
|
while (!interrupted && ms > 0) {
|
||||||
sky2_led(hw, port, onoff);
|
sky2_led(hw, port, onoff);
|
||||||
onoff = !onoff;
|
onoff = !onoff;
|
||||||
|
|
||||||
if (msleep_interruptible(250))
|
up(&sky2->phy_sema);
|
||||||
break; /* interrupted */
|
interrupted = msleep_interruptible(250);
|
||||||
|
down(&sky2->phy_sema);
|
||||||
|
|
||||||
ms -= 250;
|
ms -= 250;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* resume regularly scheduled programming */
|
/* resume regularly scheduled programming */
|
||||||
spin_lock_bh(&hw->phy_lock);
|
|
||||||
if (hw->chip_id == CHIP_ID_YUKON_XL) {
|
if (hw->chip_id == CHIP_ID_YUKON_XL) {
|
||||||
u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
|
u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
|
||||||
gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
|
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)
|
|||||||
gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
|
gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
|
||||||
gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover);
|
gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover);
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&hw->phy_lock);
|
up(&sky2->phy_sema);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2917,7 +2919,8 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
|
|||||||
sky2->speed = -1;
|
sky2->speed = -1;
|
||||||
sky2->advertising = sky2_supported_modes(hw);
|
sky2->advertising = sky2_supported_modes(hw);
|
||||||
sky2->rx_csum = 1;
|
sky2->rx_csum = 1;
|
||||||
tasklet_init(&sky2->phy_task, sky2_phy_task, (unsigned long)sky2);
|
INIT_WORK(&sky2->phy_task, sky2_phy_task, sky2);
|
||||||
|
init_MUTEX(&sky2->phy_sema);
|
||||||
sky2->tx_pending = TX_DEF_PENDING;
|
sky2->tx_pending = TX_DEF_PENDING;
|
||||||
sky2->rx_pending = is_ec_a1(hw) ? 8 : RX_DEF_PENDING;
|
sky2->rx_pending = is_ec_a1(hw) ? 8 : RX_DEF_PENDING;
|
||||||
|
|
||||||
@ -3027,7 +3030,6 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
|
|||||||
|
|
||||||
memset(hw, 0, sizeof(*hw));
|
memset(hw, 0, sizeof(*hw));
|
||||||
hw->pdev = pdev;
|
hw->pdev = pdev;
|
||||||
spin_lock_init(&hw->phy_lock);
|
|
||||||
|
|
||||||
hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
|
hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
|
||||||
if (!hw->regs) {
|
if (!hw->regs) {
|
||||||
|
@ -1823,8 +1823,10 @@ struct sky2_port {
|
|||||||
u8 rx_csum;
|
u8 rx_csum;
|
||||||
u8 wol;
|
u8 wol;
|
||||||
|
|
||||||
struct tasklet_struct phy_task;
|
|
||||||
struct net_device_stats net_stats;
|
struct net_device_stats net_stats;
|
||||||
|
|
||||||
|
struct work_struct phy_task;
|
||||||
|
struct semaphore phy_sema;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sky2_hw {
|
struct sky2_hw {
|
||||||
@ -1842,8 +1844,6 @@ struct sky2_hw {
|
|||||||
struct sky2_status_le *st_le;
|
struct sky2_status_le *st_le;
|
||||||
u32 st_idx;
|
u32 st_idx;
|
||||||
dma_addr_t st_dma;
|
dma_addr_t st_dma;
|
||||||
|
|
||||||
spinlock_t phy_lock;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Register accessor for memory mapped device */
|
/* Register accessor for memory mapped device */
|
||||||
|
Loading…
Reference in New Issue
Block a user