{ PCI_DEVICE(PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C940B) },
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_GE) },
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_YU) },
- { PCI_DEVICE(PCI_VENDOR_ID_DLINK, PCI_DEVICE_ID_DLINK_DGE510T), },
+ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, PCI_DEVICE_ID_DLINK_DGE510T) },
{ PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b01) }, /* DGE-530T */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4320) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5005) }, /* Belkin */
{ PCI_DEVICE(PCI_VENDOR_ID_CNET, PCI_DEVICE_ID_CNET_GIGACARD) },
{ PCI_DEVICE(PCI_VENDOR_ID_LINKSYS, PCI_DEVICE_ID_LINKSYS_EG1064) },
- { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0015, },
+ { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0015 },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, skge_id_table);
static const int rxqaddr[] = { Q_R1, Q_R2 };
static const u32 rxirqmask[] = { IS_R1_F, IS_R2_F };
static const u32 txirqmask[] = { IS_XA1_F, IS_XA2_F };
-static const u32 irqmask[] = { IS_R1_F|IS_XA1_F, IS_R2_F|IS_XA2_F };
+static const u32 napimask[] = { IS_R1_F|IS_XA1_F, IS_R2_F|IS_XA2_F };
+static const u32 portmask[] = { IS_PORT_1, IS_PORT_2 };
static int skge_get_regs_len(struct net_device *dev)
{
struct skge_hw *hw = skge->hw;
int port = skge->port;
- mutex_lock(&hw->phy_mutex);
+ spin_lock_bh(&hw->phy_lock);
if (hw->chip_id == CHIP_ID_GENESIS) {
switch (mode) {
case LED_MODE_OFF:
PHY_M_LED_MO_RX(MO_LED_ON));
}
}
- mutex_unlock(&hw->phy_mutex);
+ spin_unlock_bh(&hw->phy_lock);
}
/* blink LED's for finding board */
xm_phy_write(hw, port, PHY_XMAC_CTRL, ctrl);
/* Poll PHY for status changes */
- schedule_delayed_work(&skge->link_thread, LINK_HZ);
+ mod_timer(&skge->link_timer, jiffies + LINK_HZ);
}
static void xm_check_link(struct net_device *dev)
* Since internal PHY is wired to a level triggered pin, can't
* get an interrupt when carrier is detected.
*/
-static void xm_link_timer(struct work_struct *work)
+static void xm_link_timer(unsigned long arg)
{
- struct skge_port *skge =
- container_of(work, struct skge_port, link_thread.work);
+ struct skge_port *skge = (struct skge_port *) arg;
struct net_device *dev = skge->netdev;
struct skge_hw *hw = skge->hw;
int port = skge->port;
goto nochange;
}
- mutex_lock(&hw->phy_mutex);
+ spin_lock(&hw->phy_lock);
xm_check_link(dev);
- mutex_unlock(&hw->phy_mutex);
+ spin_unlock(&hw->phy_lock);
nochange:
if (netif_running(dev))
- schedule_delayed_work(&skge->link_thread, LINK_HZ);
+ mod_timer(&skge->link_timer, jiffies + LINK_HZ);
}
static void genesis_mac_init(struct skge_hw *hw, int port)
netif_stop_queue(skge->netdev);
netif_carrier_off(skge->netdev);
- mutex_lock(&hw->phy_mutex);
+ spin_lock_bh(&hw->phy_lock);
if (hw->chip_id == CHIP_ID_GENESIS) {
genesis_reset(hw, port);
genesis_mac_init(hw, port);
yukon_reset(hw, port);
yukon_init(hw, port);
}
- mutex_unlock(&hw->phy_mutex);
+ spin_unlock_bh(&hw->phy_lock);
dev->set_multicast_list(dev);
}
/* fallthru */
case SIOCGMIIREG: {
u16 val = 0;
- mutex_lock(&hw->phy_mutex);
+ spin_lock_bh(&hw->phy_lock);
if (hw->chip_id == CHIP_ID_GENESIS)
err = __xm_phy_read(hw, skge->port, data->reg_num & 0x1f, &val);
else
err = __gm_phy_read(hw, skge->port, data->reg_num & 0x1f, &val);
- mutex_unlock(&hw->phy_mutex);
+ spin_unlock_bh(&hw->phy_lock);
data->val_out = val;
break;
}
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- mutex_lock(&hw->phy_mutex);
+ spin_lock_bh(&hw->phy_lock);
if (hw->chip_id == CHIP_ID_GENESIS)
err = xm_phy_write(hw, skge->port, data->reg_num & 0x1f,
data->val_in);
else
err = gm_phy_write(hw, skge->port, data->reg_num & 0x1f,
data->val_in);
- mutex_unlock(&hw->phy_mutex);
+ spin_unlock_bh(&hw->phy_lock);
break;
}
return err;
goto free_rx_ring;
/* Initialize MAC */
- mutex_lock(&hw->phy_mutex);
+ spin_lock_bh(&hw->phy_lock);
if (hw->chip_id == CHIP_ID_GENESIS)
genesis_mac_init(hw, port);
else
yukon_mac_init(hw, port);
- mutex_unlock(&hw->phy_mutex);
+ spin_unlock_bh(&hw->phy_lock);
/* Configure RAMbuffers */
chunk = hw->ram_size / ((hw->ports + 1)*2);
skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_START | CSR_IRQ_CL_F);
skge_led(skge, LED_MODE_ON);
+ spin_lock_irq(&hw->hw_lock);
+ hw->intr_mask |= portmask[port];
+ skge_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock_irq(&hw->hw_lock);
+
netif_poll_enable(dev);
return 0;
netif_stop_queue(dev);
if (hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC)
- cancel_delayed_work(&skge->link_thread);
+ del_timer_sync(&skge->link_timer);
+
+ netif_poll_disable(dev);
+
+ spin_lock_irq(&hw->hw_lock);
+ hw->intr_mask &= ~portmask[port];
+ skge_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock_irq(&hw->hw_lock);
skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
if (hw->chip_id == CHIP_ID_GENESIS)
skge_led(skge, LED_MODE_OFF);
- netif_poll_disable(dev);
+ netif_tx_lock_bh(dev);
skge_tx_clean(dev);
+ netif_tx_unlock_bh(dev);
+
skge_rx_clean(skge);
kfree(skge->rx_ring.start);
struct skge_port *skge = netdev_priv(dev);
struct skge_element *e;
- netif_tx_lock_bh(dev);
for (e = skge->tx_ring.to_clean; e != skge->tx_ring.to_use; e = e->next) {
struct skge_tx_desc *td = e->desc;
skge_tx_free(skge, e, td->control);
skge->tx_ring.to_clean = e;
netif_wake_queue(dev);
- netif_tx_unlock_bh(dev);
}
static void skge_tx_timeout(struct net_device *dev)
return err;
}
+static const u8 pause_mc_addr[ETH_ALEN] = { 0x1, 0x80, 0xc2, 0x0, 0x0, 0x1 };
+
+static void genesis_add_filter(u8 filter[8], const u8 *addr)
+{
+ u32 crc, bit;
+
+ crc = ether_crc_le(ETH_ALEN, addr);
+ bit = ~crc & 0x3f;
+ filter[bit/8] |= 1 << (bit%8);
+}
+
static void genesis_set_multicast(struct net_device *dev)
{
struct skge_port *skge = netdev_priv(dev);
memset(filter, 0xff, sizeof(filter));
else {
memset(filter, 0, sizeof(filter));
- for (i = 0; list && i < count; i++, list = list->next) {
- u32 crc, bit;
- crc = ether_crc_le(ETH_ALEN, list->dmi_addr);
- bit = ~crc & 0x3f;
- filter[bit/8] |= 1 << (bit%8);
- }
+
+ if (skge->flow_status == FLOW_STAT_REM_SEND
+ || skge->flow_status == FLOW_STAT_SYMMETRIC)
+ genesis_add_filter(filter, pause_mc_addr);
+
+ for (i = 0; list && i < count; i++, list = list->next)
+ genesis_add_filter(filter, list->dmi_addr);
}
xm_write32(hw, port, XM_MODE, mode);
xm_outhash(hw, port, XM_HSM, filter);
}
+static void yukon_add_filter(u8 filter[8], const u8 *addr)
+{
+ u32 bit = ether_crc(ETH_ALEN, addr) & 0x3f;
+ filter[bit/8] |= 1 << (bit%8);
+}
+
static void yukon_set_multicast(struct net_device *dev)
{
struct skge_port *skge = netdev_priv(dev);
struct skge_hw *hw = skge->hw;
int port = skge->port;
struct dev_mc_list *list = dev->mc_list;
+ int rx_pause = (skge->flow_status == FLOW_STAT_REM_SEND
+ || skge->flow_status == FLOW_STAT_SYMMETRIC);
u16 reg;
u8 filter[8];
reg &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
else if (dev->flags & IFF_ALLMULTI) /* all multicast */
memset(filter, 0xff, sizeof(filter));
- else if (dev->mc_count == 0) /* no multicast */
+ else if (dev->mc_count == 0 && !rx_pause)/* no multicast */
reg &= ~GM_RXCR_MCF_ENA;
else {
int i;
reg |= GM_RXCR_MCF_ENA;
- for (i = 0; list && i < dev->mc_count; i++, list = list->next) {
- u32 bit = ether_crc(ETH_ALEN, list->dmi_addr) & 0x3f;
- filter[bit/8] |= 1 << (bit%8);
- }
+ if (rx_pause)
+ yukon_add_filter(filter, pause_mc_addr);
+
+ for (i = 0; list && i < dev->mc_count; i++, list = list->next)
+ yukon_add_filter(filter, list->dmi_addr);
}
spin_lock_irqsave(&hw->hw_lock, flags);
__netif_rx_complete(dev);
- hw->intr_mask |= irqmask[skge->port];
+ hw->intr_mask |= napimask[skge->port];
skge_write32(hw, B0_IMSK, hw->intr_mask);
skge_read32(hw, B0_IMSK);
spin_unlock_irqrestore(&hw->hw_lock, flags);
}
/*
- * Interrupt from PHY are handled in work queue
+ * Interrupt from PHY are handled in tasklet (softirq)
* because accessing phy registers requires spin wait which might
* cause excess interrupt latency.
*/
-static void skge_extirq(struct work_struct *work)
+static void skge_extirq(unsigned long arg)
{
- struct skge_hw *hw = container_of(work, struct skge_hw, phy_work);
+ struct skge_hw *hw = (struct skge_hw *) arg;
int port;
- mutex_lock(&hw->phy_mutex);
for (port = 0; port < hw->ports; port++) {
struct net_device *dev = hw->dev[port];
- struct skge_port *skge = netdev_priv(dev);
if (netif_running(dev)) {
+ struct skge_port *skge = netdev_priv(dev);
+
+ spin_lock(&hw->phy_lock);
if (hw->chip_id != CHIP_ID_GENESIS)
yukon_phy_intr(skge);
else if (hw->phy_type == SK_PHY_BCOM)
bcom_phy_intr(skge);
+ spin_unlock(&hw->phy_lock);
}
}
- mutex_unlock(&hw->phy_mutex);
spin_lock_irq(&hw->hw_lock);
hw->intr_mask |= IS_EXT_REG;
status &= hw->intr_mask;
if (status & IS_EXT_REG) {
hw->intr_mask &= ~IS_EXT_REG;
- schedule_work(&hw->phy_work);
+ tasklet_schedule(&hw->phy_task);
}
if (status & (IS_XA1_F|IS_R1_F)) {
struct skge_hw *hw = skge->hw;
unsigned port = skge->port;
const struct sockaddr *addr = p;
+ u16 ctrl;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
- mutex_lock(&hw->phy_mutex);
memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
- memcpy_toio(hw->regs + B2_MAC_1 + port*8,
- dev->dev_addr, ETH_ALEN);
- memcpy_toio(hw->regs + B2_MAC_2 + port*8,
- dev->dev_addr, ETH_ALEN);
- if (hw->chip_id == CHIP_ID_GENESIS)
- xm_outaddr(hw, port, XM_SA, dev->dev_addr);
- else {
- gma_set_addr(hw, port, GM_SRC_ADDR_1L, dev->dev_addr);
- gma_set_addr(hw, port, GM_SRC_ADDR_2L, dev->dev_addr);
+ if (!netif_running(dev)) {
+ memcpy_toio(hw->regs + B2_MAC_1 + port*8, dev->dev_addr, ETH_ALEN);
+ memcpy_toio(hw->regs + B2_MAC_2 + port*8, dev->dev_addr, ETH_ALEN);
+ } else {
+ /* disable Rx */
+ spin_lock_bh(&hw->phy_lock);
+ ctrl = gma_read16(hw, port, GM_GP_CTRL);
+ gma_write16(hw, port, GM_GP_CTRL, ctrl & ~GM_GPCR_RX_ENA);
+
+ memcpy_toio(hw->regs + B2_MAC_1 + port*8, dev->dev_addr, ETH_ALEN);
+ memcpy_toio(hw->regs + B2_MAC_2 + port*8, dev->dev_addr, ETH_ALEN);
+
+ if (hw->chip_id == CHIP_ID_GENESIS)
+ xm_outaddr(hw, port, XM_SA, dev->dev_addr);
+ else {
+ gma_set_addr(hw, port, GM_SRC_ADDR_1L, dev->dev_addr);
+ gma_set_addr(hw, port, GM_SRC_ADDR_2L, dev->dev_addr);
+ }
+
+ gma_write16(hw, port, GM_GP_CTRL, ctrl);
+ spin_unlock_bh(&hw->phy_lock);
}
- mutex_unlock(&hw->phy_mutex);
return 0;
}
else
hw->ram_size = t8 * 4096;
- hw->intr_mask = IS_HW_ERR | IS_PORT_1;
- if (hw->ports > 1)
- hw->intr_mask |= IS_PORT_2;
+ hw->intr_mask = IS_HW_ERR;
+ /* Use PHY IRQ for all but fiber based Genesis board */
if (!(hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC))
hw->intr_mask |= IS_EXT_REG;
skge_write32(hw, B0_IMSK, hw->intr_mask);
- mutex_lock(&hw->phy_mutex);
for (i = 0; i < hw->ports; i++) {
if (hw->chip_id == CHIP_ID_GENESIS)
genesis_reset(hw, i);
else
yukon_reset(hw, i);
}
- mutex_unlock(&hw->phy_mutex);
return 0;
}
skge->netdev = dev;
skge->hw = hw;
skge->msg_enable = netif_msg_init(debug, default_msg);
+
skge->tx_ring.count = DEFAULT_TX_RING_SIZE;
skge->rx_ring.count = DEFAULT_RX_RING_SIZE;
skge->port = port;
/* Only used for Genesis XMAC */
- INIT_DELAYED_WORK(&skge->link_thread, xm_link_timer);
+ setup_timer(&skge->link_timer, xm_link_timer, (unsigned long) skge);
if (hw->chip_id != CHIP_ID_GENESIS) {
dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
}
hw->pdev = pdev;
- mutex_init(&hw->phy_mutex);
- INIT_WORK(&hw->phy_work, skge_extirq);
spin_lock_init(&hw->hw_lock);
+ spin_lock_init(&hw->phy_lock);
+ tasklet_init(&hw->phy_task, &skge_extirq, (unsigned long) hw);
hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
if (!hw->regs) {
dev0 = hw->dev[0];
unregister_netdev(dev0);
+ tasklet_disable(&hw->phy_task);
+
spin_lock_irq(&hw->hw_lock);
hw->intr_mask = 0;
skge_write32(hw, B0_IMSK, 0);