xref: /qemu/hw/net/allwinner-sun8i-emac.c (revision 29d08975)
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