1 /* $NetBSD: design_gsrd1.c,v 1.8 2022/02/17 00:54:51 riastradh 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.8 2022/02/17 00:54:51 riastradh 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
xlcom0_write_4(bus_space_tag_t t,bus_space_handle_t h,uint32_t addr,uint32_t val)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
xlcom0_read_4(bus_space_tag_t t,bus_space_handle_t h,uint32_t addr)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
tft0_write_4(bus_space_tag_t t,bus_space_handle_t h,uint32_t addr,uint32_t val)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
tft0_read_4(bus_space_tag_t t,bus_space_handle_t h,uint32_t addr)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
cdmac0_write_4(bus_space_tag_t t,bus_space_handle_t h,uint32_t addr,uint32_t val)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
cdmac0_read_4(bus_space_tag_t t,bus_space_handle_t h,uint32_t addr)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
temac0_write_4(bus_space_tag_t t,bus_space_handle_t h,uint32_t addr,uint32_t val)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 *
virtex_mpmc_mapdma(int n,struct ll_dmac * chan)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
cdmac_intr(void * arg)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
virtex_autoconf(device_t self,struct plb_attach_args * paa)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(.iattr = 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 *
ll_dmac_intr_establish(int chan,void (* func)(void *),void * arg)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
ll_dmac_intr_disestablish(int chan,void * handle)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
virtex_bus_space_tag(const char * xname,bus_space_tag_t * bst)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
virtex_machdep_init(vaddr_t endva,vsize_t maxsz,struct mem_region * phys,struct mem_region * avail)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
device_register(device_t dev,void * aux)419 device_register(device_t dev, void *aux)
420 {
421 /* Nothing to do -- no property hacks needed. */
422 }
423