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