* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/config.h>
#include <linux/crc32.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include "sky2.h"
#define DRV_NAME "sky2"
-#define DRV_VERSION "1.6.1"
+#define DRV_VERSION "1.5"
#define PFX DRV_NAME " "
/*
#define RX_MAX_PENDING (RX_LE_SIZE/2 - 2)
#define RX_DEF_PENDING RX_MAX_PENDING
#define RX_SKB_ALIGN 8
+#define RX_BUF_WRITE 16
#define TX_RING_SIZE 512
#define TX_DEF_PENDING (TX_RING_SIZE - 1)
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) },
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) },
{ PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b00) }, /* DGE-560T */
+ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4001) }, /* DGE-550SX */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4340) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4341) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4342) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4350) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4351) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4352) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4353) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4360) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4361) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4362) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4363) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4364) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4365) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4366) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4367) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4368) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4369) },
{ 0 }
};
udelay(100);
if (hw->chip_id == CHIP_ID_YUKON_EC_U) {
- sky2_write16(hw, B0_CTST, Y2_HW_WOL_ON);
sky2_pci_write32(hw, PCI_DEV_REG3, 0);
reg1 = sky2_pci_read32(hw, PCI_DEV_REG4);
reg1 &= P_ASPM_CONTROL_MSK;
else
reg1 |= (PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD);
sky2_pci_write32(hw, PCI_DEV_REG1, reg1);
+ udelay(100);
if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1)
sky2_write8(hw, B2_Y2_CLK_GATE, 0);
}
ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
- if (sky2_is_copper(hw)) {
+ if (hw->copper) {
if (hw->chip_id == CHIP_ID_YUKON_FE) {
/* enable automatic crossover */
ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO) >> 1;
ctrl |= PHY_M_PC_DSC(2) | PHY_M_PC_DOWN_S_ENA;
}
}
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
} else {
/* workaround for deviation #4.88 (CRC errors) */
/* disable Automatic Crossover */
ctrl &= ~PHY_M_PC_MDIX_MSK;
- }
-
- gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
-
- /* special setup for PHY 88E1112 Fiber */
- if (hw->chip_id == CHIP_ID_YUKON_XL && !sky2_is_copper(hw)) {
- pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
-
- /* Fiber: select 1000BASE-X only mode MAC Specific Ctrl Reg. */
- gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2);
- ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
- ctrl &= ~PHY_M_MAC_MD_MSK;
- ctrl |= PHY_M_MAC_MODE_SEL(PHY_M_MAC_MD_1000BX);
gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
- if (hw->pmd_type == 'P') {
+ if (hw->chip_id == CHIP_ID_YUKON_XL) {
+ /* Fiber: select 1000BASE-X only mode MAC Specific Ctrl Reg. */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2);
+ ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+ ctrl &= ~PHY_M_MAC_MD_MSK;
+ ctrl |= PHY_M_MAC_MODE_SEL(PHY_M_MAC_MD_1000BX);
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
/* select page 1 to access Fiber registers */
gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 1);
-
- /* for SFP-module set SIGDET polarity to low */
- ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
- ctrl |= PHY_M_FIB_SIGD_POL;
- gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
}
-
- gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
}
ctrl = gm_phy_read(hw, port, PHY_MARV_CTRL);
adv = PHY_AN_CSMA;
if (sky2->autoneg == AUTONEG_ENABLE) {
- if (sky2_is_copper(hw)) {
+ if (hw->copper) {
if (sky2->advertising & ADVERTISED_1000baseT_Full)
ct1000 |= PHY_M_1000C_AFD;
if (sky2->advertising & ADVERTISED_1000baseT_Half)
adv |= PHY_M_AN_10_FD;
if (sky2->advertising & ADVERTISED_10baseT_Half)
adv |= PHY_M_AN_10_HD;
- } else { /* special defines for FIBER (88E1040S only) */
- if (sky2->advertising & ADVERTISED_1000baseT_Full)
- adv |= PHY_M_AN_1000X_AFD;
- if (sky2->advertising & ADVERTISED_1000baseT_Half)
- adv |= PHY_M_AN_1000X_AHD;
- }
+ } else /* special defines for FIBER (88E1011S only) */
+ adv |= PHY_M_AN_1000X_AHD | PHY_M_AN_1000X_AFD;
/* Set Flow-control capabilities */
if (sky2->tx_pause && sky2->rx_pause)
sky2_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON);
if (hw->chip_id == CHIP_ID_YUKON_EC_U) {
- sky2_write8(hw, SK_REG(port, RX_GMF_LP_THR), 768/8);
+ sky2_write8(hw, SK_REG(port, RX_GMF_LP_THR), 512/8);
sky2_write8(hw, SK_REG(port, RX_GMF_UP_THR), 1024/8);
if (hw->dev[port]->mtu > ETH_DATA_LEN) {
/* set Tx GMAC FIFO Almost Empty Threshold */
}
-/* Assign Ram Buffer allocation.
- * start and end are in units of 4k bytes
- * ram registers are in units of 64bit words
- */
-static void sky2_ramset(struct sky2_hw *hw, u16 q, u8 startk, u8 endk)
+/* Assign Ram Buffer allocation in units of 64bit (8 bytes) */
+static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, u32 end)
{
- u32 start, end;
-
- start = startk * 4096/8;
- end = (endk * 4096/8) - 1;
+ pr_debug(PFX "q %d %#x %#x\n", q, start, end);
sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR);
sky2_write32(hw, RB_ADDR(q, RB_START), start);
sky2_write32(hw, RB_ADDR(q, RB_RP), start);
if (q == Q_R1 || q == Q_R2) {
- u32 space = (endk - startk) * 4096/8;
+ u32 space = end - start + 1;
u32 tp = space - space/4;
/* On receive queue's set the thresholds
/*
* It appears the hardware has a bug in the FIFO logic that
* cause it to hang if the FIFO gets overrun and the receive buffer
- * is not aligned. Also dev_alloc_skb() won't align properly if slab
+ * is not aligned. ALso alloc_skb() won't align properly if slab
* debugging is enabled.
*/
static inline struct sk_buff *sky2_alloc_skb(unsigned int size, gfp_t gfp_mask)
{
struct sk_buff *skb;
- skb = __dev_alloc_skb(size + RX_SKB_ALIGN, gfp_mask);
+ skb = alloc_skb(size + RX_SKB_ALIGN, gfp_mask);
if (likely(skb)) {
unsigned long p = (unsigned long) skb->data;
skb_reserve(skb, ALIGN(p, RX_SKB_ALIGN) - p);
sky2_mac_init(hw, port);
- /* Determine available ram buffer space (in 4K blocks).
- * Note: not sure about the FE setting below yet
- */
- if (hw->chip_id == CHIP_ID_YUKON_FE)
- ramsize = 4;
- else
- ramsize = sky2_read8(hw, B2_E_0);
+ /* Determine available ram buffer space in qwords. */
+ ramsize = sky2_read8(hw, B2_E_0) * 4096/8;
- /* Give transmitter one third (rounded up) */
- rxspace = ramsize - (ramsize + 2) / 3;
+ if (ramsize > 6*1024/8)
+ rxspace = ramsize - (ramsize + 2) / 3;
+ else
+ rxspace = ramsize / 2;
- sky2_ramset(hw, rxqaddr[port], 0, rxspace);
- sky2_ramset(hw, txqaddr[port], rxspace, ramsize);
+ sky2_ramset(hw, rxqaddr[port], 0, rxspace-1);
+ sky2_ramset(hw, txqaddr[port], rxspace, ramsize-1);
/* Make sure SyncQ is disabled */
sky2_write8(hw, RB_ADDR(port == 0 ? Q_XS1 : Q_XS2, RB_CTRL),
}
sky2->tx_cons = put;
- if (tx_avail(sky2) > MAX_SKB_TX_LE)
+ if (tx_avail(sky2) > MAX_SKB_TX_LE + 4)
netif_wake_queue(dev);
}
/* Stop more packets from being queued */
netif_stop_queue(dev);
+ /* Disable port IRQ */
+ imask = sky2_read32(hw, B0_IMSK);
+ imask &= ~portirq_msk[port];
+ sky2_write32(hw, B0_IMSK, imask);
+
sky2_phy_reset(hw, port);
/* Stop transmitter */
sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
- /* Disable port IRQ */
- imask = sky2_read32(hw, B0_IMSK);
- imask &= ~portirq_msk[port];
- sky2_write32(hw, B0_IMSK, imask);
-
/* turn off LED's */
sky2_write16(hw, B0_Y2LED, LED_STAT_OFF);
static u16 sky2_phy_speed(const struct sky2_hw *hw, u16 aux)
{
- if (!sky2_is_copper(hw))
+ if (!hw->copper)
return SPEED_1000;
if (hw->chip_id == CHIP_ID_YUKON_FE)
struct sky2_port *sky2 = netdev_priv(dev);
u16 istatus, phystat;
+ if (!netif_running(dev))
+ return;
+
spin_lock(&sky2->phy_lock);
istatus = gm_phy_read(hw, port, PHY_MARV_INT_STAT);
phystat = gm_phy_read(hw, port, PHY_MARV_PHY_STAT);
- if (!netif_running(dev))
- goto out;
-
if (netif_msg_intr(sky2))
printk(KERN_INFO PFX "%s: phy interrupt status 0x%x 0x%x\n",
sky2->netdev->name, istatus, phystat);
goto oversize;
if (length < copybreak) {
- skb = dev_alloc_skb(length + 2);
+ skb = alloc_skb(length + 2, GFP_ATOMIC);
if (!skb)
goto resubmit;
re->skb->ip_summed = CHECKSUM_NONE;
sky2_rx_add(sky2, re->mapaddr);
- /* Tell receiver about new buffers. */
- sky2_put_idx(sky2->hw, rxqaddr[sky2->port], sky2->rx_put);
-
return skb;
oversize:
/* Process status response ring */
static int sky2_status_intr(struct sky2_hw *hw, int to_do)
{
+ struct sky2_port *sky2;
int work_done = 0;
+ unsigned buf_write[2] = { 0, 0 };
u16 hwidx = sky2_read16(hw, STAT_PUT_IDX);
rmb();
while (hw->st_idx != hwidx) {
struct sky2_status_le *le = hw->st_le + hw->st_idx;
struct net_device *dev;
- struct sky2_port *sky2;
struct sk_buff *skb;
u32 status;
u16 length;
#endif
netif_receive_skb(skb);
+ /* Update receiver after 16 frames */
+ if (++buf_write[le->link] == RX_BUF_WRITE) {
+ sky2_put_idx(hw, rxqaddr[le->link],
+ sky2->rx_put);
+ buf_write[le->link] = 0;
+ }
+
+ /* Stop after net poll weight */
if (++work_done >= to_do)
goto exit_loop;
break;
}
}
- /* Fully processed status ring so clear irq */
- sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
-
exit_loop:
+ if (buf_write[0]) {
+ sky2 = netdev_priv(hw->dev[0]);
+ sky2_put_idx(hw, Q_R1, sky2->rx_put);
+ }
+
+ if (buf_write[1]) {
+ sky2 = netdev_priv(hw->dev[1]);
+ sky2_put_idx(hw, Q_R2, sky2->rx_put);
+ }
+
return work_done;
}
*budget -= work_done;
dev0->quota -= work_done;
+ if (status & Y2_IS_STAT_BMU)
+ sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
+
if (sky2_more_work(hw))
return 1;
}
-static int __devinit sky2_reset(struct sky2_hw *hw)
+static int sky2_reset(struct sky2_hw *hw)
{
u16 status;
- u8 t8;
+ u8 t8, pmd_type;
int i;
sky2_write8(hw, B0_CTST, CS_RST_CLR);
sky2_pci_write32(hw, PEX_UNC_ERR_STAT, 0xffffffffUL);
- hw->pmd_type = sky2_read8(hw, B2_PMD_TYP);
+ pmd_type = sky2_read8(hw, B2_PMD_TYP);
+ hw->copper = !(pmd_type == 'L' || pmd_type == 'S');
+
hw->ports = 1;
t8 = sky2_read8(hw, B2_Y2_HW_RES);
if ((t8 & CFG_DUAL_MAC_MSK) == CFG_DUAL_MAC_MSK) {
static u32 sky2_supported_modes(const struct sky2_hw *hw)
{
- if (sky2_is_copper(hw)) {
- u32 modes = SUPPORTED_10baseT_Half
- | SUPPORTED_10baseT_Full
- | SUPPORTED_100baseT_Half
- | SUPPORTED_100baseT_Full
- | SUPPORTED_Autoneg | SUPPORTED_TP;
+ u32 modes;
+ if (hw->copper) {
+ modes = SUPPORTED_10baseT_Half
+ | SUPPORTED_10baseT_Full
+ | SUPPORTED_100baseT_Half
+ | SUPPORTED_100baseT_Full
+ | SUPPORTED_Autoneg | SUPPORTED_TP;
if (hw->chip_id != CHIP_ID_YUKON_FE)
modes |= SUPPORTED_1000baseT_Half
- | SUPPORTED_1000baseT_Full;
- return modes;
+ | SUPPORTED_1000baseT_Full;
} else
- return SUPPORTED_1000baseT_Half
- | SUPPORTED_1000baseT_Full
- | SUPPORTED_Autoneg
- | SUPPORTED_FIBRE;
+ modes = SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE
+ | SUPPORTED_Autoneg;
+ return modes;
}
static int sky2_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
ecmd->transceiver = XCVR_INTERNAL;
ecmd->supported = sky2_supported_modes(hw);
ecmd->phy_address = PHY_ADDR_MARV;
- if (sky2_is_copper(hw)) {
+ if (hw->copper) {
ecmd->supported = SUPPORTED_10baseT_Half
| SUPPORTED_10baseT_Full
| SUPPORTED_100baseT_Half
| SUPPORTED_1000baseT_Full
| SUPPORTED_Autoneg | SUPPORTED_TP;
ecmd->port = PORT_TP;
- ecmd->speed = sky2->speed;
- } else {
- ecmd->speed = SPEED_1000;
+ } else
ecmd->port = PORT_FIBRE;
- }
ecmd->advertising = sky2->advertising;
ecmd->autoneg = sky2->autoneg;
+ ecmd->speed = sky2->speed;
ecmd->duplex = sky2->duplex;
return 0;
}
return 0;
}
+static void inline sky2_add_filter(u8 filter[8], const u8 *addr)
+{
+ u32 bit;
+
+ bit = ether_crc(ETH_ALEN, addr) & 63;
+ filter[bit >> 3] |= 1 << (bit & 7);
+}
+
static void sky2_set_multicast(struct net_device *dev)
{
struct sky2_port *sky2 = netdev_priv(dev);
struct dev_mc_list *list = dev->mc_list;
u16 reg;
u8 filter[8];
+ static const u8 pause_mc_addr[ETH_ALEN] = { 0x1, 0x80, 0xc2, 0x0, 0x0, 0x1 };
memset(filter, 0, sizeof(filter));
reg &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > 16) /* all multicast */
memset(filter, 0xff, sizeof(filter));
- else if (dev->mc_count == 0) /* no multicast */
+ else if (dev->mc_count == 0 && !sky2->rx_pause)
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 (sky2->rx_pause)
+ sky2_add_filter(filter, pause_mc_addr);
+
+ for (i = 0; list && i < dev->mc_count; i++, list = list->next)
+ sky2_add_filter(filter, list->dmi_addr);
}
gma_write16(hw, port, GM_MC_ADDR_H1,
sky2_write32(hw, B0_IMSK, Y2_IS_IRQ_SW);
- err = request_irq(pdev->irq, sky2_test_intr, SA_SHIRQ, DRV_NAME, hw);
+ err = request_irq(pdev->irq, sky2_test_intr, IRQF_SHARED, DRV_NAME, hw);
if (err) {
printk(KERN_ERR PFX "%s: cannot assign irq %d\n",
pci_name(pdev), pdev->irq);
if (!hw->msi_detected) {
/* MSI test failed, go back to INTx mode */
- printk(KERN_WARNING PFX "%s: No interrupt was generated using MSI, "
- "switching to INTx mode. Please report this failure to "
- "the PCI maintainer and include system chipset information.\n",
+ printk(KERN_INFO PFX "%s: No interrupt generated using MSI, "
+ "switching to INTx mode.\n",
pci_name(pdev));
err = -EOPNOTSUPP;
}
sky2_write32(hw, B0_IMSK, 0);
+ sky2_read32(hw, B0_IMSK);
free_irq(pdev->irq, hw);
if (err)
goto err_out_iounmap;
- printk(KERN_INFO PFX "v%s addr 0x%lx irq %d Yukon-%s (0x%x) rev %d\n",
- DRV_VERSION, pci_resource_start(pdev, 0), pdev->irq,
- yukon2_name[hw->chip_id - CHIP_ID_YUKON_XL],
+ printk(KERN_INFO PFX "v%s addr 0x%llx irq %d Yukon-%s (0x%x) rev %d\n",
+ DRV_VERSION, (unsigned long long)pci_resource_start(pdev, 0),
+ pdev->irq, yukon2_name[hw->chip_id - CHIP_ID_YUKON_XL],
hw->chip_id, hw->chip_rev);
dev = sky2_init_netdev(hw, 0, using_dac);
goto err_out_unregister;
}
- err = request_irq(pdev->irq, sky2_intr, SA_SHIRQ, DRV_NAME, hw);
+ err = request_irq(pdev->irq, sky2_intr, IRQF_SHARED, DRV_NAME, hw);
if (err) {
printk(KERN_ERR PFX "%s: cannot assign irq %d\n",
pci_name(pdev), pdev->irq);
return -EINVAL;
del_timer_sync(&hw->idle_timer);
+ netif_poll_disable(hw->dev[0]);
for (i = 0; i < hw->ports; i++) {
struct net_device *dev = hw->dev[i];
- if (dev) {
- if (!netif_running(dev))
- continue;
-
+ if (netif_running(dev)) {
sky2_down(dev);
netif_device_detach(dev);
- netif_poll_disable(dev);
}
}
for (i = 0; i < hw->ports; i++) {
struct net_device *dev = hw->dev[i];
- if (dev && netif_running(dev)) {
+ if (netif_running(dev)) {
netif_device_attach(dev);
- netif_poll_enable(dev);
err = sky2_up(dev);
if (err) {
}
}
+ netif_poll_enable(hw->dev[0]);
sky2_idle_start(hw);
out:
return err;