if_le_lebuffer.c revision 166144
1272343Sngie/*-
2272343Sngie * Copyright (c) 2006 Marius Strobl <marius@FreeBSD.org>
3272343Sngie * All rights reserved.
4272343Sngie *
5272343Sngie * Redistribution and use in source and binary forms, with or without
6272343Sngie * modification, are permitted provided that the following conditions
7272343Sngie * are met:
8272343Sngie * 1. Redistributions of source code must retain the above copyright
9272343Sngie *    notice, this list of conditions and the following disclaimer.
10272343Sngie * 2. Redistributions in binary form must reproduce the above copyright
11272343Sngie *    notice, this list of conditions and the following disclaimer in the
12272343Sngie *    documentation and/or other materials provided with the distribution.
13272343Sngie *
14272343Sngie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15272343Sngie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16272343Sngie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17272343Sngie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18272343Sngie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19272343Sngie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20272343Sngie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21272343Sngie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22272343Sngie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23272343Sngie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24272343Sngie * SUCH DAMAGE.
25272343Sngie */
26272343Sngie
27272343Sngie#include <sys/cdefs.h>
28272343Sngie__FBSDID("$FreeBSD: head/sys/dev/le/if_le_lebuffer.c 166144 2007-01-20 12:53:30Z marius $");
29272343Sngie
30272343Sngie#include <sys/param.h>
31272343Sngie#include <sys/systm.h>
32272343Sngie#include <sys/bus.h>
33272343Sngie#include <sys/endian.h>
34272343Sngie#include <sys/kernel.h>
35272343Sngie#include <sys/lock.h>
36272343Sngie#include <sys/module.h>
37272343Sngie#include <sys/mutex.h>
38272343Sngie#include <sys/resource.h>
39272343Sngie#include <sys/rman.h>
40272343Sngie#include <sys/socket.h>
41272343Sngie
42272343Sngie#include <dev/ofw/ofw_bus.h>
43272343Sngie
44277443Sngie#include <machine/bus.h>
45277443Sngie#include <machine/ofw_machdep.h>
46277443Sngie#include <machine/resource.h>
47272343Sngie
48272343Sngie#include <net/ethernet.h>
49272343Sngie#include <net/if.h>
50272343Sngie#include <net/if_media.h>
51272343Sngie
52272343Sngie#include <dev/le/lancereg.h>
53272343Sngie#include <dev/le/lancevar.h>
54272343Sngie#include <dev/le/am7990reg.h>
55272343Sngie#include <dev/le/am7990var.h>
56272343Sngie
57272343Sngie/*
58272343Sngie * LANCE registers
59272343Sngie */
60272343Sngie#define	LEREG1_RDP	0	/* Register Data port */
61272343Sngie#define	LEREG1_RAP	2	/* Register Address port */
62272343Sngie
63272343Sngiestruct le_lebuffer_softc {
64272343Sngie	struct am7990_softc	sc_am7990;	/* glue to MI code */
65272343Sngie
66272343Sngie	int			sc_brid;
67272343Sngie	struct resource		*sc_bres;
68272343Sngie	bus_space_tag_t		sc_buft;
69272343Sngie	bus_space_handle_t	sc_bufh;
70272343Sngie
71272343Sngie	int			sc_rrid;
72272343Sngie	struct resource		*sc_rres;
73272343Sngie	bus_space_tag_t		sc_regt;
74272343Sngie	bus_space_handle_t	sc_regh;
75272343Sngie
76272343Sngie	int			sc_irid;
77272343Sngie	struct resource		*sc_ires;
78272343Sngie	void			*sc_ih;
79272343Sngie};
80272343Sngie
81272343Sngiestatic devclass_t le_lebuffer_devclass;
82272343Sngie
83272343Sngiestatic device_probe_t le_lebuffer_probe;
84272343Sngiestatic device_attach_t le_lebuffer_attach;
85272343Sngiestatic device_detach_t le_lebuffer_detach;
86272343Sngiestatic device_resume_t le_buffer_resume;
87272343Sngiestatic device_suspend_t le_buffer_suspend;
88272343Sngie
89272343Sngiestatic device_method_t le_lebuffer_methods[] = {
90272343Sngie	/* Device interface */
91272343Sngie	DEVMETHOD(device_probe,		le_lebuffer_probe),
92272343Sngie	DEVMETHOD(device_attach,	le_lebuffer_attach),
93272343Sngie	DEVMETHOD(device_detach,	le_lebuffer_detach),
94272343Sngie	/* We can just use the suspend method here. */
95272343Sngie	DEVMETHOD(device_shutdown,	le_buffer_suspend),
96272343Sngie	DEVMETHOD(device_suspend,	le_buffer_suspend),
97272343Sngie	DEVMETHOD(device_resume,	le_buffer_resume),
98272343Sngie
99272343Sngie	{ 0, 0 }
100272343Sngie};
101272343Sngie
102272343SngieDEFINE_CLASS_0(le, le_lebuffer_driver, le_lebuffer_methods,
103272343Sngie    sizeof(struct le_lebuffer_softc));
104272343SngieDRIVER_MODULE(le, lebuffer, le_lebuffer_driver, le_lebuffer_devclass, 0, 0);
105272343SngieMODULE_DEPEND(le, ether, 1, 1, 1);
106272343Sngie
107272343Sngie/*
108272343Sngie * Media types supported
109272343Sngie */
110272343Sngiestatic const int le_lebuffer_media[] = {
111272343Sngie	IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0)
112272343Sngie};
113272343Sngie#define	NLEMEDIA							\
114272343Sngie    (sizeof(le_lebuffer_media) / sizeof(le_lebuffer_media[0]))
115272343Sngie
116272343Sngiestatic void le_lebuffer_wrcsr(struct lance_softc *, uint16_t, uint16_t);
117272343Sngiestatic uint16_t le_lebuffer_rdcsr(struct lance_softc *, uint16_t);
118272343Sngiestatic void le_lebuffer_copytodesc(struct lance_softc *, void *, int, int);
119272343Sngiestatic void le_lebuffer_copyfromdesc(struct lance_softc *, void *, int, int);
120272343Sngiestatic void le_lebuffer_copytobuf(struct lance_softc *, void *, int, int);
121272343Sngiestatic void le_lebuffer_copyfrombuf(struct lance_softc *, void *, int, int);
122272343Sngiestatic void le_lebuffer_zerobuf(struct lance_softc *, int, int);
123272343Sngie
124272343Sngiestatic void
125272343Sngiele_lebuffer_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val)
126272343Sngie{
127272343Sngie	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
128272343Sngie
129272343Sngie	bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, port);
130272343Sngie	bus_space_barrier(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, 2,
131272343Sngie	    BUS_SPACE_BARRIER_WRITE);
132272343Sngie	bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RDP, val);
133272343Sngie}
134272343Sngie
135272343Sngiestatic uint16_t
136272343Sngiele_lebuffer_rdcsr(struct lance_softc *sc, uint16_t port)
137272343Sngie{
138272343Sngie	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
139272343Sngie
140272343Sngie	bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, port);
141272343Sngie	bus_space_barrier(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, 2,
142272343Sngie	    BUS_SPACE_BARRIER_WRITE);
143272343Sngie	return (bus_space_read_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RDP));
144272343Sngie}
145272343Sngie
146272343Sngie/*
147272343Sngie * It turns out that using bus_space(9) to access the buffers and the
148272343Sngie * descriptors yields way more throughput than accessing them via the
149272343Sngie * KVA returned by rman_get_virtual(9). The descriptor rings can be
150272343Sngie * accessed using 8-bit up to 64-bit operations while the buffers can
151272343Sngie * be only accessed using 8-bit and 16-bit operations.
152272343Sngie * NB:	For whatever reason setting LE_C3_BSWP has no effect with at
153272343Sngie *	least the 501-2981 (although their 'busmaster-regval' property
154272343Sngie *	indicates to set LE_C3_BSWP also for these cards), so we need
155272343Sngie *	to manually byte swap access to the buffers, i.e. the accesses
156272343Sngie *	going through the RX/TX FIFOs.
157272343Sngie */
158272343Sngie
159272343Sngiestatic void
160272343Sngiele_lebuffer_copytodesc(struct lance_softc *sc, void *fromv, int off, int len)
161272343Sngie{
162272343Sngie	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
163272343Sngie	caddr_t from = fromv;
164272343Sngie
165272343Sngie	for (; len >= 8; len -= 8, off += 8, from += 8)
166272343Sngie		bus_space_write_8(lesc->sc_buft, lesc->sc_bufh, off,
167272343Sngie		    be64dec(from));
168272343Sngie	for (; len >= 4; len -= 4, off += 4, from += 4)
169272343Sngie		bus_space_write_4(lesc->sc_buft, lesc->sc_bufh, off,
170272343Sngie		    be32dec(from));
171272343Sngie	for (; len >= 2; len -= 2, off += 2, from += 2)
172272343Sngie		bus_space_write_2(lesc->sc_buft, lesc->sc_bufh, off,
173272343Sngie		    be16dec(from));
174272343Sngie	if (len == 1)
175272343Sngie		bus_space_write_1(lesc->sc_buft, lesc->sc_bufh, off,
176272343Sngie		    *from);
177272343Sngie}
178272343Sngie
179272343Sngiestatic void
180272343Sngiele_lebuffer_copyfromdesc(struct lance_softc *sc, void *tov, int off, int len)
181272343Sngie{
182272343Sngie	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
183272343Sngie	caddr_t to = tov;
184272343Sngie
185272343Sngie	for (; len >= 8; len -= 8, off += 8, to += 8)
186272343Sngie		be64enc(to,
187272343Sngie		    bus_space_read_8(lesc->sc_buft, lesc->sc_bufh, off));
188272343Sngie	for (; len >= 4; len -= 4, off += 4, to += 4)
189272343Sngie		be32enc(to,
190272343Sngie		    bus_space_read_4(lesc->sc_buft, lesc->sc_bufh, off));
191272343Sngie	for (; len >= 2; len -= 2, off += 2, to += 2)
192272343Sngie		be16enc(to,
193272343Sngie		    bus_space_read_2(lesc->sc_buft, lesc->sc_bufh, off));
194272343Sngie	if (len == 1)
195272343Sngie		*to =
196272343Sngie		    bus_space_read_1(lesc->sc_buft, lesc->sc_bufh, off);
197272343Sngie}
198272343Sngie
199272343Sngiestatic void
200272343Sngiele_lebuffer_copytobuf(struct lance_softc *sc, void *fromv, int off, int len)
201272343Sngie{
202272343Sngie	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
203272343Sngie	caddr_t from = fromv;
204272343Sngie
205272343Sngie	for (; len >= 2; len -= 2, off += 2, from += 2)
206272343Sngie		bus_space_write_2(lesc->sc_buft, lesc->sc_bufh, off,
207272343Sngie		    le16dec(from));
208272343Sngie	if (len == 1)
209272343Sngie		bus_space_write_1(lesc->sc_buft, lesc->sc_bufh, off + 1,
210272343Sngie		    *from);
211272343Sngie}
212272343Sngie
213272343Sngiestatic void
214272343Sngiele_lebuffer_copyfrombuf(struct lance_softc *sc, void *tov, int off, int len)
215272343Sngie{
216272343Sngie	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
217272343Sngie	caddr_t to = tov;
218272343Sngie
219272343Sngie	for (; len >= 2; len -= 2, off += 2, to += 2)
220272343Sngie		le16enc(to,
221272343Sngie		    bus_space_read_2(lesc->sc_buft, lesc->sc_bufh, off));
222272343Sngie	if (len == 1)
223272343Sngie		*to =
224272343Sngie		    bus_space_read_1(lesc->sc_buft, lesc->sc_bufh, off + 1);
225272343Sngie}
226272343Sngie
227272343Sngiestatic void
228272343Sngiele_lebuffer_zerobuf(struct lance_softc *sc, int off, int len)
229272343Sngie{
230272343Sngie	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
231272343Sngie
232272343Sngie	for (; len >= 2; len -= 2, off += 2)
233272343Sngie		bus_space_write_2(lesc->sc_buft, lesc->sc_bufh, off, 0);
234272343Sngie	if (len == 1)
235272343Sngie		bus_space_write_1(lesc->sc_buft, lesc->sc_bufh, off + 1, 0);
236272343Sngie}
237272343Sngie
238272343Sngiestatic int
239272343Sngiele_lebuffer_probe(device_t dev)
240272343Sngie{
241272343Sngie
242272343Sngie	if (strcmp(ofw_bus_get_name(dev), "le") == 0) {
243272343Sngie		device_set_desc(dev, "LANCE Ethernet");
244272343Sngie		return (BUS_PROBE_DEFAULT);
245272343Sngie	}
246272343Sngie	return (ENXIO);
247272343Sngie}
248272343Sngie
249272343Sngiestatic int
250272343Sngiele_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	    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