xref: /netbsd/sys/arch/evbppc/virtex/design_gsrd1.c (revision beecddb6)
1 /* 	$NetBSD: design_gsrd1.c,v 1.7 2021/08/07 16:18:52 thorpej Exp $ */
2 
3 /*
4  * Copyright (c) 2006 Jachym Holecek
5  * All rights reserved.
6  *
7  * Written for DFC Design, s.r.o.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: design_gsrd1.c,v 1.7 2021/08/07 16:18:52 thorpej Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/device.h>
38 #include <sys/kernel.h>
39 #include <sys/kmem.h>
40 #include <sys/cpu.h>
41 #include <sys/bus.h>
42 #include <sys/intr.h>
43 
44 #include <powerpc/ibm4xx/cpu.h>
45 #include <powerpc/ibm4xx/dev/plbvar.h>
46 
47 #include <evbppc/virtex/dev/xcvbusvar.h>
48 
49 #include <evbppc/virtex/dev/xlcomreg.h>
50 #include <evbppc/virtex/dev/cdmacreg.h>
51 #include <evbppc/virtex/dev/temacreg.h>
52 #include <evbppc/virtex/dev/tftreg.h>
53 
54 #include <evbppc/virtex/virtex.h>
55 #include <evbppc/virtex/dcr.h>
56 
57 
58 #define DCR_CDMAC_BASE 		0x0140
59 #define DCR_XLCOM_BASE 		0x0000
60 #define DCR_TEMAC_BASE 		0x0030
61 #define DCR_LLFB_BASE 		0x0080
62 
63 #define CDMAC_TX0_STAT 		CDMAC_STAT_BASE(0)
64 #define CDMAC_RX0_STAT 		CDMAC_STAT_BASE(1)
65 #define CDMAC_TX1_STAT 		CDMAC_STAT_BASE(2)
66 #define CDMAC_RX1_STAT 		CDMAC_STAT_BASE(3)
67 
68 #define CDMAC_TX0_BASE 		CDMAC_CTRL_BASE(0)
69 #define CDMAC_RX0_BASE 		CDMAC_CTRL_BASE(1)
70 #define CDMAC_TX1_BASE 		CDMAC_CTRL_BASE(2)
71 #define CDMAC_RX1_BASE 		CDMAC_CTRL_BASE(3)
72 
73 #define CDMAC_INTR_LINE 	2
74 #define CDMAC_NCHAN 		4
75 
76 #define IPL_CDMAC 		IPL_NET
77 #define splcdmac() 		splnet()
78 
79 
80 /*
81  * CDMAC per-channel interrupt handler. CDMAC has only one interrupt signal
82  * shared by all channels on GSRD, so we have to dispatch channels manually.
83  *
84  * Note: we hardwire priority to IPL_NET, temac(4) is the only device that
85  * needs to service DMA interrupts anyway.
86  */
87 struct cdmac_intr_handle {
88 	void 			(*cih_func)(void *);
89 	void 			*cih_arg;
90 };
91 
92 static void 			*cdmac_ih = NULL; 	/* real CDMAC intr */
93 static struct cdmac_intr_handle *cdmac_intrs[CDMAC_NCHAN];
94 
95 
96 /*
97  * DCR bus space leaf access routines.
98  */
99 
100 static void
101 xlcom0_write_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr,
102     uint32_t val)
103 {
104 	addr += h;
105 
106 	switch (addr) {
107 	WCASE(DCR_XLCOM_BASE, XLCOM_TX_FIFO);
108 	WCASE(DCR_XLCOM_BASE, XLCOM_STAT);
109 	WCASE(DCR_XLCOM_BASE, XLCOM_CNTL);
110 	WDEAD(addr);
111 	}
112 }
113 
114 static uint32_t
115 xlcom0_read_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr)
116 {
117 	uint32_t 		val;
118 
119 	addr += h;
120 
121 	switch (addr) {
122 	RCASE(DCR_XLCOM_BASE, XLCOM_RX_FIFO);
123 	RCASE(DCR_XLCOM_BASE, XLCOM_STAT);
124 	RCASE(DCR_XLCOM_BASE, XLCOM_CNTL);
125 	RDEAD(addr);
126 	}
127 
128 	return (val);
129 }
130 
131 static void
132 tft0_write_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr,
133     uint32_t val)
134 {
135 	addr += h;
136 
137 	switch (addr) {
138 	WCASE(DCR_LLFB_BASE, TFT_CTRL);
139 	WDEAD(addr);
140 	}
141 }
142 
143 static uint32_t
144 tft0_read_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr)
145 {
146 	uint32_t 		val;
147 
148 	addr += h;
149 
150 	switch (addr) {
151 	RCASE(DCR_LLFB_BASE, TFT_CTRL);
152 	RDEAD(addr);
153 	}
154 
155 	return (val);
156 }
157 
158 #define DOCHAN(op, channel) \
159 	op(DCR_CDMAC_BASE, channel + CDMAC_NEXT); 	\
160 	op(DCR_CDMAC_BASE, channel + CDMAC_CURADDR); 	\
161 	op(DCR_CDMAC_BASE, channel + CDMAC_CURSIZE); 	\
162 	op(DCR_CDMAC_BASE, channel + CDMAC_CURDESC)
163 
164 static void
165 cdmac0_write_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr,
166     uint32_t val)
167 {
168 	addr += h;
169 
170 	switch (addr) {
171 	WCASE(DCR_CDMAC_BASE, CDMAC_INTR);
172 	WCASE(DCR_CDMAC_BASE, CDMAC_TX0_STAT);
173 	WCASE(DCR_CDMAC_BASE, CDMAC_RX0_STAT);
174 	WCASE(DCR_CDMAC_BASE, CDMAC_TX1_STAT);
175 	WCASE(DCR_CDMAC_BASE, CDMAC_RX1_STAT);
176 	DOCHAN(WCASE, CDMAC_TX0_BASE);
177 	DOCHAN(WCASE, CDMAC_RX0_BASE);
178 	DOCHAN(WCASE, CDMAC_TX1_BASE);
179 	DOCHAN(WCASE, CDMAC_RX1_BASE);
180 	WDEAD(addr);
181 	}
182 }
183 
184 static uint32_t
185 cdmac0_read_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr)
186 {
187 	uint32_t 		val;
188 
189 	addr += h;
190 
191 	switch (addr) {
192 	RCASE(DCR_CDMAC_BASE, CDMAC_INTR);
193 	RCASE(DCR_CDMAC_BASE, CDMAC_TX0_STAT);
194 	RCASE(DCR_CDMAC_BASE, CDMAC_RX0_STAT);
195 	RCASE(DCR_CDMAC_BASE, CDMAC_TX1_STAT);
196 	RCASE(DCR_CDMAC_BASE, CDMAC_RX1_STAT);
197 	DOCHAN(RCASE, CDMAC_TX0_BASE);
198 	DOCHAN(RCASE, CDMAC_RX0_BASE);
199 	DOCHAN(RCASE, CDMAC_TX1_BASE);
200 	DOCHAN(RCASE, CDMAC_RX1_BASE);
201 	RDEAD(addr);
202 	}
203 
204 	return (val);
205 }
206 
207 #undef DOCHAN
208 
209 static void
210 temac0_write_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr,
211     uint32_t val)
212 {
213 	addr += h;
214 
215 	switch (addr) {
216 	WCASE(DCR_TEMAC_BASE, TEMAC_RESET);
217 	WDEAD(addr);
218 	}
219 }
220 
221 static const struct powerpc_bus_space xlcom_bst = {
222 	DCR_BST_BODY(DCR_XLCOM_BASE, xlcom0_read_4, xlcom0_write_4)
223 };
224 
225 static const struct powerpc_bus_space cdmac_bst = {
226 	DCR_BST_BODY(DCR_CDMAC_BASE, cdmac0_read_4, cdmac0_write_4)
227 };
228 
229 static const struct powerpc_bus_space temac_bst = {
230 	DCR_BST_BODY(DCR_TEMAC_BASE, NULL, temac0_write_4)
231 };
232 
233 static const struct powerpc_bus_space tft_bst = {
234 	DCR_BST_BODY(DCR_LLFB_BASE, tft0_read_4, tft0_write_4)
235 };
236 
237 /*
238  * Master device configuration table for GSRD design.
239  */
240 static const struct gsrddev {
241 	const char 		*gdv_name;
242 	const char 		*gdv_attr;
243 	bus_space_tag_t 	gdv_bst;
244 	bus_addr_t 		gdv_addr;
245 	int 			gdv_intr;
246 	int 			gdv_rx_dma;
247 	int 			gdv_tx_dma;
248 } gsrd_devices[] = {
249 	{			/* gsrd_devices[0] */
250 		.gdv_name 	= "xlcom",
251 		.gdv_attr 	= "xcvbus",
252 		.gdv_bst 	= &xlcom_bst,
253 		.gdv_addr 	= 0,
254 		.gdv_intr 	= 0,
255 		.gdv_rx_dma 	= -1,
256 		.gdv_tx_dma 	= -1,
257 	},
258 	{			/* gsrd_devices[1] */
259 		.gdv_name 	= "temac",
260 		.gdv_attr 	= "xcvbus",
261 		.gdv_bst 	= &temac_bst,
262 		.gdv_addr 	= 0,
263 		.gdv_intr 	= 1,
264 		.gdv_rx_dma 	= 3,
265 		.gdv_tx_dma 	= 2,
266 	},
267 	{			/* gsrd_devices[2] */
268 		.gdv_name 	= "tft",
269 		.gdv_attr 	= "llbus",
270 		.gdv_bst 	= &tft_bst,
271 		.gdv_addr 	= 0,
272 		.gdv_intr 	= -1,
273 		.gdv_rx_dma 	= -1,
274 		.gdv_tx_dma 	= 0,
275 	}
276 };
277 
278 static struct ll_dmac *
279 virtex_mpmc_mapdma(int n, struct ll_dmac *chan)
280 {
281 	if (n == -1)
282 		return (NULL);
283 
284 	chan->dmac_iot = &cdmac_bst;
285 	chan->dmac_ctrl_addr = CDMAC_CTRL_BASE(n);
286 	chan->dmac_stat_addr = CDMAC_STAT_BASE(n);
287 	chan->dmac_chan = n;
288 
289 	return (chan);
290 }
291 
292 static int
293 cdmac_intr(void *arg)
294 {
295 	uint32_t 		isr;
296 	int 			i;
297 	int 			did = 0;
298 
299 	isr = bus_space_read_4(&cdmac_bst, 0, CDMAC_INTR);
300 	bus_space_write_4(&cdmac_bst, 0, CDMAC_INTR, isr); 	/* ack */
301 
302 	for (i = 0; i < CDMAC_NCHAN; i++)
303 		if (ISSET(isr, CDMAC_CHAN_INTR(i)) &&
304 		    cdmac_intrs[i] != NULL) {
305 			(cdmac_intrs[i]->cih_func)(cdmac_intrs[i]->cih_arg);
306 			did++;
307 		}
308 
309 	/* XXX: This happens all the time under load... bug? */
310 #if 0
311 	if (did == 0)
312 		aprint_normal("WARNING: stray cdmac isr 0x%x\n", isr);
313 #endif
314 
315 	return (0);
316 }
317 
318 /*
319  * Public interface.
320  */
321 
322 void
323 virtex_autoconf(device_t self, struct plb_attach_args *paa)
324 {
325 	struct xcvbus_attach_args 	vaa;
326 	struct ll_dmac 			rx, tx;
327 	int 				i;
328 
329 	/* Reset all CDMAC engines, disable interrupt. */
330 	bus_space_write_4(&cdmac_bst, 0, CDMAC_STAT_BASE(0), CDMAC_STAT_RESET);
331 	bus_space_write_4(&cdmac_bst, 0, CDMAC_STAT_BASE(1), CDMAC_STAT_RESET);
332 	bus_space_write_4(&cdmac_bst, 0, CDMAC_STAT_BASE(2), CDMAC_STAT_RESET);
333 	bus_space_write_4(&cdmac_bst, 0, CDMAC_STAT_BASE(3), CDMAC_STAT_RESET);
334 	bus_space_write_4(&cdmac_bst, 0, CDMAC_INTR, 0);
335 
336 	vaa.vaa_dmat = paa->plb_dmat;
337 	vaa._vaa_is_dcr = 1; 		/* XXX bst flag */
338 
339 	/* Attach all we have. */
340 	for (i = 0; i < __arraycount(gsrd_devices); i++) {
341 		const struct gsrddev 	*g = &gsrd_devices[i];
342 
343 		vaa.vaa_name 	= g->gdv_name;
344 		vaa.vaa_addr 	= g->gdv_addr;
345 		vaa.vaa_intr 	= g->gdv_intr;
346 		vaa.vaa_iot 	= g->gdv_bst;
347 
348 		vaa.vaa_rx_dmac = virtex_mpmc_mapdma(g->gdv_rx_dma, &rx);
349 		vaa.vaa_tx_dmac = virtex_mpmc_mapdma(g->gdv_tx_dma, &tx);
350 
351 		config_found(self, &vaa, xcvbus_print,
352 		    CFARGS(.attr = g->gdv_attr));
353 	}
354 
355 	/* Setup the dispatch handler. */
356 	cdmac_ih = intr_establish(CDMAC_INTR_LINE, IST_LEVEL, IPL_CDMAC,
357 	    cdmac_intr, NULL);
358 	if (cdmac_ih == NULL)
359 		panic("virtex_autoconf: could not establish cdmac intr");
360 
361 	/* Enable CDMAC interrupt. */
362 	bus_space_write_4(&cdmac_bst, 0, CDMAC_INTR, ~CDMAC_INTR_MIE);
363 	bus_space_write_4(&cdmac_bst, 0, CDMAC_INTR, CDMAC_INTR_MIE);
364 }
365 
366 void *
367 ll_dmac_intr_establish(int chan, void (*func)(void *), void *arg)
368 {
369 	struct cdmac_intr_handle *ih;
370 
371 	KASSERT(chan > 0 && chan < CDMAC_NCHAN);
372 
373 	/* We only allow one handler per channel, somewhat arbitrarily. */
374 	if (cdmac_intrs[chan] != NULL)
375 		return (NULL);
376 
377 	ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
378 	ih->cih_func = func;
379 	ih->cih_arg = arg;
380 
381 	return (cdmac_intrs[chan] = ih);
382 }
383 
384 void
385 ll_dmac_intr_disestablish(int chan, void *handle)
386 {
387 	struct cdmac_intr_handle *ih = handle;
388 	int 			s;
389 
390 	KASSERT(chan > 0 && chan < CDMAC_NCHAN);
391 	KASSERT(cdmac_intrs[chan] == handle);
392 
393 	s = splcdmac();
394 	cdmac_intrs[chan] = NULL;
395 	splx(s);
396 
397 	kmem_free(ih, sizeof(*ih));
398 }
399 
400 int
401 virtex_bus_space_tag(const char *xname, bus_space_tag_t *bst)
402 {
403 	if (strncmp(xname, "xlcom", 5) == 0) {
404 		*bst = &xlcom_bst;
405 		return (0);
406 	}
407 
408 	return (ENODEV);
409 }
410 
411 void
412 virtex_machdep_init(vaddr_t endva, vsize_t maxsz, struct mem_region *phys,
413     struct mem_region *avail)
414 {
415 	/* Nothing to do -- no memory-mapped devices. */
416 }
417 
418 void
419 device_register(device_t dev, void *aux)
420 {
421 	/* Nothing to do -- no property hacks needed. */
422 }
423