if_le_lebuffer.c revision 183337
1116742Ssam/*-
2116904Ssam * Copyright (c) 2006 Marius Strobl <marius@FreeBSD.org>
3186904Ssam * All rights reserved.
4116742Ssam *
5116742Ssam * Redistribution and use in source and binary forms, with or without
6116742Ssam * modification, are permitted provided that the following conditions
7116742Ssam * are met:
8116742Ssam * 1. Redistributions of source code must retain the above copyright
9116742Ssam *    notice, this list of conditions and the following disclaimer.
10116904Ssam * 2. Redistributions in binary form must reproduce the above copyright
11116904Ssam *    notice, this list of conditions and the following disclaimer in the
12116904Ssam *    documentation and/or other materials provided with the distribution.
13116904Ssam *
14116742Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15116904Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16116904Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17116904Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18116904Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19116904Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20116904Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21116904Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22116904Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23116904Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24116904Ssam * SUCH DAMAGE.
25116742Ssam */
26116742Ssam
27116742Ssam#include <sys/cdefs.h>
28116742Ssam__FBSDID("$FreeBSD: head/sys/dev/le/if_le_lebuffer.c 183337 2008-09-24 21:26:46Z marius $");
29116742Ssam
30116742Ssam#include <sys/param.h>
31116742Ssam#include <sys/systm.h>
32116742Ssam#include <sys/bus.h>
33178354Ssam#include <sys/endian.h>
34116742Ssam#include <sys/kernel.h>
35116742Ssam#include <sys/lock.h>
36191746Sthompsa#include <sys/module.h>
37116742Ssam#include <sys/mutex.h>
38295126Sglebius#include <sys/resource.h>
39116742Ssam#include <sys/rman.h>
40287197Sglebius#include <sys/socket.h>
41116742Ssam
42283529Sglebius#include <dev/ofw/ofw_bus.h>
43283529Sglebius
44116742Ssam#include <machine/bus.h>
45257176Sglebius#include <machine/ofw_machdep.h>
46178354Ssam#include <machine/resource.h>
47116742Ssam
48178354Ssam#include <net/ethernet.h>
49116742Ssam#include <net/if.h>
50116742Ssam#include <net/if_media.h>
51116742Ssam
52178354Ssam#include <dev/le/lancereg.h>
53190391Ssam#include <dev/le/lancevar.h>
54190391Ssam#include <dev/le/am7990reg.h>
55190391Ssam#include <dev/le/am7990var.h>
56206358Srpaulo
57116742Ssam/*
58116742Ssam * LANCE registers
59116742Ssam */
60178955Ssam#define	LEREG1_RDP	0	/* Register Data port */
61178955Ssam#define	LEREG1_RAP	2	/* Register Address port */
62178955Ssam
63178955Ssamstruct le_lebuffer_softc {
64178955Ssam	struct am7990_softc	sc_am7990;	/* glue to MI code */
65178955Ssam
66178955Ssam	struct resource		*sc_bres;
67178955Ssam
68178955Ssam	struct resource		*sc_rres;
69188782Ssam
70188782Ssam	struct resource		*sc_ires;
71178955Ssam	void			*sc_ih;
72178955Ssam};
73116742Ssam
74178957Ssamstatic devclass_t le_lebuffer_devclass;
75178957Ssam
76178957Ssamstatic device_probe_t le_lebuffer_probe;
77178957Ssamstatic device_attach_t le_lebuffer_attach;
78178957Ssamstatic device_detach_t le_lebuffer_detach;
79178957Ssamstatic device_resume_t le_buffer_resume;
80178957Ssamstatic device_suspend_t le_buffer_suspend;
81178957Ssam
82195618Srpaulostatic device_method_t le_lebuffer_methods[] = {
83195618Srpaulo	/* Device interface */
84195618Srpaulo	DEVMETHOD(device_probe,		le_lebuffer_probe),
85178957Ssam	DEVMETHOD(device_attach,	le_lebuffer_attach),
86178957Ssam	DEVMETHOD(device_detach,	le_lebuffer_detach),
87283566Sglebius	/* We can just use the suspend method here. */
88178354Ssam	DEVMETHOD(device_shutdown,	le_buffer_suspend),
89116742Ssam	DEVMETHOD(device_suspend,	le_buffer_suspend),
90178354Ssam	DEVMETHOD(device_resume,	le_buffer_resume),
91193655Ssam
92178354Ssam	{ 0, 0 }
93178354Ssam};
94178354Ssam
95178354SsamDEFINE_CLASS_0(le, le_lebuffer_driver, le_lebuffer_methods,
96178354Ssam    sizeof(struct le_lebuffer_softc));
97178354SsamDRIVER_MODULE(le, lebuffer, le_lebuffer_driver, le_lebuffer_devclass, 0, 0);
98283568SglebiusMODULE_DEPEND(le, ether, 1, 1, 1);
99178354SsamMODULE_DEPEND(le, lebuffer, 1, 1, 1);
100178354Ssam
101178354Ssam/*
102164645Ssam * Media types supported
103164645Ssam */
104164645Ssamstatic const int le_lebuffer_media[] = {
105164645Ssam	IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0)
106164645Ssam};
107164645Ssam#define	NLEMEDIA							\
108165569Ssam    (sizeof(le_lebuffer_media) / sizeof(le_lebuffer_media[0]))
109165569Ssam
110165569Ssamstatic void le_lebuffer_wrcsr(struct lance_softc *, uint16_t, uint16_t);
111165569Ssamstatic uint16_t le_lebuffer_rdcsr(struct lance_softc *, uint16_t);
112164645Ssamstatic void le_lebuffer_copytodesc(struct lance_softc *, void *, int, int);
113164645Ssamstatic void le_lebuffer_copyfromdesc(struct lance_softc *, void *, int, int);
114164645Ssamstatic void le_lebuffer_copytobuf(struct lance_softc *, void *, int, int);
115164645Ssamstatic void le_lebuffer_copyfrombuf(struct lance_softc *, void *, int, int);
116164645Ssamstatic void le_lebuffer_zerobuf(struct lance_softc *, int, int);
117164645Ssam
118164645Ssamstatic void
119140915Ssamle_lebuffer_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val)
120165569Ssam{
121165569Ssam	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
122165569Ssam
123165569Ssam	bus_write_2(lesc->sc_rres, LEREG1_RAP, port);
124287197Sglebius	bus_barrier(lesc->sc_rres, LEREG1_RAP, 2, BUS_SPACE_BARRIER_WRITE);
125165569Ssam	bus_write_2(lesc->sc_rres, LEREG1_RDP, val);
126116742Ssam}
127165569Ssam
128188782Ssamstatic uint16_t
129165574Ssamle_lebuffer_rdcsr(struct lance_softc *sc, uint16_t port)
130165569Ssam{
131116742Ssam	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
132116742Ssam
133116742Ssam	bus_write_2(lesc->sc_rres, LEREG1_RAP, port);
134186107Ssam	bus_barrier(lesc->sc_rres, LEREG1_RAP, 2, BUS_SPACE_BARRIER_WRITE);
135170530Ssam	return (bus_read_2(lesc->sc_rres, LEREG1_RDP));
136116742Ssam}
137178354Ssam
138167468Ssam/*
139170530Ssam * It turns out that using bus_space(9) to access the buffers and the
140116742Ssam * descriptors yields way more throughput than accessing them via the
141170530Ssam * KVA returned by rman_get_virtual(9). The descriptor rings can be
142187796Ssam * accessed using 8-bit up to 64-bit operations while the buffers can
143187796Ssam * be only accessed using 8-bit and 16-bit operations.
144187796Ssam * NB:	For whatever reason setting LE_C3_BSWP has no effect with at
145187796Ssam *	least the 501-2981 (although their 'busmaster-regval' property
146187796Ssam *	indicates to set LE_C3_BSWP also for these cards), so we need
147187796Ssam *	to manually byte swap access to the buffers, i.e. the accesses
148187796Ssam *	going through the RX/TX FIFOs.
149187796Ssam */
150187796Ssam
151187796Ssamstatic void
152187796Ssamle_lebuffer_copytodesc(struct lance_softc *sc, void *fromv, int off, int len)
153187796Ssam{
154187796Ssam	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
155187796Ssam	caddr_t from = fromv;
156187796Ssam
157170530Ssam	for (; len >= 8; len -= 8, off += 8, from += 8)
158170530Ssam		bus_write_8(lesc->sc_bres, off, be64dec(from));
159170530Ssam	for (; len >= 4; len -= 4, off += 4, from += 4)
160170530Ssam		bus_write_4(lesc->sc_bres, off, be32dec(from));
161170530Ssam	for (; len >= 2; len -= 2, off += 2, from += 2)
162170530Ssam		bus_write_2(lesc->sc_bres, off, be16dec(from));
163170530Ssam	if (len == 1)
164170530Ssam		bus_write_1(lesc->sc_bres, off, *from);
165170530Ssam}
166170530Ssam
167170530Ssamstatic void
168170530Ssamle_lebuffer_copyfromdesc(struct lance_softc *sc, void *tov, int off, int len)
169170530Ssam{
170170530Ssam	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
171170530Ssam	caddr_t to = tov;
172170530Ssam
173170530Ssam	for (; len >= 8; len -= 8, off += 8, to += 8)
174170530Ssam		be64enc(to,
175188782Ssam		    bus_read_8(lesc->sc_bres, off));
176188782Ssam	for (; len >= 4; len -= 4, off += 4, to += 4)
177188782Ssam		be32enc(to,
178188782Ssam		    bus_read_4(lesc->sc_bres, off));
179170530Ssam	for (; len >= 2; len -= 2, off += 2, to += 2)
180170530Ssam		be16enc(to,
181170530Ssam		    bus_read_2(lesc->sc_bres, off));
182170530Ssam	if (len == 1)
183116742Ssam		*to = bus_read_1(lesc->sc_bres, off);
184170530Ssam}
185170530Ssam
186170530Ssamstatic void
187164645Ssamle_lebuffer_copytobuf(struct lance_softc *sc, void *fromv, int off, int len)
188178354Ssam{
189178354Ssam	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
190178354Ssam	caddr_t from = fromv;
191178354Ssam
192170530Ssam	for (; len >= 2; len -= 2, off += 2, from += 2)
193172233Ssam		bus_write_2(lesc->sc_bres, off, le16dec(from));
194178354Ssam	if (len == 1)
195170530Ssam		bus_write_1(lesc->sc_bres, off + 1, *from);
196170530Ssam}
197190532Ssam
198170530Ssamstatic void
199164645Ssamle_lebuffer_copyfrombuf(struct lance_softc *sc, void *tov, int off, int len)
200165569Ssam{
201165569Ssam	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
202165569Ssam	caddr_t to = tov;
203165569Ssam
204165569Ssam	for (; len >= 2; len -= 2, off += 2, to += 2)
205187897Ssam		le16enc(to,
206188782Ssam		    bus_read_2(lesc->sc_bres, off));
207188782Ssam	if (len == 1)
208188774Ssam		*to = bus_read_1(lesc->sc_bres, off + 1);
209188774Ssam}
210165569Ssam
211165569Ssamstatic void
212219596Sbschmidtle_lebuffer_zerobuf(struct lance_softc *sc, int off, int len)
213219596Sbschmidt{
214219596Sbschmidt	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
215219596Sbschmidt
216219596Sbschmidt	for (; len >= 2; len -= 2, off += 2)
217219596Sbschmidt		bus_write_2(lesc->sc_bres, off, 0);
218219596Sbschmidt	if (len == 1)
219219596Sbschmidt		bus_write_1(lesc->sc_bres, off + 1, 0);
220219596Sbschmidt}
221165569Ssam
222165569Ssamstatic int
223165569Ssamle_lebuffer_probe(device_t dev)
224165569Ssam{
225165569Ssam
226165569Ssam	if (strcmp(ofw_bus_get_name(dev), "le") == 0) {
227178354Ssam		device_set_desc(dev, "LANCE Ethernet");
228283540Sglebius		return (BUS_PROBE_DEFAULT);
229178354Ssam	}
230283540Sglebius	return (ENXIO);
231283540Sglebius}
232178354Ssam
233178354Ssamstatic int
234178354Ssamle_lebuffer_attach(device_t dev)
235283540Sglebius{
236178354Ssam	struct le_lebuffer_softc *lesc;
237283540Sglebius	struct lance_softc *sc;
238283540Sglebius	int error, i;
239178354Ssam
240178354Ssam	lesc = device_get_softc(dev);
241178521Ssam	sc = &lesc->sc_am7990.lsc;
242233452Sadrian
243233452Sadrian	LE_LOCK_INIT(sc, device_get_nameunit(dev));
244233452Sadrian
245283529Sglebius	/*
246233452Sadrian	 * The "register space" of the parent is just a buffer where the
247233452Sadrian	 * the LANCE descriptor rings and the RX/TX buffers can be stored.
248283529Sglebius	 */
249283529Sglebius	i = 0;
250283529Sglebius	lesc->sc_bres = bus_alloc_resource_any(device_get_parent(dev),
251283529Sglebius	    SYS_RES_MEMORY, &i, RF_ACTIVE);
252283529Sglebius	if (lesc->sc_bres == NULL) {
253283529Sglebius		device_printf(dev, "cannot allocate LANCE buffer\n");
254283529Sglebius		error = ENXIO;
255283529Sglebius		goto fail_mtx;
256283529Sglebius	}
257283529Sglebius
258283529Sglebius	/* Allocate LANCE registers. */
259283529Sglebius	i = 0;
260283529Sglebius	lesc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
261287197Sglebius	    &i, RF_ACTIVE);
262287197Sglebius	if (lesc->sc_rres == NULL) {
263287197Sglebius		device_printf(dev, "cannot allocate LANCE registers\n");
264287197Sglebius		error = ENXIO;
265287197Sglebius		goto fail_bres;
266287197Sglebius	}
267287197Sglebius
268287197Sglebius	/* Allocate LANCE interrupt. */
269296114Savos	i = 0;
270287197Sglebius	if ((lesc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ,
271287197Sglebius	    &i, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
272287197Sglebius		device_printf(dev, "cannot allocate interrupt\n");
273296114Savos		error = ENXIO;
274296114Savos		goto fail_rres;
275296114Savos	}
276296114Savos
277296114Savos	/*
278287197Sglebius	 * LANCE view is offset by buffer location.
279287197Sglebius	 * Note that we don't use sc->sc_mem.
280287197Sglebius	 */
281296114Savos	sc->sc_addr = 0;
282287197Sglebius	sc->sc_memsize = rman_get_size(lesc->sc_bres);
283287197Sglebius	sc->sc_flags = 0;
284287197Sglebius
285296114Savos	/* That old black magic... */
286296114Savos	if (OF_getprop(ofw_bus_get_node(dev), "busmaster-regval",
287287197Sglebius	    &sc->sc_conf3, sizeof(sc->sc_conf3)) == -1)
288287197Sglebius		sc->sc_conf3 = LE_C3_ACON | LE_C3_BCON;
289287197Sglebius	/*
290287197Sglebius	 * Make sure LE_C3_BSWP is cleared so that for cards where
291287197Sglebius	 * that flag actually works le_lebuffer_copy{from,to}buf()
292287197Sglebius	 * don't fail...
293287197Sglebius	 */
294178354Ssam	sc->sc_conf3 &= ~LE_C3_BSWP;
295178354Ssam
296178354Ssam	OF_getetheraddr(dev, sc->sc_enaddr);
297178354Ssam
298165569Ssam	sc->sc_copytodesc = le_lebuffer_copytodesc;
299287197Sglebius	sc->sc_copyfromdesc = le_lebuffer_copyfromdesc;
300165569Ssam	sc->sc_copytobuf = le_lebuffer_copytobuf;
301165569Ssam	sc->sc_copyfrombuf = le_lebuffer_copyfrombuf;
302283529Sglebius	sc->sc_zerobuf = le_lebuffer_zerobuf;
303283529Sglebius
304178354Ssam	sc->sc_rdcsr = le_lebuffer_rdcsr;
305191746Sthompsa	sc->sc_wrcsr = le_lebuffer_wrcsr;
306191746Sthompsa	sc->sc_hwreset = NULL;
307191746Sthompsa	sc->sc_hwinit = NULL;
308191746Sthompsa	sc->sc_hwintr = NULL;
309230447Sadrian	sc->sc_nocarrier = NULL;
310283565Sglebius	sc->sc_mediachange = NULL;
311283568Sglebius	sc->sc_mediastatus = NULL;
312283568Sglebius	sc->sc_supmedia = le_lebuffer_media;
313165569Ssam	sc->sc_nsupmedia = NLEMEDIA;
314165569Ssam	sc->sc_defaultmedia = le_lebuffer_media[0];
315165569Ssam
316165569Ssam	error = am7990_config(&lesc->sc_am7990, device_get_name(dev),
317165569Ssam	    device_get_unit(dev));
318287197Sglebius	if (error != 0) {
319170530Ssam		device_printf(dev, "cannot attach Am7990\n");
320178354Ssam		goto fail_ires;
321178354Ssam	}
322233452Sadrian
323116742Ssam	error = bus_setup_intr(dev, lesc->sc_ires, INTR_TYPE_NET | INTR_MPSAFE,
324195379Ssam	    NULL, am7990_intr, sc, &lesc->sc_ih);
325155688Ssam	if (error != 0) {
326155688Ssam		device_printf(dev, "cannot set up interrupt\n");
327138568Ssam		goto fail_am7990;
328138568Ssam	}
329170530Ssam
330138568Ssam	return (0);
331170530Ssam
332138568Ssam fail_am7990:
333190391Ssam	am7990_detach(&lesc->sc_am7990);
334190391Ssam fail_ires:
335190391Ssam	bus_release_resource(dev, SYS_RES_IRQ,
336170530Ssam	    rman_get_rid(lesc->sc_ires), lesc->sc_ires);
337170530Ssam fail_rres:
338178354Ssam	bus_release_resource(dev, SYS_RES_MEMORY,
339193843Ssam	    rman_get_rid(lesc->sc_rres), lesc->sc_rres);
340138568Ssam fail_bres:
341178354Ssam	bus_release_resource(device_get_parent(dev), SYS_RES_MEMORY,
342138568Ssam	    rman_get_rid(lesc->sc_bres), lesc->sc_bres);
343287197Sglebius fail_mtx:
344287197Sglebius	LE_LOCK_DESTROY(sc);
345287197Sglebius	return (error);
346116742Ssam}
347116742Ssam
348178354Ssamstatic int
349178354Ssamle_lebuffer_detach(device_t dev)
350178354Ssam{
351178354Ssam	struct le_lebuffer_softc *lesc;
352178354Ssam	struct lance_softc *sc;
353178354Ssam
354116742Ssam	lesc = device_get_softc(dev);
355138568Ssam	sc = &lesc->sc_am7990.lsc;
356116742Ssam
357178354Ssam	bus_teardown_intr(dev, lesc->sc_ires, lesc->sc_ih);
358116742Ssam	am7990_detach(&lesc->sc_am7990);
359287197Sglebius	bus_release_resource(dev, SYS_RES_IRQ,
360287197Sglebius	    rman_get_rid(lesc->sc_ires), lesc->sc_ires);
361287197Sglebius	bus_release_resource(dev, SYS_RES_MEMORY,
362193337Ssam	    rman_get_rid(lesc->sc_rres), lesc->sc_rres);
363290058Savos	bus_release_resource(device_get_parent(dev), SYS_RES_MEMORY,
364290058Savos	    rman_get_rid(lesc->sc_bres), lesc->sc_bres);
365242149Sadrian	LE_LOCK_DESTROY(sc);
366242149Sadrian
367242149Sadrian	return (0);
368242149Sadrian}
369178354Ssam
370178354Ssamstatic int
371188533Sthompsale_buffer_suspend(device_t dev)
372138568Ssam{
373138568Ssam	struct le_lebuffer_softc *lesc;
374193843Ssam
375178354Ssam	lesc = device_get_softc(dev);
376170530Ssam
377190391Ssam	lance_suspend(&lesc->sc_am7990.lsc);
378190391Ssam
379190391Ssam	return (0);
380170530Ssam}
381166012Ssam
382138568Ssamstatic int
383138568Ssamle_buffer_resume(device_t dev)
384170530Ssam{
385138568Ssam	struct le_lebuffer_softc *lesc;
386193337Ssam
387283568Sglebius	lesc = device_get_softc(dev);
388283568Sglebius
389242149Sadrian	lance_resume(&lesc->sc_am7990.lsc);
390191746Sthompsa
391248069Sadrian	return (0);
392170530Ssam}
393178354Ssam