Staging: octeon-ethernet: Convert to use PHY Abstraction Layer.
authorDavid Daney <ddaney@caviumnetworks.com>
Wed, 14 Oct 2009 19:04:42 +0000 (12:04 -0700)
committerRalf Baechle <ralf@linux-mips.org>
Thu, 17 Dec 2009 01:57:00 +0000 (01:57 +0000)
The octeon-ethernet driver shares an mdio bus with the octeon-mgmt
driver.  Here we convert the octeon-ethernet driver to use the PHY
Abstraction Layer.

Signed-off-by: David Daney <ddaney@caviumnetworks.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
drivers/staging/octeon/Kconfig
drivers/staging/octeon/ethernet-mdio.c
drivers/staging/octeon/ethernet-mdio.h
drivers/staging/octeon/ethernet-proc.c
drivers/staging/octeon/ethernet-rgmii.c
drivers/staging/octeon/ethernet-sgmii.c
drivers/staging/octeon/ethernet-xaui.c
drivers/staging/octeon/ethernet.c
drivers/staging/octeon/octeon-ethernet.h

index 536e238..638ad6b 100644 (file)
@@ -1,7 +1,8 @@
 config OCTEON_ETHERNET
        tristate "Cavium Networks Octeon Ethernet support"
        depends on CPU_CAVIUM_OCTEON
-       select MII
+       select PHYLIB
+       select MDIO_OCTEON
        help
          This driver supports the builtin ethernet ports on Cavium
          Networks' products in the Octeon family. This driver supports the
index 31a58e5..05a5cc0 100644 (file)
@@ -26,7 +26,8 @@
 **********************************************************************/
 #include <linux/kernel.h>
 #include <linux/ethtool.h>
-#include <linux/mii.h>
+#include <linux/phy.h>
+
 #include <net/dst.h>
 
 #include <asm/octeon/octeon.h>
 #include "ethernet-defines.h"
 #include "octeon-ethernet.h"
 #include "ethernet-mdio.h"
+#include "ethernet-util.h"
 
 #include "cvmx-helper-board.h"
 
 #include "cvmx-smix-defs.h"
 
-DECLARE_MUTEX(mdio_sem);
-
-/**
- * Perform an MII read. Called by the generic MII routines
- *
- * @dev:      Device to perform read for
- * @phy_id:   The MII phy id
- * @location: Register location to read
- * Returns Result from the read or zero on failure
- */
-static int cvm_oct_mdio_read(struct net_device *dev, int phy_id, int location)
-{
-       union cvmx_smix_cmd smi_cmd;
-       union cvmx_smix_rd_dat smi_rd;
-
-       smi_cmd.u64 = 0;
-       smi_cmd.s.phy_op = 1;
-       smi_cmd.s.phy_adr = phy_id;
-       smi_cmd.s.reg_adr = location;
-       cvmx_write_csr(CVMX_SMIX_CMD(0), smi_cmd.u64);
-
-       do {
-               if (!in_interrupt())
-                       yield();
-               smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(0));
-       } while (smi_rd.s.pending);
-
-       if (smi_rd.s.val)
-               return smi_rd.s.dat;
-       else
-               return 0;
-}
-
-static int cvm_oct_mdio_dummy_read(struct net_device *dev, int phy_id,
-                                  int location)
-{
-       return 0xffff;
-}
-
-/**
- * Perform an MII write. Called by the generic MII routines
- *
- * @dev:      Device to perform write for
- * @phy_id:   The MII phy id
- * @location: Register location to write
- * @val:      Value to write
- */
-static void cvm_oct_mdio_write(struct net_device *dev, int phy_id, int location,
-                              int val)
-{
-       union cvmx_smix_cmd smi_cmd;
-       union cvmx_smix_wr_dat smi_wr;
-
-       smi_wr.u64 = 0;
-       smi_wr.s.dat = val;
-       cvmx_write_csr(CVMX_SMIX_WR_DAT(0), smi_wr.u64);
-
-       smi_cmd.u64 = 0;
-       smi_cmd.s.phy_op = 0;
-       smi_cmd.s.phy_adr = phy_id;
-       smi_cmd.s.reg_adr = location;
-       cvmx_write_csr(CVMX_SMIX_CMD(0), smi_cmd.u64);
-
-       do {
-               if (!in_interrupt())
-                       yield();
-               smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(0));
-       } while (smi_wr.s.pending);
-}
-
-static void cvm_oct_mdio_dummy_write(struct net_device *dev, int phy_id,
-                                    int location, int val)
-{
-}
-
 static void cvm_oct_get_drvinfo(struct net_device *dev,
                                struct ethtool_drvinfo *info)
 {
@@ -125,49 +52,37 @@ static void cvm_oct_get_drvinfo(struct net_device *dev,
 static int cvm_oct_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
        struct octeon_ethernet *priv = netdev_priv(dev);
-       int ret;
 
-       down(&mdio_sem);
-       ret = mii_ethtool_gset(&priv->mii_info, cmd);
-       up(&mdio_sem);
+       if (priv->phydev)
+               return phy_ethtool_gset(priv->phydev, cmd);
 
-       return ret;
+       return -EINVAL;
 }
 
 static int cvm_oct_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
        struct octeon_ethernet *priv = netdev_priv(dev);
-       int ret;
 
-       down(&mdio_sem);
-       ret = mii_ethtool_sset(&priv->mii_info, cmd);
-       up(&mdio_sem);
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       if (priv->phydev)
+               return phy_ethtool_sset(priv->phydev, cmd);
 
-       return ret;
+       return -EINVAL;
 }
 
 static int cvm_oct_nway_reset(struct net_device *dev)
 {
        struct octeon_ethernet *priv = netdev_priv(dev);
-       int ret;
 
-       down(&mdio_sem);
-       ret = mii_nway_restart(&priv->mii_info);
-       up(&mdio_sem);
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
 
-       return ret;
-}
+       if (priv->phydev)
+               return phy_start_aneg(priv->phydev);
 
-static u32 cvm_oct_get_link(struct net_device *dev)
-{
-       struct octeon_ethernet *priv = netdev_priv(dev);
-       u32 ret;
-
-       down(&mdio_sem);
-       ret = mii_link_ok(&priv->mii_info);
-       up(&mdio_sem);
-
-       return ret;
+       return -EINVAL;
 }
 
 const struct ethtool_ops cvm_oct_ethtool_ops = {
@@ -175,7 +90,7 @@ const struct ethtool_ops cvm_oct_ethtool_ops = {
        .get_settings = cvm_oct_get_settings,
        .set_settings = cvm_oct_set_settings,
        .nway_reset = cvm_oct_nway_reset,
-       .get_link = cvm_oct_get_link,
+       .get_link = ethtool_op_get_link,
        .get_sg = ethtool_op_get_sg,
        .get_tx_csum = ethtool_op_get_tx_csum,
 };
@@ -191,41 +106,78 @@ const struct ethtool_ops cvm_oct_ethtool_ops = {
 int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
        struct octeon_ethernet *priv = netdev_priv(dev);
-       struct mii_ioctl_data *data = if_mii(rq);
-       unsigned int duplex_chg;
-       int ret;
 
-       down(&mdio_sem);
-       ret = generic_mii_ioctl(&priv->mii_info, data, cmd, &duplex_chg);
-       up(&mdio_sem);
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       if (!priv->phydev)
+               return -EINVAL;
+
+       return phy_mii_ioctl(priv->phydev, if_mii(rq), cmd);
+}
 
-       return ret;
+static void cvm_oct_adjust_link(struct net_device *dev)
+{
+       struct octeon_ethernet *priv = netdev_priv(dev);
+       cvmx_helper_link_info_t link_info;
+
+       if (priv->last_link != priv->phydev->link) {
+               priv->last_link = priv->phydev->link;
+               link_info.u64 = 0;
+               link_info.s.link_up = priv->last_link ? 1 : 0;
+               link_info.s.full_duplex = priv->phydev->duplex ? 1 : 0;
+               link_info.s.speed = priv->phydev->speed;
+               cvmx_helper_link_set( priv->port, link_info);
+               if (priv->last_link) {
+                       netif_carrier_on(dev);
+                       if (priv->queue != -1)
+                               DEBUGPRINT("%s: %u Mbps %s duplex, "
+                                          "port %2d, queue %2d\n",
+                                          dev->name, priv->phydev->speed,
+                                          priv->phydev->duplex ?
+                                               "Full" : "Half",
+                                          priv->port, priv->queue);
+                       else
+                               DEBUGPRINT("%s: %u Mbps %s duplex, "
+                                          "port %2d, POW\n",
+                                          dev->name, priv->phydev->speed,
+                                          priv->phydev->duplex ?
+                                               "Full" : "Half",
+                                          priv->port);
+               } else {
+                       netif_carrier_off(dev);
+                       DEBUGPRINT("%s: Link down\n", dev->name);
+               }
+       }
 }
 
+
 /**
- * Setup the MDIO device structures
+ * Setup the PHY
  *
  * @dev:    Device to setup
  *
  * Returns Zero on success, negative on failure
  */
-int cvm_oct_mdio_setup_device(struct net_device *dev)
+int cvm_oct_phy_setup_device(struct net_device *dev)
 {
        struct octeon_ethernet *priv = netdev_priv(dev);
-       int phy_id = cvmx_helper_board_get_mii_address(priv->port);
-       if (phy_id != -1) {
-               priv->mii_info.dev = dev;
-               priv->mii_info.phy_id = phy_id;
-               priv->mii_info.phy_id_mask = 0xff;
-               priv->mii_info.supports_gmii = 1;
-               priv->mii_info.reg_num_mask = 0x1f;
-               priv->mii_info.mdio_read = cvm_oct_mdio_read;
-               priv->mii_info.mdio_write = cvm_oct_mdio_write;
-       } else {
-               /* Supply dummy MDIO routines so the kernel won't crash
-                  if the user tries to read them */
-               priv->mii_info.mdio_read = cvm_oct_mdio_dummy_read;
-               priv->mii_info.mdio_write = cvm_oct_mdio_dummy_write;
+
+       int phy_addr = cvmx_helper_board_get_mii_address(priv->port);
+       if (phy_addr != -1) {
+               char phy_id[20];
+
+               snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "0", phy_addr);
+
+               priv->phydev = phy_connect(dev, phy_id, cvm_oct_adjust_link, 0,
+                                       PHY_INTERFACE_MODE_GMII);
+
+               if (IS_ERR(priv->phydev)) {
+                       priv->phydev = NULL;
+                       return -1;
+               }
+               priv->last_link = 0;
+               phy_start_aneg(priv->phydev);
        }
        return 0;
 }
index b3328ae..55d0614 100644 (file)
@@ -43,4 +43,4 @@
 
 extern const struct ethtool_ops cvm_oct_ethtool_ops;
 int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-int cvm_oct_mdio_setup_device(struct net_device *dev);
+int cvm_oct_phy_setup_device(struct net_device *dev);
index 8fa88fc..16308d4 100644 (file)
@@ -25,7 +25,6 @@
  * Contact Cavium Networks for more information
 **********************************************************************/
 #include <linux/kernel.h>
-#include <linux/mii.h>
 #include <linux/seq_file.h>
 #include <linux/proc_fs.h>
 #include <net/dst.h>
 #include "cvmx-helper.h"
 #include "cvmx-pip.h"
 
-static unsigned long long cvm_oct_stats_read_switch(struct net_device *dev,
-                                                   int phy_id, int offset)
-{
-       struct octeon_ethernet *priv = netdev_priv(dev);
-
-       priv->mii_info.mdio_write(dev, phy_id, 0x1d, 0xcc00 | offset);
-       return ((uint64_t) priv->mii_info.
-               mdio_read(dev, phy_id,
-                         0x1e) << 16) | (uint64_t) priv->mii_info.
-           mdio_read(dev, phy_id, 0x1f);
-}
-
-static int cvm_oct_stats_switch_show(struct seq_file *m, void *v)
-{
-       static const int ports[] = { 0, 1, 2, 3, 9, -1 };
-       struct net_device *dev = cvm_oct_device[0];
-       int index = 0;
-
-       while (ports[index] != -1) {
-
-               /* Latch port */
-               struct octeon_ethernet *priv = netdev_priv(dev);
-
-               priv->mii_info.mdio_write(dev, 0x1b, 0x1d,
-                                         0xdc00 | ports[index]);
-               seq_printf(m, "\nSwitch Port %d\n", ports[index]);
-               seq_printf(m, "InGoodOctets:   %12llu\t"
-                          "OutOctets:      %12llu\t"
-                          "64 Octets:      %12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b,
-                                                    0x00) |
-                          (cvm_oct_stats_read_switch(dev, 0x1b, 0x01) << 32),
-                          cvm_oct_stats_read_switch(dev, 0x1b,
-                                                    0x0E) |
-                          (cvm_oct_stats_read_switch(dev, 0x1b, 0x0F) << 32),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x08));
-
-               seq_printf(m, "InBadOctets:    %12llu\t"
-                          "OutUnicast:     %12llu\t"
-                          "65-127 Octets:  %12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x02),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x10),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x09));
-
-               seq_printf(m, "InUnicast:      %12llu\t"
-                          "OutBroadcasts:  %12llu\t"
-                          "128-255 Octets: %12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x04),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x13),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x0A));
-
-               seq_printf(m, "InBroadcasts:   %12llu\t"
-                          "OutMulticasts:  %12llu\t"
-                          "256-511 Octets: %12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x06),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x12),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x0B));
-
-               seq_printf(m, "InMulticasts:   %12llu\t"
-                          "OutPause:       %12llu\t"
-                          "512-1023 Octets:%12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x07),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x15),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x0C));
-
-               seq_printf(m, "InPause:        %12llu\t"
-                          "Excessive:      %12llu\t"
-                          "1024-Max Octets:%12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x16),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x11),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x0D));
-
-               seq_printf(m, "InUndersize:    %12llu\t"
-                          "Collisions:     %12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x18),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x1E));
-
-               seq_printf(m, "InFragments:    %12llu\t"
-                          "Deferred:       %12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x19),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x05));
-
-               seq_printf(m, "InOversize:     %12llu\t"
-                          "Single:         %12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x1A),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x14));
-
-               seq_printf(m, "InJabber:       %12llu\t"
-                          "Multiple:       %12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x1B),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x17));
-
-               seq_printf(m, "In RxErr:       %12llu\t"
-                          "OutFCSErr:      %12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x1C),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x03));
-
-               seq_printf(m, "InFCSErr:       %12llu\t"
-                          "Late:           %12llu\n",
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x1D),
-                          cvm_oct_stats_read_switch(dev, 0x1b, 0x1F));
-               index++;
-       }
-       return 0;
-}
-
 /**
  * User is reading /proc/octeon_ethernet_stats
  *
@@ -215,11 +108,6 @@ static int cvm_oct_stats_show(struct seq_file *m, void *v)
                }
        }
 
-       if (cvm_oct_device[0]) {
-               priv = netdev_priv(cvm_oct_device[0]);
-               if (priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
-                       cvm_oct_stats_switch_show(m, v);
-       }
        return 0;
 }
 
index fbaa465..3820f1e 100644 (file)
@@ -147,32 +147,36 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
                cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
                               gmxx_rxx_int_reg.u64);
        }
-
-       link_info = cvmx_helper_link_autoconf(priv->port);
-       priv->link_info = link_info.u64;
+       if (priv->phydev == NULL) {
+               link_info = cvmx_helper_link_autoconf(priv->port);
+               priv->link_info = link_info.u64;
+       }
        spin_unlock_irqrestore(&global_register_lock, flags);
 
-       /* Tell Linux */
-       if (link_info.s.link_up) {
-
-               if (!netif_carrier_ok(dev))
-                       netif_carrier_on(dev);
-               if (priv->queue != -1)
-                       DEBUGPRINT
-                           ("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
-                            dev->name, link_info.s.speed,
-                            (link_info.s.full_duplex) ? "Full" : "Half",
-                            priv->port, priv->queue);
-               else
-                       DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, POW\n",
-                                  dev->name, link_info.s.speed,
-                                  (link_info.s.full_duplex) ? "Full" : "Half",
-                                  priv->port);
-       } else {
-
-               if (netif_carrier_ok(dev))
-                       netif_carrier_off(dev);
-               DEBUGPRINT("%s: Link down\n", dev->name);
+       if (priv->phydev == NULL) {
+               /* Tell core. */
+               if (link_info.s.link_up) {
+                       if (!netif_carrier_ok(dev))
+                               netif_carrier_on(dev);
+                       if (priv->queue != -1)
+                               DEBUGPRINT("%s: %u Mbps %s duplex, "
+                                          "port %2d, queue %2d\n",
+                                          dev->name, link_info.s.speed,
+                                          (link_info.s.full_duplex) ?
+                                               "Full" : "Half",
+                                          priv->port, priv->queue);
+                       else
+                               DEBUGPRINT("%s: %u Mbps %s duplex, "
+                                          "port %2d, POW\n",
+                                          dev->name, link_info.s.speed,
+                                          (link_info.s.full_duplex) ?
+                                               "Full" : "Half",
+                                          priv->port);
+               } else {
+                       if (netif_carrier_ok(dev))
+                               netif_carrier_off(dev);
+                       DEBUGPRINT("%s: Link down\n", dev->name);
+               }
        }
 }
 
index 2b54996..6061d01 100644 (file)
@@ -113,7 +113,7 @@ int cvm_oct_sgmii_init(struct net_device *dev)
        struct octeon_ethernet *priv = netdev_priv(dev);
        cvm_oct_common_init(dev);
        dev->netdev_ops->ndo_stop(dev);
-       if (!octeon_is_simulation())
+       if (!octeon_is_simulation() && priv->phydev == NULL)
                priv->poll = cvm_oct_sgmii_poll;
 
        /* FIXME: Need autoneg logic */
index 0c2e7cc..ee3dc41 100644 (file)
@@ -112,7 +112,7 @@ int cvm_oct_xaui_init(struct net_device *dev)
        struct octeon_ethernet *priv = netdev_priv(dev);
        cvm_oct_common_init(dev);
        dev->netdev_ops->ndo_stop(dev);
-       if (!octeon_is_simulation())
+       if (!octeon_is_simulation() && priv->phydev == NULL)
                priv->poll = cvm_oct_xaui_poll;
 
        return 0;
index 492c502..4cfd4b1 100644 (file)
@@ -30,7 +30,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/delay.h>
-#include <linux/mii.h>
+#include <linux/phy.h>
 
 #include <net/dst.h>
 
@@ -132,8 +132,6 @@ static struct timer_list cvm_oct_poll_timer;
  */
 struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
 
-extern struct semaphore mdio_sem;
-
 /**
  * Periodic timer tick for slow management operations
  *
@@ -160,13 +158,8 @@ static void cvm_do_timer(unsigned long arg)
                goto out;
 
        priv = netdev_priv(cvm_oct_device[port]);
-       if (priv->poll) {
-               /* skip polling if we don't get the lock */
-               if (!down_trylock(&mdio_sem)) {
-                       priv->poll(cvm_oct_device[port]);
-                       up(&mdio_sem);
-               }
-       }
+       if (priv->poll)
+               priv->poll(cvm_oct_device[port]);
 
        queues_per_port = cvmx_pko_get_num_queues(port);
        /* Drain any pending packets in the free list */
@@ -524,7 +517,7 @@ int cvm_oct_common_init(struct net_device *dev)
        dev->features |= NETIF_F_LLTX;
        SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops);
 
-       cvm_oct_mdio_setup_device(dev);
+       cvm_oct_phy_setup_device(dev);
        dev->netdev_ops->ndo_set_mac_address(dev, &sa);
        dev->netdev_ops->ndo_change_mtu(dev, dev->mtu);
 
@@ -540,7 +533,10 @@ int cvm_oct_common_init(struct net_device *dev)
 
 void cvm_oct_common_uninit(struct net_device *dev)
 {
-       /* Currently nothing to do */
+       struct octeon_ethernet *priv = netdev_priv(dev);
+
+       if (priv->phydev)
+               phy_disconnect(priv->phydev);
 }
 
 static const struct net_device_ops cvm_oct_npi_netdev_ops = {
@@ -627,6 +623,8 @@ static const struct net_device_ops cvm_oct_pow_netdev_ops = {
 #endif
 };
 
+extern void octeon_mdiobus_force_mod_depencency(void);
+
 /**
  * Module/ driver initialization. Creates the linux network
  * devices.
@@ -640,6 +638,7 @@ static int __init cvm_oct_init_module(void)
        int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
        int qos;
 
+       octeon_mdiobus_force_mod_depencency();
        pr_notice("cavium-ethernet %s\n", OCTEON_ETHERNET_VERSION);
 
        if (OCTEON_IS_MODEL(OCTEON_CN52XX))
index 3aef987..402a15b 100644 (file)
@@ -50,9 +50,9 @@ struct octeon_ethernet {
        /* List of outstanding tx buffers per queue */
        struct sk_buff_head tx_free_list[16];
        /* Device statistics */
-       struct net_device_stats stats
-;      /* Generic MII info structure */
-       struct mii_if_info mii_info;
+       struct net_device_stats stats;
+       struct phy_device *phydev;
+       unsigned int last_link;
        /* Last negotiated link state */
        uint64_t link_info;
        /* Called periodically to check link status */