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