#include <linux/if_vlan.h>
#include <linux/dma-mapping.h>
#include <linux/crc32.h>
+#include <linux/slab.h>
#include <asm/unaligned.h>
#include "smsc9420.h"
int last_carrier;
};
-static const struct pci_device_id smsc9420_id_table[] = {
+static DEFINE_PCI_DEVICE_TABLE(smsc9420_id_table) = {
{ PCI_VENDOR_ID_9420, PCI_DEVICE_ID_9420, PCI_ANY_ID, PCI_ANY_ID, },
{ 0, }
};
{
struct smsc9420_pdata *pd = netdev_priv(dev);
+ if (!pd->phy_dev)
+ return -ENODEV;
+
cmd->maxtxpkt = 1;
cmd->maxrxpkt = 1;
return phy_ethtool_gset(pd->phy_dev, cmd);
{
struct smsc9420_pdata *pd = netdev_priv(dev);
+ if (!pd->phy_dev)
+ return -ENODEV;
+
return phy_ethtool_sset(pd->phy_dev, cmd);
}
static int smsc9420_ethtool_nway_reset(struct net_device *netdev)
{
struct smsc9420_pdata *pd = netdev_priv(netdev);
+
+ if (!pd->phy_dev)
+ return -ENODEV;
+
return phy_start_aneg(pd->phy_dev);
}
+static int smsc9420_ethtool_getregslen(struct net_device *dev)
+{
+ /* all smsc9420 registers plus all phy registers */
+ return 0x100 + (32 * sizeof(u32));
+}
+
+static void
+smsc9420_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+ void *buf)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ struct phy_device *phy_dev = pd->phy_dev;
+ unsigned int i, j = 0;
+ u32 *data = buf;
+
+ regs->version = smsc9420_reg_read(pd, ID_REV);
+ for (i = 0; i < 0x100; i += (sizeof(u32)))
+ data[j++] = smsc9420_reg_read(pd, i);
+
+ // cannot read phy registers if the net device is down
+ if (!phy_dev)
+ return;
+
+ for (i = 0; i <= 31; i++)
+ data[j++] = smsc9420_mii_read(phy_dev->bus, phy_dev->addr, i);
+}
+
+static void smsc9420_eeprom_enable_access(struct smsc9420_pdata *pd)
+{
+ unsigned int temp = smsc9420_reg_read(pd, GPIO_CFG);
+ temp &= ~GPIO_CFG_EEPR_EN_;
+ smsc9420_reg_write(pd, GPIO_CFG, temp);
+ msleep(1);
+}
+
+static int smsc9420_eeprom_send_cmd(struct smsc9420_pdata *pd, u32 op)
+{
+ int timeout = 100;
+ u32 e2cmd;
+
+ smsc_dbg(HW, "op 0x%08x", op);
+ if (smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+ smsc_warn(HW, "Busy at start");
+ return -EBUSY;
+ }
+
+ e2cmd = op | E2P_CMD_EPC_BUSY_;
+ smsc9420_reg_write(pd, E2P_CMD, e2cmd);
+
+ do {
+ msleep(1);
+ e2cmd = smsc9420_reg_read(pd, E2P_CMD);
+ } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout));
+
+ if (!timeout) {
+ smsc_info(HW, "TIMED OUT");
+ return -EAGAIN;
+ }
+
+ if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
+ smsc_info(HW, "Error occured during eeprom operation");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smsc9420_eeprom_read_location(struct smsc9420_pdata *pd,
+ u8 address, u8 *data)
+{
+ u32 op = E2P_CMD_EPC_CMD_READ_ | address;
+ int ret;
+
+ smsc_dbg(HW, "address 0x%x", address);
+ ret = smsc9420_eeprom_send_cmd(pd, op);
+
+ if (!ret)
+ data[address] = smsc9420_reg_read(pd, E2P_DATA);
+
+ return ret;
+}
+
+static int smsc9420_eeprom_write_location(struct smsc9420_pdata *pd,
+ u8 address, u8 data)
+{
+ u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+ int ret;
+
+ smsc_dbg(HW, "address 0x%x, data 0x%x", address, data);
+ ret = smsc9420_eeprom_send_cmd(pd, op);
+
+ if (!ret) {
+ op = E2P_CMD_EPC_CMD_WRITE_ | address;
+ smsc9420_reg_write(pd, E2P_DATA, (u32)data);
+ ret = smsc9420_eeprom_send_cmd(pd, op);
+ }
+
+ return ret;
+}
+
+static int smsc9420_ethtool_get_eeprom_len(struct net_device *dev)
+{
+ return SMSC9420_EEPROM_SIZE;
+}
+
+static int smsc9420_ethtool_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u8 eeprom_data[SMSC9420_EEPROM_SIZE];
+ int len, i;
+
+ smsc9420_eeprom_enable_access(pd);
+
+ len = min(eeprom->len, SMSC9420_EEPROM_SIZE);
+ for (i = 0; i < len; i++) {
+ int ret = smsc9420_eeprom_read_location(pd, i, eeprom_data);
+ if (ret < 0) {
+ eeprom->len = 0;
+ return ret;
+ }
+ }
+
+ memcpy(data, &eeprom_data[eeprom->offset], len);
+ eeprom->magic = SMSC9420_EEPROM_MAGIC;
+ eeprom->len = len;
+ return 0;
+}
+
+static int smsc9420_ethtool_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ int ret;
+
+ if (eeprom->magic != SMSC9420_EEPROM_MAGIC)
+ return -EINVAL;
+
+ smsc9420_eeprom_enable_access(pd);
+ smsc9420_eeprom_send_cmd(pd, E2P_CMD_EPC_CMD_EWEN_);
+ ret = smsc9420_eeprom_write_location(pd, eeprom->offset, *data);
+ smsc9420_eeprom_send_cmd(pd, E2P_CMD_EPC_CMD_EWDS_);
+
+ /* Single byte write, according to man page */
+ eeprom->len = 1;
+
+ return ret;
+}
+
static const struct ethtool_ops smsc9420_ethtool_ops = {
.get_settings = smsc9420_ethtool_get_settings,
.set_settings = smsc9420_ethtool_set_settings,
.set_msglevel = smsc9420_ethtool_set_msglevel,
.nway_reset = smsc9420_ethtool_nway_reset,
.get_link = ethtool_op_get_link,
+ .get_eeprom_len = smsc9420_ethtool_get_eeprom_len,
+ .get_eeprom = smsc9420_ethtool_get_eeprom,
+ .set_eeprom = smsc9420_ethtool_set_eeprom,
+ .get_regs_len = smsc9420_ethtool_getregslen,
+ .get_regs = smsc9420_ethtool_getregs,
};
/* Sets the device MAC address to dev_addr */
static void smsc9420_stop_tx(struct smsc9420_pdata *pd)
{
u32 dmac_control, mac_cr, dma_intr_ena;
- int timeOut = 1000;
+ int timeout = 1000;
/* disable TX DMAC */
dmac_control = smsc9420_reg_read(pd, DMAC_CONTROL);
smsc9420_reg_write(pd, DMAC_CONTROL, dmac_control);
/* Wait max 10ms for transmit process to stop */
- while (timeOut--) {
+ while (--timeout) {
if (smsc9420_reg_read(pd, DMAC_STATUS) & DMAC_STS_TS_)
break;
udelay(10);
}
- if (!timeOut)
+ if (!timeout)
smsc_warn(IFDOWN, "TX DMAC failed to stop");
/* ACK Tx DMAC stop bit */
static void smsc9420_stop_rx(struct smsc9420_pdata *pd)
{
- int timeOut = 1000;
+ int timeout = 1000;
u32 mac_cr, dmac_control, dma_intr_ena;
/* mask RX DMAC interrupts */
smsc9420_pci_flush_write(pd);
/* wait up to 10ms for receive to stop */
- while (timeOut--) {
+ while (--timeout) {
if (smsc9420_reg_read(pd, DMAC_STATUS) & DMAC_STS_RS_)
break;
udelay(10);
}
- if (!timeOut)
+ if (!timeout)
smsc_warn(IFDOWN, "RX DMAC did not stop! timeout.");
/* ACK the Rx DMAC stop bit */
smsc9420_pci_flush_write(pd);
ints_to_clear |= (DMAC_STS_RX_ | DMAC_STS_NIS_);
- netif_rx_schedule(pd->dev, &pd->napi);
+ napi_schedule(&pd->napi);
}
if (ints_to_clear)
return ret;
}
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void smsc9420_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smsc9420_isr(0, dev);
+ enable_irq(dev->irq);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
static void smsc9420_dmac_soft_reset(struct smsc9420_pdata *pd)
{
smsc9420_reg_write(pd, BUS_MODE, BUS_MODE_SWR_);
if (pd->rx_csum) {
u16 hw_csum = get_unaligned_le16(skb_tail_pointer(skb) +
NET_IP_ALIGN + packet_length + 4);
- put_unaligned_le16(cpu_to_le16(hw_csum), &skb->csum);
+ put_unaligned_le16(hw_csum, &skb->csum);
skb->ip_summed = CHECKSUM_COMPLETE;
}
skb->protocol = eth_type_trans(skb, dev);
netif_receive_skb(skb);
- dev->last_rx = jiffies;
}
static int smsc9420_alloc_rx_buffer(struct smsc9420_pdata *pd, int index)
smsc9420_pci_flush_write(pd);
if (work_done < budget) {
- netif_rx_complete(dev, &pd->napi);
+ napi_complete(&pd->napi);
/* re-enable RX DMA interrupts */
dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
}
}
-static int smsc9420_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t smsc9420_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct smsc9420_pdata *pd = netdev_priv(dev);
dma_addr_t mapping;
smsc9420_reg_write(pd, TX_POLL_DEMAND, 1);
smsc9420_pci_flush_write(pd);
- dev->trans_start = jiffies;
-
return NETDEV_TX_OK;
}
mac_cr &= (~MAC_CR_PRMS_);
mac_cr |= MAC_CR_MCPAS_;
mac_cr &= (~MAC_CR_HPFILT_);
- } else if (dev->mc_count > 0) {
- struct dev_mc_list *mc_list = dev->mc_list;
+ } else if (!netdev_mc_empty(dev)) {
+ struct netdev_hw_addr *ha;
u32 hash_lo = 0, hash_hi = 0;
smsc_dbg(HW, "Multicast filter enabled");
- while (mc_list) {
- u32 bit_num = smsc9420_hash(mc_list->dmi_addr);
+ netdev_for_each_mc_addr(ha, dev) {
+ u32 bit_num = smsc9420_hash(ha->addr);
u32 mask = 1 << (bit_num & 0x1F);
if (bit_num & 0x20)
else
hash_lo |= mask;
- mc_list = mc_list->next;
}
smsc9420_reg_write(pd, HASHH, hash_hi);
smsc9420_reg_write(pd, HASHL, hash_lo);
smsc9420_pci_flush_write(pd);
}
-static u8 smsc9420_resolve_flowctrl_fulldplx(u16 lcladv, u16 rmtadv)
-{
- u8 cap = 0;
-
- if (lcladv & ADVERTISE_PAUSE_CAP) {
- if (lcladv & ADVERTISE_PAUSE_ASYM) {
- if (rmtadv & LPA_PAUSE_CAP)
- cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
- else if (rmtadv & LPA_PAUSE_ASYM)
- cap = FLOW_CTRL_RX;
- } else {
- if (rmtadv & LPA_PAUSE_CAP)
- cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
- }
- } else if (lcladv & ADVERTISE_PAUSE_ASYM) {
- if ((rmtadv & LPA_PAUSE_CAP) && (rmtadv & LPA_PAUSE_ASYM))
- cap = FLOW_CTRL_TX;
- }
-
- return cap;
-}
-
static void smsc9420_phy_update_flowcontrol(struct smsc9420_pdata *pd)
{
struct phy_device *phy_dev = pd->phy_dev;
if (phy_dev->duplex == DUPLEX_FULL) {
u16 lcladv = phy_read(phy_dev, MII_ADVERTISE);
u16 rmtadv = phy_read(phy_dev, MII_LPA);
- u8 cap = smsc9420_resolve_flowctrl_fulldplx(lcladv, rmtadv);
+ u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
if (cap & FLOW_CTRL_RX)
flow = 0xFFFF0002;
smsc_info(PROBE, "PHY addr %d, phy_id 0x%08X", phydev->addr,
phydev->phy_id);
- phydev = phy_connect(dev, phydev->dev.bus_id,
- &smsc9420_phy_adjust_link, 0, PHY_INTERFACE_MODE_MII);
+ phydev = phy_connect(dev, dev_name(&phydev->dev),
+ smsc9420_phy_adjust_link, 0, PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
pr_err("%s: Could not attach to PHY\n", dev->name);
}
pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- dev->name, phydev->drv->name, phydev->dev.bus_id, phydev->irq);
+ dev->name, phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
/* mask with MAC supported features */
phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
netif_carrier_off(dev);
- /* disable, mask and acknowlege all interrupts */
+ /* disable, mask and acknowledge all interrupts */
spin_lock_irqsave(&pd->int_lock, flags);
int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
smsc9420_reg_write(pd, INT_CFG, int_cfg);
/* test the IRQ connection to the ISR */
smsc_dbg(IFUP, "Testing ISR using IRQ %d", dev->irq);
+ pd->software_irq_signal = false;
spin_lock_irqsave(&pd->int_lock, flags);
/* configure interrupt deassertion timer and enable interrupts */
smsc9420_pci_flush_write(pd);
timeout = 1000;
- pd->software_irq_signal = false;
- smp_wmb();
while (timeout--) {
if (pd->software_irq_signal)
break;
.ndo_set_multicast_list = smsc9420_set_multicast_list,
.ndo_do_ioctl = smsc9420_do_ioctl,
.ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = smsc9420_poll_controller,
+#endif /* CONFIG_NET_POLL_CONTROLLER */
};
static int __devinit
goto out_free_netdev_2;
}
- if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
printk(KERN_ERR "No usable DMA configuration, aborting.\n");
goto out_free_regions_3;
}