1*29d08975SNiek Linnenbank /* 2*29d08975SNiek Linnenbank * Allwinner Sun8i Ethernet MAC emulation 3*29d08975SNiek Linnenbank * 4*29d08975SNiek Linnenbank * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> 5*29d08975SNiek Linnenbank * 6*29d08975SNiek Linnenbank * This program is free software: you can redistribute it and/or modify 7*29d08975SNiek Linnenbank * it under the terms of the GNU General Public License as published by 8*29d08975SNiek Linnenbank * the Free Software Foundation, either version 2 of the License, or 9*29d08975SNiek Linnenbank * (at your option) any later version. 10*29d08975SNiek Linnenbank * 11*29d08975SNiek Linnenbank * This program is distributed in the hope that it will be useful, 12*29d08975SNiek Linnenbank * but WITHOUT ANY WARRANTY; without even the implied warranty of 13*29d08975SNiek Linnenbank * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14*29d08975SNiek Linnenbank * GNU General Public License for more details. 15*29d08975SNiek Linnenbank * 16*29d08975SNiek Linnenbank * You should have received a copy of the GNU General Public License 17*29d08975SNiek Linnenbank * along with this program. If not, see <http://www.gnu.org/licenses/>. 18*29d08975SNiek Linnenbank */ 19*29d08975SNiek Linnenbank 20*29d08975SNiek Linnenbank #include "qemu/osdep.h" 21*29d08975SNiek Linnenbank #include "qemu/units.h" 22*29d08975SNiek Linnenbank #include "hw/sysbus.h" 23*29d08975SNiek Linnenbank #include "migration/vmstate.h" 24*29d08975SNiek Linnenbank #include "net/net.h" 25*29d08975SNiek Linnenbank #include "hw/irq.h" 26*29d08975SNiek Linnenbank #include "hw/qdev-properties.h" 27*29d08975SNiek Linnenbank #include "qemu/log.h" 28*29d08975SNiek Linnenbank #include "trace.h" 29*29d08975SNiek Linnenbank #include "net/checksum.h" 30*29d08975SNiek Linnenbank #include "qemu/module.h" 31*29d08975SNiek Linnenbank #include "exec/cpu-common.h" 32*29d08975SNiek Linnenbank #include "hw/net/allwinner-sun8i-emac.h" 33*29d08975SNiek Linnenbank 34*29d08975SNiek Linnenbank /* EMAC register offsets */ 35*29d08975SNiek Linnenbank enum { 36*29d08975SNiek Linnenbank REG_BASIC_CTL_0 = 0x0000, /* Basic Control 0 */ 37*29d08975SNiek Linnenbank REG_BASIC_CTL_1 = 0x0004, /* Basic Control 1 */ 38*29d08975SNiek Linnenbank REG_INT_STA = 0x0008, /* Interrupt Status */ 39*29d08975SNiek Linnenbank REG_INT_EN = 0x000C, /* Interrupt Enable */ 40*29d08975SNiek Linnenbank REG_TX_CTL_0 = 0x0010, /* Transmit Control 0 */ 41*29d08975SNiek Linnenbank REG_TX_CTL_1 = 0x0014, /* Transmit Control 1 */ 42*29d08975SNiek Linnenbank REG_TX_FLOW_CTL = 0x001C, /* Transmit Flow Control */ 43*29d08975SNiek Linnenbank REG_TX_DMA_DESC_LIST = 0x0020, /* Transmit Descriptor List Address */ 44*29d08975SNiek Linnenbank REG_RX_CTL_0 = 0x0024, /* Receive Control 0 */ 45*29d08975SNiek Linnenbank REG_RX_CTL_1 = 0x0028, /* Receive Control 1 */ 46*29d08975SNiek Linnenbank REG_RX_DMA_DESC_LIST = 0x0034, /* Receive Descriptor List Address */ 47*29d08975SNiek Linnenbank REG_FRM_FLT = 0x0038, /* Receive Frame Filter */ 48*29d08975SNiek Linnenbank REG_RX_HASH_0 = 0x0040, /* Receive Hash Table 0 */ 49*29d08975SNiek Linnenbank REG_RX_HASH_1 = 0x0044, /* Receive Hash Table 1 */ 50*29d08975SNiek Linnenbank REG_MII_CMD = 0x0048, /* Management Interface Command */ 51*29d08975SNiek Linnenbank REG_MII_DATA = 0x004C, /* Management Interface Data */ 52*29d08975SNiek Linnenbank REG_ADDR_HIGH = 0x0050, /* MAC Address High */ 53*29d08975SNiek Linnenbank REG_ADDR_LOW = 0x0054, /* MAC Address Low */ 54*29d08975SNiek Linnenbank REG_TX_DMA_STA = 0x00B0, /* Transmit DMA Status */ 55*29d08975SNiek Linnenbank REG_TX_CUR_DESC = 0x00B4, /* Transmit Current Descriptor */ 56*29d08975SNiek Linnenbank REG_TX_CUR_BUF = 0x00B8, /* Transmit Current Buffer */ 57*29d08975SNiek Linnenbank REG_RX_DMA_STA = 0x00C0, /* Receive DMA Status */ 58*29d08975SNiek Linnenbank REG_RX_CUR_DESC = 0x00C4, /* Receive Current Descriptor */ 59*29d08975SNiek Linnenbank REG_RX_CUR_BUF = 0x00C8, /* Receive Current Buffer */ 60*29d08975SNiek Linnenbank REG_RGMII_STA = 0x00D0, /* RGMII Status */ 61*29d08975SNiek Linnenbank }; 62*29d08975SNiek Linnenbank 63*29d08975SNiek Linnenbank /* EMAC register flags */ 64*29d08975SNiek Linnenbank enum { 65*29d08975SNiek Linnenbank BASIC_CTL0_100Mbps = (0b11 << 2), 66*29d08975SNiek Linnenbank BASIC_CTL0_FD = (1 << 0), 67*29d08975SNiek Linnenbank BASIC_CTL1_SOFTRST = (1 << 0), 68*29d08975SNiek Linnenbank }; 69*29d08975SNiek Linnenbank 70*29d08975SNiek Linnenbank enum { 71*29d08975SNiek Linnenbank INT_STA_RGMII_LINK = (1 << 16), 72*29d08975SNiek Linnenbank INT_STA_RX_EARLY = (1 << 13), 73*29d08975SNiek Linnenbank INT_STA_RX_OVERFLOW = (1 << 12), 74*29d08975SNiek Linnenbank INT_STA_RX_TIMEOUT = (1 << 11), 75*29d08975SNiek Linnenbank INT_STA_RX_DMA_STOP = (1 << 10), 76*29d08975SNiek Linnenbank INT_STA_RX_BUF_UA = (1 << 9), 77*29d08975SNiek Linnenbank INT_STA_RX = (1 << 8), 78*29d08975SNiek Linnenbank INT_STA_TX_EARLY = (1 << 5), 79*29d08975SNiek Linnenbank INT_STA_TX_UNDERFLOW = (1 << 4), 80*29d08975SNiek Linnenbank INT_STA_TX_TIMEOUT = (1 << 3), 81*29d08975SNiek Linnenbank INT_STA_TX_BUF_UA = (1 << 2), 82*29d08975SNiek Linnenbank INT_STA_TX_DMA_STOP = (1 << 1), 83*29d08975SNiek Linnenbank INT_STA_TX = (1 << 0), 84*29d08975SNiek Linnenbank }; 85*29d08975SNiek Linnenbank 86*29d08975SNiek Linnenbank enum { 87*29d08975SNiek Linnenbank INT_EN_RX_EARLY = (1 << 13), 88*29d08975SNiek Linnenbank INT_EN_RX_OVERFLOW = (1 << 12), 89*29d08975SNiek Linnenbank INT_EN_RX_TIMEOUT = (1 << 11), 90*29d08975SNiek Linnenbank INT_EN_RX_DMA_STOP = (1 << 10), 91*29d08975SNiek Linnenbank INT_EN_RX_BUF_UA = (1 << 9), 92*29d08975SNiek Linnenbank INT_EN_RX = (1 << 8), 93*29d08975SNiek Linnenbank INT_EN_TX_EARLY = (1 << 5), 94*29d08975SNiek Linnenbank INT_EN_TX_UNDERFLOW = (1 << 4), 95*29d08975SNiek Linnenbank INT_EN_TX_TIMEOUT = (1 << 3), 96*29d08975SNiek Linnenbank INT_EN_TX_BUF_UA = (1 << 2), 97*29d08975SNiek Linnenbank INT_EN_TX_DMA_STOP = (1 << 1), 98*29d08975SNiek Linnenbank INT_EN_TX = (1 << 0), 99*29d08975SNiek Linnenbank }; 100*29d08975SNiek Linnenbank 101*29d08975SNiek Linnenbank enum { 102*29d08975SNiek Linnenbank TX_CTL0_TX_EN = (1 << 31), 103*29d08975SNiek Linnenbank TX_CTL1_TX_DMA_START = (1 << 31), 104*29d08975SNiek Linnenbank TX_CTL1_TX_DMA_EN = (1 << 30), 105*29d08975SNiek Linnenbank TX_CTL1_TX_FLUSH = (1 << 0), 106*29d08975SNiek Linnenbank }; 107*29d08975SNiek Linnenbank 108*29d08975SNiek Linnenbank enum { 109*29d08975SNiek Linnenbank RX_CTL0_RX_EN = (1 << 31), 110*29d08975SNiek Linnenbank RX_CTL0_STRIP_FCS = (1 << 28), 111*29d08975SNiek Linnenbank RX_CTL0_CRC_IPV4 = (1 << 27), 112*29d08975SNiek Linnenbank }; 113*29d08975SNiek Linnenbank 114*29d08975SNiek Linnenbank enum { 115*29d08975SNiek Linnenbank RX_CTL1_RX_DMA_START = (1 << 31), 116*29d08975SNiek Linnenbank RX_CTL1_RX_DMA_EN = (1 << 30), 117*29d08975SNiek Linnenbank RX_CTL1_RX_MD = (1 << 1), 118*29d08975SNiek Linnenbank }; 119*29d08975SNiek Linnenbank 120*29d08975SNiek Linnenbank enum { 121*29d08975SNiek Linnenbank RX_FRM_FLT_DIS_ADDR = (1 << 31), 122*29d08975SNiek Linnenbank }; 123*29d08975SNiek Linnenbank 124*29d08975SNiek Linnenbank enum { 125*29d08975SNiek Linnenbank MII_CMD_PHY_ADDR_SHIFT = (12), 126*29d08975SNiek Linnenbank MII_CMD_PHY_ADDR_MASK = (0xf000), 127*29d08975SNiek Linnenbank MII_CMD_PHY_REG_SHIFT = (4), 128*29d08975SNiek Linnenbank MII_CMD_PHY_REG_MASK = (0xf0), 129*29d08975SNiek Linnenbank MII_CMD_PHY_RW = (1 << 1), 130*29d08975SNiek Linnenbank MII_CMD_PHY_BUSY = (1 << 0), 131*29d08975SNiek Linnenbank }; 132*29d08975SNiek Linnenbank 133*29d08975SNiek Linnenbank enum { 134*29d08975SNiek Linnenbank TX_DMA_STA_STOP = (0b000), 135*29d08975SNiek Linnenbank TX_DMA_STA_RUN_FETCH = (0b001), 136*29d08975SNiek Linnenbank TX_DMA_STA_WAIT_STA = (0b010), 137*29d08975SNiek Linnenbank }; 138*29d08975SNiek Linnenbank 139*29d08975SNiek Linnenbank enum { 140*29d08975SNiek Linnenbank RX_DMA_STA_STOP = (0b000), 141*29d08975SNiek Linnenbank RX_DMA_STA_RUN_FETCH = (0b001), 142*29d08975SNiek Linnenbank RX_DMA_STA_WAIT_FRM = (0b011), 143*29d08975SNiek Linnenbank }; 144*29d08975SNiek Linnenbank 145*29d08975SNiek Linnenbank /* EMAC register reset values */ 146*29d08975SNiek Linnenbank enum { 147*29d08975SNiek Linnenbank REG_BASIC_CTL_1_RST = 0x08000000, 148*29d08975SNiek Linnenbank }; 149*29d08975SNiek Linnenbank 150*29d08975SNiek Linnenbank /* EMAC constants */ 151*29d08975SNiek Linnenbank enum { 152*29d08975SNiek Linnenbank AW_SUN8I_EMAC_MIN_PKT_SZ = 64 153*29d08975SNiek Linnenbank }; 154*29d08975SNiek Linnenbank 155*29d08975SNiek Linnenbank /* Transmit/receive frame descriptor */ 156*29d08975SNiek Linnenbank typedef struct FrameDescriptor { 157*29d08975SNiek Linnenbank uint32_t status; 158*29d08975SNiek Linnenbank uint32_t status2; 159*29d08975SNiek Linnenbank uint32_t addr; 160*29d08975SNiek Linnenbank uint32_t next; 161*29d08975SNiek Linnenbank } FrameDescriptor; 162*29d08975SNiek Linnenbank 163*29d08975SNiek Linnenbank /* Frame descriptor flags */ 164*29d08975SNiek Linnenbank enum { 165*29d08975SNiek Linnenbank DESC_STATUS_CTL = (1 << 31), 166*29d08975SNiek Linnenbank DESC_STATUS2_BUF_SIZE_MASK = (0x7ff), 167*29d08975SNiek Linnenbank }; 168*29d08975SNiek Linnenbank 169*29d08975SNiek Linnenbank /* Transmit frame descriptor flags */ 170*29d08975SNiek Linnenbank enum { 171*29d08975SNiek Linnenbank TX_DESC_STATUS_LENGTH_ERR = (1 << 14), 172*29d08975SNiek Linnenbank TX_DESC_STATUS2_FIRST_DESC = (1 << 29), 173*29d08975SNiek Linnenbank TX_DESC_STATUS2_LAST_DESC = (1 << 30), 174*29d08975SNiek Linnenbank TX_DESC_STATUS2_CHECKSUM_MASK = (0x3 << 27), 175*29d08975SNiek Linnenbank }; 176*29d08975SNiek Linnenbank 177*29d08975SNiek Linnenbank /* Receive frame descriptor flags */ 178*29d08975SNiek Linnenbank enum { 179*29d08975SNiek Linnenbank RX_DESC_STATUS_FIRST_DESC = (1 << 9), 180*29d08975SNiek Linnenbank RX_DESC_STATUS_LAST_DESC = (1 << 8), 181*29d08975SNiek Linnenbank RX_DESC_STATUS_FRM_LEN_MASK = (0x3fff0000), 182*29d08975SNiek Linnenbank RX_DESC_STATUS_FRM_LEN_SHIFT = (16), 183*29d08975SNiek Linnenbank RX_DESC_STATUS_NO_BUF = (1 << 14), 184*29d08975SNiek Linnenbank RX_DESC_STATUS_HEADER_ERR = (1 << 7), 185*29d08975SNiek Linnenbank RX_DESC_STATUS_LENGTH_ERR = (1 << 4), 186*29d08975SNiek Linnenbank RX_DESC_STATUS_CRC_ERR = (1 << 1), 187*29d08975SNiek Linnenbank RX_DESC_STATUS_PAYLOAD_ERR = (1 << 0), 188*29d08975SNiek Linnenbank RX_DESC_STATUS2_RX_INT_CTL = (1 << 31), 189*29d08975SNiek Linnenbank }; 190*29d08975SNiek Linnenbank 191*29d08975SNiek Linnenbank /* MII register offsets */ 192*29d08975SNiek Linnenbank enum { 193*29d08975SNiek Linnenbank MII_REG_CR = (0x0), /* Control */ 194*29d08975SNiek Linnenbank MII_REG_ST = (0x1), /* Status */ 195*29d08975SNiek Linnenbank MII_REG_ID_HIGH = (0x2), /* Identifier High */ 196*29d08975SNiek Linnenbank MII_REG_ID_LOW = (0x3), /* Identifier Low */ 197*29d08975SNiek Linnenbank MII_REG_ADV = (0x4), /* Advertised abilities */ 198*29d08975SNiek Linnenbank MII_REG_LPA = (0x5), /* Link partner abilities */ 199*29d08975SNiek Linnenbank }; 200*29d08975SNiek Linnenbank 201*29d08975SNiek Linnenbank /* MII register flags */ 202*29d08975SNiek Linnenbank enum { 203*29d08975SNiek Linnenbank MII_REG_CR_RESET = (1 << 15), 204*29d08975SNiek Linnenbank MII_REG_CR_POWERDOWN = (1 << 11), 205*29d08975SNiek Linnenbank MII_REG_CR_10Mbit = (0), 206*29d08975SNiek Linnenbank MII_REG_CR_100Mbit = (1 << 13), 207*29d08975SNiek Linnenbank MII_REG_CR_1000Mbit = (1 << 6), 208*29d08975SNiek Linnenbank MII_REG_CR_AUTO_NEG = (1 << 12), 209*29d08975SNiek Linnenbank MII_REG_CR_AUTO_NEG_RESTART = (1 << 9), 210*29d08975SNiek Linnenbank MII_REG_CR_FULLDUPLEX = (1 << 8), 211*29d08975SNiek Linnenbank }; 212*29d08975SNiek Linnenbank 213*29d08975SNiek Linnenbank enum { 214*29d08975SNiek Linnenbank MII_REG_ST_100BASE_T4 = (1 << 15), 215*29d08975SNiek Linnenbank MII_REG_ST_100BASE_X_FD = (1 << 14), 216*29d08975SNiek Linnenbank MII_REG_ST_100BASE_X_HD = (1 << 13), 217*29d08975SNiek Linnenbank MII_REG_ST_10_FD = (1 << 12), 218*29d08975SNiek Linnenbank MII_REG_ST_10_HD = (1 << 11), 219*29d08975SNiek Linnenbank MII_REG_ST_100BASE_T2_FD = (1 << 10), 220*29d08975SNiek Linnenbank MII_REG_ST_100BASE_T2_HD = (1 << 9), 221*29d08975SNiek Linnenbank MII_REG_ST_AUTONEG_COMPLETE = (1 << 5), 222*29d08975SNiek Linnenbank MII_REG_ST_AUTONEG_AVAIL = (1 << 3), 223*29d08975SNiek Linnenbank MII_REG_ST_LINK_UP = (1 << 2), 224*29d08975SNiek Linnenbank }; 225*29d08975SNiek Linnenbank 226*29d08975SNiek Linnenbank enum { 227*29d08975SNiek Linnenbank MII_REG_LPA_10_HD = (1 << 5), 228*29d08975SNiek Linnenbank MII_REG_LPA_10_FD = (1 << 6), 229*29d08975SNiek Linnenbank MII_REG_LPA_100_HD = (1 << 7), 230*29d08975SNiek Linnenbank MII_REG_LPA_100_FD = (1 << 8), 231*29d08975SNiek Linnenbank MII_REG_LPA_PAUSE = (1 << 10), 232*29d08975SNiek Linnenbank MII_REG_LPA_ASYMPAUSE = (1 << 11), 233*29d08975SNiek Linnenbank }; 234*29d08975SNiek Linnenbank 235*29d08975SNiek Linnenbank /* MII constants */ 236*29d08975SNiek Linnenbank enum { 237*29d08975SNiek Linnenbank MII_PHY_ID_HIGH = 0x0044, 238*29d08975SNiek Linnenbank MII_PHY_ID_LOW = 0x1400, 239*29d08975SNiek Linnenbank }; 240*29d08975SNiek Linnenbank 241*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_mii_set_link(AwSun8iEmacState *s, 242*29d08975SNiek Linnenbank bool link_active) 243*29d08975SNiek Linnenbank { 244*29d08975SNiek Linnenbank if (link_active) { 245*29d08975SNiek Linnenbank s->mii_st |= MII_REG_ST_LINK_UP; 246*29d08975SNiek Linnenbank } else { 247*29d08975SNiek Linnenbank s->mii_st &= ~MII_REG_ST_LINK_UP; 248*29d08975SNiek Linnenbank } 249*29d08975SNiek Linnenbank } 250*29d08975SNiek Linnenbank 251*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_mii_reset(AwSun8iEmacState *s, 252*29d08975SNiek Linnenbank bool link_active) 253*29d08975SNiek Linnenbank { 254*29d08975SNiek Linnenbank s->mii_cr = MII_REG_CR_100Mbit | MII_REG_CR_AUTO_NEG | 255*29d08975SNiek Linnenbank MII_REG_CR_FULLDUPLEX; 256*29d08975SNiek Linnenbank s->mii_st = MII_REG_ST_100BASE_T4 | MII_REG_ST_100BASE_X_FD | 257*29d08975SNiek Linnenbank MII_REG_ST_100BASE_X_HD | MII_REG_ST_10_FD | MII_REG_ST_10_HD | 258*29d08975SNiek Linnenbank MII_REG_ST_100BASE_T2_FD | MII_REG_ST_100BASE_T2_HD | 259*29d08975SNiek Linnenbank MII_REG_ST_AUTONEG_COMPLETE | MII_REG_ST_AUTONEG_AVAIL; 260*29d08975SNiek Linnenbank s->mii_adv = 0; 261*29d08975SNiek Linnenbank 262*29d08975SNiek Linnenbank allwinner_sun8i_emac_mii_set_link(s, link_active); 263*29d08975SNiek Linnenbank } 264*29d08975SNiek Linnenbank 265*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_mii_cmd(AwSun8iEmacState *s) 266*29d08975SNiek Linnenbank { 267*29d08975SNiek Linnenbank uint8_t addr, reg; 268*29d08975SNiek Linnenbank 269*29d08975SNiek Linnenbank addr = (s->mii_cmd & MII_CMD_PHY_ADDR_MASK) >> MII_CMD_PHY_ADDR_SHIFT; 270*29d08975SNiek Linnenbank reg = (s->mii_cmd & MII_CMD_PHY_REG_MASK) >> MII_CMD_PHY_REG_SHIFT; 271*29d08975SNiek Linnenbank 272*29d08975SNiek Linnenbank if (addr != s->mii_phy_addr) { 273*29d08975SNiek Linnenbank return; 274*29d08975SNiek Linnenbank } 275*29d08975SNiek Linnenbank 276*29d08975SNiek Linnenbank /* Read or write a PHY register? */ 277*29d08975SNiek Linnenbank if (s->mii_cmd & MII_CMD_PHY_RW) { 278*29d08975SNiek Linnenbank trace_allwinner_sun8i_emac_mii_write_reg(reg, s->mii_data); 279*29d08975SNiek Linnenbank 280*29d08975SNiek Linnenbank switch (reg) { 281*29d08975SNiek Linnenbank case MII_REG_CR: 282*29d08975SNiek Linnenbank if (s->mii_data & MII_REG_CR_RESET) { 283*29d08975SNiek Linnenbank allwinner_sun8i_emac_mii_reset(s, s->mii_st & 284*29d08975SNiek Linnenbank MII_REG_ST_LINK_UP); 285*29d08975SNiek Linnenbank } else { 286*29d08975SNiek Linnenbank s->mii_cr = s->mii_data & ~(MII_REG_CR_RESET | 287*29d08975SNiek Linnenbank MII_REG_CR_AUTO_NEG_RESTART); 288*29d08975SNiek Linnenbank } 289*29d08975SNiek Linnenbank break; 290*29d08975SNiek Linnenbank case MII_REG_ADV: 291*29d08975SNiek Linnenbank s->mii_adv = s->mii_data; 292*29d08975SNiek Linnenbank break; 293*29d08975SNiek Linnenbank case MII_REG_ID_HIGH: 294*29d08975SNiek Linnenbank case MII_REG_ID_LOW: 295*29d08975SNiek Linnenbank case MII_REG_LPA: 296*29d08975SNiek Linnenbank break; 297*29d08975SNiek Linnenbank default: 298*29d08975SNiek Linnenbank qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to " 299*29d08975SNiek Linnenbank "unknown MII register 0x%x\n", reg); 300*29d08975SNiek Linnenbank break; 301*29d08975SNiek Linnenbank } 302*29d08975SNiek Linnenbank } else { 303*29d08975SNiek Linnenbank switch (reg) { 304*29d08975SNiek Linnenbank case MII_REG_CR: 305*29d08975SNiek Linnenbank s->mii_data = s->mii_cr; 306*29d08975SNiek Linnenbank break; 307*29d08975SNiek Linnenbank case MII_REG_ST: 308*29d08975SNiek Linnenbank s->mii_data = s->mii_st; 309*29d08975SNiek Linnenbank break; 310*29d08975SNiek Linnenbank case MII_REG_ID_HIGH: 311*29d08975SNiek Linnenbank s->mii_data = MII_PHY_ID_HIGH; 312*29d08975SNiek Linnenbank break; 313*29d08975SNiek Linnenbank case MII_REG_ID_LOW: 314*29d08975SNiek Linnenbank s->mii_data = MII_PHY_ID_LOW; 315*29d08975SNiek Linnenbank break; 316*29d08975SNiek Linnenbank case MII_REG_ADV: 317*29d08975SNiek Linnenbank s->mii_data = s->mii_adv; 318*29d08975SNiek Linnenbank break; 319*29d08975SNiek Linnenbank case MII_REG_LPA: 320*29d08975SNiek Linnenbank s->mii_data = MII_REG_LPA_10_HD | MII_REG_LPA_10_FD | 321*29d08975SNiek Linnenbank MII_REG_LPA_100_HD | MII_REG_LPA_100_FD | 322*29d08975SNiek Linnenbank MII_REG_LPA_PAUSE | MII_REG_LPA_ASYMPAUSE; 323*29d08975SNiek Linnenbank break; 324*29d08975SNiek Linnenbank default: 325*29d08975SNiek Linnenbank qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to " 326*29d08975SNiek Linnenbank "unknown MII register 0x%x\n", reg); 327*29d08975SNiek Linnenbank s->mii_data = 0; 328*29d08975SNiek Linnenbank break; 329*29d08975SNiek Linnenbank } 330*29d08975SNiek Linnenbank 331*29d08975SNiek Linnenbank trace_allwinner_sun8i_emac_mii_read_reg(reg, s->mii_data); 332*29d08975SNiek Linnenbank } 333*29d08975SNiek Linnenbank } 334*29d08975SNiek Linnenbank 335*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_update_irq(AwSun8iEmacState *s) 336*29d08975SNiek Linnenbank { 337*29d08975SNiek Linnenbank qemu_set_irq(s->irq, (s->int_sta & s->int_en) != 0); 338*29d08975SNiek Linnenbank } 339*29d08975SNiek Linnenbank 340*29d08975SNiek Linnenbank static uint32_t allwinner_sun8i_emac_next_desc(FrameDescriptor *desc, 341*29d08975SNiek Linnenbank size_t min_size) 342*29d08975SNiek Linnenbank { 343*29d08975SNiek Linnenbank uint32_t paddr = desc->next; 344*29d08975SNiek Linnenbank 345*29d08975SNiek Linnenbank cpu_physical_memory_read(paddr, desc, sizeof(*desc)); 346*29d08975SNiek Linnenbank 347*29d08975SNiek Linnenbank if ((desc->status & DESC_STATUS_CTL) && 348*29d08975SNiek Linnenbank (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) { 349*29d08975SNiek Linnenbank return paddr; 350*29d08975SNiek Linnenbank } else { 351*29d08975SNiek Linnenbank return 0; 352*29d08975SNiek Linnenbank } 353*29d08975SNiek Linnenbank } 354*29d08975SNiek Linnenbank 355*29d08975SNiek Linnenbank static uint32_t allwinner_sun8i_emac_get_desc(FrameDescriptor *desc, 356*29d08975SNiek Linnenbank uint32_t start_addr, 357*29d08975SNiek Linnenbank size_t min_size) 358*29d08975SNiek Linnenbank { 359*29d08975SNiek Linnenbank uint32_t desc_addr = start_addr; 360*29d08975SNiek Linnenbank 361*29d08975SNiek Linnenbank /* Note that the list is a cycle. Last entry points back to the head. */ 362*29d08975SNiek Linnenbank while (desc_addr != 0) { 363*29d08975SNiek Linnenbank cpu_physical_memory_read(desc_addr, desc, sizeof(*desc)); 364*29d08975SNiek Linnenbank 365*29d08975SNiek Linnenbank if ((desc->status & DESC_STATUS_CTL) && 366*29d08975SNiek Linnenbank (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) { 367*29d08975SNiek Linnenbank return desc_addr; 368*29d08975SNiek Linnenbank } else if (desc->next == start_addr) { 369*29d08975SNiek Linnenbank break; 370*29d08975SNiek Linnenbank } else { 371*29d08975SNiek Linnenbank desc_addr = desc->next; 372*29d08975SNiek Linnenbank } 373*29d08975SNiek Linnenbank } 374*29d08975SNiek Linnenbank 375*29d08975SNiek Linnenbank return 0; 376*29d08975SNiek Linnenbank } 377*29d08975SNiek Linnenbank 378*29d08975SNiek Linnenbank static uint32_t allwinner_sun8i_emac_rx_desc(AwSun8iEmacState *s, 379*29d08975SNiek Linnenbank FrameDescriptor *desc, 380*29d08975SNiek Linnenbank size_t min_size) 381*29d08975SNiek Linnenbank { 382*29d08975SNiek Linnenbank return allwinner_sun8i_emac_get_desc(desc, s->rx_desc_curr, min_size); 383*29d08975SNiek Linnenbank } 384*29d08975SNiek Linnenbank 385*29d08975SNiek Linnenbank static uint32_t allwinner_sun8i_emac_tx_desc(AwSun8iEmacState *s, 386*29d08975SNiek Linnenbank FrameDescriptor *desc, 387*29d08975SNiek Linnenbank size_t min_size) 388*29d08975SNiek Linnenbank { 389*29d08975SNiek Linnenbank return allwinner_sun8i_emac_get_desc(desc, s->tx_desc_head, min_size); 390*29d08975SNiek Linnenbank } 391*29d08975SNiek Linnenbank 392*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_flush_desc(FrameDescriptor *desc, 393*29d08975SNiek Linnenbank uint32_t phys_addr) 394*29d08975SNiek Linnenbank { 395*29d08975SNiek Linnenbank cpu_physical_memory_write(phys_addr, desc, sizeof(*desc)); 396*29d08975SNiek Linnenbank } 397*29d08975SNiek Linnenbank 398*29d08975SNiek Linnenbank static int allwinner_sun8i_emac_can_receive(NetClientState *nc) 399*29d08975SNiek Linnenbank { 400*29d08975SNiek Linnenbank AwSun8iEmacState *s = qemu_get_nic_opaque(nc); 401*29d08975SNiek Linnenbank FrameDescriptor desc; 402*29d08975SNiek Linnenbank 403*29d08975SNiek Linnenbank return (s->rx_ctl0 & RX_CTL0_RX_EN) && 404*29d08975SNiek Linnenbank (allwinner_sun8i_emac_rx_desc(s, &desc, 0) != 0); 405*29d08975SNiek Linnenbank } 406*29d08975SNiek Linnenbank 407*29d08975SNiek Linnenbank static ssize_t allwinner_sun8i_emac_receive(NetClientState *nc, 408*29d08975SNiek Linnenbank const uint8_t *buf, 409*29d08975SNiek Linnenbank size_t size) 410*29d08975SNiek Linnenbank { 411*29d08975SNiek Linnenbank AwSun8iEmacState *s = qemu_get_nic_opaque(nc); 412*29d08975SNiek Linnenbank FrameDescriptor desc; 413*29d08975SNiek Linnenbank size_t bytes_left = size; 414*29d08975SNiek Linnenbank size_t desc_bytes = 0; 415*29d08975SNiek Linnenbank size_t pad_fcs_size = 4; 416*29d08975SNiek Linnenbank size_t padding = 0; 417*29d08975SNiek Linnenbank 418*29d08975SNiek Linnenbank if (!(s->rx_ctl0 & RX_CTL0_RX_EN)) { 419*29d08975SNiek Linnenbank return -1; 420*29d08975SNiek Linnenbank } 421*29d08975SNiek Linnenbank 422*29d08975SNiek Linnenbank s->rx_desc_curr = allwinner_sun8i_emac_rx_desc(s, &desc, 423*29d08975SNiek Linnenbank AW_SUN8I_EMAC_MIN_PKT_SZ); 424*29d08975SNiek Linnenbank if (!s->rx_desc_curr) { 425*29d08975SNiek Linnenbank s->int_sta |= INT_STA_RX_BUF_UA; 426*29d08975SNiek Linnenbank } 427*29d08975SNiek Linnenbank 428*29d08975SNiek Linnenbank /* Keep filling RX descriptors until the whole frame is written */ 429*29d08975SNiek Linnenbank while (s->rx_desc_curr && bytes_left > 0) { 430*29d08975SNiek Linnenbank desc.status &= ~DESC_STATUS_CTL; 431*29d08975SNiek Linnenbank desc.status &= ~RX_DESC_STATUS_FRM_LEN_MASK; 432*29d08975SNiek Linnenbank 433*29d08975SNiek Linnenbank if (bytes_left == size) { 434*29d08975SNiek Linnenbank desc.status |= RX_DESC_STATUS_FIRST_DESC; 435*29d08975SNiek Linnenbank } 436*29d08975SNiek Linnenbank 437*29d08975SNiek Linnenbank if ((desc.status2 & DESC_STATUS2_BUF_SIZE_MASK) < 438*29d08975SNiek Linnenbank (bytes_left + pad_fcs_size)) { 439*29d08975SNiek Linnenbank desc_bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK; 440*29d08975SNiek Linnenbank desc.status |= desc_bytes << RX_DESC_STATUS_FRM_LEN_SHIFT; 441*29d08975SNiek Linnenbank } else { 442*29d08975SNiek Linnenbank padding = pad_fcs_size; 443*29d08975SNiek Linnenbank if (bytes_left < AW_SUN8I_EMAC_MIN_PKT_SZ) { 444*29d08975SNiek Linnenbank padding += (AW_SUN8I_EMAC_MIN_PKT_SZ - bytes_left); 445*29d08975SNiek Linnenbank } 446*29d08975SNiek Linnenbank 447*29d08975SNiek Linnenbank desc_bytes = (bytes_left); 448*29d08975SNiek Linnenbank desc.status |= RX_DESC_STATUS_LAST_DESC; 449*29d08975SNiek Linnenbank desc.status |= (bytes_left + padding) 450*29d08975SNiek Linnenbank << RX_DESC_STATUS_FRM_LEN_SHIFT; 451*29d08975SNiek Linnenbank } 452*29d08975SNiek Linnenbank 453*29d08975SNiek Linnenbank cpu_physical_memory_write(desc.addr, buf, desc_bytes); 454*29d08975SNiek Linnenbank allwinner_sun8i_emac_flush_desc(&desc, s->rx_desc_curr); 455*29d08975SNiek Linnenbank trace_allwinner_sun8i_emac_receive(s->rx_desc_curr, desc.addr, 456*29d08975SNiek Linnenbank desc_bytes); 457*29d08975SNiek Linnenbank 458*29d08975SNiek Linnenbank /* Check if frame needs to raise the receive interrupt */ 459*29d08975SNiek Linnenbank if (!(desc.status2 & RX_DESC_STATUS2_RX_INT_CTL)) { 460*29d08975SNiek Linnenbank s->int_sta |= INT_STA_RX; 461*29d08975SNiek Linnenbank } 462*29d08975SNiek Linnenbank 463*29d08975SNiek Linnenbank /* Increment variables */ 464*29d08975SNiek Linnenbank buf += desc_bytes; 465*29d08975SNiek Linnenbank bytes_left -= desc_bytes; 466*29d08975SNiek Linnenbank 467*29d08975SNiek Linnenbank /* Move to the next descriptor */ 468*29d08975SNiek Linnenbank s->rx_desc_curr = allwinner_sun8i_emac_next_desc(&desc, 64); 469*29d08975SNiek Linnenbank if (!s->rx_desc_curr) { 470*29d08975SNiek Linnenbank /* Not enough buffer space available */ 471*29d08975SNiek Linnenbank s->int_sta |= INT_STA_RX_BUF_UA; 472*29d08975SNiek Linnenbank s->rx_desc_curr = s->rx_desc_head; 473*29d08975SNiek Linnenbank break; 474*29d08975SNiek Linnenbank } 475*29d08975SNiek Linnenbank } 476*29d08975SNiek Linnenbank 477*29d08975SNiek Linnenbank /* Report receive DMA is finished */ 478*29d08975SNiek Linnenbank s->rx_ctl1 &= ~RX_CTL1_RX_DMA_START; 479*29d08975SNiek Linnenbank allwinner_sun8i_emac_update_irq(s); 480*29d08975SNiek Linnenbank 481*29d08975SNiek Linnenbank return size; 482*29d08975SNiek Linnenbank } 483*29d08975SNiek Linnenbank 484*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_transmit(AwSun8iEmacState *s) 485*29d08975SNiek Linnenbank { 486*29d08975SNiek Linnenbank NetClientState *nc = qemu_get_queue(s->nic); 487*29d08975SNiek Linnenbank FrameDescriptor desc; 488*29d08975SNiek Linnenbank size_t bytes = 0; 489*29d08975SNiek Linnenbank size_t packet_bytes = 0; 490*29d08975SNiek Linnenbank size_t transmitted = 0; 491*29d08975SNiek Linnenbank static uint8_t packet_buf[2048]; 492*29d08975SNiek Linnenbank 493*29d08975SNiek Linnenbank s->tx_desc_curr = allwinner_sun8i_emac_tx_desc(s, &desc, 0); 494*29d08975SNiek Linnenbank 495*29d08975SNiek Linnenbank /* Read all transmit descriptors */ 496*29d08975SNiek Linnenbank while (s->tx_desc_curr != 0) { 497*29d08975SNiek Linnenbank 498*29d08975SNiek Linnenbank /* Read from physical memory into packet buffer */ 499*29d08975SNiek Linnenbank bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK; 500*29d08975SNiek Linnenbank if (bytes + packet_bytes > sizeof(packet_buf)) { 501*29d08975SNiek Linnenbank desc.status |= TX_DESC_STATUS_LENGTH_ERR; 502*29d08975SNiek Linnenbank break; 503*29d08975SNiek Linnenbank } 504*29d08975SNiek Linnenbank cpu_physical_memory_read(desc.addr, packet_buf + packet_bytes, bytes); 505*29d08975SNiek Linnenbank packet_bytes += bytes; 506*29d08975SNiek Linnenbank desc.status &= ~DESC_STATUS_CTL; 507*29d08975SNiek Linnenbank allwinner_sun8i_emac_flush_desc(&desc, s->tx_desc_curr); 508*29d08975SNiek Linnenbank 509*29d08975SNiek Linnenbank /* After the last descriptor, send the packet */ 510*29d08975SNiek Linnenbank if (desc.status2 & TX_DESC_STATUS2_LAST_DESC) { 511*29d08975SNiek Linnenbank if (desc.status2 & TX_DESC_STATUS2_CHECKSUM_MASK) { 512*29d08975SNiek Linnenbank net_checksum_calculate(packet_buf, packet_bytes); 513*29d08975SNiek Linnenbank } 514*29d08975SNiek Linnenbank 515*29d08975SNiek Linnenbank qemu_send_packet(nc, packet_buf, packet_bytes); 516*29d08975SNiek Linnenbank trace_allwinner_sun8i_emac_transmit(s->tx_desc_curr, desc.addr, 517*29d08975SNiek Linnenbank bytes); 518*29d08975SNiek Linnenbank 519*29d08975SNiek Linnenbank packet_bytes = 0; 520*29d08975SNiek Linnenbank transmitted++; 521*29d08975SNiek Linnenbank } 522*29d08975SNiek Linnenbank s->tx_desc_curr = allwinner_sun8i_emac_next_desc(&desc, 0); 523*29d08975SNiek Linnenbank } 524*29d08975SNiek Linnenbank 525*29d08975SNiek Linnenbank /* Raise transmit completed interrupt */ 526*29d08975SNiek Linnenbank if (transmitted > 0) { 527*29d08975SNiek Linnenbank s->int_sta |= INT_STA_TX; 528*29d08975SNiek Linnenbank s->tx_ctl1 &= ~TX_CTL1_TX_DMA_START; 529*29d08975SNiek Linnenbank allwinner_sun8i_emac_update_irq(s); 530*29d08975SNiek Linnenbank } 531*29d08975SNiek Linnenbank } 532*29d08975SNiek Linnenbank 533*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_reset(DeviceState *dev) 534*29d08975SNiek Linnenbank { 535*29d08975SNiek Linnenbank AwSun8iEmacState *s = AW_SUN8I_EMAC(dev); 536*29d08975SNiek Linnenbank NetClientState *nc = qemu_get_queue(s->nic); 537*29d08975SNiek Linnenbank 538*29d08975SNiek Linnenbank trace_allwinner_sun8i_emac_reset(); 539*29d08975SNiek Linnenbank 540*29d08975SNiek Linnenbank s->mii_cmd = 0; 541*29d08975SNiek Linnenbank s->mii_data = 0; 542*29d08975SNiek Linnenbank s->basic_ctl0 = 0; 543*29d08975SNiek Linnenbank s->basic_ctl1 = REG_BASIC_CTL_1_RST; 544*29d08975SNiek Linnenbank s->int_en = 0; 545*29d08975SNiek Linnenbank s->int_sta = 0; 546*29d08975SNiek Linnenbank s->frm_flt = 0; 547*29d08975SNiek Linnenbank s->rx_ctl0 = 0; 548*29d08975SNiek Linnenbank s->rx_ctl1 = RX_CTL1_RX_MD; 549*29d08975SNiek Linnenbank s->rx_desc_head = 0; 550*29d08975SNiek Linnenbank s->rx_desc_curr = 0; 551*29d08975SNiek Linnenbank s->tx_ctl0 = 0; 552*29d08975SNiek Linnenbank s->tx_ctl1 = 0; 553*29d08975SNiek Linnenbank s->tx_desc_head = 0; 554*29d08975SNiek Linnenbank s->tx_desc_curr = 0; 555*29d08975SNiek Linnenbank s->tx_flowctl = 0; 556*29d08975SNiek Linnenbank 557*29d08975SNiek Linnenbank allwinner_sun8i_emac_mii_reset(s, !nc->link_down); 558*29d08975SNiek Linnenbank } 559*29d08975SNiek Linnenbank 560*29d08975SNiek Linnenbank static uint64_t allwinner_sun8i_emac_read(void *opaque, hwaddr offset, 561*29d08975SNiek Linnenbank unsigned size) 562*29d08975SNiek Linnenbank { 563*29d08975SNiek Linnenbank AwSun8iEmacState *s = AW_SUN8I_EMAC(opaque); 564*29d08975SNiek Linnenbank uint64_t value = 0; 565*29d08975SNiek Linnenbank FrameDescriptor desc; 566*29d08975SNiek Linnenbank 567*29d08975SNiek Linnenbank switch (offset) { 568*29d08975SNiek Linnenbank case REG_BASIC_CTL_0: /* Basic Control 0 */ 569*29d08975SNiek Linnenbank value = s->basic_ctl0; 570*29d08975SNiek Linnenbank break; 571*29d08975SNiek Linnenbank case REG_BASIC_CTL_1: /* Basic Control 1 */ 572*29d08975SNiek Linnenbank value = s->basic_ctl1; 573*29d08975SNiek Linnenbank break; 574*29d08975SNiek Linnenbank case REG_INT_STA: /* Interrupt Status */ 575*29d08975SNiek Linnenbank value = s->int_sta; 576*29d08975SNiek Linnenbank break; 577*29d08975SNiek Linnenbank case REG_INT_EN: /* Interupt Enable */ 578*29d08975SNiek Linnenbank value = s->int_en; 579*29d08975SNiek Linnenbank break; 580*29d08975SNiek Linnenbank case REG_TX_CTL_0: /* Transmit Control 0 */ 581*29d08975SNiek Linnenbank value = s->tx_ctl0; 582*29d08975SNiek Linnenbank break; 583*29d08975SNiek Linnenbank case REG_TX_CTL_1: /* Transmit Control 1 */ 584*29d08975SNiek Linnenbank value = s->tx_ctl1; 585*29d08975SNiek Linnenbank break; 586*29d08975SNiek Linnenbank case REG_TX_FLOW_CTL: /* Transmit Flow Control */ 587*29d08975SNiek Linnenbank value = s->tx_flowctl; 588*29d08975SNiek Linnenbank break; 589*29d08975SNiek Linnenbank case REG_TX_DMA_DESC_LIST: /* Transmit Descriptor List Address */ 590*29d08975SNiek Linnenbank value = s->tx_desc_head; 591*29d08975SNiek Linnenbank break; 592*29d08975SNiek Linnenbank case REG_RX_CTL_0: /* Receive Control 0 */ 593*29d08975SNiek Linnenbank value = s->rx_ctl0; 594*29d08975SNiek Linnenbank break; 595*29d08975SNiek Linnenbank case REG_RX_CTL_1: /* Receive Control 1 */ 596*29d08975SNiek Linnenbank value = s->rx_ctl1; 597*29d08975SNiek Linnenbank break; 598*29d08975SNiek Linnenbank case REG_RX_DMA_DESC_LIST: /* Receive Descriptor List Address */ 599*29d08975SNiek Linnenbank value = s->rx_desc_head; 600*29d08975SNiek Linnenbank break; 601*29d08975SNiek Linnenbank case REG_FRM_FLT: /* Receive Frame Filter */ 602*29d08975SNiek Linnenbank value = s->frm_flt; 603*29d08975SNiek Linnenbank break; 604*29d08975SNiek Linnenbank case REG_RX_HASH_0: /* Receive Hash Table 0 */ 605*29d08975SNiek Linnenbank case REG_RX_HASH_1: /* Receive Hash Table 1 */ 606*29d08975SNiek Linnenbank break; 607*29d08975SNiek Linnenbank case REG_MII_CMD: /* Management Interface Command */ 608*29d08975SNiek Linnenbank value = s->mii_cmd; 609*29d08975SNiek Linnenbank break; 610*29d08975SNiek Linnenbank case REG_MII_DATA: /* Management Interface Data */ 611*29d08975SNiek Linnenbank value = s->mii_data; 612*29d08975SNiek Linnenbank break; 613*29d08975SNiek Linnenbank case REG_ADDR_HIGH: /* MAC Address High */ 614*29d08975SNiek Linnenbank value = *(((uint32_t *) (s->conf.macaddr.a)) + 1); 615*29d08975SNiek Linnenbank break; 616*29d08975SNiek Linnenbank case REG_ADDR_LOW: /* MAC Address Low */ 617*29d08975SNiek Linnenbank value = *(uint32_t *) (s->conf.macaddr.a); 618*29d08975SNiek Linnenbank break; 619*29d08975SNiek Linnenbank case REG_TX_DMA_STA: /* Transmit DMA Status */ 620*29d08975SNiek Linnenbank break; 621*29d08975SNiek Linnenbank case REG_TX_CUR_DESC: /* Transmit Current Descriptor */ 622*29d08975SNiek Linnenbank value = s->tx_desc_curr; 623*29d08975SNiek Linnenbank break; 624*29d08975SNiek Linnenbank case REG_TX_CUR_BUF: /* Transmit Current Buffer */ 625*29d08975SNiek Linnenbank if (s->tx_desc_curr != 0) { 626*29d08975SNiek Linnenbank cpu_physical_memory_read(s->tx_desc_curr, &desc, sizeof(desc)); 627*29d08975SNiek Linnenbank value = desc.addr; 628*29d08975SNiek Linnenbank } else { 629*29d08975SNiek Linnenbank value = 0; 630*29d08975SNiek Linnenbank } 631*29d08975SNiek Linnenbank break; 632*29d08975SNiek Linnenbank case REG_RX_DMA_STA: /* Receive DMA Status */ 633*29d08975SNiek Linnenbank break; 634*29d08975SNiek Linnenbank case REG_RX_CUR_DESC: /* Receive Current Descriptor */ 635*29d08975SNiek Linnenbank value = s->rx_desc_curr; 636*29d08975SNiek Linnenbank break; 637*29d08975SNiek Linnenbank case REG_RX_CUR_BUF: /* Receive Current Buffer */ 638*29d08975SNiek Linnenbank if (s->rx_desc_curr != 0) { 639*29d08975SNiek Linnenbank cpu_physical_memory_read(s->rx_desc_curr, &desc, sizeof(desc)); 640*29d08975SNiek Linnenbank value = desc.addr; 641*29d08975SNiek Linnenbank } else { 642*29d08975SNiek Linnenbank value = 0; 643*29d08975SNiek Linnenbank } 644*29d08975SNiek Linnenbank break; 645*29d08975SNiek Linnenbank case REG_RGMII_STA: /* RGMII Status */ 646*29d08975SNiek Linnenbank break; 647*29d08975SNiek Linnenbank default: 648*29d08975SNiek Linnenbank qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to unknown " 649*29d08975SNiek Linnenbank "EMAC register 0x" TARGET_FMT_plx "\n", 650*29d08975SNiek Linnenbank offset); 651*29d08975SNiek Linnenbank } 652*29d08975SNiek Linnenbank 653*29d08975SNiek Linnenbank trace_allwinner_sun8i_emac_read(offset, value); 654*29d08975SNiek Linnenbank return value; 655*29d08975SNiek Linnenbank } 656*29d08975SNiek Linnenbank 657*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_write(void *opaque, hwaddr offset, 658*29d08975SNiek Linnenbank uint64_t value, unsigned size) 659*29d08975SNiek Linnenbank { 660*29d08975SNiek Linnenbank AwSun8iEmacState *s = AW_SUN8I_EMAC(opaque); 661*29d08975SNiek Linnenbank NetClientState *nc = qemu_get_queue(s->nic); 662*29d08975SNiek Linnenbank 663*29d08975SNiek Linnenbank trace_allwinner_sun8i_emac_write(offset, value); 664*29d08975SNiek Linnenbank 665*29d08975SNiek Linnenbank switch (offset) { 666*29d08975SNiek Linnenbank case REG_BASIC_CTL_0: /* Basic Control 0 */ 667*29d08975SNiek Linnenbank s->basic_ctl0 = value; 668*29d08975SNiek Linnenbank break; 669*29d08975SNiek Linnenbank case REG_BASIC_CTL_1: /* Basic Control 1 */ 670*29d08975SNiek Linnenbank if (value & BASIC_CTL1_SOFTRST) { 671*29d08975SNiek Linnenbank allwinner_sun8i_emac_reset(DEVICE(s)); 672*29d08975SNiek Linnenbank value &= ~BASIC_CTL1_SOFTRST; 673*29d08975SNiek Linnenbank } 674*29d08975SNiek Linnenbank s->basic_ctl1 = value; 675*29d08975SNiek Linnenbank if (allwinner_sun8i_emac_can_receive(nc)) { 676*29d08975SNiek Linnenbank qemu_flush_queued_packets(nc); 677*29d08975SNiek Linnenbank } 678*29d08975SNiek Linnenbank break; 679*29d08975SNiek Linnenbank case REG_INT_STA: /* Interrupt Status */ 680*29d08975SNiek Linnenbank s->int_sta &= ~value; 681*29d08975SNiek Linnenbank allwinner_sun8i_emac_update_irq(s); 682*29d08975SNiek Linnenbank break; 683*29d08975SNiek Linnenbank case REG_INT_EN: /* Interrupt Enable */ 684*29d08975SNiek Linnenbank s->int_en = value; 685*29d08975SNiek Linnenbank allwinner_sun8i_emac_update_irq(s); 686*29d08975SNiek Linnenbank break; 687*29d08975SNiek Linnenbank case REG_TX_CTL_0: /* Transmit Control 0 */ 688*29d08975SNiek Linnenbank s->tx_ctl0 = value; 689*29d08975SNiek Linnenbank break; 690*29d08975SNiek Linnenbank case REG_TX_CTL_1: /* Transmit Control 1 */ 691*29d08975SNiek Linnenbank s->tx_ctl1 = value; 692*29d08975SNiek Linnenbank if (value & TX_CTL1_TX_DMA_EN) { 693*29d08975SNiek Linnenbank allwinner_sun8i_emac_transmit(s); 694*29d08975SNiek Linnenbank } 695*29d08975SNiek Linnenbank break; 696*29d08975SNiek Linnenbank case REG_TX_FLOW_CTL: /* Transmit Flow Control */ 697*29d08975SNiek Linnenbank s->tx_flowctl = value; 698*29d08975SNiek Linnenbank break; 699*29d08975SNiek Linnenbank case REG_TX_DMA_DESC_LIST: /* Transmit Descriptor List Address */ 700*29d08975SNiek Linnenbank s->tx_desc_head = value; 701*29d08975SNiek Linnenbank s->tx_desc_curr = value; 702*29d08975SNiek Linnenbank break; 703*29d08975SNiek Linnenbank case REG_RX_CTL_0: /* Receive Control 0 */ 704*29d08975SNiek Linnenbank s->rx_ctl0 = value; 705*29d08975SNiek Linnenbank break; 706*29d08975SNiek Linnenbank case REG_RX_CTL_1: /* Receive Control 1 */ 707*29d08975SNiek Linnenbank s->rx_ctl1 = value | RX_CTL1_RX_MD; 708*29d08975SNiek Linnenbank if ((value & RX_CTL1_RX_DMA_EN) && 709*29d08975SNiek Linnenbank allwinner_sun8i_emac_can_receive(nc)) { 710*29d08975SNiek Linnenbank qemu_flush_queued_packets(nc); 711*29d08975SNiek Linnenbank } 712*29d08975SNiek Linnenbank break; 713*29d08975SNiek Linnenbank case REG_RX_DMA_DESC_LIST: /* Receive Descriptor List Address */ 714*29d08975SNiek Linnenbank s->rx_desc_head = value; 715*29d08975SNiek Linnenbank s->rx_desc_curr = value; 716*29d08975SNiek Linnenbank break; 717*29d08975SNiek Linnenbank case REG_FRM_FLT: /* Receive Frame Filter */ 718*29d08975SNiek Linnenbank s->frm_flt = value; 719*29d08975SNiek Linnenbank break; 720*29d08975SNiek Linnenbank case REG_RX_HASH_0: /* Receive Hash Table 0 */ 721*29d08975SNiek Linnenbank case REG_RX_HASH_1: /* Receive Hash Table 1 */ 722*29d08975SNiek Linnenbank break; 723*29d08975SNiek Linnenbank case REG_MII_CMD: /* Management Interface Command */ 724*29d08975SNiek Linnenbank s->mii_cmd = value & ~MII_CMD_PHY_BUSY; 725*29d08975SNiek Linnenbank allwinner_sun8i_emac_mii_cmd(s); 726*29d08975SNiek Linnenbank break; 727*29d08975SNiek Linnenbank case REG_MII_DATA: /* Management Interface Data */ 728*29d08975SNiek Linnenbank s->mii_data = value; 729*29d08975SNiek Linnenbank break; 730*29d08975SNiek Linnenbank case REG_ADDR_HIGH: /* MAC Address High */ 731*29d08975SNiek Linnenbank s->conf.macaddr.a[4] = (value & 0xff); 732*29d08975SNiek Linnenbank s->conf.macaddr.a[5] = (value & 0xff00) >> 8; 733*29d08975SNiek Linnenbank break; 734*29d08975SNiek Linnenbank case REG_ADDR_LOW: /* MAC Address Low */ 735*29d08975SNiek Linnenbank s->conf.macaddr.a[0] = (value & 0xff); 736*29d08975SNiek Linnenbank s->conf.macaddr.a[1] = (value & 0xff00) >> 8; 737*29d08975SNiek Linnenbank s->conf.macaddr.a[2] = (value & 0xff0000) >> 16; 738*29d08975SNiek Linnenbank s->conf.macaddr.a[3] = (value & 0xff000000) >> 24; 739*29d08975SNiek Linnenbank break; 740*29d08975SNiek Linnenbank case REG_TX_DMA_STA: /* Transmit DMA Status */ 741*29d08975SNiek Linnenbank case REG_TX_CUR_DESC: /* Transmit Current Descriptor */ 742*29d08975SNiek Linnenbank case REG_TX_CUR_BUF: /* Transmit Current Buffer */ 743*29d08975SNiek Linnenbank case REG_RX_DMA_STA: /* Receive DMA Status */ 744*29d08975SNiek Linnenbank case REG_RX_CUR_DESC: /* Receive Current Descriptor */ 745*29d08975SNiek Linnenbank case REG_RX_CUR_BUF: /* Receive Current Buffer */ 746*29d08975SNiek Linnenbank case REG_RGMII_STA: /* RGMII Status */ 747*29d08975SNiek Linnenbank break; 748*29d08975SNiek Linnenbank default: 749*29d08975SNiek Linnenbank qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to unknown " 750*29d08975SNiek Linnenbank "EMAC register 0x" TARGET_FMT_plx "\n", 751*29d08975SNiek Linnenbank offset); 752*29d08975SNiek Linnenbank } 753*29d08975SNiek Linnenbank } 754*29d08975SNiek Linnenbank 755*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_set_link(NetClientState *nc) 756*29d08975SNiek Linnenbank { 757*29d08975SNiek Linnenbank AwSun8iEmacState *s = qemu_get_nic_opaque(nc); 758*29d08975SNiek Linnenbank 759*29d08975SNiek Linnenbank trace_allwinner_sun8i_emac_set_link(!nc->link_down); 760*29d08975SNiek Linnenbank allwinner_sun8i_emac_mii_set_link(s, !nc->link_down); 761*29d08975SNiek Linnenbank } 762*29d08975SNiek Linnenbank 763*29d08975SNiek Linnenbank static const MemoryRegionOps allwinner_sun8i_emac_mem_ops = { 764*29d08975SNiek Linnenbank .read = allwinner_sun8i_emac_read, 765*29d08975SNiek Linnenbank .write = allwinner_sun8i_emac_write, 766*29d08975SNiek Linnenbank .endianness = DEVICE_NATIVE_ENDIAN, 767*29d08975SNiek Linnenbank .valid = { 768*29d08975SNiek Linnenbank .min_access_size = 4, 769*29d08975SNiek Linnenbank .max_access_size = 4, 770*29d08975SNiek Linnenbank }, 771*29d08975SNiek Linnenbank .impl.min_access_size = 4, 772*29d08975SNiek Linnenbank }; 773*29d08975SNiek Linnenbank 774*29d08975SNiek Linnenbank static NetClientInfo net_allwinner_sun8i_emac_info = { 775*29d08975SNiek Linnenbank .type = NET_CLIENT_DRIVER_NIC, 776*29d08975SNiek Linnenbank .size = sizeof(NICState), 777*29d08975SNiek Linnenbank .can_receive = allwinner_sun8i_emac_can_receive, 778*29d08975SNiek Linnenbank .receive = allwinner_sun8i_emac_receive, 779*29d08975SNiek Linnenbank .link_status_changed = allwinner_sun8i_emac_set_link, 780*29d08975SNiek Linnenbank }; 781*29d08975SNiek Linnenbank 782*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_init(Object *obj) 783*29d08975SNiek Linnenbank { 784*29d08975SNiek Linnenbank SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 785*29d08975SNiek Linnenbank AwSun8iEmacState *s = AW_SUN8I_EMAC(obj); 786*29d08975SNiek Linnenbank 787*29d08975SNiek Linnenbank memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sun8i_emac_mem_ops, 788*29d08975SNiek Linnenbank s, TYPE_AW_SUN8I_EMAC, 64 * KiB); 789*29d08975SNiek Linnenbank sysbus_init_mmio(sbd, &s->iomem); 790*29d08975SNiek Linnenbank sysbus_init_irq(sbd, &s->irq); 791*29d08975SNiek Linnenbank } 792*29d08975SNiek Linnenbank 793*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_realize(DeviceState *dev, Error **errp) 794*29d08975SNiek Linnenbank { 795*29d08975SNiek Linnenbank AwSun8iEmacState *s = AW_SUN8I_EMAC(dev); 796*29d08975SNiek Linnenbank 797*29d08975SNiek Linnenbank qemu_macaddr_default_if_unset(&s->conf.macaddr); 798*29d08975SNiek Linnenbank s->nic = qemu_new_nic(&net_allwinner_sun8i_emac_info, &s->conf, 799*29d08975SNiek Linnenbank object_get_typename(OBJECT(dev)), dev->id, s); 800*29d08975SNiek Linnenbank qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 801*29d08975SNiek Linnenbank } 802*29d08975SNiek Linnenbank 803*29d08975SNiek Linnenbank static Property allwinner_sun8i_emac_properties[] = { 804*29d08975SNiek Linnenbank DEFINE_NIC_PROPERTIES(AwSun8iEmacState, conf), 805*29d08975SNiek Linnenbank DEFINE_PROP_UINT8("phy-addr", AwSun8iEmacState, mii_phy_addr, 0), 806*29d08975SNiek Linnenbank DEFINE_PROP_END_OF_LIST(), 807*29d08975SNiek Linnenbank }; 808*29d08975SNiek Linnenbank 809*29d08975SNiek Linnenbank static int allwinner_sun8i_emac_post_load(void *opaque, int version_id) 810*29d08975SNiek Linnenbank { 811*29d08975SNiek Linnenbank AwSun8iEmacState *s = opaque; 812*29d08975SNiek Linnenbank 813*29d08975SNiek Linnenbank allwinner_sun8i_emac_set_link(qemu_get_queue(s->nic)); 814*29d08975SNiek Linnenbank 815*29d08975SNiek Linnenbank return 0; 816*29d08975SNiek Linnenbank } 817*29d08975SNiek Linnenbank 818*29d08975SNiek Linnenbank static const VMStateDescription vmstate_aw_emac = { 819*29d08975SNiek Linnenbank .name = "allwinner-sun8i-emac", 820*29d08975SNiek Linnenbank .version_id = 1, 821*29d08975SNiek Linnenbank .minimum_version_id = 1, 822*29d08975SNiek Linnenbank .post_load = allwinner_sun8i_emac_post_load, 823*29d08975SNiek Linnenbank .fields = (VMStateField[]) { 824*29d08975SNiek Linnenbank VMSTATE_UINT8(mii_phy_addr, AwSun8iEmacState), 825*29d08975SNiek Linnenbank VMSTATE_UINT32(mii_cmd, AwSun8iEmacState), 826*29d08975SNiek Linnenbank VMSTATE_UINT32(mii_data, AwSun8iEmacState), 827*29d08975SNiek Linnenbank VMSTATE_UINT32(mii_cr, AwSun8iEmacState), 828*29d08975SNiek Linnenbank VMSTATE_UINT32(mii_st, AwSun8iEmacState), 829*29d08975SNiek Linnenbank VMSTATE_UINT32(mii_adv, AwSun8iEmacState), 830*29d08975SNiek Linnenbank VMSTATE_UINT32(basic_ctl0, AwSun8iEmacState), 831*29d08975SNiek Linnenbank VMSTATE_UINT32(basic_ctl1, AwSun8iEmacState), 832*29d08975SNiek Linnenbank VMSTATE_UINT32(int_en, AwSun8iEmacState), 833*29d08975SNiek Linnenbank VMSTATE_UINT32(int_sta, AwSun8iEmacState), 834*29d08975SNiek Linnenbank VMSTATE_UINT32(frm_flt, AwSun8iEmacState), 835*29d08975SNiek Linnenbank VMSTATE_UINT32(rx_ctl0, AwSun8iEmacState), 836*29d08975SNiek Linnenbank VMSTATE_UINT32(rx_ctl1, AwSun8iEmacState), 837*29d08975SNiek Linnenbank VMSTATE_UINT32(rx_desc_head, AwSun8iEmacState), 838*29d08975SNiek Linnenbank VMSTATE_UINT32(rx_desc_curr, AwSun8iEmacState), 839*29d08975SNiek Linnenbank VMSTATE_UINT32(tx_ctl0, AwSun8iEmacState), 840*29d08975SNiek Linnenbank VMSTATE_UINT32(tx_ctl1, AwSun8iEmacState), 841*29d08975SNiek Linnenbank VMSTATE_UINT32(tx_desc_head, AwSun8iEmacState), 842*29d08975SNiek Linnenbank VMSTATE_UINT32(tx_desc_curr, AwSun8iEmacState), 843*29d08975SNiek Linnenbank VMSTATE_UINT32(tx_flowctl, AwSun8iEmacState), 844*29d08975SNiek Linnenbank VMSTATE_END_OF_LIST() 845*29d08975SNiek Linnenbank } 846*29d08975SNiek Linnenbank }; 847*29d08975SNiek Linnenbank 848*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_class_init(ObjectClass *klass, void *data) 849*29d08975SNiek Linnenbank { 850*29d08975SNiek Linnenbank DeviceClass *dc = DEVICE_CLASS(klass); 851*29d08975SNiek Linnenbank 852*29d08975SNiek Linnenbank dc->realize = allwinner_sun8i_emac_realize; 853*29d08975SNiek Linnenbank dc->reset = allwinner_sun8i_emac_reset; 854*29d08975SNiek Linnenbank dc->vmsd = &vmstate_aw_emac; 855*29d08975SNiek Linnenbank device_class_set_props(dc, allwinner_sun8i_emac_properties); 856*29d08975SNiek Linnenbank } 857*29d08975SNiek Linnenbank 858*29d08975SNiek Linnenbank static const TypeInfo allwinner_sun8i_emac_info = { 859*29d08975SNiek Linnenbank .name = TYPE_AW_SUN8I_EMAC, 860*29d08975SNiek Linnenbank .parent = TYPE_SYS_BUS_DEVICE, 861*29d08975SNiek Linnenbank .instance_size = sizeof(AwSun8iEmacState), 862*29d08975SNiek Linnenbank .instance_init = allwinner_sun8i_emac_init, 863*29d08975SNiek Linnenbank .class_init = allwinner_sun8i_emac_class_init, 864*29d08975SNiek Linnenbank }; 865*29d08975SNiek Linnenbank 866*29d08975SNiek Linnenbank static void allwinner_sun8i_emac_register_types(void) 867*29d08975SNiek Linnenbank { 868*29d08975SNiek Linnenbank type_register_static(&allwinner_sun8i_emac_info); 869*29d08975SNiek Linnenbank } 870*29d08975SNiek Linnenbank 871*29d08975SNiek Linnenbank type_init(allwinner_sun8i_emac_register_types) 872