xref: /openbsd/sys/dev/sbus/if_le_ledma.c (revision db3296cf)
1 /*	$OpenBSD: if_le_ledma.c,v 1.10 2003/07/07 15:37:07 jason Exp $	*/
2 /*	$NetBSD: if_le_ledma.c,v 1.14 2001/05/30 11:46:35 mrg Exp $	*/
3 
4 /*-
5  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Charles M. Hannum; Jason R. Thorpe of the Numerical Aerospace
10  * Simulation Facility, NASA Ames Research Center; Paul Kranenburg.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the NetBSD
23  *	Foundation, Inc. and its contributors.
24  * 4. Neither the name of The NetBSD Foundation nor the names of its
25  *    contributors may be used to endorse or promote products derived
26  *    from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  */
40 
41 #include "bpfilter.h"
42 
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/mbuf.h>
46 #include <sys/syslog.h>
47 #include <sys/socket.h>
48 #include <sys/device.h>
49 #include <sys/malloc.h>
50 
51 #include <net/if.h>
52 #include <net/if_media.h>
53 
54 #ifdef INET
55 #include <netinet/in.h>
56 #include <netinet/if_ether.h>
57 #endif
58 
59 #include <machine/bus.h>
60 #include <machine/intr.h>
61 #include <machine/autoconf.h>
62 
63 #include <dev/sbus/sbusvar.h>
64 
65 #include <dev/ic/lsi64854reg.h>
66 #include <dev/ic/lsi64854var.h>
67 
68 #include <dev/ic/am7990reg.h>
69 #include <dev/ic/am7990var.h>
70 
71 /*
72  * LANCE registers.
73  */
74 #define LEREG1_RDP	0	/* Register Data port */
75 #define LEREG1_RAP	2	/* Register Address port */
76 
77 struct	le_softc {
78 	struct	am7990_softc	sc_am7990;	/* glue to MI code */
79 	struct	sbusdev		sc_sd;		/* sbus device */
80 	bus_space_tag_t		sc_bustag;
81 	bus_dmamap_t		sc_dmamap;
82 	bus_space_handle_t	sc_reg;		/* LANCE registers */
83 	struct	lsi64854_softc	*sc_dma;	/* pointer to my dma */
84 	u_int			sc_laddr;	/* LANCE DMA address */
85 };
86 
87 #define MEMSIZE		(16*1024)	/* LANCE memory size */
88 #define LEDMA_BOUNDARY	(16*1024*1024)	/* must not cross 16MB boundary */
89 
90 int	lematch_ledma(struct device *, void *, void *);
91 void	leattach_ledma(struct device *, struct device *, void *);
92 
93 /*
94  * Media types supported by the Sun4m.
95  */
96 
97 void	le_ledma_setutp(struct am7990_softc *);
98 void	le_ledma_setaui(struct am7990_softc *);
99 
100 int	lemediachange(struct ifnet *);
101 void	lemediastatus(struct ifnet *, struct ifmediareq *);
102 
103 struct cfattach le_ledma_ca = {
104 	sizeof(struct le_softc), lematch_ledma, leattach_ledma
105 };
106 
107 void le_ledma_wrcsr(struct am7990_softc *, u_int16_t, u_int16_t);
108 u_int16_t le_ledma_rdcsr(struct am7990_softc *, u_int16_t);
109 void le_ledma_hwreset(struct am7990_softc *);
110 void le_ledma_hwinit(struct am7990_softc *);
111 void le_ledma_nocarrier(struct am7990_softc *);
112 
113 void
114 le_ledma_wrcsr(struct am7990_softc *sc, u_int16_t port, u_int16_t val)
115 {
116 	struct le_softc *lesc = (struct le_softc *)sc;
117 
118 	bus_space_write_2(lesc->sc_bustag, lesc->sc_reg, LEREG1_RAP, port);
119 	bus_space_write_2(lesc->sc_bustag, lesc->sc_reg, LEREG1_RDP, val);
120 
121 #if defined(SUN4M)
122 	/*
123 	 * We need to flush the Sbus->Mbus write buffers. This can most
124 	 * easily be accomplished by reading back the register that we
125 	 * just wrote (thanks to Chris Torek for this solution).
126 	 */
127 	if (CPU_ISSUN4M) {
128 		volatile u_int16_t discard;
129 		discard = bus_space_read_2(lesc->sc_bustag, lesc->sc_reg,
130 					   LEREG1_RDP);
131 	}
132 #endif
133 }
134 
135 u_int16_t
136 le_ledma_rdcsr(struct am7990_softc *sc, u_int16_t port)
137 {
138 	struct le_softc *lesc = (struct le_softc *)sc;
139 
140 	bus_space_write_2(lesc->sc_bustag, lesc->sc_reg, LEREG1_RAP, port);
141 	return (bus_space_read_2(lesc->sc_bustag, lesc->sc_reg, LEREG1_RDP));
142 }
143 
144 void
145 le_ledma_setutp(struct am7990_softc *sc)
146 {
147 	struct lsi64854_softc *dma = ((struct le_softc *)sc)->sc_dma;
148 	u_int32_t csr;
149 
150 	csr = L64854_GCSR(dma);
151 	csr |= E_TP_AUI;
152 	L64854_SCSR(dma, csr);
153 	delay(20000);	/* must not touch le for 20ms */
154 }
155 
156 void
157 le_ledma_setaui(struct am7990_softc *sc)
158 {
159 	struct lsi64854_softc *dma = ((struct le_softc *)sc)->sc_dma;
160 	u_int32_t csr;
161 
162 	csr = L64854_GCSR(dma);
163 	csr &= ~E_TP_AUI;
164 	L64854_SCSR(dma, csr);
165 	delay(20000);	/* must not touch le for 20ms */
166 }
167 
168 int
169 lemediachange(struct ifnet *ifp)
170 {
171 	struct am7990_softc *sc = ifp->if_softc;
172 	struct ifmedia *ifm = &sc->sc_ifmedia;
173 
174 	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
175 		return (EINVAL);
176 
177 	/*
178 	 * Switch to the selected media.  If autoselect is
179 	 * set, we don't really have to do anything.  We'll
180 	 * switch to the other media when we detect loss of
181 	 * carrier.
182 	 */
183 	switch (IFM_SUBTYPE(ifm->ifm_media)) {
184 	case IFM_10_T:
185 		le_ledma_setutp(sc);
186 		break;
187 
188 	case IFM_10_5:
189 		le_ledma_setaui(sc);
190 		break;
191 
192 	case IFM_AUTO:
193 		break;
194 
195 	default:
196 		return (EINVAL);
197 	}
198 
199 	return (0);
200 }
201 
202 void
203 lemediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
204 {
205 	struct am7990_softc *sc = ifp->if_softc;
206 	struct lsi64854_softc *dma = ((struct le_softc *)sc)->sc_dma;
207 
208 	/*
209 	 * Notify the world which media we're currently using.
210 	 */
211 	if (L64854_GCSR(dma) & E_TP_AUI)
212 		ifmr->ifm_active = IFM_ETHER|IFM_10_T;
213 	else
214 		ifmr->ifm_active = IFM_ETHER|IFM_10_5;
215 }
216 
217 void
218 le_ledma_hwreset(struct am7990_softc *sc)
219 {
220 	struct le_softc *lesc = (struct le_softc *)sc;
221 	struct lsi64854_softc *dma = lesc->sc_dma;
222 	u_int32_t csr;
223 	u_int aui_bit;
224 
225 	/*
226 	 * Reset DMA channel.
227 	 */
228 	csr = L64854_GCSR(dma);
229 	aui_bit = csr & E_TP_AUI;
230 	DMA_RESET(dma);
231 
232 	/* Write bits 24-31 of Lance address */
233 	bus_space_write_4(dma->sc_bustag, dma->sc_regs, L64854_REG_ENBAR,
234 			  lesc->sc_laddr & 0xff000000);
235 
236 	DMA_ENINTR(dma);
237 
238 	/*
239 	 * Disable E-cache invalidates on chip writes.
240 	 * Retain previous cable selection bit.
241 	 */
242 	csr = L64854_GCSR(dma);
243 	csr |= (E_DSBL_WR_INVAL | aui_bit);
244 	L64854_SCSR(dma, csr);
245 	delay(20000);	/* must not touch le for 20ms */
246 }
247 
248 void
249 le_ledma_hwinit(struct am7990_softc *sc)
250 {
251 
252 	/*
253 	 * Make sure we're using the currently-enabled media type.
254 	 * XXX Actually, this is probably unnecessary, now.
255 	 */
256 	switch (IFM_SUBTYPE(sc->sc_ifmedia.ifm_cur->ifm_media)) {
257 	case IFM_10_T:
258 		le_ledma_setutp(sc);
259 		break;
260 
261 	case IFM_10_5:
262 		le_ledma_setaui(sc);
263 		break;
264 	}
265 }
266 
267 void
268 le_ledma_nocarrier(struct am7990_softc *sc)
269 {
270 	struct le_softc *lesc = (struct le_softc *)sc;
271 
272 	/*
273 	 * Check if the user has requested a certain cable type, and
274 	 * if so, honor that request.
275 	 */
276 
277 	if (L64854_GCSR(lesc->sc_dma) & E_TP_AUI) {
278 		switch (IFM_SUBTYPE(sc->sc_ifmedia.ifm_media)) {
279 		case IFM_10_5:
280 		case IFM_AUTO:
281 			printf("%s: lost carrier on UTP port"
282 			    ", switching to AUI port\n", sc->sc_dev.dv_xname);
283 			le_ledma_setaui(sc);
284 		}
285 	} else {
286 		switch (IFM_SUBTYPE(sc->sc_ifmedia.ifm_media)) {
287 		case IFM_10_T:
288 		case IFM_AUTO:
289 			printf("%s: lost carrier on AUI port"
290 			    ", switching to UTP port\n", sc->sc_dev.dv_xname);
291 			le_ledma_setutp(sc);
292 		}
293 	}
294 }
295 
296 int
297 lematch_ledma(struct device *parent, void *vcf, void *aux)
298 {
299 	struct cfdata *cf = vcf;
300 	struct sbus_attach_args *sa = aux;
301 
302 	return (strcmp(cf->cf_driver->cd_name, sa->sa_name) == 0);
303 }
304 
305 
306 void
307 leattach_ledma(struct device *parent, struct device *self, void *aux)
308 {
309 	struct sbus_attach_args *sa = aux;
310 	struct le_softc *lesc = (struct le_softc *)self;
311 	struct lsi64854_softc *lsi = (struct lsi64854_softc *)parent;
312 	struct am7990_softc *sc = &lesc->sc_am7990;
313 	bus_dma_tag_t dmatag = sa->sa_dmatag;
314 	bus_dma_segment_t seg;
315 	int rseg, error;
316 	/* XXX the following declarations should be elsewhere */
317 	extern void myetheraddr(u_char *);
318 
319 	lesc->sc_bustag = sa->sa_bustag;
320 
321 	/* Establish link to `ledma' device */
322 	lesc->sc_dma = lsi;
323 	lesc->sc_dma->sc_client = lesc;
324 
325 	/* Map device registers */
326 	if (sbus_bus_map(sa->sa_bustag,
327 			   sa->sa_slot,
328 			   sa->sa_offset,
329 			   sa->sa_size,
330 			   BUS_SPACE_MAP_LINEAR,
331 			   0, &lesc->sc_reg) != 0) {
332 		printf("%s @ ledma: cannot map registers\n", self->dv_xname);
333 		return;
334 	}
335 
336 	/* Allocate buffer memory */
337 	sc->sc_memsize = MEMSIZE;
338 
339 	/* Get a DMA handle */
340 	if ((error = bus_dmamap_create(dmatag, MEMSIZE, 1, MEMSIZE,
341 					LEDMA_BOUNDARY, BUS_DMA_NOWAIT,
342 					&lesc->sc_dmamap)) != 0) {
343 		printf("%s: DMA map create error %d\n", self->dv_xname, error);
344 		return;
345 	}
346 
347 	/* Allocate DMA buffer */
348 	if ((error = bus_dmamem_alloc(dmatag, MEMSIZE, 0, LEDMA_BOUNDARY,
349 				 &seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) {
350 		printf("%s @ ledma: DMA buffer alloc error %d\n",
351 			self->dv_xname, error);
352 		return;
353 	}
354 
355 	/* Map DMA buffer into kernel space */
356 	if ((error = bus_dmamem_map(dmatag, &seg, rseg, MEMSIZE,
357 			       (caddr_t *)&sc->sc_mem,
358 			       BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) {
359 		printf("%s @ ledma: DMA buffer map error %d\n",
360 			self->dv_xname, error);
361 		bus_dmamem_free(dmatag, &seg, rseg);
362 		return;
363 	}
364 
365 	/* Load DMA buffer */
366 	if ((error = bus_dmamap_load(dmatag, lesc->sc_dmamap, sc->sc_mem,
367 			MEMSIZE, NULL, BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) {
368 		printf("%s: DMA buffer map load error %d\n",
369 			self->dv_xname, error);
370 		bus_dmamem_free(dmatag, &seg, rseg);
371 		bus_dmamem_unmap(dmatag, sc->sc_mem, MEMSIZE);
372 		return;
373 	}
374 
375 	lesc->sc_laddr = lesc->sc_dmamap->dm_segs[0].ds_addr;
376 	sc->sc_addr = lesc->sc_laddr & 0xffffff;
377 	sc->sc_conf3 = LE_C3_BSWP | LE_C3_ACON | LE_C3_BCON;
378 
379 
380 	/* Assume SBus is grandparent */
381 	lesc->sc_sd.sd_reset = (void *)am7990_reset;
382 	sbus_establish(&lesc->sc_sd, parent);
383 
384 	ifmedia_init(&sc->sc_ifmedia, 0, lemediachange, lemediastatus);
385 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T, 0, NULL);
386 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_5, 0, NULL);
387 	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
388 	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO);
389 	sc->sc_hasifmedia = 1;
390 
391 	myetheraddr(sc->sc_arpcom.ac_enaddr);
392 
393 	sc->sc_copytodesc = am7990_copytobuf_contig;
394 	sc->sc_copyfromdesc = am7990_copyfrombuf_contig;
395 	sc->sc_copytobuf = am7990_copytobuf_contig;
396 	sc->sc_copyfrombuf = am7990_copyfrombuf_contig;
397 	sc->sc_zerobuf = am7990_zerobuf_contig;
398 
399 	sc->sc_rdcsr = le_ledma_rdcsr;
400 	sc->sc_wrcsr = le_ledma_wrcsr;
401 	sc->sc_hwinit = le_ledma_hwinit;
402 	sc->sc_nocarrier = le_ledma_nocarrier;
403 	sc->sc_hwreset = le_ledma_hwreset;
404 
405 	/* Establish interrupt handler */
406 	if (sa->sa_nintr != 0)
407 		(void)bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_NET, 0,
408 					 am7990_intr, sc, self->dv_xname);
409 
410 	am7990_config(&lesc->sc_am7990);
411 
412 	/* now initialize DMA */
413 	le_ledma_hwreset(sc);
414 }
415