r8169: magic.
authorfrançois romieu <romieu@fr.zoreil.com>
Mon, 3 Jan 2011 15:08:21 +0000 (15:08 +0000)
committerDavid S. Miller <davem@davemloft.net>
Tue, 4 Jan 2011 17:48:34 +0000 (09:48 -0800)
Adapted from version 8.019.00 of Realtek's r8168 driver.

Signed-off-by: Francois Romieu <romieu@fr.zoreil.com>
Cc: Hayes <hayeswang@realtek.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/r8169.c

index 0ec47f4..a73dd28 100644 (file)
@@ -278,6 +278,18 @@ enum rtl8168_8101_registers {
 };
 
 enum rtl8168_registers {
+       ERIDR                   = 0x70,
+       ERIAR                   = 0x74,
+#define ERIAR_FLAG                     0x80000000
+#define ERIAR_WRITE_CMD                        0x80000000
+#define ERIAR_READ_CMD                 0x00000000
+#define ERIAR_ADDR_BYTE_ALIGN          4
+#define ERIAR_EXGMAC                   0
+#define ERIAR_MSIX                     1
+#define ERIAR_ASF                      2
+#define ERIAR_TYPE_SHIFT               16
+#define ERIAR_BYTEEN                   0x0f
+#define ERIAR_BYTEEN_SHIFT             12
        EPHY_RXER_NUM           = 0x7c,
        OCPDR                   = 0xb0, /* OCP GPHY access */
 #define OCPDR_WRITE_CMD                        0x80000000
@@ -572,6 +584,81 @@ static int rtl8169_poll(struct napi_struct *napi, int budget);
 static const unsigned int rtl8169_rx_config =
        (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift);
 
+static u32 ocp_read(struct rtl8169_private *tp, u8 mask, u16 reg)
+{
+       void __iomem *ioaddr = tp->mmio_addr;
+       int i;
+
+       RTL_W32(OCPAR, ((u32)mask & 0x0f) << 12 | (reg & 0x0fff));
+       for (i = 0; i < 20; i++) {
+               udelay(100);
+               if (RTL_R32(OCPAR) & OCPAR_FLAG)
+                       break;
+       }
+       return RTL_R32(OCPDR);
+}
+
+static void ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg, u32 data)
+{
+       void __iomem *ioaddr = tp->mmio_addr;
+       int i;
+
+       RTL_W32(OCPDR, data);
+       RTL_W32(OCPAR, OCPAR_FLAG | ((u32)mask & 0x0f) << 12 | (reg & 0x0fff));
+       for (i = 0; i < 20; i++) {
+               udelay(100);
+               if ((RTL_R32(OCPAR) & OCPAR_FLAG) == 0)
+                       break;
+       }
+}
+
+static void rtl8168_oob_notify(void __iomem *ioaddr, u8 cmd)
+{
+       int i;
+
+       RTL_W8(ERIDR, cmd);
+       RTL_W32(ERIAR, 0x800010e8);
+       msleep(2);
+       for (i = 0; i < 5; i++) {
+               udelay(100);
+               if (!(RTL_R32(ERIDR) & ERIAR_FLAG))
+                       break;
+       }
+
+       ocp_write(ioaddr, 0x1, 0x30, 0x00000001);
+}
+
+#define OOB_CMD_RESET          0x00
+#define OOB_CMD_DRIVER_START   0x05
+#define OOB_CMD_DRIVER_STOP    0x06
+
+static void rtl8168_driver_start(struct rtl8169_private *tp)
+{
+       int i;
+
+       rtl8168_oob_notify(tp, OOB_CMD_DRIVER_START);
+
+       for (i = 0; i < 10; i++) {
+               msleep(10);
+               if (ocp_read(tp, 0x0f, 0x0010) & 0x00000800)
+                       break;
+       }
+}
+
+static void rtl8168_driver_stop(struct rtl8169_private *tp)
+{
+       int i;
+
+       rtl8168_oob_notify(tp, OOB_CMD_DRIVER_STOP);
+
+       for (i = 0; i < 10; i++) {
+               msleep(10);
+               if ((ocp_read(tp, 0x0f, 0x0010) & 0x00000800) == 0)
+                       break;
+       }
+}
+
+
 static void r8169_mdio_write(void __iomem *ioaddr, int reg_addr, int value)
 {
        int i;
@@ -2913,6 +3000,9 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                   dev->base_addr, dev->dev_addr,
                   (u32)(RTL_R32(TxConfig) & 0x9cf0f8ff), dev->irq);
 
+       if (tp->mac_version == RTL_GIGA_MAC_VER_27)
+               rtl8168_driver_start(tp);
+
        rtl8169_init_phy(dev, tp);
 
        /*
@@ -2948,6 +3038,9 @@ static void __devexit rtl8169_remove_one(struct pci_dev *pdev)
        struct net_device *dev = pci_get_drvdata(pdev);
        struct rtl8169_private *tp = netdev_priv(dev);
 
+       if (tp->mac_version == RTL_GIGA_MAC_VER_27)
+               rtl8168_driver_stop(tp);
+
        cancel_delayed_work_sync(&tp->task);
 
        unregister_netdev(dev);