1 /* $OpenBSD: sxie.c,v 1.34 2023/11/10 15:51:19 bluhm Exp $ */
2 /*
3 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
4 * Copyright (c) 2013 Artturi Alm
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /* TODO this should use dedicated dma for RX, at least */
20
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/sockio.h>
24 #include <sys/queue.h>
25 #include <sys/device.h>
26 #include <sys/mbuf.h>
27 #include <machine/intr.h>
28 #include <machine/bus.h>
29 #include <machine/fdt.h>
30
31 #include "bpfilter.h"
32
33 #include <net/if.h>
34 #include <net/if_media.h>
35 #if NBPFILTER > 0
36 #include <net/bpf.h>
37 #endif
38
39 #include <netinet/in.h>
40 #include <netinet/if_ether.h>
41
42 #include <dev/mii/miivar.h>
43
44 #include <dev/fdt/sunxireg.h>
45
46 #include <dev/ofw/openfirm.h>
47 #include <dev/ofw/ofw_clock.h>
48 #include <dev/ofw/ofw_pinctrl.h>
49 #include <dev/ofw/ofw_regulator.h>
50 #include <dev/ofw/fdt.h>
51
52 /* configuration registers */
53 #define SXIE_CR 0x0000
54 #define SXIE_TXMODE 0x0004
55 #define SXIE_TXFLOW 0x0008
56 #define SXIE_TXCR0 0x000c
57 #define SXIE_TXCR1 0x0010
58 #define SXIE_TXINS 0x0014
59 #define SXIE_TXPKTLEN0 0x0018
60 #define SXIE_TXPKTLEN1 0x001c
61 #define SXIE_TXSR 0x0020
62 #define SXIE_TXIO0 0x0024
63 #define SXIE_TXIO1 0x0028
64 #define SXIE_TXTSVL0 0x002c
65 #define SXIE_TXTSVH0 0x0030
66 #define SXIE_TXTSVL1 0x0034
67 #define SXIE_TXTSVH1 0x0038
68 #define SXIE_RXCR 0x003c
69 #define SXIE_RXHASH0 0x0040
70 #define SXIE_RXHASH1 0x0044
71 #define SXIE_RXSR 0x0048
72 #define SXIE_RXIO 0x004C
73 #define SXIE_RXFBC 0x0050
74 #define SXIE_INTCR 0x0054
75 #define SXIE_INTSR 0x0058
76 #define SXIE_MACCR0 0x005C
77 #define SXIE_MACCR1 0x0060
78 #define SXIE_MACIPGT 0x0064
79 #define SXIE_MACIPGR 0x0068
80 #define SXIE_MACCLRT 0x006C
81 #define SXIE_MACMFL 0x0070
82 #define SXIE_MACSUPP 0x0074
83 #define SXIE_MACTEST 0x0078
84 #define SXIE_MACMCFG 0x007C
85 #define SXIE_MACMCMD 0x0080
86 #define SXIE_MACMADR 0x0084
87 #define SXIE_MACMWTD 0x0088
88 #define SXIE_MACMRDD 0x008C
89 #define SXIE_MACMIND 0x0090
90 #define SXIE_MACSSRR 0x0094
91 #define SXIE_MACA0 0x0098
92 #define SXIE_MACA1 0x009c
93 #define SXIE_MACA2 0x00a0
94
95 /* i once spent hours on pretty defines, cvs up ate 'em. these shall do */
96 #define SXIE_INTR_ENABLE 0x010f
97 #define SXIE_INTR_DISABLE 0x0000
98 #define SXIE_INTR_CLEAR 0x0000
99
100 #define SXIE_TX_FIFO0 0x0001
101 #define SXIE_TX_FIFO1 0x0002
102
103 #define SXIE_RX_ENABLE 0x0004
104 #define SXIE_TX_ENABLE 0x0003
105 #define SXIE_RXTX_ENABLE 0x0007
106
107 #define SXIE_RXDRQM 0x0002
108 #define SXIE_RXTM 0x0004
109 #define SXIE_RXFLUSH 0x0008
110 #define SXIE_RXPA 0x0010
111 #define SXIE_RXPCF 0x0020
112 #define SXIE_RXPCRCE 0x0040
113 #define SXIE_RXPLE 0x0080
114 #define SXIE_RXPOR 0x0100
115 #define SXIE_RXUCAD 0x10000
116 #define SXIE_RXDAF 0x20000
117 #define SXIE_RXMCO 0x100000
118 #define SXIE_RXMHF 0x200000
119 #define SXIE_RXBCO 0x400000
120 #define SXIE_RXSAF 0x1000000
121 #define SXIE_RXSAIF 0x2000000
122
123 #define SXIE_MACRXFC 0x0004
124 #define SXIE_MACTXFC 0x0008
125 #define SXIE_MACSOFTRESET 0x8000
126
127 #define SXIE_MACDUPLEX 0x0001 /* full = 1 */
128 #define SXIE_MACFLC 0x0002
129 #define SXIE_MACHF 0x0004
130 #define SXIE_MACDCRC 0x0008
131 #define SXIE_MACCRC 0x0010
132 #define SXIE_MACPC 0x0020
133 #define SXIE_MACVC 0x0040
134 #define SXIE_MACADP 0x0080
135 #define SXIE_MACPRE 0x0100
136 #define SXIE_MACLPE 0x0200
137 #define SXIE_MACNB 0x1000
138 #define SXIE_MACBNB 0x2000
139 #define SXIE_MACED 0x4000
140
141 #define SXIE_RX_ERRLENOOR 0x0040
142 #define SXIE_RX_ERRLENCHK 0x0020
143 #define SXIE_RX_ERRCRC 0x0010
144 #define SXIE_RX_ERRRCV 0x0008 /* XXX receive code violation ? */
145 #define SXIE_RX_ERRMASK 0x0070
146
147 #define SXIE_MII_TIMEOUT 100
148 #define SXIE_MAX_RXD 8
149 #define SXIE_MAX_PKT_SIZE ETHER_MAX_DIX_LEN
150
151 #define SXIE_ROUNDUP(size, unit) (((size) + (unit) - 1) & ~((unit) - 1))
152
153 struct sxie_softc {
154 struct device sc_dev;
155 struct arpcom sc_ac;
156 struct mii_data sc_mii;
157 int sc_phyno;
158 bus_space_tag_t sc_iot;
159 bus_space_handle_t sc_ioh;
160 bus_space_handle_t sc_sid_ioh;
161 void *sc_ih; /* Interrupt handler */
162 uint32_t intr_status; /* soft interrupt status */
163 uint32_t pauseframe;
164 uint32_t txf_inuse;
165 };
166
167 struct sxie_softc *sxie_sc;
168
169 int sxie_match(struct device *, void *, void *);
170 void sxie_attach(struct device *, struct device *, void *);
171 void sxie_setup_interface(struct sxie_softc *, struct device *);
172 void sxie_socware_init(struct sxie_softc *);
173 int sxie_ioctl(struct ifnet *, u_long, caddr_t);
174 void sxie_start(struct ifnet *);
175 void sxie_watchdog(struct ifnet *);
176 void sxie_init(struct sxie_softc *);
177 void sxie_stop(struct sxie_softc *);
178 void sxie_reset(struct sxie_softc *);
179 void sxie_iff(struct sxie_softc *, struct ifnet *);
180 int sxie_intr(void *);
181 void sxie_recv(struct sxie_softc *);
182 int sxie_miibus_readreg(struct device *, int, int);
183 void sxie_miibus_writereg(struct device *, int, int, int);
184 void sxie_miibus_statchg(struct device *);
185 int sxie_ifm_change(struct ifnet *);
186 void sxie_ifm_status(struct ifnet *, struct ifmediareq *);
187
188 const struct cfattach sxie_ca = {
189 sizeof (struct sxie_softc), sxie_match, sxie_attach
190 };
191
192 struct cfdriver sxie_cd = {
193 NULL, "sxie", DV_IFNET
194 };
195
196 int
sxie_match(struct device * parent,void * match,void * aux)197 sxie_match(struct device *parent, void *match, void *aux)
198 {
199 struct fdt_attach_args *faa = aux;
200
201 return OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-emac");
202 }
203
204 void
sxie_attach(struct device * parent,struct device * self,void * aux)205 sxie_attach(struct device *parent, struct device *self, void *aux)
206 {
207 struct sxie_softc *sc = (struct sxie_softc *) self;
208 struct fdt_attach_args *faa = aux;
209 struct mii_data *mii;
210 struct ifnet *ifp;
211 int phy, node, phy_supply, phyloc = MII_PHY_ANY;
212 int s;
213
214 if (faa->fa_nreg < 1)
215 return;
216
217 pinctrl_byname(faa->fa_node, "default");
218
219 sc->sc_iot = faa->fa_iot;
220
221 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
222 faa->fa_reg[0].size, 0, &sc->sc_ioh))
223 panic("sxie_attach: bus_space_map ioh failed!");
224
225 if (bus_space_map(sc->sc_iot, SID_ADDR, SID_SIZE, 0, &sc->sc_sid_ioh))
226 panic("sxie_attach: bus_space_map sid_ioh failed!");
227
228 clock_enable_all(faa->fa_node);
229
230 /* Lookup PHY. */
231 phy = OF_getpropint(faa->fa_node, "phy", 0);
232 if (phy == 0)
233 phy = OF_getpropint(faa->fa_node, "phy-handle", 0);
234 node = OF_getnodebyphandle(phy);
235 if (node) {
236 phyloc = OF_getpropint(node, "reg", MII_PHY_ANY);
237
238 /* Power up PHY. */
239 phy_supply = OF_getpropint(OF_parent(node), "phy-supply", 0);
240 if (phy_supply)
241 regulator_enable(phy_supply);
242 }
243 sc->sc_phyno = phyloc == MII_PHY_ANY ? 1 : phyloc;
244
245 sxie_socware_init(sc);
246 sc->txf_inuse = 0;
247
248 sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_NET,
249 sxie_intr, sc, sc->sc_dev.dv_xname);
250
251 s = splnet();
252
253 printf(", address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr));
254
255 /* XXX verify flags & capabilities */
256 ifp = &sc->sc_ac.ac_if;
257 ifp->if_softc = sc;
258 strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
259 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
260 ifp->if_ioctl = sxie_ioctl;
261 ifp->if_start = sxie_start;
262 ifp->if_watchdog = sxie_watchdog;
263 ifp->if_capabilities = IFCAP_VLAN_MTU; /* XXX status check in recv? */
264
265 ifq_init_maxlen(&ifp->if_snd, IFQ_MAXLEN);
266
267 /* Initialize MII/media info. */
268 mii = &sc->sc_mii;
269 mii->mii_ifp = ifp;
270 mii->mii_readreg = sxie_miibus_readreg;
271 mii->mii_writereg = sxie_miibus_writereg;
272 mii->mii_statchg = sxie_miibus_statchg;
273
274 ifmedia_init(&mii->mii_media, 0, sxie_ifm_change, sxie_ifm_status);
275 mii_attach(self, mii, 0xffffffff, phyloc, MII_OFFSET_ANY, 0);
276
277 if (LIST_FIRST(&mii->mii_phys) == NULL) {
278 ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
279 ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
280 } else
281 ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
282
283 if_attach(ifp);
284 ether_ifattach(ifp);
285 splx(s);
286
287 sxie_sc = sc;
288 }
289
290 void
sxie_socware_init(struct sxie_softc * sc)291 sxie_socware_init(struct sxie_softc *sc)
292 {
293 int have_mac = 0;
294 uint32_t reg;
295
296 /* MII clock cfg */
297 SXICMS4(sc, SXIE_MACMCFG, 15 << 2, 13 << 2);
298
299 SXIWRITE4(sc, SXIE_INTCR, SXIE_INTR_DISABLE);
300 SXISET4(sc, SXIE_INTSR, SXIE_INTR_CLEAR);
301
302 /*
303 * If u-boot doesn't set emac, use the Security ID area
304 * to generate a consistent MAC address of.
305 */
306 reg = SXIREAD4(sc, SXIE_MACA0);
307 if (reg != 0) {
308 sc->sc_ac.ac_enaddr[3] = reg >> 16 & 0xff;
309 sc->sc_ac.ac_enaddr[4] = reg >> 8 & 0xff;
310 sc->sc_ac.ac_enaddr[5] = reg & 0xff;
311 reg = SXIREAD4(sc, SXIE_MACA1);
312 sc->sc_ac.ac_enaddr[0] = reg >> 16 & 0xff;
313 sc->sc_ac.ac_enaddr[1] = reg >> 8 & 0xff;
314 sc->sc_ac.ac_enaddr[2] = reg & 0xff;
315
316 have_mac = 1;
317 }
318
319 reg = bus_space_read_4(sc->sc_iot, sc->sc_sid_ioh, 0x0);
320
321 if (!have_mac && reg != 0) {
322 sc->sc_ac.ac_enaddr[0] = 0x02;
323 sc->sc_ac.ac_enaddr[1] = reg & 0xff;
324 reg = bus_space_read_4(sc->sc_iot, sc->sc_sid_ioh, 0x0c);
325 sc->sc_ac.ac_enaddr[2] = reg >> 24 & 0xff;
326 sc->sc_ac.ac_enaddr[3] = reg >> 16 & 0xff;
327 sc->sc_ac.ac_enaddr[4] = reg >> 8 & 0xff;
328 sc->sc_ac.ac_enaddr[5] = reg & 0xff;
329
330 have_mac = 1;
331 }
332
333 if (!have_mac)
334 ether_fakeaddr(&sc->sc_ac.ac_if);
335 }
336
337 void
sxie_setup_interface(struct sxie_softc * sc,struct device * dev)338 sxie_setup_interface(struct sxie_softc *sc, struct device *dev)
339 {
340 uint32_t clr_m, set_m;
341
342 /* configure TX */
343 SXICMS4(sc, SXIE_TXMODE, 3, 1); /* cpu mode */
344
345 /* configure RX */
346 clr_m = SXIE_RXDRQM | SXIE_RXTM | SXIE_RXPA | SXIE_RXPCF |
347 SXIE_RXPCRCE | SXIE_RXPLE | SXIE_RXMHF | SXIE_RXSAF |
348 SXIE_RXSAIF;
349 set_m = SXIE_RXPOR | SXIE_RXUCAD | SXIE_RXDAF | SXIE_RXBCO;
350 SXICMS4(sc, SXIE_RXCR, clr_m, set_m);
351
352 /* configure MAC */
353 SXISET4(sc, SXIE_MACCR0, SXIE_MACTXFC | SXIE_MACRXFC);
354 clr_m = SXIE_MACHF | SXIE_MACDCRC | SXIE_MACVC | SXIE_MACADP |
355 SXIE_MACPRE | SXIE_MACLPE | SXIE_MACNB | SXIE_MACBNB |
356 SXIE_MACED;
357 set_m = SXIE_MACFLC | SXIE_MACCRC | SXIE_MACPC;
358 set_m |= sxie_miibus_readreg(dev, sc->sc_phyno, 0) >> 8 & 1;
359 SXICMS4(sc, SXIE_MACCR1, clr_m, set_m);
360
361 /* XXX */
362 SXIWRITE4(sc, SXIE_MACIPGT, 0x0015);
363 SXIWRITE4(sc, SXIE_MACIPGR, 0x0c12);
364
365 /* XXX set collision window */
366 SXIWRITE4(sc, SXIE_MACCLRT, 0x370f);
367
368 /* set max frame length */
369 SXIWRITE4(sc, SXIE_MACMFL, SXIE_MAX_PKT_SIZE);
370
371 /* set lladdr */
372 SXIWRITE4(sc, SXIE_MACA0,
373 sc->sc_ac.ac_enaddr[3] << 16 |
374 sc->sc_ac.ac_enaddr[4] << 8 |
375 sc->sc_ac.ac_enaddr[5]);
376 SXIWRITE4(sc, SXIE_MACA1,
377 sc->sc_ac.ac_enaddr[0] << 16 |
378 sc->sc_ac.ac_enaddr[1] << 8 |
379 sc->sc_ac.ac_enaddr[2]);
380
381 sxie_reset(sc);
382 /* XXX possibly missing delay in here. */
383 }
384
385 void
sxie_init(struct sxie_softc * sc)386 sxie_init(struct sxie_softc *sc)
387 {
388 struct ifnet *ifp = &sc->sc_ac.ac_if;
389 struct device *dev = (struct device *)sc;
390 int phyreg;
391
392 sxie_reset(sc);
393
394 SXIWRITE4(sc, SXIE_INTCR, SXIE_INTR_DISABLE);
395
396 SXISET4(sc, SXIE_INTSR, SXIE_INTR_CLEAR);
397
398 SXISET4(sc, SXIE_RXCR, SXIE_RXFLUSH);
399
400 /* soft reset */
401 SXICLR4(sc, SXIE_MACCR0, SXIE_MACSOFTRESET);
402
403 /* zero rx counter */
404 SXIWRITE4(sc, SXIE_RXFBC, 0);
405
406 sxie_setup_interface(sc, dev);
407
408 /* power up PHY */
409 sxie_miibus_writereg(dev, sc->sc_phyno, 0,
410 sxie_miibus_readreg(dev, sc->sc_phyno, 0) & ~(1 << 11));
411 delay(1000);
412 phyreg = sxie_miibus_readreg(dev, sc->sc_phyno, 0);
413
414 /* set duplex */
415 SXICMS4(sc, SXIE_MACCR1, 1, phyreg >> 8 & 1);
416
417 /* set speed */
418 SXICMS4(sc, SXIE_MACSUPP, 1 << 8, (phyreg >> 13 & 1) << 8);
419
420 SXISET4(sc, SXIE_CR, SXIE_RXTX_ENABLE);
421
422 /* Indicate we are up and running. */
423 ifp->if_flags |= IFF_RUNNING;
424 ifq_clr_oactive(&ifp->if_snd);
425
426 SXISET4(sc, SXIE_INTCR, SXIE_INTR_ENABLE);
427
428 sxie_start(ifp);
429 }
430
431 int
sxie_intr(void * arg)432 sxie_intr(void *arg)
433 {
434 struct sxie_softc *sc = arg;
435 struct ifnet *ifp = &sc->sc_ac.ac_if;
436 uint32_t pending;
437
438 SXIWRITE4(sc, SXIE_INTCR, SXIE_INTR_DISABLE);
439
440 pending = SXIREAD4(sc, SXIE_INTSR);
441 SXIWRITE4(sc, SXIE_INTSR, pending);
442
443 /*
444 * Handle incoming packets.
445 */
446 if (pending & 0x0100) {
447 if (ifp->if_flags & IFF_RUNNING)
448 sxie_recv(sc);
449 }
450
451 if (pending & (SXIE_TX_FIFO0 | SXIE_TX_FIFO1)) {
452 sc->txf_inuse &= ~pending;
453 if (sc->txf_inuse == 0)
454 ifp->if_timer = 0;
455 else
456 ifp->if_timer = 5;
457
458 if (ifq_is_oactive(&ifp->if_snd))
459 ifq_restart(&ifp->if_snd);
460 }
461
462 SXISET4(sc, SXIE_INTCR, SXIE_INTR_ENABLE);
463
464 return 1;
465 }
466
467 /*
468 * XXX there's secondary tx fifo to be used.
469 */
470 void
sxie_start(struct ifnet * ifp)471 sxie_start(struct ifnet *ifp)
472 {
473 struct sxie_softc *sc = ifp->if_softc;
474 struct mbuf *m;
475 struct mbuf *head;
476 uint8_t *td;
477 uint32_t fifo;
478 uint32_t txbuf[SXIE_MAX_PKT_SIZE / sizeof(uint32_t)]; /* XXX !!! */
479
480 if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd))
481 return;
482
483
484 td = (uint8_t *)&txbuf[0];
485 m = NULL;
486 head = NULL;
487
488 for (;;) {
489 if (sc->txf_inuse == (SXIE_TX_FIFO0 | SXIE_TX_FIFO1)) {
490 ifq_set_oactive(&ifp->if_snd);
491 break;
492 }
493
494 m = ifq_dequeue(&ifp->if_snd);
495 if (m == NULL)
496 break;
497
498 if (m->m_pkthdr.len > SXIE_MAX_PKT_SIZE) {
499 m_freem(m);
500 continue;
501 }
502
503 #if NBPFILTER > 0
504 if (ifp->if_bpf)
505 bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
506 #endif
507
508 /* select fifo */
509 if (sc->txf_inuse & SXIE_TX_FIFO0) {
510 sc->txf_inuse |= SXIE_TX_FIFO1;
511 fifo = 1;
512 } else {
513 sc->txf_inuse |= SXIE_TX_FIFO0;
514 fifo = 0;
515 }
516 SXIWRITE4(sc, SXIE_TXINS, fifo);
517
518 /* set packet length */
519 SXIWRITE4(sc, SXIE_TXPKTLEN0 + (fifo * 4), m->m_pkthdr.len);
520
521 /* copy the actual packet to fifo XXX through 'align buffer' */
522 m_copydata(m, 0, m->m_pkthdr.len, td);
523 bus_space_write_multi_4(sc->sc_iot, sc->sc_ioh,
524 SXIE_TXIO0,
525 (uint32_t *)td, SXIE_ROUNDUP(m->m_pkthdr.len, 4) >> 2);
526
527 /* transmit to PHY from fifo */
528 SXISET4(sc, SXIE_TXCR0 + (fifo * 4), 1);
529 ifp->if_timer = 5;
530
531 m_freem(m);
532 }
533 }
534
535 void
sxie_stop(struct sxie_softc * sc)536 sxie_stop(struct sxie_softc *sc)
537 {
538 struct ifnet *ifp = &sc->sc_ac.ac_if;
539
540 sxie_reset(sc);
541
542 ifp->if_flags &= ~IFF_RUNNING;
543 ifp->if_timer = 0;
544 ifq_clr_oactive(&ifp->if_snd);
545 }
546
547 void
sxie_reset(struct sxie_softc * sc)548 sxie_reset(struct sxie_softc *sc)
549 {
550 /* reset the controller */
551 SXIWRITE4(sc, SXIE_CR, 0);
552 delay(200);
553 SXIWRITE4(sc, SXIE_CR, 1);
554 delay(200);
555 }
556
557 void
sxie_watchdog(struct ifnet * ifp)558 sxie_watchdog(struct ifnet *ifp)
559 {
560 struct sxie_softc *sc = ifp->if_softc;
561 if (sc->pauseframe) {
562 ifp->if_timer = 5;
563 return;
564 }
565 printf("%s: watchdog tx timeout\n", sc->sc_dev.dv_xname);
566 ifp->if_oerrors++;
567 sxie_init(sc);
568 sxie_start(ifp);
569 }
570
571 /*
572 * XXX DMA?
573 */
574 void
sxie_recv(struct sxie_softc * sc)575 sxie_recv(struct sxie_softc *sc)
576 {
577 struct ifnet *ifp = &sc->sc_ac.ac_if;
578 uint32_t fbc, reg;
579 struct mbuf_list ml = MBUF_LIST_INITIALIZER();
580 struct mbuf *m;
581 uint16_t pktstat;
582 int16_t pktlen;
583 int rlen;
584 char rxbuf[SXIE_MAX_PKT_SIZE]; /* XXX !!! */
585 trynext:
586 fbc = SXIREAD4(sc, SXIE_RXFBC);
587 if (!fbc)
588 goto done;
589
590 /*
591 * first bit of MSB is packet valid flag,
592 * it is 'padded' with 0x43414d = "MAC"
593 */
594 reg = SXIREAD4(sc, SXIE_RXIO);
595 if (reg != 0x0143414d) { /* invalid packet */
596 /* disable, flush, enable */
597 SXICLR4(sc, SXIE_CR, SXIE_RX_ENABLE);
598 SXISET4(sc, SXIE_RXCR, SXIE_RXFLUSH);
599 while (SXIREAD4(sc, SXIE_RXCR) & SXIE_RXFLUSH);
600 SXISET4(sc, SXIE_CR, SXIE_RX_ENABLE);
601
602 goto err_out;
603 }
604
605 reg = SXIREAD4(sc, SXIE_RXIO);
606 pktstat = (uint16_t)reg >> 16;
607 pktlen = (int16_t)reg; /* length of useful data */
608
609 if (pktstat & SXIE_RX_ERRMASK || pktlen < ETHER_MIN_LEN) {
610 ifp->if_ierrors++;
611 goto trynext;
612 }
613 if (pktlen > SXIE_MAX_PKT_SIZE)
614 pktlen = SXIE_MAX_PKT_SIZE; /* XXX is truncating ok? */
615
616 /* read the actual packet from fifo XXX through 'align buffer'.. */
617 if (pktlen & 3)
618 rlen = SXIE_ROUNDUP(pktlen, 4);
619 else
620 rlen = pktlen;
621 bus_space_read_multi_4(sc->sc_iot, sc->sc_ioh,
622 SXIE_RXIO, (uint32_t *)&rxbuf[0], rlen >> 2);
623
624 m = m_devget(&rxbuf[0], pktlen, ETHER_ALIGN);
625 if (m == NULL) {
626 ifp->if_ierrors++;
627 goto err_out;
628 }
629
630 ml_enqueue(&ml, m);
631 goto trynext;
632 err_out:
633 ifp->if_ierrors++;
634 done:
635 if_input(ifp, &ml);
636 }
637
638 int
sxie_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)639 sxie_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
640 {
641 struct sxie_softc *sc = ifp->if_softc;
642 struct ifreq *ifr = (struct ifreq *)data;
643 int s, error = 0;
644
645 s = splnet();
646
647 switch (cmd) {
648 case SIOCSIFADDR:
649 if (!(ifp->if_flags & IFF_UP)) {
650 ifp->if_flags |= IFF_UP;
651 sxie_init(sc);
652 }
653 break;
654 case SIOCSIFFLAGS:
655 if (ifp->if_flags & IFF_UP) {
656 if (ifp->if_flags & IFF_RUNNING)
657 error = ENETRESET;
658 else
659 sxie_init(sc);
660 } else if (ifp->if_flags & IFF_RUNNING)
661 sxie_stop(sc);
662 break;
663 case SIOCGIFMEDIA:
664 case SIOCSIFMEDIA:
665 error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
666 break;
667 default:
668 error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
669 }
670 if (error == ENETRESET) {
671 if (ifp->if_flags & IFF_RUNNING)
672 sxie_iff(sc, ifp);
673 error = 0;
674 }
675
676 splx(s);
677 return error;
678 }
679
680 void
sxie_iff(struct sxie_softc * sc,struct ifnet * ifp)681 sxie_iff(struct sxie_softc *sc, struct ifnet *ifp)
682 {
683 /* XXX set interface features */
684 }
685
686 /*
687 * MII
688 */
689 int
sxie_miibus_readreg(struct device * dev,int phy,int reg)690 sxie_miibus_readreg(struct device *dev, int phy, int reg)
691 {
692 struct sxie_softc *sc = (struct sxie_softc *)dev;
693 int timo = SXIE_MII_TIMEOUT;
694
695 SXIWRITE4(sc, SXIE_MACMADR, phy << 8 | reg);
696
697 SXIWRITE4(sc, SXIE_MACMCMD, 1);
698 while (SXIREAD4(sc, SXIE_MACMIND) & 1 && --timo)
699 delay(10);
700 #ifdef DIAGNOSTIC
701 if (!timo)
702 printf("%s: sxie_miibus_readreg timeout.\n",
703 sc->sc_dev.dv_xname);
704 #endif
705
706 SXIWRITE4(sc, SXIE_MACMCMD, 0);
707
708 return SXIREAD4(sc, SXIE_MACMRDD) & 0xffff;
709 }
710
711 void
sxie_miibus_writereg(struct device * dev,int phy,int reg,int val)712 sxie_miibus_writereg(struct device *dev, int phy, int reg, int val)
713 {
714 struct sxie_softc *sc = (struct sxie_softc *)dev;
715 int timo = SXIE_MII_TIMEOUT;
716
717 SXIWRITE4(sc, SXIE_MACMADR, phy << 8 | reg);
718
719 SXIWRITE4(sc, SXIE_MACMCMD, 1);
720 while (SXIREAD4(sc, SXIE_MACMIND) & 1 && --timo)
721 delay(10);
722 #ifdef DIAGNOSTIC
723 if (!timo)
724 printf("%s: sxie_miibus_readreg timeout.\n",
725 sc->sc_dev.dv_xname);
726 #endif
727
728 SXIWRITE4(sc, SXIE_MACMCMD, 0);
729
730 SXIWRITE4(sc, SXIE_MACMWTD, val);
731 }
732
733 void
sxie_miibus_statchg(struct device * dev)734 sxie_miibus_statchg(struct device *dev)
735 {
736 /* XXX */
737 #if 0
738 struct sxie_softc *sc = (struct sxie_softc *)dev;
739
740 switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) {
741 case IFM_10_T:
742 case IFM_100_TX:
743 /*case IFM_1000_T: only on GMAC */
744 break;
745 default:
746 break;
747 }
748 #endif
749 }
750
751 int
sxie_ifm_change(struct ifnet * ifp)752 sxie_ifm_change(struct ifnet *ifp)
753 {
754 struct sxie_softc *sc = ifp->if_softc;
755 struct mii_data *mii = &sc->sc_mii;
756
757 if (mii->mii_instance) {
758 struct mii_softc *miisc;
759
760 LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
761 mii_phy_reset(miisc);
762 }
763 return mii_mediachg(mii);
764 }
765
766 void
sxie_ifm_status(struct ifnet * ifp,struct ifmediareq * ifmr)767 sxie_ifm_status(struct ifnet *ifp, struct ifmediareq *ifmr)
768 {
769 struct sxie_softc *sc = (struct sxie_softc *)ifp->if_softc;
770
771 mii_pollstat(&sc->sc_mii);
772 ifmr->ifm_active = sc->sc_mii.mii_media_active;
773 ifmr->ifm_status = sc->sc_mii.mii_media_status;
774 }
775