xref: /freebsd/sys/dev/tsec/if_tsec_fdt.c (revision 675be9115aae86ad6b3d877155d4fd7822892105)
1 /*-
2  * Copyright (C) 2007-2008 Semihalf, Rafal Jaworowski
3  * Copyright (C) 2006-2007 Semihalf, Piotr Kruszynski
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
18  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
20  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
22  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * From: FreeBSD: head/sys/dev/tsec/if_tsec_ocp.c 188712 2009-02-17 14:59:47Z raj
27  */
28 
29 /*
30  * FDT 'simple-bus' attachment for Freescale TSEC controller.
31  */
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/endian.h>
38 #include <sys/mbuf.h>
39 #include <sys/kernel.h>
40 #include <sys/module.h>
41 #include <sys/socket.h>
42 #include <sys/sysctl.h>
43 
44 #include <sys/bus.h>
45 #include <machine/bus.h>
46 #include <sys/rman.h>
47 #include <machine/resource.h>
48 
49 #include <net/ethernet.h>
50 #include <net/if.h>
51 #include <net/if_dl.h>
52 #include <net/if_media.h>
53 #include <net/if_arp.h>
54 
55 #include <dev/fdt/fdt_common.h>
56 #include <dev/mii/mii.h>
57 #include <dev/mii/miivar.h>
58 #include <dev/ofw/ofw_bus.h>
59 #include <dev/ofw/ofw_bus_subr.h>
60 #include <dev/ofw/openfirm.h>
61 
62 #include <dev/tsec/if_tsec.h>
63 #include <dev/tsec/if_tsecreg.h>
64 
65 #include "miibus_if.h"
66 
67 #define	TSEC_RID_TXIRQ	0
68 #define	TSEC_RID_RXIRQ	1
69 #define	TSEC_RID_ERRIRQ	2
70 
71 extern struct tsec_softc *tsec0_sc;
72 
73 static int tsec_fdt_probe(device_t dev);
74 static int tsec_fdt_attach(device_t dev);
75 static int tsec_fdt_detach(device_t dev);
76 static int tsec_setup_intr(struct tsec_softc *sc, struct resource **ires,
77     void **ihand, int *irid, driver_intr_t handler, const char *iname);
78 static void tsec_release_intr(struct tsec_softc *sc, struct resource *ires,
79     void *ihand, int irid, const char *iname);
80 
81 static device_method_t tsec_methods[] = {
82 	/* Device interface */
83 	DEVMETHOD(device_probe,		tsec_fdt_probe),
84 	DEVMETHOD(device_attach,	tsec_fdt_attach),
85 	DEVMETHOD(device_detach,	tsec_fdt_detach),
86 
87 	DEVMETHOD(device_shutdown,	tsec_shutdown),
88 	DEVMETHOD(device_suspend,	tsec_suspend),
89 	DEVMETHOD(device_resume,	tsec_resume),
90 
91 	/* MII interface */
92 	DEVMETHOD(miibus_readreg,	tsec_miibus_readreg),
93 	DEVMETHOD(miibus_writereg,	tsec_miibus_writereg),
94 	DEVMETHOD(miibus_statchg,	tsec_miibus_statchg),
95 
96 	DEVMETHOD_END
97 };
98 
99 static driver_t tsec_fdt_driver = {
100 	"tsec",
101 	tsec_methods,
102 	sizeof(struct tsec_softc),
103 };
104 
105 DRIVER_MODULE(tsec, simplebus, tsec_fdt_driver, tsec_devclass, 0, 0);
106 MODULE_DEPEND(tsec, simplebus, 1, 1, 1);
107 MODULE_DEPEND(tsec, ether, 1, 1, 1);
108 
109 static int
110 tsec_fdt_probe(device_t dev)
111 {
112 	struct tsec_softc *sc;
113 	uint32_t id;
114 
115 	if (!ofw_bus_is_compatible(dev, "gianfar"))
116 		return (ENXIO);
117 
118 	sc = device_get_softc(dev);
119 
120 	sc->sc_rrid = 0;
121 	sc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rrid,
122 	    RF_ACTIVE);
123 	if (sc->sc_rres == NULL)
124 		return (ENXIO);
125 
126 	sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres);
127 	sc->sc_bas.bst = rman_get_bustag(sc->sc_rres);
128 
129 	/* Check if we are eTSEC (enhanced TSEC) */
130 	id = TSEC_READ(sc, TSEC_REG_ID);
131 	sc->is_etsec = ((id >> 16) == TSEC_ETSEC_ID) ? 1 : 0;
132 	id |= TSEC_READ(sc, TSEC_REG_ID2);
133 
134 	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rrid, sc->sc_rres);
135 
136 	if (id == 0) {
137 		device_printf(dev, "could not identify TSEC type\n");
138 		return (ENXIO);
139 	}
140 
141 	if (sc->is_etsec)
142 		device_set_desc(dev, "Enhanced Three-Speed Ethernet Controller");
143 	else
144 		device_set_desc(dev, "Three-Speed Ethernet Controller");
145 
146 	return (BUS_PROBE_DEFAULT);
147 }
148 
149 static int
150 tsec_fdt_attach(device_t dev)
151 {
152 	struct tsec_softc *sc;
153 	int error = 0;
154 
155 	sc = device_get_softc(dev);
156 	sc->dev = dev;
157 	sc->node = ofw_bus_get_node(dev);
158 
159 	/* XXX add comment on weird FSL's MII registers access design */
160 	if (device_get_unit(dev) == 0)
161 		tsec0_sc = sc;
162 
163 	/* Get phy address from fdt */
164 	if (fdt_get_phyaddr(sc->node, &sc->phyaddr) != 0)
165 		return (ENXIO);
166 
167 	/* Init timer */
168 	callout_init(&sc->tsec_callout, 1);
169 
170 	/* Init locks */
171 	mtx_init(&sc->transmit_lock, device_get_nameunit(dev), "TSEC TX lock",
172 	    MTX_DEF);
173 	mtx_init(&sc->receive_lock, device_get_nameunit(dev), "TSEC RX lock",
174 	    MTX_DEF);
175 	mtx_init(&sc->ic_lock, device_get_nameunit(dev), "TSEC IC lock",
176 	    MTX_DEF);
177 
178 	/* Allocate IO memory for TSEC registers */
179 	sc->sc_rrid = 0;
180 	sc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rrid,
181 	    RF_ACTIVE);
182 	if (sc->sc_rres == NULL) {
183 		device_printf(dev, "could not allocate IO memory range!\n");
184 		goto fail1;
185 	}
186 	sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres);
187 	sc->sc_bas.bst = rman_get_bustag(sc->sc_rres);
188 
189 	/* TSEC attach */
190 	if (tsec_attach(sc) != 0) {
191 		device_printf(dev, "could not be configured\n");
192 		goto fail2;
193 	}
194 
195 	/* Set up interrupts (TX/RX/ERR) */
196 	sc->sc_transmit_irid = TSEC_RID_TXIRQ;
197 	error = tsec_setup_intr(sc, &sc->sc_transmit_ires,
198 	    &sc->sc_transmit_ihand, &sc->sc_transmit_irid,
199 	    tsec_transmit_intr, "TX");
200 	if (error)
201 		goto fail2;
202 
203 	sc->sc_receive_irid = TSEC_RID_RXIRQ;
204 	error = tsec_setup_intr(sc, &sc->sc_receive_ires,
205 	    &sc->sc_receive_ihand, &sc->sc_receive_irid,
206 	    tsec_receive_intr, "RX");
207 	if (error)
208 		goto fail3;
209 
210 	sc->sc_error_irid = TSEC_RID_ERRIRQ;
211 	error = tsec_setup_intr(sc, &sc->sc_error_ires,
212 	    &sc->sc_error_ihand, &sc->sc_error_irid,
213 	    tsec_error_intr, "ERR");
214 	if (error)
215 		goto fail4;
216 
217 	return (0);
218 
219 fail4:
220 	tsec_release_intr(sc, sc->sc_receive_ires, sc->sc_receive_ihand,
221 	    sc->sc_receive_irid, "RX");
222 fail3:
223 	tsec_release_intr(sc, sc->sc_transmit_ires, sc->sc_transmit_ihand,
224 	    sc->sc_transmit_irid, "TX");
225 fail2:
226 	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rrid, sc->sc_rres);
227 fail1:
228 	mtx_destroy(&sc->receive_lock);
229 	mtx_destroy(&sc->transmit_lock);
230 	return (ENXIO);
231 }
232 
233 static int
234 tsec_setup_intr(struct tsec_softc *sc, struct resource **ires, void **ihand,
235     int *irid, driver_intr_t handler, const char *iname)
236 {
237 	int error;
238 
239 	*ires = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, irid, RF_ACTIVE);
240 	if (*ires == NULL) {
241 		device_printf(sc->dev, "could not allocate %s IRQ\n", iname);
242 		return (ENXIO);
243 	}
244 	error = bus_setup_intr(sc->dev, *ires, INTR_TYPE_NET | INTR_MPSAFE,
245 	    NULL, handler, sc, ihand);
246 	if (error) {
247 		device_printf(sc->dev, "failed to set up %s IRQ\n", iname);
248 		if (bus_release_resource(sc->dev, SYS_RES_IRQ, *irid, *ires))
249 			device_printf(sc->dev, "could not release %s IRQ\n", iname);
250 		*ires = NULL;
251 		return (error);
252 	}
253 	return (0);
254 }
255 
256 static void
257 tsec_release_intr(struct tsec_softc *sc, struct resource *ires, void *ihand,
258     int irid, const char *iname)
259 {
260 	int error;
261 
262 	if (ires == NULL)
263 		return;
264 
265 	error = bus_teardown_intr(sc->dev, ires, ihand);
266 	if (error)
267 		device_printf(sc->dev, "bus_teardown_intr() failed for %s intr"
268 		    ", error %d\n", iname, error);
269 
270 	error = bus_release_resource(sc->dev, SYS_RES_IRQ, irid, ires);
271 	if (error)
272 		device_printf(sc->dev, "bus_release_resource() failed for %s "
273 		    "intr, error %d\n", iname, error);
274 }
275 
276 static int
277 tsec_fdt_detach(device_t dev)
278 {
279 	struct tsec_softc *sc;
280 	int error;
281 
282 	sc = device_get_softc(dev);
283 
284 	/* Wait for stopping watchdog */
285 	callout_drain(&sc->tsec_callout);
286 
287 	/* Stop and release all interrupts */
288 	tsec_release_intr(sc, sc->sc_transmit_ires, sc->sc_transmit_ihand,
289 	    sc->sc_transmit_irid, "TX");
290 	tsec_release_intr(sc, sc->sc_receive_ires, sc->sc_receive_ihand,
291 	    sc->sc_receive_irid, "RX");
292 	tsec_release_intr(sc, sc->sc_error_ires, sc->sc_error_ihand,
293 	    sc->sc_error_irid, "ERR");
294 
295 	/* TSEC detach */
296 	tsec_detach(sc);
297 
298 	/* Free IO memory handler */
299 	if (sc->sc_rres) {
300 		error = bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rrid,
301 		    sc->sc_rres);
302 		if (error)
303 			device_printf(dev, "bus_release_resource() failed for"
304 			    " IO memory, error %d\n", error);
305 	}
306 
307 	/* Destroy locks */
308 	mtx_destroy(&sc->receive_lock);
309 	mtx_destroy(&sc->transmit_lock);
310 	mtx_destroy(&sc->ic_lock);
311 	return (0);
312 }
313 
314 void
315 tsec_get_hwaddr(struct tsec_softc *sc, uint8_t *addr)
316 {
317 	union {
318 		uint32_t reg[2];
319 		uint8_t addr[6];
320 	} hw;
321 	int i;
322 
323 	hw.reg[0] = hw.reg[1] = 0;
324 
325 	/* Retrieve the hardware address from the device tree. */
326 	i = OF_getprop(sc->node, "local-mac-address", (void *)hw.addr, 6);
327 	if (i == 6 && (hw.reg[0] != 0 || hw.reg[1] != 0)) {
328 		bcopy(hw.addr, addr, 6);
329 		return;
330 	}
331 
332 	/*
333 	 * Fall back -- use the currently programmed address in the hope that
334 	 * it was set be firmware...
335 	 */
336 	hw.reg[0] = TSEC_READ(sc, TSEC_REG_MACSTNADDR1);
337 	hw.reg[1] = TSEC_READ(sc, TSEC_REG_MACSTNADDR2);
338 	for (i = 0; i < 6; i++)
339 		addr[5-i] = hw.addr[i];
340 }
341