/*
- * QEMU Xilinx GEM emulation
+ * QEMU Cadence GEM emulation
*
* Copyright (c) 2011 Xilinx, Inc.
*
#include <zlib.h> /* For crc32 */
-#include "hw/sysbus.h"
-#include "net/net.h"
+#include "hw/net/cadence_gem.h"
#include "net/checksum.h"
#ifdef CADENCE_GEM_ERR_DEBUG
#define GEM_IER (0x00000028/4) /* Interrupt Enable reg */
#define GEM_IDR (0x0000002C/4) /* Interrupt Disable reg */
#define GEM_IMR (0x00000030/4) /* Interrupt Mask reg */
-#define GEM_PHYMNTNC (0x00000034/4) /* Phy Maintaince reg */
+#define GEM_PHYMNTNC (0x00000034/4) /* Phy Maintenance reg */
#define GEM_RXPAUSE (0x00000038/4) /* RX Pause Time reg */
#define GEM_TXPAUSE (0x0000003C/4) /* TX Pause Time reg */
#define GEM_TXPARTIALSF (0x00000040/4) /* TX Partial Store and Forward */
#define GEM_DESCONF6 (0x00000294/4)
#define GEM_DESCONF7 (0x00000298/4)
-#define GEM_MAXREG (0x00000640/4) /* Last valid GEM address */
-
/*****************************************/
#define GEM_NWCTRL_TXSTART 0x00000200 /* Transmit Enable */
#define GEM_NWCTRL_TXENA 0x00000008 /* Transmit Enable */
#define GEM_NWCTRL_LOCALLOOP 0x00000002 /* Local Loopback */
#define GEM_NWCFG_STRIP_FCS 0x00020000 /* Strip FCS field */
-#define GEM_NWCFG_LERR_DISC 0x00010000 /* Discard RX frames with lenth err */
+#define GEM_NWCFG_LERR_DISC 0x00010000 /* Discard RX frames with len err */
#define GEM_NWCFG_BUFF_OFST_M 0x0000C000 /* Receive buffer offset mask */
#define GEM_NWCFG_BUFF_OFST_S 14 /* Receive buffer offset shift */
#define GEM_NWCFG_UCAST_HASH 0x00000080 /* accept unicast if hash match */
#define GEM_NWCFG_BCAST_REJ 0x00000020 /* Reject broadcast packets */
#define GEM_NWCFG_PROMISC 0x00000010 /* Accept all packets */
-#define GEM_DMACFG_RBUFSZ_M 0x007F0000 /* DMA RX Buffer Size mask */
+#define GEM_DMACFG_RBUFSZ_M 0x00FF0000 /* DMA RX Buffer Size mask */
#define GEM_DMACFG_RBUFSZ_S 16 /* DMA RX Buffer Size shift */
#define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */
#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */
desc[1] |= R_DESC_1_RX_SAR_MATCH;
}
-#define TYPE_CADENCE_GEM "cadence_gem"
-#define GEM(obj) OBJECT_CHECK(GemState, (obj), TYPE_CADENCE_GEM)
-
-typedef struct GemState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- NICState *nic;
- NICConf conf;
- qemu_irq irq;
-
- /* GEM registers backing store */
- uint32_t regs[GEM_MAXREG];
- /* Mask of register bits which are write only */
- uint32_t regs_wo[GEM_MAXREG];
- /* Mask of register bits which are read only */
- uint32_t regs_ro[GEM_MAXREG];
- /* Mask of register bits which are clear on read */
- uint32_t regs_rtc[GEM_MAXREG];
- /* Mask of register bits which are write 1 to clear */
- uint32_t regs_w1c[GEM_MAXREG];
-
- /* PHY registers backing store */
- uint16_t phy_regs[32];
-
- uint8_t phy_loop; /* Are we in phy loopback? */
-
- /* The current DMA descriptor pointers */
- uint32_t rx_desc_addr;
- uint32_t tx_desc_addr;
-
- unsigned rx_desc[2];
-
- bool sar_active[4];
-} GemState;
-
/* The broadcast MAC address: 0xFFFFFFFFFFFF */
-const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
/*
* gem_init_register_masks:
* One time initialization.
* Set masks to identify which register bits have magical clear properties
*/
-static void gem_init_register_masks(GemState *s)
+static void gem_init_register_masks(CadenceGEMState *s)
{
- /* Mask of register bits which are read only*/
+ /* Mask of register bits which are read only */
memset(&s->regs_ro[0], 0, sizeof(s->regs_ro));
s->regs_ro[GEM_NWCTRL] = 0xFFF80000;
s->regs_ro[GEM_NWSTATUS] = 0xFFFFFFFF;
* phy_update_link:
* Make the emulated PHY link state match the QEMU "interface" state.
*/
-static void phy_update_link(GemState *s)
+static void phy_update_link(CadenceGEMState *s)
{
DB_PRINT("down %d\n", qemu_get_queue(s->nic)->link_down);
static int gem_can_receive(NetClientState *nc)
{
- GemState *s;
+ CadenceGEMState *s;
s = qemu_get_nic_opaque(nc);
- DB_PRINT("\n");
-
/* Do nothing if receive is not enabled. */
if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) {
+ if (s->can_rx_state != 1) {
+ s->can_rx_state = 1;
+ DB_PRINT("can't receive - no enable\n");
+ }
return 0;
}
+ if (rx_desc_get_ownership(s->rx_desc) == 1) {
+ if (s->can_rx_state != 2) {
+ s->can_rx_state = 2;
+ DB_PRINT("can't receive - busy buffer descriptor 0x%x\n",
+ s->rx_desc_addr);
+ }
+ return 0;
+ }
+
+ if (s->can_rx_state != 0) {
+ s->can_rx_state = 0;
+ DB_PRINT("can receive 0x%x\n", s->rx_desc_addr);
+ }
return 1;
}
* gem_update_int_status:
* Raise or lower interrupt based on current status.
*/
-static void gem_update_int_status(GemState *s)
+static void gem_update_int_status(CadenceGEMState *s)
{
if (s->regs[GEM_ISR]) {
DB_PRINT("asserting int. (0x%08x)\n", s->regs[GEM_ISR]);
* gem_receive_updatestats:
* Increment receive statistics.
*/
-static void gem_receive_updatestats(GemState *s, const uint8_t *packet,
+static void gem_receive_updatestats(CadenceGEMState *s, const uint8_t *packet,
unsigned bytes)
{
uint64_t octets;
* GEM_RM_PROMISCUOUS_ACCEPT, GEM_RX_BROADCAST_ACCEPT,
* GEM_RX_MULTICAST_HASH_ACCEPT or GEM_RX_UNICAST_HASH_ACCEPT
*/
-static int gem_mac_address_filter(GemState *s, const uint8_t *packet)
+static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet)
{
uint8_t *gem_spaddr;
int i;
return GEM_RX_REJECT;
}
-static void gem_get_rx_desc(GemState *s)
+static void gem_get_rx_desc(CadenceGEMState *s)
{
DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr);
/* read current descriptor */
*/
static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
- GemState *s;
+ CadenceGEMState *s;
unsigned rxbufsize, bytes_to_copy;
unsigned rxbuf_offset;
uint8_t rxbuf[2048];
rxbuf_ptr = (void *)buf;
} else {
unsigned crc_val;
- int crc_offset;
/* The application wants the FCS field, which QEMU does not provide.
- * We must try and caclculate one.
+ * We must try and calculate one.
*/
memcpy(rxbuf, buf, size);
memset(rxbuf + size, 0, sizeof(rxbuf) - size);
rxbuf_ptr = rxbuf;
crc_val = cpu_to_le32(crc32(0, rxbuf, MAX(size, 60)));
- if (size < 60) {
- crc_offset = 60;
- } else {
- crc_offset = size;
- }
- memcpy(rxbuf + crc_offset, &crc_val, sizeof(crc_val));
+ memcpy(rxbuf + size, &crc_val, sizeof(crc_val));
bytes_to_copy += 4;
size += 4;
* gem_transmit_updatestats:
* Increment transmit statistics.
*/
-static void gem_transmit_updatestats(GemState *s, const uint8_t *packet,
+static void gem_transmit_updatestats(CadenceGEMState *s, const uint8_t *packet,
unsigned bytes)
{
uint64_t octets;
* gem_transmit:
* Fish packets out of the descriptor ring and feed them to QEMU
*/
-static void gem_transmit(GemState *s)
+static void gem_transmit(CadenceGEMState *s)
{
unsigned desc[2];
hwaddr packet_desc_addr;
DB_PRINT("\n");
- /* The packet we will hand off to qemu.
+ /* The packet we will hand off to QEMU.
* Packets scattered across multiple descriptors are gathered to this
* one contiguous buffer first.
*/
/* read current descriptor */
packet_desc_addr = s->tx_desc_addr;
+
+ DB_PRINT("read descriptor 0x%" HWADDR_PRIx "\n", packet_desc_addr);
cpu_physical_memory_read(packet_desc_addr,
- (uint8_t *)&desc[0], sizeof(desc));
+ (uint8_t *)desc, sizeof(desc));
/* Handle all descriptors owned by hardware */
while (tx_desc_get_used(desc) == 0) {
/* Last descriptor for this packet; hand the whole thing off */
if (tx_desc_get_last(desc)) {
+ unsigned desc_first[2];
+
/* Modify the 1st descriptor of this packet to be owned by
* the processor.
*/
- cpu_physical_memory_read(s->tx_desc_addr,
- (uint8_t *)&desc[0], sizeof(desc));
- tx_desc_set_used(desc);
- cpu_physical_memory_write(s->tx_desc_addr,
- (uint8_t *)&desc[0], sizeof(desc));
- /* Advance the hardare current descriptor past this packet */
+ cpu_physical_memory_read(s->tx_desc_addr, (uint8_t *)desc_first,
+ sizeof(desc_first));
+ tx_desc_set_used(desc_first);
+ cpu_physical_memory_write(s->tx_desc_addr, (uint8_t *)desc_first,
+ sizeof(desc_first));
+ /* Advance the hardware current descriptor past this packet */
if (tx_desc_get_wrap(desc)) {
s->tx_desc_addr = s->regs[GEM_TXQBASE];
} else {
} else {
packet_desc_addr += 8;
}
+ DB_PRINT("read descriptor 0x%" HWADDR_PRIx "\n", packet_desc_addr);
cpu_physical_memory_read(packet_desc_addr,
- (uint8_t *)&desc[0], sizeof(desc));
+ (uint8_t *)desc, sizeof(desc));
}
if (tx_desc_get_used(desc)) {
}
}
-static void gem_phy_reset(GemState *s)
+static void gem_phy_reset(CadenceGEMState *s)
{
memset(&s->phy_regs[0], 0, sizeof(s->phy_regs));
s->phy_regs[PHY_REG_CONTROL] = 0x1140;
s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00;
s->phy_regs[PHY_REG_EXTSTAT] = 0x3000;
s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078;
- s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00;
+ s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0x7C00;
s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60;
s->phy_regs[PHY_REG_LED] = 0x4100;
s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A;
static void gem_reset(DeviceState *d)
{
int i;
- GemState *s = GEM(d);
+ CadenceGEMState *s = CADENCE_GEM(d);
DB_PRINT("\n");
gem_update_int_status(s);
}
-static uint16_t gem_phy_read(GemState *s, unsigned reg_num)
+static uint16_t gem_phy_read(CadenceGEMState *s, unsigned reg_num)
{
DB_PRINT("reg: %d value: 0x%04x\n", reg_num, s->phy_regs[reg_num]);
return s->phy_regs[reg_num];
}
-static void gem_phy_write(GemState *s, unsigned reg_num, uint16_t val)
+static void gem_phy_write(CadenceGEMState *s, unsigned reg_num, uint16_t val)
{
DB_PRINT("reg: %d value: 0x%04x\n", reg_num, val);
*/
static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size)
{
- GemState *s;
+ CadenceGEMState *s;
uint32_t retval;
- s = (GemState *)opaque;
+ s = (CadenceGEMState *)opaque;
offset >>= 2;
retval = s->regs[offset];
uint32_t phy_addr, reg_num;
phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT;
- if (phy_addr == BOARD_PHY_ADDRESS) {
+ if (phy_addr == BOARD_PHY_ADDRESS || phy_addr == 0) {
reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT;
retval &= 0xFFFF0000;
retval |= gem_phy_read(s, reg_num);
static void gem_write(void *opaque, hwaddr offset, uint64_t val,
unsigned size)
{
- GemState *s = (GemState *)opaque;
+ CadenceGEMState *s = (CadenceGEMState *)opaque;
uint32_t readonly;
DB_PRINT("offset: 0x%04x write: 0x%08x ", (unsigned)offset, (unsigned)val);
/* Reset to start of Q when transmit disabled. */
s->tx_desc_addr = s->regs[GEM_TXQBASE];
}
- if (val & GEM_NWCTRL_RXENA) {
+ if (gem_can_receive(qemu_get_queue(s->nic))) {
qemu_flush_queued_packets(qemu_get_queue(s->nic));
}
break;
uint32_t phy_addr, reg_num;
phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT;
- if (phy_addr == BOARD_PHY_ADDRESS) {
+ if (phy_addr == BOARD_PHY_ADDRESS || phy_addr == 0) {
reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT;
gem_phy_write(s, reg_num, val);
}
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static void gem_cleanup(NetClientState *nc)
-{
- GemState *s = qemu_get_nic_opaque(nc);
-
- DB_PRINT("\n");
- s->nic = NULL;
-}
-
static void gem_set_link(NetClientState *nc)
{
DB_PRINT("\n");
.size = sizeof(NICState),
.can_receive = gem_can_receive,
.receive = gem_receive,
- .cleanup = gem_cleanup,
.link_status_changed = gem_set_link,
};
static int gem_init(SysBusDevice *sbd)
{
DeviceState *dev = DEVICE(sbd);
- GemState *s = GEM(dev);
+ CadenceGEMState *s = CADENCE_GEM(dev);
DB_PRINT("\n");
.name = "cadence_gem",
.version_id = 2,
.minimum_version_id = 2,
- .minimum_version_id_old = 2,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, GemState, GEM_MAXREG),
- VMSTATE_UINT16_ARRAY(phy_regs, GemState, 32),
- VMSTATE_UINT8(phy_loop, GemState),
- VMSTATE_UINT32(rx_desc_addr, GemState),
- VMSTATE_UINT32(tx_desc_addr, GemState),
- VMSTATE_BOOL_ARRAY(sar_active, GemState, 4),
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, CadenceGEMState, CADENCE_GEM_MAXREG),
+ VMSTATE_UINT16_ARRAY(phy_regs, CadenceGEMState, 32),
+ VMSTATE_UINT8(phy_loop, CadenceGEMState),
+ VMSTATE_UINT32(rx_desc_addr, CadenceGEMState),
+ VMSTATE_UINT32(tx_desc_addr, CadenceGEMState),
+ VMSTATE_BOOL_ARRAY(sar_active, CadenceGEMState, 4),
VMSTATE_END_OF_LIST(),
}
};
static Property gem_properties[] = {
- DEFINE_NIC_PROPERTIES(GemState, conf),
+ DEFINE_NIC_PROPERTIES(CadenceGEMState, conf),
DEFINE_PROP_END_OF_LIST(),
};
static const TypeInfo gem_info = {
.name = TYPE_CADENCE_GEM,
.parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(GemState),
+ .instance_size = sizeof(CadenceGEMState),
.class_init = gem_class_init,
};