1258210Srpaulo/* $OpenBSD: sbbc.c,v 1.7 2009/11/09 17:53:39 nicm Exp $ */ 2258210Srpaulo/*- 3327595Sian * Copyright (c) 2008 Mark Kettenis 4258210Srpaulo * 5258210Srpaulo * Permission to use, copy, modify, and distribute this software for any 6258210Srpaulo * purpose with or without fee is hereby granted, provided that the above 7258210Srpaulo * copyright notice and this permission notice appear in all copies. 8258210Srpaulo * 9258210Srpaulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10258210Srpaulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11258210Srpaulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12258210Srpaulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13258210Srpaulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14258210Srpaulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15258210Srpaulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16258210Srpaulo */ 17258210Srpaulo/*- 18258210Srpaulo * Copyright (c) 2010 Marius Strobl <marius@FreeBSD.org> 19258210Srpaulo * All rights reserved. 20258210Srpaulo * 21258210Srpaulo * Redistribution and use in source and binary forms, with or without 22258210Srpaulo * modification, are permitted provided that the following conditions 23258210Srpaulo * are met: 24258210Srpaulo * 1. Redistributions of source code must retain the above copyright 25258210Srpaulo * notice, this list of conditions and the following disclaimer. 26258210Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 27258210Srpaulo * notice, this list of conditions and the following disclaimer in the 28258210Srpaulo * documentation and/or other materials provided with the distribution. 29258210Srpaulo * 30327595Sian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 31327595Sian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32327595Sian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33258210Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 34258210Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35327595Sian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36258210Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37258210Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38258210Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39258210Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40258210Srpaulo * SUCH DAMAGE. 41258210Srpaulo */ 42327595Sian 43327595Sian#include <sys/cdefs.h> 44258210Srpaulo__FBSDID("$FreeBSD$"); 45258210Srpaulo 46258210Srpaulo#include <sys/param.h> 47258210Srpaulo#include <sys/systm.h> 48258210Srpaulo#include <sys/bus.h> 49258210Srpaulo#include <sys/clock.h> 50327595Sian#include <sys/endian.h> 51258210Srpaulo#include <sys/kernel.h> 52258210Srpaulo#include <sys/lock.h> 53258210Srpaulo#include <sys/module.h> 54258210Srpaulo#include <sys/mutex.h> 55258210Srpaulo#include <sys/resource.h> 56258210Srpaulo#include <sys/rman.h> 57258210Srpaulo 58258210Srpaulo#include <dev/ofw/ofw_bus.h> 59258210Srpaulo#include <dev/ofw/openfirm.h> 60258210Srpaulo 61258210Srpaulo#include <machine/bus.h> 62258210Srpaulo#include <machine/cpu.h> 63258210Srpaulo#include <machine/resource.h> 64258210Srpaulo 65258210Srpaulo#include <dev/pci/pcireg.h> 66258210Srpaulo#include <dev/pci/pcivar.h> 67258210Srpaulo#include <dev/uart/uart.h> 68258210Srpaulo#include <dev/uart/uart_cpu.h> 69327595Sian#include <dev/uart/uart_bus.h> 70327595Sian 71327595Sian#include "clock_if.h" 72327595Sian#include "uart_if.h" 73258210Srpaulo 74258210Srpaulo#define SBBC_PCI_BAR PCIR_BAR(0) 75258210Srpaulo#define SBBC_PCI_VENDOR 0x108e 76258210Srpaulo#define SBBC_PCI_PRODUCT 0xc416 77258210Srpaulo 78258210Srpaulo#define SBBC_REGS_OFFSET 0x800000 79327595Sian#define SBBC_REGS_SIZE 0x6230 80327595Sian#define SBBC_EPLD_OFFSET 0x8e0000 81327595Sian#define SBBC_EPLD_SIZE 0x20 82327595Sian#define SBBC_SRAM_OFFSET 0x900000 83258210Srpaulo#define SBBC_SRAM_SIZE 0x20000 /* 128KB SRAM */ 84327595Sian 85327595Sian#define SBBC_PCI_INT_STATUS 0x2320 86327595Sian#define SBBC_PCI_INT_ENABLE 0x2330 87327595Sian#define SBBC_PCI_ENABLE_INT_A 0x11 88327595Sian 89327595Sian#define SBBC_EPLD_INTERRUPT 0x13 90283138Srpaulo#define SBBC_EPLD_INTERRUPT_ON 0x01 91327595Sian 92327595Sian#define SBBC_SRAM_CONS_IN 0x00000001 93327595Sian#define SBBC_SRAM_CONS_OUT 0x00000002 94327595Sian#define SBBC_SRAM_CONS_BRK 0x00000004 95327595Sian#define SBBC_SRAM_CONS_SPACE_IN 0x00000008 96327595Sian#define SBBC_SRAM_CONS_SPACE_OUT 0x00000010 97327595Sian 98327595Sian#define SBBC_TAG_KEY_SIZE 8 99327595Sian#define SBBC_TAG_KEY_SCSOLIE "SCSOLIE" /* SC -> OS int. enable */ 100327595Sian#define SBBC_TAG_KEY_SCSOLIR "SCSOLIR" /* SC -> OS int. reason */ 101327595Sian#define SBBC_TAG_KEY_SOLCONS "SOLCONS" /* OS console buffer */ 102327595Sian#define SBBC_TAG_KEY_SOLSCIE "SOLSCIE" /* OS -> SC int. enable */ 103327595Sian#define SBBC_TAG_KEY_SOLSCIR "SOLSCIR" /* OS -> SC int. reason */ 104327595Sian#define SBBC_TAG_KEY_TODDATA "TODDATA" /* OS TOD struct */ 105327595Sian#define SBBC_TAG_OFF(x) offsetof(struct sbbc_sram_tag, x) 106327595Sian 107327595Sianstruct sbbc_sram_tag { 108327595Sian char tag_key[SBBC_TAG_KEY_SIZE]; 109327595Sian uint32_t tag_size; 110327595Sian uint32_t tag_offset; 111327595Sian} __packed; 112327595Sian 113327595Sian#define SBBC_TOC_MAGIC "TOCSRAM" 114327595Sian#define SBBC_TOC_MAGIC_SIZE 8 115327595Sian#define SBBC_TOC_TAGS_MAX 32 116327595Sian#define SBBC_TOC_OFF(x) offsetof(struct sbbc_sram_toc, x) 117327595Sian 118327595Sianstruct sbbc_sram_toc { 119327595Sian char toc_magic[SBBC_TOC_MAGIC_SIZE]; 120327595Sian uint8_t toc_reserved; 121327595Sian uint8_t toc_type; 122327595Sian uint16_t toc_version; 123327595Sian uint32_t toc_ntags; 124258210Srpaulo struct sbbc_sram_tag toc_tag[SBBC_TOC_TAGS_MAX]; 125258210Srpaulo} __packed; 126258210Srpaulo 127327595Sian#define SBBC_TOD_MAGIC 0x54443100 /* "TD1" */ 128327595Sian#define SBBC_TOD_VERSION 1 129327595Sian#define SBBC_TOD_OFF(x) offsetof(struct sbbc_sram_tod, x) 130258210Srpaulo 131258210Srpaulostruct sbbc_sram_tod { 132258210Srpaulo uint32_t tod_magic; 133258210Srpaulo uint32_t tod_version; 134327595Sian uint64_t tod_time; 135258210Srpaulo uint64_t tod_skew; 136258210Srpaulo uint32_t tod_reserved; 137258210Srpaulo uint32_t tod_heartbeat; 138258210Srpaulo uint32_t tod_timeout; 139258210Srpaulo} __packed; 140258210Srpaulo 141258210Srpaulo#define SBBC_CONS_MAGIC 0x434f4e00 /* "CON" */ 142258210Srpaulo#define SBBC_CONS_VERSION 1 143258210Srpaulo#define SBBC_CONS_OFF(x) offsetof(struct sbbc_sram_cons, x) 144258210Srpaulo 145258210Srpaulostruct sbbc_sram_cons { 146258210Srpaulo uint32_t cons_magic; 147258210Srpaulo uint32_t cons_version; 148258210Srpaulo uint32_t cons_size; 149258210Srpaulo 150258210Srpaulo uint32_t cons_in_begin; 151258210Srpaulo uint32_t cons_in_end; 152258210Srpaulo uint32_t cons_in_rdptr; 153258210Srpaulo uint32_t cons_in_wrptr; 154258210Srpaulo 155258210Srpaulo uint32_t cons_out_begin; 156258210Srpaulo uint32_t cons_out_end; 157258210Srpaulo uint32_t cons_out_rdptr; 158258210Srpaulo uint32_t cons_out_wrptr; 159258210Srpaulo} __packed; 160258210Srpaulo 161327595Sianstruct sbbc_softc { 162258210Srpaulo struct resource *sc_res; 163258210Srpaulo}; 164258210Srpaulo 165258210Srpaulo#define SBBC_READ_N(wdth, offs) \ 166258210Srpaulo bus_space_read_ ## wdth((bst), (bsh), (offs)) 167258210Srpaulo#define SBBC_WRITE_N(wdth, offs, val) \ 168258210Srpaulo bus_space_write_ ## wdth((bst), (bsh), (offs), (val)) 169258210Srpaulo 170258210Srpaulo#define SBBC_READ_1(offs) \ 171258210Srpaulo SBBC_READ_N(1, (offs)) 172258210Srpaulo#define SBBC_READ_2(offs) \ 173258210Srpaulo bswap16(SBBC_READ_N(2, (offs))) 174327595Sian#define SBBC_READ_4(offs) \ 175258210Srpaulo bswap32(SBBC_READ_N(4, (offs))) 176327595Sian#define SBBC_READ_8(offs) \ 177327595Sian bswap64(SBBC_READ_N(8, (offs))) 178327595Sian#define SBBC_WRITE_1(offs, val) \ 179327595Sian SBBC_WRITE_N(1, (offs), (val)) 180327595Sian#define SBBC_WRITE_2(offs, val) \ 181327595Sian SBBC_WRITE_N(2, (offs), bswap16(val)) 182327595Sian#define SBBC_WRITE_4(offs, val) \ 183327595Sian SBBC_WRITE_N(4, (offs), bswap32(val)) 184327595Sian#define SBBC_WRITE_8(offs, val) \ 185327595Sian SBBC_WRITE_N(8, (offs), bswap64(val)) 186327595Sian 187327595Sian#define SBBC_REGS_READ_1(offs) \ 188327595Sian SBBC_READ_1((offs) + SBBC_REGS_OFFSET) 189327595Sian#define SBBC_REGS_READ_2(offs) \ 190327595Sian SBBC_READ_2((offs) + SBBC_REGS_OFFSET) 191327595Sian#define SBBC_REGS_READ_4(offs) \ 192327595Sian SBBC_READ_4((offs) + SBBC_REGS_OFFSET) 193327595Sian#define SBBC_REGS_READ_8(offs) \ 194327595Sian SBBC_READ_8((offs) + SBBC_REGS_OFFSET) 195327595Sian#define SBBC_REGS_WRITE_1(offs, val) \ 196327595Sian SBBC_WRITE_1((offs) + SBBC_REGS_OFFSET, (val)) 197327595Sian#define SBBC_REGS_WRITE_2(offs, val) \ 198327595Sian SBBC_WRITE_2((offs) + SBBC_REGS_OFFSET, (val)) 199327595Sian#define SBBC_REGS_WRITE_4(offs, val) \ 200327595Sian SBBC_WRITE_4((offs) + SBBC_REGS_OFFSET, (val)) 201327595Sian#define SBBC_REGS_WRITE_8(offs, val) \ 202327595Sian SBBC_WRITE_8((offs) + SBBC_REGS_OFFSET, (val)) 203327595Sian 204327595Sian#define SBBC_EPLD_READ_1(offs) \ 205327595Sian SBBC_READ_1((offs) + SBBC_EPLD_OFFSET) 206327595Sian#define SBBC_EPLD_READ_2(offs) \ 207327595Sian SBBC_READ_2((offs) + SBBC_EPLD_OFFSET) 208327595Sian#define SBBC_EPLD_READ_4(offs) \ 209327595Sian SBBC_READ_4((offs) + SBBC_EPLD_OFFSET) 210327595Sian#define SBBC_EPLD_READ_8(offs) \ 211327595Sian SBBC_READ_8((offs) + SBBC_EPLD_OFFSET) 212327595Sian#define SBBC_EPLD_WRITE_1(offs, val) \ 213327595Sian SBBC_WRITE_1((offs) + SBBC_EPLD_OFFSET, (val)) 214327595Sian#define SBBC_EPLD_WRITE_2(offs, val) \ 215327595Sian SBBC_WRITE_2((offs) + SBBC_EPLD_OFFSET, (val)) 216327595Sian#define SBBC_EPLD_WRITE_4(offs, val) \ 217327595Sian SBBC_WRITE_4((offs) + SBBC_EPLD_OFFSET, (val)) 218327595Sian#define SBBC_EPLD_WRITE_8(offs, val) \ 219327595Sian SBBC_WRITE_8((offs) + SBBC_EPLD_OFFSET, (val)) 220327595Sian 221327595Sian#define SBBC_SRAM_READ_1(offs) \ 222327595Sian SBBC_READ_1((offs) + SBBC_SRAM_OFFSET) 223327595Sian#define SBBC_SRAM_READ_2(offs) \ 224327595Sian SBBC_READ_2((offs) + SBBC_SRAM_OFFSET) 225327595Sian#define SBBC_SRAM_READ_4(offs) \ 226327595Sian SBBC_READ_4((offs) + SBBC_SRAM_OFFSET) 227327595Sian#define SBBC_SRAM_READ_8(offs) \ 228327595Sian SBBC_READ_8((offs) + SBBC_SRAM_OFFSET) 229327595Sian#define SBBC_SRAM_WRITE_1(offs, val) \ 230327595Sian SBBC_WRITE_1((offs) + SBBC_SRAM_OFFSET, (val)) 231327595Sian#define SBBC_SRAM_WRITE_2(offs, val) \ 232327595Sian SBBC_WRITE_2((offs) + SBBC_SRAM_OFFSET, (val)) 233327595Sian#define SBBC_SRAM_WRITE_4(offs, val) \ 234327595Sian SBBC_WRITE_4((offs) + SBBC_SRAM_OFFSET, (val)) 235327595Sian#define SBBC_SRAM_WRITE_8(offs, val) \ 236327595Sian SBBC_WRITE_8((offs) + SBBC_SRAM_OFFSET, (val)) 237327595Sian 238327595Sian#define SUNW_SETCONSINPUT "SUNW,set-console-input" 239327595Sian#define SUNW_SETCONSINPUT_CLNT "CON_CLNT" 240327595Sian#define SUNW_SETCONSINPUT_OBP "CON_OBP" 241327595Sian 242327595Sianstatic u_int sbbc_console; 243327595Sian 244327595Sianstatic uint32_t sbbc_scsolie; 245327595Sianstatic uint32_t sbbc_scsolir; 246327595Sianstatic uint32_t sbbc_solcons; 247327595Sianstatic uint32_t sbbc_solscie; 248327595Sianstatic uint32_t sbbc_solscir; 249327595Sianstatic uint32_t sbbc_toddata; 250327595Sian 251327595Sian/* 252327595Sian * internal helpers 253327595Sian */ 254327595Sianstatic int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh); 255327595Sianstatic inline void sbbc_send_intr(bus_space_tag_t bst, 256327595Sian bus_space_handle_t bsh); 257327595Sianstatic const char *sbbc_serengeti_set_console_input(char *new); 258327595Sian 259327595Sian/* 260327595Sian * SBBC PCI interface 261327595Sian */ 262327595Sianstatic bus_activate_resource_t sbbc_bus_activate_resource; 263327595Sianstatic bus_adjust_resource_t sbbc_bus_adjust_resource; 264327595Sianstatic bus_deactivate_resource_t sbbc_bus_deactivate_resource; 265327595Sianstatic bus_alloc_resource_t sbbc_bus_alloc_resource; 266327595Sianstatic bus_release_resource_t sbbc_bus_release_resource; 267327595Sianstatic bus_get_resource_list_t sbbc_bus_get_resource_list; 268327595Sianstatic bus_setup_intr_t sbbc_bus_setup_intr; 269327595Sianstatic bus_teardown_intr_t sbbc_bus_teardown_intr; 270327595Sian 271327595Sianstatic device_attach_t sbbc_pci_attach; 272327595Sianstatic device_probe_t sbbc_pci_probe; 273327595Sian 274327595Sianstatic clock_gettime_t sbbc_tod_gettime; 275327595Sianstatic clock_settime_t sbbc_tod_settime; 276327595Sian 277327595Sianstatic device_method_t sbbc_pci_methods[] = { 278327595Sian /* Device interface */ 279327595Sian DEVMETHOD(device_probe, sbbc_pci_probe), 280258210Srpaulo DEVMETHOD(device_attach, sbbc_pci_attach), 281258210Srpaulo 282258210Srpaulo DEVMETHOD(bus_alloc_resource, sbbc_bus_alloc_resource), 283258210Srpaulo DEVMETHOD(bus_activate_resource,sbbc_bus_activate_resource), 284258210Srpaulo DEVMETHOD(bus_deactivate_resource,sbbc_bus_deactivate_resource), 285258210Srpaulo DEVMETHOD(bus_adjust_resource, sbbc_bus_adjust_resource), 286258210Srpaulo DEVMETHOD(bus_release_resource, sbbc_bus_release_resource), 287258210Srpaulo DEVMETHOD(bus_setup_intr, sbbc_bus_setup_intr), 288258210Srpaulo DEVMETHOD(bus_teardown_intr, sbbc_bus_teardown_intr), 289258210Srpaulo DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 290258210Srpaulo DEVMETHOD(bus_get_resource_list, sbbc_bus_get_resource_list), 291258210Srpaulo 292258210Srpaulo /* clock interface */ 293258210Srpaulo DEVMETHOD(clock_gettime, sbbc_tod_gettime), 294258210Srpaulo DEVMETHOD(clock_settime, sbbc_tod_settime), 295258210Srpaulo 296258210Srpaulo DEVMETHOD_END 297327595Sian}; 298327595Sian 299327595Sianstatic devclass_t sbbc_devclass; 300327595Sian 301327595SianDEFINE_CLASS_0(sbbc, sbbc_driver, sbbc_pci_methods, sizeof(struct sbbc_softc)); 302327595SianDRIVER_MODULE(sbbc, pci, sbbc_driver, sbbc_devclass, NULL, NULL); 303327595Sian 304327595Sianstatic int 305327595Siansbbc_pci_probe(device_t dev) 306327595Sian{ 307327595Sian 308327595Sian if (pci_get_vendor(dev) == SBBC_PCI_VENDOR && 309327595Sian pci_get_device(dev) == SBBC_PCI_PRODUCT) { 310327595Sian device_set_desc(dev, "Sun BootBus controller"); 311327595Sian return (BUS_PROBE_DEFAULT); 312327595Sian } 313327595Sian return (ENXIO); 314327595Sian} 315327595Sian 316327595Sianstatic int 317327595Siansbbc_pci_attach(device_t dev) 318327595Sian{ 319327595Sian struct sbbc_softc *sc; 320327595Sian struct timespec ts; 321327595Sian device_t child; 322327595Sian bus_space_tag_t bst; 323327595Sian bus_space_handle_t bsh; 324327595Sian phandle_t node; 325327595Sian int error, rid; 326327595Sian uint32_t val; 327327595Sian 328327595Sian /* Nothing to to if we're not the chosen one. */ 329327595Sian if ((node = OF_finddevice("/chosen")) == -1) { 330327595Sian device_printf(dev, "failed to find /chosen\n"); 331327595Sian return (ENXIO); 332327595Sian } 333327595Sian if (OF_getprop(node, "iosram", &node, sizeof(node)) == -1) { 334327595Sian device_printf(dev, "failed to get iosram\n"); 335327595Sian return (ENXIO); 336327595Sian } 337327595Sian if (node != ofw_bus_get_node(dev)) 338327595Sian return (0); 339327595Sian 340327595Sian sc = device_get_softc(dev); 341327595Sian rid = SBBC_PCI_BAR; 342327595Sian sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 343327595Sian RF_ACTIVE); 344327595Sian if (sc->sc_res == NULL) { 345327595Sian device_printf(dev, "failed to allocate resources\n"); 346327595Sian return (ENXIO); 347327595Sian } 348327595Sian bst = rman_get_bustag(sc->sc_res); 349327595Sian bsh = rman_get_bushandle(sc->sc_res); 350327595Sian if (sbbc_console != 0) { 351327595Sian /* Once again the interrupt pin isn't set. */ 352327595Sian if (pci_get_intpin(dev) == 0) 353327595Sian pci_set_intpin(dev, 1); 354327595Sian child = device_add_child(dev, NULL, -1); 355327595Sian if (child == NULL) 356258210Srpaulo device_printf(dev, "failed to add UART device\n"); 357327595Sian error = bus_generic_attach(dev); 358327595Sian if (error != 0) 359327595Sian device_printf(dev, "failed to attach UART device\n"); 360327595Sian } else { 361327595Sian error = sbbc_parse_toc(bst, bsh); 362327595Sian if (error != 0) { 363327595Sian device_printf(dev, "failed to parse TOC\n"); 364327595Sian if (sbbc_console != 0) { 365327595Sian bus_release_resource(dev, SYS_RES_MEMORY, rid, 366327595Sian sc->sc_res); 367327595Sian return (error); 368327595Sian } 369327595Sian } 370327595Sian } 371327595Sian if (sbbc_toddata != 0) { 372327595Sian if ((val = SBBC_SRAM_READ_4(sbbc_toddata + 373327595Sian SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC) 374327595Sian device_printf(dev, "invalid TOD magic %#x\n", val); 375327595Sian else if ((val = SBBC_SRAM_READ_4(sbbc_toddata + 376327595Sian SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION) 377327595Sian device_printf(dev, "invalid TOD version %#x\n", val); 378327595Sian else { 379327595Sian clock_register(dev, 1000000); /* 1 sec. resolution */ 380327595Sian if (bootverbose) { 381327595Sian sbbc_tod_gettime(dev, &ts); 382327595Sian device_printf(dev, 383327595Sian "current time: %ld.%09ld\n", 384327595Sian (long)ts.tv_sec, ts.tv_nsec); 385327595Sian } 386327595Sian } 387327595Sian } 388327595Sian return (0); 389327595Sian} 390327595Sian 391327595Sian/* 392327595Sian * Note that the bus methods don't pass-through the uart(4) requests but act 393327595Sian * as if they would come from sbbc(4) in order to avoid complications with 394327595Sian * pci(4) (actually, uart(4) isn't a real child but rather a function of 395327595Sian * sbbc(4) anyway). 396327595Sian */ 397327595Sian 398327595Sianstatic struct resource * 399327595Siansbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type, 400327595Sian int *rid, u_long start, u_long end, u_long count, u_int flags) 401327595Sian{ 402327595Sian struct sbbc_softc *sc; 403327595Sian 404327595Sian sc = device_get_softc(dev); 405327595Sian switch (type) { 406327595Sian case SYS_RES_IRQ: 407327595Sian return (bus_generic_alloc_resource(dev, dev, type, rid, start, 408327595Sian end, count, flags)); 409327595Sian case SYS_RES_MEMORY: 410327595Sian return (sc->sc_res); 411327595Sian default: 412327595Sian return (NULL); 413327595Sian } 414327595Sian} 415327595Sian 416327595Sianstatic int 417327595Siansbbc_bus_activate_resource(device_t bus, device_t child, int type, int rid, 418327595Sian struct resource *res) 419327595Sian{ 420327595Sian 421327595Sian if (type == SYS_RES_MEMORY) 422327595Sian return (0); 423327595Sian return (bus_generic_activate_resource(bus, child, type, rid, res)); 424327595Sian} 425327595Sian 426327595Sianstatic int 427327595Siansbbc_bus_deactivate_resource(device_t bus, device_t child, int type, int rid, 428327595Sian struct resource *res) 429327595Sian{ 430327595Sian 431327595Sian if (type == SYS_RES_MEMORY) 432327595Sian return (0); 433327595Sian return (bus_generic_deactivate_resource(bus, child, type, rid, res)); 434327595Sian} 435327595Sian 436327595Sianstatic int 437327595Siansbbc_bus_adjust_resource(device_t bus __unused, device_t child __unused, 438327595Sian int type __unused, struct resource *res __unused, u_long start __unused, 439327595Sian u_long end __unused) 440327595Sian{ 441327595Sian 442327595Sian return (ENXIO); 443327595Sian} 444327595Sian 445327595Sianstatic int 446327595Siansbbc_bus_release_resource(device_t dev, device_t child __unused, int type, 447327595Sian int rid, struct resource *res) 448327595Sian{ 449327595Sian 450327595Sian if (type == SYS_RES_IRQ) 451327595Sian return (bus_generic_release_resource(dev, dev, type, rid, 452327595Sian res)); 453327595Sian return (0); 454327595Sian} 455327595Sian 456327595Sianstatic struct resource_list * 457327595Siansbbc_bus_get_resource_list(device_t dev, device_t child __unused) 458327595Sian{ 459327595Sian 460327595Sian return (bus_generic_get_resource_list(dev, dev)); 461327595Sian} 462327595Sian 463327595Sianstatic int 464327595Siansbbc_bus_setup_intr(device_t dev, device_t child __unused, 465327595Sian struct resource *res, int flags, driver_filter_t *filt, 466327595Sian driver_intr_t *intr, void *arg, void **cookiep) 467327595Sian{ 468327595Sian 469327595Sian return (bus_generic_setup_intr(dev, dev, res, flags, filt, intr, arg, 470327595Sian cookiep)); 471327595Sian} 472327595Sian 473327595Sianstatic int 474327595Siansbbc_bus_teardown_intr(device_t dev, device_t child __unused, 475327595Sian struct resource *res, void *cookie) 476327595Sian{ 477327595Sian 478327595Sian return (bus_generic_teardown_intr(dev, dev, res, cookie)); 479327595Sian} 480327595Sian 481327595Sian/* 482327595Sian * internal helpers 483327595Sian */ 484327595Sianstatic int 485327595Siansbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh) 486327595Sian{ 487327595Sian char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)]; 488327595Sian bus_size_t tag; 489327595Sian phandle_t node; 490327595Sian uint32_t off, sram_toc; 491327595Sian u_int i, tags; 492327595Sian 493327595Sian if ((node = OF_finddevice("/chosen")) == -1) 494327595Sian return (ENXIO); 495327595Sian /* SRAM TOC offset defaults to 0. */ 496258210Srpaulo if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0) 497258210Srpaulo sram_toc = 0; 498261410Sian 499261410Sian bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc + 500261410Sian SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE); 501261410Sian buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0'; 502258210Srpaulo if (strcmp(buf, SBBC_TOC_MAGIC) != 0) 503258210Srpaulo return (ENXIO); 504258210Srpaulo 505258210Srpaulo tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags)); 506258210Srpaulo for (i = 0; i < tags; i++) { 507258210Srpaulo tag = sram_toc + SBBC_TOC_OFF(toc_tag) + 508258210Srpaulo i * sizeof(struct sbbc_sram_tag); 509258210Srpaulo bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag + 510258210Srpaulo SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE); 511258210Srpaulo buf[SBBC_TAG_KEY_SIZE - 1] = '\0'; 512258210Srpaulo off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset)); 513258210Srpaulo if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0) 514258210Srpaulo sbbc_scsolie = off; 515258210Srpaulo else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0) 516258210Srpaulo sbbc_scsolir = off; 517258210Srpaulo else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0) 518258210Srpaulo sbbc_solcons = off; 519258210Srpaulo else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0) 520258210Srpaulo sbbc_solscie = off; 521258210Srpaulo else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0) 522258210Srpaulo sbbc_solscir = off; 523261211Sjmg else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0) 524258210Srpaulo sbbc_toddata = off; 525258210Srpaulo } 526258210Srpaulo return (0); 527258210Srpaulo} 528258210Srpaulo 529258210Srpaulostatic const char * 530327595Siansbbc_serengeti_set_console_input(char *new) 531327595Sian{ 532327595Sian struct { 533327595Sian cell_t name; 534327595Sian cell_t nargs; 535327595Sian cell_t nreturns; 536327595Sian cell_t new; 537327595Sian cell_t old; 538327595Sian } args = { 539327595Sian (cell_t)SUNW_SETCONSINPUT, 540327595Sian 1, 541327595Sian 1, 542327595Sian }; 543327595Sian 544327595Sian args.new = (cell_t)new; 545327595Sian if (ofw_entry(&args) == -1) 546327595Sian return (NULL); 547327595Sian return ((const char *)args.old); 548327595Sian} 549258210Srpaulo 550258210Srpaulostatic inline void 551258210Srpaulosbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh) 552258210Srpaulo{ 553258210Srpaulo 554258210Srpaulo SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON); 555258210Srpaulo bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1, 556327595Sian BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 557327595Sian} 558327595Sian 559258210Srpaulo/* 560327595Sian * TOD interface 561327595Sian */ 562327595Sianstatic int 563327595Siansbbc_tod_gettime(device_t dev, struct timespec *ts) 564327595Sian{ 565327595Sian struct sbbc_softc *sc; 566327595Sian bus_space_tag_t bst; 567327595Sian bus_space_handle_t bsh; 568327595Sian 569327595Sian sc = device_get_softc(dev); 570327595Sian bst = rman_get_bustag(sc->sc_res); 571327595Sian bsh = rman_get_bushandle(sc->sc_res); 572327595Sian 573327595Sian ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) + 574327595Sian SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew)); 575327595Sian ts->tv_nsec = 0; 576327595Sian return (0); 577327595Sian} 578327595Sian 579327595Sianstatic int 580327595Siansbbc_tod_settime(device_t dev, struct timespec *ts) 581327595Sian{ 582327595Sian struct sbbc_softc *sc; 583327595Sian bus_space_tag_t bst; 584327595Sian bus_space_handle_t bsh; 585327595Sian 586327595Sian sc = device_get_softc(dev); 587327595Sian bst = rman_get_bustag(sc->sc_res); 588327595Sian bsh = rman_get_bushandle(sc->sc_res); 589327595Sian 590327595Sian SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec - 591327595Sian SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time))); 592327595Sian return (0); 593327595Sian} 594327595Sian 595258210Srpaulo/* 596258210Srpaulo * UART bus front-end 597327595Sian */ 598327595Sianstatic device_probe_t sbbc_uart_sbbc_probe; 599258210Srpaulo 600258210Srpaulostatic device_method_t sbbc_uart_sbbc_methods[] = { 601258210Srpaulo /* Device interface */ 602258210Srpaulo DEVMETHOD(device_probe, sbbc_uart_sbbc_probe), 603258210Srpaulo DEVMETHOD(device_attach, uart_bus_attach), 604258210Srpaulo DEVMETHOD(device_detach, uart_bus_detach), 605327595Sian 606327595Sian DEVMETHOD_END 607327595Sian}; 608327595Sian 609327595SianDEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods, 610327595Sian sizeof(struct uart_softc)); 611327595SianDRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, NULL, NULL); 612327595Sian 613258210Srpaulostatic int 614258210Srpaulosbbc_uart_sbbc_probe(device_t dev) 615258210Srpaulo{ 616258210Srpaulo struct uart_softc *sc; 617258210Srpaulo 618258210Srpaulo sc = device_get_softc(dev); 619327595Sian sc->sc_class = &uart_sbbc_class; 620258210Srpaulo device_set_desc(dev, "Serengeti console"); 621327595Sian return (uart_bus_probe(dev, 0, 0, SBBC_PCI_BAR, 0)); 622327595Sian} 623327595Sian 624327595Sian/* 625327595Sian * Low-level UART interface 626258210Srpaulo */ 627258210Srpaulostatic int sbbc_uart_probe(struct uart_bas *bas); 628258210Srpaulostatic void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits, 629275376Srpaulo int stopbits, int parity); 630258210Srpaulostatic void sbbc_uart_term(struct uart_bas *bas); 631258210Srpaulostatic void sbbc_uart_putc(struct uart_bas *bas, int c); 632327595Sianstatic int sbbc_uart_rxready(struct uart_bas *bas); 633327595Sianstatic int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx); 634327595Sian 635327595Sianstatic struct uart_ops sbbc_uart_ops = { 636327595Sian .probe = sbbc_uart_probe, 637327595Sian .init = sbbc_uart_init, 638327595Sian .term = sbbc_uart_term, 639258210Srpaulo .putc = sbbc_uart_putc, 640327595Sian .rxready = sbbc_uart_rxready, 641283138Srpaulo .getc = sbbc_uart_getc, 642258210Srpaulo}; 643258210Srpaulo 644258210Srpaulostatic int 645258210Srpaulosbbc_uart_probe(struct uart_bas *bas) 646258210Srpaulo{ 647258210Srpaulo bus_space_tag_t bst; 648258210Srpaulo bus_space_handle_t bsh; 649258210Srpaulo int error; 650258210Srpaulo 651258210Srpaulo sbbc_console = 1; 652258210Srpaulo bst = bas->bst; 653258210Srpaulo bsh = bas->bsh; 654283138Srpaulo error = sbbc_parse_toc(bst, bsh); 655283138Srpaulo if (error != 0) 656283138Srpaulo return (error); 657283138Srpaulo 658327595Sian if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 || 659283138Srpaulo sbbc_solscie == 0 || sbbc_solscir == 0) 660283138Srpaulo return (ENXIO); 661327595Sian 662283138Srpaulo if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) != 663327595Sian SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons + 664327595Sian SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION) 665258210Srpaulo return (ENXIO); 666327595Sian return (0); 667283138Srpaulo} 668283138Srpaulo 669327595Sianstatic void 670327595Siansbbc_uart_init(struct uart_bas *bas, int baudrate __unused, 671327595Sian int databits __unused, int stopbits __unused, int parity __unused) 672327595Sian{ 673327595Sian bus_space_tag_t bst; 674327595Sian bus_space_handle_t bsh; 675327595Sian 676327595Sian bst = bas->bst; 677327595Sian bsh = bas->bsh; 678327595Sian 679327595Sian /* Enable output to and space in from the SC interrupts. */ 680327595Sian SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) | 681327595Sian SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN); 682327595Sian uart_barrier(bas); 683327595Sian 684327595Sian /* Take over the console input. */ 685327595Sian sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT); 686327595Sian} 687327595Sian 688327595Sianstatic void 689327595Siansbbc_uart_term(struct uart_bas *bas __unused) 690258210Srpaulo{ 691258210Srpaulo 692258210Srpaulo /* Give back the console input. */ 693275376Srpaulo sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); 694275376Srpaulo} 695258210Srpaulo 696258210Srpaulostatic void 697258210Srpaulosbbc_uart_putc(struct uart_bas *bas, int c) 698258210Srpaulo{ 699258210Srpaulo bus_space_tag_t bst; 700258210Srpaulo bus_space_handle_t bsh; 701258210Srpaulo uint32_t wrptr; 702258210Srpaulo 703258210Srpaulo bst = bas->bst; 704258210Srpaulo bsh = bas->bsh; 705258210Srpaulo 706258210Srpaulo wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 707327595Sian SBBC_CONS_OFF(cons_out_wrptr)); 708258210Srpaulo SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c); 709277958Srpaulo uart_barrier(bas); 710258210Srpaulo if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons + 711258210Srpaulo SBBC_CONS_OFF(cons_out_end))) 712258210Srpaulo wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 713258210Srpaulo SBBC_CONS_OFF(cons_out_begin)); 714258210Srpaulo SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr), 715258210Srpaulo wrptr); 716327595Sian uart_barrier(bas); 717327595Sian 718258210Srpaulo SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 719258210Srpaulo SBBC_SRAM_CONS_OUT); 720258210Srpaulo uart_barrier(bas); 721327595Sian sbbc_send_intr(bst, bsh); 722258210Srpaulo} 723327595Sian 724258210Srpaulostatic int 725258210Srpaulosbbc_uart_rxready(struct uart_bas *bas) 726258210Srpaulo{ 727258210Srpaulo bus_space_tag_t bst; 728258210Srpaulo bus_space_handle_t bsh; 729327595Sian 730258210Srpaulo bst = bas->bst; 731327595Sian bsh = bas->bsh; 732327595Sian 733258210Srpaulo if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) == 734327595Sian SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr))) 735327595Sian return (0); 736327595Sian return (1); 737327595Sian} 738327595Sian 739327595Sianstatic int 740327595Siansbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) 741327595Sian{ 742327595Sian bus_space_tag_t bst; 743327595Sian bus_space_handle_t bsh; 744327595Sian int c; 745327595Sian uint32_t rdptr; 746327595Sian 747258210Srpaulo bst = bas->bst; 748258210Srpaulo bsh = bas->bsh; 749258210Srpaulo 750327595Sian uart_lock(hwmtx); 751258210Srpaulo 752327595Sian while (sbbc_uart_rxready(bas) == 0) { 753258210Srpaulo uart_unlock(hwmtx); 754258210Srpaulo DELAY(4); 755258210Srpaulo uart_lock(hwmtx); 756258210Srpaulo } 757258210Srpaulo 758305572Sgonzo rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)); 759258210Srpaulo c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr); 760258210Srpaulo uart_barrier(bas); 761258210Srpaulo if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons + 762258210Srpaulo SBBC_CONS_OFF(cons_in_end))) 763258210Srpaulo rdptr = SBBC_SRAM_READ_4(sbbc_solcons + 764258210Srpaulo SBBC_CONS_OFF(cons_in_begin)); 765258210Srpaulo SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), 766 rdptr); 767 uart_barrier(bas); 768 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 769 SBBC_SRAM_CONS_SPACE_IN); 770 uart_barrier(bas); 771 sbbc_send_intr(bst, bsh); 772 773 uart_unlock(hwmtx); 774 return (c); 775} 776 777/* 778 * High-level UART interface 779 */ 780static int sbbc_uart_bus_attach(struct uart_softc *sc); 781static int sbbc_uart_bus_detach(struct uart_softc *sc); 782static int sbbc_uart_bus_flush(struct uart_softc *sc, int what); 783static int sbbc_uart_bus_getsig(struct uart_softc *sc); 784static int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, 785 intptr_t data); 786static int sbbc_uart_bus_ipend(struct uart_softc *sc); 787static int sbbc_uart_bus_param(struct uart_softc *sc, int baudrate, 788 int databits, int stopbits, int parity); 789static int sbbc_uart_bus_probe(struct uart_softc *sc); 790static int sbbc_uart_bus_receive(struct uart_softc *sc); 791static int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig); 792static int sbbc_uart_bus_transmit(struct uart_softc *sc); 793 794static kobj_method_t sbbc_uart_methods[] = { 795 KOBJMETHOD(uart_attach, sbbc_uart_bus_attach), 796 KOBJMETHOD(uart_detach, sbbc_uart_bus_detach), 797 KOBJMETHOD(uart_flush, sbbc_uart_bus_flush), 798 KOBJMETHOD(uart_getsig, sbbc_uart_bus_getsig), 799 KOBJMETHOD(uart_ioctl, sbbc_uart_bus_ioctl), 800 KOBJMETHOD(uart_ipend, sbbc_uart_bus_ipend), 801 KOBJMETHOD(uart_param, sbbc_uart_bus_param), 802 KOBJMETHOD(uart_probe, sbbc_uart_bus_probe), 803 KOBJMETHOD(uart_receive, sbbc_uart_bus_receive), 804 KOBJMETHOD(uart_setsig, sbbc_uart_bus_setsig), 805 KOBJMETHOD(uart_transmit, sbbc_uart_bus_transmit), 806 807 DEVMETHOD_END 808}; 809 810struct uart_class uart_sbbc_class = { 811 "sbbc", 812 sbbc_uart_methods, 813 sizeof(struct uart_softc), 814 .uc_ops = &sbbc_uart_ops, 815 .uc_range = 1, 816 .uc_rclk = 0x5bbc /* arbitrary */ 817}; 818 819#define SIGCHG(c, i, s, d) \ 820 if ((c) != 0) { \ 821 i |= (((i) & (s)) != 0) ? (s) : (s) | (d); \ 822 } else { \ 823 i = (((i) & (s)) != 0) ? ((i) & ~(s)) | (d) : (i); \ 824 } 825 826static int 827sbbc_uart_bus_attach(struct uart_softc *sc) 828{ 829 struct uart_bas *bas; 830 bus_space_tag_t bst; 831 bus_space_handle_t bsh; 832 uint32_t wrptr; 833 834 bas = &sc->sc_bas; 835 bst = bas->bst; 836 bsh = bas->bsh; 837 838 uart_lock(sc->sc_hwmtx); 839 840 /* 841 * Let the current output drain before enabling interrupts. Not 842 * doing so tends to cause lost output when turning them on. 843 */ 844 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 845 SBBC_CONS_OFF(cons_out_wrptr)); 846 while (SBBC_SRAM_READ_4(sbbc_solcons + 847 SBBC_CONS_OFF(cons_out_rdptr)) != wrptr); 848 cpu_spinwait(); 849 850 /* Clear and acknowledge possibly outstanding interrupts. */ 851 SBBC_SRAM_WRITE_4(sbbc_scsolir, 0); 852 uart_barrier(bas); 853 SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, 854 SBBC_SRAM_READ_4(sbbc_scsolir)); 855 uart_barrier(bas); 856 /* Enable PCI interrupts. */ 857 SBBC_REGS_WRITE_4(SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A); 858 uart_barrier(bas); 859 /* Enable input from and output to SC as well as break interrupts. */ 860 SBBC_SRAM_WRITE_4(sbbc_scsolie, SBBC_SRAM_READ_4(sbbc_scsolie) | 861 SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK | 862 SBBC_SRAM_CONS_SPACE_OUT); 863 uart_barrier(bas); 864 865 uart_unlock(sc->sc_hwmtx); 866 return (0); 867} 868 869static int 870sbbc_uart_bus_detach(struct uart_softc *sc) 871{ 872 873 /* Give back the console input. */ 874 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); 875 return (0); 876} 877 878static int 879sbbc_uart_bus_flush(struct uart_softc *sc, int what) 880{ 881 struct uart_bas *bas; 882 bus_space_tag_t bst; 883 bus_space_handle_t bsh; 884 885 bas = &sc->sc_bas; 886 bst = bas->bst; 887 bsh = bas->bsh; 888 889 if ((what & UART_FLUSH_TRANSMITTER) != 0) 890 return (ENODEV); 891 if ((what & UART_FLUSH_RECEIVER) != 0) { 892 SBBC_SRAM_WRITE_4(sbbc_solcons + 893 SBBC_CONS_OFF(cons_in_rdptr), 894 SBBC_SRAM_READ_4(sbbc_solcons + 895 SBBC_CONS_OFF(cons_in_wrptr))); 896 uart_barrier(bas); 897 } 898 return (0); 899} 900 901static int 902sbbc_uart_bus_getsig(struct uart_softc *sc) 903{ 904 uint32_t dummy, new, old, sig; 905 906 do { 907 old = sc->sc_hwsig; 908 sig = old; 909 dummy = 0; 910 SIGCHG(dummy, sig, SER_CTS, SER_DCTS); 911 SIGCHG(dummy, sig, SER_DCD, SER_DDCD); 912 SIGCHG(dummy, sig, SER_DSR, SER_DDSR); 913 new = sig & ~SER_MASK_DELTA; 914 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 915 return (sig); 916} 917 918static int 919sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) 920{ 921 int error; 922 923 error = 0; 924 uart_lock(sc->sc_hwmtx); 925 switch (request) { 926 case UART_IOCTL_BAUD: 927 *(int*)data = 9600; /* arbitrary */ 928 break; 929 default: 930 error = EINVAL; 931 break; 932 } 933 uart_unlock(sc->sc_hwmtx); 934 return (error); 935} 936 937static int 938sbbc_uart_bus_ipend(struct uart_softc *sc) 939{ 940 struct uart_bas *bas; 941 bus_space_tag_t bst; 942 bus_space_handle_t bsh; 943 int ipend; 944 uint32_t reason, status; 945 946 bas = &sc->sc_bas; 947 bst = bas->bst; 948 bsh = bas->bsh; 949 950 uart_lock(sc->sc_hwmtx); 951 status = SBBC_REGS_READ_4(SBBC_PCI_INT_STATUS); 952 if (status == 0) { 953 uart_unlock(sc->sc_hwmtx); 954 return (0); 955 } 956 957 /* 958 * Unfortunately, we can't use compare and swap for non-cachable 959 * memory. 960 */ 961 reason = SBBC_SRAM_READ_4(sbbc_scsolir); 962 SBBC_SRAM_WRITE_4(sbbc_scsolir, 0); 963 uart_barrier(bas); 964 /* Acknowledge the interrupt. */ 965 SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, status); 966 uart_barrier(bas); 967 968 uart_unlock(sc->sc_hwmtx); 969 970 ipend = 0; 971 if ((reason & SBBC_SRAM_CONS_IN) != 0) 972 ipend |= SER_INT_RXREADY; 973 if ((reason & SBBC_SRAM_CONS_BRK) != 0) 974 ipend |= SER_INT_BREAK; 975 if ((reason & SBBC_SRAM_CONS_SPACE_OUT) != 0 && 976 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) == 977 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr))) 978 ipend |= SER_INT_TXIDLE; 979 return (ipend); 980} 981 982static int 983sbbc_uart_bus_param(struct uart_softc *sc __unused, int baudrate __unused, 984 int databits __unused, int stopbits __unused, int parity __unused) 985{ 986 987 return (0); 988} 989 990static int 991sbbc_uart_bus_probe(struct uart_softc *sc) 992{ 993 struct uart_bas *bas; 994 bus_space_tag_t bst; 995 bus_space_handle_t bsh; 996 997 if (sbbc_console != 0) { 998 bas = &sc->sc_bas; 999 bst = bas->bst; 1000 bsh = bas->bsh; 1001 sc->sc_rxfifosz = SBBC_SRAM_READ_4(sbbc_solcons + 1002 SBBC_CONS_OFF(cons_in_end)) - SBBC_SRAM_READ_4(sbbc_solcons + 1003 SBBC_CONS_OFF(cons_in_begin)) - 1; 1004 sc->sc_txfifosz = SBBC_SRAM_READ_4(sbbc_solcons + 1005 SBBC_CONS_OFF(cons_out_end)) - SBBC_SRAM_READ_4(sbbc_solcons + 1006 SBBC_CONS_OFF(cons_out_begin)) - 1; 1007 return (0); 1008 } 1009 return (ENXIO); 1010} 1011 1012static int 1013sbbc_uart_bus_receive(struct uart_softc *sc) 1014{ 1015 struct uart_bas *bas; 1016 bus_space_tag_t bst; 1017 bus_space_handle_t bsh; 1018 int c; 1019 uint32_t end, rdptr, wrptr; 1020 1021 bas = &sc->sc_bas; 1022 bst = bas->bst; 1023 bsh = bas->bsh; 1024 1025 uart_lock(sc->sc_hwmtx); 1026 1027 end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end)); 1028 rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)); 1029 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr)); 1030 while (rdptr != wrptr) { 1031 if (uart_rx_full(sc) != 0) { 1032 sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; 1033 break; 1034 } 1035 c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr); 1036 uart_rx_put(sc, c); 1037 if (++rdptr == end) 1038 rdptr = SBBC_SRAM_READ_4(sbbc_solcons + 1039 SBBC_CONS_OFF(cons_in_begin)); 1040 } 1041 uart_barrier(bas); 1042 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), 1043 rdptr); 1044 uart_barrier(bas); 1045 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 1046 SBBC_SRAM_CONS_SPACE_IN); 1047 uart_barrier(bas); 1048 sbbc_send_intr(bst, bsh); 1049 1050 uart_unlock(sc->sc_hwmtx); 1051 return (0); 1052} 1053 1054static int 1055sbbc_uart_bus_setsig(struct uart_softc *sc, int sig) 1056{ 1057 struct uart_bas *bas; 1058 uint32_t new, old; 1059 1060 bas = &sc->sc_bas; 1061 do { 1062 old = sc->sc_hwsig; 1063 new = old; 1064 if ((sig & SER_DDTR) != 0) { 1065 SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); 1066 } 1067 if ((sig & SER_DRTS) != 0) { 1068 SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); 1069 } 1070 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); 1071 return (0); 1072} 1073 1074static int 1075sbbc_uart_bus_transmit(struct uart_softc *sc) 1076{ 1077 struct uart_bas *bas; 1078 bus_space_tag_t bst; 1079 bus_space_handle_t bsh; 1080 int i; 1081 uint32_t end, wrptr; 1082 1083 bas = &sc->sc_bas; 1084 bst = bas->bst; 1085 bsh = bas->bsh; 1086 1087 uart_lock(sc->sc_hwmtx); 1088 1089 end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end)); 1090 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 1091 SBBC_CONS_OFF(cons_out_wrptr)); 1092 for (i = 0; i < sc->sc_txdatasz; i++) { 1093 SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, sc->sc_txbuf[i]); 1094 if (++wrptr == end) 1095 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + 1096 SBBC_CONS_OFF(cons_out_begin)); 1097 } 1098 uart_barrier(bas); 1099 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr), 1100 wrptr); 1101 uart_barrier(bas); 1102 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | 1103 SBBC_SRAM_CONS_OUT); 1104 uart_barrier(bas); 1105 sbbc_send_intr(bst, bsh); 1106 sc->sc_txbusy = 1; 1107 1108 uart_unlock(sc->sc_hwmtx); 1109 return (0); 1110} 1111