ETRAX-FS: Correct ethernet PHY diagnostics register reads.
authoredgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>
Mon, 22 Sep 2008 20:34:18 +0000 (20:34 +0000)
committeredgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>
Mon, 22 Sep 2008 20:34:18 +0000 (20:34 +0000)
* Correct ethernet PHY diagnostics register reads.
* Add friendly names for the speed/duplex fields.
* Report duplex mismatches between MAC and PHY.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5300 c046a42c-6fe2-441c-8c8c-71466251a162

hw/etraxfs_eth.c

index 9ef610f..5079377 100644 (file)
 
 #define D(x)
 
+/* Advertisement control register. */
+#define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
+#define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
+#define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
+#define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
+
 /* 
  * The MDIO extensions in the TDK PHY model were reversed engineered from the 
  * linux driver (PHYID and Diagnostics reg).
@@ -78,9 +84,12 @@ static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req)
                        int speed_100 = 0;
 
                        /* Are we advertising 100 half or 100 duplex ? */
-                       speed_100 = !!(phy->regs[4] & 0x180);
+                       speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF);
+                       speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL);
+
                        /* Are we advertising 10 duplex or 100 duplex ? */
-                       duplex = !!(phy->regs[4] & 0x180);
+                       duplex = !!(phy->regs[4] & ADVERTISE_100FULL);
+                       duplex |= !!(phy->regs[4] & ADVERTISE_10FULL);
                        r = (speed_100 << 10) | (duplex << 11);
                }
                break;
@@ -322,10 +331,40 @@ struct fs_eth
 
        /* MDIO bus.  */
        struct qemu_mdio mdio_bus;
+       unsigned int phyaddr;
+       int duplex_mismatch;
+
        /* PHY.  */
        struct qemu_phy phy;
 };
 
+static void eth_validate_duplex(struct fs_eth *eth)
+{
+       struct qemu_phy *phy;
+       unsigned int phy_duplex;
+       unsigned int mac_duplex;
+       int new_mm = 0;
+
+       phy = eth->mdio_bus.devs[eth->phyaddr];
+       phy_duplex = !!(phy->read(phy, 18) & (1 << 11));
+       mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128);
+
+       if (mac_duplex != phy_duplex)
+               new_mm = 1;
+
+       if (eth->regs[RW_GEN_CTRL] & 1) {
+               if (new_mm != eth->duplex_mismatch) {
+                       if (new_mm)
+                               printf("HW: WARNING "
+                                      "ETH duplex mismatch MAC=%d PHY=%d\n",
+                                      mac_duplex, phy_duplex);
+                       else
+                               printf("HW: ETH duplex ok.\n");
+               }
+               eth->duplex_mismatch = new_mm;
+       }
+}
+
 static uint32_t eth_rinvalid (void *opaque, target_phys_addr_t addr)
 {
        struct fs_eth *eth = opaque;
@@ -418,11 +457,18 @@ eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
                        /* Attach an MDIO/PHY abstraction.  */
                        if (value & 2)
                                eth->mdio_bus.mdio = value & 1;
-                       if (eth->mdio_bus.mdc != (value & 4))
+                       if (eth->mdio_bus.mdc != (value & 4)) {
                                mdio_cycle(&eth->mdio_bus);
+                               eth_validate_duplex(eth);
+                       }
                        eth->mdio_bus.mdc = !!(value & 4);
                        break;
 
+               case RW_REC_CTRL:
+                       eth->regs[addr] = value;
+                       eth_validate_duplex(eth);
+                       break;
+
                default:
                        eth->regs[addr] = value;
                        D(printf ("%s %x %x\n",
@@ -591,8 +637,9 @@ void *etraxfs_eth_init(NICInfo *nd, CPUState *env,
        eth->dma_in = dma + 1;
 
        /* Connect the phy.  */
+       eth->phyaddr = 1;
        tdk_init(&eth->phy);
-       mdio_attach(&eth->mdio_bus, &eth->phy, 0x1);
+       mdio_attach(&eth->mdio_bus, &eth->phy, eth->phyaddr);
 
        eth->ethregs = cpu_register_io_memory(0, eth_read, eth_write, eth);
        cpu_register_physical_memory (base, 0x5c, eth->ethregs);