Staging: octeon-ethernet: Convert to use PHY Abstraction Layer.
[linux-2.6-block.git] / drivers / staging / octeon / ethernet-mdio.c
index 31a58e50892417b57f58b28374e6a320eaff5c43..05a5cc0f43eda40a1b358edec808dfad7ce039b7 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;
 }