sbbc.c revision 227843
1206451Smarius/* $OpenBSD: sbbc.c,v 1.7 2009/11/09 17:53:39 nicm Exp $ */ 2206453Smarius/*- 3206451Smarius * Copyright (c) 2008 Mark Kettenis 4206451Smarius * 5206451Smarius * Permission to use, copy, modify, and distribute this software for any 6206451Smarius * purpose with or without fee is hereby granted, provided that the above 7206451Smarius * copyright notice and this permission notice appear in all copies. 8206451Smarius * 9206451Smarius * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10206451Smarius * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11206451Smarius * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12206451Smarius * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13206451Smarius * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14206451Smarius * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15206451Smarius * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16206451Smarius */ 17206451Smarius/*- 18206451Smarius * Copyright (c) 2010 Marius Strobl <marius@FreeBSD.org> 19206451Smarius * All rights reserved. 20206451Smarius * 21206451Smarius * Redistribution and use in source and binary forms, with or without 22206451Smarius * modification, are permitted provided that the following conditions 23206451Smarius * are met: 24206451Smarius * 1. Redistributions of source code must retain the above copyright 25206451Smarius * notice, this list of conditions and the following disclaimer. 26206451Smarius * 2. Redistributions in binary form must reproduce the above copyright 27206451Smarius * notice, this list of conditions and the following disclaimer in the 28206451Smarius * documentation and/or other materials provided with the distribution. 29206451Smarius * 30206451Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 31206451Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32206451Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33206451Smarius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 34206451Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35206451Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36206451Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37206451Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38206451Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39206451Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40206451Smarius * SUCH DAMAGE. 41206451Smarius */ 42206451Smarius 43206451Smarius#include <sys/cdefs.h> 44206451Smarius__FBSDID("$FreeBSD: head/sys/sparc64/pci/sbbc.c 227843 2011-11-22 21:28:20Z marius $"); 45206451Smarius 46206451Smarius#include <sys/param.h> 47206451Smarius#include <sys/systm.h> 48206451Smarius#include <sys/bus.h> 49206451Smarius#include <sys/clock.h> 50206451Smarius#include <sys/endian.h> 51206451Smarius#include <sys/kernel.h> 52206451Smarius#include <sys/lock.h> 53206451Smarius#include <sys/module.h> 54206451Smarius#include <sys/mutex.h> 55206451Smarius#include <sys/resource.h> 56206451Smarius#include <sys/rman.h> 57206451Smarius 58206451Smarius#include <dev/ofw/ofw_bus.h> 59206451Smarius#include <dev/ofw/openfirm.h> 60206451Smarius 61206451Smarius#include <machine/bus.h> 62206451Smarius#include <machine/cpu.h> 63206451Smarius#include <machine/resource.h> 64206451Smarius 65206451Smarius#include <dev/pci/pcireg.h> 66206451Smarius#include <dev/pci/pcivar.h> 67206451Smarius#include <dev/uart/uart.h> 68206451Smarius#include <dev/uart/uart_cpu.h> 69206451Smarius#include <dev/uart/uart_bus.h> 70206451Smarius 71206451Smarius#include "clock_if.h" 72206451Smarius#include "uart_if.h" 73206451Smarius 74206451Smarius#define SBBC_PCI_BAR PCIR_BAR(0) 75206451Smarius#define SBBC_PCI_VENDOR 0x108e 76206451Smarius#define SBBC_PCI_PRODUCT 0xc416 77206451Smarius 78206451Smarius#define SBBC_REGS_OFFSET 0x800000 79206451Smarius#define SBBC_REGS_SIZE 0x6230 80206451Smarius#define SBBC_EPLD_OFFSET 0x8e0000 81206451Smarius#define SBBC_EPLD_SIZE 0x20 82206451Smarius#define SBBC_SRAM_OFFSET 0x900000 83206451Smarius#define SBBC_SRAM_SIZE 0x20000 /* 128KB SRAM */ 84206451Smarius 85206451Smarius#define SBBC_PCI_INT_STATUS 0x2320 86206451Smarius#define SBBC_PCI_INT_ENABLE 0x2330 87206451Smarius#define SBBC_PCI_ENABLE_INT_A 0x11 88206451Smarius 89206451Smarius#define SBBC_EPLD_INTERRUPT 0x13 90206451Smarius#define SBBC_EPLD_INTERRUPT_ON 0x01 91206451Smarius 92206451Smarius#define SBBC_SRAM_CONS_IN 0x00000001 93206451Smarius#define SBBC_SRAM_CONS_OUT 0x00000002 94206451Smarius#define SBBC_SRAM_CONS_BRK 0x00000004 95206451Smarius#define SBBC_SRAM_CONS_SPACE_IN 0x00000008 96206451Smarius#define SBBC_SRAM_CONS_SPACE_OUT 0x00000010 97206451Smarius 98206451Smarius#define SBBC_TAG_KEY_SIZE 8 99206451Smarius#define SBBC_TAG_KEY_SCSOLIE "SCSOLIE" /* SC -> OS int. enable */ 100206451Smarius#define SBBC_TAG_KEY_SCSOLIR "SCSOLIR" /* SC -> OS int. reason */ 101206451Smarius#define SBBC_TAG_KEY_SOLCONS "SOLCONS" /* OS console buffer */ 102206451Smarius#define SBBC_TAG_KEY_SOLSCIE "SOLSCIE" /* OS -> SC int. enable */ 103206451Smarius#define SBBC_TAG_KEY_SOLSCIR "SOLSCIR" /* OS -> SC int. reason */ 104206451Smarius#define SBBC_TAG_KEY_TODDATA "TODDATA" /* OS TOD struct */ 105206451Smarius#define SBBC_TAG_OFF(x) offsetof(struct sbbc_sram_tag, x) 106206451Smarius 107206451Smariusstruct sbbc_sram_tag { 108206451Smarius char tag_key[SBBC_TAG_KEY_SIZE]; 109206451Smarius uint32_t tag_size; 110206451Smarius uint32_t tag_offset; 111206451Smarius} __packed; 112206451Smarius 113206451Smarius#define SBBC_TOC_MAGIC "TOCSRAM" 114206451Smarius#define SBBC_TOC_MAGIC_SIZE 8 115206451Smarius#define SBBC_TOC_TAGS_MAX 32 116206451Smarius#define SBBC_TOC_OFF(x) offsetof(struct sbbc_sram_toc, x) 117206451Smarius 118206451Smariusstruct sbbc_sram_toc { 119206451Smarius char toc_magic[SBBC_TOC_MAGIC_SIZE]; 120206451Smarius uint8_t toc_reserved; 121206451Smarius uint8_t toc_type; 122206451Smarius uint16_t toc_version; 123206451Smarius uint32_t toc_ntags; 124206451Smarius struct sbbc_sram_tag toc_tag[SBBC_TOC_TAGS_MAX]; 125206451Smarius} __packed; 126206451Smarius 127206451Smarius#define SBBC_TOD_MAGIC 0x54443100 /* "TD1" */ 128206451Smarius#define SBBC_TOD_VERSION 1 129206451Smarius#define SBBC_TOD_OFF(x) offsetof(struct sbbc_sram_tod, x) 130206451Smarius 131206451Smariusstruct sbbc_sram_tod { 132206451Smarius uint32_t tod_magic; 133206451Smarius uint32_t tod_version; 134206451Smarius uint64_t tod_time; 135206451Smarius uint64_t tod_skew; 136206451Smarius uint32_t tod_reserved; 137206451Smarius uint32_t tod_heartbeat; 138206451Smarius uint32_t tod_timeout; 139206451Smarius} __packed; 140206451Smarius 141206451Smarius#define SBBC_CONS_MAGIC 0x434f4e00 /* "CON" */ 142206451Smarius#define SBBC_CONS_VERSION 1 143206451Smarius#define SBBC_CONS_OFF(x) offsetof(struct sbbc_sram_cons, x) 144206451Smarius 145206451Smariusstruct sbbc_sram_cons { 146206451Smarius uint32_t cons_magic; 147206451Smarius uint32_t cons_version; 148206451Smarius uint32_t cons_size; 149206451Smarius 150206451Smarius uint32_t cons_in_begin; 151206451Smarius uint32_t cons_in_end; 152206451Smarius uint32_t cons_in_rdptr; 153206451Smarius uint32_t cons_in_wrptr; 154206451Smarius 155206451Smarius uint32_t cons_out_begin; 156206451Smarius uint32_t cons_out_end; 157206451Smarius uint32_t cons_out_rdptr; 158206451Smarius uint32_t cons_out_wrptr; 159206451Smarius} __packed; 160206451Smarius 161206451Smariusstruct sbbc_softc { 162206451Smarius struct resource *sc_res; 163206451Smarius}; 164206451Smarius 165206451Smarius#define SBBC_READ_N(wdth, offs) \ 166206451Smarius bus_space_read_ ## wdth((bst), (bsh), (offs)) 167206451Smarius#define SBBC_WRITE_N(wdth, offs, val) \ 168206451Smarius bus_space_write_ ## wdth((bst), (bsh), (offs), (val)) 169206451Smarius 170206451Smarius#define SBBC_READ_1(offs) \ 171206451Smarius SBBC_READ_N(1, (offs)) 172206451Smarius#define SBBC_READ_2(offs) \ 173206451Smarius bswap16(SBBC_READ_N(2, (offs))) 174206451Smarius#define SBBC_READ_4(offs) \ 175206451Smarius bswap32(SBBC_READ_N(4, (offs))) 176206451Smarius#define SBBC_READ_8(offs) \ 177206451Smarius bswap64(SBBC_READ_N(8, (offs))) 178206451Smarius#define SBBC_WRITE_1(offs, val) \ 179206451Smarius SBBC_WRITE_N(1, (offs), (val)) 180206451Smarius#define SBBC_WRITE_2(offs, val) \ 181206451Smarius SBBC_WRITE_N(2, (offs), bswap16(val)) 182206451Smarius#define SBBC_WRITE_4(offs, val) \ 183206451Smarius SBBC_WRITE_N(4, (offs), bswap32(val)) 184206451Smarius#define SBBC_WRITE_8(offs, val) \ 185206451Smarius SBBC_WRITE_N(8, (offs), bswap64(val)) 186206451Smarius 187206451Smarius#define SBBC_REGS_READ_1(offs) \ 188206451Smarius SBBC_READ_1((offs) + SBBC_REGS_OFFSET) 189206451Smarius#define SBBC_REGS_READ_2(offs) \ 190206451Smarius SBBC_READ_2((offs) + SBBC_REGS_OFFSET) 191206451Smarius#define SBBC_REGS_READ_4(offs) \ 192206451Smarius SBBC_READ_4((offs) + SBBC_REGS_OFFSET) 193206451Smarius#define SBBC_REGS_READ_8(offs) \ 194206451Smarius SBBC_READ_8((offs) + SBBC_REGS_OFFSET) 195206451Smarius#define SBBC_REGS_WRITE_1(offs, val) \ 196206451Smarius SBBC_WRITE_1((offs) + SBBC_REGS_OFFSET, (val)) 197206451Smarius#define SBBC_REGS_WRITE_2(offs, val) \ 198206451Smarius SBBC_WRITE_2((offs) + SBBC_REGS_OFFSET, (val)) 199206451Smarius#define SBBC_REGS_WRITE_4(offs, val) \ 200206451Smarius SBBC_WRITE_4((offs) + SBBC_REGS_OFFSET, (val)) 201206451Smarius#define SBBC_REGS_WRITE_8(offs, val) \ 202206451Smarius SBBC_WRITE_8((offs) + SBBC_REGS_OFFSET, (val)) 203206451Smarius 204206451Smarius#define SBBC_EPLD_READ_1(offs) \ 205206451Smarius SBBC_READ_1((offs) + SBBC_EPLD_OFFSET) 206206451Smarius#define SBBC_EPLD_READ_2(offs) \ 207206451Smarius SBBC_READ_2((offs) + SBBC_EPLD_OFFSET) 208206451Smarius#define SBBC_EPLD_READ_4(offs) \ 209206451Smarius SBBC_READ_4((offs) + SBBC_EPLD_OFFSET) 210206451Smarius#define SBBC_EPLD_READ_8(offs) \ 211206451Smarius SBBC_READ_8((offs) + SBBC_EPLD_OFFSET) 212206451Smarius#define SBBC_EPLD_WRITE_1(offs, val) \ 213206451Smarius SBBC_WRITE_1((offs) + SBBC_EPLD_OFFSET, (val)) 214206451Smarius#define SBBC_EPLD_WRITE_2(offs, val) \ 215206451Smarius SBBC_WRITE_2((offs) + SBBC_EPLD_OFFSET, (val)) 216206451Smarius#define SBBC_EPLD_WRITE_4(offs, val) \ 217206451Smarius SBBC_WRITE_4((offs) + SBBC_EPLD_OFFSET, (val)) 218206451Smarius#define SBBC_EPLD_WRITE_8(offs, val) \ 219206451Smarius SBBC_WRITE_8((offs) + SBBC_EPLD_OFFSET, (val)) 220206451Smarius 221206451Smarius#define SBBC_SRAM_READ_1(offs) \ 222206451Smarius SBBC_READ_1((offs) + SBBC_SRAM_OFFSET) 223206451Smarius#define SBBC_SRAM_READ_2(offs) \ 224206451Smarius SBBC_READ_2((offs) + SBBC_SRAM_OFFSET) 225206451Smarius#define SBBC_SRAM_READ_4(offs) \ 226206451Smarius SBBC_READ_4((offs) + SBBC_SRAM_OFFSET) 227206451Smarius#define SBBC_SRAM_READ_8(offs) \ 228206451Smarius SBBC_READ_8((offs) + SBBC_SRAM_OFFSET) 229206451Smarius#define SBBC_SRAM_WRITE_1(offs, val) \ 230206451Smarius SBBC_WRITE_1((offs) + SBBC_SRAM_OFFSET, (val)) 231206451Smarius#define SBBC_SRAM_WRITE_2(offs, val) \ 232206451Smarius SBBC_WRITE_2((offs) + SBBC_SRAM_OFFSET, (val)) 233206451Smarius#define SBBC_SRAM_WRITE_4(offs, val) \ 234206451Smarius SBBC_WRITE_4((offs) + SBBC_SRAM_OFFSET, (val)) 235206451Smarius#define SBBC_SRAM_WRITE_8(offs, val) \ 236206451Smarius SBBC_WRITE_8((offs) + SBBC_SRAM_OFFSET, (val)) 237206451Smarius 238206451Smarius#define SUNW_SETCONSINPUT "SUNW,set-console-input" 239206451Smarius#define SUNW_SETCONSINPUT_CLNT "CON_CLNT" 240206451Smarius#define SUNW_SETCONSINPUT_OBP "CON_OBP" 241206451Smarius 242206451Smariusstatic u_int sbbc_console; 243206451Smarius 244206451Smariusstatic uint32_t sbbc_scsolie; 245206451Smariusstatic uint32_t sbbc_scsolir; 246206451Smariusstatic uint32_t sbbc_solcons; 247206451Smariusstatic uint32_t sbbc_solscie; 248206451Smariusstatic uint32_t sbbc_solscir; 249206451Smariusstatic uint32_t sbbc_toddata; 250206451Smarius 251206451Smarius/* 252206451Smarius * internal helpers 253206451Smarius */ 254206451Smariusstatic int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh); 255206451Smariusstatic inline void sbbc_send_intr(bus_space_tag_t bst, 256206451Smarius bus_space_handle_t bsh); 257206451Smariusstatic const char *sbbc_serengeti_set_console_input(char *new); 258206451Smarius 259206451Smarius/* 260206451Smarius * SBBC PCI interface 261206451Smarius */ 262225931Smariusstatic bus_activate_resource_t sbbc_bus_activate_resource; 263225931Smariusstatic bus_adjust_resource_t sbbc_bus_adjust_resource; 264225931Smariusstatic bus_deactivate_resource_t sbbc_bus_deactivate_resource; 265206451Smariusstatic bus_alloc_resource_t sbbc_bus_alloc_resource; 266206451Smariusstatic bus_release_resource_t sbbc_bus_release_resource; 267206451Smariusstatic bus_get_resource_list_t sbbc_bus_get_resource_list; 268206451Smariusstatic bus_setup_intr_t sbbc_bus_setup_intr; 269206451Smariusstatic bus_teardown_intr_t sbbc_bus_teardown_intr; 270206451Smarius 271206451Smariusstatic device_attach_t sbbc_pci_attach; 272206451Smariusstatic device_probe_t sbbc_pci_probe; 273206451Smarius 274206451Smariusstatic clock_gettime_t sbbc_tod_gettime; 275206451Smariusstatic clock_settime_t sbbc_tod_settime; 276206451Smarius 277206451Smariusstatic device_method_t sbbc_pci_methods[] = { 278206451Smarius /* Device interface */ 279206451Smarius DEVMETHOD(device_probe, sbbc_pci_probe), 280206451Smarius DEVMETHOD(device_attach, sbbc_pci_attach), 281206451Smarius 282206451Smarius DEVMETHOD(bus_alloc_resource, sbbc_bus_alloc_resource), 283225931Smarius DEVMETHOD(bus_activate_resource,sbbc_bus_activate_resource), 284225931Smarius DEVMETHOD(bus_deactivate_resource,sbbc_bus_deactivate_resource), 285225931Smarius DEVMETHOD(bus_adjust_resource, sbbc_bus_adjust_resource), 286206451Smarius DEVMETHOD(bus_release_resource, sbbc_bus_release_resource), 287206451Smarius DEVMETHOD(bus_setup_intr, sbbc_bus_setup_intr), 288206451Smarius DEVMETHOD(bus_teardown_intr, sbbc_bus_teardown_intr), 289225931Smarius DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 290206451Smarius DEVMETHOD(bus_get_resource_list, sbbc_bus_get_resource_list), 291206451Smarius 292206451Smarius /* clock interface */ 293206451Smarius DEVMETHOD(clock_gettime, sbbc_tod_gettime), 294206451Smarius DEVMETHOD(clock_settime, sbbc_tod_settime), 295206451Smarius 296227843Smarius DEVMETHOD_END 297206451Smarius}; 298206451Smarius 299206451Smariusstatic devclass_t sbbc_devclass; 300206451Smarius 301206451SmariusDEFINE_CLASS_0(sbbc, sbbc_driver, sbbc_pci_methods, sizeof(struct sbbc_softc)); 302206451SmariusDRIVER_MODULE(sbbc, pci, sbbc_driver, sbbc_devclass, 0, 0); 303206451Smarius 304206451Smariusstatic int 305206451Smariussbbc_pci_probe(device_t dev) 306206451Smarius{ 307206451Smarius 308206451Smarius if (pci_get_vendor(dev) == SBBC_PCI_VENDOR && 309206451Smarius pci_get_device(dev) == SBBC_PCI_PRODUCT) { 310206451Smarius device_set_desc(dev, "Sun BootBus controller"); 311206451Smarius return (BUS_PROBE_DEFAULT); 312206451Smarius } 313206451Smarius return (ENXIO); 314206451Smarius} 315206451Smarius 316206451Smariusstatic int 317206451Smariussbbc_pci_attach(device_t dev) 318206451Smarius{ 319206451Smarius struct sbbc_softc *sc; 320206451Smarius struct timespec ts; 321206451Smarius device_t child; 322206451Smarius bus_space_tag_t bst; 323206451Smarius bus_space_handle_t bsh; 324206451Smarius phandle_t node; 325206451Smarius int error, rid; 326206451Smarius uint32_t val; 327206451Smarius 328206451Smarius /* Nothing to to if we're not the chosen one. */ 329206451Smarius if ((node = OF_finddevice("/chosen")) == -1) { 330206451Smarius device_printf(dev, "failed to find /chosen\n"); 331206451Smarius return (ENXIO); 332206451Smarius } 333206451Smarius if (OF_getprop(node, "iosram", &node, sizeof(node)) == -1) { 334206451Smarius device_printf(dev, "failed to get iosram\n"); 335206451Smarius return (ENXIO); 336206451Smarius } 337206451Smarius if (node != ofw_bus_get_node(dev)) 338206451Smarius return (0); 339206451Smarius 340206451Smarius sc = device_get_softc(dev); 341206451Smarius rid = SBBC_PCI_BAR; 342225931Smarius sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 343225931Smarius RF_ACTIVE); 344206451Smarius if (sc->sc_res == NULL) { 345206451Smarius device_printf(dev, "failed to allocate resources\n"); 346206451Smarius return (ENXIO); 347206451Smarius } 348206451Smarius bst = rman_get_bustag(sc->sc_res); 349206451Smarius bsh = rman_get_bushandle(sc->sc_res); 350206451Smarius if (sbbc_console != 0) { 351206451Smarius /* Once again the interrupt pin isn't set. */ 352206451Smarius if (pci_get_intpin(dev) == 0) 353206451Smarius pci_set_intpin(dev, 1); 354206451Smarius child = device_add_child(dev, NULL, -1); 355206451Smarius if (child == NULL) 356206451Smarius device_printf(dev, "failed to add UART device\n"); 357206451Smarius error = bus_generic_attach(dev); 358206451Smarius if (error != 0) 359206451Smarius device_printf(dev, "failed to attach UART device\n"); 360206451Smarius } else { 361206451Smarius error = sbbc_parse_toc(rman_get_bustag(sc->sc_res), 362206451Smarius rman_get_bushandle(sc->sc_res)); 363206451Smarius if (error != 0) { 364206451Smarius device_printf(dev, "failed to parse TOC\n"); 365206451Smarius if (sbbc_console != 0) { 366206451Smarius bus_release_resource(dev, SYS_RES_MEMORY, rid, 367206451Smarius sc->sc_res); 368206451Smarius return (error); 369206451Smarius } 370206451Smarius } 371206451Smarius } 372206451Smarius if (sbbc_toddata != 0) { 373206451Smarius if ((val = SBBC_SRAM_READ_4(sbbc_toddata + 374206451Smarius SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC) 375206451Smarius device_printf(dev, "invalid TOD magic %#x\n", val); 376206451Smarius else if ((val = SBBC_SRAM_READ_4(sbbc_toddata + 377206451Smarius SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION) 378206451Smarius device_printf(dev, "invalid TOD version %#x\n", val); 379206451Smarius else { 380206451Smarius clock_register(dev, 1000000); /* 1 sec. resolution */ 381206451Smarius if (bootverbose) { 382206451Smarius sbbc_tod_gettime(dev, &ts); 383206451Smarius device_printf(dev, 384206451Smarius "current time: %ld.%09ld\n", 385206451Smarius (long)ts.tv_sec, ts.tv_nsec); 386206451Smarius } 387206451Smarius } 388206451Smarius } 389206451Smarius return (0); 390206451Smarius} 391206451Smarius 392206451Smarius/* 393206451Smarius * Note that the bus methods don't pass-through the uart(4) requests but act 394206451Smarius * as if they would come from sbbc(4) in order to avoid complications with 395206451Smarius * pci(4) (actually, uart(4) isn't a real child but rather a function of 396206451Smarius * sbbc(4) anyway). 397206451Smarius */ 398206451Smarius 399206451Smariusstatic struct resource * 400206451Smariussbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type, 401206451Smarius int *rid, u_long start, u_long end, u_long count, u_int flags) 402206451Smarius{ 403206451Smarius struct sbbc_softc *sc; 404206451Smarius 405206451Smarius sc = device_get_softc(dev); 406206451Smarius switch (type) { 407206451Smarius case SYS_RES_IRQ: 408225931Smarius return (bus_generic_alloc_resource(dev, dev, type, rid, start, 409225931Smarius end, count, flags)); 410206451Smarius case SYS_RES_MEMORY: 411206451Smarius return (sc->sc_res); 412206451Smarius default: 413206451Smarius return (NULL); 414206451Smarius } 415206451Smarius} 416206451Smarius 417206451Smariusstatic int 418225931Smariussbbc_bus_activate_resource(device_t bus, device_t child, int type, int rid, 419225931Smarius struct resource *res) 420225931Smarius{ 421225931Smarius 422225931Smarius if (type == SYS_RES_MEMORY) 423225931Smarius return (0); 424225931Smarius return (bus_generic_activate_resource(bus, child, type, rid, res)); 425225931Smarius} 426225931Smarius 427225931Smariusstatic int 428225931Smariussbbc_bus_deactivate_resource(device_t bus, device_t child, int type, int rid, 429225931Smarius struct resource *res) 430225931Smarius{ 431225931Smarius 432225931Smarius if (type == SYS_RES_MEMORY) 433225931Smarius return (0); 434225931Smarius return (bus_generic_deactivate_resource(bus, child, type, rid, res)); 435225931Smarius} 436225931Smarius 437225931Smariusstatic int 438225931Smariussbbc_bus_adjust_resource(device_t bus __unused, device_t child __unused, 439225931Smarius int type __unused, struct resource *res __unused, u_long start __unused, 440225931Smarius u_long end __unused) 441225931Smarius{ 442225931Smarius 443225931Smarius return (ENXIO); 444225931Smarius} 445225931Smarius 446225931Smariusstatic int 447206451Smariussbbc_bus_release_resource(device_t dev, device_t child __unused, int type, 448206451Smarius int rid, struct resource *res) 449206451Smarius{ 450206451Smarius 451206451Smarius if (type == SYS_RES_IRQ) 452225931Smarius return (bus_generic_release_resource(dev, dev, type, rid, 453225931Smarius res)); 454206451Smarius return (0); 455206451Smarius} 456206451Smarius 457206451Smariusstatic struct resource_list * 458206451Smariussbbc_bus_get_resource_list(device_t dev, device_t child __unused) 459206451Smarius{ 460206451Smarius 461225931Smarius return (bus_generic_get_resource_list(dev, dev)); 462206451Smarius} 463206451Smarius 464206451Smariusstatic int 465206451Smariussbbc_bus_setup_intr(device_t dev, device_t child __unused, 466206451Smarius struct resource *res, int flags, driver_filter_t *filt, 467206451Smarius driver_intr_t *intr, void *arg, void **cookiep) 468206451Smarius{ 469206451Smarius 470225931Smarius return (bus_generic_setup_intr(dev, dev, res, flags, filt, intr, arg, 471225931Smarius cookiep)); 472206451Smarius} 473206451Smarius 474206451Smariusstatic int 475206451Smariussbbc_bus_teardown_intr(device_t dev, device_t child __unused, 476206451Smarius struct resource *res, void *cookie) 477206451Smarius{ 478206451Smarius 479225931Smarius return (bus_generic_teardown_intr(dev, dev, res, cookie)); 480206451Smarius} 481206451Smarius 482206451Smarius/* 483206451Smarius * internal helpers 484206451Smarius */ 485206451Smariusstatic int 486206451Smariussbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh) 487206451Smarius{ 488206451Smarius char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)]; 489206451Smarius bus_size_t tag; 490206451Smarius phandle_t node; 491206451Smarius uint32_t off, sram_toc; 492206451Smarius u_int i, tags; 493206451Smarius 494206451Smarius if ((node = OF_finddevice("/chosen")) == -1) 495206451Smarius return (ENXIO); 496206451Smarius /* SRAM TOC offset defaults to 0. */ 497206451Smarius if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0) 498206451Smarius sram_toc = 0; 499206451Smarius 500206451Smarius bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc + 501206451Smarius SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE); 502206451Smarius buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0'; 503206451Smarius if (strcmp(buf, SBBC_TOC_MAGIC) != 0) 504206451Smarius return (ENXIO); 505206451Smarius 506206451Smarius tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags)); 507206451Smarius for (i = 0; i < tags; i++) { 508206451Smarius tag = sram_toc + SBBC_TOC_OFF(toc_tag) + 509206451Smarius i * sizeof(struct sbbc_sram_tag); 510206451Smarius bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag + 511206451Smarius SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE); 512206451Smarius buf[SBBC_TAG_KEY_SIZE - 1] = '\0'; 513206451Smarius off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset)); 514206451Smarius if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0) 515206451Smarius sbbc_scsolie = off; 516206451Smarius else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0) 517206451Smarius sbbc_scsolir = off; 518206451Smarius else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0) 519206451Smarius sbbc_solcons = off; 520206451Smarius else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0) 521206451Smarius sbbc_solscie = off; 522206451Smarius else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0) 523206451Smarius sbbc_solscir = off; 524206451Smarius else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0) 525206451Smarius sbbc_toddata = off; 526206451Smarius } 527206451Smarius return (0); 528206451Smarius} 529206451Smarius 530206451Smariusstatic const char * 531206451Smariussbbc_serengeti_set_console_input(char *new) 532206451Smarius{ 533206451Smarius struct { 534206451Smarius cell_t name; 535206451Smarius cell_t nargs; 536206451Smarius cell_t nreturns; 537206451Smarius cell_t new; 538206451Smarius cell_t old; 539206451Smarius } args = { 540206451Smarius (cell_t)SUNW_SETCONSINPUT, 541206451Smarius 1, 542206451Smarius 1, 543206451Smarius }; 544206451Smarius 545206451Smarius args.new = (cell_t)new; 546206451Smarius if (ofw_entry(&args) == -1) 547206451Smarius return (NULL); 548206451Smarius return ((const char *)args.old); 549206451Smarius} 550206451Smarius 551206451Smariusstatic inline void 552206451Smariussbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh) 553206451Smarius{ 554206451Smarius 555206451Smarius SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON); 556206451Smarius bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1, 557206451Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 558206451Smarius} 559206451Smarius 560206451Smarius/* 561206451Smarius * TOD interface 562206451Smarius */ 563206451Smariusstatic int 564206451Smariussbbc_tod_gettime(device_t dev, struct timespec *ts) 565206451Smarius{ 566206451Smarius struct sbbc_softc *sc; 567206451Smarius bus_space_tag_t bst; 568206451Smarius bus_space_handle_t bsh; 569206451Smarius 570206451Smarius sc = device_get_softc(dev); 571206451Smarius bst = rman_get_bustag(sc->sc_res); 572206451Smarius bsh = rman_get_bushandle(sc->sc_res); 573206451Smarius 574206451Smarius ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) + 575206451Smarius SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew)); 576206451Smarius ts->tv_nsec = 0; 577206451Smarius return (0); 578206451Smarius} 579206451Smarius 580206451Smariusstatic int 581206451Smariussbbc_tod_settime(device_t dev, struct timespec *ts) 582206451Smarius{ 583206451Smarius struct sbbc_softc *sc; 584206451Smarius bus_space_tag_t bst; 585206451Smarius bus_space_handle_t bsh; 586206451Smarius 587206451Smarius sc = device_get_softc(dev); 588206451Smarius bst = rman_get_bustag(sc->sc_res); 589206451Smarius bsh = rman_get_bushandle(sc->sc_res); 590206451Smarius 591206451Smarius SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec - 592206451Smarius SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time))); 593206451Smarius return (0); 594206451Smarius} 595206451Smarius 596206451Smarius/* 597206451Smarius * UART bus front-end 598206451Smarius */ 599206451Smariusstatic device_probe_t sbbc_uart_sbbc_probe; 600206451Smarius 601206451Smariusstatic device_method_t sbbc_uart_sbbc_methods[] = { 602206451Smarius /* Device interface */ 603206451Smarius DEVMETHOD(device_probe, sbbc_uart_sbbc_probe), 604206451Smarius DEVMETHOD(device_attach, uart_bus_attach), 605206451Smarius DEVMETHOD(device_detach, uart_bus_detach), 606206451Smarius 607227843Smarius DEVMETHOD_END 608206451Smarius}; 609206451Smarius 610206451SmariusDEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods, 611206451Smarius sizeof(struct uart_softc)); 612206451SmariusDRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, 0, 0); 613206451Smarius 614206451Smariusstatic int 615206451Smariussbbc_uart_sbbc_probe(device_t dev) 616206451Smarius{ 617206451Smarius struct uart_softc *sc; 618206451Smarius 619206451Smarius sc = device_get_softc(dev); 620206451Smarius sc->sc_class = &uart_sbbc_class; 621206451Smarius device_set_desc(dev, "Serengeti console"); 622206451Smarius return (uart_bus_probe(dev, 0, 0, SBBC_PCI_BAR, 0)); 623206451Smarius} 624206451Smarius 625206451Smarius/* 626206451Smarius * Low-level UART interface 627206451Smarius */ 628206451Smariusstatic int sbbc_uart_probe(struct uart_bas *bas); 629206451Smariusstatic void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits, 630206451Smarius int stopbits, int parity); 631206451Smariusstatic void sbbc_uart_term(struct uart_bas *bas); 632206451Smariusstatic void sbbc_uart_putc(struct uart_bas *bas, int c); 633206451Smariusstatic int sbbc_uart_rxready(struct uart_bas *bas); 634206451Smariusstatic int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx); 635206451Smarius 636206451Smariusstatic struct uart_ops sbbc_uart_ops = { 637206451Smarius .probe = sbbc_uart_probe, 638206451Smarius .init = sbbc_uart_init, 639206451Smarius .term = sbbc_uart_term, 640206451Smarius .putc = sbbc_uart_putc, 641206451Smarius .rxready = sbbc_uart_rxready, 642206451Smarius .getc = sbbc_uart_getc, 643206451Smarius}; 644206451Smarius 645206451Smariusstatic int 646206451Smariussbbc_uart_probe(struct uart_bas *bas) 647206451Smarius{ 648206451Smarius bus_space_tag_t bst; 649206451Smarius bus_space_handle_t bsh; 650206451Smarius int error; 651206451Smarius 652206451Smarius sbbc_console = 1; 653206451Smarius bst = bas->bst; 654206451Smarius bsh = bas->bsh; 655206451Smarius error = sbbc_parse_toc(bst, bsh); 656206451Smarius if (error != 0) 657206451Smarius return (error); 658206451Smarius 659206451Smarius if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 || 660206451Smarius sbbc_solscie == 0 || sbbc_solscir == 0) 661206451Smarius return (ENXIO); 662206451Smarius 663206451Smarius if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) != 664206451Smarius SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons + 665206451Smarius SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION) 666206451Smarius return (ENXIO); 667206451Smarius return (0); 668206451Smarius} 669206451Smarius 670206451Smariusstatic void 671206451Smariussbbc_uart_init(struct uart_bas *bas, int baudrate __unused, 672206451Smarius int databits __unused, int stopbits __unused, int parity __unused) 673206451Smarius{ 674206451Smarius bus_space_tag_t bst; 675206451Smarius bus_space_handle_t bsh; 676206451Smarius 677206451Smarius bst = bas->bst; 678206451Smarius bsh = bas->bsh; 679206451Smarius 680206451Smarius /* Enable output to and space in from the SC interrupts. */ 681206451Smarius SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) | 682206451Smarius SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN); 683206451Smarius uart_barrier(bas); 684206451Smarius 685206451Smarius /* Take over the console input. */ 686206451Smarius sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT); 687206451Smarius} 688206451Smarius 689206451Smariusstatic void 690206451Smariussbbc_uart_term(struct uart_bas *bas __unused) 691206451Smarius{ 692206451Smarius 693206451Smarius /* Give back the console input. */ 694206451Smarius sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); 695206451Smarius} 696206451Smarius 697206451Smariusstatic void 698206451Smariussbbc_uart_putc(struct uart_bas *bas, int c) 699206451Smarius{ 700206451Smarius bus_space_tag_t bst; 701206451Smarius bus_space_handle_t bsh; 702206451Smarius uint32_t wrptr; 703206451Smarius 704206451Smarius bst = bas->bst; 705206451Smarius bsh = bas->bsh; 706206451Smarius 707206451Smarius wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 708206451Smarius SBBC_CONS_OFF(cons_out_wrptr)); 709206451Smarius SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c); 710206451Smarius uart_barrier(bas); 711206451Smarius if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons + 712206451Smarius SBBC_CONS_OFF(cons_out_end))) 713206451Smarius wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 714206451Smarius SBBC_CONS_OFF(cons_out_begin)); 715206451Smarius SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr), 716206451Smarius wrptr); 717206451Smarius uart_barrier(bas); 718206451Smarius 719206451Smarius SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 720206451Smarius SBBC_SRAM_CONS_OUT); 721206451Smarius uart_barrier(bas); 722206451Smarius sbbc_send_intr(bst, bsh); 723206451Smarius} 724206451Smarius 725206451Smariusstatic int 726206451Smariussbbc_uart_rxready(struct uart_bas *bas) 727206451Smarius{ 728206451Smarius bus_space_tag_t bst; 729206451Smarius bus_space_handle_t bsh; 730206451Smarius 731206451Smarius bst = bas->bst; 732206451Smarius bsh = bas->bsh; 733206451Smarius 734206451Smarius if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) == 735206451Smarius SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr))) 736206451Smarius return (0); 737206451Smarius return (1); 738206451Smarius} 739206451Smarius 740206451Smariusstatic int 741206451Smariussbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) 742206451Smarius{ 743206451Smarius bus_space_tag_t bst; 744206451Smarius bus_space_handle_t bsh; 745206451Smarius int c; 746206451Smarius uint32_t rdptr; 747206451Smarius 748206451Smarius bst = bas->bst; 749206451Smarius bsh = bas->bsh; 750206451Smarius 751206451Smarius uart_lock(hwmtx); 752206451Smarius 753206451Smarius while (sbbc_uart_rxready(bas) == 0) { 754206451Smarius uart_unlock(hwmtx); 755206451Smarius DELAY(4); 756206451Smarius uart_lock(hwmtx); 757206451Smarius } 758206451Smarius 759206451Smarius rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)); 760206451Smarius c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr); 761206451Smarius uart_barrier(bas); 762206451Smarius if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons + 763206451Smarius SBBC_CONS_OFF(cons_in_end))) 764206451Smarius rdptr = SBBC_SRAM_READ_4(sbbc_solcons + 765206451Smarius SBBC_CONS_OFF(cons_in_begin)); 766206451Smarius SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), 767206451Smarius rdptr); 768206451Smarius uart_barrier(bas); 769206451Smarius SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 770206451Smarius SBBC_SRAM_CONS_SPACE_IN); 771206451Smarius uart_barrier(bas); 772206451Smarius sbbc_send_intr(bst, bsh); 773206451Smarius 774206451Smarius uart_unlock(hwmtx); 775206451Smarius return (c); 776206451Smarius} 777206451Smarius 778206451Smarius/* 779206451Smarius * High-level UART interface 780206451Smarius */ 781206451Smariusstatic int sbbc_uart_bus_attach(struct uart_softc *sc); 782206451Smariusstatic int sbbc_uart_bus_detach(struct uart_softc *sc); 783206451Smariusstatic int sbbc_uart_bus_flush(struct uart_softc *sc, int what); 784206451Smariusstatic int sbbc_uart_bus_getsig(struct uart_softc *sc); 785206451Smariusstatic int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, 786206451Smarius intptr_t data); 787206451Smariusstatic int sbbc_uart_bus_ipend(struct uart_softc *sc); 788206451Smariusstatic int sbbc_uart_bus_param(struct uart_softc *sc, int baudrate, 789206451Smarius int databits, int stopbits, int parity); 790206451Smariusstatic int sbbc_uart_bus_probe(struct uart_softc *sc); 791206451Smariusstatic int sbbc_uart_bus_receive(struct uart_softc *sc); 792206451Smariusstatic int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig); 793206451Smariusstatic int sbbc_uart_bus_transmit(struct uart_softc *sc); 794206451Smarius 795206451Smariusstatic kobj_method_t sbbc_uart_methods[] = { 796206451Smarius KOBJMETHOD(uart_attach, sbbc_uart_bus_attach), 797206451Smarius KOBJMETHOD(uart_detach, sbbc_uart_bus_detach), 798206451Smarius KOBJMETHOD(uart_flush, sbbc_uart_bus_flush), 799206451Smarius KOBJMETHOD(uart_getsig, sbbc_uart_bus_getsig), 800206451Smarius KOBJMETHOD(uart_ioctl, sbbc_uart_bus_ioctl), 801206451Smarius KOBJMETHOD(uart_ipend, sbbc_uart_bus_ipend), 802206451Smarius KOBJMETHOD(uart_param, sbbc_uart_bus_param), 803206451Smarius KOBJMETHOD(uart_probe, sbbc_uart_bus_probe), 804206451Smarius KOBJMETHOD(uart_receive, sbbc_uart_bus_receive), 805206451Smarius KOBJMETHOD(uart_setsig, sbbc_uart_bus_setsig), 806206451Smarius KOBJMETHOD(uart_transmit, sbbc_uart_bus_transmit), 807206451Smarius 808227843Smarius DEVMETHOD_END 809206451Smarius}; 810206451Smarius 811206451Smariusstruct uart_class uart_sbbc_class = { 812206451Smarius "sbbc", 813206451Smarius sbbc_uart_methods, 814206451Smarius sizeof(struct uart_softc), 815206451Smarius .uc_ops = &sbbc_uart_ops, 816206451Smarius .uc_range = 1, 817206451Smarius .uc_rclk = 0x5bbc /* arbitrary */ 818206451Smarius}; 819206451Smarius 820206451Smarius#define SIGCHG(c, i, s, d) \ 821206451Smarius if ((c) != 0) { \ 822206451Smarius i |= (((i) & (s)) != 0) ? (s) : (s) | (d); \ 823206451Smarius } else { \ 824206451Smarius i = (((i) & (s)) != 0) ? ((i) & ~(s)) | (d) : (i); \ 825206451Smarius } 826206451Smarius 827206451Smariusstatic int 828206451Smariussbbc_uart_bus_attach(struct uart_softc *sc) 829206451Smarius{ 830206451Smarius struct uart_bas *bas; 831206451Smarius bus_space_tag_t bst; 832206451Smarius bus_space_handle_t bsh; 833206451Smarius uint32_t wrptr; 834206451Smarius 835206451Smarius bas = &sc->sc_bas; 836206451Smarius bst = bas->bst; 837206451Smarius bsh = bas->bsh; 838206451Smarius 839206451Smarius sc->sc_rxfifosz = SBBC_SRAM_READ_4(sbbc_solcons + 840206451Smarius SBBC_CONS_OFF(cons_in_end)) - SBBC_SRAM_READ_4(sbbc_solcons + 841206451Smarius SBBC_CONS_OFF(cons_in_begin)) - 1; 842206451Smarius sc->sc_txfifosz = SBBC_SRAM_READ_4(sbbc_solcons + 843206451Smarius SBBC_CONS_OFF(cons_out_end)) - SBBC_SRAM_READ_4(sbbc_solcons + 844206451Smarius SBBC_CONS_OFF(cons_out_begin)) - 1; 845206451Smarius 846206451Smarius uart_lock(sc->sc_hwmtx); 847206451Smarius 848206451Smarius /* 849206451Smarius * Let the current output drain before enabling interrupts. Not 850206451Smarius * doing so tends to cause lost output when turning them on. 851206451Smarius */ 852206451Smarius wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 853206451Smarius SBBC_CONS_OFF(cons_out_wrptr)); 854206451Smarius while (SBBC_SRAM_READ_4(sbbc_solcons + 855206451Smarius SBBC_CONS_OFF(cons_out_rdptr)) != wrptr); 856206451Smarius cpu_spinwait(); 857206451Smarius 858206451Smarius /* Clear and acknowledge possibly outstanding interrupts. */ 859206451Smarius SBBC_SRAM_WRITE_4(sbbc_scsolir, 0); 860206451Smarius uart_barrier(bas); 861206451Smarius SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, 862206451Smarius SBBC_SRAM_READ_4(sbbc_scsolir)); 863206451Smarius uart_barrier(bas); 864206451Smarius /* Enable PCI interrupts. */ 865206451Smarius SBBC_REGS_WRITE_4(SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A); 866206451Smarius uart_barrier(bas); 867206451Smarius /* Enable input from and output to SC as well as break interrupts. */ 868206451Smarius SBBC_SRAM_WRITE_4(sbbc_scsolie, SBBC_SRAM_READ_4(sbbc_scsolie) | 869206451Smarius SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK | 870206451Smarius SBBC_SRAM_CONS_SPACE_OUT); 871206451Smarius uart_barrier(bas); 872206451Smarius 873206451Smarius uart_unlock(sc->sc_hwmtx); 874206451Smarius return (0); 875206451Smarius} 876206451Smarius 877206451Smariusstatic int 878206451Smariussbbc_uart_bus_detach(struct uart_softc *sc) 879206451Smarius{ 880206451Smarius 881206451Smarius /* Give back the console input. */ 882206451Smarius sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); 883206451Smarius return (0); 884206451Smarius} 885206451Smarius 886206451Smariusstatic int 887206451Smariussbbc_uart_bus_flush(struct uart_softc *sc, int what) 888206451Smarius{ 889206451Smarius struct uart_bas *bas; 890206451Smarius bus_space_tag_t bst; 891206451Smarius bus_space_handle_t bsh; 892206451Smarius 893206451Smarius bas = &sc->sc_bas; 894206451Smarius bst = bas->bst; 895206451Smarius bsh = bas->bsh; 896206451Smarius 897206451Smarius if ((what & UART_FLUSH_TRANSMITTER) != 0) 898206451Smarius return (ENODEV); 899206451Smarius if ((what & UART_FLUSH_RECEIVER) != 0) { 900206451Smarius SBBC_SRAM_WRITE_4(sbbc_solcons + 901206451Smarius SBBC_CONS_OFF(cons_in_rdptr), 902206451Smarius SBBC_SRAM_READ_4(sbbc_solcons + 903206451Smarius SBBC_CONS_OFF(cons_in_wrptr))); 904206451Smarius uart_barrier(bas); 905206451Smarius } 906206451Smarius return (0); 907206451Smarius} 908206451Smarius 909206451Smariusstatic int 910206451Smariussbbc_uart_bus_getsig(struct uart_softc *sc) 911206451Smarius{ 912206451Smarius uint32_t dummy, new, old, sig; 913206451Smarius 914206451Smarius do { 915206451Smarius old = sc->sc_hwsig; 916206451Smarius sig = old; 917206451Smarius dummy = 0; 918206451Smarius SIGCHG(dummy, sig, SER_CTS, SER_DCTS); 919206451Smarius SIGCHG(dummy, sig, SER_DCD, SER_DDCD); 920206451Smarius SIGCHG(dummy, sig, SER_DSR, SER_DDSR); 921206451Smarius new = sig & ~SER_MASK_DELTA; 922206451Smarius } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 923206451Smarius return (sig); 924206451Smarius} 925206451Smarius 926206451Smariusstatic int 927206451Smariussbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 928206451Smarius{ 929206451Smarius int error; 930206451Smarius 931206451Smarius error = 0; 932206451Smarius uart_lock(sc->sc_hwmtx); 933206451Smarius switch (request) { 934206451Smarius case UART_IOCTL_BAUD: 935206451Smarius *(int*)data = 9600; /* arbitrary */ 936206451Smarius break; 937206451Smarius default: 938206451Smarius error = EINVAL; 939206451Smarius break; 940206451Smarius } 941206451Smarius uart_unlock(sc->sc_hwmtx); 942206451Smarius return (error); 943206451Smarius} 944206451Smarius 945206451Smariusstatic int 946206451Smariussbbc_uart_bus_ipend(struct uart_softc *sc) 947206451Smarius{ 948206451Smarius struct uart_bas *bas; 949206451Smarius bus_space_tag_t bst; 950206451Smarius bus_space_handle_t bsh; 951206451Smarius int ipend; 952206451Smarius uint32_t reason, status; 953206451Smarius 954206451Smarius bas = &sc->sc_bas; 955206451Smarius bst = bas->bst; 956206451Smarius bsh = bas->bsh; 957206451Smarius 958206451Smarius uart_lock(sc->sc_hwmtx); 959206451Smarius status = SBBC_REGS_READ_4(SBBC_PCI_INT_STATUS); 960206451Smarius if (status == 0) { 961206451Smarius uart_unlock(sc->sc_hwmtx); 962206451Smarius return (0); 963206451Smarius } 964206451Smarius 965206451Smarius /* 966206451Smarius * Unfortunately, we can't use compare and swap for non-cachable 967206451Smarius * memory. 968206451Smarius */ 969206451Smarius reason = SBBC_SRAM_READ_4(sbbc_scsolir); 970206451Smarius SBBC_SRAM_WRITE_4(sbbc_scsolir, 0); 971206451Smarius uart_barrier(bas); 972206451Smarius /* Acknowledge the interrupt. */ 973206451Smarius SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, status); 974206451Smarius uart_barrier(bas); 975206451Smarius 976206451Smarius uart_unlock(sc->sc_hwmtx); 977206451Smarius 978206451Smarius ipend = 0; 979206451Smarius if ((reason & SBBC_SRAM_CONS_IN) != 0) 980206451Smarius ipend |= SER_INT_RXREADY; 981206451Smarius if ((reason & SBBC_SRAM_CONS_BRK) != 0) 982206451Smarius ipend |= SER_INT_BREAK; 983206451Smarius if ((reason & SBBC_SRAM_CONS_SPACE_OUT) != 0 && 984206451Smarius SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) == 985206451Smarius SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr))) 986206451Smarius ipend |= SER_INT_TXIDLE; 987206451Smarius return (ipend); 988206451Smarius} 989206451Smarius 990206451Smariusstatic int 991206451Smariussbbc_uart_bus_param(struct uart_softc *sc __unused, int baudrate __unused, 992206451Smarius int databits __unused, int stopbits __unused, int parity __unused) 993206451Smarius{ 994206451Smarius 995206451Smarius return (0); 996206451Smarius} 997206451Smarius 998206451Smariusstatic int 999206451Smariussbbc_uart_bus_probe(struct uart_softc *sc __unused) 1000206451Smarius{ 1001206451Smarius 1002206451Smarius if (sbbc_console != 0) 1003206451Smarius return (0); 1004206451Smarius return (ENXIO); 1005206451Smarius} 1006206451Smarius 1007206451Smariusstatic int 1008206451Smariussbbc_uart_bus_receive(struct uart_softc *sc) 1009206451Smarius{ 1010206451Smarius struct uart_bas *bas; 1011206451Smarius bus_space_tag_t bst; 1012206451Smarius bus_space_handle_t bsh; 1013206451Smarius int c; 1014206451Smarius uint32_t end, rdptr, wrptr; 1015206451Smarius 1016206451Smarius bas = &sc->sc_bas; 1017206451Smarius bst = bas->bst; 1018206451Smarius bsh = bas->bsh; 1019206451Smarius 1020206451Smarius uart_lock(sc->sc_hwmtx); 1021206451Smarius 1022206451Smarius end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end)); 1023206451Smarius rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)); 1024206451Smarius wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr)); 1025206451Smarius while (rdptr != wrptr) { 1026206451Smarius if (uart_rx_full(sc) != 0) { 1027206451Smarius sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; 1028206451Smarius break; 1029206451Smarius } 1030206451Smarius c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr); 1031206451Smarius uart_rx_put(sc, c); 1032206451Smarius if (++rdptr == end) 1033206451Smarius rdptr = SBBC_SRAM_READ_4(sbbc_solcons + 1034206451Smarius SBBC_CONS_OFF(cons_in_begin)); 1035206451Smarius } 1036206451Smarius uart_barrier(bas); 1037206451Smarius SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), 1038206451Smarius rdptr); 1039206451Smarius uart_barrier(bas); 1040206451Smarius SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 1041206451Smarius SBBC_SRAM_CONS_SPACE_IN); 1042206451Smarius uart_barrier(bas); 1043206451Smarius sbbc_send_intr(bst, bsh); 1044206451Smarius 1045206451Smarius uart_unlock(sc->sc_hwmtx); 1046206451Smarius return (0); 1047206451Smarius} 1048206451Smarius 1049206451Smariusstatic int 1050206451Smariussbbc_uart_bus_setsig(struct uart_softc *sc, int sig) 1051206451Smarius{ 1052206451Smarius struct uart_bas *bas; 1053206451Smarius uint32_t new, old; 1054206451Smarius 1055206451Smarius bas = &sc->sc_bas; 1056206451Smarius do { 1057206451Smarius old = sc->sc_hwsig; 1058206451Smarius new = old; 1059206451Smarius if ((sig & SER_DDTR) != 0) { 1060206451Smarius SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); 1061206451Smarius } 1062206451Smarius if ((sig & SER_DRTS) != 0) { 1063206451Smarius SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); 1064206451Smarius } 1065206451Smarius } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 1066206451Smarius return (0); 1067206451Smarius} 1068206451Smarius 1069206451Smariusstatic int 1070206451Smariussbbc_uart_bus_transmit(struct uart_softc *sc) 1071206451Smarius{ 1072206451Smarius struct uart_bas *bas; 1073206451Smarius bus_space_tag_t bst; 1074206451Smarius bus_space_handle_t bsh; 1075206451Smarius int i; 1076206451Smarius uint32_t end, wrptr; 1077206451Smarius 1078206451Smarius bas = &sc->sc_bas; 1079206451Smarius bst = bas->bst; 1080206451Smarius bsh = bas->bsh; 1081206451Smarius 1082206451Smarius uart_lock(sc->sc_hwmtx); 1083206451Smarius 1084206451Smarius end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end)); 1085206451Smarius wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 1086206451Smarius SBBC_CONS_OFF(cons_out_wrptr)); 1087206451Smarius for (i = 0; i < sc->sc_txdatasz; i++) { 1088206451Smarius SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, sc->sc_txbuf[i]); 1089206451Smarius if (++wrptr == end) 1090206451Smarius wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 1091206451Smarius SBBC_CONS_OFF(cons_out_begin)); 1092206451Smarius } 1093206451Smarius uart_barrier(bas); 1094206451Smarius SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr), 1095206451Smarius wrptr); 1096206451Smarius uart_barrier(bas); 1097206451Smarius SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 1098206451Smarius SBBC_SRAM_CONS_OUT); 1099206451Smarius uart_barrier(bas); 1100206451Smarius sbbc_send_intr(bst, bsh); 1101206451Smarius sc->sc_txbusy = 1; 1102206451Smarius 1103206451Smarius uart_unlock(sc->sc_hwmtx); 1104206451Smarius return (0); 1105206451Smarius} 1106