forcedeth: mgmt unit interface
Ayaz Abdulla [Sat, 7 Feb 2009 08:23:57 +0000 (00:23 -0800)]
This patch updates the logic used to communicate with the mgmt unit. It
also adds a version check for a newer mgmt unit firmware.

* Fixed udelay to schedule_timeout_uninterruptible

Signed-off-by: Ayaz Abdulla <aabdulla@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

drivers/net/forcedeth.c

index 7825be7..da7c9ee 100644 (file)
@@ -157,6 +157,9 @@ enum {
 #define NVREG_XMITCTL_HOST_SEMA_ACQ    0x0000f000
 #define NVREG_XMITCTL_HOST_LOADED      0x00004000
 #define NVREG_XMITCTL_TX_PATH_EN       0x01000000
+#define NVREG_XMITCTL_DATA_START       0x00100000
+#define NVREG_XMITCTL_DATA_READY       0x00010000
+#define NVREG_XMITCTL_DATA_ERROR       0x00020000
        NvRegTransmitterStatus = 0x088,
 #define NVREG_XMITSTAT_BUSY    0x01
 
@@ -289,8 +292,10 @@ enum {
 #define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE    0x04
 #define NVREG_WAKEUPFLAGS_ENABLE       0x1111
 
-       NvRegPatternCRC = 0x204,
-       NvRegPatternMask = 0x208,
+       NvRegMgmtUnitGetVersion = 0x204,
+#define NVREG_MGMTUNITGETVERSION       0x01
+       NvRegMgmtUnitVersion = 0x208,
+#define NVREG_MGMTUNITVERSION          0x08
        NvRegPowerCap = 0x268,
 #define NVREG_POWERCAP_D3SUPP  (1<<30)
 #define NVREG_POWERCAP_D2SUPP  (1<<26)
@@ -303,6 +308,8 @@ enum {
 #define NVREG_POWERSTATE_D1            0x0001
 #define NVREG_POWERSTATE_D2            0x0002
 #define NVREG_POWERSTATE_D3            0x0003
+       NvRegMgmtUnitControl = 0x278,
+#define NVREG_MGMTUNITCONTROL_INUSE    0x20000
        NvRegTxCnt = 0x280,
        NvRegTxZeroReXmt = 0x284,
        NvRegTxOneReXmt = 0x288,
@@ -758,6 +765,8 @@ struct fe_priv {
        u32 register_size;
        int rx_csum;
        u32 mac_in_use;
+       int mgmt_version;
+       int mgmt_sema;
 
        void __iomem *base;
 
@@ -5182,6 +5191,7 @@ static void nv_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
 /* The mgmt unit and driver use a semaphore to access the phy during init */
 static int nv_mgmt_acquire_sema(struct net_device *dev)
 {
+       struct fe_priv *np = netdev_priv(dev);
        u8 __iomem *base = get_hwbase(dev);
        int i;
        u32 tx_ctrl, mgmt_sema;
@@ -5204,8 +5214,10 @@ static int nv_mgmt_acquire_sema(struct net_device *dev)
                /* verify that semaphore was acquired */
                tx_ctrl = readl(base + NvRegTransmitterControl);
                if (((tx_ctrl & NVREG_XMITCTL_HOST_SEMA_MASK) == NVREG_XMITCTL_HOST_SEMA_ACQ) &&
-                   ((tx_ctrl & NVREG_XMITCTL_MGMT_SEMA_MASK) == NVREG_XMITCTL_MGMT_SEMA_FREE))
+                   ((tx_ctrl & NVREG_XMITCTL_MGMT_SEMA_MASK) == NVREG_XMITCTL_MGMT_SEMA_FREE)) {
+                       np->mgmt_sema = 1;
                        return 1;
+               }
                else
                        udelay(50);
        }
@@ -5213,6 +5225,51 @@ static int nv_mgmt_acquire_sema(struct net_device *dev)
        return 0;
 }
 
+static void nv_mgmt_release_sema(struct net_device *dev)
+{
+       struct fe_priv *np = netdev_priv(dev);
+       u8 __iomem *base = get_hwbase(dev);
+       u32 tx_ctrl;
+
+       if (np->driver_data & DEV_HAS_MGMT_UNIT) {
+               if (np->mgmt_sema) {
+                       tx_ctrl = readl(base + NvRegTransmitterControl);
+                       tx_ctrl &= ~NVREG_XMITCTL_HOST_SEMA_ACQ;
+                       writel(tx_ctrl, base + NvRegTransmitterControl);
+               }
+       }
+}
+
+
+static int nv_mgmt_get_version(struct net_device *dev)
+{
+       struct fe_priv *np = netdev_priv(dev);
+       u8 __iomem *base = get_hwbase(dev);
+       u32 data_ready = readl(base + NvRegTransmitterControl);
+       u32 data_ready2 = 0;
+       unsigned long start;
+       int ready = 0;
+
+       writel(NVREG_MGMTUNITGETVERSION, base + NvRegMgmtUnitGetVersion);
+       writel(data_ready ^ NVREG_XMITCTL_DATA_START, base + NvRegTransmitterControl);
+       start = jiffies;
+       while (time_before(jiffies, start + 5*HZ)) {
+               data_ready2 = readl(base + NvRegTransmitterControl);
+               if ((data_ready & NVREG_XMITCTL_DATA_READY) != (data_ready2 & NVREG_XMITCTL_DATA_READY)) {
+                       ready = 1;
+                       break;
+               }
+               schedule_timeout_uninterruptible(1);
+       }
+
+       if (!ready || (data_ready2 & NVREG_XMITCTL_DATA_ERROR))
+               return 0;
+
+       np->mgmt_version = readl(base + NvRegMgmtUnitVersion) & NVREG_MGMTUNITVERSION;
+
+       return 1;
+}
+
 static int nv_open(struct net_device *dev)
 {
        struct fe_priv *np = netdev_priv(dev);
@@ -5784,19 +5841,26 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
 
        if (id->driver_data & DEV_HAS_MGMT_UNIT) {
                /* management unit running on the mac? */
-               if (readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_PHY_INIT) {
-                       np->mac_in_use = readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_ST;
-                       dprintk(KERN_INFO "%s: mgmt unit is running. mac in use %x.\n", pci_name(pci_dev), np->mac_in_use);
-                       if (nv_mgmt_acquire_sema(dev)) {
-                               /* management unit setup the phy already? */
-                               if ((readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_MASK) ==
-                                   NVREG_XMITCTL_SYNC_PHY_INIT) {
-                                       /* phy is inited by mgmt unit */
-                                       phyinitialized = 1;
-                                       dprintk(KERN_INFO "%s: Phy already initialized by mgmt unit.\n", pci_name(pci_dev));
-                               } else {
-                                       /* we need to init the phy */
-                               }
+               if ((readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_ST) &&
+                   (readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_PHY_INIT) &&
+                   nv_mgmt_acquire_sema(dev) &&
+                   nv_mgmt_get_version(dev)) {
+                       np->mac_in_use = 1;
+                       if (np->mgmt_version > 0) {
+                               np->mac_in_use = readl(base + NvRegMgmtUnitControl) & NVREG_MGMTUNITCONTROL_INUSE;
+                       }
+                       dprintk(KERN_INFO "%s: mgmt unit is running. mac in use %x.\n",
+                               pci_name(pci_dev), np->mac_in_use);
+                       /* management unit setup the phy already? */
+                       if (np->mac_in_use &&
+                           ((readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_MASK) ==
+                            NVREG_XMITCTL_SYNC_PHY_INIT)) {
+                               /* phy is inited by mgmt unit */
+                               phyinitialized = 1;
+                               dprintk(KERN_INFO "%s: Phy already initialized by mgmt unit.\n",
+                                       pci_name(pci_dev));
+                       } else {
+                               /* we need to init the phy */
                        }
                }
        }
@@ -5958,6 +6022,8 @@ static void __devexit nv_remove(struct pci_dev *pci_dev)
        /* restore any phy related changes */
        nv_restore_phy(dev);
 
+       nv_mgmt_release_sema(dev);
+
        /* free all structures */
        free_rings(dev);
        iounmap(get_hwbase(dev));