1/* $NetBSD: mal.c,v 1.2 2011/06/18 06:41:42 matt Exp $ */
2/*
3 * Copyright (c) 2010 KIYOHARA Takashi
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__KERNEL_RCSID(0, "$NetBSD: mal.c,v 1.2 2011/06/18 06:41:42 matt Exp $");
30
31#include <sys/param.h>
32#include <sys/device.h>
33#include <sys/cpu.h>
34#include <sys/intr.h>
35
36#include <powerpc/ibm4xx/cpu.h>
37#include <powerpc/ibm4xx/dcr4xx.h>
38#include <powerpc/ibm4xx/dev/if_emacvar.h>
39#include <powerpc/ibm4xx/dev/malvar.h>
40#include <powerpc/ibm4xx/spr.h>
41
42#define STAT_TO_CHAN(stat)	__builtin_clz(stat)
43
44
45static int mal_txeob_intr(void *);
46static int mal_rxeob_intr(void *);
47static int mal_txde_intr(void *);
48static int mal_rxde_intr(void *);
49static int mal_serr_intr(void *);
50
51
52const static struct maltbl {
53	int pvr;
54	int intrs[5];
55	int flags;
56#define MAL_GEN2	(1<<0)	/* Generation 2 (405EX/EXr/440GP/GX/SP/SPe) */
57} maltbl[] = {		/* TXEOB  RXEOB   TXDE   RXDE   SERR */
58	{ IBM405GP,	{    11,    12,    13,    14,    10  },	0 },
59	{ IBM405GPR,	{    11,    12,    13,    14,    10  },	0 },
60	{ AMCC405EX,	{    10,    11, 32+ 1, 32+ 2, 32+ 0  }, MAL_GEN2 },
61};
62
63/* Max channel is 4 on 440GX.  Others is 2 or 1. */
64static void *iargs[4];
65
66
67void
68mal_attach(int pvr)
69{
70	int i, to;
71
72	for (i = 0; i < __arraycount(maltbl); i++)
73		if (maltbl[i].pvr == pvr)
74			break;
75	if (i == __arraycount(maltbl)) {
76		aprint_error("%s: unknwon pvr 0x%x\n", __func__, pvr);
77		return;
78	}
79
80	/*
81	 * Reset MAL.
82	 * We wait for the completion of reset in maximums for five seconds.
83	 */
84	mtdcr(DCR_MAL0_CFG, MAL0_CFG_SR);
85	to = 0;
86	while (mfdcr(DCR_MAL0_CFG) & MAL0_CFG_SR) {
87		if (to > 5000) {
88			aprint_error("%s: Soft reset failed\n", __func__);
89			return;
90		}
91		delay(1000);	/* delay 1m sec */
92		to++;
93	}
94
95	/* establish MAL interrupts */
96	intr_establish(maltbl[i].intrs[0], IST_LEVEL, IPL_NET,
97	    mal_txeob_intr, NULL);
98	intr_establish(maltbl[i].intrs[1], IST_LEVEL, IPL_NET,
99	    mal_rxeob_intr, NULL);
100	intr_establish(maltbl[i].intrs[2], IST_LEVEL, IPL_NET,
101	    mal_txde_intr, NULL);
102	intr_establish(maltbl[i].intrs[3], IST_LEVEL, IPL_NET,
103	    mal_rxde_intr, NULL);
104	intr_establish(maltbl[i].intrs[4], IST_LEVEL, IPL_NET,
105	    mal_serr_intr, NULL);
106
107	/* Set the MAL configuration register */
108	if (maltbl[i].flags & MAL_GEN2)
109		mtdcr(DCR_MAL0_CFG,
110		    MAL0_CFG_RMBS_32	|
111		    MAL0_CFG_WMBS_32	|
112		    MAL0_CFG_PLBLT	|
113		    MAL0_CFG_EOPIE	|
114		    MAL0_CFG_PLBB	|
115		    MAL0_CFG_OPBBL	|
116		    MAL0_CFG_LEA	|
117		    MAL0_CFG_SD);
118	else
119		mtdcr(DCR_MAL0_CFG,
120		    MAL0_CFG_PLBLT	|
121		    MAL0_CFG_PLBB	|
122		    MAL0_CFG_OPBBL	|
123		    MAL0_CFG_LEA	|
124		    MAL0_CFG_SD);
125
126	/* Enable MAL SERR Interrupt */
127	mtdcr(DCR_MAL0_IER,
128	    MAL0_IER_PT		|
129	    MAL0_IER_PRE	|
130	    MAL0_IER_PWE	|
131	    MAL0_IER_DE		|
132	    MAL0_IER_NWE	|
133	    MAL0_IER_TO		|
134	    MAL0_IER_OPB	|
135	    MAL0_IER_PLB);
136}
137
138static int
139mal_txeob_intr(void *arg)
140{
141	uint32_t tcei;
142	int chan, handled = 0;
143
144	while ((tcei = mfdcr(DCR_MAL0_TXEOBISR))) {
145		chan = STAT_TO_CHAN(tcei);
146		if (iargs[chan] != NULL) {
147			mtdcr(DCR_MAL0_TXEOBISR, MAL0__XCAR_CHAN(chan));
148			handled |= emac_txeob_intr(iargs[chan]);
149		}
150	}
151	return handled;
152}
153
154static int
155mal_rxeob_intr(void *arg)
156{
157	uint32_t rcei;
158	int chan, handled = 0;
159
160	while ((rcei = mfdcr(DCR_MAL0_RXEOBISR))) {
161		chan = STAT_TO_CHAN(rcei);
162		if (iargs[chan] != NULL) {
163			/* Clear the interrupt */
164			mtdcr(DCR_MAL0_RXEOBISR, MAL0__XCAR_CHAN(chan));
165
166			handled |= emac_rxeob_intr(iargs[chan]);
167		}
168	}
169	return handled;
170}
171
172static int
173mal_txde_intr(void *arg)
174{
175	uint32_t txde;
176	int chan, handled = 0;
177
178	while ((txde = mfdcr(DCR_MAL0_TXDEIR))) {
179		chan = STAT_TO_CHAN(txde);
180		if (iargs[chan] != NULL) {
181			handled |= emac_txde_intr(iargs[chan]);
182
183			/* Clear the interrupt */
184			mtdcr(DCR_MAL0_TXDEIR, MAL0__XCAR_CHAN(chan));
185		}
186	}
187	return handled;
188}
189
190static int
191mal_rxde_intr(void *arg)
192{
193	uint32_t rxde;
194	int chan, handled = 0;
195
196	while ((rxde = mfdcr(DCR_MAL0_RXDEIR))) {
197		chan = STAT_TO_CHAN(rxde);
198		if (iargs[chan] != NULL) {
199			handled |= emac_rxde_intr(iargs[chan]);
200
201			/* Clear the interrupt */
202			mtdcr(DCR_MAL0_RXDEIR, MAL0__XCAR_CHAN(chan));
203
204		        /* Reenable the receive channel */
205			mtdcr(DCR_MAL0_RXCASR, MAL0__XCAR_CHAN(chan));
206		}
207	}
208	return handled;
209}
210
211static int
212mal_serr_intr(void *arg)
213{
214	uint32_t esr;
215
216	esr = mfdcr(DCR_MAL0_ESR);
217
218	/* not yet... */
219	aprint_error("MAL SERR: ESR 0x%08x\n", esr);
220
221	/* Clear the interrupt status bits. */
222	mtdcr(DCR_MAL0_ESR, esr);
223
224	return 1;
225}
226
227
228void
229mal_intr_establish(int chan, void *arg)
230{
231
232	if (chan >= __arraycount(iargs))
233		panic("MAL channel %d not support (max %d)\n",
234		    chan, __arraycount(iargs));
235
236	iargs[chan] = arg;
237}
238
239int
240mal_start(int chan, uint32_t cdtxaddr, uint32_t cdrxaddr)
241{
242
243	/*
244	 * Give the transmit and receive rings to the MAL.
245	 * And set the receive channel buffer size (in units of 16 bytes).
246	 */
247#if MCLBYTES > (4096 - 16)	/* XXX! */
248# error MCLBYTES > max rx channel buffer size
249#endif
250        /* The mtdcr() allows only the constant in the first argument... */
251	switch (chan) {
252	case 0:
253		mtdcr(DCR_MAL0_TXCTP0R, cdtxaddr);
254		mtdcr(DCR_MAL0_RXCTP0R, cdrxaddr);
255		mtdcr(DCR_MAL0_RCBS0, MCLBYTES / 16);
256		break;
257
258	case 1:
259		mtdcr(DCR_MAL0_TXCTP1R, cdtxaddr);
260		mtdcr(DCR_MAL0_RXCTP1R, cdrxaddr);
261		mtdcr(DCR_MAL0_RCBS1, MCLBYTES / 16);
262		break;
263
264	case 2:
265		mtdcr(DCR_MAL0_TXCTP2R, cdtxaddr);
266		mtdcr(DCR_MAL0_RXCTP2R, cdrxaddr);
267		mtdcr(DCR_MAL0_RCBS2, MCLBYTES / 16);
268		break;
269
270	case 3:
271		mtdcr(DCR_MAL0_TXCTP3R, cdtxaddr);
272		mtdcr(DCR_MAL0_RXCTP3R, cdrxaddr);
273		mtdcr(DCR_MAL0_RCBS3, MCLBYTES / 16);
274		break;
275
276	default:
277		aprint_error("MAL unknown channel no.%d\n", chan);
278		return EINVAL;
279	}
280
281	/* Enable the transmit and receive channel on the MAL. */
282	mtdcr(DCR_MAL0_RXCASR, MAL0__XCAR_CHAN(chan));
283	mtdcr(DCR_MAL0_TXCASR, MAL0__XCAR_CHAN(chan));
284
285	return 0;
286}
287
288void
289mal_stop(int chan)
290{
291
292	/* Disable the receive and transmit channels. */
293	mtdcr(DCR_MAL0_RXCARR, MAL0__XCAR_CHAN(chan));
294	mtdcr(DCR_MAL0_TXCARR, MAL0__XCAR_CHAN(chan));
295}
296