1/*	$NetBSD: if_mc_obio.c,v 1.16 2006/12/12 11:46:33 martin Exp $	*/
2
3/*-
4 * Copyright (c) 1997 David Huang <khym@azeotrope.org>
5 * All rights reserved.
6 *
7 * Portions of this code are based on code by Denton Gentry <denny1@home.com>
8 * and Yanagisawa Takeshi <yanagisw@aa.ap.titech.ac.jp>.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30
31/*
32 * Bus attachment and DMA routines for the mc driver (Centris/Quadra
33 * 660av and Quadra 840av onboard ethernet, based on the AMD Am79C940
34 * MACE ethernet chip). Also uses the PSC (Peripheral Subsystem
35 * Controller) for DMA to and from the MACE.
36 */
37
38#include <sys/cdefs.h>
39__KERNEL_RCSID(0, "$NetBSD: if_mc_obio.c,v 1.16 2006/12/12 11:46:33 martin Exp $");
40
41#include "opt_ddb.h"
42
43#include <sys/param.h>
44#include <sys/device.h>
45#include <sys/malloc.h>
46#include <sys/socket.h>
47#include <sys/systm.h>
48
49#include <net/if.h>
50#include <net/if_ether.h>
51
52#include <uvm/uvm_extern.h>
53
54#include <machine/bus.h>
55#include <machine/psc.h>
56
57#include <mac68k/obio/obiovar.h>
58#include <mac68k/dev/if_mcreg.h>
59#include <mac68k/dev/if_mcvar.h>
60
61#define MACE_REG_BASE	0x50F1C000
62#define MACE_PROM_BASE	0x50F08000
63
64hide int	mc_obio_match(struct device *, struct cfdata *, void *);
65hide void	mc_obio_attach(struct device *, struct device *, void *);
66hide void	mc_obio_init(struct mc_softc *);
67hide void	mc_obio_put(struct mc_softc *, u_int);
68hide int	mc_dmaintr(void *);
69hide void	mc_reset_rxdma(struct mc_softc *);
70hide void	mc_reset_rxdma_set(struct mc_softc *, int);
71hide void	mc_reset_txdma(struct mc_softc *);
72hide int	mc_obio_getaddr(struct mc_softc *, u_int8_t *);
73
74CFATTACH_DECL(mc_obio, sizeof(struct mc_softc),
75    mc_obio_match, mc_obio_attach, NULL, NULL);
76
77hide int
78mc_obio_match(struct device *parent, struct cfdata *cf, void *aux)
79{
80	struct obio_attach_args *oa = aux;
81	bus_space_handle_t bsh;
82	int found = 0;
83
84        if (current_mac_model->class != MACH_CLASSAV)
85		return 0;
86
87	if (bus_space_map(oa->oa_tag, MACE_REG_BASE, MC_REGSIZE, 0, &bsh))
88		return 0;
89
90	/*
91	 * Make sure the MACE's I/O space is readable, and if it is,
92	 * try to read the CHIPID register. A MACE will always have
93	 * 0x?940, where the ? depends on the chip version.
94	 */
95	if (mac68k_bus_space_probe(oa->oa_tag, bsh, 0, 1)) {
96		if ((bus_space_read_1(
97			oa->oa_tag, bsh, MACE_REG(MACE_CHIPIDL)) == 0x40) &&
98		    ((bus_space_read_1(
99			oa->oa_tag, bsh, MACE_REG(MACE_CHIPIDH)) & 0xf) == 9))
100			found = 1;
101	}
102
103	bus_space_unmap(oa->oa_tag, bsh, MC_REGSIZE);
104
105	return found;
106}
107
108hide void
109mc_obio_attach(struct device *parent, struct device *self, void *aux)
110{
111	struct obio_attach_args *oa = (struct obio_attach_args *)aux;
112	struct mc_softc *sc = (void *)self;
113	u_int8_t myaddr[ETHER_ADDR_LEN];
114	int rsegs;
115
116	sc->sc_regt = oa->oa_tag;
117	sc->sc_biucc = XMTSP_64;
118	sc->sc_fifocc = XMTFW_16 | RCVFW_64 | XMTFWU | RCVFWU |
119	    XMTBRST | RCVBRST;
120	sc->sc_plscc = PORTSEL_AUI;
121
122	if (bus_space_map(sc->sc_regt, MACE_REG_BASE, MC_REGSIZE, 0,
123	    &sc->sc_regh)) {
124		printf(": failed to map space for MACE regs.\n");
125		return;
126	}
127
128	if (mc_obio_getaddr(sc, myaddr)) {
129		printf(": failed to get MAC address.\n");
130		return;
131	}
132
133	/* allocate memory for transmit and receive DMA buffers */
134	sc->sc_dmat = oa->oa_dmat;
135	if (bus_dmamem_alloc(sc->sc_dmat, 2 * 0x800, 0, 0, &sc->sc_dmasegs_tx,
136		1, &rsegs, BUS_DMA_NOWAIT) != 0) {
137		printf(": failed to allocate TX DMA buffers.\n");
138		return;
139	}
140
141	if (bus_dmamem_map(sc->sc_dmat, &sc->sc_dmasegs_tx, rsegs, 2 * 0x800,
142		(void*)&sc->sc_txbuf, BUS_DMA_NOWAIT | BUS_DMA_COHERENT) != 0) {
143		printf(": failed to map TX DMA buffers.\n");
144		return;
145	}
146
147	if (bus_dmamem_alloc(sc->sc_dmat, MC_RXDMABUFS * 0x800, 0, 0,
148		&sc->sc_dmasegs_rx, 1, &rsegs, BUS_DMA_NOWAIT) != 0) {
149		printf(": failed to allocate RX DMA buffers.\n");
150		return;
151	}
152
153	if (bus_dmamem_map(sc->sc_dmat, &sc->sc_dmasegs_rx, rsegs,
154		MC_RXDMABUFS * 0x800, (void*)&sc->sc_rxbuf,
155		BUS_DMA_NOWAIT | BUS_DMA_COHERENT) != 0) {
156		printf(": failed to map RX DMA buffers.\n");
157		return;
158	}
159
160	if (bus_dmamap_create(sc->sc_dmat, 2 * 0x800, 1, 2 * 0x800, 0,
161	    BUS_DMA_NOWAIT, &sc->sc_dmam_tx) != 0) {
162		printf(": failed to allocate TX DMA map.\n");
163		return;
164	}
165
166	if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmam_tx, sc->sc_txbuf,
167		2 * 0x800, NULL, BUS_DMA_NOWAIT) != 0) {
168		printf(": failed to map TX DMA mapping.\n");
169		return;
170	}
171
172	if (bus_dmamap_create(sc->sc_dmat, MC_RXDMABUFS * 0x800, 1,
173		MC_RXDMABUFS * 0x800, 0, BUS_DMA_NOWAIT, &sc->sc_dmam_rx) != 0) {
174		printf(": failed to allocate RX DMA map.\n");
175		return;
176	}
177
178	if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmam_rx, sc->sc_rxbuf,
179		MC_RXDMABUFS * 0x800, NULL, BUS_DMA_NOWAIT) != 0) {
180		printf(": failed to map RX DMA mapping.\n");
181		return;
182	}
183
184	sc->sc_txbuf_phys = sc->sc_dmasegs_tx.ds_addr;
185	sc->sc_rxbuf_phys = sc->sc_dmasegs_rx.ds_addr;
186
187	sc->sc_bus_init = mc_obio_init;
188	sc->sc_putpacket = mc_obio_put;
189
190	/* disable receive DMA */
191	psc_reg2(PSC_ENETRD_CTL) = 0x8800;
192	psc_reg2(PSC_ENETRD_CTL) = 0x1000;
193	psc_reg2(PSC_ENETRD_CMD + PSC_SET0) = 0x1100;
194	psc_reg2(PSC_ENETRD_CMD + PSC_SET1) = 0x1100;
195
196	/* disable transmit DMA */
197	psc_reg2(PSC_ENETWR_CTL) = 0x8800;
198	psc_reg2(PSC_ENETWR_CTL) = 0x1000;
199	psc_reg2(PSC_ENETWR_CMD + PSC_SET0) = 0x1100;
200	psc_reg2(PSC_ENETWR_CMD + PSC_SET1) = 0x1100;
201
202	/* install interrupt handlers */
203	add_psc_lev4_intr(PSCINTR_ENET_DMA, mc_dmaintr, sc);
204	add_psc_lev3_intr(mcintr, sc);
205
206	/* enable MACE DMA interrupts */
207	psc_reg1(PSC_LEV4_IER) = 0x80 | (1 << PSCINTR_ENET_DMA);
208
209	/* don't know what this does */
210	psc_reg2(PSC_ENETWR_CTL) = 0x9000;
211	psc_reg2(PSC_ENETRD_CTL) = 0x9000;
212	psc_reg2(PSC_ENETWR_CTL) = 0x0400;
213	psc_reg2(PSC_ENETRD_CTL) = 0x0400;
214
215	/* enable MACE interrupts */
216	psc_reg1(PSC_LEV3_IER) = 0x80 | (1 << PSCINTR_ENET);
217
218	/* mcsetup returns 1 if something fails */
219	if (mcsetup(sc, myaddr)) {
220		/* disable interrupts */
221		psc_reg1(PSC_LEV4_IER) = (1 << PSCINTR_ENET_DMA);
222		psc_reg1(PSC_LEV3_IER) = (1 << PSCINTR_ENET);
223		/* remove interrupt handlers */
224		remove_psc_lev4_intr(PSCINTR_ENET_DMA);
225		remove_psc_lev3_intr();
226
227		bus_space_unmap(sc->sc_regt, sc->sc_regh, MC_REGSIZE);
228		return;
229	}
230}
231
232/* Bus-specific initialization */
233hide void
234mc_obio_init(struct mc_softc *sc)
235{
236	mc_reset_rxdma(sc);
237	mc_reset_txdma(sc);
238}
239
240hide void
241mc_obio_put(struct mc_softc *sc, u_int len)
242{
243	int offset = sc->sc_txset == 0 ? 0 : 0x800;
244
245	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmam_tx, offset, 0x800,
246	    BUS_DMASYNC_PREWRITE);
247	psc_reg4(PSC_ENETWR_ADDR + sc->sc_txset) = sc->sc_txbuf_phys + offset;
248	psc_reg4(PSC_ENETWR_LEN + sc->sc_txset) = len;
249	psc_reg2(PSC_ENETWR_CMD + sc->sc_txset) = 0x9800;
250	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmam_tx, offset, 0x800,
251	    BUS_DMASYNC_POSTWRITE);
252
253	sc->sc_txset ^= 0x10;
254}
255
256/*
257 * Interrupt handler for the MACE DMA completion interrupts
258 */
259int
260mc_dmaintr(void *arg)
261{
262	struct mc_softc *sc = arg;
263	u_int16_t status;
264	u_int32_t bufsleft, which;
265	int head;
266
267	/*
268	 * Not sure what this does... figure out if this interrupt is
269	 * really ours?
270	 */
271	while ((which = psc_reg4(0x804)) != psc_reg4(0x804))
272		;
273	if ((which & 0x60000000) == 0)
274		return 0;
275
276	/* Get the read channel status */
277	status = psc_reg2(PSC_ENETRD_CTL);
278	if (status & 0x2000) {
279		/* I think this is an exceptional condition. Reset the DMA */
280		mc_reset_rxdma(sc);
281#ifdef MCDEBUG
282		printf("%s: resetting receive DMA channel (status 0x%04x)\n",
283		    sc->sc_dev.dv_xname, status);
284#endif
285	} else if (status & 0x100) {
286		/* We've received some packets from the MACE */
287		int offset;
288
289		/* Clear the interrupt */
290		psc_reg2(PSC_ENETRD_CMD + sc->sc_rxset) = 0x1100;
291
292		/* See how may receive buffers are left */
293		bufsleft = psc_reg4(PSC_ENETRD_LEN + sc->sc_rxset);
294		head = MC_RXDMABUFS - bufsleft;
295
296#if 0 /* I don't think this should ever happen */
297		if (head == sc->sc_tail) {
298#ifdef MCDEBUG
299			printf("%s: head == tail: suspending DMA?\n",
300			    sc->sc_dev.dv_xname);
301#endif
302			psc_reg2(PSC_ENETRD_CMD + sc->sc_rxset) = 0x9000;
303		}
304#endif
305
306		/* Loop through, processing each of the packets */
307		for (; sc->sc_tail < head; sc->sc_tail++) {
308			offset = sc->sc_tail * 0x800;
309
310			bus_dmamap_sync(sc->sc_dmat, sc->sc_dmam_rx,
311					PAGE_SIZE + offset, 0x800,
312					BUS_DMASYNC_PREREAD);
313
314			sc->sc_rxframe.rx_rcvcnt = sc->sc_rxbuf[offset];
315			sc->sc_rxframe.rx_rcvsts = sc->sc_rxbuf[offset+2];
316			sc->sc_rxframe.rx_rntpc = sc->sc_rxbuf[offset+4];
317			sc->sc_rxframe.rx_rcvcc = sc->sc_rxbuf[offset+6];
318			sc->sc_rxframe.rx_frame = sc->sc_rxbuf + offset + 16;
319
320			mc_rint(sc);
321
322			bus_dmamap_sync(sc->sc_dmat, sc->sc_dmam_rx,
323					PAGE_SIZE + offset, 0x800,
324					BUS_DMASYNC_POSTREAD);
325		}
326
327		/*
328		 * If we're out of buffers, reset this register set
329		 * and switch to the other one. Otherwise, reactivate
330		 * this set.
331		 */
332		if (bufsleft == 0) {
333			mc_reset_rxdma_set(sc, sc->sc_rxset);
334			sc->sc_rxset ^= 0x10;
335		} else
336			psc_reg2(PSC_ENETRD_CMD + sc->sc_rxset) = 0x9800;
337	}
338
339	/* Get the write channel status */
340	status = psc_reg2(PSC_ENETWR_CTL);
341	if (status & 0x2000) {
342		/* I think this is an exceptional condition. Reset the DMA */
343		mc_reset_txdma(sc);
344#ifdef MCDEBUG
345		printf("%s: resetting transmit DMA channel (status 0x%04x)\n",
346			sc->sc_dev.dv_xname, status);
347#endif
348	} else if (status & 0x100) {
349		/* Clear the interrupt and switch register sets */
350		psc_reg2(PSC_ENETWR_CMD + sc->sc_txseti) = 0x100;
351		sc->sc_txseti ^= 0x10;
352	}
353
354	return 1;
355}
356
357
358hide void
359mc_reset_rxdma(struct mc_softc *sc)
360{
361	u_int8_t maccc;
362
363	/* Disable receiver, reset the DMA channels */
364	maccc = NIC_GET(sc, MACE_MACCC);
365	NIC_PUT(sc, MACE_MACCC, maccc & ~ENRCV);
366	psc_reg2(PSC_ENETRD_CTL) = 0x8800;
367	mc_reset_rxdma_set(sc, 0);
368	psc_reg2(PSC_ENETRD_CTL) = 0x400;
369
370	psc_reg2(PSC_ENETRD_CTL) = 0x8800;
371	mc_reset_rxdma_set(sc, 0x10);
372	psc_reg2(PSC_ENETRD_CTL) = 0x400;
373
374	/* Reenable receiver, reenable DMA */
375	NIC_PUT(sc, MACE_MACCC, maccc);
376	sc->sc_rxset = 0;
377
378	psc_reg2(PSC_ENETRD_CMD + PSC_SET0) = 0x9800;
379	psc_reg2(PSC_ENETRD_CMD + PSC_SET1) = 0x9800;
380}
381
382hide void
383mc_reset_rxdma_set(struct mc_softc *sc, int set)
384{
385	/* disable DMA while modifying the registers, then reenable DMA */
386	psc_reg2(PSC_ENETRD_CMD + set) = 0x0100;
387	psc_reg4(PSC_ENETRD_ADDR + set) = sc->sc_rxbuf_phys;
388	psc_reg4(PSC_ENETRD_LEN + set) = MC_RXDMABUFS;
389	psc_reg2(PSC_ENETRD_CMD + set) = 0x9800;
390	sc->sc_tail = 0;
391}
392
393hide void
394mc_reset_txdma(struct mc_softc *sc)
395{
396	u_int8_t maccc;
397
398	psc_reg2(PSC_ENETWR_CTL) = 0x8800;
399	maccc = NIC_GET(sc, MACE_MACCC);
400	NIC_PUT(sc, MACE_MACCC, maccc & ~ENXMT);
401	sc->sc_txset = sc->sc_txseti = 0;
402	psc_reg2(PSC_ENETWR_CTL) = 0x400;
403	NIC_PUT(sc, MACE_MACCC, maccc);
404}
405
406hide int
407mc_obio_getaddr(struct mc_softc *sc, u_int8_t *lladdr)
408{
409	bus_space_handle_t bsh;
410	u_char csum;
411
412	if (bus_space_map(sc->sc_regt, MACE_PROM_BASE, 8*16, 0, &bsh)) {
413		printf(": failed to map space to read MACE address.\n%s",
414		    sc->sc_dev.dv_xname);
415		return (-1);
416	}
417
418	if (!mac68k_bus_space_probe(sc->sc_regt, bsh, 0, 1)) {
419		bus_space_unmap(sc->sc_regt, bsh, 8*16);
420		return (-1);
421	}
422
423	csum = mc_get_enaddr(sc->sc_regt, bsh, 1, lladdr);
424	if (csum != 0xff)
425		printf(": ethernet PROM checksum failed (0x%x != 0xff)\n%s",
426		    (int)csum, sc->sc_dev.dv_xname);
427
428	bus_space_unmap(sc->sc_regt, bsh, 8*16);
429
430	return (csum == 0xff ? 0 : -1);
431}
432