xref: /qemu/hw/net/allwinner_emac.c (revision 22f90bcb2be021bb894438ddfeb10c75fa7502d8)
1*22f90bcbSBeniamino Galvani /*
2*22f90bcbSBeniamino Galvani  * Emulation of Allwinner EMAC Fast Ethernet controller and
3*22f90bcbSBeniamino Galvani  * Realtek RTL8201CP PHY
4*22f90bcbSBeniamino Galvani  *
5*22f90bcbSBeniamino Galvani  * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
6*22f90bcbSBeniamino Galvani  *
7*22f90bcbSBeniamino Galvani  * This model is based on reverse-engineering of Linux kernel driver.
8*22f90bcbSBeniamino Galvani  *
9*22f90bcbSBeniamino Galvani  * This program is free software; you can redistribute it and/or modify
10*22f90bcbSBeniamino Galvani  * it under the terms of the GNU General Public License version 2 as
11*22f90bcbSBeniamino Galvani  * published by the Free Software Foundation.
12*22f90bcbSBeniamino Galvani  *
13*22f90bcbSBeniamino Galvani  * This program is distributed in the hope that it will be useful,
14*22f90bcbSBeniamino Galvani  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15*22f90bcbSBeniamino Galvani  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16*22f90bcbSBeniamino Galvani  * GNU General Public License for more details.
17*22f90bcbSBeniamino Galvani  *
18*22f90bcbSBeniamino Galvani  */
19*22f90bcbSBeniamino Galvani #include "hw/sysbus.h"
20*22f90bcbSBeniamino Galvani #include "net/net.h"
21*22f90bcbSBeniamino Galvani #include "qemu/fifo8.h"
22*22f90bcbSBeniamino Galvani #include "hw/net/allwinner_emac.h"
23*22f90bcbSBeniamino Galvani #include <zlib.h>
24*22f90bcbSBeniamino Galvani 
25*22f90bcbSBeniamino Galvani static uint8_t padding[60];
26*22f90bcbSBeniamino Galvani 
27*22f90bcbSBeniamino Galvani static void mii_set_link(RTL8201CPState *mii, bool link_ok)
28*22f90bcbSBeniamino Galvani {
29*22f90bcbSBeniamino Galvani     if (link_ok) {
30*22f90bcbSBeniamino Galvani         mii->bmsr |= MII_BMSR_LINK_ST;
31*22f90bcbSBeniamino Galvani         mii->anlpar |= MII_ANAR_TXFD | MII_ANAR_10FD | MII_ANAR_10 |
32*22f90bcbSBeniamino Galvani                        MII_ANAR_CSMACD;
33*22f90bcbSBeniamino Galvani     } else {
34*22f90bcbSBeniamino Galvani         mii->bmsr &= ~MII_BMSR_LINK_ST;
35*22f90bcbSBeniamino Galvani         mii->anlpar = MII_ANAR_TX;
36*22f90bcbSBeniamino Galvani     }
37*22f90bcbSBeniamino Galvani }
38*22f90bcbSBeniamino Galvani 
39*22f90bcbSBeniamino Galvani static void mii_reset(RTL8201CPState *mii, bool link_ok)
40*22f90bcbSBeniamino Galvani {
41*22f90bcbSBeniamino Galvani     mii->bmcr = MII_BMCR_FD | MII_BMCR_AUTOEN | MII_BMCR_SPEED;
42*22f90bcbSBeniamino Galvani     mii->bmsr = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD |
43*22f90bcbSBeniamino Galvani                 MII_BMSR_10T_HD | MII_BMSR_MFPS | MII_BMSR_AUTONEG;
44*22f90bcbSBeniamino Galvani     mii->anar = MII_ANAR_TXFD | MII_ANAR_TX | MII_ANAR_10FD | MII_ANAR_10 |
45*22f90bcbSBeniamino Galvani                 MII_ANAR_CSMACD;
46*22f90bcbSBeniamino Galvani     mii->anlpar = MII_ANAR_TX;
47*22f90bcbSBeniamino Galvani 
48*22f90bcbSBeniamino Galvani     mii_set_link(mii, link_ok);
49*22f90bcbSBeniamino Galvani }
50*22f90bcbSBeniamino Galvani 
51*22f90bcbSBeniamino Galvani static uint16_t RTL8201CP_mdio_read(AwEmacState *s, uint8_t addr, uint8_t reg)
52*22f90bcbSBeniamino Galvani {
53*22f90bcbSBeniamino Galvani     RTL8201CPState *mii = &s->mii;
54*22f90bcbSBeniamino Galvani     uint16_t ret = 0xffff;
55*22f90bcbSBeniamino Galvani 
56*22f90bcbSBeniamino Galvani     if (addr == s->phy_addr) {
57*22f90bcbSBeniamino Galvani         switch (reg) {
58*22f90bcbSBeniamino Galvani         case MII_BMCR:
59*22f90bcbSBeniamino Galvani             return mii->bmcr;
60*22f90bcbSBeniamino Galvani         case MII_BMSR:
61*22f90bcbSBeniamino Galvani             return mii->bmsr;
62*22f90bcbSBeniamino Galvani         case MII_PHYID1:
63*22f90bcbSBeniamino Galvani             return RTL8201CP_PHYID1;
64*22f90bcbSBeniamino Galvani         case MII_PHYID2:
65*22f90bcbSBeniamino Galvani             return RTL8201CP_PHYID2;
66*22f90bcbSBeniamino Galvani         case MII_ANAR:
67*22f90bcbSBeniamino Galvani             return mii->anar;
68*22f90bcbSBeniamino Galvani         case MII_ANLPAR:
69*22f90bcbSBeniamino Galvani             return mii->anlpar;
70*22f90bcbSBeniamino Galvani         case MII_ANER:
71*22f90bcbSBeniamino Galvani         case MII_NSR:
72*22f90bcbSBeniamino Galvani         case MII_LBREMR:
73*22f90bcbSBeniamino Galvani         case MII_REC:
74*22f90bcbSBeniamino Galvani         case MII_SNRDR:
75*22f90bcbSBeniamino Galvani         case MII_TEST:
76*22f90bcbSBeniamino Galvani             qemu_log_mask(LOG_UNIMP,
77*22f90bcbSBeniamino Galvani                           "allwinner_emac: read from unimpl. mii reg 0x%x\n",
78*22f90bcbSBeniamino Galvani                           reg);
79*22f90bcbSBeniamino Galvani             return 0;
80*22f90bcbSBeniamino Galvani         default:
81*22f90bcbSBeniamino Galvani             qemu_log_mask(LOG_GUEST_ERROR,
82*22f90bcbSBeniamino Galvani                           "allwinner_emac: read from invalid mii reg 0x%x\n",
83*22f90bcbSBeniamino Galvani                           reg);
84*22f90bcbSBeniamino Galvani             return 0;
85*22f90bcbSBeniamino Galvani         }
86*22f90bcbSBeniamino Galvani     }
87*22f90bcbSBeniamino Galvani     return ret;
88*22f90bcbSBeniamino Galvani }
89*22f90bcbSBeniamino Galvani 
90*22f90bcbSBeniamino Galvani static void RTL8201CP_mdio_write(AwEmacState *s, uint8_t addr, uint8_t reg,
91*22f90bcbSBeniamino Galvani                                  uint16_t value)
92*22f90bcbSBeniamino Galvani {
93*22f90bcbSBeniamino Galvani     RTL8201CPState *mii = &s->mii;
94*22f90bcbSBeniamino Galvani     NetClientState *nc;
95*22f90bcbSBeniamino Galvani 
96*22f90bcbSBeniamino Galvani     if (addr == s->phy_addr) {
97*22f90bcbSBeniamino Galvani         switch (reg) {
98*22f90bcbSBeniamino Galvani         case MII_BMCR:
99*22f90bcbSBeniamino Galvani             if (value & MII_BMCR_RESET) {
100*22f90bcbSBeniamino Galvani                 nc = qemu_get_queue(s->nic);
101*22f90bcbSBeniamino Galvani                 mii_reset(mii, !nc->link_down);
102*22f90bcbSBeniamino Galvani             } else {
103*22f90bcbSBeniamino Galvani                 mii->bmcr = value;
104*22f90bcbSBeniamino Galvani             }
105*22f90bcbSBeniamino Galvani             break;
106*22f90bcbSBeniamino Galvani         case MII_ANAR:
107*22f90bcbSBeniamino Galvani             mii->anar = value;
108*22f90bcbSBeniamino Galvani             break;
109*22f90bcbSBeniamino Galvani         case MII_BMSR:
110*22f90bcbSBeniamino Galvani         case MII_PHYID1:
111*22f90bcbSBeniamino Galvani         case MII_PHYID2:
112*22f90bcbSBeniamino Galvani         case MII_ANLPAR:
113*22f90bcbSBeniamino Galvani         case MII_ANER:
114*22f90bcbSBeniamino Galvani             qemu_log_mask(LOG_GUEST_ERROR,
115*22f90bcbSBeniamino Galvani                           "allwinner_emac: write to read-only mii reg 0x%x\n",
116*22f90bcbSBeniamino Galvani                           reg);
117*22f90bcbSBeniamino Galvani             break;
118*22f90bcbSBeniamino Galvani         case MII_NSR:
119*22f90bcbSBeniamino Galvani         case MII_LBREMR:
120*22f90bcbSBeniamino Galvani         case MII_REC:
121*22f90bcbSBeniamino Galvani         case MII_SNRDR:
122*22f90bcbSBeniamino Galvani         case MII_TEST:
123*22f90bcbSBeniamino Galvani             qemu_log_mask(LOG_UNIMP,
124*22f90bcbSBeniamino Galvani                           "allwinner_emac: write to unimpl. mii reg 0x%x\n",
125*22f90bcbSBeniamino Galvani                           reg);
126*22f90bcbSBeniamino Galvani             break;
127*22f90bcbSBeniamino Galvani         default:
128*22f90bcbSBeniamino Galvani             qemu_log_mask(LOG_GUEST_ERROR,
129*22f90bcbSBeniamino Galvani                           "allwinner_emac: write to invalid mii reg 0x%x\n",
130*22f90bcbSBeniamino Galvani                           reg);
131*22f90bcbSBeniamino Galvani         }
132*22f90bcbSBeniamino Galvani     }
133*22f90bcbSBeniamino Galvani }
134*22f90bcbSBeniamino Galvani 
135*22f90bcbSBeniamino Galvani static void aw_emac_update_irq(AwEmacState *s)
136*22f90bcbSBeniamino Galvani {
137*22f90bcbSBeniamino Galvani     qemu_set_irq(s->irq, (s->int_sta & s->int_ctl) != 0);
138*22f90bcbSBeniamino Galvani }
139*22f90bcbSBeniamino Galvani 
140*22f90bcbSBeniamino Galvani static void aw_emac_tx_reset(AwEmacState *s, int chan)
141*22f90bcbSBeniamino Galvani {
142*22f90bcbSBeniamino Galvani     fifo8_reset(&s->tx_fifo[chan]);
143*22f90bcbSBeniamino Galvani     s->tx_length[chan] = 0;
144*22f90bcbSBeniamino Galvani }
145*22f90bcbSBeniamino Galvani 
146*22f90bcbSBeniamino Galvani static void aw_emac_rx_reset(AwEmacState *s)
147*22f90bcbSBeniamino Galvani {
148*22f90bcbSBeniamino Galvani     fifo8_reset(&s->rx_fifo);
149*22f90bcbSBeniamino Galvani     s->rx_num_packets = 0;
150*22f90bcbSBeniamino Galvani     s->rx_packet_size = 0;
151*22f90bcbSBeniamino Galvani     s->rx_packet_pos = 0;
152*22f90bcbSBeniamino Galvani }
153*22f90bcbSBeniamino Galvani 
154*22f90bcbSBeniamino Galvani static void fifo8_push_word(Fifo8 *fifo, uint32_t val)
155*22f90bcbSBeniamino Galvani {
156*22f90bcbSBeniamino Galvani     fifo8_push(fifo, val);
157*22f90bcbSBeniamino Galvani     fifo8_push(fifo, val >> 8);
158*22f90bcbSBeniamino Galvani     fifo8_push(fifo, val >> 16);
159*22f90bcbSBeniamino Galvani     fifo8_push(fifo, val >> 24);
160*22f90bcbSBeniamino Galvani }
161*22f90bcbSBeniamino Galvani 
162*22f90bcbSBeniamino Galvani static uint32_t fifo8_pop_word(Fifo8 *fifo)
163*22f90bcbSBeniamino Galvani {
164*22f90bcbSBeniamino Galvani     uint32_t ret;
165*22f90bcbSBeniamino Galvani 
166*22f90bcbSBeniamino Galvani     ret = fifo8_pop(fifo);
167*22f90bcbSBeniamino Galvani     ret |= fifo8_pop(fifo) << 8;
168*22f90bcbSBeniamino Galvani     ret |= fifo8_pop(fifo) << 16;
169*22f90bcbSBeniamino Galvani     ret |= fifo8_pop(fifo) << 24;
170*22f90bcbSBeniamino Galvani 
171*22f90bcbSBeniamino Galvani     return ret;
172*22f90bcbSBeniamino Galvani }
173*22f90bcbSBeniamino Galvani 
174*22f90bcbSBeniamino Galvani static int aw_emac_can_receive(NetClientState *nc)
175*22f90bcbSBeniamino Galvani {
176*22f90bcbSBeniamino Galvani     AwEmacState *s = qemu_get_nic_opaque(nc);
177*22f90bcbSBeniamino Galvani 
178*22f90bcbSBeniamino Galvani     /*
179*22f90bcbSBeniamino Galvani      * To avoid packet drops, allow reception only when there is space
180*22f90bcbSBeniamino Galvani      * for a full frame: 1522 + 8 (rx headers) + 2 (padding).
181*22f90bcbSBeniamino Galvani      */
182*22f90bcbSBeniamino Galvani     return (s->ctl & EMAC_CTL_RX_EN) && (fifo8_num_free(&s->rx_fifo) >= 1532);
183*22f90bcbSBeniamino Galvani }
184*22f90bcbSBeniamino Galvani 
185*22f90bcbSBeniamino Galvani static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf,
186*22f90bcbSBeniamino Galvani                                size_t size)
187*22f90bcbSBeniamino Galvani {
188*22f90bcbSBeniamino Galvani     AwEmacState *s = qemu_get_nic_opaque(nc);
189*22f90bcbSBeniamino Galvani     Fifo8 *fifo = &s->rx_fifo;
190*22f90bcbSBeniamino Galvani     size_t padded_size, total_size;
191*22f90bcbSBeniamino Galvani     uint32_t crc;
192*22f90bcbSBeniamino Galvani 
193*22f90bcbSBeniamino Galvani     padded_size = size > 60 ? size : 60;
194*22f90bcbSBeniamino Galvani     total_size = QEMU_ALIGN_UP(RX_HDR_SIZE + padded_size + CRC_SIZE, 4);
195*22f90bcbSBeniamino Galvani 
196*22f90bcbSBeniamino Galvani     if (!(s->ctl & EMAC_CTL_RX_EN) || (fifo8_num_free(fifo) < total_size)) {
197*22f90bcbSBeniamino Galvani         return -1;
198*22f90bcbSBeniamino Galvani     }
199*22f90bcbSBeniamino Galvani 
200*22f90bcbSBeniamino Galvani     fifo8_push_word(fifo, EMAC_UNDOCUMENTED_MAGIC);
201*22f90bcbSBeniamino Galvani     fifo8_push_word(fifo, EMAC_RX_HEADER(padded_size + CRC_SIZE,
202*22f90bcbSBeniamino Galvani                                          EMAC_RX_IO_DATA_STATUS_OK));
203*22f90bcbSBeniamino Galvani     fifo8_push_all(fifo, buf, size);
204*22f90bcbSBeniamino Galvani     crc = crc32(~0, buf, size);
205*22f90bcbSBeniamino Galvani 
206*22f90bcbSBeniamino Galvani     if (padded_size != size) {
207*22f90bcbSBeniamino Galvani         fifo8_push_all(fifo, padding, padded_size - size);
208*22f90bcbSBeniamino Galvani         crc = crc32(crc, padding, padded_size - size);
209*22f90bcbSBeniamino Galvani     }
210*22f90bcbSBeniamino Galvani 
211*22f90bcbSBeniamino Galvani     fifo8_push_word(fifo, crc);
212*22f90bcbSBeniamino Galvani     fifo8_push_all(fifo, padding, QEMU_ALIGN_UP(padded_size, 4) - padded_size);
213*22f90bcbSBeniamino Galvani     s->rx_num_packets++;
214*22f90bcbSBeniamino Galvani 
215*22f90bcbSBeniamino Galvani     s->int_sta |= EMAC_INT_RX;
216*22f90bcbSBeniamino Galvani     aw_emac_update_irq(s);
217*22f90bcbSBeniamino Galvani 
218*22f90bcbSBeniamino Galvani     return size;
219*22f90bcbSBeniamino Galvani }
220*22f90bcbSBeniamino Galvani 
221*22f90bcbSBeniamino Galvani static void aw_emac_cleanup(NetClientState *nc)
222*22f90bcbSBeniamino Galvani {
223*22f90bcbSBeniamino Galvani     AwEmacState *s = qemu_get_nic_opaque(nc);
224*22f90bcbSBeniamino Galvani 
225*22f90bcbSBeniamino Galvani     s->nic = NULL;
226*22f90bcbSBeniamino Galvani }
227*22f90bcbSBeniamino Galvani 
228*22f90bcbSBeniamino Galvani static void aw_emac_reset(DeviceState *dev)
229*22f90bcbSBeniamino Galvani {
230*22f90bcbSBeniamino Galvani     AwEmacState *s = AW_EMAC(dev);
231*22f90bcbSBeniamino Galvani     NetClientState *nc = qemu_get_queue(s->nic);
232*22f90bcbSBeniamino Galvani 
233*22f90bcbSBeniamino Galvani     s->ctl = 0;
234*22f90bcbSBeniamino Galvani     s->tx_mode = 0;
235*22f90bcbSBeniamino Galvani     s->int_ctl = 0;
236*22f90bcbSBeniamino Galvani     s->int_sta = 0;
237*22f90bcbSBeniamino Galvani     s->tx_channel = 0;
238*22f90bcbSBeniamino Galvani     s->phy_target = 0;
239*22f90bcbSBeniamino Galvani 
240*22f90bcbSBeniamino Galvani     aw_emac_tx_reset(s, 0);
241*22f90bcbSBeniamino Galvani     aw_emac_tx_reset(s, 1);
242*22f90bcbSBeniamino Galvani     aw_emac_rx_reset(s);
243*22f90bcbSBeniamino Galvani 
244*22f90bcbSBeniamino Galvani     mii_reset(&s->mii, !nc->link_down);
245*22f90bcbSBeniamino Galvani }
246*22f90bcbSBeniamino Galvani 
247*22f90bcbSBeniamino Galvani static uint64_t aw_emac_read(void *opaque, hwaddr offset, unsigned size)
248*22f90bcbSBeniamino Galvani {
249*22f90bcbSBeniamino Galvani     AwEmacState *s = opaque;
250*22f90bcbSBeniamino Galvani     Fifo8 *fifo = &s->rx_fifo;
251*22f90bcbSBeniamino Galvani     NetClientState *nc;
252*22f90bcbSBeniamino Galvani     uint64_t ret;
253*22f90bcbSBeniamino Galvani 
254*22f90bcbSBeniamino Galvani     switch (offset) {
255*22f90bcbSBeniamino Galvani     case EMAC_CTL_REG:
256*22f90bcbSBeniamino Galvani         return s->ctl;
257*22f90bcbSBeniamino Galvani     case EMAC_TX_MODE_REG:
258*22f90bcbSBeniamino Galvani         return s->tx_mode;
259*22f90bcbSBeniamino Galvani     case EMAC_TX_INS_REG:
260*22f90bcbSBeniamino Galvani         return s->tx_channel;
261*22f90bcbSBeniamino Galvani     case EMAC_RX_CTL_REG:
262*22f90bcbSBeniamino Galvani         return s->rx_ctl;
263*22f90bcbSBeniamino Galvani     case EMAC_RX_IO_DATA_REG:
264*22f90bcbSBeniamino Galvani         if (!s->rx_num_packets) {
265*22f90bcbSBeniamino Galvani             qemu_log_mask(LOG_GUEST_ERROR,
266*22f90bcbSBeniamino Galvani                           "Read IO data register when no packet available");
267*22f90bcbSBeniamino Galvani             return 0;
268*22f90bcbSBeniamino Galvani         }
269*22f90bcbSBeniamino Galvani 
270*22f90bcbSBeniamino Galvani         ret = fifo8_pop_word(fifo);
271*22f90bcbSBeniamino Galvani 
272*22f90bcbSBeniamino Galvani         switch (s->rx_packet_pos) {
273*22f90bcbSBeniamino Galvani         case 0:     /* Word is magic header */
274*22f90bcbSBeniamino Galvani             s->rx_packet_pos += 4;
275*22f90bcbSBeniamino Galvani             break;
276*22f90bcbSBeniamino Galvani         case 4:     /* Word is rx info header */
277*22f90bcbSBeniamino Galvani             s->rx_packet_pos += 4;
278*22f90bcbSBeniamino Galvani             s->rx_packet_size = QEMU_ALIGN_UP(extract32(ret, 0, 16), 4);
279*22f90bcbSBeniamino Galvani             break;
280*22f90bcbSBeniamino Galvani         default:    /* Word is packet data */
281*22f90bcbSBeniamino Galvani             s->rx_packet_pos += 4;
282*22f90bcbSBeniamino Galvani             s->rx_packet_size -= 4;
283*22f90bcbSBeniamino Galvani 
284*22f90bcbSBeniamino Galvani             if (!s->rx_packet_size) {
285*22f90bcbSBeniamino Galvani                 s->rx_packet_pos = 0;
286*22f90bcbSBeniamino Galvani                 s->rx_num_packets--;
287*22f90bcbSBeniamino Galvani                 nc = qemu_get_queue(s->nic);
288*22f90bcbSBeniamino Galvani                 if (aw_emac_can_receive(nc)) {
289*22f90bcbSBeniamino Galvani                     qemu_flush_queued_packets(nc);
290*22f90bcbSBeniamino Galvani                 }
291*22f90bcbSBeniamino Galvani             }
292*22f90bcbSBeniamino Galvani         }
293*22f90bcbSBeniamino Galvani         return ret;
294*22f90bcbSBeniamino Galvani     case EMAC_RX_FBC_REG:
295*22f90bcbSBeniamino Galvani         return s->rx_num_packets;
296*22f90bcbSBeniamino Galvani     case EMAC_INT_CTL_REG:
297*22f90bcbSBeniamino Galvani         return s->int_ctl;
298*22f90bcbSBeniamino Galvani     case EMAC_INT_STA_REG:
299*22f90bcbSBeniamino Galvani         return s->int_sta;
300*22f90bcbSBeniamino Galvani     case EMAC_MAC_MRDD_REG:
301*22f90bcbSBeniamino Galvani         return RTL8201CP_mdio_read(s,
302*22f90bcbSBeniamino Galvani                                    extract32(s->phy_target, PHY_ADDR_SHIFT, 8),
303*22f90bcbSBeniamino Galvani                                    extract32(s->phy_target, PHY_REG_SHIFT, 8));
304*22f90bcbSBeniamino Galvani     default:
305*22f90bcbSBeniamino Galvani         qemu_log_mask(LOG_UNIMP,
306*22f90bcbSBeniamino Galvani                       "allwinner_emac: read access to unknown register 0x"
307*22f90bcbSBeniamino Galvani                       TARGET_FMT_plx "\n", offset);
308*22f90bcbSBeniamino Galvani         ret = 0;
309*22f90bcbSBeniamino Galvani     }
310*22f90bcbSBeniamino Galvani 
311*22f90bcbSBeniamino Galvani     return ret;
312*22f90bcbSBeniamino Galvani }
313*22f90bcbSBeniamino Galvani 
314*22f90bcbSBeniamino Galvani static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value,
315*22f90bcbSBeniamino Galvani                           unsigned size)
316*22f90bcbSBeniamino Galvani {
317*22f90bcbSBeniamino Galvani     AwEmacState *s = opaque;
318*22f90bcbSBeniamino Galvani     Fifo8 *fifo;
319*22f90bcbSBeniamino Galvani     NetClientState *nc = qemu_get_queue(s->nic);
320*22f90bcbSBeniamino Galvani     int chan;
321*22f90bcbSBeniamino Galvani 
322*22f90bcbSBeniamino Galvani     switch (offset) {
323*22f90bcbSBeniamino Galvani     case EMAC_CTL_REG:
324*22f90bcbSBeniamino Galvani         if (value & EMAC_CTL_RESET) {
325*22f90bcbSBeniamino Galvani             aw_emac_reset(DEVICE(s));
326*22f90bcbSBeniamino Galvani             value &= ~EMAC_CTL_RESET;
327*22f90bcbSBeniamino Galvani         }
328*22f90bcbSBeniamino Galvani         s->ctl = value;
329*22f90bcbSBeniamino Galvani         if (aw_emac_can_receive(nc)) {
330*22f90bcbSBeniamino Galvani             qemu_flush_queued_packets(nc);
331*22f90bcbSBeniamino Galvani         }
332*22f90bcbSBeniamino Galvani         break;
333*22f90bcbSBeniamino Galvani     case EMAC_TX_MODE_REG:
334*22f90bcbSBeniamino Galvani         s->tx_mode = value;
335*22f90bcbSBeniamino Galvani         break;
336*22f90bcbSBeniamino Galvani     case EMAC_TX_CTL0_REG:
337*22f90bcbSBeniamino Galvani     case EMAC_TX_CTL1_REG:
338*22f90bcbSBeniamino Galvani         chan = (offset == EMAC_TX_CTL0_REG ? 0 : 1);
339*22f90bcbSBeniamino Galvani         if ((value & 1) && (s->ctl & EMAC_CTL_TX_EN)) {
340*22f90bcbSBeniamino Galvani             uint32_t len, ret;
341*22f90bcbSBeniamino Galvani             const uint8_t *data;
342*22f90bcbSBeniamino Galvani 
343*22f90bcbSBeniamino Galvani             fifo = &s->tx_fifo[chan];
344*22f90bcbSBeniamino Galvani             len = s->tx_length[chan];
345*22f90bcbSBeniamino Galvani 
346*22f90bcbSBeniamino Galvani             if (len > fifo8_num_used(fifo)) {
347*22f90bcbSBeniamino Galvani                 len = fifo8_num_used(fifo);
348*22f90bcbSBeniamino Galvani                 qemu_log_mask(LOG_GUEST_ERROR,
349*22f90bcbSBeniamino Galvani                               "allwinner_emac: TX length > fifo data length\n");
350*22f90bcbSBeniamino Galvani             }
351*22f90bcbSBeniamino Galvani             if (len > 0) {
352*22f90bcbSBeniamino Galvani                 data = fifo8_pop_buf(fifo, len, &ret);
353*22f90bcbSBeniamino Galvani                 qemu_send_packet(nc, data, ret);
354*22f90bcbSBeniamino Galvani                 aw_emac_tx_reset(s, chan);
355*22f90bcbSBeniamino Galvani                 /* Raise TX interrupt */
356*22f90bcbSBeniamino Galvani                 s->int_sta |= EMAC_INT_TX_CHAN(chan);
357*22f90bcbSBeniamino Galvani                 aw_emac_update_irq(s);
358*22f90bcbSBeniamino Galvani             }
359*22f90bcbSBeniamino Galvani         }
360*22f90bcbSBeniamino Galvani         break;
361*22f90bcbSBeniamino Galvani     case EMAC_TX_INS_REG:
362*22f90bcbSBeniamino Galvani         s->tx_channel = value < NUM_TX_FIFOS ? value : 0;
363*22f90bcbSBeniamino Galvani         break;
364*22f90bcbSBeniamino Galvani     case EMAC_TX_PL0_REG:
365*22f90bcbSBeniamino Galvani     case EMAC_TX_PL1_REG:
366*22f90bcbSBeniamino Galvani         chan = (offset == EMAC_TX_PL0_REG ? 0 : 1);
367*22f90bcbSBeniamino Galvani         if (value > TX_FIFO_SIZE) {
368*22f90bcbSBeniamino Galvani             qemu_log_mask(LOG_GUEST_ERROR,
369*22f90bcbSBeniamino Galvani                           "allwinner_emac: invalid TX frame length %d\n",
370*22f90bcbSBeniamino Galvani                           (int)value);
371*22f90bcbSBeniamino Galvani             value = TX_FIFO_SIZE;
372*22f90bcbSBeniamino Galvani         }
373*22f90bcbSBeniamino Galvani         s->tx_length[chan] = value;
374*22f90bcbSBeniamino Galvani         break;
375*22f90bcbSBeniamino Galvani     case EMAC_TX_IO_DATA_REG:
376*22f90bcbSBeniamino Galvani         fifo = &s->tx_fifo[s->tx_channel];
377*22f90bcbSBeniamino Galvani         if (fifo8_num_free(fifo) < 4) {
378*22f90bcbSBeniamino Galvani             qemu_log_mask(LOG_GUEST_ERROR,
379*22f90bcbSBeniamino Galvani                           "allwinner_emac: TX data overruns fifo\n");
380*22f90bcbSBeniamino Galvani             break;
381*22f90bcbSBeniamino Galvani         }
382*22f90bcbSBeniamino Galvani         fifo8_push_word(fifo, value);
383*22f90bcbSBeniamino Galvani         break;
384*22f90bcbSBeniamino Galvani     case EMAC_RX_CTL_REG:
385*22f90bcbSBeniamino Galvani         s->rx_ctl = value;
386*22f90bcbSBeniamino Galvani         break;
387*22f90bcbSBeniamino Galvani     case EMAC_RX_FBC_REG:
388*22f90bcbSBeniamino Galvani         if (value == 0) {
389*22f90bcbSBeniamino Galvani             aw_emac_rx_reset(s);
390*22f90bcbSBeniamino Galvani         }
391*22f90bcbSBeniamino Galvani         break;
392*22f90bcbSBeniamino Galvani     case EMAC_INT_CTL_REG:
393*22f90bcbSBeniamino Galvani         s->int_ctl = value;
394*22f90bcbSBeniamino Galvani         break;
395*22f90bcbSBeniamino Galvani     case EMAC_INT_STA_REG:
396*22f90bcbSBeniamino Galvani         s->int_sta &= ~value;
397*22f90bcbSBeniamino Galvani         break;
398*22f90bcbSBeniamino Galvani     case EMAC_MAC_MADR_REG:
399*22f90bcbSBeniamino Galvani         s->phy_target = value;
400*22f90bcbSBeniamino Galvani         break;
401*22f90bcbSBeniamino Galvani     case EMAC_MAC_MWTD_REG:
402*22f90bcbSBeniamino Galvani         RTL8201CP_mdio_write(s, extract32(s->phy_target, PHY_ADDR_SHIFT, 8),
403*22f90bcbSBeniamino Galvani                              extract32(s->phy_target, PHY_REG_SHIFT, 8), value);
404*22f90bcbSBeniamino Galvani         break;
405*22f90bcbSBeniamino Galvani     default:
406*22f90bcbSBeniamino Galvani         qemu_log_mask(LOG_UNIMP,
407*22f90bcbSBeniamino Galvani                       "allwinner_emac: write access to unknown register 0x"
408*22f90bcbSBeniamino Galvani                       TARGET_FMT_plx "\n", offset);
409*22f90bcbSBeniamino Galvani     }
410*22f90bcbSBeniamino Galvani }
411*22f90bcbSBeniamino Galvani 
412*22f90bcbSBeniamino Galvani static void aw_emac_set_link(NetClientState *nc)
413*22f90bcbSBeniamino Galvani {
414*22f90bcbSBeniamino Galvani     AwEmacState *s = qemu_get_nic_opaque(nc);
415*22f90bcbSBeniamino Galvani 
416*22f90bcbSBeniamino Galvani     mii_set_link(&s->mii, !nc->link_down);
417*22f90bcbSBeniamino Galvani }
418*22f90bcbSBeniamino Galvani 
419*22f90bcbSBeniamino Galvani static const MemoryRegionOps aw_emac_mem_ops = {
420*22f90bcbSBeniamino Galvani     .read = aw_emac_read,
421*22f90bcbSBeniamino Galvani     .write = aw_emac_write,
422*22f90bcbSBeniamino Galvani     .endianness = DEVICE_NATIVE_ENDIAN,
423*22f90bcbSBeniamino Galvani     .valid = {
424*22f90bcbSBeniamino Galvani         .min_access_size = 4,
425*22f90bcbSBeniamino Galvani         .max_access_size = 4,
426*22f90bcbSBeniamino Galvani     },
427*22f90bcbSBeniamino Galvani };
428*22f90bcbSBeniamino Galvani 
429*22f90bcbSBeniamino Galvani static NetClientInfo net_aw_emac_info = {
430*22f90bcbSBeniamino Galvani     .type = NET_CLIENT_OPTIONS_KIND_NIC,
431*22f90bcbSBeniamino Galvani     .size = sizeof(NICState),
432*22f90bcbSBeniamino Galvani     .can_receive = aw_emac_can_receive,
433*22f90bcbSBeniamino Galvani     .receive = aw_emac_receive,
434*22f90bcbSBeniamino Galvani     .cleanup = aw_emac_cleanup,
435*22f90bcbSBeniamino Galvani     .link_status_changed = aw_emac_set_link,
436*22f90bcbSBeniamino Galvani };
437*22f90bcbSBeniamino Galvani 
438*22f90bcbSBeniamino Galvani static void aw_emac_init(Object *obj)
439*22f90bcbSBeniamino Galvani {
440*22f90bcbSBeniamino Galvani     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
441*22f90bcbSBeniamino Galvani     AwEmacState *s = AW_EMAC(obj);
442*22f90bcbSBeniamino Galvani 
443*22f90bcbSBeniamino Galvani     memory_region_init_io(&s->iomem, OBJECT(s), &aw_emac_mem_ops, s,
444*22f90bcbSBeniamino Galvani                           "aw_emac", 0x1000);
445*22f90bcbSBeniamino Galvani     sysbus_init_mmio(sbd, &s->iomem);
446*22f90bcbSBeniamino Galvani     sysbus_init_irq(sbd, &s->irq);
447*22f90bcbSBeniamino Galvani }
448*22f90bcbSBeniamino Galvani 
449*22f90bcbSBeniamino Galvani static void aw_emac_realize(DeviceState *dev, Error **errp)
450*22f90bcbSBeniamino Galvani {
451*22f90bcbSBeniamino Galvani     AwEmacState *s = AW_EMAC(dev);
452*22f90bcbSBeniamino Galvani 
453*22f90bcbSBeniamino Galvani     qemu_macaddr_default_if_unset(&s->conf.macaddr);
454*22f90bcbSBeniamino Galvani     s->nic = qemu_new_nic(&net_aw_emac_info, &s->conf,
455*22f90bcbSBeniamino Galvani                           object_get_typename(OBJECT(dev)), dev->id, s);
456*22f90bcbSBeniamino Galvani     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
457*22f90bcbSBeniamino Galvani 
458*22f90bcbSBeniamino Galvani     fifo8_create(&s->rx_fifo, RX_FIFO_SIZE);
459*22f90bcbSBeniamino Galvani     fifo8_create(&s->tx_fifo[0], TX_FIFO_SIZE);
460*22f90bcbSBeniamino Galvani     fifo8_create(&s->tx_fifo[1], TX_FIFO_SIZE);
461*22f90bcbSBeniamino Galvani }
462*22f90bcbSBeniamino Galvani 
463*22f90bcbSBeniamino Galvani static Property aw_emac_properties[] = {
464*22f90bcbSBeniamino Galvani     DEFINE_NIC_PROPERTIES(AwEmacState, conf),
465*22f90bcbSBeniamino Galvani     DEFINE_PROP_UINT8("phy-addr", AwEmacState, phy_addr, 0),
466*22f90bcbSBeniamino Galvani     DEFINE_PROP_END_OF_LIST(),
467*22f90bcbSBeniamino Galvani };
468*22f90bcbSBeniamino Galvani 
469*22f90bcbSBeniamino Galvani static const VMStateDescription vmstate_mii = {
470*22f90bcbSBeniamino Galvani     .name = "rtl8201cp",
471*22f90bcbSBeniamino Galvani     .version_id = 1,
472*22f90bcbSBeniamino Galvani     .minimum_version_id = 1,
473*22f90bcbSBeniamino Galvani     .fields = (VMStateField[]) {
474*22f90bcbSBeniamino Galvani         VMSTATE_UINT16(bmcr, RTL8201CPState),
475*22f90bcbSBeniamino Galvani         VMSTATE_UINT16(bmsr, RTL8201CPState),
476*22f90bcbSBeniamino Galvani         VMSTATE_UINT16(anar, RTL8201CPState),
477*22f90bcbSBeniamino Galvani         VMSTATE_UINT16(anlpar, RTL8201CPState),
478*22f90bcbSBeniamino Galvani         VMSTATE_END_OF_LIST()
479*22f90bcbSBeniamino Galvani     }
480*22f90bcbSBeniamino Galvani };
481*22f90bcbSBeniamino Galvani 
482*22f90bcbSBeniamino Galvani static int aw_emac_post_load(void *opaque, int version_id)
483*22f90bcbSBeniamino Galvani {
484*22f90bcbSBeniamino Galvani     AwEmacState *s = opaque;
485*22f90bcbSBeniamino Galvani 
486*22f90bcbSBeniamino Galvani     aw_emac_set_link(qemu_get_queue(s->nic));
487*22f90bcbSBeniamino Galvani 
488*22f90bcbSBeniamino Galvani     return 0;
489*22f90bcbSBeniamino Galvani }
490*22f90bcbSBeniamino Galvani 
491*22f90bcbSBeniamino Galvani static const VMStateDescription vmstate_aw_emac = {
492*22f90bcbSBeniamino Galvani     .name = "allwinner_emac",
493*22f90bcbSBeniamino Galvani     .version_id = 1,
494*22f90bcbSBeniamino Galvani     .minimum_version_id = 1,
495*22f90bcbSBeniamino Galvani     .post_load = aw_emac_post_load,
496*22f90bcbSBeniamino Galvani     .fields = (VMStateField[]) {
497*22f90bcbSBeniamino Galvani         VMSTATE_STRUCT(mii, AwEmacState, 1, vmstate_mii, RTL8201CPState),
498*22f90bcbSBeniamino Galvani         VMSTATE_UINT32(ctl, AwEmacState),
499*22f90bcbSBeniamino Galvani         VMSTATE_UINT32(tx_mode, AwEmacState),
500*22f90bcbSBeniamino Galvani         VMSTATE_UINT32(rx_ctl, AwEmacState),
501*22f90bcbSBeniamino Galvani         VMSTATE_UINT32(int_ctl, AwEmacState),
502*22f90bcbSBeniamino Galvani         VMSTATE_UINT32(int_sta, AwEmacState),
503*22f90bcbSBeniamino Galvani         VMSTATE_UINT32(phy_target, AwEmacState),
504*22f90bcbSBeniamino Galvani         VMSTATE_FIFO8(rx_fifo, AwEmacState),
505*22f90bcbSBeniamino Galvani         VMSTATE_UINT32(rx_num_packets, AwEmacState),
506*22f90bcbSBeniamino Galvani         VMSTATE_UINT32(rx_packet_size, AwEmacState),
507*22f90bcbSBeniamino Galvani         VMSTATE_UINT32(rx_packet_pos, AwEmacState),
508*22f90bcbSBeniamino Galvani         VMSTATE_STRUCT_ARRAY(tx_fifo, AwEmacState, NUM_TX_FIFOS, 1,
509*22f90bcbSBeniamino Galvani                              vmstate_fifo8, Fifo8),
510*22f90bcbSBeniamino Galvani         VMSTATE_UINT32_ARRAY(tx_length, AwEmacState, NUM_TX_FIFOS),
511*22f90bcbSBeniamino Galvani         VMSTATE_UINT32(tx_channel, AwEmacState),
512*22f90bcbSBeniamino Galvani         VMSTATE_END_OF_LIST()
513*22f90bcbSBeniamino Galvani     }
514*22f90bcbSBeniamino Galvani };
515*22f90bcbSBeniamino Galvani 
516*22f90bcbSBeniamino Galvani static void aw_emac_class_init(ObjectClass *klass, void *data)
517*22f90bcbSBeniamino Galvani {
518*22f90bcbSBeniamino Galvani     DeviceClass *dc = DEVICE_CLASS(klass);
519*22f90bcbSBeniamino Galvani 
520*22f90bcbSBeniamino Galvani     dc->realize = aw_emac_realize;
521*22f90bcbSBeniamino Galvani     dc->props = aw_emac_properties;
522*22f90bcbSBeniamino Galvani     dc->reset = aw_emac_reset;
523*22f90bcbSBeniamino Galvani     dc->vmsd = &vmstate_aw_emac;
524*22f90bcbSBeniamino Galvani }
525*22f90bcbSBeniamino Galvani 
526*22f90bcbSBeniamino Galvani static const TypeInfo aw_emac_info = {
527*22f90bcbSBeniamino Galvani     .name           = TYPE_AW_EMAC,
528*22f90bcbSBeniamino Galvani     .parent         = TYPE_SYS_BUS_DEVICE,
529*22f90bcbSBeniamino Galvani     .instance_size  = sizeof(AwEmacState),
530*22f90bcbSBeniamino Galvani     .instance_init   = aw_emac_init,
531*22f90bcbSBeniamino Galvani     .class_init     = aw_emac_class_init,
532*22f90bcbSBeniamino Galvani };
533*22f90bcbSBeniamino Galvani 
534*22f90bcbSBeniamino Galvani static void aw_emac_register_types(void)
535*22f90bcbSBeniamino Galvani {
536*22f90bcbSBeniamino Galvani     type_register_static(&aw_emac_info);
537*22f90bcbSBeniamino Galvani }
538*22f90bcbSBeniamino Galvani 
539*22f90bcbSBeniamino Galvani type_init(aw_emac_register_types)
540