xref: /openbsd/sys/arch/armv7/sunxi/sxie.c (revision cf96265b)
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