emac: Fix clock control for 405EX and 405EXr chips
[safe/jmp/linux-2.6] / drivers / net / smsc9420.c
index b04bfb2..4e15ae0 100644 (file)
@@ -293,6 +293,151 @@ static int smsc9420_ethtool_nway_reset(struct net_device *netdev)
        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);
+
+       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,
@@ -301,6 +446,11 @@ static const struct ethtool_ops smsc9420_ethtool_ops = {
        .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 */
@@ -352,7 +502,7 @@ static void smsc9420_check_mac_address(struct net_device *dev)
 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);
@@ -360,13 +510,13 @@ static void smsc9420_stop_tx(struct smsc9420_pdata *pd)
        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 */
@@ -450,7 +600,7 @@ static void smsc9420_free_rx_ring(struct smsc9420_pdata *pd)
 
 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 */
@@ -471,13 +621,13 @@ static void smsc9420_stop_rx(struct smsc9420_pdata *pd)
        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 */
@@ -520,7 +670,7 @@ static irqreturn_t smsc9420_isr(int irq, void *dev_id)
                        smsc9420_pci_flush_write(pd);
 
                        ints_to_clear |= (DMAC_STS_RX_ | DMAC_STS_NIS_);
-                       netif_rx_schedule(pd->dev, &pd->napi);
+                       netif_rx_schedule(&pd->napi);
                }
 
                if (ints_to_clear)
@@ -550,6 +700,15 @@ static irqreturn_t smsc9420_isr(int irq, void *dev_id)
        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_);
@@ -734,7 +893,7 @@ static int smsc9420_rx_poll(struct napi_struct *napi, int budget)
        smsc9420_pci_flush_write(pd);
 
        if (work_done < budget) {
-               netif_rx_complete(dev, &pd->napi);
+               netif_rx_complete(&pd->napi);
 
                /* re-enable RX DMA interrupts */
                dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
@@ -925,28 +1084,6 @@ static void smsc9420_set_multicast_list(struct net_device *dev)
        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;
@@ -955,7 +1092,7 @@ static void smsc9420_phy_update_flowcontrol(struct smsc9420_pdata *pd)
        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;
@@ -1245,6 +1382,7 @@ static int smsc9420_open(struct net_device *dev)
 
        /* 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 */
@@ -1260,8 +1398,6 @@ static int smsc9420_open(struct net_device *dev)
        smsc9420_pci_flush_write(pd);
 
        timeout = 1000;
-       pd->software_irq_signal = false;
-       smp_wmb();
        while (timeout--) {
                if (pd->software_irq_signal)
                        break;
@@ -1418,6 +1554,10 @@ static const struct net_device_ops smsc9420_netdev_ops = {
        .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