[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH pic32 v3 15/16] pic32: add file pic32_ethernet.c
From: |
Serge Vakulenko |
Subject: |
[Qemu-devel] [PATCH pic32 v3 15/16] pic32: add file pic32_ethernet.c |
Date: |
Sun, 5 Jul 2015 23:15:03 -0700 |
Implement pic32 Ethernet interface.
Signed-off-by: Serge Vakulenko <address@hidden>
---
hw/mips/pic32_ethernet.c | 557 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 557 insertions(+)
create mode 100644 hw/mips/pic32_ethernet.c
diff --git a/hw/mips/pic32_ethernet.c b/hw/mips/pic32_ethernet.c
new file mode 100644
index 0000000..383effd
--- /dev/null
+++ b/hw/mips/pic32_ethernet.c
@@ -0,0 +1,557 @@
+/*
+ * Ethernet port.
+ *
+ * Copyright (C) 2014-2015 Serge Vakulenko
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * and its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and this
+ * permission notice and warranty disclaimer appear in supporting
+ * documentation, and that the name of the author not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * The author disclaim all warranties with regard to this
+ * software, including all implied warranties of merchantability
+ * and fitness. In no event shall the author be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether
+ * in an action of contract, negligence or other tortious action,
+ * arising out of or in connection with the use or performance of
+ * this software.
+ */
+#include "hw/hw.h"
+#include "exec/address-spaces.h"
+#include "sysemu/dma.h"
+#include "pic32_peripherals.h"
+
+#include "pic32mz.h"
+
+/*#define DPRINT printf */
+#ifndef DPRINT
+#define DPRINT(...) /*empty*/
+#endif
+
+/*
+ * TODO: add option to enable tracing.
+ */
+#define TRACE 0
+
+#define NAME_PIC32_ETH "pic32-eth"
+
+#define MIN_RX_SIZE 60
+
+/*
+ * PIC32 Ethernet device.
+ */
+struct _eth_t {
+ SysBusDevice parent_obj;
+
+ pic32_t *pic32;
+ NICState *eth_nic; /* virtual network interface */
+ NICConf eth_conf; /* network configuration */
+
+ uint16_t phy_reg[32]; /* PHY registers */
+
+};
+
+/*
+ * DMA buffer descriptor.
+ */
+typedef struct {
+ uint32_t hdr; /* Flags */
+ uint32_t paddr; /* Phys address of data buffer */
+ uint32_t ctl; /* TX options / RX filter status */
+ uint32_t status; /* Status */
+} eth_desc_t;
+
+/* Start of packet */
+#define DESC_SOP(d) ((d)->hdr & 0x80000000)
+#define DESC_SET_SOP(d) ((d)->hdr |= 0x80000000)
+#define DESC_CLEAR_SOP(d) ((d)->hdr &= ~0x80000000)
+
+/* End of packet */
+#define DESC_EOP(d) ((d)->hdr & 0x40000000)
+#define DESC_SET_EOP(d) ((d)->hdr |= 0x40000000)
+#define DESC_CLEAR_EOP(d) ((d)->hdr &= ~0x40000000)
+
+/* Number of data bytes */
+#define DESC_BYTECNT(d) ((d)->hdr >> 16 & 0x7ff)
+#define DESC_SET_BYTECNT(d, n) ((d)->hdr = ((d)->hdr & ~0x7ff0000) | (n) <<
16)
+
+/* Next descriptor pointer valid */
+#define DESC_NPV(d) ((d)->hdr & 0x00000100)
+#define DESC_SET_NPV(d) ((d)->hdr |= 0x00000100)
+#define DESC_CLEAR_NPV(d) ((d)->hdr &= ~0x00000100)
+
+/* Eth controller owns this desc */
+#define DESC_EOWN(d) ((d)->hdr & 0x00000080)
+#define DESC_SET_EOWN(d) ((d)->hdr |= 0x00000080)
+#define DESC_CLEAR_EOWN(d) ((d)->hdr &= ~0x00000080)
+
+/* Size of received packet */
+#define DESC_FRAMESZ(d) ((d)->status & 0xffff)
+#define DESC_SET_FRAMESZ(d, n) ((d)->status = ((d)->status & ~0xffff) | (n))
+
+/* Receive filter status */
+#define DESC_RXF(d) ((d)->ctl >> 24)
+#define DESC_SET_RXF(d, n) ((d)->ctl = ((d)->ctl & 0xffffff) | (n) << 24)
+
+/*-------------------------------------------------------------
+ * PHY declarations for SMSC LAN8720A/8740A chip.
+ */
+#define PHY_CONTROL 0 /* Basic Control Register */
+#define PHY_STATUS 1 /* Basic Status Register */
+#define PHY_ID1 2 /* PHY identifier 1 */
+#define PHY_ID2 3 /* PHY identifier 2 */
+#define PHY_MODE 18 /* Special Modes */
+#define PHY_SPECIAL 31 /* Special Control/Status Register */
+
+#define PHY_CONTROL_RESET 0x8000 /* Soft reset, bit self cleared */
+
+#define PHY_STATUS_CAP_100_FDX 0x4000 /* Can do 100Base-TX full duplex */
+#define PHY_STATUS_CAP_100_HDX 0x2000 /* Can do 100Base-TX half duplex */
+#define PHY_STATUS_CAP_10_FDX 0x1000 /* Can do 10Base-TX full duplex */
+#define PHY_STATUS_CAP_10_HDX 0x0800 /* Can do 10Base-TX half duplex */
+#define PHY_STATUS_ANEG_ACK 0x0020 /* Auto-negotiation acknowledge */
+#define PHY_STATUS_CAP_ANEG 0x0008 /* Auto-negotiation available */
+#define PHY_STATUS_LINK 0x0004 /* Link valid */
+
+#define PHY_MODE_PHYAD 0x000f /* PHY address mask */
+
+#define PHY_SPECIAL_AUTODONE 0x1000 /* Auto-negotiation is done */
+#define PHY_SPECIAL_FDX 0x0010 /* Full duplex */
+#define PHY_SPECIAL_100 0x0008 /* Speed 100 Mbps */
+
+/*
+ * Reset the PHY chip.
+ */
+static void phy_reset(eth_t *e)
+{
+ DPRINT("--- PHY reset\n");
+ e->phy_reg[PHY_ID1] = 0x0007; /* Vendor: SMSC */
+ e->phy_reg[PHY_ID2] = 0xc111; /* Device: LAN8740A */
+ e->phy_reg[PHY_CONTROL] = 0x1000;
+ e->phy_reg[PHY_MODE] = 0x4000; /* RMII interface */
+ e->phy_reg[PHY_STATUS] = PHY_STATUS_ANEG_ACK |
+ PHY_STATUS_CAP_ANEG |
+ PHY_STATUS_LINK |
+ PHY_STATUS_CAP_100_FDX |
+ PHY_STATUS_CAP_100_HDX |
+ PHY_STATUS_CAP_10_FDX |
+ PHY_STATUS_CAP_10_HDX;
+ e->phy_reg[PHY_SPECIAL] = PHY_SPECIAL_AUTODONE |
+ PHY_SPECIAL_FDX |
+ PHY_SPECIAL_100;
+}
+
+/*
+ * Reset the Ethernet controller.
+ */
+static void eth_reset(eth_t *e)
+{
+ uint8_t *macaddr = e->eth_conf.macaddr.a;
+ pic32_t *s = e->pic32;
+
+ DPRINT("--- %s()\n", __func__);
+ if (TRACE) {
+ fprintf(qemu_logfile, "--- Ethernet reset\n");
+ }
+ s->irq_clear(s, PIC32_IRQ_ETH);
+ VALUE(ETHSTAT) = 0;
+ VALUE(EMAC1SA2) = macaddr[0] | (macaddr[1] << 8);
+ VALUE(EMAC1SA1) = macaddr[2] | (macaddr[3] << 8);
+ VALUE(EMAC1SA0) = macaddr[4] | (macaddr[5] << 8);
+}
+
+/*
+ * Write to ETHCON1 register.
+ */
+void pic32_eth_control(pic32_t *s)
+{
+ eth_t *e = s->eth;
+
+ if (!(VALUE(ETHCON1) & PIC32_ETHCON1_ON)) {
+ /* Ethernet controller is disabled. */
+ DPRINT("--- %s() ETH disabled\n", __func__);
+ eth_reset(e);
+ return;
+ }
+
+ if (VALUE(ETHCON1) & PIC32_ETHCON1_TXRTS) {
+ /* Transmit request. */
+ eth_desc_t d = { 0 };
+ unsigned paddr = VALUE(ETHTXST);
+ unsigned nbytes, i;
+ unsigned char buf[2048];
+
+ d.hdr = ldl_le_phys(&address_space_memory, paddr);
+ d.paddr = ldl_le_phys(&address_space_memory, paddr + 4);
+
+ nbytes = DESC_BYTECNT(&d);
+ DPRINT("--- %s() trasmit request %u bytes\n", __func__, nbytes);
+ DPRINT("--- TX desc [%08x] = %08x %08x\n", paddr, d.hdr, d.paddr);
+ if (TRACE) {
+ fprintf(qemu_logfile, "--- Ethernet transmit request, %u bytes\n",
+ nbytes);
+ }
+ if (nbytes > 0 && nbytes <= sizeof(buf)) {
+ for (i = 0; i < nbytes; i += 4)
+ *(uint32_t *) (buf + i) = ldl_le_phys(&address_space_memory,
+ d.paddr + i);
+
+ DPRINT("--- %u bytes of data: %02x", nbytes, buf[0]);
+ for (i = 1; i < nbytes; i++) {
+ DPRINT("-%02x", buf[i]);
+ }
+ DPRINT("\n");
+
+ qemu_send_packet(qemu_get_queue(e->eth_nic), buf, nbytes);
+ }
+
+ /* Activate TXDONE interrupt. */
+ VALUE(ETHCON1) &= ~PIC32_ETHCON1_TXRTS;
+ VALUE(ETHIRQ) |= PIC32_ETHIRQ_TXDONE;
+ s->irq_raise(s, PIC32_IRQ_ETH);
+ }
+}
+
+/*
+ * Write to EMAC1MCMD register.
+ */
+void pic32_mii_command(pic32_t *s)
+{
+ eth_t *e = s->eth;
+ unsigned cmd = VALUE(EMAC1MCMD);
+ unsigned addr = VALUE(EMAC1MADR);
+ unsigned data;
+
+ if (cmd & (PIC32_EMAC1MCMD_READ | PIC32_EMAC1MCMD_SCAN)) {
+ data = e->phy_reg[addr & 0x1f];
+ /*DPRINT("--- %s() cmd = %04x, addr = %04x\n", __func__, cmd, addr);*/
+ DPRINT("--- %s() read register [%u] = %04x\n", __func__, addr & 0x1f,
+ data);
+ if (TRACE) {
+ fprintf(qemu_logfile,
+ "--- Ethernet MII read register [%u] = %04x\n",
+ addr & 0x1f, data);
+ }
+ VALUE(EMAC1MRDD) = data;
+ }
+}
+
+/*
+ * Write to EMAC1MWTD register.
+ */
+void pic32_mii_write(pic32_t *s)
+{
+ eth_t *e = s->eth;
+ unsigned addr = VALUE(EMAC1MADR);
+ unsigned data = VALUE(EMAC1MWTD);
+
+ DPRINT("--- %s() write register [%u] = %04x\n", __func__, addr & 0x1f,
+ data);
+ if (TRACE) {
+ fprintf(qemu_logfile, "--- Ethernet MII write register [%u] = %04x\n",
+ addr & 0x1f, data);
+ }
+ switch (addr & 0x1f) {
+ case PHY_CONTROL: /* Basic Control Register */
+ e->phy_reg[PHY_CONTROL] = data;
+ if (data & PHY_CONTROL_RESET) {
+ phy_reset(e);
+ }
+ break;
+ }
+}
+
+/*
+ * Return true when no receive buffers available.
+ */
+static int eth_buffer_full(pic32_t *s)
+{
+ eth_desc_t d = { 0 };
+
+ d.hdr = ldl_le_phys(&address_space_memory, VALUE(ETHRXST));
+ return !DESC_EOWN(&d);
+}
+
+/*
+ * Return true when we are ready to receive a packet.
+ */
+static int nic_can_receive(NetClientState *nc)
+{
+ eth_t *e = qemu_get_nic_opaque(nc);
+ pic32_t *s = e->pic32;
+
+ /*DPRINT("--- %s()\n", __func__);*/
+ if (!(VALUE(ETHCON1) & PIC32_ETHCON1_ON)) {
+ /* While interface is down,
+ * we can receive anything (and discard). */
+ return 1;
+ }
+
+ /* Check whether we have at least one receive buffer available. */
+ return !eth_buffer_full(s);
+}
+
+/*
+ * Receive a packet.
+ */
+static ssize_t nic_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ eth_t *e = qemu_get_nic_opaque(nc);
+ pic32_t *s = e->pic32;
+ unsigned rxfc, rxf, rxst, rxst0, rxbufsz, nbytes, len;
+ int start_of_packet;
+ uint8_t buf1[60];
+ eth_desc_t *d, desc0, desc1;
+
+ DPRINT("--- %s: %d bytes\n", __func__, (int) size);
+ if (TRACE) {
+ fprintf(qemu_logfile, "--- Ethernet receive, %u bytes\n",
+ (unsigned)size);
+ }
+ if (!(VALUE(ETHCON1) & PIC32_ETHCON1_ON)) {
+ /* Ethernet controller is down. */
+ return -1;
+ }
+
+ if (eth_buffer_full(s)) {
+ return -1;
+ }
+
+ /* Cast acceptance filter. */
+ rxfc = VALUE(ETHRXFC);
+ if (buf[0] == 0xff && buf[1] == 0xff && buf[2] == 0xff &&
+ buf[3] == 0xff && buf[4] == 0xff && buf[5] == 0xff) {
+ /* Broadcast address. */
+ if (!(rxfc & PIC32_ETHRXFC_BCEN)) {
+ return size;
+ }
+ rxf = 0x40;
+ } else if (buf[0] & 0x01) {
+ /* Multicast. */
+ if (!(rxfc & PIC32_ETHRXFC_MCEN)) {
+ return size;
+ }
+ rxf = 0x80;
+ } else if (VALUE(EMAC1SA2) == (buf[0] | (buf[1] << 8)) ||
+ VALUE(EMAC1SA2) == (buf[2] | (buf[3] << 8)) ||
+ VALUE(EMAC1SA2) == (buf[4] | (buf[5] << 8))) {
+ /* Unicast for our MAC address. */
+ if (!(rxfc & PIC32_ETHRXFC_UCEN)) {
+ return size;
+ }
+ rxf = 0x20;
+ } else {
+ /* Not-Me Unicast. */
+ if (!(rxfc & PIC32_ETHRXFC_NOTMEEN)) {
+ return size;
+ }
+ rxf = 0x00;
+ }
+
+ /* If packet too small, then expand it */
+ nbytes = size;
+ if (nbytes < MIN_RX_SIZE) {
+ memcpy(buf1, buf, nbytes);
+ memset(buf1 + nbytes, 0, MIN_RX_SIZE - nbytes);
+ buf = buf1;
+ nbytes = MIN_RX_SIZE;
+ }
+
+ /* Copy data to descriptor chain. */
+ rxst0 = VALUE(ETHRXST);
+ rxbufsz = VALUE(ETHCON2) & 0x7f0;
+ start_of_packet = 1;
+ d = &desc0;
+ rxst = rxst0;
+ for (;;) {
+ /* Get descriptor. */
+ dma_memory_read(&address_space_memory, rxst, d, 16);
+ if (!DESC_EOWN(d) || (!start_of_packet && rxst == rxst0)) {
+ DPRINT("--- No more descriptors available\n");
+ VALUE(ETHIRQ) |= PIC32_ETHIRQ_RXBUFNA;
+ break;
+ }
+
+ /* Copy data to buffer. */
+ len = nbytes;
+ if (len > rxbufsz) {
+ len = rxbufsz;
+ }
+ DPRINT("--- Copy %d bytes to %08x\n", len, d->paddr);
+ dma_memory_write(&address_space_memory, d->paddr, buf, len);
+ buf += len;
+
+ /* Update the descriptor. */
+ DESC_CLEAR_EOWN(d);
+ DESC_SET_BYTECNT(d, len);
+ if (start_of_packet) {
+ DESC_SET_SOP(d);
+ DESC_SET_FRAMESZ(d, nbytes);
+ DESC_SET_RXF(d, rxf);
+ start_of_packet = 0;
+ } else {
+ DESC_CLEAR_SOP(d);
+ }
+ if (nbytes == len) {
+ DESC_SET_EOP(d);
+ } else {
+ DESC_CLEAR_EOP(d);
+ }
+
+ /* Write the descriptor back memory (all but the first). */
+ if (d == &desc1) {
+ DPRINT("--- RX desc [%08x] = %08x ... ... %08x\n", rxst,
+ desc1.hdr, desc1.status);
+ dma_memory_write(&address_space_memory, rxst, &desc1, 16);
+ }
+
+ /* Switch to next descriptor. */
+ rxst += 16;
+ if (DESC_NPV(d)) {
+ rxst = ldl_le_phys(&address_space_memory, rxst);
+ }
+ VALUE(ETHRXST) = rxst;
+ d = &desc1;
+
+ nbytes -= len;
+ if (nbytes == 0) {
+ /* Update the first descriptor. */
+ DPRINT("--- RX desc [%08x] = %08x ... ... %08x\n",
+ rxst0, desc0.hdr, desc0.status);
+ dma_memory_write(&address_space_memory, rxst0, &desc0, 16);
+
+ /* Packet successfully received. */
+ VALUE(ETHIRQ) |= PIC32_ETHIRQ_RXDONE;
+ break;
+ }
+ }
+
+ /* Activate interrupt. */
+ s->irq_raise(s, PIC32_IRQ_ETH);
+ return size;
+}
+
+/*
+ * Deallocate the QEMU network interface.
+ */
+static void nic_cleanup(NetClientState *nc)
+{
+ eth_t *e = qemu_get_nic_opaque(nc);
+
+ /*DPRINT("--- %s()\n", __func__);*/
+ e->eth_nic = 0;
+}
+
+/*
+ * Initialize Ethernet device.
+ */
+void pic32_eth_init(pic32_t *s, NICInfo *nd)
+{
+ DPRINT("--- %s()\n", __func__);
+
+ /* Create Ethernet device. */
+ s->eth_dev = qdev_create(NULL, NAME_PIC32_ETH);
+ qdev_set_nic_properties(s->eth_dev, nd);
+ qdev_init_nofail(s->eth_dev);
+
+ /* Setup back pointer from Ethernet device to pic32 object. */
+ s->eth = OBJECT_CHECK(eth_t, s->eth_dev, NAME_PIC32_ETH);
+ s->eth->pic32 = s;
+}
+
+/*
+ * Initialize pic32-eth object.
+ */
+static int eth_object_init(SysBusDevice *sbd)
+{
+ /* Table of methods for QEMU network interface. */
+ static NetClientInfo nic_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = nic_can_receive,
+ .receive = nic_receive,
+ .cleanup = nic_cleanup,
+ };
+ DeviceState *dev = DEVICE(sbd);
+ eth_t *e = OBJECT_CHECK(eth_t, dev, NAME_PIC32_ETH);
+
+ /*DPRINT("--- %s()\n", __func__);*/
+
+ /* Initialize MAC address. */
+ qemu_macaddr_default_if_unset(&e->eth_conf.macaddr);
+
+ /* Create a QEMU network interface. */
+ e->eth_nic = qemu_new_nic(&nic_info, &e->eth_conf,
+ object_get_typename(OBJECT(dev)), dev->id, e);
+
+ qemu_format_nic_info_str(qemu_get_queue(e->eth_nic),
+ e->eth_conf.macaddr.a);
+
+ return 0;
+}
+
+/*
+ * Reset pic32-eth object.
+ */
+static void eth_object_reset(DeviceState *dev)
+{
+ eth_t *e = OBJECT_CHECK(eth_t, dev, NAME_PIC32_ETH);
+
+ /*DPRINT("--- %s()\n", __func__);*/
+ eth_reset(e);
+ phy_reset(e);
+}
+
+/*
+ * Descriptor of eth_t data structure.
+ */
+static const VMStateDescription vmstate_info = {
+ .name = NAME_PIC32_ETH,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/*
+ * Build pic32-eth object.
+ */
+static void eth_object_constructor(ObjectClass *klass, void *data)
+{
+ static Property eth_properties[] = {
+ DEFINE_NIC_PROPERTIES(eth_t, eth_conf),
+ DEFINE_PROP_END_OF_LIST(),
+ };
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = eth_object_init;
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+ dc->desc = "PIC32 Ethernet device";
+ dc->reset = eth_object_reset;
+ dc->vmsd = &vmstate_info;
+ dc->props = eth_properties;
+}
+
+/*
+ * Instantiate pic32-eth class.
+ */
+static void eth_register_types(void)
+{
+ static const TypeInfo eth_class_info = {
+ .name = NAME_PIC32_ETH,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(eth_t),
+ .class_init = eth_object_constructor,
+ };
+
+ type_register_static(ð_class_info);
+}
+
+type_init(eth_register_types)
--
2.2.2
- [Qemu-devel] [PATCH pic32 v3 11/16] pic32: add file pic32_uart.c, (continued)
- [Qemu-devel] [PATCH pic32 v3 11/16] pic32: add file pic32_uart.c, Serge Vakulenko, 2015/07/06
- [Qemu-devel] [PATCH pic32 v3 08/16] pic32: add file mips_pic32mx7.c, Serge Vakulenko, 2015/07/06
- [Qemu-devel] [PATCH pic32 v3 13/16] pic32: add file pic32_spi.c, Serge Vakulenko, 2015/07/06
- [Qemu-devel] [PATCH pic32 v3 07/16] pic32: add file pic32mz.h, Serge Vakulenko, 2015/07/06
- [Qemu-devel] [PATCH pic32 v3 14/16] pic32: add file pic32_sdcard.c, Serge Vakulenko, 2015/07/06
- [Qemu-devel] [PATCH pic32 v3 16/16] pic32: update makefiles to cover pic32 support, Serge Vakulenko, 2015/07/06
- [Qemu-devel] [PATCH pic32 v3 15/16] pic32: add file pic32_ethernet.c,
Serge Vakulenko <=
- [Qemu-devel] [PATCH pic32 v3 09/16] pic32: add file mips_pic32mz.c, Serge Vakulenko, 2015/07/06