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