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