1fcbd8018SJean-Christophe Dubois /*
2fcbd8018SJean-Christophe Dubois * i.MX Fast Ethernet Controller emulation.
3fcbd8018SJean-Christophe Dubois *
4fcbd8018SJean-Christophe Dubois * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net>
5fcbd8018SJean-Christophe Dubois *
6fcbd8018SJean-Christophe Dubois * Based on Coldfire Fast Ethernet Controller emulation.
7fcbd8018SJean-Christophe Dubois *
8fcbd8018SJean-Christophe Dubois * Copyright (c) 2007 CodeSourcery.
9fcbd8018SJean-Christophe Dubois *
10fcbd8018SJean-Christophe Dubois * This program is free software; you can redistribute it and/or modify it
11fcbd8018SJean-Christophe Dubois * under the terms of the GNU General Public License as published by the
12fcbd8018SJean-Christophe Dubois * Free Software Foundation; either version 2 of the License, or
13fcbd8018SJean-Christophe Dubois * (at your option) any later version.
14fcbd8018SJean-Christophe Dubois *
15fcbd8018SJean-Christophe Dubois * This program is distributed in the hope that it will be useful, but WITHOUT
16fcbd8018SJean-Christophe Dubois * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17fcbd8018SJean-Christophe Dubois * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18fcbd8018SJean-Christophe Dubois * for more details.
19fcbd8018SJean-Christophe Dubois *
20fcbd8018SJean-Christophe Dubois * You should have received a copy of the GNU General Public License along
21fcbd8018SJean-Christophe Dubois * with this program; if not, see <http://www.gnu.org/licenses/>.
22fcbd8018SJean-Christophe Dubois */
23fcbd8018SJean-Christophe Dubois
248ef94f0bSPeter Maydell #include "qemu/osdep.h"
2564552b6bSMarkus Armbruster #include "hw/irq.h"
26fcbd8018SJean-Christophe Dubois #include "hw/net/imx_fec.h"
27a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
28d6454270SMarkus Armbruster #include "migration/vmstate.h"
2932cad1ffSPhilippe Mathieu-Daudé #include "system/dma.h"
3003dd024fSPaolo Bonzini #include "qemu/log.h"
310b8fa32fSMarkus Armbruster #include "qemu/module.h"
32a699b410SJean-Christophe Dubois #include "net/checksum.h"
33a699b410SJean-Christophe Dubois #include "net/eth.h"
348095508aSJean-Christophe Dubois #include "trace.h"
35fcbd8018SJean-Christophe Dubois
365691f477SMichael Tokarev #include <zlib.h> /* for crc32 */
37fcbd8018SJean-Christophe Dubois
3881f17e0dSPrasad J Pandit #define IMX_MAX_DESC 1024
3981f17e0dSPrasad J Pandit
imx_default_reg_name(IMXFECState * s,uint32_t index)40a699b410SJean-Christophe Dubois static const char *imx_default_reg_name(IMXFECState *s, uint32_t index)
41db0de352SJean-Christophe Dubois {
42db0de352SJean-Christophe Dubois static char tmp[20];
43ca4af17cSPhilippe Mathieu-Daudé snprintf(tmp, sizeof(tmp), "index %d", index);
44a699b410SJean-Christophe Dubois return tmp;
45a699b410SJean-Christophe Dubois }
46db0de352SJean-Christophe Dubois
imx_fec_reg_name(IMXFECState * s,uint32_t index)47a699b410SJean-Christophe Dubois static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index)
48a699b410SJean-Christophe Dubois {
49a699b410SJean-Christophe Dubois switch (index) {
50a699b410SJean-Christophe Dubois case ENET_FRBR:
51a699b410SJean-Christophe Dubois return "FRBR";
52a699b410SJean-Christophe Dubois case ENET_FRSR:
53a699b410SJean-Christophe Dubois return "FRSR";
54a699b410SJean-Christophe Dubois case ENET_MIIGSK_CFGR:
55a699b410SJean-Christophe Dubois return "MIIGSK_CFGR";
56a699b410SJean-Christophe Dubois case ENET_MIIGSK_ENR:
57a699b410SJean-Christophe Dubois return "MIIGSK_ENR";
58a699b410SJean-Christophe Dubois default:
59a699b410SJean-Christophe Dubois return imx_default_reg_name(s, index);
60a699b410SJean-Christophe Dubois }
61a699b410SJean-Christophe Dubois }
62a699b410SJean-Christophe Dubois
imx_enet_reg_name(IMXFECState * s,uint32_t index)63a699b410SJean-Christophe Dubois static const char *imx_enet_reg_name(IMXFECState *s, uint32_t index)
64a699b410SJean-Christophe Dubois {
65a699b410SJean-Christophe Dubois switch (index) {
66a699b410SJean-Christophe Dubois case ENET_RSFL:
67a699b410SJean-Christophe Dubois return "RSFL";
68a699b410SJean-Christophe Dubois case ENET_RSEM:
69a699b410SJean-Christophe Dubois return "RSEM";
70a699b410SJean-Christophe Dubois case ENET_RAEM:
71a699b410SJean-Christophe Dubois return "RAEM";
72a699b410SJean-Christophe Dubois case ENET_RAFL:
73a699b410SJean-Christophe Dubois return "RAFL";
74a699b410SJean-Christophe Dubois case ENET_TSEM:
75a699b410SJean-Christophe Dubois return "TSEM";
76a699b410SJean-Christophe Dubois case ENET_TAEM:
77a699b410SJean-Christophe Dubois return "TAEM";
78a699b410SJean-Christophe Dubois case ENET_TAFL:
79a699b410SJean-Christophe Dubois return "TAFL";
80a699b410SJean-Christophe Dubois case ENET_TIPG:
81a699b410SJean-Christophe Dubois return "TIPG";
82a699b410SJean-Christophe Dubois case ENET_FTRL:
83a699b410SJean-Christophe Dubois return "FTRL";
84a699b410SJean-Christophe Dubois case ENET_TACC:
85a699b410SJean-Christophe Dubois return "TACC";
86a699b410SJean-Christophe Dubois case ENET_RACC:
87a699b410SJean-Christophe Dubois return "RACC";
88a699b410SJean-Christophe Dubois case ENET_ATCR:
89a699b410SJean-Christophe Dubois return "ATCR";
90a699b410SJean-Christophe Dubois case ENET_ATVR:
91a699b410SJean-Christophe Dubois return "ATVR";
92a699b410SJean-Christophe Dubois case ENET_ATOFF:
93a699b410SJean-Christophe Dubois return "ATOFF";
94a699b410SJean-Christophe Dubois case ENET_ATPER:
95a699b410SJean-Christophe Dubois return "ATPER";
96a699b410SJean-Christophe Dubois case ENET_ATCOR:
97a699b410SJean-Christophe Dubois return "ATCOR";
98a699b410SJean-Christophe Dubois case ENET_ATINC:
99a699b410SJean-Christophe Dubois return "ATINC";
100a699b410SJean-Christophe Dubois case ENET_ATSTMP:
101a699b410SJean-Christophe Dubois return "ATSTMP";
102a699b410SJean-Christophe Dubois case ENET_TGSR:
103a699b410SJean-Christophe Dubois return "TGSR";
104a699b410SJean-Christophe Dubois case ENET_TCSR0:
105a699b410SJean-Christophe Dubois return "TCSR0";
106a699b410SJean-Christophe Dubois case ENET_TCCR0:
107a699b410SJean-Christophe Dubois return "TCCR0";
108a699b410SJean-Christophe Dubois case ENET_TCSR1:
109a699b410SJean-Christophe Dubois return "TCSR1";
110a699b410SJean-Christophe Dubois case ENET_TCCR1:
111a699b410SJean-Christophe Dubois return "TCCR1";
112a699b410SJean-Christophe Dubois case ENET_TCSR2:
113a699b410SJean-Christophe Dubois return "TCSR2";
114a699b410SJean-Christophe Dubois case ENET_TCCR2:
115a699b410SJean-Christophe Dubois return "TCCR2";
116a699b410SJean-Christophe Dubois case ENET_TCSR3:
117a699b410SJean-Christophe Dubois return "TCSR3";
118a699b410SJean-Christophe Dubois case ENET_TCCR3:
119a699b410SJean-Christophe Dubois return "TCCR3";
120a699b410SJean-Christophe Dubois default:
121a699b410SJean-Christophe Dubois return imx_default_reg_name(s, index);
122a699b410SJean-Christophe Dubois }
123a699b410SJean-Christophe Dubois }
124a699b410SJean-Christophe Dubois
imx_eth_reg_name(IMXFECState * s,uint32_t index)125a699b410SJean-Christophe Dubois static const char *imx_eth_reg_name(IMXFECState *s, uint32_t index)
126a699b410SJean-Christophe Dubois {
127db0de352SJean-Christophe Dubois switch (index) {
128db0de352SJean-Christophe Dubois case ENET_EIR:
129db0de352SJean-Christophe Dubois return "EIR";
130db0de352SJean-Christophe Dubois case ENET_EIMR:
131db0de352SJean-Christophe Dubois return "EIMR";
132db0de352SJean-Christophe Dubois case ENET_RDAR:
133db0de352SJean-Christophe Dubois return "RDAR";
134db0de352SJean-Christophe Dubois case ENET_TDAR:
135db0de352SJean-Christophe Dubois return "TDAR";
136db0de352SJean-Christophe Dubois case ENET_ECR:
137db0de352SJean-Christophe Dubois return "ECR";
138db0de352SJean-Christophe Dubois case ENET_MMFR:
139db0de352SJean-Christophe Dubois return "MMFR";
140db0de352SJean-Christophe Dubois case ENET_MSCR:
141db0de352SJean-Christophe Dubois return "MSCR";
142db0de352SJean-Christophe Dubois case ENET_MIBC:
143db0de352SJean-Christophe Dubois return "MIBC";
144db0de352SJean-Christophe Dubois case ENET_RCR:
145db0de352SJean-Christophe Dubois return "RCR";
146db0de352SJean-Christophe Dubois case ENET_TCR:
147db0de352SJean-Christophe Dubois return "TCR";
148db0de352SJean-Christophe Dubois case ENET_PALR:
149db0de352SJean-Christophe Dubois return "PALR";
150db0de352SJean-Christophe Dubois case ENET_PAUR:
151db0de352SJean-Christophe Dubois return "PAUR";
152db0de352SJean-Christophe Dubois case ENET_OPD:
153db0de352SJean-Christophe Dubois return "OPD";
154db0de352SJean-Christophe Dubois case ENET_IAUR:
155db0de352SJean-Christophe Dubois return "IAUR";
156db0de352SJean-Christophe Dubois case ENET_IALR:
157db0de352SJean-Christophe Dubois return "IALR";
158db0de352SJean-Christophe Dubois case ENET_GAUR:
159db0de352SJean-Christophe Dubois return "GAUR";
160db0de352SJean-Christophe Dubois case ENET_GALR:
161db0de352SJean-Christophe Dubois return "GALR";
162db0de352SJean-Christophe Dubois case ENET_TFWR:
163db0de352SJean-Christophe Dubois return "TFWR";
164db0de352SJean-Christophe Dubois case ENET_RDSR:
165db0de352SJean-Christophe Dubois return "RDSR";
166db0de352SJean-Christophe Dubois case ENET_TDSR:
167db0de352SJean-Christophe Dubois return "TDSR";
168db0de352SJean-Christophe Dubois case ENET_MRBR:
169db0de352SJean-Christophe Dubois return "MRBR";
170db0de352SJean-Christophe Dubois default:
171a699b410SJean-Christophe Dubois if (s->is_fec) {
172a699b410SJean-Christophe Dubois return imx_fec_reg_name(s, index);
173a699b410SJean-Christophe Dubois } else {
174a699b410SJean-Christophe Dubois return imx_enet_reg_name(s, index);
175a699b410SJean-Christophe Dubois }
176db0de352SJean-Christophe Dubois }
177db0de352SJean-Christophe Dubois }
178db0de352SJean-Christophe Dubois
179f93f961cSAndrey Smirnov /*
180f93f961cSAndrey Smirnov * Versions of this device with more than one TX descriptor save the
181f93f961cSAndrey Smirnov * 2nd and 3rd descriptors in a subsection, to maintain migration
182f93f961cSAndrey Smirnov * compatibility with previous versions of the device that only
183f93f961cSAndrey Smirnov * supported a single descriptor.
184f93f961cSAndrey Smirnov */
imx_eth_is_multi_tx_ring(void * opaque)185f93f961cSAndrey Smirnov static bool imx_eth_is_multi_tx_ring(void *opaque)
186f93f961cSAndrey Smirnov {
187f93f961cSAndrey Smirnov IMXFECState *s = IMX_FEC(opaque);
188f93f961cSAndrey Smirnov
189f93f961cSAndrey Smirnov return s->tx_ring_num > 1;
190f93f961cSAndrey Smirnov }
191f93f961cSAndrey Smirnov
192f93f961cSAndrey Smirnov static const VMStateDescription vmstate_imx_eth_txdescs = {
193f93f961cSAndrey Smirnov .name = "imx.fec/txdescs",
194f93f961cSAndrey Smirnov .version_id = 1,
195f93f961cSAndrey Smirnov .minimum_version_id = 1,
196f93f961cSAndrey Smirnov .needed = imx_eth_is_multi_tx_ring,
1971de81b42SRichard Henderson .fields = (const VMStateField[]) {
198f93f961cSAndrey Smirnov VMSTATE_UINT32(tx_descriptor[1], IMXFECState),
199f93f961cSAndrey Smirnov VMSTATE_UINT32(tx_descriptor[2], IMXFECState),
200f93f961cSAndrey Smirnov VMSTATE_END_OF_LIST()
201f93f961cSAndrey Smirnov }
202f93f961cSAndrey Smirnov };
203f93f961cSAndrey Smirnov
204a699b410SJean-Christophe Dubois static const VMStateDescription vmstate_imx_eth = {
205fcbd8018SJean-Christophe Dubois .name = TYPE_IMX_FEC,
206c01194e1SBernhard Beschow .version_id = 3,
207c01194e1SBernhard Beschow .minimum_version_id = 3,
2081de81b42SRichard Henderson .fields = (const VMStateField[]) {
209db0de352SJean-Christophe Dubois VMSTATE_UINT32_ARRAY(regs, IMXFECState, ENET_MAX),
210fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(rx_descriptor, IMXFECState),
211f93f961cSAndrey Smirnov VMSTATE_UINT32(tx_descriptor[0], IMXFECState),
212fcbd8018SJean-Christophe Dubois VMSTATE_END_OF_LIST()
213f93f961cSAndrey Smirnov },
2141de81b42SRichard Henderson .subsections = (const VMStateDescription * const []) {
215f93f961cSAndrey Smirnov &vmstate_imx_eth_txdescs,
216f93f961cSAndrey Smirnov NULL
217f93f961cSAndrey Smirnov },
218fcbd8018SJean-Christophe Dubois };
219fcbd8018SJean-Christophe Dubois
220a699b410SJean-Christophe Dubois static void imx_eth_update(IMXFECState *s);
221fcbd8018SJean-Christophe Dubois
222fcbd8018SJean-Christophe Dubois /*
223fcbd8018SJean-Christophe Dubois * The MII phy could raise a GPIO to the processor which in turn
224fcbd8018SJean-Christophe Dubois * could be handled as an interrpt by the OS.
225fcbd8018SJean-Christophe Dubois * For now we don't handle any GPIO/interrupt line, so the OS will
226fcbd8018SJean-Christophe Dubois * have to poll for the PHY status.
227fcbd8018SJean-Christophe Dubois */
imx_phy_update_irq(void * opaque,int n,int level)228c01194e1SBernhard Beschow static void imx_phy_update_irq(void *opaque, int n, int level)
229fcbd8018SJean-Christophe Dubois {
230c01194e1SBernhard Beschow imx_eth_update(opaque);
231fcbd8018SJean-Christophe Dubois }
232fcbd8018SJean-Christophe Dubois
imx_eth_set_link(NetClientState * nc)233a699b410SJean-Christophe Dubois static void imx_eth_set_link(NetClientState *nc)
234fcbd8018SJean-Christophe Dubois {
235c01194e1SBernhard Beschow lan9118_phy_update_link(&IMX_FEC(qemu_get_nic_opaque(nc))->mii,
236c01194e1SBernhard Beschow nc->link_down);
237fcbd8018SJean-Christophe Dubois }
238fcbd8018SJean-Christophe Dubois
imx_phy_read(IMXFECState * s,int reg)2398095508aSJean-Christophe Dubois static uint32_t imx_phy_read(IMXFECState *s, int reg)
240fcbd8018SJean-Christophe Dubois {
241461c51adSJean-Christophe Dubois uint32_t phy = reg / 32;
242fcbd8018SJean-Christophe Dubois
243df3f5efeSGuenter Roeck if (!s->phy_connected) {
244df3f5efeSGuenter Roeck return 0xffff;
245df3f5efeSGuenter Roeck }
246df3f5efeSGuenter Roeck
247461c51adSJean-Christophe Dubois if (phy != s->phy_num) {
248df3f5efeSGuenter Roeck if (s->phy_consumer && phy == s->phy_consumer->phy_num) {
249df3f5efeSGuenter Roeck s = s->phy_consumer;
250df3f5efeSGuenter Roeck } else {
251f607dce2SGuenter Roeck trace_imx_phy_read_num(phy, s->phy_num);
252f607dce2SGuenter Roeck return 0xffff;
253fcbd8018SJean-Christophe Dubois }
254df3f5efeSGuenter Roeck }
255fcbd8018SJean-Christophe Dubois
256461c51adSJean-Christophe Dubois reg %= 32;
257461c51adSJean-Christophe Dubois
258c01194e1SBernhard Beschow return lan9118_phy_read(&s->mii, reg);
259fcbd8018SJean-Christophe Dubois }
260fcbd8018SJean-Christophe Dubois
imx_phy_write(IMXFECState * s,int reg,uint32_t val)2618095508aSJean-Christophe Dubois static void imx_phy_write(IMXFECState *s, int reg, uint32_t val)
262fcbd8018SJean-Christophe Dubois {
263461c51adSJean-Christophe Dubois uint32_t phy = reg / 32;
264fcbd8018SJean-Christophe Dubois
265df3f5efeSGuenter Roeck if (!s->phy_connected) {
266df3f5efeSGuenter Roeck return;
267df3f5efeSGuenter Roeck }
268df3f5efeSGuenter Roeck
269461c51adSJean-Christophe Dubois if (phy != s->phy_num) {
270df3f5efeSGuenter Roeck if (s->phy_consumer && phy == s->phy_consumer->phy_num) {
271df3f5efeSGuenter Roeck s = s->phy_consumer;
272df3f5efeSGuenter Roeck } else {
273f607dce2SGuenter Roeck trace_imx_phy_write_num(phy, s->phy_num);
274fcbd8018SJean-Christophe Dubois return;
275fcbd8018SJean-Christophe Dubois }
276df3f5efeSGuenter Roeck }
277fcbd8018SJean-Christophe Dubois
278461c51adSJean-Christophe Dubois reg %= 32;
279461c51adSJean-Christophe Dubois
280c01194e1SBernhard Beschow lan9118_phy_write(&s->mii, reg, val);
281fcbd8018SJean-Christophe Dubois }
282fcbd8018SJean-Christophe Dubois
imx_fec_read_bd(IMXFECBufDesc * bd,dma_addr_t addr)283fcbd8018SJean-Christophe Dubois static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr)
284fcbd8018SJean-Christophe Dubois {
285ba06fe8aSPhilippe Mathieu-Daudé dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd),
286ba06fe8aSPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED);
2878095508aSJean-Christophe Dubois
2888095508aSJean-Christophe Dubois trace_imx_fec_read_bd(addr, bd->flags, bd->length, bd->data);
289fcbd8018SJean-Christophe Dubois }
290fcbd8018SJean-Christophe Dubois
imx_fec_write_bd(IMXFECBufDesc * bd,dma_addr_t addr)291fcbd8018SJean-Christophe Dubois static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr)
292fcbd8018SJean-Christophe Dubois {
293ba06fe8aSPhilippe Mathieu-Daudé dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd),
294ba06fe8aSPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED);
295fcbd8018SJean-Christophe Dubois }
296fcbd8018SJean-Christophe Dubois
imx_enet_read_bd(IMXENETBufDesc * bd,dma_addr_t addr)297a699b410SJean-Christophe Dubois static void imx_enet_read_bd(IMXENETBufDesc *bd, dma_addr_t addr)
298fcbd8018SJean-Christophe Dubois {
299ba06fe8aSPhilippe Mathieu-Daudé dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd),
300ba06fe8aSPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED);
3018095508aSJean-Christophe Dubois
3028095508aSJean-Christophe Dubois trace_imx_enet_read_bd(addr, bd->flags, bd->length, bd->data,
3038095508aSJean-Christophe Dubois bd->option, bd->status);
304a699b410SJean-Christophe Dubois }
305a699b410SJean-Christophe Dubois
imx_enet_write_bd(IMXENETBufDesc * bd,dma_addr_t addr)306a699b410SJean-Christophe Dubois static void imx_enet_write_bd(IMXENETBufDesc *bd, dma_addr_t addr)
307a699b410SJean-Christophe Dubois {
308ba06fe8aSPhilippe Mathieu-Daudé dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd),
309ba06fe8aSPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED);
310a699b410SJean-Christophe Dubois }
311a699b410SJean-Christophe Dubois
imx_eth_update(IMXFECState * s)312a699b410SJean-Christophe Dubois static void imx_eth_update(IMXFECState *s)
313a699b410SJean-Christophe Dubois {
3146461d7e2SGuenter Roeck /*
3156461d7e2SGuenter Roeck * Previous versions of qemu had the ENET_INT_MAC and ENET_INT_TS_TIMER
3166461d7e2SGuenter Roeck * interrupts swapped. This worked with older versions of Linux (4.14
3176461d7e2SGuenter Roeck * and older) since Linux associated both interrupt lines with Ethernet
3186461d7e2SGuenter Roeck * MAC interrupts. Specifically,
3196461d7e2SGuenter Roeck * - Linux 4.15 and later have separate interrupt handlers for the MAC and
3206461d7e2SGuenter Roeck * timer interrupts. Those versions of Linux fail with versions of QEMU
3216461d7e2SGuenter Roeck * with swapped interrupt assignments.
3226461d7e2SGuenter Roeck * - In linux 4.14, both interrupt lines were registered with the Ethernet
3236461d7e2SGuenter Roeck * MAC interrupt handler. As a result, all versions of qemu happen to
3246461d7e2SGuenter Roeck * work, though that is accidental.
3256461d7e2SGuenter Roeck * - In Linux 4.9 and older, the timer interrupt was registered directly
3266461d7e2SGuenter Roeck * with the Ethernet MAC interrupt handler. The MAC interrupt was
3276461d7e2SGuenter Roeck * redirected to a GPIO interrupt to work around erratum ERR006687.
3286461d7e2SGuenter Roeck * This was implemented using the SOC's IOMUX block. In qemu, this GPIO
3296461d7e2SGuenter Roeck * interrupt never fired since IOMUX is currently not supported in qemu.
3306461d7e2SGuenter Roeck * Linux instead received MAC interrupts on the timer interrupt.
3316461d7e2SGuenter Roeck * As a result, qemu versions with the swapped interrupt assignment work,
3326461d7e2SGuenter Roeck * albeit accidentally, but qemu versions with the correct interrupt
3336461d7e2SGuenter Roeck * assignment fail.
3346461d7e2SGuenter Roeck *
3356461d7e2SGuenter Roeck * To ensure that all versions of Linux work, generate ENET_INT_MAC
336118d4ed0SDr. David Alan Gilbert * interrupts on both interrupt lines. This should be changed if and when
3376461d7e2SGuenter Roeck * qemu supports IOMUX.
3386461d7e2SGuenter Roeck */
3396461d7e2SGuenter Roeck if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] &
3406461d7e2SGuenter Roeck (ENET_INT_MAC | ENET_INT_TS_TIMER)) {
341a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[1], 1);
342db0de352SJean-Christophe Dubois } else {
343a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[1], 0);
344a699b410SJean-Christophe Dubois }
345a699b410SJean-Christophe Dubois
346a699b410SJean-Christophe Dubois if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & ENET_INT_MAC) {
347a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[0], 1);
348a699b410SJean-Christophe Dubois } else {
349a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[0], 0);
350fcbd8018SJean-Christophe Dubois }
351fcbd8018SJean-Christophe Dubois }
352fcbd8018SJean-Christophe Dubois
imx_fec_do_tx(IMXFECState * s)353fcbd8018SJean-Christophe Dubois static void imx_fec_do_tx(IMXFECState *s)
354fcbd8018SJean-Christophe Dubois {
35581f17e0dSPrasad J Pandit int frame_size = 0, descnt = 0;
3567bac20dcSAndrey Smirnov uint8_t *ptr = s->frame;
357f93f961cSAndrey Smirnov uint32_t addr = s->tx_descriptor[0];
358fcbd8018SJean-Christophe Dubois
35981f17e0dSPrasad J Pandit while (descnt++ < IMX_MAX_DESC) {
360fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd;
361fcbd8018SJean-Christophe Dubois int len;
362fcbd8018SJean-Christophe Dubois
363fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, addr);
3641bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_R) == 0) {
3658095508aSJean-Christophe Dubois
366fcbd8018SJean-Christophe Dubois /* Run out of descriptors to transmit. */
3678095508aSJean-Christophe Dubois trace_imx_eth_tx_bd_busy();
3688095508aSJean-Christophe Dubois
369fcbd8018SJean-Christophe Dubois break;
370fcbd8018SJean-Christophe Dubois }
371fcbd8018SJean-Christophe Dubois len = bd.length;
3721bb3c371SJean-Christophe Dubois if (frame_size + len > ENET_MAX_FRAME_SIZE) {
3731bb3c371SJean-Christophe Dubois len = ENET_MAX_FRAME_SIZE - frame_size;
374db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_BABT;
375fcbd8018SJean-Christophe Dubois }
376ba06fe8aSPhilippe Mathieu-Daudé dma_memory_read(&address_space_memory, bd.data, ptr, len,
377ba06fe8aSPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED);
378fcbd8018SJean-Christophe Dubois ptr += len;
379fcbd8018SJean-Christophe Dubois frame_size += len;
3801bb3c371SJean-Christophe Dubois if (bd.flags & ENET_BD_L) {
381fcbd8018SJean-Christophe Dubois /* Last buffer in frame. */
3827bac20dcSAndrey Smirnov qemu_send_packet(qemu_get_queue(s->nic), s->frame, frame_size);
3837bac20dcSAndrey Smirnov ptr = s->frame;
384fcbd8018SJean-Christophe Dubois frame_size = 0;
385db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_TXF;
386fcbd8018SJean-Christophe Dubois }
387db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_TXB;
3881bb3c371SJean-Christophe Dubois bd.flags &= ~ENET_BD_R;
389fcbd8018SJean-Christophe Dubois /* Write back the modified descriptor. */
390fcbd8018SJean-Christophe Dubois imx_fec_write_bd(&bd, addr);
391fcbd8018SJean-Christophe Dubois /* Advance to the next descriptor. */
3921bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) {
393db0de352SJean-Christophe Dubois addr = s->regs[ENET_TDSR];
394fcbd8018SJean-Christophe Dubois } else {
395db0de352SJean-Christophe Dubois addr += sizeof(bd);
396fcbd8018SJean-Christophe Dubois }
397fcbd8018SJean-Christophe Dubois }
398fcbd8018SJean-Christophe Dubois
399f93f961cSAndrey Smirnov s->tx_descriptor[0] = addr;
400fcbd8018SJean-Christophe Dubois
401a699b410SJean-Christophe Dubois imx_eth_update(s);
402fcbd8018SJean-Christophe Dubois }
403fcbd8018SJean-Christophe Dubois
imx_enet_do_tx(IMXFECState * s,uint32_t index)404f93f961cSAndrey Smirnov static void imx_enet_do_tx(IMXFECState *s, uint32_t index)
405a699b410SJean-Christophe Dubois {
40681f17e0dSPrasad J Pandit int frame_size = 0, descnt = 0;
407f93f961cSAndrey Smirnov
4087bac20dcSAndrey Smirnov uint8_t *ptr = s->frame;
409f93f961cSAndrey Smirnov uint32_t addr, int_txb, int_txf, tdsr;
410f93f961cSAndrey Smirnov size_t ring;
411f93f961cSAndrey Smirnov
412f93f961cSAndrey Smirnov switch (index) {
413f93f961cSAndrey Smirnov case ENET_TDAR:
414f93f961cSAndrey Smirnov ring = 0;
415f93f961cSAndrey Smirnov int_txb = ENET_INT_TXB;
416f93f961cSAndrey Smirnov int_txf = ENET_INT_TXF;
417f93f961cSAndrey Smirnov tdsr = ENET_TDSR;
418f93f961cSAndrey Smirnov break;
419f93f961cSAndrey Smirnov case ENET_TDAR1:
420f93f961cSAndrey Smirnov ring = 1;
421f93f961cSAndrey Smirnov int_txb = ENET_INT_TXB1;
422f93f961cSAndrey Smirnov int_txf = ENET_INT_TXF1;
423f93f961cSAndrey Smirnov tdsr = ENET_TDSR1;
424f93f961cSAndrey Smirnov break;
425f93f961cSAndrey Smirnov case ENET_TDAR2:
426f93f961cSAndrey Smirnov ring = 2;
427f93f961cSAndrey Smirnov int_txb = ENET_INT_TXB2;
428f93f961cSAndrey Smirnov int_txf = ENET_INT_TXF2;
429f93f961cSAndrey Smirnov tdsr = ENET_TDSR2;
430f93f961cSAndrey Smirnov break;
431f93f961cSAndrey Smirnov default:
432f93f961cSAndrey Smirnov qemu_log_mask(LOG_GUEST_ERROR,
433f93f961cSAndrey Smirnov "%s: bogus value for index %x\n",
434f93f961cSAndrey Smirnov __func__, index);
435f93f961cSAndrey Smirnov abort();
436f93f961cSAndrey Smirnov break;
437f93f961cSAndrey Smirnov }
438f93f961cSAndrey Smirnov
439f93f961cSAndrey Smirnov addr = s->tx_descriptor[ring];
440a699b410SJean-Christophe Dubois
44181f17e0dSPrasad J Pandit while (descnt++ < IMX_MAX_DESC) {
442a699b410SJean-Christophe Dubois IMXENETBufDesc bd;
443a699b410SJean-Christophe Dubois int len;
444a699b410SJean-Christophe Dubois
445a699b410SJean-Christophe Dubois imx_enet_read_bd(&bd, addr);
446a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_R) == 0) {
447a699b410SJean-Christophe Dubois /* Run out of descriptors to transmit. */
4488095508aSJean-Christophe Dubois
4498095508aSJean-Christophe Dubois trace_imx_eth_tx_bd_busy();
4508095508aSJean-Christophe Dubois
451a699b410SJean-Christophe Dubois break;
452a699b410SJean-Christophe Dubois }
453a699b410SJean-Christophe Dubois len = bd.length;
454a699b410SJean-Christophe Dubois if (frame_size + len > ENET_MAX_FRAME_SIZE) {
455a699b410SJean-Christophe Dubois len = ENET_MAX_FRAME_SIZE - frame_size;
456a699b410SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_BABT;
457a699b410SJean-Christophe Dubois }
458ba06fe8aSPhilippe Mathieu-Daudé dma_memory_read(&address_space_memory, bd.data, ptr, len,
459ba06fe8aSPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED);
460a699b410SJean-Christophe Dubois ptr += len;
461a699b410SJean-Christophe Dubois frame_size += len;
462a699b410SJean-Christophe Dubois if (bd.flags & ENET_BD_L) {
463f5746335SBin Meng int csum = 0;
464f5746335SBin Meng
465a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_PINS) {
466f5746335SBin Meng csum |= (CSUM_TCP | CSUM_UDP);
467a699b410SJean-Christophe Dubois }
468a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_IINS) {
469f5746335SBin Meng csum |= CSUM_IP;
470a699b410SJean-Christophe Dubois }
471f5746335SBin Meng if (csum) {
472f5746335SBin Meng net_checksum_calculate(s->frame, frame_size, csum);
473a699b410SJean-Christophe Dubois }
474f5746335SBin Meng
475a699b410SJean-Christophe Dubois /* Last buffer in frame. */
4767bac20dcSAndrey Smirnov
47752cfd584SAndrey Smirnov qemu_send_packet(qemu_get_queue(s->nic), s->frame, frame_size);
4787bac20dcSAndrey Smirnov ptr = s->frame;
4797bac20dcSAndrey Smirnov
480a699b410SJean-Christophe Dubois frame_size = 0;
481a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_TX_INT) {
482f93f961cSAndrey Smirnov s->regs[ENET_EIR] |= int_txf;
483a699b410SJean-Christophe Dubois }
48488e1b59eSAaron Hill /* Indicate that we've updated the last buffer descriptor. */
48588e1b59eSAaron Hill bd.last_buffer = ENET_BD_BDU;
486a699b410SJean-Christophe Dubois }
487a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_TX_INT) {
488f93f961cSAndrey Smirnov s->regs[ENET_EIR] |= int_txb;
489a699b410SJean-Christophe Dubois }
490a699b410SJean-Christophe Dubois bd.flags &= ~ENET_BD_R;
491a699b410SJean-Christophe Dubois /* Write back the modified descriptor. */
492a699b410SJean-Christophe Dubois imx_enet_write_bd(&bd, addr);
493a699b410SJean-Christophe Dubois /* Advance to the next descriptor. */
494a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) {
495f93f961cSAndrey Smirnov addr = s->regs[tdsr];
496a699b410SJean-Christophe Dubois } else {
497a699b410SJean-Christophe Dubois addr += sizeof(bd);
498a699b410SJean-Christophe Dubois }
499a699b410SJean-Christophe Dubois }
500a699b410SJean-Christophe Dubois
501f93f961cSAndrey Smirnov s->tx_descriptor[ring] = addr;
502a699b410SJean-Christophe Dubois
503a699b410SJean-Christophe Dubois imx_eth_update(s);
504a699b410SJean-Christophe Dubois }
505a699b410SJean-Christophe Dubois
imx_eth_do_tx(IMXFECState * s,uint32_t index)506f93f961cSAndrey Smirnov static void imx_eth_do_tx(IMXFECState *s, uint32_t index)
507a699b410SJean-Christophe Dubois {
508a699b410SJean-Christophe Dubois if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) {
509f93f961cSAndrey Smirnov imx_enet_do_tx(s, index);
510a699b410SJean-Christophe Dubois } else {
511a699b410SJean-Christophe Dubois imx_fec_do_tx(s);
512a699b410SJean-Christophe Dubois }
513a699b410SJean-Christophe Dubois }
514a699b410SJean-Christophe Dubois
imx_eth_enable_rx(IMXFECState * s,bool flush)515b2b012afSAndrey Smirnov static void imx_eth_enable_rx(IMXFECState *s, bool flush)
516fcbd8018SJean-Christophe Dubois {
517fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd;
518fcbd8018SJean-Christophe Dubois
519fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, s->rx_descriptor);
520fcbd8018SJean-Christophe Dubois
5211b58d58fSJean-Christophe Dubois s->regs[ENET_RDAR] = (bd.flags & ENET_BD_E) ? ENET_RDAR_RDAR : 0;
522fcbd8018SJean-Christophe Dubois
5231b58d58fSJean-Christophe Dubois if (!s->regs[ENET_RDAR]) {
5248095508aSJean-Christophe Dubois trace_imx_eth_rx_bd_full();
525b2b012afSAndrey Smirnov } else if (flush) {
526fcbd8018SJean-Christophe Dubois qemu_flush_queued_packets(qemu_get_queue(s->nic));
527fcbd8018SJean-Christophe Dubois }
528fcbd8018SJean-Christophe Dubois }
529fcbd8018SJean-Christophe Dubois
imx_eth_reset(DeviceState * d)530a699b410SJean-Christophe Dubois static void imx_eth_reset(DeviceState *d)
531fcbd8018SJean-Christophe Dubois {
532fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(d);
533fcbd8018SJean-Christophe Dubois
534a699b410SJean-Christophe Dubois /* Reset the Device */
535db0de352SJean-Christophe Dubois memset(s->regs, 0, sizeof(s->regs));
536db0de352SJean-Christophe Dubois s->regs[ENET_ECR] = 0xf0000000;
537db0de352SJean-Christophe Dubois s->regs[ENET_MIBC] = 0xc0000000;
538db0de352SJean-Christophe Dubois s->regs[ENET_RCR] = 0x05ee0001;
539db0de352SJean-Christophe Dubois s->regs[ENET_OPD] = 0x00010000;
540db0de352SJean-Christophe Dubois
541db0de352SJean-Christophe Dubois s->regs[ENET_PALR] = (s->conf.macaddr.a[0] << 24)
542db0de352SJean-Christophe Dubois | (s->conf.macaddr.a[1] << 16)
543db0de352SJean-Christophe Dubois | (s->conf.macaddr.a[2] << 8)
544db0de352SJean-Christophe Dubois | s->conf.macaddr.a[3];
545db0de352SJean-Christophe Dubois s->regs[ENET_PAUR] = (s->conf.macaddr.a[4] << 24)
546db0de352SJean-Christophe Dubois | (s->conf.macaddr.a[5] << 16)
547db0de352SJean-Christophe Dubois | 0x8808;
548db0de352SJean-Christophe Dubois
549a699b410SJean-Christophe Dubois if (s->is_fec) {
550db0de352SJean-Christophe Dubois s->regs[ENET_FRBR] = 0x00000600;
551db0de352SJean-Christophe Dubois s->regs[ENET_FRSR] = 0x00000500;
552db0de352SJean-Christophe Dubois s->regs[ENET_MIIGSK_ENR] = 0x00000006;
553a699b410SJean-Christophe Dubois } else {
554a699b410SJean-Christophe Dubois s->regs[ENET_RAEM] = 0x00000004;
555a699b410SJean-Christophe Dubois s->regs[ENET_RAFL] = 0x00000004;
556a699b410SJean-Christophe Dubois s->regs[ENET_TAEM] = 0x00000004;
557a699b410SJean-Christophe Dubois s->regs[ENET_TAFL] = 0x00000008;
558a699b410SJean-Christophe Dubois s->regs[ENET_TIPG] = 0x0000000c;
559a699b410SJean-Christophe Dubois s->regs[ENET_FTRL] = 0x000007ff;
560a699b410SJean-Christophe Dubois s->regs[ENET_ATPER] = 0x3b9aca00;
561a699b410SJean-Christophe Dubois }
562db0de352SJean-Christophe Dubois
563db0de352SJean-Christophe Dubois s->rx_descriptor = 0;
564f93f961cSAndrey Smirnov memset(s->tx_descriptor, 0, sizeof(s->tx_descriptor));
565fcbd8018SJean-Christophe Dubois }
566fcbd8018SJean-Christophe Dubois
imx_default_read(IMXFECState * s,uint32_t index)567a699b410SJean-Christophe Dubois static uint32_t imx_default_read(IMXFECState *s, uint32_t index)
568a699b410SJean-Christophe Dubois {
569a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
570a699b410SJean-Christophe Dubois PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
571a699b410SJean-Christophe Dubois return 0;
572a699b410SJean-Christophe Dubois }
573a699b410SJean-Christophe Dubois
imx_fec_read(IMXFECState * s,uint32_t index)574a699b410SJean-Christophe Dubois static uint32_t imx_fec_read(IMXFECState *s, uint32_t index)
575a699b410SJean-Christophe Dubois {
576a699b410SJean-Christophe Dubois switch (index) {
577a699b410SJean-Christophe Dubois case ENET_FRBR:
578a699b410SJean-Christophe Dubois case ENET_FRSR:
579a699b410SJean-Christophe Dubois case ENET_MIIGSK_CFGR:
580a699b410SJean-Christophe Dubois case ENET_MIIGSK_ENR:
581a699b410SJean-Christophe Dubois return s->regs[index];
582a699b410SJean-Christophe Dubois default:
583a699b410SJean-Christophe Dubois return imx_default_read(s, index);
584a699b410SJean-Christophe Dubois }
585a699b410SJean-Christophe Dubois }
586a699b410SJean-Christophe Dubois
imx_enet_read(IMXFECState * s,uint32_t index)587a699b410SJean-Christophe Dubois static uint32_t imx_enet_read(IMXFECState *s, uint32_t index)
588a699b410SJean-Christophe Dubois {
589a699b410SJean-Christophe Dubois switch (index) {
590a699b410SJean-Christophe Dubois case ENET_RSFL:
591a699b410SJean-Christophe Dubois case ENET_RSEM:
592a699b410SJean-Christophe Dubois case ENET_RAEM:
593a699b410SJean-Christophe Dubois case ENET_RAFL:
594a699b410SJean-Christophe Dubois case ENET_TSEM:
595a699b410SJean-Christophe Dubois case ENET_TAEM:
596a699b410SJean-Christophe Dubois case ENET_TAFL:
597a699b410SJean-Christophe Dubois case ENET_TIPG:
598a699b410SJean-Christophe Dubois case ENET_FTRL:
599a699b410SJean-Christophe Dubois case ENET_TACC:
600a699b410SJean-Christophe Dubois case ENET_RACC:
601a699b410SJean-Christophe Dubois case ENET_ATCR:
602a699b410SJean-Christophe Dubois case ENET_ATVR:
603a699b410SJean-Christophe Dubois case ENET_ATOFF:
604a699b410SJean-Christophe Dubois case ENET_ATPER:
605a699b410SJean-Christophe Dubois case ENET_ATCOR:
606a699b410SJean-Christophe Dubois case ENET_ATINC:
607a699b410SJean-Christophe Dubois case ENET_ATSTMP:
608a699b410SJean-Christophe Dubois case ENET_TGSR:
609a699b410SJean-Christophe Dubois case ENET_TCSR0:
610a699b410SJean-Christophe Dubois case ENET_TCCR0:
611a699b410SJean-Christophe Dubois case ENET_TCSR1:
612a699b410SJean-Christophe Dubois case ENET_TCCR1:
613a699b410SJean-Christophe Dubois case ENET_TCSR2:
614a699b410SJean-Christophe Dubois case ENET_TCCR2:
615a699b410SJean-Christophe Dubois case ENET_TCSR3:
616a699b410SJean-Christophe Dubois case ENET_TCCR3:
617a699b410SJean-Christophe Dubois return s->regs[index];
618a699b410SJean-Christophe Dubois default:
619a699b410SJean-Christophe Dubois return imx_default_read(s, index);
620a699b410SJean-Christophe Dubois }
621a699b410SJean-Christophe Dubois }
622a699b410SJean-Christophe Dubois
imx_eth_read(void * opaque,hwaddr offset,unsigned size)623a699b410SJean-Christophe Dubois static uint64_t imx_eth_read(void *opaque, hwaddr offset, unsigned size)
624fcbd8018SJean-Christophe Dubois {
625db0de352SJean-Christophe Dubois uint32_t value = 0;
626fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(opaque);
627a699b410SJean-Christophe Dubois uint32_t index = offset >> 2;
628fcbd8018SJean-Christophe Dubois
629db0de352SJean-Christophe Dubois switch (index) {
630db0de352SJean-Christophe Dubois case ENET_EIR:
631db0de352SJean-Christophe Dubois case ENET_EIMR:
632db0de352SJean-Christophe Dubois case ENET_RDAR:
633db0de352SJean-Christophe Dubois case ENET_TDAR:
634db0de352SJean-Christophe Dubois case ENET_ECR:
635db0de352SJean-Christophe Dubois case ENET_MMFR:
636db0de352SJean-Christophe Dubois case ENET_MSCR:
637db0de352SJean-Christophe Dubois case ENET_MIBC:
638db0de352SJean-Christophe Dubois case ENET_RCR:
639db0de352SJean-Christophe Dubois case ENET_TCR:
640db0de352SJean-Christophe Dubois case ENET_PALR:
641db0de352SJean-Christophe Dubois case ENET_PAUR:
642db0de352SJean-Christophe Dubois case ENET_OPD:
643db0de352SJean-Christophe Dubois case ENET_IAUR:
644db0de352SJean-Christophe Dubois case ENET_IALR:
645db0de352SJean-Christophe Dubois case ENET_GAUR:
646db0de352SJean-Christophe Dubois case ENET_GALR:
647db0de352SJean-Christophe Dubois case ENET_TFWR:
648db0de352SJean-Christophe Dubois case ENET_RDSR:
649db0de352SJean-Christophe Dubois case ENET_TDSR:
650db0de352SJean-Christophe Dubois case ENET_MRBR:
651db0de352SJean-Christophe Dubois value = s->regs[index];
652fcbd8018SJean-Christophe Dubois break;
653fcbd8018SJean-Christophe Dubois default:
654a699b410SJean-Christophe Dubois if (s->is_fec) {
655a699b410SJean-Christophe Dubois value = imx_fec_read(s, index);
656a699b410SJean-Christophe Dubois } else {
657a699b410SJean-Christophe Dubois value = imx_enet_read(s, index);
658a699b410SJean-Christophe Dubois }
659db0de352SJean-Christophe Dubois break;
660fcbd8018SJean-Christophe Dubois }
661db0de352SJean-Christophe Dubois
6628095508aSJean-Christophe Dubois trace_imx_eth_read(index, imx_eth_reg_name(s, index), value);
663db0de352SJean-Christophe Dubois
664db0de352SJean-Christophe Dubois return value;
665fcbd8018SJean-Christophe Dubois }
666fcbd8018SJean-Christophe Dubois
imx_default_write(IMXFECState * s,uint32_t index,uint32_t value)667a699b410SJean-Christophe Dubois static void imx_default_write(IMXFECState *s, uint32_t index, uint32_t value)
668a699b410SJean-Christophe Dubois {
669a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
670a699b410SJean-Christophe Dubois PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
671a699b410SJean-Christophe Dubois }
672a699b410SJean-Christophe Dubois
imx_fec_write(IMXFECState * s,uint32_t index,uint32_t value)673a699b410SJean-Christophe Dubois static void imx_fec_write(IMXFECState *s, uint32_t index, uint32_t value)
674a699b410SJean-Christophe Dubois {
675a699b410SJean-Christophe Dubois switch (index) {
676a699b410SJean-Christophe Dubois case ENET_FRBR:
677a699b410SJean-Christophe Dubois /* FRBR is read only */
678a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register FRBR is read only\n",
679a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__);
680a699b410SJean-Christophe Dubois break;
681a699b410SJean-Christophe Dubois case ENET_FRSR:
682a699b410SJean-Christophe Dubois s->regs[index] = (value & 0x000003fc) | 0x00000400;
683a699b410SJean-Christophe Dubois break;
684a699b410SJean-Christophe Dubois case ENET_MIIGSK_CFGR:
685a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00000053;
686a699b410SJean-Christophe Dubois break;
687a699b410SJean-Christophe Dubois case ENET_MIIGSK_ENR:
688a699b410SJean-Christophe Dubois s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0;
689a699b410SJean-Christophe Dubois break;
690a699b410SJean-Christophe Dubois default:
691a699b410SJean-Christophe Dubois imx_default_write(s, index, value);
692a699b410SJean-Christophe Dubois break;
693a699b410SJean-Christophe Dubois }
694a699b410SJean-Christophe Dubois }
695a699b410SJean-Christophe Dubois
imx_enet_write(IMXFECState * s,uint32_t index,uint32_t value)696a699b410SJean-Christophe Dubois static void imx_enet_write(IMXFECState *s, uint32_t index, uint32_t value)
697a699b410SJean-Christophe Dubois {
698a699b410SJean-Christophe Dubois switch (index) {
699a699b410SJean-Christophe Dubois case ENET_RSFL:
700a699b410SJean-Christophe Dubois case ENET_RSEM:
701a699b410SJean-Christophe Dubois case ENET_RAEM:
702a699b410SJean-Christophe Dubois case ENET_RAFL:
703a699b410SJean-Christophe Dubois case ENET_TSEM:
704a699b410SJean-Christophe Dubois case ENET_TAEM:
705a699b410SJean-Christophe Dubois case ENET_TAFL:
706a699b410SJean-Christophe Dubois s->regs[index] = value & 0x000001ff;
707a699b410SJean-Christophe Dubois break;
708a699b410SJean-Christophe Dubois case ENET_TIPG:
709a699b410SJean-Christophe Dubois s->regs[index] = value & 0x0000001f;
710a699b410SJean-Christophe Dubois break;
711a699b410SJean-Christophe Dubois case ENET_FTRL:
712a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00003fff;
713a699b410SJean-Christophe Dubois break;
714a699b410SJean-Christophe Dubois case ENET_TACC:
715a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00000019;
716a699b410SJean-Christophe Dubois break;
717a699b410SJean-Christophe Dubois case ENET_RACC:
718a699b410SJean-Christophe Dubois s->regs[index] = value & 0x000000C7;
719a699b410SJean-Christophe Dubois break;
720a699b410SJean-Christophe Dubois case ENET_ATCR:
721a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00002a9d;
722a699b410SJean-Christophe Dubois break;
723a699b410SJean-Christophe Dubois case ENET_ATVR:
724a699b410SJean-Christophe Dubois case ENET_ATOFF:
725a699b410SJean-Christophe Dubois case ENET_ATPER:
726a699b410SJean-Christophe Dubois s->regs[index] = value;
727a699b410SJean-Christophe Dubois break;
728a699b410SJean-Christophe Dubois case ENET_ATSTMP:
729a699b410SJean-Christophe Dubois /* ATSTMP is read only */
730a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register ATSTMP is read only\n",
731a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__);
732a699b410SJean-Christophe Dubois break;
733a699b410SJean-Christophe Dubois case ENET_ATCOR:
734a699b410SJean-Christophe Dubois s->regs[index] = value & 0x7fffffff;
735a699b410SJean-Christophe Dubois break;
736a699b410SJean-Christophe Dubois case ENET_ATINC:
737a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00007f7f;
738a699b410SJean-Christophe Dubois break;
739a699b410SJean-Christophe Dubois case ENET_TGSR:
740a699b410SJean-Christophe Dubois /* implement clear timer flag */
741a510d0c1SChen Qun s->regs[index] &= ~(value & 0x0000000f); /* all bits W1C */
742a699b410SJean-Christophe Dubois break;
743a699b410SJean-Christophe Dubois case ENET_TCSR0:
744a699b410SJean-Christophe Dubois case ENET_TCSR1:
745a699b410SJean-Christophe Dubois case ENET_TCSR2:
746a699b410SJean-Christophe Dubois case ENET_TCSR3:
747a510d0c1SChen Qun s->regs[index] &= ~(value & 0x00000080); /* W1C bits */
748a510d0c1SChen Qun s->regs[index] &= ~0x0000007d; /* writable fields */
749a510d0c1SChen Qun s->regs[index] |= (value & 0x0000007d);
750a699b410SJean-Christophe Dubois break;
751a699b410SJean-Christophe Dubois case ENET_TCCR0:
752a699b410SJean-Christophe Dubois case ENET_TCCR1:
753a699b410SJean-Christophe Dubois case ENET_TCCR2:
754a699b410SJean-Christophe Dubois case ENET_TCCR3:
755a699b410SJean-Christophe Dubois s->regs[index] = value;
756a699b410SJean-Christophe Dubois break;
757a699b410SJean-Christophe Dubois default:
758a699b410SJean-Christophe Dubois imx_default_write(s, index, value);
759a699b410SJean-Christophe Dubois break;
760a699b410SJean-Christophe Dubois }
761a699b410SJean-Christophe Dubois }
762a699b410SJean-Christophe Dubois
imx_eth_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)763a699b410SJean-Christophe Dubois static void imx_eth_write(void *opaque, hwaddr offset, uint64_t value,
764a699b410SJean-Christophe Dubois unsigned size)
765fcbd8018SJean-Christophe Dubois {
766fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(opaque);
767f93f961cSAndrey Smirnov const bool single_tx_ring = !imx_eth_is_multi_tx_ring(s);
768a699b410SJean-Christophe Dubois uint32_t index = offset >> 2;
769fcbd8018SJean-Christophe Dubois
7708095508aSJean-Christophe Dubois trace_imx_eth_write(index, imx_eth_reg_name(s, index), value);
771fcbd8018SJean-Christophe Dubois
772db0de352SJean-Christophe Dubois switch (index) {
773db0de352SJean-Christophe Dubois case ENET_EIR:
774db0de352SJean-Christophe Dubois s->regs[index] &= ~value;
775fcbd8018SJean-Christophe Dubois break;
776db0de352SJean-Christophe Dubois case ENET_EIMR:
777db0de352SJean-Christophe Dubois s->regs[index] = value;
778fcbd8018SJean-Christophe Dubois break;
779db0de352SJean-Christophe Dubois case ENET_RDAR:
780db0de352SJean-Christophe Dubois if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) {
781db0de352SJean-Christophe Dubois if (!s->regs[index]) {
782b2b012afSAndrey Smirnov imx_eth_enable_rx(s, true);
783fcbd8018SJean-Christophe Dubois }
784db0de352SJean-Christophe Dubois } else {
785db0de352SJean-Christophe Dubois s->regs[index] = 0;
786db0de352SJean-Christophe Dubois }
787fcbd8018SJean-Christophe Dubois break;
7887c45c1d3SPhilippe Mathieu-Daudé case ENET_TDAR1:
7897c45c1d3SPhilippe Mathieu-Daudé case ENET_TDAR2:
790f93f961cSAndrey Smirnov if (unlikely(single_tx_ring)) {
791f93f961cSAndrey Smirnov qemu_log_mask(LOG_GUEST_ERROR,
792f93f961cSAndrey Smirnov "[%s]%s: trying to access TDAR2 or TDAR1\n",
793f93f961cSAndrey Smirnov TYPE_IMX_FEC, __func__);
794f93f961cSAndrey Smirnov return;
795f93f961cSAndrey Smirnov }
796174c556cSPhilippe Mathieu-Daudé /* fall through */
797174c556cSPhilippe Mathieu-Daudé case ENET_TDAR:
798db0de352SJean-Christophe Dubois if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) {
799db0de352SJean-Christophe Dubois s->regs[index] = ENET_TDAR_TDAR;
800f93f961cSAndrey Smirnov imx_eth_do_tx(s, index);
801fcbd8018SJean-Christophe Dubois }
802db0de352SJean-Christophe Dubois s->regs[index] = 0;
803fcbd8018SJean-Christophe Dubois break;
804db0de352SJean-Christophe Dubois case ENET_ECR:
8051bb3c371SJean-Christophe Dubois if (value & ENET_ECR_RESET) {
806a699b410SJean-Christophe Dubois return imx_eth_reset(DEVICE(s));
807fcbd8018SJean-Christophe Dubois }
808db0de352SJean-Christophe Dubois s->regs[index] = value;
809db0de352SJean-Christophe Dubois if ((s->regs[index] & ENET_ECR_ETHEREN) == 0) {
810db0de352SJean-Christophe Dubois s->regs[ENET_RDAR] = 0;
811db0de352SJean-Christophe Dubois s->rx_descriptor = s->regs[ENET_RDSR];
812db0de352SJean-Christophe Dubois s->regs[ENET_TDAR] = 0;
813f93f961cSAndrey Smirnov s->regs[ENET_TDAR1] = 0;
814f93f961cSAndrey Smirnov s->regs[ENET_TDAR2] = 0;
815f93f961cSAndrey Smirnov s->tx_descriptor[0] = s->regs[ENET_TDSR];
816f93f961cSAndrey Smirnov s->tx_descriptor[1] = s->regs[ENET_TDSR1];
817f93f961cSAndrey Smirnov s->tx_descriptor[2] = s->regs[ENET_TDSR2];
818fcbd8018SJean-Christophe Dubois }
819fcbd8018SJean-Christophe Dubois break;
820db0de352SJean-Christophe Dubois case ENET_MMFR:
821db0de352SJean-Christophe Dubois s->regs[index] = value;
8224816dc16SJean-Christophe Dubois if (extract32(value, 29, 1)) {
823db0de352SJean-Christophe Dubois /* This is a read operation */
824db0de352SJean-Christophe Dubois s->regs[ENET_MMFR] = deposit32(s->regs[ENET_MMFR], 0, 16,
8258095508aSJean-Christophe Dubois imx_phy_read(s,
826db0de352SJean-Christophe Dubois extract32(value,
827db0de352SJean-Christophe Dubois 18, 10)));
8284816dc16SJean-Christophe Dubois } else {
829461c51adSJean-Christophe Dubois /* This is a write operation */
8308095508aSJean-Christophe Dubois imx_phy_write(s, extract32(value, 18, 10), extract32(value, 0, 16));
831fcbd8018SJean-Christophe Dubois }
832fcbd8018SJean-Christophe Dubois /* raise the interrupt as the PHY operation is done */
833db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_MII;
834fcbd8018SJean-Christophe Dubois break;
835db0de352SJean-Christophe Dubois case ENET_MSCR:
836db0de352SJean-Christophe Dubois s->regs[index] = value & 0xfe;
837fcbd8018SJean-Christophe Dubois break;
838db0de352SJean-Christophe Dubois case ENET_MIBC:
839fcbd8018SJean-Christophe Dubois /* TODO: Implement MIB. */
840db0de352SJean-Christophe Dubois s->regs[index] = (value & 0x80000000) ? 0xc0000000 : 0;
841fcbd8018SJean-Christophe Dubois break;
842db0de352SJean-Christophe Dubois case ENET_RCR:
843db0de352SJean-Christophe Dubois s->regs[index] = value & 0x07ff003f;
844fcbd8018SJean-Christophe Dubois /* TODO: Implement LOOP mode. */
845fcbd8018SJean-Christophe Dubois break;
846db0de352SJean-Christophe Dubois case ENET_TCR:
847fcbd8018SJean-Christophe Dubois /* We transmit immediately, so raise GRA immediately. */
848db0de352SJean-Christophe Dubois s->regs[index] = value;
849fcbd8018SJean-Christophe Dubois if (value & 1) {
850db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_GRA;
851fcbd8018SJean-Christophe Dubois }
852fcbd8018SJean-Christophe Dubois break;
853db0de352SJean-Christophe Dubois case ENET_PALR:
854db0de352SJean-Christophe Dubois s->regs[index] = value;
855fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[0] = value >> 24;
856fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[1] = value >> 16;
857fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[2] = value >> 8;
858fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[3] = value;
859fcbd8018SJean-Christophe Dubois break;
860db0de352SJean-Christophe Dubois case ENET_PAUR:
861db0de352SJean-Christophe Dubois s->regs[index] = (value | 0x0000ffff) & 0xffff8808;
862fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[4] = value >> 24;
863fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[5] = value >> 16;
864fcbd8018SJean-Christophe Dubois break;
865db0de352SJean-Christophe Dubois case ENET_OPD:
866db0de352SJean-Christophe Dubois s->regs[index] = (value & 0x0000ffff) | 0x00010000;
867fcbd8018SJean-Christophe Dubois break;
868db0de352SJean-Christophe Dubois case ENET_IAUR:
869db0de352SJean-Christophe Dubois case ENET_IALR:
870db0de352SJean-Christophe Dubois case ENET_GAUR:
871db0de352SJean-Christophe Dubois case ENET_GALR:
872fcbd8018SJean-Christophe Dubois /* TODO: implement MAC hash filtering. */
873fcbd8018SJean-Christophe Dubois break;
874db0de352SJean-Christophe Dubois case ENET_TFWR:
875a699b410SJean-Christophe Dubois if (s->is_fec) {
876a699b410SJean-Christophe Dubois s->regs[index] = value & 0x3;
877a699b410SJean-Christophe Dubois } else {
878a699b410SJean-Christophe Dubois s->regs[index] = value & 0x13f;
879a699b410SJean-Christophe Dubois }
880fcbd8018SJean-Christophe Dubois break;
881db0de352SJean-Christophe Dubois case ENET_RDSR:
882a699b410SJean-Christophe Dubois if (s->is_fec) {
883db0de352SJean-Christophe Dubois s->regs[index] = value & ~3;
884a699b410SJean-Christophe Dubois } else {
885a699b410SJean-Christophe Dubois s->regs[index] = value & ~7;
886a699b410SJean-Christophe Dubois }
887db0de352SJean-Christophe Dubois s->rx_descriptor = s->regs[index];
888fcbd8018SJean-Christophe Dubois break;
889db0de352SJean-Christophe Dubois case ENET_TDSR:
890a699b410SJean-Christophe Dubois if (s->is_fec) {
891db0de352SJean-Christophe Dubois s->regs[index] = value & ~3;
892a699b410SJean-Christophe Dubois } else {
893a699b410SJean-Christophe Dubois s->regs[index] = value & ~7;
894a699b410SJean-Christophe Dubois }
895f93f961cSAndrey Smirnov s->tx_descriptor[0] = s->regs[index];
896f93f961cSAndrey Smirnov break;
897f93f961cSAndrey Smirnov case ENET_TDSR1:
898f93f961cSAndrey Smirnov if (unlikely(single_tx_ring)) {
899f93f961cSAndrey Smirnov qemu_log_mask(LOG_GUEST_ERROR,
900f93f961cSAndrey Smirnov "[%s]%s: trying to access TDSR1\n",
901f93f961cSAndrey Smirnov TYPE_IMX_FEC, __func__);
902f93f961cSAndrey Smirnov return;
903f93f961cSAndrey Smirnov }
904f93f961cSAndrey Smirnov
905f93f961cSAndrey Smirnov s->regs[index] = value & ~7;
906f93f961cSAndrey Smirnov s->tx_descriptor[1] = s->regs[index];
907f93f961cSAndrey Smirnov break;
908f93f961cSAndrey Smirnov case ENET_TDSR2:
909f93f961cSAndrey Smirnov if (unlikely(single_tx_ring)) {
910f93f961cSAndrey Smirnov qemu_log_mask(LOG_GUEST_ERROR,
911f93f961cSAndrey Smirnov "[%s]%s: trying to access TDSR2\n",
912f93f961cSAndrey Smirnov TYPE_IMX_FEC, __func__);
913f93f961cSAndrey Smirnov return;
914f93f961cSAndrey Smirnov }
915f93f961cSAndrey Smirnov
916f93f961cSAndrey Smirnov s->regs[index] = value & ~7;
917f93f961cSAndrey Smirnov s->tx_descriptor[2] = s->regs[index];
918fcbd8018SJean-Christophe Dubois break;
919db0de352SJean-Christophe Dubois case ENET_MRBR:
920a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00003ff0;
921fcbd8018SJean-Christophe Dubois break;
922fcbd8018SJean-Christophe Dubois default:
923a699b410SJean-Christophe Dubois if (s->is_fec) {
924a699b410SJean-Christophe Dubois imx_fec_write(s, index, value);
925a699b410SJean-Christophe Dubois } else {
926a699b410SJean-Christophe Dubois imx_enet_write(s, index, value);
927a699b410SJean-Christophe Dubois }
928a699b410SJean-Christophe Dubois return;
929fcbd8018SJean-Christophe Dubois }
930fcbd8018SJean-Christophe Dubois
931a699b410SJean-Christophe Dubois imx_eth_update(s);
932fcbd8018SJean-Christophe Dubois }
933fcbd8018SJean-Christophe Dubois
imx_eth_can_receive(NetClientState * nc)934b8c4b67eSPhilippe Mathieu-Daudé static bool imx_eth_can_receive(NetClientState *nc)
935fcbd8018SJean-Christophe Dubois {
936fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
937fcbd8018SJean-Christophe Dubois
938b2b012afSAndrey Smirnov return !!s->regs[ENET_RDAR];
939fcbd8018SJean-Christophe Dubois }
940fcbd8018SJean-Christophe Dubois
imx_fec_receive(NetClientState * nc,const uint8_t * buf,size_t len)941fcbd8018SJean-Christophe Dubois static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
942fcbd8018SJean-Christophe Dubois size_t len)
943fcbd8018SJean-Christophe Dubois {
944fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
945fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd;
946fcbd8018SJean-Christophe Dubois uint32_t flags = 0;
947fcbd8018SJean-Christophe Dubois uint32_t addr;
948fcbd8018SJean-Christophe Dubois uint32_t crc;
949fcbd8018SJean-Christophe Dubois uint32_t buf_addr;
950fcbd8018SJean-Christophe Dubois uint8_t *crc_ptr;
951fcbd8018SJean-Christophe Dubois unsigned int buf_len;
952fcbd8018SJean-Christophe Dubois size_t size = len;
953fcbd8018SJean-Christophe Dubois
9548095508aSJean-Christophe Dubois trace_imx_fec_receive(size);
955fcbd8018SJean-Christophe Dubois
956db0de352SJean-Christophe Dubois if (!s->regs[ENET_RDAR]) {
957b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n",
958fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__);
959fcbd8018SJean-Christophe Dubois return 0;
960fcbd8018SJean-Christophe Dubois }
961fcbd8018SJean-Christophe Dubois
962fcbd8018SJean-Christophe Dubois crc = cpu_to_be32(crc32(~0, buf, size));
96393c9678dSStephen Longfield /* Increase size by 4, loop below reads the last 4 bytes from crc_ptr. */
96493c9678dSStephen Longfield size += 4;
965fcbd8018SJean-Christophe Dubois crc_ptr = (uint8_t *) &crc;
966fcbd8018SJean-Christophe Dubois
967a699b410SJean-Christophe Dubois /* Huge frames are truncated. */
9681bb3c371SJean-Christophe Dubois if (size > ENET_MAX_FRAME_SIZE) {
9691bb3c371SJean-Christophe Dubois size = ENET_MAX_FRAME_SIZE;
9701bb3c371SJean-Christophe Dubois flags |= ENET_BD_TR | ENET_BD_LG;
971fcbd8018SJean-Christophe Dubois }
972fcbd8018SJean-Christophe Dubois
973fcbd8018SJean-Christophe Dubois /* Frames larger than the user limit just set error flags. */
974db0de352SJean-Christophe Dubois if (size > (s->regs[ENET_RCR] >> 16)) {
9751bb3c371SJean-Christophe Dubois flags |= ENET_BD_LG;
976fcbd8018SJean-Christophe Dubois }
977fcbd8018SJean-Christophe Dubois
978fcbd8018SJean-Christophe Dubois addr = s->rx_descriptor;
979fcbd8018SJean-Christophe Dubois while (size > 0) {
980fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, addr);
9811bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_E) == 0) {
982fcbd8018SJean-Christophe Dubois /* No descriptors available. Bail out. */
983fcbd8018SJean-Christophe Dubois /*
984fcbd8018SJean-Christophe Dubois * FIXME: This is wrong. We should probably either
985fcbd8018SJean-Christophe Dubois * save the remainder for when more RX buffers are
986fcbd8018SJean-Christophe Dubois * available, or flag an error.
987fcbd8018SJean-Christophe Dubois */
988b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n",
989fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__);
990fcbd8018SJean-Christophe Dubois break;
991fcbd8018SJean-Christophe Dubois }
992db0de352SJean-Christophe Dubois buf_len = (size <= s->regs[ENET_MRBR]) ? size : s->regs[ENET_MRBR];
993fcbd8018SJean-Christophe Dubois bd.length = buf_len;
994fcbd8018SJean-Christophe Dubois size -= buf_len;
995b72d8d25SJean-Christophe Dubois
9968095508aSJean-Christophe Dubois trace_imx_fec_receive_len(addr, bd.length);
997b72d8d25SJean-Christophe Dubois
998fcbd8018SJean-Christophe Dubois /* The last 4 bytes are the CRC. */
999fcbd8018SJean-Christophe Dubois if (size < 4) {
1000fcbd8018SJean-Christophe Dubois buf_len += size - 4;
1001fcbd8018SJean-Christophe Dubois }
1002fcbd8018SJean-Christophe Dubois buf_addr = bd.data;
1003ba06fe8aSPhilippe Mathieu-Daudé dma_memory_write(&address_space_memory, buf_addr, buf, buf_len,
1004ba06fe8aSPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED);
1005fcbd8018SJean-Christophe Dubois buf += buf_len;
1006fcbd8018SJean-Christophe Dubois if (size < 4) {
1007fcbd8018SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr + buf_len,
1008ba06fe8aSPhilippe Mathieu-Daudé crc_ptr, 4 - size, MEMTXATTRS_UNSPECIFIED);
1009fcbd8018SJean-Christophe Dubois crc_ptr += 4 - size;
1010fcbd8018SJean-Christophe Dubois }
10111bb3c371SJean-Christophe Dubois bd.flags &= ~ENET_BD_E;
1012fcbd8018SJean-Christophe Dubois if (size == 0) {
1013fcbd8018SJean-Christophe Dubois /* Last buffer in frame. */
10141bb3c371SJean-Christophe Dubois bd.flags |= flags | ENET_BD_L;
10158095508aSJean-Christophe Dubois
10168095508aSJean-Christophe Dubois trace_imx_fec_receive_last(bd.flags);
10178095508aSJean-Christophe Dubois
1018db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXF;
1019fcbd8018SJean-Christophe Dubois } else {
1020db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXB;
1021fcbd8018SJean-Christophe Dubois }
1022fcbd8018SJean-Christophe Dubois imx_fec_write_bd(&bd, addr);
1023fcbd8018SJean-Christophe Dubois /* Advance to the next descriptor. */
10241bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) {
1025db0de352SJean-Christophe Dubois addr = s->regs[ENET_RDSR];
1026fcbd8018SJean-Christophe Dubois } else {
1027db0de352SJean-Christophe Dubois addr += sizeof(bd);
1028fcbd8018SJean-Christophe Dubois }
1029fcbd8018SJean-Christophe Dubois }
1030fcbd8018SJean-Christophe Dubois s->rx_descriptor = addr;
1031b2b012afSAndrey Smirnov imx_eth_enable_rx(s, false);
1032a699b410SJean-Christophe Dubois imx_eth_update(s);
1033fcbd8018SJean-Christophe Dubois return len;
1034fcbd8018SJean-Christophe Dubois }
1035fcbd8018SJean-Christophe Dubois
imx_enet_receive(NetClientState * nc,const uint8_t * buf,size_t len)1036a699b410SJean-Christophe Dubois static ssize_t imx_enet_receive(NetClientState *nc, const uint8_t *buf,
1037a699b410SJean-Christophe Dubois size_t len)
1038a699b410SJean-Christophe Dubois {
1039a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
1040a699b410SJean-Christophe Dubois IMXENETBufDesc bd;
1041a699b410SJean-Christophe Dubois uint32_t flags = 0;
1042a699b410SJean-Christophe Dubois uint32_t addr;
1043a699b410SJean-Christophe Dubois uint32_t crc;
1044a699b410SJean-Christophe Dubois uint32_t buf_addr;
1045a699b410SJean-Christophe Dubois uint8_t *crc_ptr;
1046a699b410SJean-Christophe Dubois unsigned int buf_len;
1047a699b410SJean-Christophe Dubois size_t size = len;
1048ebdd8cddSAndrey Smirnov bool shift16 = s->regs[ENET_RACC] & ENET_RACC_SHIFT16;
1049a699b410SJean-Christophe Dubois
10508095508aSJean-Christophe Dubois trace_imx_enet_receive(size);
1051a699b410SJean-Christophe Dubois
1052a699b410SJean-Christophe Dubois if (!s->regs[ENET_RDAR]) {
1053a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n",
1054a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__);
1055a699b410SJean-Christophe Dubois return 0;
1056a699b410SJean-Christophe Dubois }
1057a699b410SJean-Christophe Dubois
1058a699b410SJean-Christophe Dubois crc = cpu_to_be32(crc32(~0, buf, size));
105993c9678dSStephen Longfield /* Increase size by 4, loop below reads the last 4 bytes from crc_ptr. */
106093c9678dSStephen Longfield size += 4;
1061a699b410SJean-Christophe Dubois crc_ptr = (uint8_t *) &crc;
1062a699b410SJean-Christophe Dubois
1063ebdd8cddSAndrey Smirnov if (shift16) {
1064ebdd8cddSAndrey Smirnov size += 2;
1065ebdd8cddSAndrey Smirnov }
1066ebdd8cddSAndrey Smirnov
1067894d74ccSAndrey Smirnov /* Huge frames are truncated. */
1068ff9a7feeSAndrey Smirnov if (size > s->regs[ENET_FTRL]) {
1069ff9a7feeSAndrey Smirnov size = s->regs[ENET_FTRL];
1070a699b410SJean-Christophe Dubois flags |= ENET_BD_TR | ENET_BD_LG;
1071a699b410SJean-Christophe Dubois }
1072a699b410SJean-Christophe Dubois
1073a699b410SJean-Christophe Dubois /* Frames larger than the user limit just set error flags. */
1074a699b410SJean-Christophe Dubois if (size > (s->regs[ENET_RCR] >> 16)) {
1075a699b410SJean-Christophe Dubois flags |= ENET_BD_LG;
1076a699b410SJean-Christophe Dubois }
1077a699b410SJean-Christophe Dubois
1078a699b410SJean-Christophe Dubois addr = s->rx_descriptor;
1079a699b410SJean-Christophe Dubois while (size > 0) {
1080a699b410SJean-Christophe Dubois imx_enet_read_bd(&bd, addr);
1081a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_E) == 0) {
1082a699b410SJean-Christophe Dubois /* No descriptors available. Bail out. */
1083a699b410SJean-Christophe Dubois /*
1084a699b410SJean-Christophe Dubois * FIXME: This is wrong. We should probably either
1085a699b410SJean-Christophe Dubois * save the remainder for when more RX buffers are
1086a699b410SJean-Christophe Dubois * available, or flag an error.
1087a699b410SJean-Christophe Dubois */
1088a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n",
1089a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__);
1090a699b410SJean-Christophe Dubois break;
1091a699b410SJean-Christophe Dubois }
10924c5e7a6cSAndrey Smirnov buf_len = MIN(size, s->regs[ENET_MRBR]);
1093a699b410SJean-Christophe Dubois bd.length = buf_len;
1094a699b410SJean-Christophe Dubois size -= buf_len;
1095a699b410SJean-Christophe Dubois
10968095508aSJean-Christophe Dubois trace_imx_enet_receive_len(addr, bd.length);
1097a699b410SJean-Christophe Dubois
1098a699b410SJean-Christophe Dubois /* The last 4 bytes are the CRC. */
1099a699b410SJean-Christophe Dubois if (size < 4) {
1100a699b410SJean-Christophe Dubois buf_len += size - 4;
1101a699b410SJean-Christophe Dubois }
1102a699b410SJean-Christophe Dubois buf_addr = bd.data;
1103ebdd8cddSAndrey Smirnov
1104ebdd8cddSAndrey Smirnov if (shift16) {
1105ebdd8cddSAndrey Smirnov /*
1106ebdd8cddSAndrey Smirnov * If SHIFT16 bit of ENETx_RACC register is set we need to
1107ebdd8cddSAndrey Smirnov * align the payload to 4-byte boundary.
1108ebdd8cddSAndrey Smirnov */
1109ebdd8cddSAndrey Smirnov const uint8_t zeros[2] = { 0 };
1110ebdd8cddSAndrey Smirnov
1111ba06fe8aSPhilippe Mathieu-Daudé dma_memory_write(&address_space_memory, buf_addr, zeros,
1112ba06fe8aSPhilippe Mathieu-Daudé sizeof(zeros), MEMTXATTRS_UNSPECIFIED);
1113ebdd8cddSAndrey Smirnov
1114ebdd8cddSAndrey Smirnov buf_addr += sizeof(zeros);
1115ebdd8cddSAndrey Smirnov buf_len -= sizeof(zeros);
1116ebdd8cddSAndrey Smirnov
1117ebdd8cddSAndrey Smirnov /* We only do this once per Ethernet frame */
1118ebdd8cddSAndrey Smirnov shift16 = false;
1119ebdd8cddSAndrey Smirnov }
1120ebdd8cddSAndrey Smirnov
1121ba06fe8aSPhilippe Mathieu-Daudé dma_memory_write(&address_space_memory, buf_addr, buf, buf_len,
1122ba06fe8aSPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED);
1123a699b410SJean-Christophe Dubois buf += buf_len;
1124a699b410SJean-Christophe Dubois if (size < 4) {
1125a699b410SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr + buf_len,
1126ba06fe8aSPhilippe Mathieu-Daudé crc_ptr, 4 - size, MEMTXATTRS_UNSPECIFIED);
1127a699b410SJean-Christophe Dubois crc_ptr += 4 - size;
1128a699b410SJean-Christophe Dubois }
1129a699b410SJean-Christophe Dubois bd.flags &= ~ENET_BD_E;
1130a699b410SJean-Christophe Dubois if (size == 0) {
1131a699b410SJean-Christophe Dubois /* Last buffer in frame. */
1132a699b410SJean-Christophe Dubois bd.flags |= flags | ENET_BD_L;
11338095508aSJean-Christophe Dubois
11348095508aSJean-Christophe Dubois trace_imx_enet_receive_last(bd.flags);
11358095508aSJean-Christophe Dubois
113688e1b59eSAaron Hill /* Indicate that we've updated the last buffer descriptor. */
113788e1b59eSAaron Hill bd.last_buffer = ENET_BD_BDU;
1138a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_RX_INT) {
1139a699b410SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXF;
1140a699b410SJean-Christophe Dubois }
1141a699b410SJean-Christophe Dubois } else {
1142a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_RX_INT) {
1143a699b410SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXB;
1144a699b410SJean-Christophe Dubois }
1145a699b410SJean-Christophe Dubois }
1146a699b410SJean-Christophe Dubois imx_enet_write_bd(&bd, addr);
1147a699b410SJean-Christophe Dubois /* Advance to the next descriptor. */
1148a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) {
1149a699b410SJean-Christophe Dubois addr = s->regs[ENET_RDSR];
1150a699b410SJean-Christophe Dubois } else {
1151a699b410SJean-Christophe Dubois addr += sizeof(bd);
1152a699b410SJean-Christophe Dubois }
1153a699b410SJean-Christophe Dubois }
1154a699b410SJean-Christophe Dubois s->rx_descriptor = addr;
1155b2b012afSAndrey Smirnov imx_eth_enable_rx(s, false);
1156a699b410SJean-Christophe Dubois imx_eth_update(s);
1157a699b410SJean-Christophe Dubois return len;
1158a699b410SJean-Christophe Dubois }
1159a699b410SJean-Christophe Dubois
imx_eth_receive(NetClientState * nc,const uint8_t * buf,size_t len)1160a699b410SJean-Christophe Dubois static ssize_t imx_eth_receive(NetClientState *nc, const uint8_t *buf,
1161a699b410SJean-Christophe Dubois size_t len)
1162a699b410SJean-Christophe Dubois {
1163a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
1164a699b410SJean-Christophe Dubois
1165a699b410SJean-Christophe Dubois if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) {
1166a699b410SJean-Christophe Dubois return imx_enet_receive(nc, buf, len);
1167a699b410SJean-Christophe Dubois } else {
1168a699b410SJean-Christophe Dubois return imx_fec_receive(nc, buf, len);
1169a699b410SJean-Christophe Dubois }
1170a699b410SJean-Christophe Dubois }
1171a699b410SJean-Christophe Dubois
1172a699b410SJean-Christophe Dubois static const MemoryRegionOps imx_eth_ops = {
1173a699b410SJean-Christophe Dubois .read = imx_eth_read,
1174a699b410SJean-Christophe Dubois .write = imx_eth_write,
1175fcbd8018SJean-Christophe Dubois .valid.min_access_size = 4,
1176fcbd8018SJean-Christophe Dubois .valid.max_access_size = 4,
1177fcbd8018SJean-Christophe Dubois .endianness = DEVICE_NATIVE_ENDIAN,
1178fcbd8018SJean-Christophe Dubois };
1179fcbd8018SJean-Christophe Dubois
imx_eth_cleanup(NetClientState * nc)1180a699b410SJean-Christophe Dubois static void imx_eth_cleanup(NetClientState *nc)
1181fcbd8018SJean-Christophe Dubois {
1182fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
1183fcbd8018SJean-Christophe Dubois
1184fcbd8018SJean-Christophe Dubois s->nic = NULL;
1185fcbd8018SJean-Christophe Dubois }
1186fcbd8018SJean-Christophe Dubois
1187a699b410SJean-Christophe Dubois static NetClientInfo imx_eth_net_info = {
1188f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC,
1189fcbd8018SJean-Christophe Dubois .size = sizeof(NICState),
1190a699b410SJean-Christophe Dubois .can_receive = imx_eth_can_receive,
1191a699b410SJean-Christophe Dubois .receive = imx_eth_receive,
1192a699b410SJean-Christophe Dubois .cleanup = imx_eth_cleanup,
1193a699b410SJean-Christophe Dubois .link_status_changed = imx_eth_set_link,
1194fcbd8018SJean-Christophe Dubois };
1195fcbd8018SJean-Christophe Dubois
1196fcbd8018SJean-Christophe Dubois
imx_eth_realize(DeviceState * dev,Error ** errp)1197a699b410SJean-Christophe Dubois static void imx_eth_realize(DeviceState *dev, Error **errp)
1198fcbd8018SJean-Christophe Dubois {
1199fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(dev);
1200fcbd8018SJean-Christophe Dubois SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
1201fcbd8018SJean-Christophe Dubois
1202a699b410SJean-Christophe Dubois memory_region_init_io(&s->iomem, OBJECT(dev), &imx_eth_ops, s,
1203831858adSAndrey Smirnov TYPE_IMX_FEC, FSL_IMX25_FEC_SIZE);
1204fcbd8018SJean-Christophe Dubois sysbus_init_mmio(sbd, &s->iomem);
1205a699b410SJean-Christophe Dubois sysbus_init_irq(sbd, &s->irq[0]);
1206a699b410SJean-Christophe Dubois sysbus_init_irq(sbd, &s->irq[1]);
1207a699b410SJean-Christophe Dubois
1208c01194e1SBernhard Beschow qemu_init_irq(&s->mii_irq, imx_phy_update_irq, s, 0);
1209c01194e1SBernhard Beschow object_initialize_child(OBJECT(s), "mii", &s->mii, TYPE_LAN9118_PHY);
1210c01194e1SBernhard Beschow if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->mii), errp)) {
1211c01194e1SBernhard Beschow return;
1212c01194e1SBernhard Beschow }
1213c01194e1SBernhard Beschow qdev_connect_gpio_out(DEVICE(&s->mii), 0, &s->mii_irq);
1214c01194e1SBernhard Beschow
1215fcbd8018SJean-Christophe Dubois qemu_macaddr_default_if_unset(&s->conf.macaddr);
1216fcbd8018SJean-Christophe Dubois
1217a699b410SJean-Christophe Dubois s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf,
1218a699b410SJean-Christophe Dubois object_get_typename(OBJECT(dev)),
12197d0fefdfSAkihiko Odaki dev->id, &dev->mem_reentrancy_guard, s);
1220a699b410SJean-Christophe Dubois
1221fcbd8018SJean-Christophe Dubois qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
1222fcbd8018SJean-Christophe Dubois }
1223fcbd8018SJean-Christophe Dubois
1224e732f00fSRichard Henderson static const Property imx_eth_properties[] = {
1225fcbd8018SJean-Christophe Dubois DEFINE_NIC_PROPERTIES(IMXFECState, conf),
1226f93f961cSAndrey Smirnov DEFINE_PROP_UINT32("tx-ring-num", IMXFECState, tx_ring_num, 1),
1227461c51adSJean-Christophe Dubois DEFINE_PROP_UINT32("phy-num", IMXFECState, phy_num, 0),
1228df3f5efeSGuenter Roeck DEFINE_PROP_BOOL("phy-connected", IMXFECState, phy_connected, true),
1229df3f5efeSGuenter Roeck DEFINE_PROP_LINK("phy-consumer", IMXFECState, phy_consumer, TYPE_IMX_FEC,
1230df3f5efeSGuenter Roeck IMXFECState *),
1231fcbd8018SJean-Christophe Dubois };
1232fcbd8018SJean-Christophe Dubois
imx_eth_class_init(ObjectClass * klass,const void * data)1233*12d1a768SPhilippe Mathieu-Daudé static void imx_eth_class_init(ObjectClass *klass, const void *data)
1234fcbd8018SJean-Christophe Dubois {
1235fcbd8018SJean-Christophe Dubois DeviceClass *dc = DEVICE_CLASS(klass);
1236fcbd8018SJean-Christophe Dubois
1237a699b410SJean-Christophe Dubois dc->vmsd = &vmstate_imx_eth;
1238e3d08143SPeter Maydell device_class_set_legacy_reset(dc, imx_eth_reset);
12394f67d30bSMarc-André Lureau device_class_set_props(dc, imx_eth_properties);
1240a699b410SJean-Christophe Dubois dc->realize = imx_eth_realize;
1241a699b410SJean-Christophe Dubois dc->desc = "i.MX FEC/ENET Ethernet Controller";
1242a699b410SJean-Christophe Dubois }
1243a699b410SJean-Christophe Dubois
imx_fec_init(Object * obj)1244a699b410SJean-Christophe Dubois static void imx_fec_init(Object *obj)
1245a699b410SJean-Christophe Dubois {
1246a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(obj);
1247a699b410SJean-Christophe Dubois
1248a699b410SJean-Christophe Dubois s->is_fec = true;
1249a699b410SJean-Christophe Dubois }
1250a699b410SJean-Christophe Dubois
imx_enet_init(Object * obj)1251a699b410SJean-Christophe Dubois static void imx_enet_init(Object *obj)
1252a699b410SJean-Christophe Dubois {
1253a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(obj);
1254a699b410SJean-Christophe Dubois
1255a699b410SJean-Christophe Dubois s->is_fec = false;
1256fcbd8018SJean-Christophe Dubois }
1257fcbd8018SJean-Christophe Dubois
1258fcbd8018SJean-Christophe Dubois static const TypeInfo imx_fec_info = {
1259fcbd8018SJean-Christophe Dubois .name = TYPE_IMX_FEC,
1260fcbd8018SJean-Christophe Dubois .parent = TYPE_SYS_BUS_DEVICE,
1261fcbd8018SJean-Christophe Dubois .instance_size = sizeof(IMXFECState),
1262a699b410SJean-Christophe Dubois .instance_init = imx_fec_init,
1263a699b410SJean-Christophe Dubois .class_init = imx_eth_class_init,
1264fcbd8018SJean-Christophe Dubois };
1265fcbd8018SJean-Christophe Dubois
1266a699b410SJean-Christophe Dubois static const TypeInfo imx_enet_info = {
1267a699b410SJean-Christophe Dubois .name = TYPE_IMX_ENET,
1268a699b410SJean-Christophe Dubois .parent = TYPE_IMX_FEC,
1269a699b410SJean-Christophe Dubois .instance_init = imx_enet_init,
1270a699b410SJean-Christophe Dubois };
1271a699b410SJean-Christophe Dubois
imx_eth_register_types(void)1272a699b410SJean-Christophe Dubois static void imx_eth_register_types(void)
1273fcbd8018SJean-Christophe Dubois {
1274fcbd8018SJean-Christophe Dubois type_register_static(&imx_fec_info);
1275a699b410SJean-Christophe Dubois type_register_static(&imx_enet_info);
1276fcbd8018SJean-Christophe Dubois }
1277fcbd8018SJean-Christophe Dubois
1278a699b410SJean-Christophe Dubois type_init(imx_eth_register_types)
1279