hw/net: Move MV88W8618 network device out of hw/arm/ directory
The Marvell 88W8618 network device is hidden in the Musicpal machine. Move it into a new unit file under the hw/net/ directory. Acked-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-id: 20220107184429.423572-4-f4bug@amsat.org Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
8ede0245e2
commit
6d81f4887f
@ -774,6 +774,8 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/arm/musicpal.c
|
||||
F: hw/net/mv88w8618_eth.c
|
||||
F: include/hw/net/mv88w8618_eth.h
|
||||
F: docs/system/arm/musicpal.rst
|
||||
|
||||
Nuvoton NPCM7xx
|
||||
|
@ -34,12 +34,12 @@
|
||||
#include "ui/pixel_ops.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/net/mv88w8618_eth.h"
|
||||
|
||||
#define MP_MISC_BASE 0x80002000
|
||||
#define MP_MISC_SIZE 0x00001000
|
||||
|
||||
#define MP_ETH_BASE 0x80008000
|
||||
#define MP_ETH_SIZE 0x00001000
|
||||
|
||||
#define MP_WLAN_BASE 0x8000C000
|
||||
#define MP_WLAN_SIZE 0x00000800
|
||||
@ -84,384 +84,6 @@
|
||||
/* Wolfson 8750 I2C address */
|
||||
#define MP_WM_ADDR 0x1A
|
||||
|
||||
/* Ethernet register offsets */
|
||||
#define MP_ETH_SMIR 0x010
|
||||
#define MP_ETH_PCXR 0x408
|
||||
#define MP_ETH_SDCMR 0x448
|
||||
#define MP_ETH_ICR 0x450
|
||||
#define MP_ETH_IMR 0x458
|
||||
#define MP_ETH_FRDP0 0x480
|
||||
#define MP_ETH_FRDP1 0x484
|
||||
#define MP_ETH_FRDP2 0x488
|
||||
#define MP_ETH_FRDP3 0x48C
|
||||
#define MP_ETH_CRDP0 0x4A0
|
||||
#define MP_ETH_CRDP1 0x4A4
|
||||
#define MP_ETH_CRDP2 0x4A8
|
||||
#define MP_ETH_CRDP3 0x4AC
|
||||
#define MP_ETH_CTDP0 0x4E0
|
||||
#define MP_ETH_CTDP1 0x4E4
|
||||
|
||||
/* MII PHY access */
|
||||
#define MP_ETH_SMIR_DATA 0x0000FFFF
|
||||
#define MP_ETH_SMIR_ADDR 0x03FF0000
|
||||
#define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */
|
||||
#define MP_ETH_SMIR_RDVALID (1 << 27)
|
||||
|
||||
/* PHY registers */
|
||||
#define MP_ETH_PHY1_BMSR 0x00210000
|
||||
#define MP_ETH_PHY1_PHYSID1 0x00410000
|
||||
#define MP_ETH_PHY1_PHYSID2 0x00610000
|
||||
|
||||
#define MP_PHY_BMSR_LINK 0x0004
|
||||
#define MP_PHY_BMSR_AUTONEG 0x0008
|
||||
|
||||
#define MP_PHY_88E3015 0x01410E20
|
||||
|
||||
/* TX descriptor status */
|
||||
#define MP_ETH_TX_OWN (1U << 31)
|
||||
|
||||
/* RX descriptor status */
|
||||
#define MP_ETH_RX_OWN (1U << 31)
|
||||
|
||||
/* Interrupt cause/mask bits */
|
||||
#define MP_ETH_IRQ_RX_BIT 0
|
||||
#define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT)
|
||||
#define MP_ETH_IRQ_TXHI_BIT 2
|
||||
#define MP_ETH_IRQ_TXLO_BIT 3
|
||||
|
||||
/* Port config bits */
|
||||
#define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */
|
||||
|
||||
/* SDMA command bits */
|
||||
#define MP_ETH_CMD_TXHI (1 << 23)
|
||||
#define MP_ETH_CMD_TXLO (1 << 22)
|
||||
|
||||
typedef struct mv88w8618_tx_desc {
|
||||
uint32_t cmdstat;
|
||||
uint16_t res;
|
||||
uint16_t bytes;
|
||||
uint32_t buffer;
|
||||
uint32_t next;
|
||||
} mv88w8618_tx_desc;
|
||||
|
||||
typedef struct mv88w8618_rx_desc {
|
||||
uint32_t cmdstat;
|
||||
uint16_t bytes;
|
||||
uint16_t buffer_size;
|
||||
uint32_t buffer;
|
||||
uint32_t next;
|
||||
} mv88w8618_rx_desc;
|
||||
|
||||
#define TYPE_MV88W8618_ETH "mv88w8618_eth"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
|
||||
|
||||
struct mv88w8618_eth_state {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
MemoryRegion *dma_mr;
|
||||
AddressSpace dma_as;
|
||||
uint32_t smir;
|
||||
uint32_t icr;
|
||||
uint32_t imr;
|
||||
int mmio_index;
|
||||
uint32_t vlan_header;
|
||||
uint32_t tx_queue[2];
|
||||
uint32_t rx_queue[4];
|
||||
uint32_t frx_queue[4];
|
||||
uint32_t cur_rx[4];
|
||||
NICState *nic;
|
||||
NICConf conf;
|
||||
};
|
||||
|
||||
static void eth_rx_desc_put(AddressSpace *dma_as, uint32_t addr,
|
||||
mv88w8618_rx_desc *desc)
|
||||
{
|
||||
cpu_to_le32s(&desc->cmdstat);
|
||||
cpu_to_le16s(&desc->bytes);
|
||||
cpu_to_le16s(&desc->buffer_size);
|
||||
cpu_to_le32s(&desc->buffer);
|
||||
cpu_to_le32s(&desc->next);
|
||||
dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
|
||||
}
|
||||
|
||||
static void eth_rx_desc_get(AddressSpace *dma_as, uint32_t addr,
|
||||
mv88w8618_rx_desc *desc)
|
||||
{
|
||||
dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
|
||||
le32_to_cpus(&desc->cmdstat);
|
||||
le16_to_cpus(&desc->bytes);
|
||||
le16_to_cpus(&desc->buffer_size);
|
||||
le32_to_cpus(&desc->buffer);
|
||||
le32_to_cpus(&desc->next);
|
||||
}
|
||||
|
||||
static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
|
||||
{
|
||||
mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
|
||||
uint32_t desc_addr;
|
||||
mv88w8618_rx_desc desc;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
desc_addr = s->cur_rx[i];
|
||||
if (!desc_addr) {
|
||||
continue;
|
||||
}
|
||||
do {
|
||||
eth_rx_desc_get(&s->dma_as, desc_addr, &desc);
|
||||
if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
|
||||
dma_memory_write(&s->dma_as, desc.buffer + s->vlan_header,
|
||||
buf, size, MEMTXATTRS_UNSPECIFIED);
|
||||
desc.bytes = size + s->vlan_header;
|
||||
desc.cmdstat &= ~MP_ETH_RX_OWN;
|
||||
s->cur_rx[i] = desc.next;
|
||||
|
||||
s->icr |= MP_ETH_IRQ_RX;
|
||||
if (s->icr & s->imr) {
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
eth_rx_desc_put(&s->dma_as, desc_addr, &desc);
|
||||
return size;
|
||||
}
|
||||
desc_addr = desc.next;
|
||||
} while (desc_addr != s->rx_queue[i]);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static void eth_tx_desc_put(AddressSpace *dma_as, uint32_t addr,
|
||||
mv88w8618_tx_desc *desc)
|
||||
{
|
||||
cpu_to_le32s(&desc->cmdstat);
|
||||
cpu_to_le16s(&desc->res);
|
||||
cpu_to_le16s(&desc->bytes);
|
||||
cpu_to_le32s(&desc->buffer);
|
||||
cpu_to_le32s(&desc->next);
|
||||
dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
|
||||
}
|
||||
|
||||
static void eth_tx_desc_get(AddressSpace *dma_as, uint32_t addr,
|
||||
mv88w8618_tx_desc *desc)
|
||||
{
|
||||
dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
|
||||
le32_to_cpus(&desc->cmdstat);
|
||||
le16_to_cpus(&desc->res);
|
||||
le16_to_cpus(&desc->bytes);
|
||||
le32_to_cpus(&desc->buffer);
|
||||
le32_to_cpus(&desc->next);
|
||||
}
|
||||
|
||||
static void eth_send(mv88w8618_eth_state *s, int queue_index)
|
||||
{
|
||||
uint32_t desc_addr = s->tx_queue[queue_index];
|
||||
mv88w8618_tx_desc desc;
|
||||
uint32_t next_desc;
|
||||
uint8_t buf[2048];
|
||||
int len;
|
||||
|
||||
do {
|
||||
eth_tx_desc_get(&s->dma_as, desc_addr, &desc);
|
||||
next_desc = desc.next;
|
||||
if (desc.cmdstat & MP_ETH_TX_OWN) {
|
||||
len = desc.bytes;
|
||||
if (len < 2048) {
|
||||
dma_memory_read(&s->dma_as, desc.buffer, buf, len,
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
qemu_send_packet(qemu_get_queue(s->nic), buf, len);
|
||||
}
|
||||
desc.cmdstat &= ~MP_ETH_TX_OWN;
|
||||
s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
|
||||
eth_tx_desc_put(&s->dma_as, desc_addr, &desc);
|
||||
}
|
||||
desc_addr = next_desc;
|
||||
} while (desc_addr != s->tx_queue[queue_index]);
|
||||
}
|
||||
|
||||
static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
mv88w8618_eth_state *s = opaque;
|
||||
|
||||
switch (offset) {
|
||||
case MP_ETH_SMIR:
|
||||
if (s->smir & MP_ETH_SMIR_OPCODE) {
|
||||
switch (s->smir & MP_ETH_SMIR_ADDR) {
|
||||
case MP_ETH_PHY1_BMSR:
|
||||
return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
|
||||
MP_ETH_SMIR_RDVALID;
|
||||
case MP_ETH_PHY1_PHYSID1:
|
||||
return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
|
||||
case MP_ETH_PHY1_PHYSID2:
|
||||
return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
|
||||
default:
|
||||
return MP_ETH_SMIR_RDVALID;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
case MP_ETH_ICR:
|
||||
return s->icr;
|
||||
|
||||
case MP_ETH_IMR:
|
||||
return s->imr;
|
||||
|
||||
case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
|
||||
return s->frx_queue[(offset - MP_ETH_FRDP0) / 4];
|
||||
|
||||
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
|
||||
return s->rx_queue[(offset - MP_ETH_CRDP0) / 4];
|
||||
|
||||
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
|
||||
return s->tx_queue[(offset - MP_ETH_CTDP0) / 4];
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void mv88w8618_eth_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
mv88w8618_eth_state *s = opaque;
|
||||
|
||||
switch (offset) {
|
||||
case MP_ETH_SMIR:
|
||||
s->smir = value;
|
||||
break;
|
||||
|
||||
case MP_ETH_PCXR:
|
||||
s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
|
||||
break;
|
||||
|
||||
case MP_ETH_SDCMR:
|
||||
if (value & MP_ETH_CMD_TXHI) {
|
||||
eth_send(s, 1);
|
||||
}
|
||||
if (value & MP_ETH_CMD_TXLO) {
|
||||
eth_send(s, 0);
|
||||
}
|
||||
if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
break;
|
||||
|
||||
case MP_ETH_ICR:
|
||||
s->icr &= value;
|
||||
break;
|
||||
|
||||
case MP_ETH_IMR:
|
||||
s->imr = value;
|
||||
if (s->icr & s->imr) {
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
break;
|
||||
|
||||
case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
|
||||
s->frx_queue[(offset - MP_ETH_FRDP0) / 4] = value;
|
||||
break;
|
||||
|
||||
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
|
||||
s->rx_queue[(offset - MP_ETH_CRDP0) / 4] =
|
||||
s->cur_rx[(offset - MP_ETH_CRDP0) / 4] = value;
|
||||
break;
|
||||
|
||||
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
|
||||
s->tx_queue[(offset - MP_ETH_CTDP0) / 4] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps mv88w8618_eth_ops = {
|
||||
.read = mv88w8618_eth_read,
|
||||
.write = mv88w8618_eth_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void eth_cleanup(NetClientState *nc)
|
||||
{
|
||||
mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
|
||||
|
||||
s->nic = NULL;
|
||||
}
|
||||
|
||||
static NetClientInfo net_mv88w8618_info = {
|
||||
.type = NET_CLIENT_DRIVER_NIC,
|
||||
.size = sizeof(NICState),
|
||||
.receive = eth_receive,
|
||||
.cleanup = eth_cleanup,
|
||||
};
|
||||
|
||||
static void mv88w8618_eth_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
|
||||
"mv88w8618-eth", MP_ETH_SIZE);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
|
||||
|
||||
if (!s->dma_mr) {
|
||||
error_setg(errp, TYPE_MV88W8618_ETH " 'dma-memory' link not set");
|
||||
return;
|
||||
}
|
||||
|
||||
address_space_init(&s->dma_as, s->dma_mr, "emac-dma");
|
||||
s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
|
||||
object_get_typename(OBJECT(dev)), dev->id, s);
|
||||
}
|
||||
|
||||
static const VMStateDescription mv88w8618_eth_vmsd = {
|
||||
.name = "mv88w8618_eth",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(smir, mv88w8618_eth_state),
|
||||
VMSTATE_UINT32(icr, mv88w8618_eth_state),
|
||||
VMSTATE_UINT32(imr, mv88w8618_eth_state),
|
||||
VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
|
||||
VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
|
||||
VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
|
||||
VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
|
||||
VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property mv88w8618_eth_properties[] = {
|
||||
DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
|
||||
DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr,
|
||||
TYPE_MEMORY_REGION, MemoryRegion *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &mv88w8618_eth_vmsd;
|
||||
device_class_set_props(dc, mv88w8618_eth_properties);
|
||||
dc->realize = mv88w8618_eth_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo mv88w8618_eth_info = {
|
||||
.name = TYPE_MV88W8618_ETH,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(mv88w8618_eth_state),
|
||||
.instance_init = mv88w8618_eth_init,
|
||||
.class_init = mv88w8618_eth_class_init,
|
||||
};
|
||||
|
||||
/* LCD register offsets */
|
||||
#define MP_LCD_IRQCTRL 0x180
|
||||
#define MP_LCD_IRQSTAT 0x184
|
||||
@ -1746,7 +1368,6 @@ static void musicpal_register_types(void)
|
||||
type_register_static(&mv88w8618_pic_info);
|
||||
type_register_static(&mv88w8618_pit_info);
|
||||
type_register_static(&mv88w8618_flashcfg_info);
|
||||
type_register_static(&mv88w8618_eth_info);
|
||||
type_register_static(&mv88w8618_wlan_info);
|
||||
type_register_static(&musicpal_lcd_info);
|
||||
type_register_static(&musicpal_gpio_info);
|
||||
|
@ -26,6 +26,7 @@ softmmu_ss.add(when: 'CONFIG_ALLWINNER_EMAC', if_true: files('allwinner_emac.c')
|
||||
softmmu_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_IMX_FEC', if_true: files('imx_fec.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-emac.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('mv88w8618_eth.c'))
|
||||
|
||||
softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_gem.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.c'))
|
||||
|
403
hw/net/mv88w8618_eth.c
Normal file
403
hw/net/mv88w8618_eth.c
Normal file
@ -0,0 +1,403 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Marvell MV88W8618 / Freecom MusicPal emulation.
|
||||
*
|
||||
* Copyright (c) 2008 Jan Kiszka
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/net/mv88w8618_eth.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "sysemu/dma.h"
|
||||
#include "net/net.h"
|
||||
|
||||
#define MP_ETH_SIZE 0x00001000
|
||||
|
||||
/* Ethernet register offsets */
|
||||
#define MP_ETH_SMIR 0x010
|
||||
#define MP_ETH_PCXR 0x408
|
||||
#define MP_ETH_SDCMR 0x448
|
||||
#define MP_ETH_ICR 0x450
|
||||
#define MP_ETH_IMR 0x458
|
||||
#define MP_ETH_FRDP0 0x480
|
||||
#define MP_ETH_FRDP1 0x484
|
||||
#define MP_ETH_FRDP2 0x488
|
||||
#define MP_ETH_FRDP3 0x48C
|
||||
#define MP_ETH_CRDP0 0x4A0
|
||||
#define MP_ETH_CRDP1 0x4A4
|
||||
#define MP_ETH_CRDP2 0x4A8
|
||||
#define MP_ETH_CRDP3 0x4AC
|
||||
#define MP_ETH_CTDP0 0x4E0
|
||||
#define MP_ETH_CTDP1 0x4E4
|
||||
|
||||
/* MII PHY access */
|
||||
#define MP_ETH_SMIR_DATA 0x0000FFFF
|
||||
#define MP_ETH_SMIR_ADDR 0x03FF0000
|
||||
#define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */
|
||||
#define MP_ETH_SMIR_RDVALID (1 << 27)
|
||||
|
||||
/* PHY registers */
|
||||
#define MP_ETH_PHY1_BMSR 0x00210000
|
||||
#define MP_ETH_PHY1_PHYSID1 0x00410000
|
||||
#define MP_ETH_PHY1_PHYSID2 0x00610000
|
||||
|
||||
#define MP_PHY_BMSR_LINK 0x0004
|
||||
#define MP_PHY_BMSR_AUTONEG 0x0008
|
||||
|
||||
#define MP_PHY_88E3015 0x01410E20
|
||||
|
||||
/* TX descriptor status */
|
||||
#define MP_ETH_TX_OWN (1U << 31)
|
||||
|
||||
/* RX descriptor status */
|
||||
#define MP_ETH_RX_OWN (1U << 31)
|
||||
|
||||
/* Interrupt cause/mask bits */
|
||||
#define MP_ETH_IRQ_RX_BIT 0
|
||||
#define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT)
|
||||
#define MP_ETH_IRQ_TXHI_BIT 2
|
||||
#define MP_ETH_IRQ_TXLO_BIT 3
|
||||
|
||||
/* Port config bits */
|
||||
#define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */
|
||||
|
||||
/* SDMA command bits */
|
||||
#define MP_ETH_CMD_TXHI (1 << 23)
|
||||
#define MP_ETH_CMD_TXLO (1 << 22)
|
||||
|
||||
typedef struct mv88w8618_tx_desc {
|
||||
uint32_t cmdstat;
|
||||
uint16_t res;
|
||||
uint16_t bytes;
|
||||
uint32_t buffer;
|
||||
uint32_t next;
|
||||
} mv88w8618_tx_desc;
|
||||
|
||||
typedef struct mv88w8618_rx_desc {
|
||||
uint32_t cmdstat;
|
||||
uint16_t bytes;
|
||||
uint16_t buffer_size;
|
||||
uint32_t buffer;
|
||||
uint32_t next;
|
||||
} mv88w8618_rx_desc;
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
|
||||
|
||||
struct mv88w8618_eth_state {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
MemoryRegion *dma_mr;
|
||||
AddressSpace dma_as;
|
||||
uint32_t smir;
|
||||
uint32_t icr;
|
||||
uint32_t imr;
|
||||
int mmio_index;
|
||||
uint32_t vlan_header;
|
||||
uint32_t tx_queue[2];
|
||||
uint32_t rx_queue[4];
|
||||
uint32_t frx_queue[4];
|
||||
uint32_t cur_rx[4];
|
||||
NICState *nic;
|
||||
NICConf conf;
|
||||
};
|
||||
|
||||
static void eth_rx_desc_put(AddressSpace *dma_as, uint32_t addr,
|
||||
mv88w8618_rx_desc *desc)
|
||||
{
|
||||
cpu_to_le32s(&desc->cmdstat);
|
||||
cpu_to_le16s(&desc->bytes);
|
||||
cpu_to_le16s(&desc->buffer_size);
|
||||
cpu_to_le32s(&desc->buffer);
|
||||
cpu_to_le32s(&desc->next);
|
||||
dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
|
||||
}
|
||||
|
||||
static void eth_rx_desc_get(AddressSpace *dma_as, uint32_t addr,
|
||||
mv88w8618_rx_desc *desc)
|
||||
{
|
||||
dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
|
||||
le32_to_cpus(&desc->cmdstat);
|
||||
le16_to_cpus(&desc->bytes);
|
||||
le16_to_cpus(&desc->buffer_size);
|
||||
le32_to_cpus(&desc->buffer);
|
||||
le32_to_cpus(&desc->next);
|
||||
}
|
||||
|
||||
static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
|
||||
{
|
||||
mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
|
||||
uint32_t desc_addr;
|
||||
mv88w8618_rx_desc desc;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
desc_addr = s->cur_rx[i];
|
||||
if (!desc_addr) {
|
||||
continue;
|
||||
}
|
||||
do {
|
||||
eth_rx_desc_get(&s->dma_as, desc_addr, &desc);
|
||||
if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
|
||||
dma_memory_write(&s->dma_as, desc.buffer + s->vlan_header,
|
||||
buf, size, MEMTXATTRS_UNSPECIFIED);
|
||||
desc.bytes = size + s->vlan_header;
|
||||
desc.cmdstat &= ~MP_ETH_RX_OWN;
|
||||
s->cur_rx[i] = desc.next;
|
||||
|
||||
s->icr |= MP_ETH_IRQ_RX;
|
||||
if (s->icr & s->imr) {
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
eth_rx_desc_put(&s->dma_as, desc_addr, &desc);
|
||||
return size;
|
||||
}
|
||||
desc_addr = desc.next;
|
||||
} while (desc_addr != s->rx_queue[i]);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static void eth_tx_desc_put(AddressSpace *dma_as, uint32_t addr,
|
||||
mv88w8618_tx_desc *desc)
|
||||
{
|
||||
cpu_to_le32s(&desc->cmdstat);
|
||||
cpu_to_le16s(&desc->res);
|
||||
cpu_to_le16s(&desc->bytes);
|
||||
cpu_to_le32s(&desc->buffer);
|
||||
cpu_to_le32s(&desc->next);
|
||||
dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
|
||||
}
|
||||
|
||||
static void eth_tx_desc_get(AddressSpace *dma_as, uint32_t addr,
|
||||
mv88w8618_tx_desc *desc)
|
||||
{
|
||||
dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
|
||||
le32_to_cpus(&desc->cmdstat);
|
||||
le16_to_cpus(&desc->res);
|
||||
le16_to_cpus(&desc->bytes);
|
||||
le32_to_cpus(&desc->buffer);
|
||||
le32_to_cpus(&desc->next);
|
||||
}
|
||||
|
||||
static void eth_send(mv88w8618_eth_state *s, int queue_index)
|
||||
{
|
||||
uint32_t desc_addr = s->tx_queue[queue_index];
|
||||
mv88w8618_tx_desc desc;
|
||||
uint32_t next_desc;
|
||||
uint8_t buf[2048];
|
||||
int len;
|
||||
|
||||
do {
|
||||
eth_tx_desc_get(&s->dma_as, desc_addr, &desc);
|
||||
next_desc = desc.next;
|
||||
if (desc.cmdstat & MP_ETH_TX_OWN) {
|
||||
len = desc.bytes;
|
||||
if (len < 2048) {
|
||||
dma_memory_read(&s->dma_as, desc.buffer, buf, len,
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
qemu_send_packet(qemu_get_queue(s->nic), buf, len);
|
||||
}
|
||||
desc.cmdstat &= ~MP_ETH_TX_OWN;
|
||||
s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
|
||||
eth_tx_desc_put(&s->dma_as, desc_addr, &desc);
|
||||
}
|
||||
desc_addr = next_desc;
|
||||
} while (desc_addr != s->tx_queue[queue_index]);
|
||||
}
|
||||
|
||||
static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
mv88w8618_eth_state *s = opaque;
|
||||
|
||||
switch (offset) {
|
||||
case MP_ETH_SMIR:
|
||||
if (s->smir & MP_ETH_SMIR_OPCODE) {
|
||||
switch (s->smir & MP_ETH_SMIR_ADDR) {
|
||||
case MP_ETH_PHY1_BMSR:
|
||||
return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
|
||||
MP_ETH_SMIR_RDVALID;
|
||||
case MP_ETH_PHY1_PHYSID1:
|
||||
return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
|
||||
case MP_ETH_PHY1_PHYSID2:
|
||||
return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
|
||||
default:
|
||||
return MP_ETH_SMIR_RDVALID;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
case MP_ETH_ICR:
|
||||
return s->icr;
|
||||
|
||||
case MP_ETH_IMR:
|
||||
return s->imr;
|
||||
|
||||
case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
|
||||
return s->frx_queue[(offset - MP_ETH_FRDP0) / 4];
|
||||
|
||||
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
|
||||
return s->rx_queue[(offset - MP_ETH_CRDP0) / 4];
|
||||
|
||||
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
|
||||
return s->tx_queue[(offset - MP_ETH_CTDP0) / 4];
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void mv88w8618_eth_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
mv88w8618_eth_state *s = opaque;
|
||||
|
||||
switch (offset) {
|
||||
case MP_ETH_SMIR:
|
||||
s->smir = value;
|
||||
break;
|
||||
|
||||
case MP_ETH_PCXR:
|
||||
s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
|
||||
break;
|
||||
|
||||
case MP_ETH_SDCMR:
|
||||
if (value & MP_ETH_CMD_TXHI) {
|
||||
eth_send(s, 1);
|
||||
}
|
||||
if (value & MP_ETH_CMD_TXLO) {
|
||||
eth_send(s, 0);
|
||||
}
|
||||
if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
break;
|
||||
|
||||
case MP_ETH_ICR:
|
||||
s->icr &= value;
|
||||
break;
|
||||
|
||||
case MP_ETH_IMR:
|
||||
s->imr = value;
|
||||
if (s->icr & s->imr) {
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
break;
|
||||
|
||||
case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
|
||||
s->frx_queue[(offset - MP_ETH_FRDP0) / 4] = value;
|
||||
break;
|
||||
|
||||
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
|
||||
s->rx_queue[(offset - MP_ETH_CRDP0) / 4] =
|
||||
s->cur_rx[(offset - MP_ETH_CRDP0) / 4] = value;
|
||||
break;
|
||||
|
||||
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
|
||||
s->tx_queue[(offset - MP_ETH_CTDP0) / 4] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps mv88w8618_eth_ops = {
|
||||
.read = mv88w8618_eth_read,
|
||||
.write = mv88w8618_eth_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void eth_cleanup(NetClientState *nc)
|
||||
{
|
||||
mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
|
||||
|
||||
s->nic = NULL;
|
||||
}
|
||||
|
||||
static NetClientInfo net_mv88w8618_info = {
|
||||
.type = NET_CLIENT_DRIVER_NIC,
|
||||
.size = sizeof(NICState),
|
||||
.receive = eth_receive,
|
||||
.cleanup = eth_cleanup,
|
||||
};
|
||||
|
||||
static void mv88w8618_eth_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
|
||||
"mv88w8618-eth", MP_ETH_SIZE);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
|
||||
|
||||
if (!s->dma_mr) {
|
||||
error_setg(errp, TYPE_MV88W8618_ETH " 'dma-memory' link not set");
|
||||
return;
|
||||
}
|
||||
|
||||
address_space_init(&s->dma_as, s->dma_mr, "emac-dma");
|
||||
s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
|
||||
object_get_typename(OBJECT(dev)), dev->id, s);
|
||||
}
|
||||
|
||||
static const VMStateDescription mv88w8618_eth_vmsd = {
|
||||
.name = "mv88w8618_eth",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(smir, mv88w8618_eth_state),
|
||||
VMSTATE_UINT32(icr, mv88w8618_eth_state),
|
||||
VMSTATE_UINT32(imr, mv88w8618_eth_state),
|
||||
VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
|
||||
VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
|
||||
VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
|
||||
VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
|
||||
VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property mv88w8618_eth_properties[] = {
|
||||
DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
|
||||
DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr,
|
||||
TYPE_MEMORY_REGION, MemoryRegion *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &mv88w8618_eth_vmsd;
|
||||
device_class_set_props(dc, mv88w8618_eth_properties);
|
||||
dc->realize = mv88w8618_eth_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo mv88w8618_eth_info = {
|
||||
.name = TYPE_MV88W8618_ETH,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(mv88w8618_eth_state),
|
||||
.instance_init = mv88w8618_eth_init,
|
||||
.class_init = mv88w8618_eth_class_init,
|
||||
};
|
||||
|
||||
static void musicpal_register_types(void)
|
||||
{
|
||||
type_register_static(&mv88w8618_eth_info);
|
||||
}
|
||||
|
||||
type_init(musicpal_register_types)
|
||||
|
12
include/hw/net/mv88w8618_eth.h
Normal file
12
include/hw/net/mv88w8618_eth.h
Normal file
@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Marvell MV88W8618 / Freecom MusicPal emulation.
|
||||
*
|
||||
* Copyright (c) 2008-2021 QEMU contributors
|
||||
*/
|
||||
#ifndef HW_NET_MV88W8618_H
|
||||
#define HW_NET_MV88W8618_H
|
||||
|
||||
#define TYPE_MV88W8618_ETH "mv88w8618_eth"
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user