aboutsummaryrefslogtreecommitdiff
path: root/hw/lan9118.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/lan9118.c')
-rw-r--r--hw/lan9118.c124
1 files changed, 118 insertions, 6 deletions
diff --git a/hw/lan9118.c b/hw/lan9118.c
index aeb0c39529..7b4fe87fca 100644
--- a/hw/lan9118.c
+++ b/hw/lan9118.c
@@ -235,11 +235,21 @@ typedef struct {
int32_t rxp_offset;
int32_t rxp_size;
int32_t rxp_pad;
+
+ uint32_t write_word_prev_offset;
+ uint32_t write_word_n;
+ uint16_t write_word_l;
+ uint16_t write_word_h;
+ uint32_t read_word_prev_offset;
+ uint32_t read_word_n;
+ uint32_t read_long;
+
+ uint32_t mode_16bit;
} lan9118_state;
static const VMStateDescription vmstate_lan9118 = {
.name = "lan9118",
- .version_id = 1,
+ .version_id = 2,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_PTIMER(timer, lan9118_state),
@@ -294,6 +304,14 @@ static const VMStateDescription vmstate_lan9118 = {
VMSTATE_INT32(rxp_offset, lan9118_state),
VMSTATE_INT32(rxp_size, lan9118_state),
VMSTATE_INT32(rxp_pad, lan9118_state),
+ VMSTATE_UINT32_V(write_word_prev_offset, lan9118_state, 2),
+ VMSTATE_UINT32_V(write_word_n, lan9118_state, 2),
+ VMSTATE_UINT16_V(write_word_l, lan9118_state, 2),
+ VMSTATE_UINT16_V(write_word_h, lan9118_state, 2),
+ VMSTATE_UINT32_V(read_word_prev_offset, lan9118_state, 2),
+ VMSTATE_UINT32_V(read_word_n, lan9118_state, 2),
+ VMSTATE_UINT32_V(read_long, lan9118_state, 2),
+ VMSTATE_UINT32_V(mode_16bit, lan9118_state, 2),
VMSTATE_END_OF_LIST()
}
};
@@ -390,7 +408,7 @@ static void lan9118_reset(DeviceState *d)
s->fifo_int = 0x48000000;
s->rx_cfg = 0;
s->tx_cfg = 0;
- s->hw_cfg = 0x00050000;
+ s->hw_cfg = s->mode_16bit ? 0x00050000 : 0x00050004;
s->pmt_ctrl &= 0x45;
s->gpio_cfg = 0;
s->txp->fifo_used = 0;
@@ -429,6 +447,9 @@ static void lan9118_reset(DeviceState *d)
s->mac_mii_data = 0;
s->mac_flow = 0;
+ s->read_word_n = 0;
+ s->write_word_n = 0;
+
phy_reset(s);
s->eeprom_writable = 0;
@@ -984,7 +1005,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
{
lan9118_state *s = (lan9118_state *)opaque;
offset &= 0xff;
-
+
//DPRINTF("Write reg 0x%02x = 0x%08x\n", (int)offset, val);
if (offset >= 0x20 && offset < 0x40) {
/* TX FIFO */
@@ -1034,7 +1055,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
/* SRST */
lan9118_reset(&s->busdev.qdev);
} else {
- s->hw_cfg = val & 0x003f300;
+ s->hw_cfg = (val & 0x003f300) | (s->hw_cfg & 0x4);
}
break;
case CSR_RX_DP_CTRL:
@@ -1113,6 +1134,46 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
lan9118_update(s);
}
+static void lan9118_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ lan9118_state *s = (lan9118_state *)opaque;
+ offset &= 0xff;
+
+ if (s->write_word_prev_offset != (offset & ~0x3)) {
+ /* New offset, reset word counter */
+ s->write_word_n = 0;
+ s->write_word_prev_offset = offset & ~0x3;
+ }
+
+ if (offset & 0x2) {
+ s->write_word_h = val;
+ } else {
+ s->write_word_l = val;
+ }
+
+ //DPRINTF("Writew reg 0x%02x = 0x%08x\n", (int)offset, val);
+ s->write_word_n++;
+ if (s->write_word_n == 2) {
+ s->write_word_n = 0;
+ lan9118_writel(s, offset & ~3, s->write_word_l +
+ (s->write_word_h << 16), 4);
+ }
+}
+
+static void lan9118_16bit_mode_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ switch (size) {
+ case 2:
+ return lan9118_writew(opaque, offset, (uint32_t)val);
+ case 4:
+ return lan9118_writel(opaque, offset, val, size);
+ }
+
+ hw_error("lan9118_write: Bad size 0x%x\n", size);
+}
+
static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
unsigned size)
{
@@ -1149,7 +1210,7 @@ static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
case CSR_TX_CFG:
return s->tx_cfg;
case CSR_HW_CFG:
- return s->hw_cfg | 0x4;
+ return s->hw_cfg;
case CSR_RX_DP_CTRL:
return 0;
case CSR_RX_FIFO_INF:
@@ -1187,12 +1248,60 @@ static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
return 0;
}
+static uint32_t lan9118_readw(void *opaque, target_phys_addr_t offset)
+{
+ lan9118_state *s = (lan9118_state *)opaque;
+ uint32_t val;
+
+ if (s->read_word_prev_offset != (offset & ~0x3)) {
+ /* New offset, reset word counter */
+ s->read_word_n = 0;
+ s->read_word_prev_offset = offset & ~0x3;
+ }
+
+ s->read_word_n++;
+ if (s->read_word_n == 1) {
+ s->read_long = lan9118_readl(s, offset & ~3, 4);
+ } else {
+ s->read_word_n = 0;
+ }
+
+ if (offset & 2) {
+ val = s->read_long >> 16;
+ } else {
+ val = s->read_long & 0xFFFF;
+ }
+
+ //DPRINTF("Readw reg 0x%02x, val 0x%x\n", (int)offset, val);
+ return val;
+}
+
+static uint64_t lan9118_16bit_mode_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ switch (size) {
+ case 2:
+ return lan9118_readw(opaque, offset);
+ case 4:
+ return lan9118_readl(opaque, offset, size);
+ }
+
+ hw_error("lan9118_read: Bad size 0x%x\n", size);
+ return 0;
+}
+
static const MemoryRegionOps lan9118_mem_ops = {
.read = lan9118_readl,
.write = lan9118_writel,
.endianness = DEVICE_NATIVE_ENDIAN,
};
+static const MemoryRegionOps lan9118_16bit_mem_ops = {
+ .read = lan9118_16bit_mode_read,
+ .write = lan9118_16bit_mode_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
static void lan9118_cleanup(VLANClientState *nc)
{
lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
@@ -1214,8 +1323,10 @@ static int lan9118_init1(SysBusDevice *dev)
lan9118_state *s = FROM_SYSBUS(lan9118_state, dev);
QEMUBH *bh;
int i;
+ const MemoryRegionOps *mem_ops =
+ s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops;
- memory_region_init_io(&s->mmio, &lan9118_mem_ops, s, "lan9118-mmio", 0x100);
+ memory_region_init_io(&s->mmio, mem_ops, s, "lan9118-mmio", 0x100);
sysbus_init_mmio(dev, &s->mmio);
sysbus_init_irq(dev, &s->irq);
qemu_macaddr_default_if_unset(&s->conf.macaddr);
@@ -1240,6 +1351,7 @@ static int lan9118_init1(SysBusDevice *dev)
static Property lan9118_properties[] = {
DEFINE_NIC_PROPERTIES(lan9118_state, conf),
+ DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0),
DEFINE_PROP_END_OF_LIST(),
};