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