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