aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/etraxfs_eth.c55
1 files changed, 51 insertions, 4 deletions
diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c
index 9ef610fd19..507937705b 100644
--- a/hw/etraxfs_eth.c
+++ b/hw/etraxfs_eth.c
@@ -30,6 +30,12 @@
#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);