1166144Smarius/*-
2166144Smarius * Copyright (c) 2006 Marius Strobl <marius@FreeBSD.org>
3166144Smarius * All rights reserved.
4166144Smarius *
5166144Smarius * Redistribution and use in source and binary forms, with or without
6166144Smarius * modification, are permitted provided that the following conditions
7166144Smarius * are met:
8166144Smarius * 1. Redistributions of source code must retain the above copyright
9166144Smarius *    notice, this list of conditions and the following disclaimer.
10166144Smarius * 2. Redistributions in binary form must reproduce the above copyright
11166144Smarius *    notice, this list of conditions and the following disclaimer in the
12166144Smarius *    documentation and/or other materials provided with the distribution.
13166144Smarius *
14166144Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15166144Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16166144Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17166144Smarius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18166144Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19166144Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20166144Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21166144Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22166144Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23166144Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24166144Smarius * SUCH DAMAGE.
25166144Smarius */
26166144Smarius
27166144Smarius#include <sys/cdefs.h>
28166144Smarius__FBSDID("$FreeBSD$");
29166144Smarius
30166144Smarius#include <sys/param.h>
31166144Smarius#include <sys/systm.h>
32166144Smarius#include <sys/bus.h>
33166144Smarius#include <sys/endian.h>
34166144Smarius#include <sys/kernel.h>
35166144Smarius#include <sys/lock.h>
36166144Smarius#include <sys/module.h>
37166144Smarius#include <sys/mutex.h>
38166144Smarius#include <sys/resource.h>
39166144Smarius#include <sys/rman.h>
40166144Smarius#include <sys/socket.h>
41166144Smarius
42166144Smarius#include <dev/ofw/ofw_bus.h>
43166144Smarius
44166144Smarius#include <machine/bus.h>
45166144Smarius#include <machine/ofw_machdep.h>
46166144Smarius#include <machine/resource.h>
47166144Smarius
48166144Smarius#include <net/ethernet.h>
49166144Smarius#include <net/if.h>
50166144Smarius#include <net/if_media.h>
51166144Smarius
52166144Smarius#include <dev/le/lancereg.h>
53166144Smarius#include <dev/le/lancevar.h>
54166144Smarius#include <dev/le/am7990reg.h>
55166144Smarius#include <dev/le/am7990var.h>
56166144Smarius
57166144Smarius/*
58166144Smarius * LANCE registers
59166144Smarius */
60166144Smarius#define	LEREG1_RDP	0	/* Register Data port */
61166144Smarius#define	LEREG1_RAP	2	/* Register Address port */
62166144Smarius
63166144Smariusstruct le_lebuffer_softc {
64166144Smarius	struct am7990_softc	sc_am7990;	/* glue to MI code */
65166144Smarius
66166144Smarius	struct resource		*sc_bres;
67166144Smarius
68166144Smarius	struct resource		*sc_rres;
69166144Smarius
70166144Smarius	struct resource		*sc_ires;
71166144Smarius	void			*sc_ih;
72166144Smarius};
73166144Smarius
74166144Smariusstatic devclass_t le_lebuffer_devclass;
75166144Smarius
76166144Smariusstatic device_probe_t le_lebuffer_probe;
77166144Smariusstatic device_attach_t le_lebuffer_attach;
78166144Smariusstatic device_detach_t le_lebuffer_detach;
79166144Smariusstatic device_resume_t le_buffer_resume;
80166144Smariusstatic device_suspend_t le_buffer_suspend;
81166144Smarius
82166144Smariusstatic device_method_t le_lebuffer_methods[] = {
83166144Smarius	/* Device interface */
84166144Smarius	DEVMETHOD(device_probe,		le_lebuffer_probe),
85166144Smarius	DEVMETHOD(device_attach,	le_lebuffer_attach),
86166144Smarius	DEVMETHOD(device_detach,	le_lebuffer_detach),
87166144Smarius	/* We can just use the suspend method here. */
88166144Smarius	DEVMETHOD(device_shutdown,	le_buffer_suspend),
89166144Smarius	DEVMETHOD(device_suspend,	le_buffer_suspend),
90166144Smarius	DEVMETHOD(device_resume,	le_buffer_resume),
91166144Smarius
92166144Smarius	{ 0, 0 }
93166144Smarius};
94166144Smarius
95166144SmariusDEFINE_CLASS_0(le, le_lebuffer_driver, le_lebuffer_methods,
96166144Smarius    sizeof(struct le_lebuffer_softc));
97166144SmariusDRIVER_MODULE(le, lebuffer, le_lebuffer_driver, le_lebuffer_devclass, 0, 0);
98166144SmariusMODULE_DEPEND(le, ether, 1, 1, 1);
99183337SmariusMODULE_DEPEND(le, lebuffer, 1, 1, 1);
100166144Smarius
101166144Smarius/*
102166144Smarius * Media types supported
103166144Smarius */
104166144Smariusstatic const int le_lebuffer_media[] = {
105166144Smarius	IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0)
106166144Smarius};
107298431Spfg#define	NLEMEDIA nitems(le_lebuffer_media)
108166144Smarius
109166144Smariusstatic void le_lebuffer_wrcsr(struct lance_softc *, uint16_t, uint16_t);
110166144Smariusstatic uint16_t le_lebuffer_rdcsr(struct lance_softc *, uint16_t);
111166144Smariusstatic void le_lebuffer_copytodesc(struct lance_softc *, void *, int, int);
112166144Smariusstatic void le_lebuffer_copyfromdesc(struct lance_softc *, void *, int, int);
113166144Smariusstatic void le_lebuffer_copytobuf(struct lance_softc *, void *, int, int);
114166144Smariusstatic void le_lebuffer_copyfrombuf(struct lance_softc *, void *, int, int);
115166144Smariusstatic void le_lebuffer_zerobuf(struct lance_softc *, int, int);
116166144Smarius
117166144Smariusstatic void
118166144Smariusle_lebuffer_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val)
119166144Smarius{
120166144Smarius	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
121166144Smarius
122183337Smarius	bus_write_2(lesc->sc_rres, LEREG1_RAP, port);
123183337Smarius	bus_barrier(lesc->sc_rres, LEREG1_RAP, 2, BUS_SPACE_BARRIER_WRITE);
124183337Smarius	bus_write_2(lesc->sc_rres, LEREG1_RDP, val);
125166144Smarius}
126166144Smarius
127166144Smariusstatic uint16_t
128166144Smariusle_lebuffer_rdcsr(struct lance_softc *sc, uint16_t port)
129166144Smarius{
130166144Smarius	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
131166144Smarius
132183337Smarius	bus_write_2(lesc->sc_rres, LEREG1_RAP, port);
133183337Smarius	bus_barrier(lesc->sc_rres, LEREG1_RAP, 2, BUS_SPACE_BARRIER_WRITE);
134183337Smarius	return (bus_read_2(lesc->sc_rres, LEREG1_RDP));
135166144Smarius}
136166144Smarius
137166144Smarius/*
138166144Smarius * It turns out that using bus_space(9) to access the buffers and the
139166144Smarius * descriptors yields way more throughput than accessing them via the
140166144Smarius * KVA returned by rman_get_virtual(9). The descriptor rings can be
141166144Smarius * accessed using 8-bit up to 64-bit operations while the buffers can
142166144Smarius * be only accessed using 8-bit and 16-bit operations.
143166144Smarius * NB:	For whatever reason setting LE_C3_BSWP has no effect with at
144166144Smarius *	least the 501-2981 (although their 'busmaster-regval' property
145166144Smarius *	indicates to set LE_C3_BSWP also for these cards), so we need
146166144Smarius *	to manually byte swap access to the buffers, i.e. the accesses
147166144Smarius *	going through the RX/TX FIFOs.
148166144Smarius */
149166144Smarius
150166144Smariusstatic void
151166144Smariusle_lebuffer_copytodesc(struct lance_softc *sc, void *fromv, int off, int len)
152166144Smarius{
153166144Smarius	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
154166144Smarius	caddr_t from = fromv;
155166144Smarius
156166144Smarius	for (; len >= 8; len -= 8, off += 8, from += 8)
157183337Smarius		bus_write_8(lesc->sc_bres, off, be64dec(from));
158166144Smarius	for (; len >= 4; len -= 4, off += 4, from += 4)
159183337Smarius		bus_write_4(lesc->sc_bres, off, be32dec(from));
160166144Smarius	for (; len >= 2; len -= 2, off += 2, from += 2)
161183337Smarius		bus_write_2(lesc->sc_bres, off, be16dec(from));
162166144Smarius	if (len == 1)
163183337Smarius		bus_write_1(lesc->sc_bres, off, *from);
164166144Smarius}
165166144Smarius
166166144Smariusstatic void
167166144Smariusle_lebuffer_copyfromdesc(struct lance_softc *sc, void *tov, int off, int len)
168166144Smarius{
169166144Smarius	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
170166144Smarius	caddr_t to = tov;
171166144Smarius
172166144Smarius	for (; len >= 8; len -= 8, off += 8, to += 8)
173166144Smarius		be64enc(to,
174183337Smarius		    bus_read_8(lesc->sc_bres, off));
175166144Smarius	for (; len >= 4; len -= 4, off += 4, to += 4)
176166144Smarius		be32enc(to,
177183337Smarius		    bus_read_4(lesc->sc_bres, off));
178166144Smarius	for (; len >= 2; len -= 2, off += 2, to += 2)
179166144Smarius		be16enc(to,
180183337Smarius		    bus_read_2(lesc->sc_bres, off));
181166144Smarius	if (len == 1)
182183337Smarius		*to = bus_read_1(lesc->sc_bres, off);
183166144Smarius}
184166144Smarius
185166144Smariusstatic void
186166144Smariusle_lebuffer_copytobuf(struct lance_softc *sc, void *fromv, int off, int len)
187166144Smarius{
188166144Smarius	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
189166144Smarius	caddr_t from = fromv;
190166144Smarius
191166144Smarius	for (; len >= 2; len -= 2, off += 2, from += 2)
192183337Smarius		bus_write_2(lesc->sc_bres, off, le16dec(from));
193166144Smarius	if (len == 1)
194183337Smarius		bus_write_1(lesc->sc_bres, off + 1, *from);
195166144Smarius}
196166144Smarius
197166144Smariusstatic void
198166144Smariusle_lebuffer_copyfrombuf(struct lance_softc *sc, void *tov, int off, int len)
199166144Smarius{
200166144Smarius	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
201166144Smarius	caddr_t to = tov;
202166144Smarius
203166144Smarius	for (; len >= 2; len -= 2, off += 2, to += 2)
204166144Smarius		le16enc(to,
205183337Smarius		    bus_read_2(lesc->sc_bres, off));
206166144Smarius	if (len == 1)
207183337Smarius		*to = bus_read_1(lesc->sc_bres, off + 1);
208166144Smarius}
209166144Smarius
210166144Smariusstatic void
211166144Smariusle_lebuffer_zerobuf(struct lance_softc *sc, int off, int len)
212166144Smarius{
213166144Smarius	struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc;
214166144Smarius
215166144Smarius	for (; len >= 2; len -= 2, off += 2)
216183337Smarius		bus_write_2(lesc->sc_bres, off, 0);
217166144Smarius	if (len == 1)
218183337Smarius		bus_write_1(lesc->sc_bres, off + 1, 0);
219166144Smarius}
220166144Smarius
221166144Smariusstatic int
222166144Smariusle_lebuffer_probe(device_t dev)
223166144Smarius{
224166144Smarius
225166144Smarius	if (strcmp(ofw_bus_get_name(dev), "le") == 0) {
226166144Smarius		device_set_desc(dev, "LANCE Ethernet");
227166144Smarius		return (BUS_PROBE_DEFAULT);
228166144Smarius	}
229166144Smarius	return (ENXIO);
230166144Smarius}
231166144Smarius
232166144Smariusstatic int
233166144Smariusle_lebuffer_attach(device_t dev)
234166144Smarius{
235166144Smarius	struct le_lebuffer_softc *lesc;
236166144Smarius	struct lance_softc *sc;
237183337Smarius	int error, i;
238166144Smarius
239166144Smarius	lesc = device_get_softc(dev);
240166144Smarius	sc = &lesc->sc_am7990.lsc;
241166144Smarius
242166144Smarius	LE_LOCK_INIT(sc, device_get_nameunit(dev));
243166144Smarius
244166144Smarius	/*
245166144Smarius	 * The "register space" of the parent is just a buffer where the
246166144Smarius	 * the LANCE descriptor rings and the RX/TX buffers can be stored.
247166144Smarius	 */
248183337Smarius	i = 0;
249166144Smarius	lesc->sc_bres = bus_alloc_resource_any(device_get_parent(dev),
250183337Smarius	    SYS_RES_MEMORY, &i, RF_ACTIVE);
251166144Smarius	if (lesc->sc_bres == NULL) {
252166144Smarius		device_printf(dev, "cannot allocate LANCE buffer\n");
253166144Smarius		error = ENXIO;
254166144Smarius		goto fail_mtx;
255166144Smarius	}
256166144Smarius
257166144Smarius	/* Allocate LANCE registers. */
258183337Smarius	i = 0;
259166144Smarius	lesc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
260183337Smarius	    &i, RF_ACTIVE);
261166144Smarius	if (lesc->sc_rres == NULL) {
262166144Smarius		device_printf(dev, "cannot allocate LANCE registers\n");
263166144Smarius		error = ENXIO;
264166144Smarius		goto fail_bres;
265166144Smarius	}
266166144Smarius
267166144Smarius	/* Allocate LANCE interrupt. */
268183337Smarius	i = 0;
269166144Smarius	if ((lesc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ,
270183337Smarius	    &i, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
271166144Smarius		device_printf(dev, "cannot allocate interrupt\n");
272166144Smarius		error = ENXIO;
273166144Smarius		goto fail_rres;
274166144Smarius	}
275166144Smarius
276166144Smarius	/*
277166144Smarius	 * LANCE view is offset by buffer location.
278166144Smarius	 * Note that we don't use sc->sc_mem.
279166144Smarius	 */
280166144Smarius	sc->sc_addr = 0;
281166144Smarius	sc->sc_memsize = rman_get_size(lesc->sc_bres);
282166144Smarius	sc->sc_flags = 0;
283166144Smarius
284166144Smarius	/* That old black magic... */
285166144Smarius	if (OF_getprop(ofw_bus_get_node(dev), "busmaster-regval",
286166144Smarius	    &sc->sc_conf3, sizeof(sc->sc_conf3)) == -1)
287166144Smarius		sc->sc_conf3 = LE_C3_ACON | LE_C3_BCON;
288166144Smarius	/*
289166144Smarius	 * Make sure LE_C3_BSWP is cleared so that for cards where
290166144Smarius	 * that flag actually works le_lebuffer_copy{from,to}buf()
291166144Smarius	 * don't fail...
292166144Smarius	 */
293166144Smarius	sc->sc_conf3 &= ~LE_C3_BSWP;
294166144Smarius
295166144Smarius	OF_getetheraddr(dev, sc->sc_enaddr);
296166144Smarius
297166144Smarius	sc->sc_copytodesc = le_lebuffer_copytodesc;
298166144Smarius	sc->sc_copyfromdesc = le_lebuffer_copyfromdesc;
299166144Smarius	sc->sc_copytobuf = le_lebuffer_copytobuf;
300166144Smarius	sc->sc_copyfrombuf = le_lebuffer_copyfrombuf;
301166144Smarius	sc->sc_zerobuf = le_lebuffer_zerobuf;
302166144Smarius
303166144Smarius	sc->sc_rdcsr = le_lebuffer_rdcsr;
304166144Smarius	sc->sc_wrcsr = le_lebuffer_wrcsr;
305166144Smarius	sc->sc_hwreset = NULL;
306166144Smarius	sc->sc_hwinit = NULL;
307166144Smarius	sc->sc_hwintr = NULL;
308166144Smarius	sc->sc_nocarrier = NULL;
309166144Smarius	sc->sc_mediachange = NULL;
310166144Smarius	sc->sc_mediastatus = NULL;
311166144Smarius	sc->sc_supmedia = le_lebuffer_media;
312166144Smarius	sc->sc_nsupmedia = NLEMEDIA;
313166144Smarius	sc->sc_defaultmedia = le_lebuffer_media[0];
314166144Smarius
315166144Smarius	error = am7990_config(&lesc->sc_am7990, device_get_name(dev),
316166144Smarius	    device_get_unit(dev));
317166144Smarius	if (error != 0) {
318166144Smarius		device_printf(dev, "cannot attach Am7990\n");
319166144Smarius		goto fail_ires;
320166144Smarius	}
321166144Smarius
322166144Smarius	error = bus_setup_intr(dev, lesc->sc_ires, INTR_TYPE_NET | INTR_MPSAFE,
323166901Spiso	    NULL, am7990_intr, sc, &lesc->sc_ih);
324166144Smarius	if (error != 0) {
325166144Smarius		device_printf(dev, "cannot set up interrupt\n");
326166144Smarius		goto fail_am7990;
327166144Smarius	}
328166144Smarius
329166144Smarius	return (0);
330166144Smarius
331166144Smarius fail_am7990:
332166144Smarius	am7990_detach(&lesc->sc_am7990);
333166144Smarius fail_ires:
334183337Smarius	bus_release_resource(dev, SYS_RES_IRQ,
335183337Smarius	    rman_get_rid(lesc->sc_ires), lesc->sc_ires);
336166144Smarius fail_rres:
337183337Smarius	bus_release_resource(dev, SYS_RES_MEMORY,
338183337Smarius	    rman_get_rid(lesc->sc_rres), lesc->sc_rres);
339166144Smarius fail_bres:
340166144Smarius	bus_release_resource(device_get_parent(dev), SYS_RES_MEMORY,
341183337Smarius	    rman_get_rid(lesc->sc_bres), lesc->sc_bres);
342166144Smarius fail_mtx:
343166144Smarius	LE_LOCK_DESTROY(sc);
344166144Smarius	return (error);
345166144Smarius}
346166144Smarius
347166144Smariusstatic int
348166144Smariusle_lebuffer_detach(device_t dev)
349166144Smarius{
350166144Smarius	struct le_lebuffer_softc *lesc;
351166144Smarius	struct lance_softc *sc;
352166144Smarius
353166144Smarius	lesc = device_get_softc(dev);
354166144Smarius	sc = &lesc->sc_am7990.lsc;
355166144Smarius
356166144Smarius	bus_teardown_intr(dev, lesc->sc_ires, lesc->sc_ih);
357166144Smarius	am7990_detach(&lesc->sc_am7990);
358183337Smarius	bus_release_resource(dev, SYS_RES_IRQ,
359183337Smarius	    rman_get_rid(lesc->sc_ires), lesc->sc_ires);
360183337Smarius	bus_release_resource(dev, SYS_RES_MEMORY,
361183337Smarius	    rman_get_rid(lesc->sc_rres), lesc->sc_rres);
362166144Smarius	bus_release_resource(device_get_parent(dev), SYS_RES_MEMORY,
363183337Smarius	    rman_get_rid(lesc->sc_bres), lesc->sc_bres);
364166144Smarius	LE_LOCK_DESTROY(sc);
365166144Smarius
366166144Smarius	return (0);
367166144Smarius}
368166144Smarius
369166144Smariusstatic int
370166144Smariusle_buffer_suspend(device_t dev)
371166144Smarius{
372166144Smarius	struct le_lebuffer_softc *lesc;
373166144Smarius
374166144Smarius	lesc = device_get_softc(dev);
375166144Smarius
376166144Smarius	lance_suspend(&lesc->sc_am7990.lsc);
377166144Smarius
378166144Smarius	return (0);
379166144Smarius}
380166144Smarius
381166144Smariusstatic int
382166144Smariusle_buffer_resume(device_t dev)
383166144Smarius{
384166144Smarius	struct le_lebuffer_softc *lesc;
385166144Smarius
386166144Smarius	lesc = device_get_softc(dev);
387166144Smarius
388166144Smarius	lance_resume(&lesc->sc_am7990.lsc);
389166144Smarius
390166144Smarius	return (0);
391166144Smarius}
392