1 /* $NetBSD: design_gsrd2.c,v 1.8 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 "opt_virtex.h" 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: design_gsrd2.c,v 1.8 2021/08/07 16:18:52 thorpej Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/device.h> 40 #include <sys/kernel.h> 41 #include <sys/malloc.h> 42 #include <sys/extent.h> 43 #include <sys/cpu.h> 44 #include <sys/bus.h> 45 #include <sys/intr.h> 46 47 #include <machine/powerpc.h> 48 49 #include <powerpc/ibm4xx/cpu.h> 50 #include <powerpc/ibm4xx/tlb.h> 51 #include <powerpc/ibm4xx/dev/plbvar.h> 52 53 #include <evbppc/virtex/dev/xcvbusvar.h> 54 #include <evbppc/virtex/dev/cdmacreg.h> 55 #include <evbppc/virtex/dev/temacreg.h> 56 #include <evbppc/virtex/dev/tftreg.h> 57 58 #include <evbppc/virtex/virtex.h> 59 #include <evbppc/virtex/dcr.h> 60 61 62 #define DCR_TEMAC_BASE 0x0030 63 #define DCR_TFT0_BASE 0x0082 64 #define DCR_TFT1_BASE 0x0086 65 #define DCR_CDMAC_BASE 0x0140 66 67 #define OPB_BASE 0x80000000 /* below are offsets in opb */ 68 #define OPB_XLCOM_BASE 0x010000 69 #define OPB_GPIO_BASE 0x020000 70 #define OPB_PSTWO0_BASE 0x040000 71 #define OPB_PSTWO1_BASE 0x041000 72 #define CDMAC_NCHAN 2 /* cdmac {Tx,Rx} */ 73 #define CDMAC_INTR_LINE 0 74 75 #define TFT_FB_BASE 0x3c00000 76 #define TFT_FB_SIZE (2*1024*1024) 77 78 /* 79 * CDMAC per-channel interrupt handler. CDMAC has one interrupt signal 80 * per two channels on mpmc2, so we have to dispatch channels manually. 81 * 82 * Note: we hardwire priority to IPL_NET, temac(4) is the only device that 83 * needs to service DMA interrupts anyway. 84 */ 85 typedef struct cdmac_intrhand { 86 void (*cih_func)(void *); 87 void *cih_arg; 88 } *cdmac_intrhand_t; 89 90 /* Two instantiated channels, one logical interrupt per direction. */ 91 static struct cdmac_intrhand cdmacintr[CDMAC_NCHAN]; 92 static void *cdmac_ih; 93 94 95 /* 96 * DCR bus space leaf access routines. 97 */ 98 99 #ifndef DESIGN_DFC 100 static void 101 tft0_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_TFT0_BASE, TFT_CTRL); 108 WCASE(DCR_TFT0_BASE, TFT_ADDR); 109 WDEAD(addr); 110 } 111 } 112 113 static uint32_t 114 tft0_read_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr) 115 { 116 uint32_t val; 117 118 addr += h; 119 120 switch (addr) { 121 RCASE(DCR_TFT0_BASE, TFT_CTRL); 122 RCASE(DCR_TFT0_BASE, TFT_ADDR); 123 RDEAD(addr); 124 } 125 126 return (val); 127 } 128 #endif /* !DESIGN_DFC */ 129 130 static void 131 tft1_write_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr, 132 uint32_t val) 133 { 134 addr += h; 135 136 switch (addr) { 137 WCASE(DCR_TFT1_BASE, TFT_CTRL); 138 WCASE(DCR_TFT0_BASE, TFT_ADDR); 139 WDEAD(addr); 140 } 141 } 142 143 static uint32_t 144 tft1_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_TFT1_BASE, TFT_CTRL); 152 RCASE(DCR_TFT0_BASE, TFT_ADDR); 153 RDEAD(addr); 154 } 155 156 return (val); 157 } 158 159 #define DOCHAN(op, base, channel) \ 160 op(base, channel + CDMAC_NEXT); \ 161 op(base, channel + CDMAC_CURADDR); \ 162 op(base, channel + CDMAC_CURSIZE); \ 163 op(base, channel + CDMAC_CURDESC) 164 165 static void 166 cdmac_write_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr, 167 uint32_t val) 168 { 169 addr += h; 170 171 switch (addr) { 172 WCASE(DCR_CDMAC_BASE, CDMAC_STAT_BASE(0)); /* Tx engine */ 173 WCASE(DCR_CDMAC_BASE, CDMAC_STAT_BASE(1)); /* Rx engine */ 174 WCASE(DCR_CDMAC_BASE, CDMAC_INTR); 175 DOCHAN(WCASE, DCR_CDMAC_BASE, CDMAC_CTRL_BASE(0)); 176 DOCHAN(WCASE, DCR_CDMAC_BASE, CDMAC_CTRL_BASE(1)); 177 WDEAD(addr); 178 } 179 } 180 181 static uint32_t 182 cdmac_read_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr) 183 { 184 uint32_t val; 185 186 addr += h; 187 188 switch (addr) { 189 RCASE(DCR_CDMAC_BASE, CDMAC_STAT_BASE(0)); /* Tx engine */ 190 RCASE(DCR_CDMAC_BASE, CDMAC_STAT_BASE(1)); /* Rx engine */ 191 RCASE(DCR_CDMAC_BASE, CDMAC_INTR); 192 DOCHAN(RCASE, DCR_CDMAC_BASE, CDMAC_CTRL_BASE(0)); 193 DOCHAN(RCASE, DCR_CDMAC_BASE, CDMAC_CTRL_BASE(1)); 194 RDEAD(addr); 195 } 196 197 return (val); 198 } 199 200 #undef DOCHAN 201 202 static void 203 temac_write_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr, 204 uint32_t val) 205 { 206 addr += h; 207 208 switch (addr) { 209 WCASE(DCR_TEMAC_BASE, TEMAC_RESET); 210 WDEAD(addr); 211 } 212 } 213 214 static uint32_t 215 temac_read_4(bus_space_tag_t t, bus_space_handle_t h, uint32_t addr) 216 { 217 uint32_t val; 218 219 addr += h; 220 221 switch (addr) { 222 RCASE(DCR_TEMAC_BASE, TEMAC_RESET); 223 RDEAD(addr); 224 } 225 226 return (val); 227 } 228 229 static const struct powerpc_bus_space cdmac_bst = { 230 DCR_BST_BODY(DCR_CDMAC_BASE, cdmac_read_4, cdmac_write_4) 231 }; 232 233 static const struct powerpc_bus_space temac_bst = { 234 DCR_BST_BODY(DCR_TEMAC_BASE, temac_read_4, temac_write_4) 235 }; 236 237 #ifndef DESIGN_DFC 238 static const struct powerpc_bus_space tft0_bst = { 239 DCR_BST_BODY(DCR_TFT0_BASE, tft0_read_4, tft0_write_4) 240 }; 241 #endif 242 243 static const struct powerpc_bus_space tft1_bst = { 244 DCR_BST_BODY(DCR_TFT1_BASE, tft1_read_4, tft1_write_4) 245 }; 246 247 static struct powerpc_bus_space opb_bst = { 248 .pbs_flags = _BUS_SPACE_BIG_ENDIAN|_BUS_SPACE_MEM_TYPE, 249 .pbs_base = 0 /*OPB_BASE*/, 250 .pbs_offset = OPB_BASE, 251 }; 252 253 static char opb_extent_storage[EXTENT_FIXED_STORAGE_SIZE(8)] __aligned(8); 254 255 /* 256 * Master device configuration table for GSRD2 design. 257 */ 258 static const struct gsrddev { 259 const char *gdv_name; 260 const char *gdv_attr; 261 bus_space_tag_t gdv_bst; 262 bus_addr_t gdv_addr; 263 int gdv_intr; 264 int gdv_rx_dma; 265 int gdv_tx_dma; 266 int gdv_dcr; /* XXX bst flag */ 267 } gsrd_devices[] = { 268 { /* gsrd_devices[0] */ 269 .gdv_name = "xlcom", 270 .gdv_attr = "xcvbus", 271 .gdv_bst = &opb_bst, 272 .gdv_addr = OPB_XLCOM_BASE, 273 .gdv_intr = 2, 274 .gdv_rx_dma = -1, 275 .gdv_tx_dma = -1, 276 .gdv_dcr = 0, 277 }, 278 { /* gsrd_devices[1] */ 279 .gdv_name = "temac", 280 .gdv_attr = "xcvbus", 281 .gdv_bst = &temac_bst, 282 .gdv_addr = 0, 283 .gdv_intr = 1, /* unused MII intr */ 284 .gdv_rx_dma = 1, /* cdmac Rx */ 285 .gdv_tx_dma = 0, /* cdmac Tx */ 286 .gdv_dcr = 1, 287 }, 288 #ifndef DESIGN_DFC 289 { /* gsrd_devices[2] */ 290 .gdv_name = "tft", 291 .gdv_attr = "plbus", 292 .gdv_bst = &tft0_bst, 293 .gdv_addr = 0, 294 .gdv_intr = -1, 295 .gdv_rx_dma = -1, 296 .gdv_tx_dma = -1, 297 .gdv_dcr = 1, 298 }, 299 #endif 300 { /* gsrd_devices[2] */ 301 .gdv_name = "tft", 302 .gdv_attr = "plbus", 303 .gdv_bst = &tft1_bst, 304 .gdv_addr = 0, 305 .gdv_intr = -1, 306 .gdv_rx_dma = -1, 307 .gdv_tx_dma = -1, 308 .gdv_dcr = 1, 309 }, 310 #ifdef DESIGN_DFC 311 { /* gsrd_devices[3] */ 312 .gdv_name = "pstwo", 313 .gdv_attr = "xcvbus", 314 .gdv_bst = &opb_bst, 315 .gdv_addr = OPB_PSTWO0_BASE, 316 .gdv_intr = 3, 317 .gdv_rx_dma = -1, 318 .gdv_tx_dma = -1, 319 .gdv_dcr = 0, 320 }, 321 { /* gsrd_devices[4] */ 322 .gdv_name = "pstwo", 323 .gdv_attr = "xcvbus", 324 .gdv_bst = &opb_bst, 325 .gdv_addr = OPB_PSTWO1_BASE, 326 .gdv_intr = 4, 327 .gdv_rx_dma = -1, 328 .gdv_tx_dma = -1, 329 .gdv_dcr = 0, 330 }, 331 #endif 332 }; 333 334 static struct ll_dmac * 335 virtex_mpmc_mapdma(int idx, struct ll_dmac *chan) 336 { 337 if (idx == -1) 338 return (NULL); 339 340 KASSERT(idx >= 0 && idx < CDMAC_NCHAN); 341 342 chan->dmac_iot = &cdmac_bst; 343 chan->dmac_ctrl_addr = CDMAC_CTRL_BASE(idx); 344 chan->dmac_stat_addr = CDMAC_STAT_BASE(idx); 345 chan->dmac_chan = idx; 346 347 return (chan); 348 } 349 350 static int 351 cdmac_intr(void *arg) 352 { 353 uint32_t isr; 354 int did = 0; 355 356 isr = bus_space_read_4(&cdmac_bst, 0, CDMAC_INTR); 357 358 if (ISSET(isr, CDMAC_INTR_TX0) && cdmacintr[0].cih_func) { 359 (cdmacintr[0].cih_func)(cdmacintr[0].cih_arg); 360 did++; 361 } 362 if (ISSET(isr, CDMAC_INTR_RX0) && cdmacintr[1].cih_func) { 363 (cdmacintr[1].cih_func)(cdmacintr[1].cih_arg); 364 did++; 365 } 366 367 bus_space_write_4(&cdmac_bst, 0, CDMAC_INTR, isr); /* ack */ 368 369 /* XXX This still happens all the time under load. */ 370 #if 0 371 if (did == 0) 372 aprint_normal("WARNING: stray cdmac isr 0x%x\n", isr); 373 #endif 374 return (0); 375 } 376 377 /* 378 * Public interface. 379 */ 380 381 void 382 virtex_autoconf(device_t self, struct plb_attach_args *paa) 383 { 384 385 struct xcvbus_attach_args vaa; 386 struct ll_dmac rx, tx; 387 int i; 388 389 /* Reset DMA channels. */ 390 bus_space_write_4(&cdmac_bst, 0, CDMAC_STAT_BASE(0), CDMAC_STAT_RESET); 391 bus_space_write_4(&cdmac_bst, 0, CDMAC_STAT_BASE(1), CDMAC_STAT_RESET); 392 bus_space_write_4(&cdmac_bst, 0, CDMAC_INTR, 0); 393 394 vaa.vaa_dmat = paa->plb_dmat; 395 396 for (i = 0; i < __arraycount(gsrd_devices); i++) { 397 const struct gsrddev *g = &gsrd_devices[i]; 398 399 vaa._vaa_is_dcr = g->gdv_dcr; /* XXX bst flag */ 400 vaa.vaa_name = g->gdv_name; 401 vaa.vaa_addr = g->gdv_addr; 402 vaa.vaa_intr = g->gdv_intr; 403 vaa.vaa_iot = g->gdv_bst; 404 405 vaa.vaa_rx_dmac = virtex_mpmc_mapdma(g->gdv_rx_dma, &rx); 406 vaa.vaa_tx_dmac = virtex_mpmc_mapdma(g->gdv_tx_dma, &tx); 407 408 config_found(self, &vaa, xcvbus_print, 409 CFARGS(.iattr = g->gdv_attr)); 410 } 411 412 /* Setup the dispatch handler. */ 413 cdmac_ih = intr_establish(CDMAC_INTR_LINE, IST_LEVEL, IPL_NET, 414 cdmac_intr, NULL); 415 if (cdmac_ih == NULL) 416 panic("virtex_mpmc_done: could not establish cdmac intr"); 417 418 /* Clear (XXX?) and enable interrupts. */ 419 bus_space_write_4(&cdmac_bst, 0, CDMAC_INTR, ~CDMAC_INTR_MIE); 420 bus_space_write_4(&cdmac_bst, 0, CDMAC_INTR, CDMAC_INTR_MIE); 421 } 422 423 void * 424 ll_dmac_intr_establish(int chan, void (*handler)(void *), void *arg) 425 { 426 KASSERT(chan >= 0 && chan < CDMAC_NCHAN); 427 KASSERT(cdmacintr[chan].cih_func == NULL); 428 KASSERT(cdmacintr[chan].cih_arg == NULL); 429 430 cdmacintr[chan].cih_func = handler; 431 cdmacintr[chan].cih_arg = arg; 432 433 return (&cdmacintr[chan]); 434 } 435 436 void 437 ll_dmac_intr_disestablish(int chan, void *handle) 438 { 439 int s; 440 441 KASSERT(chan >= 0 && chan < CDMAC_NCHAN); 442 KASSERT(&cdmacintr[chan] == handle); 443 444 s = splnet(); 445 cdmacintr[chan].cih_func = NULL; 446 cdmacintr[chan].cih_arg = NULL; 447 splx(s); 448 } 449 450 int 451 virtex_bus_space_tag(const char *xname, bus_space_tag_t *bst) 452 { 453 if (strncmp(xname, "xlcom", 5) == 0) { 454 *bst = &opb_bst; 455 return (0); 456 } 457 458 return (ENODEV); 459 } 460 461 void 462 virtex_machdep_init(vaddr_t endva, vsize_t maxsz, struct mem_region *phys, 463 struct mem_region *avail) 464 { 465 ppc4xx_tlb_reserve(OPB_BASE, endva, maxsz, TLB_I | TLB_G); 466 endva += maxsz; 467 468 opb_bst.pbs_limit = maxsz; 469 470 if (bus_space_init(&opb_bst, "opbtag", opb_extent_storage, 471 sizeof(opb_extent_storage))) 472 panic("virtex_machdep_init: failed to initialize opb_bst"); 473 474 /* 475 * The TFT controller is broken, we can't change FB address. 476 * Hardwire it at predefined base address, create uncached 477 * mapping. 478 */ 479 480 avail[0].size = TFT_FB_BASE - avail[0].start; 481 ppc4xx_tlb_reserve(TFT_FB_BASE, endva, TFT_FB_SIZE, TLB_I | TLB_G); 482 } 483 484 void 485 device_register(device_t dev, void *aux) 486 { 487 prop_number_t pn; 488 void *fb; 489 490 if (strncmp(device_xname(dev), "tft0", 4) == 0) { 491 fb = ppc4xx_tlb_mapiodev(TFT_FB_BASE, TFT_FB_SIZE); 492 if (fb == NULL) 493 panic("device_register: framebuffer mapping gone!\n"); 494 495 pn = prop_number_create_unsigned_integer(TFT_FB_BASE); 496 if (pn == NULL) { 497 printf("WARNING: could not allocate virtex-tft-pa\n"); 498 return ; 499 } 500 if (prop_dictionary_set(device_properties(dev), 501 "virtex-tft-pa", pn) != true) 502 printf("WARNING: could not set virtex-tft-pa\n"); 503 prop_object_release(pn); 504 505 pn = prop_number_create_unsigned_integer((uintptr_t)fb); 506 if (pn == NULL) { 507 printf("WARNING: could not allocate virtex-tft-va\n"); 508 return ; 509 } 510 if (prop_dictionary_set(device_properties(dev), 511 "virtex-tft-va", pn) != true) 512 printf("WARNING: could not set virtex-tft-va\n"); 513 prop_object_release(pn); 514 } 515 } 516