if_le_lebuffer.c revision 166901
1/*-
2 * Copyright (c) 2006 Marius Strobl <marius@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/le/if_le_lebuffer.c 166901 2007-02-23 12:19:07Z piso $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33#include <sys/endian.h>
34#include <sys/kernel.h>
35#include <sys/lock.h>
36#include <sys/module.h>
37#include <sys/mutex.h>
38#include <sys/resource.h>
39#include <sys/rman.h>
40#include <sys/socket.h>
41
42#include <dev/ofw/ofw_bus.h>
43
44#include <machine/bus.h>
45#include <machine/ofw_machdep.h>
46#include <machine/resource.h>
47
48#include <net/ethernet.h>
49#include <net/if.h>
50#include <net/if_media.h>
51
52#include <dev/le/lancereg.h>
53#include <dev/le/lancevar.h>
54#include <dev/le/am7990reg.h>
55#include <dev/le/am7990var.h>
56
57/*
58 * LANCE registers
59 */
60#define	LEREG1_RDP	0	/* Register Data port */
61#define	LEREG1_RAP	2	/* Register Address port */
62
63struct le_lebuffer_softc {
64	struct am7990_softc	sc_am7990;	/* glue to MI code */
65
66	int			sc_brid;
67	struct resource		*sc_bres;
68	bus_space_tag_t		sc_buft;
69	bus_space_handle_t	sc_bufh;
70
71	int			sc_rrid;
72	struct resource		*sc_rres;
73	bus_space_tag_t		sc_regt;
74	bus_space_handle_t	sc_regh;
75
76	int			sc_irid;
77	struct resource		*sc_ires;
78	void			*sc_ih;
79};
80
81static devclass_t le_lebuffer_devclass;
82
83static device_probe_t le_lebuffer_probe;
84static device_attach_t le_lebuffer_attach;
85static device_detach_t le_lebuffer_detach;
86static device_resume_t le_buffer_resume;
87static device_suspend_t le_buffer_suspend;
88
89static device_method_t le_lebuffer_methods[] = {
90	/* Device interface */
91	DEVMETHOD(device_probe,		le_lebuffer_probe),
92	DEVMETHOD(device_attach,	le_lebuffer_attach),
93	DEVMETHOD(device_detach,	le_lebuffer_detach),
94	/* We can just use the suspend method here. */
95	DEVMETHOD(device_shutdown,	le_buffer_suspend),
96	DEVMETHOD(device_suspend,	le_buffer_suspend),
97	DEVMETHOD(device_resume,	le_buffer_resume),
98
99	{ 0, 0 }
100};
101
102DEFINE_CLASS_0(le, le_lebuffer_driver, le_lebuffer_methods,
103    sizeof(struct le_lebuffer_softc));
104DRIVER_MODULE(le, lebuffer, le_lebuffer_driver, le_lebuffer_devclass, 0, 0);
105MODULE_DEPEND(le, ether, 1, 1, 1);
106
107/*
108 * Media types supported
109 */
110static const int le_lebuffer_media[] = {
111	IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0)
112};
113#define	NLEMEDIA							\
114    (sizeof(le_lebuffer_media) / sizeof(le_lebuffer_media[0]))
115
116static void le_lebuffer_wrcsr(struct lance_softc *, uint16_t, uint16_t);
117static uint16_t le_lebuffer_rdcsr(struct lance_softc *, uint16_t);
118static void le_lebuffer_copytodesc(struct lance_softc *, void *, int, int);
119static void le_lebuffer_copyfromdesc(struct lance_softc *, void *, int, int);
120static void le_lebuffer_copytobuf(struct lance_softc *, void *, int, int);
121static void le_lebuffer_copyfrombuf(struct lance_softc *, void *, int, int);
122static void le_lebuffer_zerobuf(struct lance_softc *, int, int);
123
124static void
125le_lebuffer_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val)
126{
127	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
128
129	bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, port);
130	bus_space_barrier(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, 2,
131	    BUS_SPACE_BARRIER_WRITE);
132	bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RDP, val);
133}
134
135static uint16_t
136le_lebuffer_rdcsr(struct lance_softc *sc, uint16_t port)
137{
138	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
139
140	bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, port);
141	bus_space_barrier(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, 2,
142	    BUS_SPACE_BARRIER_WRITE);
143	return (bus_space_read_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RDP));
144}
145
146/*
147 * It turns out that using bus_space(9) to access the buffers and the
148 * descriptors yields way more throughput than accessing them via the
149 * KVA returned by rman_get_virtual(9). The descriptor rings can be
150 * accessed using 8-bit up to 64-bit operations while the buffers can
151 * be only accessed using 8-bit and 16-bit operations.
152 * NB:	For whatever reason setting LE_C3_BSWP has no effect with at
153 *	least the 501-2981 (although their 'busmaster-regval' property
154 *	indicates to set LE_C3_BSWP also for these cards), so we need
155 *	to manually byte swap access to the buffers, i.e. the accesses
156 *	going through the RX/TX FIFOs.
157 */
158
159static void
160le_lebuffer_copytodesc(struct lance_softc *sc, void *fromv, int off, int len)
161{
162	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
163	caddr_t from = fromv;
164
165	for (; len >= 8; len -= 8, off += 8, from += 8)
166		bus_space_write_8(lesc->sc_buft, lesc->sc_bufh, off,
167		    be64dec(from));
168	for (; len >= 4; len -= 4, off += 4, from += 4)
169		bus_space_write_4(lesc->sc_buft, lesc->sc_bufh, off,
170		    be32dec(from));
171	for (; len >= 2; len -= 2, off += 2, from += 2)
172		bus_space_write_2(lesc->sc_buft, lesc->sc_bufh, off,
173		    be16dec(from));
174	if (len == 1)
175		bus_space_write_1(lesc->sc_buft, lesc->sc_bufh, off,
176		    *from);
177}
178
179static void
180le_lebuffer_copyfromdesc(struct lance_softc *sc, void *tov, int off, int len)
181{
182	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
183	caddr_t to = tov;
184
185	for (; len >= 8; len -= 8, off += 8, to += 8)
186		be64enc(to,
187		    bus_space_read_8(lesc->sc_buft, lesc->sc_bufh, off));
188	for (; len >= 4; len -= 4, off += 4, to += 4)
189		be32enc(to,
190		    bus_space_read_4(lesc->sc_buft, lesc->sc_bufh, off));
191	for (; len >= 2; len -= 2, off += 2, to += 2)
192		be16enc(to,
193		    bus_space_read_2(lesc->sc_buft, lesc->sc_bufh, off));
194	if (len == 1)
195		*to =
196		    bus_space_read_1(lesc->sc_buft, lesc->sc_bufh, off);
197}
198
199static void
200le_lebuffer_copytobuf(struct lance_softc *sc, void *fromv, int off, int len)
201{
202	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
203	caddr_t from = fromv;
204
205	for (; len >= 2; len -= 2, off += 2, from += 2)
206		bus_space_write_2(lesc->sc_buft, lesc->sc_bufh, off,
207		    le16dec(from));
208	if (len == 1)
209		bus_space_write_1(lesc->sc_buft, lesc->sc_bufh, off + 1,
210		    *from);
211}
212
213static void
214le_lebuffer_copyfrombuf(struct lance_softc *sc, void *tov, int off, int len)
215{
216	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
217	caddr_t to = tov;
218
219	for (; len >= 2; len -= 2, off += 2, to += 2)
220		le16enc(to,
221		    bus_space_read_2(lesc->sc_buft, lesc->sc_bufh, off));
222	if (len == 1)
223		*to =
224		    bus_space_read_1(lesc->sc_buft, lesc->sc_bufh, off + 1);
225}
226
227static void
228le_lebuffer_zerobuf(struct lance_softc *sc, int off, int len)
229{
230	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
231
232	for (; len >= 2; len -= 2, off += 2)
233		bus_space_write_2(lesc->sc_buft, lesc->sc_bufh, off, 0);
234	if (len == 1)
235		bus_space_write_1(lesc->sc_buft, lesc->sc_bufh, off + 1, 0);
236}
237
238static int
239le_lebuffer_probe(device_t dev)
240{
241
242	if (strcmp(ofw_bus_get_name(dev), "le") == 0) {
243		device_set_desc(dev, "LANCE Ethernet");
244		return (BUS_PROBE_DEFAULT);
245	}
246	return (ENXIO);
247}
248
249static int
250le_lebuffer_attach(device_t dev)
251{
252	struct le_lebuffer_softc *lesc;
253	struct lance_softc *sc;
254	int error;
255
256	lesc = device_get_softc(dev);
257	sc = &lesc->sc_am7990.lsc;
258
259	LE_LOCK_INIT(sc, device_get_nameunit(dev));
260
261	/*
262	 * The "register space" of the parent is just a buffer where the
263	 * the LANCE descriptor rings and the RX/TX buffers can be stored.
264	 */
265	lesc->sc_brid = 0;
266	lesc->sc_bres = bus_alloc_resource_any(device_get_parent(dev),
267	    SYS_RES_MEMORY, &lesc->sc_brid, RF_ACTIVE);
268	if (lesc->sc_bres == NULL) {
269		device_printf(dev, "cannot allocate LANCE buffer\n");
270		error = ENXIO;
271		goto fail_mtx;
272	}
273	lesc->sc_buft = rman_get_bustag(lesc->sc_bres);
274	lesc->sc_bufh = rman_get_bushandle(lesc->sc_bres);
275
276	/* Allocate LANCE registers. */
277	lesc->sc_rrid = 0;
278	lesc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
279	    &lesc->sc_rrid, RF_ACTIVE);
280	if (lesc->sc_rres == NULL) {
281		device_printf(dev, "cannot allocate LANCE registers\n");
282		error = ENXIO;
283		goto fail_bres;
284	}
285	lesc->sc_regt = rman_get_bustag(lesc->sc_rres);
286	lesc->sc_regh = rman_get_bushandle(lesc->sc_rres);
287
288	/* Allocate LANCE interrupt. */
289	lesc->sc_irid = 0;
290	if ((lesc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ,
291	    &lesc->sc_irid, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
292		device_printf(dev, "cannot allocate interrupt\n");
293		error = ENXIO;
294		goto fail_rres;
295	}
296
297	/*
298	 * LANCE view is offset by buffer location.
299	 * Note that we don't use sc->sc_mem.
300	 */
301	sc->sc_addr = 0;
302	sc->sc_memsize = rman_get_size(lesc->sc_bres);
303	sc->sc_flags = 0;
304
305	/* That old black magic... */
306	if (OF_getprop(ofw_bus_get_node(dev), "busmaster-regval",
307	    &sc->sc_conf3, sizeof(sc->sc_conf3)) == -1)
308		sc->sc_conf3 = LE_C3_ACON | LE_C3_BCON;
309	/*
310	 * Make sure LE_C3_BSWP is cleared so that for cards where
311	 * that flag actually works le_lebuffer_copy{from,to}buf()
312	 * don't fail...
313	 */
314	sc->sc_conf3 &= ~LE_C3_BSWP;
315
316	OF_getetheraddr(dev, sc->sc_enaddr);
317
318	sc->sc_copytodesc = le_lebuffer_copytodesc;
319	sc->sc_copyfromdesc = le_lebuffer_copyfromdesc;
320	sc->sc_copytobuf = le_lebuffer_copytobuf;
321	sc->sc_copyfrombuf = le_lebuffer_copyfrombuf;
322	sc->sc_zerobuf = le_lebuffer_zerobuf;
323
324	sc->sc_rdcsr = le_lebuffer_rdcsr;
325	sc->sc_wrcsr = le_lebuffer_wrcsr;
326	sc->sc_hwreset = NULL;
327	sc->sc_hwinit = NULL;
328	sc->sc_hwintr = NULL;
329	sc->sc_nocarrier = NULL;
330	sc->sc_mediachange = NULL;
331	sc->sc_mediastatus = NULL;
332	sc->sc_supmedia = le_lebuffer_media;
333	sc->sc_nsupmedia = NLEMEDIA;
334	sc->sc_defaultmedia = le_lebuffer_media[0];
335
336	error = am7990_config(&lesc->sc_am7990, device_get_name(dev),
337	    device_get_unit(dev));
338	if (error != 0) {
339		device_printf(dev, "cannot attach Am7990\n");
340		goto fail_ires;
341	}
342
343	error = bus_setup_intr(dev, lesc->sc_ires, INTR_TYPE_NET | INTR_MPSAFE,
344	    NULL, am7990_intr, sc, &lesc->sc_ih);
345	if (error != 0) {
346		device_printf(dev, "cannot set up interrupt\n");
347		goto fail_am7990;
348	}
349
350	return (0);
351
352 fail_am7990:
353	am7990_detach(&lesc->sc_am7990);
354 fail_ires:
355	bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires);
356 fail_rres:
357	bus_release_resource(dev, SYS_RES_MEMORY, lesc->sc_rrid, lesc->sc_rres);
358 fail_bres:
359	bus_release_resource(device_get_parent(dev), SYS_RES_MEMORY,
360	    lesc->sc_brid, lesc->sc_bres);
361 fail_mtx:
362	LE_LOCK_DESTROY(sc);
363	return (error);
364}
365
366static int
367le_lebuffer_detach(device_t dev)
368{
369	struct le_lebuffer_softc *lesc;
370	struct lance_softc *sc;
371
372	lesc = device_get_softc(dev);
373	sc = &lesc->sc_am7990.lsc;
374
375	bus_teardown_intr(dev, lesc->sc_ires, lesc->sc_ih);
376	am7990_detach(&lesc->sc_am7990);
377	bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires);
378	bus_release_resource(dev, SYS_RES_MEMORY, lesc->sc_rrid, lesc->sc_rres);
379	bus_release_resource(device_get_parent(dev), SYS_RES_MEMORY,
380	    lesc->sc_brid, lesc->sc_bres);
381	LE_LOCK_DESTROY(sc);
382
383	return (0);
384}
385
386static int
387le_buffer_suspend(device_t dev)
388{
389	struct le_lebuffer_softc *lesc;
390
391	lesc = device_get_softc(dev);
392
393	lance_suspend(&lesc->sc_am7990.lsc);
394
395	return (0);
396}
397
398static int
399le_buffer_resume(device_t dev)
400{
401	struct le_lebuffer_softc *lesc;
402
403	lesc = device_get_softc(dev);
404
405	lance_resume(&lesc->sc_am7990.lsc);
406
407	return (0);
408}
409