#define RTL8169_TX_TIMEOUT (6*HZ)
#define RTL8169_PHY_TIMEOUT (10*HZ)
-#define RTL_EEPROM_SIG cpu_to_le32(0x8129)
-#define RTL_EEPROM_SIG_MASK cpu_to_le32(0xffff)
+#define RTL_EEPROM_SIG 0x8129
#define RTL_EEPROM_SIG_ADDR 0x0000
+#define RTL_EEPROM_MAC_ADDR 0x0007
/* write/read MMIO register */
#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg))
/* Cfg9346Bits */
Cfg9346_Lock = 0x00,
Cfg9346_Unlock = 0xc0,
+ Cfg9346_Program = 0x80, /* Programming mode */
+ Cfg9346_EECS = 0x08, /* Chip select */
+ Cfg9346_EESK = 0x04, /* Serial data clock */
+ Cfg9346_EEDI = 0x02, /* Data input */
+ Cfg9346_EEDO = 0x01, /* Data output */
/* rx_mode_bits */
AcceptErr = 0x20,
/* RxConfigBits */
RxCfgFIFOShift = 13,
RxCfgDMAShift = 8,
+ RxCfg9356SEL = 6, /* EEPROM type: 0 = 9346, 1 = 9356 */
/* TxConfigBits */
TxInterFrameGapShift = 24,
RTL_FEATURE_GMII = (1 << 2),
};
+struct rtl8169_counters {
+ __le64 tx_packets;
+ __le64 rx_packets;
+ __le64 tx_errors;
+ __le32 rx_errors;
+ __le16 rx_missed;
+ __le16 align_errors;
+ __le32 tx_one_collision;
+ __le32 tx_multi_collision;
+ __le64 rx_unicast;
+ __le64 rx_broadcast;
+ __le32 rx_multicast;
+ __le16 tx_aborted;
+ __le16 tx_underun;
+};
+
struct rtl8169_private {
void __iomem *mmio_addr; /* memory map physical address */
struct pci_dev *pci_dev; /* Index of PCI device */
void (*hw_start)(struct net_device *);
unsigned int (*phy_reset_pending)(void __iomem *);
unsigned int (*link_ok)(void __iomem *);
+ int (*do_ioctl)(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd);
int pcie_cap;
struct delayed_work task;
unsigned features;
struct mii_if_info mii;
+ struct rtl8169_counters counters;
};
MODULE_AUTHOR("Realtek and the Linux r8169 crew <netdev@vger.kernel.org>");
"tx_underrun",
};
-struct rtl8169_counters {
- __le64 tx_packets;
- __le64 rx_packets;
- __le64 tx_errors;
- __le32 rx_errors;
- __le16 rx_missed;
- __le16 align_errors;
- __le32 tx_one_collision;
- __le32 tx_multi_collision;
- __le64 rx_unicast;
- __le64 rx_broadcast;
- __le32 rx_multicast;
- __le16 tx_aborted;
- __le16 tx_underun;
-};
-
static int rtl8169_get_sset_count(struct net_device *dev, int sset)
{
switch (sset) {
}
}
-static void rtl8169_get_ethtool_stats(struct net_device *dev,
- struct ethtool_stats *stats, u64 *data)
+static void rtl8169_update_counters(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->mmio_addr;
struct rtl8169_counters *counters;
dma_addr_t paddr;
u32 cmd;
+ int wait = 1000;
- ASSERT_RTNL();
+ /*
+ * Some chips are unable to dump tally counters when the receiver
+ * is disabled.
+ */
+ if ((RTL_R8(ChipCmd) & CmdRxEnb) == 0)
+ return;
counters = pci_alloc_consistent(tp->pci_dev, sizeof(*counters), &paddr);
if (!counters)
RTL_W32(CounterAddrLow, cmd);
RTL_W32(CounterAddrLow, cmd | CounterDump);
- while (RTL_R32(CounterAddrLow) & CounterDump) {
- if (msleep_interruptible(1))
+ while (wait--) {
+ if ((RTL_R32(CounterAddrLow) & CounterDump) == 0) {
+ /* copy updated counters */
+ memcpy(&tp->counters, counters, sizeof(*counters));
break;
+ }
+ udelay(10);
}
RTL_W32(CounterAddrLow, 0);
RTL_W32(CounterAddrHigh, 0);
- data[0] = le64_to_cpu(counters->tx_packets);
- data[1] = le64_to_cpu(counters->rx_packets);
- data[2] = le64_to_cpu(counters->tx_errors);
- data[3] = le32_to_cpu(counters->rx_errors);
- data[4] = le16_to_cpu(counters->rx_missed);
- data[5] = le16_to_cpu(counters->align_errors);
- data[6] = le32_to_cpu(counters->tx_one_collision);
- data[7] = le32_to_cpu(counters->tx_multi_collision);
- data[8] = le64_to_cpu(counters->rx_unicast);
- data[9] = le64_to_cpu(counters->rx_broadcast);
- data[10] = le32_to_cpu(counters->rx_multicast);
- data[11] = le16_to_cpu(counters->tx_aborted);
- data[12] = le16_to_cpu(counters->tx_underun);
-
pci_free_consistent(tp->pci_dev, sizeof(*counters), counters, paddr);
}
+static void rtl8169_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct rtl8169_private *tp = netdev_priv(dev);
+
+ ASSERT_RTNL();
+
+ rtl8169_update_counters(dev);
+
+ data[0] = le64_to_cpu(tp->counters.tx_packets);
+ data[1] = le64_to_cpu(tp->counters.rx_packets);
+ data[2] = le64_to_cpu(tp->counters.tx_errors);
+ data[3] = le32_to_cpu(tp->counters.rx_errors);
+ data[4] = le16_to_cpu(tp->counters.rx_missed);
+ data[5] = le16_to_cpu(tp->counters.align_errors);
+ data[6] = le32_to_cpu(tp->counters.tx_one_collision);
+ data[7] = le32_to_cpu(tp->counters.tx_multi_collision);
+ data[8] = le64_to_cpu(tp->counters.rx_unicast);
+ data[9] = le64_to_cpu(tp->counters.rx_broadcast);
+ data[10] = le32_to_cpu(tp->counters.rx_multicast);
+ data[11] = le16_to_cpu(tp->counters.tx_aborted);
+ data[12] = le16_to_cpu(tp->counters.tx_underun);
+}
+
static void rtl8169_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
switch(stringset) {
struct rtl8169_private *tp = netdev_priv(dev);
struct mii_ioctl_data *data = if_mii(ifr);
- if (!netif_running(dev))
- return -ENODEV;
+ return netif_running(dev) ? tp->do_ioctl(tp, data, cmd) : -ENODEV;
+}
+static int rtl_xmii_ioctl(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd)
+{
switch (cmd) {
case SIOCGMIIPHY:
data->phy_id = 32; /* Internal PHY */
return -EOPNOTSUPP;
}
+static int rtl_tbi_ioctl(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd)
+{
+ return -EOPNOTSUPP;
+}
+
static const struct rtl_cfg_info {
void (*hw_start)(struct net_device *);
unsigned int region;
}
}
+static const struct net_device_ops rtl8169_netdev_ops = {
+ .ndo_open = rtl8169_open,
+ .ndo_stop = rtl8169_close,
+ .ndo_get_stats = rtl8169_get_stats,
+ .ndo_start_xmit = rtl8169_start_xmit,
+ .ndo_tx_timeout = rtl8169_tx_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = rtl8169_change_mtu,
+ .ndo_set_mac_address = rtl_set_mac_address,
+ .ndo_do_ioctl = rtl8169_ioctl,
+ .ndo_set_multicast_list = rtl_set_rx_mode,
+#ifdef CONFIG_R8169_VLAN
+ .ndo_vlan_rx_register = rtl8169_vlan_rx_register,
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = rtl8169_netpoll,
+#endif
+
+};
+
+/* Delay between EEPROM clock transitions. Force out buffered PCI writes. */
+#define RTL_EEPROM_DELAY() RTL_R8(Cfg9346)
+#define RTL_EEPROM_READ_CMD 6
+
+/* read 16bit word stored in EEPROM. EEPROM is addressed by words. */
+static u16 rtl_eeprom_read(void __iomem *ioaddr, int addr)
+{
+ u16 result = 0;
+ int cmd, cmd_len, i;
+
+ /* check for EEPROM address size (in bits) */
+ if (RTL_R32(RxConfig) & (1 << RxCfg9356SEL)) {
+ /* EEPROM is 93C56 */
+ cmd_len = 3 + 8; /* 3 bits for command id and 8 for address */
+ cmd = (RTL_EEPROM_READ_CMD << 8) | (addr & 0xff);
+ } else {
+ /* EEPROM is 93C46 */
+ cmd_len = 3 + 6; /* 3 bits for command id and 6 for address */
+ cmd = (RTL_EEPROM_READ_CMD << 6) | (addr & 0x3f);
+ }
+
+ /* enter programming mode */
+ RTL_W8(Cfg9346, Cfg9346_Program | Cfg9346_EECS);
+ RTL_EEPROM_DELAY();
+
+ /* write command and requested address */
+ while (cmd_len--) {
+ u8 x = Cfg9346_Program | Cfg9346_EECS;
+
+ x |= (cmd & (1 << cmd_len)) ? Cfg9346_EEDI : 0;
+
+ /* write a bit */
+ RTL_W8(Cfg9346, x);
+ RTL_EEPROM_DELAY();
+
+ /* raise clock */
+ RTL_W8(Cfg9346, x | Cfg9346_EESK);
+ RTL_EEPROM_DELAY();
+ }
+
+ /* lower clock */
+ RTL_W8(Cfg9346, Cfg9346_Program | Cfg9346_EECS);
+ RTL_EEPROM_DELAY();
+
+ /* read back 16bit value */
+ for (i = 16; i > 0; i--) {
+ /* raise clock */
+ RTL_W8(Cfg9346, Cfg9346_Program | Cfg9346_EECS | Cfg9346_EESK);
+ RTL_EEPROM_DELAY();
+
+ result <<= 1;
+ result |= (RTL_R8(Cfg9346) & Cfg9346_EEDO) ? 1 : 0;
+
+ /* lower clock */
+ RTL_W8(Cfg9346, Cfg9346_Program | Cfg9346_EECS);
+ RTL_EEPROM_DELAY();
+ }
+
+ RTL_W8(Cfg9346, Cfg9346_Program);
+ /* leave programming mode */
+ RTL_W8(Cfg9346, Cfg9346_Lock);
+
+ return result;
+}
+
+static void rtl_init_mac_address(struct rtl8169_private *tp,
+ void __iomem *ioaddr)
+{
+ struct pci_dev *pdev = tp->pci_dev;
+ u16 x;
+ u8 mac[8];
+
+ /* read EEPROM signature */
+ x = rtl_eeprom_read(ioaddr, RTL_EEPROM_SIG_ADDR);
+
+ if (x != RTL_EEPROM_SIG) {
+ dev_info(&pdev->dev, "Missing EEPROM signature: %04x\n", x);
+ return;
+ }
+
+ /* read MAC address */
+ x = rtl_eeprom_read(ioaddr, RTL_EEPROM_MAC_ADDR);
+ mac[0] = x & 0xff;
+ mac[1] = x >> 8;
+ x = rtl_eeprom_read(ioaddr, RTL_EEPROM_MAC_ADDR + 1);
+ mac[2] = x & 0xff;
+ mac[3] = x >> 8;
+ x = rtl_eeprom_read(ioaddr, RTL_EEPROM_MAC_ADDR + 2);
+ mac[4] = x & 0xff;
+ mac[5] = x >> 8;
+
+ if (netif_msg_probe(tp)) {
+ DECLARE_MAC_BUF(buf);
+
+ dev_info(&pdev->dev, "MAC address found in EEPROM: %s\n",
+ print_mac(buf, mac));
+ }
+
+ if (is_valid_ether_addr(mac))
+ rtl_rar_set(tp, mac);
+}
+
static int __devinit
rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
}
SET_NETDEV_DEV(dev, &pdev->dev);
+ dev->netdev_ops = &rtl8169_netdev_ops;
tp = netdev_priv(dev);
tp->dev = dev;
tp->pci_dev = pdev;
tp->phy_reset_enable = rtl8169_tbi_reset_enable;
tp->phy_reset_pending = rtl8169_tbi_reset_pending;
tp->link_ok = rtl8169_tbi_link_ok;
+ tp->do_ioctl = rtl_tbi_ioctl;
tp->phy_1000_ctrl_reg = ADVERTISE_1000FULL; /* Implied by TBI */
} else {
tp->phy_reset_enable = rtl8169_xmii_reset_enable;
tp->phy_reset_pending = rtl8169_xmii_reset_pending;
tp->link_ok = rtl8169_xmii_link_ok;
-
- dev->do_ioctl = rtl8169_ioctl;
+ tp->do_ioctl = rtl_xmii_ioctl;
}
spin_lock_init(&tp->lock);
tp->mmio_addr = ioaddr;
+ rtl_init_mac_address(tp, ioaddr);
+
/* Get MAC address */
for (i = 0; i < MAC_ADDR_LEN; i++)
dev->dev_addr[i] = RTL_R8(MAC0 + i);
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
- dev->open = rtl8169_open;
- dev->hard_start_xmit = rtl8169_start_xmit;
- dev->get_stats = rtl8169_get_stats;
SET_ETHTOOL_OPS(dev, &rtl8169_ethtool_ops);
- dev->stop = rtl8169_close;
- dev->tx_timeout = rtl8169_tx_timeout;
- dev->set_multicast_list = rtl_set_rx_mode;
dev->watchdog_timeo = RTL8169_TX_TIMEOUT;
dev->irq = pdev->irq;
dev->base_addr = (unsigned long) ioaddr;
- dev->change_mtu = rtl8169_change_mtu;
- dev->set_mac_address = rtl_set_mac_address;
netif_napi_add(dev, &tp->napi, rtl8169_poll, R8169_NAPI_WEIGHT);
#ifdef CONFIG_R8169_VLAN
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- dev->vlan_rx_register = rtl8169_vlan_rx_register;
-#endif
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = rtl8169_netpoll;
#endif
tp->intr_mask = 0xffff;
if (rtl8169_rx_vlan_skb(tp, desc, skb) < 0)
netif_receive_skb(skb);
- dev->last_rx = jiffies;
dev->stats.rx_bytes += pkt_size;
dev->stats.rx_packets++;
}
RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event);
tp->intr_mask = ~tp->napi_event;
- if (likely(netif_rx_schedule_prep(dev, &tp->napi)))
- __netif_rx_schedule(dev, &tp->napi);
+ if (likely(netif_rx_schedule_prep(&tp->napi)))
+ __netif_rx_schedule(&tp->napi);
else if (netif_msg_intr(tp)) {
printk(KERN_INFO "%s: interrupt %04x in poll\n",
dev->name, status);
rtl8169_tx_interrupt(dev, tp, ioaddr);
if (work_done < budget) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
tp->intr_mask = 0xffff;
/*
* 20040426: the barrier is not strictly required but the
struct rtl8169_private *tp = netdev_priv(dev);
struct pci_dev *pdev = tp->pci_dev;
+ /* update counters before going down */
+ rtl8169_update_counters(dev);
+
rtl8169_down(dev);
free_irq(dev->irq, dev);