|
@@ -16,6 +16,7 @@
|
|
|
#include "net/net.h"
|
|
|
#include "net/eth.h"
|
|
|
#include "hw/irq.h"
|
|
|
+#include "hw/net/lan9118_phy.h"
|
|
|
#include "hw/net/lan9118.h"
|
|
|
#include "hw/ptimer.h"
|
|
|
#include "hw/qdev-properties.h"
|
|
@@ -139,14 +140,6 @@ do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0)
|
|
|
#define MAC_CR_RXEN 0x00000004
|
|
|
#define MAC_CR_RESERVED 0x7f404213
|
|
|
|
|
|
-#define PHY_INT_ENERGYON 0x80
|
|
|
-#define PHY_INT_AUTONEG_COMPLETE 0x40
|
|
|
-#define PHY_INT_FAULT 0x20
|
|
|
-#define PHY_INT_DOWN 0x10
|
|
|
-#define PHY_INT_AUTONEG_LP 0x08
|
|
|
-#define PHY_INT_PARFAULT 0x04
|
|
|
-#define PHY_INT_AUTONEG_PAGE 0x02
|
|
|
-
|
|
|
#define GPT_TIMER_EN 0x20000000
|
|
|
|
|
|
/*
|
|
@@ -228,11 +221,8 @@ struct lan9118_state {
|
|
|
uint32_t mac_mii_data;
|
|
|
uint32_t mac_flow;
|
|
|
|
|
|
- uint32_t phy_status;
|
|
|
- uint32_t phy_control;
|
|
|
- uint32_t phy_advertise;
|
|
|
- uint32_t phy_int;
|
|
|
- uint32_t phy_int_mask;
|
|
|
+ Lan9118PhyState mii;
|
|
|
+ IRQState mii_irq;
|
|
|
|
|
|
int32_t eeprom_writable;
|
|
|
uint8_t eeprom[128];
|
|
@@ -274,8 +264,8 @@ struct lan9118_state {
|
|
|
|
|
|
static const VMStateDescription vmstate_lan9118 = {
|
|
|
.name = "lan9118",
|
|
|
- .version_id = 2,
|
|
|
- .minimum_version_id = 1,
|
|
|
+ .version_id = 3,
|
|
|
+ .minimum_version_id = 3,
|
|
|
.fields = (const VMStateField[]) {
|
|
|
VMSTATE_PTIMER(timer, lan9118_state),
|
|
|
VMSTATE_UINT32(irq_cfg, lan9118_state),
|
|
@@ -301,11 +291,6 @@ static const VMStateDescription vmstate_lan9118 = {
|
|
|
VMSTATE_UINT32(mac_mii_acc, lan9118_state),
|
|
|
VMSTATE_UINT32(mac_mii_data, lan9118_state),
|
|
|
VMSTATE_UINT32(mac_flow, lan9118_state),
|
|
|
- VMSTATE_UINT32(phy_status, lan9118_state),
|
|
|
- VMSTATE_UINT32(phy_control, lan9118_state),
|
|
|
- VMSTATE_UINT32(phy_advertise, lan9118_state),
|
|
|
- VMSTATE_UINT32(phy_int, lan9118_state),
|
|
|
- VMSTATE_UINT32(phy_int_mask, lan9118_state),
|
|
|
VMSTATE_INT32(eeprom_writable, lan9118_state),
|
|
|
VMSTATE_UINT8_ARRAY(eeprom, lan9118_state, 128),
|
|
|
VMSTATE_INT32(tx_fifo_size, lan9118_state),
|
|
@@ -385,9 +370,11 @@ static void lan9118_reload_eeprom(lan9118_state *s)
|
|
|
lan9118_mac_changed(s);
|
|
|
}
|
|
|
|
|
|
-static void phy_update_irq(lan9118_state *s)
|
|
|
+static void lan9118_update_irq(void *opaque, int n, int level)
|
|
|
{
|
|
|
- if (s->phy_int & s->phy_int_mask) {
|
|
|
+ lan9118_state *s = opaque;
|
|
|
+
|
|
|
+ if (level) {
|
|
|
s->int_sts |= PHY_INT;
|
|
|
} else {
|
|
|
s->int_sts &= ~PHY_INT;
|
|
@@ -395,33 +382,10 @@ static void phy_update_irq(lan9118_state *s)
|
|
|
lan9118_update(s);
|
|
|
}
|
|
|
|
|
|
-static void phy_update_link(lan9118_state *s)
|
|
|
-{
|
|
|
- /* Autonegotiation status mirrors link status. */
|
|
|
- if (qemu_get_queue(s->nic)->link_down) {
|
|
|
- s->phy_status &= ~0x0024;
|
|
|
- s->phy_int |= PHY_INT_DOWN;
|
|
|
- } else {
|
|
|
- s->phy_status |= 0x0024;
|
|
|
- s->phy_int |= PHY_INT_ENERGYON;
|
|
|
- s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
|
|
|
- }
|
|
|
- phy_update_irq(s);
|
|
|
-}
|
|
|
-
|
|
|
static void lan9118_set_link(NetClientState *nc)
|
|
|
{
|
|
|
- phy_update_link(qemu_get_nic_opaque(nc));
|
|
|
-}
|
|
|
-
|
|
|
-static void phy_reset(lan9118_state *s)
|
|
|
-{
|
|
|
- s->phy_status = 0x7809;
|
|
|
- s->phy_control = 0x3000;
|
|
|
- s->phy_advertise = 0x01e1;
|
|
|
- s->phy_int_mask = 0;
|
|
|
- s->phy_int = 0;
|
|
|
- phy_update_link(s);
|
|
|
+ lan9118_phy_update_link(&LAN9118(qemu_get_nic_opaque(nc))->mii,
|
|
|
+ nc->link_down);
|
|
|
}
|
|
|
|
|
|
static void lan9118_reset(DeviceState *d)
|
|
@@ -478,8 +442,6 @@ static void lan9118_reset(DeviceState *d)
|
|
|
s->read_word_n = 0;
|
|
|
s->write_word_n = 0;
|
|
|
|
|
|
- phy_reset(s);
|
|
|
-
|
|
|
s->eeprom_writable = 0;
|
|
|
lan9118_reload_eeprom(s);
|
|
|
}
|
|
@@ -678,7 +640,7 @@ static void do_tx_packet(lan9118_state *s)
|
|
|
uint32_t status;
|
|
|
|
|
|
/* FIXME: Honor TX disable, and allow queueing of packets. */
|
|
|
- if (s->phy_control & 0x4000) {
|
|
|
+ if (s->mii.control & 0x4000) {
|
|
|
/* This assumes the receive routine doesn't touch the VLANClient. */
|
|
|
qemu_receive_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len);
|
|
|
} else {
|
|
@@ -834,68 +796,6 @@ static void tx_fifo_push(lan9118_state *s, uint32_t val)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static uint32_t do_phy_read(lan9118_state *s, int reg)
|
|
|
-{
|
|
|
- uint32_t val;
|
|
|
-
|
|
|
- switch (reg) {
|
|
|
- case 0: /* Basic Control */
|
|
|
- return s->phy_control;
|
|
|
- case 1: /* Basic Status */
|
|
|
- return s->phy_status;
|
|
|
- case 2: /* ID1 */
|
|
|
- return 0x0007;
|
|
|
- case 3: /* ID2 */
|
|
|
- return 0xc0d1;
|
|
|
- case 4: /* Auto-neg advertisement */
|
|
|
- return s->phy_advertise;
|
|
|
- case 5: /* Auto-neg Link Partner Ability */
|
|
|
- return 0x0f71;
|
|
|
- case 6: /* Auto-neg Expansion */
|
|
|
- return 1;
|
|
|
- /* TODO 17, 18, 27, 29, 30, 31 */
|
|
|
- case 29: /* Interrupt source. */
|
|
|
- val = s->phy_int;
|
|
|
- s->phy_int = 0;
|
|
|
- phy_update_irq(s);
|
|
|
- return val;
|
|
|
- case 30: /* Interrupt mask */
|
|
|
- return s->phy_int_mask;
|
|
|
- default:
|
|
|
- qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
- "do_phy_read: PHY read reg %d\n", reg);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static void do_phy_write(lan9118_state *s, int reg, uint32_t val)
|
|
|
-{
|
|
|
- switch (reg) {
|
|
|
- case 0: /* Basic Control */
|
|
|
- if (val & 0x8000) {
|
|
|
- phy_reset(s);
|
|
|
- break;
|
|
|
- }
|
|
|
- s->phy_control = val & 0x7980;
|
|
|
- /* Complete autonegotiation immediately. */
|
|
|
- if (val & 0x1000) {
|
|
|
- s->phy_status |= 0x0020;
|
|
|
- }
|
|
|
- break;
|
|
|
- case 4: /* Auto-neg advertisement */
|
|
|
- s->phy_advertise = (val & 0x2d7f) | 0x80;
|
|
|
- break;
|
|
|
- /* TODO 17, 18, 27, 31 */
|
|
|
- case 30: /* Interrupt mask */
|
|
|
- s->phy_int_mask = val & 0xff;
|
|
|
- phy_update_irq(s);
|
|
|
- break;
|
|
|
- default:
|
|
|
- qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
- "do_phy_write: PHY write reg %d = 0x%04x\n", reg, val);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
static void do_mac_write(lan9118_state *s, int reg, uint32_t val)
|
|
|
{
|
|
|
switch (reg) {
|
|
@@ -929,9 +829,9 @@ static void do_mac_write(lan9118_state *s, int reg, uint32_t val)
|
|
|
if (val & 2) {
|
|
|
DPRINTF("PHY write %d = 0x%04x\n",
|
|
|
(val >> 6) & 0x1f, s->mac_mii_data);
|
|
|
- do_phy_write(s, (val >> 6) & 0x1f, s->mac_mii_data);
|
|
|
+ lan9118_phy_write(&s->mii, (val >> 6) & 0x1f, s->mac_mii_data);
|
|
|
} else {
|
|
|
- s->mac_mii_data = do_phy_read(s, (val >> 6) & 0x1f);
|
|
|
+ s->mac_mii_data = lan9118_phy_read(&s->mii, (val >> 6) & 0x1f);
|
|
|
DPRINTF("PHY read %d = 0x%04x\n",
|
|
|
(val >> 6) & 0x1f, s->mac_mii_data);
|
|
|
}
|
|
@@ -1126,7 +1026,7 @@ static void lan9118_writel(void *opaque, hwaddr offset,
|
|
|
break;
|
|
|
case CSR_PMT_CTRL:
|
|
|
if (val & 0x400) {
|
|
|
- phy_reset(s);
|
|
|
+ lan9118_phy_reset(&s->mii);
|
|
|
}
|
|
|
s->pmt_ctrl &= ~0x34e;
|
|
|
s->pmt_ctrl |= (val & 0x34e);
|
|
@@ -1373,6 +1273,13 @@ static void lan9118_realize(DeviceState *dev, Error **errp)
|
|
|
const MemoryRegionOps *mem_ops =
|
|
|
s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops;
|
|
|
|
|
|
+ qemu_init_irq(&s->mii_irq, lan9118_update_irq, s, 0);
|
|
|
+ object_initialize_child(OBJECT(s), "mii", &s->mii, TYPE_LAN9118_PHY);
|
|
|
+ if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->mii), errp)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ qdev_connect_gpio_out(DEVICE(&s->mii), 0, &s->mii_irq);
|
|
|
+
|
|
|
memory_region_init_io(&s->mmio, OBJECT(dev), mem_ops, s,
|
|
|
"lan9118-mmio", 0x100);
|
|
|
sysbus_init_mmio(sbd, &s->mmio);
|