sbbc.c revision 206451
1/* $OpenBSD: sbbc.c,v 1.7 2009/11/09 17:53:39 nicm Exp $ */ 2/* 3 * Copyright (c) 2008 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17/*- 18 * Copyright (c) 2010 Marius Strobl <marius@FreeBSD.org> 19 * All rights reserved. 20 * 21 * Redistribution and use in source and binary forms, with or without 22 * modification, are permitted provided that the following conditions 23 * are met: 24 * 1. Redistributions of source code must retain the above copyright 25 * notice, this list of conditions and the following disclaimer. 26 * 2. Redistributions in binary form must reproduce the above copyright 27 * notice, this list of conditions and the following disclaimer in the 28 * documentation and/or other materials provided with the distribution. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 */ 42 43#include <sys/cdefs.h> 44__FBSDID("$FreeBSD: head/sys/sparc64/pci/sbbc.c 206451 2010-04-10 11:52:12Z marius $"); 45 46#include <sys/param.h> 47#include <sys/systm.h> 48#include <sys/bus.h> 49#include <sys/clock.h> 50#include <sys/endian.h> 51#include <sys/kernel.h> 52#include <sys/lock.h> 53#include <sys/module.h> 54#include <sys/mutex.h> 55#include <sys/resource.h> 56#include <sys/rman.h> 57 58#include <dev/ofw/ofw_bus.h> 59#include <dev/ofw/openfirm.h> 60 61#include <machine/bus.h> 62#include <machine/cpu.h> 63#include <machine/resource.h> 64 65#include <dev/pci/pcireg.h> 66#include <dev/pci/pcivar.h> 67#include <dev/uart/uart.h> 68#include <dev/uart/uart_cpu.h> 69#include <dev/uart/uart_bus.h> 70 71#include "clock_if.h" 72#include "uart_if.h" 73 74#define SBBC_PCI_BAR PCIR_BAR(0) 75#define SBBC_PCI_VENDOR 0x108e 76#define SBBC_PCI_PRODUCT 0xc416 77 78#define SBBC_REGS_OFFSET 0x800000 79#define SBBC_REGS_SIZE 0x6230 80#define SBBC_EPLD_OFFSET 0x8e0000 81#define SBBC_EPLD_SIZE 0x20 82#define SBBC_SRAM_OFFSET 0x900000 83#define SBBC_SRAM_SIZE 0x20000 /* 128KB SRAM */ 84 85#define SBBC_PCI_INT_STATUS 0x2320 86#define SBBC_PCI_INT_ENABLE 0x2330 87#define SBBC_PCI_ENABLE_INT_A 0x11 88 89#define SBBC_EPLD_INTERRUPT 0x13 90#define SBBC_EPLD_INTERRUPT_ON 0x01 91 92#define SBBC_SRAM_CONS_IN 0x00000001 93#define SBBC_SRAM_CONS_OUT 0x00000002 94#define SBBC_SRAM_CONS_BRK 0x00000004 95#define SBBC_SRAM_CONS_SPACE_IN 0x00000008 96#define SBBC_SRAM_CONS_SPACE_OUT 0x00000010 97 98#define SBBC_TAG_KEY_SIZE 8 99#define SBBC_TAG_KEY_SCSOLIE "SCSOLIE" /* SC -> OS int. enable */ 100#define SBBC_TAG_KEY_SCSOLIR "SCSOLIR" /* SC -> OS int. reason */ 101#define SBBC_TAG_KEY_SOLCONS "SOLCONS" /* OS console buffer */ 102#define SBBC_TAG_KEY_SOLSCIE "SOLSCIE" /* OS -> SC int. enable */ 103#define SBBC_TAG_KEY_SOLSCIR "SOLSCIR" /* OS -> SC int. reason */ 104#define SBBC_TAG_KEY_TODDATA "TODDATA" /* OS TOD struct */ 105#define SBBC_TAG_OFF(x) offsetof(struct sbbc_sram_tag, x) 106 107struct sbbc_sram_tag { 108 char tag_key[SBBC_TAG_KEY_SIZE]; 109 uint32_t tag_size; 110 uint32_t tag_offset; 111} __packed; 112 113#define SBBC_TOC_MAGIC "TOCSRAM" 114#define SBBC_TOC_MAGIC_SIZE 8 115#define SBBC_TOC_TAGS_MAX 32 116#define SBBC_TOC_OFF(x) offsetof(struct sbbc_sram_toc, x) 117 118struct sbbc_sram_toc { 119 char toc_magic[SBBC_TOC_MAGIC_SIZE]; 120 uint8_t toc_reserved; 121 uint8_t toc_type; 122 uint16_t toc_version; 123 uint32_t toc_ntags; 124 struct sbbc_sram_tag toc_tag[SBBC_TOC_TAGS_MAX]; 125} __packed; 126 127#define SBBC_TOD_MAGIC 0x54443100 /* "TD1" */ 128#define SBBC_TOD_VERSION 1 129#define SBBC_TOD_OFF(x) offsetof(struct sbbc_sram_tod, x) 130 131struct sbbc_sram_tod { 132 uint32_t tod_magic; 133 uint32_t tod_version; 134 uint64_t tod_time; 135 uint64_t tod_skew; 136 uint32_t tod_reserved; 137 uint32_t tod_heartbeat; 138 uint32_t tod_timeout; 139} __packed; 140 141#define SBBC_CONS_MAGIC 0x434f4e00 /* "CON" */ 142#define SBBC_CONS_VERSION 1 143#define SBBC_CONS_OFF(x) offsetof(struct sbbc_sram_cons, x) 144 145struct sbbc_sram_cons { 146 uint32_t cons_magic; 147 uint32_t cons_version; 148 uint32_t cons_size; 149 150 uint32_t cons_in_begin; 151 uint32_t cons_in_end; 152 uint32_t cons_in_rdptr; 153 uint32_t cons_in_wrptr; 154 155 uint32_t cons_out_begin; 156 uint32_t cons_out_end; 157 uint32_t cons_out_rdptr; 158 uint32_t cons_out_wrptr; 159} __packed; 160 161struct sbbc_softc { 162 struct resource *sc_res; 163}; 164 165#define SBBC_READ_N(wdth, offs) \ 166 bus_space_read_ ## wdth((bst), (bsh), (offs)) 167#define SBBC_WRITE_N(wdth, offs, val) \ 168 bus_space_write_ ## wdth((bst), (bsh), (offs), (val)) 169 170#define SBBC_READ_1(offs) \ 171 SBBC_READ_N(1, (offs)) 172#define SBBC_READ_2(offs) \ 173 bswap16(SBBC_READ_N(2, (offs))) 174#define SBBC_READ_4(offs) \ 175 bswap32(SBBC_READ_N(4, (offs))) 176#define SBBC_READ_8(offs) \ 177 bswap64(SBBC_READ_N(8, (offs))) 178#define SBBC_WRITE_1(offs, val) \ 179 SBBC_WRITE_N(1, (offs), (val)) 180#define SBBC_WRITE_2(offs, val) \ 181 SBBC_WRITE_N(2, (offs), bswap16(val)) 182#define SBBC_WRITE_4(offs, val) \ 183 SBBC_WRITE_N(4, (offs), bswap32(val)) 184#define SBBC_WRITE_8(offs, val) \ 185 SBBC_WRITE_N(8, (offs), bswap64(val)) 186 187#define SBBC_REGS_READ_1(offs) \ 188 SBBC_READ_1((offs) + SBBC_REGS_OFFSET) 189#define SBBC_REGS_READ_2(offs) \ 190 SBBC_READ_2((offs) + SBBC_REGS_OFFSET) 191#define SBBC_REGS_READ_4(offs) \ 192 SBBC_READ_4((offs) + SBBC_REGS_OFFSET) 193#define SBBC_REGS_READ_8(offs) \ 194 SBBC_READ_8((offs) + SBBC_REGS_OFFSET) 195#define SBBC_REGS_WRITE_1(offs, val) \ 196 SBBC_WRITE_1((offs) + SBBC_REGS_OFFSET, (val)) 197#define SBBC_REGS_WRITE_2(offs, val) \ 198 SBBC_WRITE_2((offs) + SBBC_REGS_OFFSET, (val)) 199#define SBBC_REGS_WRITE_4(offs, val) \ 200 SBBC_WRITE_4((offs) + SBBC_REGS_OFFSET, (val)) 201#define SBBC_REGS_WRITE_8(offs, val) \ 202 SBBC_WRITE_8((offs) + SBBC_REGS_OFFSET, (val)) 203 204#define SBBC_EPLD_READ_1(offs) \ 205 SBBC_READ_1((offs) + SBBC_EPLD_OFFSET) 206#define SBBC_EPLD_READ_2(offs) \ 207 SBBC_READ_2((offs) + SBBC_EPLD_OFFSET) 208#define SBBC_EPLD_READ_4(offs) \ 209 SBBC_READ_4((offs) + SBBC_EPLD_OFFSET) 210#define SBBC_EPLD_READ_8(offs) \ 211 SBBC_READ_8((offs) + SBBC_EPLD_OFFSET) 212#define SBBC_EPLD_WRITE_1(offs, val) \ 213 SBBC_WRITE_1((offs) + SBBC_EPLD_OFFSET, (val)) 214#define SBBC_EPLD_WRITE_2(offs, val) \ 215 SBBC_WRITE_2((offs) + SBBC_EPLD_OFFSET, (val)) 216#define SBBC_EPLD_WRITE_4(offs, val) \ 217 SBBC_WRITE_4((offs) + SBBC_EPLD_OFFSET, (val)) 218#define SBBC_EPLD_WRITE_8(offs, val) \ 219 SBBC_WRITE_8((offs) + SBBC_EPLD_OFFSET, (val)) 220 221#define SBBC_SRAM_READ_1(offs) \ 222 SBBC_READ_1((offs) + SBBC_SRAM_OFFSET) 223#define SBBC_SRAM_READ_2(offs) \ 224 SBBC_READ_2((offs) + SBBC_SRAM_OFFSET) 225#define SBBC_SRAM_READ_4(offs) \ 226 SBBC_READ_4((offs) + SBBC_SRAM_OFFSET) 227#define SBBC_SRAM_READ_8(offs) \ 228 SBBC_READ_8((offs) + SBBC_SRAM_OFFSET) 229#define SBBC_SRAM_WRITE_1(offs, val) \ 230 SBBC_WRITE_1((offs) + SBBC_SRAM_OFFSET, (val)) 231#define SBBC_SRAM_WRITE_2(offs, val) \ 232 SBBC_WRITE_2((offs) + SBBC_SRAM_OFFSET, (val)) 233#define SBBC_SRAM_WRITE_4(offs, val) \ 234 SBBC_WRITE_4((offs) + SBBC_SRAM_OFFSET, (val)) 235#define SBBC_SRAM_WRITE_8(offs, val) \ 236 SBBC_WRITE_8((offs) + SBBC_SRAM_OFFSET, (val)) 237 238#define SUNW_SETCONSINPUT "SUNW,set-console-input" 239#define SUNW_SETCONSINPUT_CLNT "CON_CLNT" 240#define SUNW_SETCONSINPUT_OBP "CON_OBP" 241 242static u_int sbbc_console; 243 244static uint32_t sbbc_scsolie; 245static uint32_t sbbc_scsolir; 246static uint32_t sbbc_solcons; 247static uint32_t sbbc_solscie; 248static uint32_t sbbc_solscir; 249static uint32_t sbbc_toddata; 250 251/* 252 * internal helpers 253 */ 254static int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh); 255static inline void sbbc_send_intr(bus_space_tag_t bst, 256 bus_space_handle_t bsh); 257static const char *sbbc_serengeti_set_console_input(char *new); 258 259/* 260 * SBBC PCI interface 261 */ 262static bus_alloc_resource_t sbbc_bus_alloc_resource; 263static bus_release_resource_t sbbc_bus_release_resource; 264static bus_get_resource_list_t sbbc_bus_get_resource_list; 265static bus_setup_intr_t sbbc_bus_setup_intr; 266static bus_teardown_intr_t sbbc_bus_teardown_intr; 267 268static device_attach_t sbbc_pci_attach; 269static device_probe_t sbbc_pci_probe; 270 271static clock_gettime_t sbbc_tod_gettime; 272static clock_settime_t sbbc_tod_settime; 273 274static device_method_t sbbc_pci_methods[] = { 275 /* Device interface */ 276 DEVMETHOD(device_probe, sbbc_pci_probe), 277 DEVMETHOD(device_attach, sbbc_pci_attach), 278 279 DEVMETHOD(bus_print_child, bus_generic_print_child), 280 DEVMETHOD(bus_alloc_resource, sbbc_bus_alloc_resource), 281 DEVMETHOD(bus_release_resource, sbbc_bus_release_resource), 282 DEVMETHOD(bus_setup_intr, sbbc_bus_setup_intr), 283 DEVMETHOD(bus_teardown_intr, sbbc_bus_teardown_intr), 284 DEVMETHOD(bus_get_resource_list, sbbc_bus_get_resource_list), 285 286 /* clock interface */ 287 DEVMETHOD(clock_gettime, sbbc_tod_gettime), 288 DEVMETHOD(clock_settime, sbbc_tod_settime), 289 290 KOBJMETHOD_END 291}; 292 293static devclass_t sbbc_devclass; 294 295DEFINE_CLASS_0(sbbc, sbbc_driver, sbbc_pci_methods, sizeof(struct sbbc_softc)); 296DRIVER_MODULE(sbbc, pci, sbbc_driver, sbbc_devclass, 0, 0); 297 298static int 299sbbc_pci_probe(device_t dev) 300{ 301 302 if (pci_get_vendor(dev) == SBBC_PCI_VENDOR && 303 pci_get_device(dev) == SBBC_PCI_PRODUCT) { 304 device_set_desc(dev, "Sun BootBus controller"); 305 return (BUS_PROBE_DEFAULT); 306 } 307 return (ENXIO); 308} 309 310static int 311sbbc_pci_attach(device_t dev) 312{ 313 struct sbbc_softc *sc; 314 struct timespec ts; 315 device_t child; 316 bus_space_tag_t bst; 317 bus_space_handle_t bsh; 318 phandle_t node; 319 int error, rid; 320 uint32_t val; 321 322 /* Nothing to to if we're not the chosen one. */ 323 if ((node = OF_finddevice("/chosen")) == -1) { 324 device_printf(dev, "failed to find /chosen\n"); 325 return (ENXIO); 326 } 327 if (OF_getprop(node, "iosram", &node, sizeof(node)) == -1) { 328 device_printf(dev, "failed to get iosram\n"); 329 return (ENXIO); 330 } 331 if (node != ofw_bus_get_node(dev)) 332 return (0); 333 334 sc = device_get_softc(dev); 335 rid = SBBC_PCI_BAR; 336 /* 337 * Note that we don't activate the resource so it's not mapped twice 338 * but only once by the the firmware. 339 */ 340 sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 0); 341 if (sc->sc_res == NULL) { 342 device_printf(dev, "failed to allocate resources\n"); 343 return (ENXIO); 344 } 345 bst = rman_get_bustag(sc->sc_res); 346 bsh = rman_get_bushandle(sc->sc_res); 347 if (sbbc_console != 0) { 348 /* Once again the interrupt pin isn't set. */ 349 if (pci_get_intpin(dev) == 0) 350 pci_set_intpin(dev, 1); 351 child = device_add_child(dev, NULL, -1); 352 if (child == NULL) 353 device_printf(dev, "failed to add UART device\n"); 354 error = bus_generic_attach(dev); 355 if (error != 0) 356 device_printf(dev, "failed to attach UART device\n"); 357 } else { 358 error = sbbc_parse_toc(rman_get_bustag(sc->sc_res), 359 rman_get_bushandle(sc->sc_res)); 360 if (error != 0) { 361 device_printf(dev, "failed to parse TOC\n"); 362 if (sbbc_console != 0) { 363 bus_release_resource(dev, SYS_RES_MEMORY, rid, 364 sc->sc_res); 365 return (error); 366 } 367 } 368 } 369 if (sbbc_toddata != 0) { 370 if ((val = SBBC_SRAM_READ_4(sbbc_toddata + 371 SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC) 372 device_printf(dev, "invalid TOD magic %#x\n", val); 373 else if ((val = SBBC_SRAM_READ_4(sbbc_toddata + 374 SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION) 375 device_printf(dev, "invalid TOD version %#x\n", val); 376 else { 377 clock_register(dev, 1000000); /* 1 sec. resolution */ 378 if (bootverbose) { 379 sbbc_tod_gettime(dev, &ts); 380 device_printf(dev, 381 "current time: %ld.%09ld\n", 382 (long)ts.tv_sec, ts.tv_nsec); 383 } 384 } 385 } 386 return (0); 387} 388 389/* 390 * Note that the bus methods don't pass-through the uart(4) requests but act 391 * as if they would come from sbbc(4) in order to avoid complications with 392 * pci(4) (actually, uart(4) isn't a real child but rather a function of 393 * sbbc(4) anyway). 394 */ 395 396static struct resource * 397sbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type, 398 int *rid, u_long start, u_long end, u_long count, u_int flags) 399{ 400 struct sbbc_softc *sc; 401 402 sc = device_get_softc(dev); 403 switch (type) { 404 case SYS_RES_IRQ: 405 return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, type, 406 rid, start, end, count, flags)); 407 case SYS_RES_MEMORY: 408 return (sc->sc_res); 409 default: 410 return (NULL); 411 /* NOTREACHED */ 412 } 413} 414 415static int 416sbbc_bus_release_resource(device_t dev, device_t child __unused, int type, 417 int rid, struct resource *res) 418{ 419 420 if (type == SYS_RES_IRQ) 421 return (BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, 422 type, rid, res)); 423 return (0); 424} 425 426static struct resource_list * 427sbbc_bus_get_resource_list(device_t dev, device_t child __unused) 428{ 429 430 return (BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev)); 431} 432 433static int 434sbbc_bus_setup_intr(device_t dev, device_t child __unused, 435 struct resource *res, int flags, driver_filter_t *filt, 436 driver_intr_t *intr, void *arg, void **cookiep) 437{ 438 439 return (BUS_SETUP_INTR(device_get_parent(dev), dev, res, flags, filt, 440 intr, arg, cookiep)); 441} 442 443static int 444sbbc_bus_teardown_intr(device_t dev, device_t child __unused, 445 struct resource *res, void *cookie) 446{ 447 448 return (BUS_TEARDOWN_INTR(device_get_parent(dev), dev, res, cookie)); 449} 450 451/* 452 * internal helpers 453 */ 454static int 455sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh) 456{ 457 char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)]; 458 bus_size_t tag; 459 phandle_t node; 460 uint32_t off, sram_toc; 461 u_int i, tags; 462 463 if ((node = OF_finddevice("/chosen")) == -1) 464 return (ENXIO); 465 /* SRAM TOC offset defaults to 0. */ 466 if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0) 467 sram_toc = 0; 468 469 bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc + 470 SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE); 471 buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0'; 472 if (strcmp(buf, SBBC_TOC_MAGIC) != 0) 473 return (ENXIO); 474 475 tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags)); 476 for (i = 0; i < tags; i++) { 477 tag = sram_toc + SBBC_TOC_OFF(toc_tag) + 478 i * sizeof(struct sbbc_sram_tag); 479 bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag + 480 SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE); 481 buf[SBBC_TAG_KEY_SIZE - 1] = '\0'; 482 off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset)); 483 if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0) 484 sbbc_scsolie = off; 485 else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0) 486 sbbc_scsolir = off; 487 else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0) 488 sbbc_solcons = off; 489 else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0) 490 sbbc_solscie = off; 491 else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0) 492 sbbc_solscir = off; 493 else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0) 494 sbbc_toddata = off; 495 } 496 return (0); 497} 498 499static const char * 500sbbc_serengeti_set_console_input(char *new) 501{ 502 struct { 503 cell_t name; 504 cell_t nargs; 505 cell_t nreturns; 506 cell_t new; 507 cell_t old; 508 } args = { 509 (cell_t)SUNW_SETCONSINPUT, 510 1, 511 1, 512 }; 513 514 args.new = (cell_t)new; 515 if (ofw_entry(&args) == -1) 516 return (NULL); 517 return ((const char *)args.old); 518} 519 520static inline void 521sbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh) 522{ 523 524 SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON); 525 bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1, 526 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 527} 528 529/* 530 * TOD interface 531 */ 532static int 533sbbc_tod_gettime(device_t dev, struct timespec *ts) 534{ 535 struct sbbc_softc *sc; 536 bus_space_tag_t bst; 537 bus_space_handle_t bsh; 538 539 sc = device_get_softc(dev); 540 bst = rman_get_bustag(sc->sc_res); 541 bsh = rman_get_bushandle(sc->sc_res); 542 543 ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) + 544 SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew)); 545 ts->tv_nsec = 0; 546 return (0); 547} 548 549static int 550sbbc_tod_settime(device_t dev, struct timespec *ts) 551{ 552 struct sbbc_softc *sc; 553 bus_space_tag_t bst; 554 bus_space_handle_t bsh; 555 556 sc = device_get_softc(dev); 557 bst = rman_get_bustag(sc->sc_res); 558 bsh = rman_get_bushandle(sc->sc_res); 559 560 SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec - 561 SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time))); 562 return (0); 563} 564 565/* 566 * UART bus front-end 567 */ 568static device_probe_t sbbc_uart_sbbc_probe; 569 570static device_method_t sbbc_uart_sbbc_methods[] = { 571 /* Device interface */ 572 DEVMETHOD(device_probe, sbbc_uart_sbbc_probe), 573 DEVMETHOD(device_attach, uart_bus_attach), 574 DEVMETHOD(device_detach, uart_bus_detach), 575 576 KOBJMETHOD_END 577}; 578 579DEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods, 580 sizeof(struct uart_softc)); 581DRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, 0, 0); 582 583static int 584sbbc_uart_sbbc_probe(device_t dev) 585{ 586 struct uart_softc *sc; 587 588 sc = device_get_softc(dev); 589 sc->sc_class = &uart_sbbc_class; 590 device_set_desc(dev, "Serengeti console"); 591 return (uart_bus_probe(dev, 0, 0, SBBC_PCI_BAR, 0)); 592} 593 594/* 595 * Low-level UART interface 596 */ 597static int sbbc_uart_probe(struct uart_bas *bas); 598static void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits, 599 int stopbits, int parity); 600static void sbbc_uart_term(struct uart_bas *bas); 601static void sbbc_uart_putc(struct uart_bas *bas, int c); 602static int sbbc_uart_rxready(struct uart_bas *bas); 603static int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx); 604 605static struct uart_ops sbbc_uart_ops = { 606 .probe = sbbc_uart_probe, 607 .init = sbbc_uart_init, 608 .term = sbbc_uart_term, 609 .putc = sbbc_uart_putc, 610 .rxready = sbbc_uart_rxready, 611 .getc = sbbc_uart_getc, 612}; 613 614static int 615sbbc_uart_probe(struct uart_bas *bas) 616{ 617 bus_space_tag_t bst; 618 bus_space_handle_t bsh; 619 int error; 620 621 sbbc_console = 1; 622 bst = bas->bst; 623 bsh = bas->bsh; 624 error = sbbc_parse_toc(bst, bsh); 625 if (error != 0) 626 return (error); 627 628 if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 || 629 sbbc_solscie == 0 || sbbc_solscir == 0) 630 return (ENXIO); 631 632 if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) != 633 SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons + 634 SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION) 635 return (ENXIO); 636 return (0); 637} 638 639static void 640sbbc_uart_init(struct uart_bas *bas, int baudrate __unused, 641 int databits __unused, int stopbits __unused, int parity __unused) 642{ 643 bus_space_tag_t bst; 644 bus_space_handle_t bsh; 645 646 bst = bas->bst; 647 bsh = bas->bsh; 648 649 /* Enable output to and space in from the SC interrupts. */ 650 SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) | 651 SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN); 652 uart_barrier(bas); 653 654 /* Take over the console input. */ 655 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT); 656} 657 658static void 659sbbc_uart_term(struct uart_bas *bas __unused) 660{ 661 662 /* Give back the console input. */ 663 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); 664} 665 666static void 667sbbc_uart_putc(struct uart_bas *bas, int c) 668{ 669 bus_space_tag_t bst; 670 bus_space_handle_t bsh; 671 uint32_t wrptr; 672 673 bst = bas->bst; 674 bsh = bas->bsh; 675 676 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 677 SBBC_CONS_OFF(cons_out_wrptr)); 678 SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c); 679 uart_barrier(bas); 680 if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons + 681 SBBC_CONS_OFF(cons_out_end))) 682 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 683 SBBC_CONS_OFF(cons_out_begin)); 684 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr), 685 wrptr); 686 uart_barrier(bas); 687 688 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 689 SBBC_SRAM_CONS_OUT); 690 uart_barrier(bas); 691 sbbc_send_intr(bst, bsh); 692} 693 694static int 695sbbc_uart_rxready(struct uart_bas *bas) 696{ 697 bus_space_tag_t bst; 698 bus_space_handle_t bsh; 699 700 bst = bas->bst; 701 bsh = bas->bsh; 702 703 if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) == 704 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr))) 705 return (0); 706 return (1); 707} 708 709static int 710sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) 711{ 712 bus_space_tag_t bst; 713 bus_space_handle_t bsh; 714 int c; 715 uint32_t rdptr; 716 717 bst = bas->bst; 718 bsh = bas->bsh; 719 720 uart_lock(hwmtx); 721 722 while (sbbc_uart_rxready(bas) == 0) { 723 uart_unlock(hwmtx); 724 DELAY(4); 725 uart_lock(hwmtx); 726 } 727 728 rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)); 729 c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr); 730 uart_barrier(bas); 731 if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons + 732 SBBC_CONS_OFF(cons_in_end))) 733 rdptr = SBBC_SRAM_READ_4(sbbc_solcons + 734 SBBC_CONS_OFF(cons_in_begin)); 735 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), 736 rdptr); 737 uart_barrier(bas); 738 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 739 SBBC_SRAM_CONS_SPACE_IN); 740 uart_barrier(bas); 741 sbbc_send_intr(bst, bsh); 742 743 uart_unlock(hwmtx); 744 return (c); 745} 746 747/* 748 * High-level UART interface 749 */ 750static int sbbc_uart_bus_attach(struct uart_softc *sc); 751static int sbbc_uart_bus_detach(struct uart_softc *sc); 752static int sbbc_uart_bus_flush(struct uart_softc *sc, int what); 753static int sbbc_uart_bus_getsig(struct uart_softc *sc); 754static int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, 755 intptr_t data); 756static int sbbc_uart_bus_ipend(struct uart_softc *sc); 757static int sbbc_uart_bus_param(struct uart_softc *sc, int baudrate, 758 int databits, int stopbits, int parity); 759static int sbbc_uart_bus_probe(struct uart_softc *sc); 760static int sbbc_uart_bus_receive(struct uart_softc *sc); 761static int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig); 762static int sbbc_uart_bus_transmit(struct uart_softc *sc); 763 764static kobj_method_t sbbc_uart_methods[] = { 765 KOBJMETHOD(uart_attach, sbbc_uart_bus_attach), 766 KOBJMETHOD(uart_detach, sbbc_uart_bus_detach), 767 KOBJMETHOD(uart_flush, sbbc_uart_bus_flush), 768 KOBJMETHOD(uart_getsig, sbbc_uart_bus_getsig), 769 KOBJMETHOD(uart_ioctl, sbbc_uart_bus_ioctl), 770 KOBJMETHOD(uart_ipend, sbbc_uart_bus_ipend), 771 KOBJMETHOD(uart_param, sbbc_uart_bus_param), 772 KOBJMETHOD(uart_probe, sbbc_uart_bus_probe), 773 KOBJMETHOD(uart_receive, sbbc_uart_bus_receive), 774 KOBJMETHOD(uart_setsig, sbbc_uart_bus_setsig), 775 KOBJMETHOD(uart_transmit, sbbc_uart_bus_transmit), 776 777 KOBJMETHOD_END 778}; 779 780struct uart_class uart_sbbc_class = { 781 "sbbc", 782 sbbc_uart_methods, 783 sizeof(struct uart_softc), 784 .uc_ops = &sbbc_uart_ops, 785 .uc_range = 1, 786 .uc_rclk = 0x5bbc /* arbitrary */ 787}; 788 789#define SIGCHG(c, i, s, d) \ 790 if ((c) != 0) { \ 791 i |= (((i) & (s)) != 0) ? (s) : (s) | (d); \ 792 } else { \ 793 i = (((i) & (s)) != 0) ? ((i) & ~(s)) | (d) : (i); \ 794 } 795 796static int 797sbbc_uart_bus_attach(struct uart_softc *sc) 798{ 799 struct uart_bas *bas; 800 bus_space_tag_t bst; 801 bus_space_handle_t bsh; 802 uint32_t wrptr; 803 804 bas = &sc->sc_bas; 805 bst = bas->bst; 806 bsh = bas->bsh; 807 808 sc->sc_rxfifosz = SBBC_SRAM_READ_4(sbbc_solcons + 809 SBBC_CONS_OFF(cons_in_end)) - SBBC_SRAM_READ_4(sbbc_solcons + 810 SBBC_CONS_OFF(cons_in_begin)) - 1; 811 sc->sc_txfifosz = SBBC_SRAM_READ_4(sbbc_solcons + 812 SBBC_CONS_OFF(cons_out_end)) - SBBC_SRAM_READ_4(sbbc_solcons + 813 SBBC_CONS_OFF(cons_out_begin)) - 1; 814 815 uart_lock(sc->sc_hwmtx); 816 817 /* 818 * Let the current output drain before enabling interrupts. Not 819 * doing so tends to cause lost output when turning them on. 820 */ 821 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 822 SBBC_CONS_OFF(cons_out_wrptr)); 823 while (SBBC_SRAM_READ_4(sbbc_solcons + 824 SBBC_CONS_OFF(cons_out_rdptr)) != wrptr); 825 cpu_spinwait(); 826 827 /* Clear and acknowledge possibly outstanding interrupts. */ 828 SBBC_SRAM_WRITE_4(sbbc_scsolir, 0); 829 uart_barrier(bas); 830 SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, 831 SBBC_SRAM_READ_4(sbbc_scsolir)); 832 uart_barrier(bas); 833 /* Enable PCI interrupts. */ 834 SBBC_REGS_WRITE_4(SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A); 835 uart_barrier(bas); 836 /* Enable input from and output to SC as well as break interrupts. */ 837 SBBC_SRAM_WRITE_4(sbbc_scsolie, SBBC_SRAM_READ_4(sbbc_scsolie) | 838 SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK | 839 SBBC_SRAM_CONS_SPACE_OUT); 840 uart_barrier(bas); 841 842 uart_unlock(sc->sc_hwmtx); 843 return (0); 844} 845 846static int 847sbbc_uart_bus_detach(struct uart_softc *sc) 848{ 849 850 /* Give back the console input. */ 851 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); 852 return (0); 853} 854 855static int 856sbbc_uart_bus_flush(struct uart_softc *sc, int what) 857{ 858 struct uart_bas *bas; 859 bus_space_tag_t bst; 860 bus_space_handle_t bsh; 861 862 bas = &sc->sc_bas; 863 bst = bas->bst; 864 bsh = bas->bsh; 865 866 if ((what & UART_FLUSH_TRANSMITTER) != 0) 867 return (ENODEV); 868 if ((what & UART_FLUSH_RECEIVER) != 0) { 869 SBBC_SRAM_WRITE_4(sbbc_solcons + 870 SBBC_CONS_OFF(cons_in_rdptr), 871 SBBC_SRAM_READ_4(sbbc_solcons + 872 SBBC_CONS_OFF(cons_in_wrptr))); 873 uart_barrier(bas); 874 } 875 return (0); 876} 877 878static int 879sbbc_uart_bus_getsig(struct uart_softc *sc) 880{ 881 uint32_t dummy, new, old, sig; 882 883 do { 884 old = sc->sc_hwsig; 885 sig = old; 886 dummy = 0; 887 SIGCHG(dummy, sig, SER_CTS, SER_DCTS); 888 SIGCHG(dummy, sig, SER_DCD, SER_DDCD); 889 SIGCHG(dummy, sig, SER_DSR, SER_DDSR); 890 new = sig & ~SER_MASK_DELTA; 891 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 892 return (sig); 893} 894 895static int 896sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 897{ 898 int error; 899 900 error = 0; 901 uart_lock(sc->sc_hwmtx); 902 switch (request) { 903 case UART_IOCTL_BAUD: 904 *(int*)data = 9600; /* arbitrary */ 905 break; 906 default: 907 error = EINVAL; 908 break; 909 } 910 uart_unlock(sc->sc_hwmtx); 911 return (error); 912} 913 914static int 915sbbc_uart_bus_ipend(struct uart_softc *sc) 916{ 917 struct uart_bas *bas; 918 bus_space_tag_t bst; 919 bus_space_handle_t bsh; 920 int ipend; 921 uint32_t reason, status; 922 923 bas = &sc->sc_bas; 924 bst = bas->bst; 925 bsh = bas->bsh; 926 927 uart_lock(sc->sc_hwmtx); 928 status = SBBC_REGS_READ_4(SBBC_PCI_INT_STATUS); 929 if (status == 0) { 930 uart_unlock(sc->sc_hwmtx); 931 return (0); 932 } 933 934 /* 935 * Unfortunately, we can't use compare and swap for non-cachable 936 * memory. 937 */ 938 reason = SBBC_SRAM_READ_4(sbbc_scsolir); 939 SBBC_SRAM_WRITE_4(sbbc_scsolir, 0); 940 uart_barrier(bas); 941 /* Acknowledge the interrupt. */ 942 SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, status); 943 uart_barrier(bas); 944 945 uart_unlock(sc->sc_hwmtx); 946 947 ipend = 0; 948 if ((reason & SBBC_SRAM_CONS_IN) != 0) 949 ipend |= SER_INT_RXREADY; 950 if ((reason & SBBC_SRAM_CONS_BRK) != 0) 951 ipend |= SER_INT_BREAK; 952 if ((reason & SBBC_SRAM_CONS_SPACE_OUT) != 0 && 953 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) == 954 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr))) 955 ipend |= SER_INT_TXIDLE; 956 return (ipend); 957} 958 959static int 960sbbc_uart_bus_param(struct uart_softc *sc __unused, int baudrate __unused, 961 int databits __unused, int stopbits __unused, int parity __unused) 962{ 963 964 return (0); 965} 966 967static int 968sbbc_uart_bus_probe(struct uart_softc *sc __unused) 969{ 970 971 if (sbbc_console != 0) 972 return (0); 973 return (ENXIO); 974} 975 976static int 977sbbc_uart_bus_receive(struct uart_softc *sc) 978{ 979 struct uart_bas *bas; 980 bus_space_tag_t bst; 981 bus_space_handle_t bsh; 982 int c; 983 uint32_t end, rdptr, wrptr; 984 985 bas = &sc->sc_bas; 986 bst = bas->bst; 987 bsh = bas->bsh; 988 989 uart_lock(sc->sc_hwmtx); 990 991 end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end)); 992 rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)); 993 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr)); 994 while (rdptr != wrptr) { 995 if (uart_rx_full(sc) != 0) { 996 sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; 997 break; 998 } 999 c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr); 1000 uart_rx_put(sc, c); 1001 if (++rdptr == end) 1002 rdptr = SBBC_SRAM_READ_4(sbbc_solcons + 1003 SBBC_CONS_OFF(cons_in_begin)); 1004 } 1005 uart_barrier(bas); 1006 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), 1007 rdptr); 1008 uart_barrier(bas); 1009 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 1010 SBBC_SRAM_CONS_SPACE_IN); 1011 uart_barrier(bas); 1012 sbbc_send_intr(bst, bsh); 1013 1014 uart_unlock(sc->sc_hwmtx); 1015 return (0); 1016} 1017 1018static int 1019sbbc_uart_bus_setsig(struct uart_softc *sc, int sig) 1020{ 1021 struct uart_bas *bas; 1022 uint32_t new, old; 1023 1024 bas = &sc->sc_bas; 1025 do { 1026 old = sc->sc_hwsig; 1027 new = old; 1028 if ((sig & SER_DDTR) != 0) { 1029 SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); 1030 } 1031 if ((sig & SER_DRTS) != 0) { 1032 SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); 1033 } 1034 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 1035 return (0); 1036} 1037 1038static int 1039sbbc_uart_bus_transmit(struct uart_softc *sc) 1040{ 1041 struct uart_bas *bas; 1042 bus_space_tag_t bst; 1043 bus_space_handle_t bsh; 1044 int i; 1045 uint32_t end, wrptr; 1046 1047 bas = &sc->sc_bas; 1048 bst = bas->bst; 1049 bsh = bas->bsh; 1050 1051 uart_lock(sc->sc_hwmtx); 1052 1053 end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end)); 1054 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 1055 SBBC_CONS_OFF(cons_out_wrptr)); 1056 for (i = 0; i < sc->sc_txdatasz; i++) { 1057 SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, sc->sc_txbuf[i]); 1058 if (++wrptr == end) 1059 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 1060 SBBC_CONS_OFF(cons_out_begin)); 1061 } 1062 uart_barrier(bas); 1063 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr), 1064 wrptr); 1065 uart_barrier(bas); 1066 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 1067 SBBC_SRAM_CONS_OUT); 1068 uart_barrier(bas); 1069 sbbc_send_intr(bst, bsh); 1070 sc->sc_txbusy = 1; 1071 1072 uart_unlock(sc->sc_hwmtx); 1073 return (0); 1074} 1075