hw/lan9118: Add basic 16-bit mode support.

Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Evgeny Voevodin 2012-02-16 09:56:06 +00:00 committed by Peter Maydell
parent 12c775db14
commit 1248f8d4cb
1 changed files with 118 additions and 6 deletions

View File

@ -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(),
};