The Allwinner H3 System on Chip includes an Ethernet MAC (EMAC)
which provides 10M/100M/1000M Ethernet connectivity. This commit
adds support for the Allwinner H3 EMAC, including emulation for
the following functionality:
* DMA transfers
* MII interface
* Transmit CRC calculation
Signed-off-by: Niek Linnenbank <nieklinnenb...@gmail.com>
---
hw/arm/Kconfig | 1 +
hw/arm/allwinner-h3.c | 17 +
hw/arm/orangepi.c | 7 +
hw/net/Kconfig | 3 +
hw/net/Makefile.objs | 1 +
hw/net/allwinner-h3-emac.c | 786 +++++++++++++++++++++++++++++
hw/net/trace-events | 10 +
include/hw/arm/allwinner-h3.h | 2 +
include/hw/net/allwinner-h3-emac.h | 69 +++
9 files changed, 896 insertions(+)
create mode 100644 hw/net/allwinner-h3-emac.c
create mode 100644 include/hw/net/allwinner-h3-emac.h
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index ebf8d2325f..551cff3442 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -294,6 +294,7 @@ config ALLWINNER_A10
config ALLWINNER_H3
bool
select ALLWINNER_A10_PIT
+ select ALLWINNER_H3_EMAC
select SERIAL
select ARM_TIMER
select ARM_GIC
diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
index c2972caf88..274b8548c0 100644
--- a/hw/arm/allwinner-h3.c
+++ b/hw/arm/allwinner-h3.c
@@ -53,6 +53,9 @@ static void aw_h3_init(Object *obj)
sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0),
TYPE_AW_H3_SDHOST);
+
+ sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac),
+ TYPE_AW_H3_EMAC);
}
static void aw_h3_realize(DeviceState *dev, Error **errp)
@@ -237,6 +240,20 @@ static void aw_h3_realize(DeviceState *dev, Error
**errp)
return;
}
+ /* EMAC */
+ if (nd_table[0].used) {
+ qemu_check_nic_model(&nd_table[0], TYPE_AW_H3_EMAC);
+ qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
+ }
+ object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbusdev = SYS_BUS_DEVICE(&s->emac);
+ sysbus_mmio_map(sysbusdev, 0, AW_H3_EMAC_BASE);
+ sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_EMAC]);
+
/* Universal Serial Bus */
sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
s->irq[AW_H3_GIC_SPI_EHCI0]);
diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
index dee3efaf08..8a61eb0e69 100644
--- a/hw/arm/orangepi.c
+++ b/hw/arm/orangepi.c
@@ -61,6 +61,13 @@ static void orangepi_init(MachineState *machine)
exit(1);
}
+ /* Setup EMAC properties */
+ object_property_set_int(OBJECT(&s->h3->emac), 1, "phy-addr", &err);
+ if (err != NULL) {
+ error_reportf_err(err, "Couldn't set phy address: ");
+ exit(1);
+ }
+
/* Mark H3 object realized */
object_property_set_bool(OBJECT(s->h3), true, "realized", &err);
if (err != NULL) {
diff --git a/hw/net/Kconfig b/hw/net/Kconfig
index 3856417d42..36d3923992 100644
--- a/hw/net/Kconfig
+++ b/hw/net/Kconfig
@@ -74,6 +74,9 @@ config MIPSNET
config ALLWINNER_EMAC
bool
+config ALLWINNER_H3_EMAC
+ bool
+
config IMX_FEC
bool
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 7907d2c199..5548deb07a 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -23,6 +23,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o
common-obj-$(CONFIG_MIPSNET) += mipsnet.o
common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o
+common-obj-$(CONFIG_ALLWINNER_H3_EMAC) += allwinner-h3-emac.o
common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
common-obj-$(CONFIG_CADENCE) += cadence_gem.o
diff --git a/hw/net/allwinner-h3-emac.c b/hw/net/allwinner-h3-emac.c
new file mode 100644
index 0000000000..37f6f44406
--- /dev/null
+++ b/hw/net/allwinner-h3-emac.c
@@ -0,0 +1,786 @@
+/*
+ * Allwinner H3 EMAC emulation
+ *
+ * Copyright (C) 2019 Niek Linnenbank <nieklinnenb...@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "net/net.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "net/checksum.h"
+#include "qemu/module.h"
+#include "exec/cpu-common.h"
+#include "hw/net/allwinner-h3-emac.h"
+
+/* EMAC register offsets */
+#define REG_BASIC_CTL_0 (0x0000) /* Basic Control 0 */
+#define REG_BASIC_CTL_1 (0x0004) /* Basic Control 1 */
+#define REG_INT_STA (0x0008) /* Interrupt Status */
+#define REG_INT_EN (0x000C) /* Interrupt Enable */
+#define REG_TX_CTL_0 (0x0010) /* Transmit Control 0 */
+#define REG_TX_CTL_1 (0x0014) /* Transmit Control 1 */
+#define REG_TX_FLOW_CTL (0x001C) /* Transmit Flow Control */
+#define REG_TX_DMA_DESC_LIST (0x0020) /* Transmit Descriptor List
Address */
+#define REG_RX_CTL_0 (0x0024) /* Receive Control 0 */
+#define REG_RX_CTL_1 (0x0028) /* Receive Control 1 */
+#define REG_RX_DMA_DESC_LIST (0x0034) /* Receive Descriptor List
Address */
+#define REG_FRM_FLT (0x0038) /* Receive Frame Filter */
+#define REG_RX_HASH_0 (0x0040) /* Receive Hash Table 0 */
+#define REG_RX_HASH_1 (0x0044) /* Receive Hash Table 1 */
+#define REG_MII_CMD (0x0048) /* Management Interface
Command */
+#define REG_MII_DATA (0x004C) /* Management Interface Data */
+#define REG_ADDR_HIGH (0x0050) /* MAC Address High */
+#define REG_ADDR_LOW (0x0054) /* MAC Address Low */
+#define REG_TX_DMA_STA (0x00B0) /* Transmit DMA Status */
+#define REG_TX_CUR_DESC (0x00B4) /* Transmit Current
Descriptor */
+#define REG_TX_CUR_BUF (0x00B8) /* Transmit Current Buffer */
+#define REG_RX_DMA_STA (0x00C0) /* Receive DMA Status */
+#define REG_RX_CUR_DESC (0x00C4) /* Receive Current Descriptor */
+#define REG_RX_CUR_BUF (0x00C8) /* Receive Current Buffer */
+#define REG_RGMII_STA (0x00D0) /* RGMII Status */
+
+/* EMAC register flags */
+#define BASIC_CTL0_100Mbps (0b11 << 2)
+#define BASIC_CTL0_FD (1 << 0)
+#define BASIC_CTL1_SOFTRST (1 << 0)
+
+#define INT_STA_RGMII_LINK (1 << 16)
+#define INT_STA_RX_EARLY (1 << 13)
+#define INT_STA_RX_OVERFLOW (1 << 12)
+#define INT_STA_RX_TIMEOUT (1 << 11)
+#define INT_STA_RX_DMA_STOP (1 << 10)
+#define INT_STA_RX_BUF_UA (1 << 9)
+#define INT_STA_RX (1 << 8)
+#define INT_STA_TX_EARLY (1 << 5)
+#define INT_STA_TX_UNDERFLOW (1 << 4)
+#define INT_STA_TX_TIMEOUT (1 << 3)
+#define INT_STA_TX_BUF_UA (1 << 2)
+#define INT_STA_TX_DMA_STOP (1 << 1)
+#define INT_STA_TX (1 << 0)
+
+#define INT_EN_RX_EARLY (1 << 13)
+#define INT_EN_RX_OVERFLOW (1 << 12)
+#define INT_EN_RX_TIMEOUT (1 << 11)
+#define INT_EN_RX_DMA_STOP (1 << 10)
+#define INT_EN_RX_BUF_UA (1 << 9)
+#define INT_EN_RX (1 << 8)
+#define INT_EN_TX_EARLY (1 << 5)
+#define INT_EN_TX_UNDERFLOW (1 << 4)
+#define INT_EN_TX_TIMEOUT (1 << 3)
+#define INT_EN_TX_BUF_UA (1 << 2)
+#define INT_EN_TX_DMA_STOP (1 << 1)
+#define INT_EN_TX (1 << 0)
+
+#define TX_CTL0_TX_EN (1 << 31)
+#define TX_CTL1_TX_DMA_START (1 << 31)
+#define TX_CTL1_TX_DMA_EN (1 << 30)
+#define TX_CTL1_TX_FLUSH (1 << 0)
+
+#define RX_CTL0_RX_EN (1 << 31)
+#define RX_CTL0_STRIP_FCS (1 << 28)
+#define RX_CTL0_CRC_IPV4 (1 << 27)
+
+#define RX_CTL1_RX_DMA_START (1 << 31)
+#define RX_CTL1_RX_DMA_EN (1 << 30)
+#define RX_CTL1_RX_MD (1 << 1)
+
+#define RX_FRM_FLT_DIS_ADDR (1 << 31)
+
+#define MII_CMD_PHY_ADDR_SHIFT (12)
+#define MII_CMD_PHY_ADDR_MASK (0xf000)
+#define MII_CMD_PHY_REG_SHIFT (4)
+#define MII_CMD_PHY_REG_MASK (0xf0)
+#define MII_CMD_PHY_RW (1 << 1)
+#define MII_CMD_PHY_BUSY (1 << 0)
+
+#define TX_DMA_STA_STOP (0b000)
+#define TX_DMA_STA_RUN_FETCH (0b001)
+#define TX_DMA_STA_WAIT_STA (0b010)
+
+#define RX_DMA_STA_STOP (0b000)
+#define RX_DMA_STA_RUN_FETCH (0b001)
+#define RX_DMA_STA_WAIT_FRM (0b011)
+
+#define RGMII_LINK_UP (1 << 3)
+#define RGMII_FD (1 << 0)
+
+/* EMAC register reset values */
+#define REG_BASIC_CTL_1_RST (0x08000000)
+
+/* EMAC constants */
+#define AW_H3_EMAC_MIN_PKT_SZ (64)
+
+/* Transmit/receive frame descriptor */
+typedef struct FrameDescriptor {
+ uint32_t status;
+ uint32_t status2;
+ uint32_t addr;
+ uint32_t next;
+} FrameDescriptor;
+
+/* Frame descriptor flags */
+#define DESC_STATUS_CTL (1 << 31)
+#define DESC_STATUS2_BUF_SIZE_MASK (0x7ff)
+
+/* Transmit frame descriptor flags */
+#define TX_DESC_STATUS_LENGTH_ERR (1 << 14)
+#define TX_DESC_STATUS2_FIRST_DESC (1 << 29)
+#define TX_DESC_STATUS2_LAST_DESC (1 << 30)
+#define TX_DESC_STATUS2_CHECKSUM_MASK (0x3 << 27)
+
+/* Receive frame descriptor flags */
+#define RX_DESC_STATUS_FIRST_DESC (1 << 9)
+#define RX_DESC_STATUS_LAST_DESC (1 << 8)
+#define RX_DESC_STATUS_FRM_LEN_MASK (0x3fff0000)
+#define RX_DESC_STATUS_FRM_LEN_SHIFT (16)
+#define RX_DESC_STATUS_NO_BUF (1 << 14)
+#define RX_DESC_STATUS_HEADER_ERR (1 << 7)
+#define RX_DESC_STATUS_LENGTH_ERR (1 << 4)
+#define RX_DESC_STATUS_CRC_ERR (1 << 1)
+#define RX_DESC_STATUS_PAYLOAD_ERR (1 << 0)
+#define RX_DESC_STATUS2_RX_INT_CTL (1 << 31)
+
+/* MII register offsets */
+#define MII_REG_CR (0x0)
+#define MII_REG_ST (0x1)
+#define MII_REG_ID_HIGH (0x2)
+#define MII_REG_ID_LOW (0x3)
+
+/* MII register flags */
+#define MII_REG_CR_RESET (1 << 15)
+#define MII_REG_CR_POWERDOWN (1 << 11)
+#define MII_REG_CR_10Mbit (0)
+#define MII_REG_CR_100Mbit (1 << 13)
+#define MII_REG_CR_1000Mbit (1 << 6)
+#define MII_REG_CR_AUTO_NEG (1 << 12)
+#define MII_REG_CR_AUTO_NEG_RESTART (1 << 9)
+#define MII_REG_CR_FULLDUPLEX (1 << 8)
+
+#define MII_REG_ST_100BASE_T4 (1 << 15)
+#define MII_REG_ST_100BASE_X_FD (1 << 14)
+#define MII_REG_ST_100BASE_X_HD (1 << 13)
+#define MII_REG_ST_10_FD (1 << 12)
+#define MII_REG_ST_10_HD (1 << 11)
+#define MII_REG_ST_100BASE_T2_FD (1 << 10)
+#define MII_REG_ST_100BASE_T2_HD (1 << 9)
+#define MII_REG_ST_AUTONEG_COMPLETE (1 << 5)
+#define MII_REG_ST_AUTONEG_AVAIL (1 << 3)
+#define MII_REG_ST_LINK_UP (1 << 2)
+
+/* MII constants */
+#define MII_PHY_ID_HIGH (0x0044)
+#define MII_PHY_ID_LOW (0x1400)