]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
inmates: Power-up PHY on E1000 takeover
authorJan Kiszka <jan.kiszka@siemens.com>
Sun, 1 Feb 2015 10:38:18 +0000 (11:38 +0100)
committerJan Kiszka <jan.kiszka@siemens.com>
Mon, 2 Feb 2015 15:57:00 +0000 (16:57 +0100)
Clear the power-down bit in the PHY control register in case the
previous user turned it off. Linux does so since about 3.15.

Note that we do not try to reset the PHY. Getting it running again with
the proper link speed turned out to be too complicated (too many PHY
variants) for this little demo.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
inmates/demos/x86/e1000-demo.c

index 49d9a36b8cfca04efb29279ce2313b58a4cd81c6..d7c2a662fba14786860473bc35e0953750b8fa81 100644 (file)
 # define E1000_CTRL_RST                (1 << 26)
 #define E1000_REG_STATUS       0x0008
 # define E1000_STATUS_LU       (1 << 1)
+# define E1000_STATUS_SPEEDSHFT        6
+# define E1000_STATUS_SPEED    (3 << E1000_STATUS_SPEEDSHFT)
 #define E1000_REG_EERD         0x0014
 # define E1000_EERD_START      (1 << 0)
 # define E1000_EERD_DONE       (1 << 4)
 # define E1000_EERD_ADDR_SHIFT 8
 # define E1000_EERD_DATA_SHIFT 16
+#define E1000_REG_MDIC         0x0020
+# define E1000_MDIC_REGADD_SHFT        16
+# define E1000_MDIC_PHYADD_SHFT        21
+# define E1000_MDIC_OP_WRITE   (0x1 << 26)
+# define E1000_MDIC_OP_READ    (0x2 << 26)
+# define E1000_MDIC_READY      (0x1 << 28)
 #define E1000_REG_RCTL         0x0100
 # define E1000_RCTL_EN         (1 << 1)
 # define E1000_RCTL_BAM                (1 << 15)
 #define E1000_REG_RAH          0x5404
 # define E1000_RAH_AV          (1 << 31)
 
+#define E1000_MAX_PHYADD       7
+
+#define E1000_PHY_CTRL         0
+# define E1000_PHYC_POWER_DOWN (1 << 11)
+#define E1000_PHY_PSTATUS      1
+#define E1000_PHY_ID1          2
+
 struct eth_header {
        u8      dst[6];
        u8      src[6];
@@ -117,12 +132,39 @@ struct e1000_txd {
 #define RX_BUFFER_SIZE         2048
 #define TX_DESCRIPTORS         8
 
+static const char *speed_info[] = { "10", "100", "1000", "1000" };
+
 static void *mmiobar;
 static u8 buffer[RX_DESCRIPTORS * RX_BUFFER_SIZE];
 static struct e1000_rxd rx_ring[RX_DESCRIPTORS];
 static struct e1000_txd tx_ring[TX_DESCRIPTORS];
 static unsigned int rx_idx, tx_idx;
 static struct eth_header tx_packet;
+static unsigned int phyadd;
+
+static u16 phy_read(unsigned int reg)
+{
+       u32 val;
+
+       mmio_write32(mmiobar + E1000_REG_MDIC,
+                    (reg << E1000_MDIC_REGADD_SHFT) |
+                    (phyadd << E1000_MDIC_PHYADD_SHFT) | E1000_MDIC_OP_READ);
+       do {
+               val = mmio_read32(mmiobar + E1000_REG_MDIC);
+               cpu_relax();
+       } while (!(val & E1000_MDIC_READY));
+
+       return (u16)val;
+}
+
+static void phy_write(unsigned int reg, u16 val)
+{
+       mmio_write32(mmiobar + E1000_REG_MDIC,
+                    val | (reg << E1000_MDIC_REGADD_SHFT) |
+                    (phyadd << E1000_MDIC_PHYADD_SHFT) | E1000_MDIC_OP_WRITE);
+       while (!(mmio_read32(mmiobar + E1000_REG_MDIC) & E1000_MDIC_READY))
+               cpu_relax();
+}
 
 static void send_packet(void *buffer, unsigned int size)
 {
@@ -202,12 +244,24 @@ void inmate_main(void)
        val &= ~(E1000_CTRL_LRST | E1000_CTRL_FRCSPD);
        val |= E1000_CTRL_ASDE | E1000_CTRL_SLU;
        mmio_write32(mmiobar + E1000_REG_CTRL, val);
-       printk("Reset done, waiting for link...");
 
+       for (phyadd = 0; phyadd <= E1000_MAX_PHYADD; phyadd++)
+               if (phy_read(E1000_PHY_ID1) != 0)
+                       break;
+       printk("PHY address: %d\n", phyadd);
+       /* power up again in case the previous user turned it off */
+       phy_write(E1000_PHY_CTRL,
+                 phy_read(E1000_PHY_CTRL) & ~E1000_PHYC_POWER_DOWN);
+
+       printk("Waiting for link...");
        while (!(mmio_read32(mmiobar + E1000_REG_STATUS) & E1000_STATUS_LU))
                cpu_relax();
        printk(" ok\n");
 
+       val = mmio_read32(mmiobar + E1000_REG_STATUS) & E1000_STATUS_SPEED;
+       val >>= E1000_STATUS_SPEEDSHFT;
+       printk("Link speed: %s Mb/s\n", speed_info[val]);
+
        if (mmio_read32(mmiobar + E1000_REG_RAH) & E1000_RAH_AV) {
                *(u32 *)mac = mmio_read32(mmiobar + E1000_REG_RAL);
                *(u16 *)&mac[4] = mmio_read32(mmiobar + E1000_REG_RAH);