From owner-svn-src-head@FreeBSD.ORG Sat Apr 10 11:52:13 2010 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 381D1106564A; Sat, 10 Apr 2010 11:52:13 +0000 (UTC) (envelope-from marius@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 257178FC13; Sat, 10 Apr 2010 11:52:13 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o3ABqD7Q071333; Sat, 10 Apr 2010 11:52:13 GMT (envelope-from marius@svn.freebsd.org) Received: (from marius@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o3ABqDfi071326; Sat, 10 Apr 2010 11:52:13 GMT (envelope-from marius@svn.freebsd.org) Message-Id: <201004101152.o3ABqDfi071326@svn.freebsd.org> From: Marius Strobl Date: Sat, 10 Apr 2010 11:52:13 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r206451 - in head/sys: conf dev/uart sparc64/conf sparc64/pci X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 10 Apr 2010 11:52:13 -0000 Author: marius Date: Sat Apr 10 11:52:12 2010 New Revision: 206451 URL: http://svn.freebsd.org/changeset/base/206451 Log: Add sbbc(4), a driver for the BootBus controller found in Serengeti and StarCat systems which provides time-of-day services for both as well as console service for Serengeti, i.e. Sun Fire V1280. While the latter is described with a device type of serial in the OFW device tree, it isn't actually an UART. Nevertheless the console service is handled by uart(4) as this allowed to re-use quite a bit of MD and MI code. Actually, this idea is stolen from Linux which interfaces the sun4v hypervisor console with the Linux counterpart of uart(4). Added: head/sys/sparc64/pci/sbbc.c (contents, props changed) Modified: head/sys/conf/files.sparc64 head/sys/dev/uart/uart.h head/sys/dev/uart/uart_cpu_sparc64.c head/sys/sparc64/conf/GENERIC head/sys/sparc64/conf/NOTES Modified: head/sys/conf/files.sparc64 ============================================================================== --- head/sys/conf/files.sparc64 Sat Apr 10 11:13:51 2010 (r206450) +++ head/sys/conf/files.sparc64 Sat Apr 10 11:52:12 2010 (r206451) @@ -79,6 +79,7 @@ sparc64/pci/ofw_pcib.c optional pci sparc64/pci/ofw_pcib_subr.c optional pci sparc64/pci/ofw_pcibus.c optional pci sparc64/pci/psycho.c optional pci +sparc64/pci/sbbc.c optional uart sbbc sparc64/pci/schizo.c optional pci sparc64/sbus/dma_sbus.c optional sbus sparc64/sbus/sbus.c optional sbus Modified: head/sys/dev/uart/uart.h ============================================================================== --- head/sys/dev/uart/uart.h Sat Apr 10 11:13:51 2010 (r206450) +++ head/sys/dev/uart/uart.h Sat Apr 10 11:52:12 2010 (r206451) @@ -67,6 +67,7 @@ struct uart_class; extern struct uart_class uart_ns8250_class __attribute__((weak)); extern struct uart_class uart_quicc_class __attribute__((weak)); extern struct uart_class uart_sab82532_class __attribute__((weak)); +extern struct uart_class uart_sbbc_class __attribute__((weak)); extern struct uart_class uart_z8530_class __attribute__((weak)); #ifdef PC98 Modified: head/sys/dev/uart/uart_cpu_sparc64.c ============================================================================== --- head/sys/dev/uart/uart_cpu_sparc64.c Sat Apr 10 11:13:51 2010 (r206450) +++ head/sys/dev/uart/uart_cpu_sparc64.c Sat Apr 10 11:52:12 2010 (r206451) @@ -133,6 +133,14 @@ uart_cpu_getdev_console(phandle_t option return (-1); if (strcmp(buf, "serial") != 0) return (-1); + /* For a Serengeti console device point to the bootbus controller. */ + if (OF_getprop(input, "name", buf, sizeof(buf)) > 0 && + !strcmp(buf, "sgcn")) { + if ((chosen = OF_finddevice("/chosen")) == -1) + return (-1); + if (OF_getprop(chosen, "iosram", &input, sizeof(input)) == -1) + return (-1); + } return (input); } @@ -258,6 +266,9 @@ uart_cpu_getdev(int devtype, struct uart !strcmp(compat, "su16552")) { class = &uart_ns8250_class; di->bas.chan = 0; + } else if (!strcmp(compat, "sgsbbc")) { + class = &uart_sbbc_class; + di->bas.chan = 0; } if (class == NULL) return (ENXIO); Modified: head/sys/sparc64/conf/GENERIC ============================================================================== --- head/sys/sparc64/conf/GENERIC Sat Apr 10 11:13:51 2010 (r206450) +++ head/sys/sparc64/conf/GENERIC Sat Apr 10 11:52:12 2010 (r206451) @@ -143,6 +143,9 @@ device mk48txx # Mostek MK48Txx clocks device rtc # rtc (really a front-end for the MC146818) device mc146818 # Motorola MC146818 and compatible clocks device epic # Sun Fire V215/V245 LEDs +device sbbc # Sun BootBus controller (time-of-day clock for + # Serengeti and StarCat, console for Serengeti, + # requires device uart) # Serial (COM) ports device puc # Multi-channel uarts Modified: head/sys/sparc64/conf/NOTES ============================================================================== --- head/sys/sparc64/conf/NOTES Sat Apr 10 11:13:51 2010 (r206450) +++ head/sys/sparc64/conf/NOTES Sat Apr 10 11:52:12 2010 (r206451) @@ -37,6 +37,7 @@ device eeprom # eeprom (really a front device mk48txx # Mostek MK48Txx clocks device rtc # rtc (really a front-end for the MC146818) device mc146818 # Motorola MC146818 and compatible clocks +device sbbc # Sun BootBus controller # # Optional devices: Added: head/sys/sparc64/pci/sbbc.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/sparc64/pci/sbbc.c Sat Apr 10 11:52:12 2010 (r206451) @@ -0,0 +1,1074 @@ +/* $OpenBSD: sbbc.c,v 1.7 2009/11/09 17:53:39 nicm Exp $ */ +/* + * Copyright (c) 2008 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/*- + * Copyright (c) 2010 Marius Strobl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "clock_if.h" +#include "uart_if.h" + +#define SBBC_PCI_BAR PCIR_BAR(0) +#define SBBC_PCI_VENDOR 0x108e +#define SBBC_PCI_PRODUCT 0xc416 + +#define SBBC_REGS_OFFSET 0x800000 +#define SBBC_REGS_SIZE 0x6230 +#define SBBC_EPLD_OFFSET 0x8e0000 +#define SBBC_EPLD_SIZE 0x20 +#define SBBC_SRAM_OFFSET 0x900000 +#define SBBC_SRAM_SIZE 0x20000 /* 128KB SRAM */ + +#define SBBC_PCI_INT_STATUS 0x2320 +#define SBBC_PCI_INT_ENABLE 0x2330 +#define SBBC_PCI_ENABLE_INT_A 0x11 + +#define SBBC_EPLD_INTERRUPT 0x13 +#define SBBC_EPLD_INTERRUPT_ON 0x01 + +#define SBBC_SRAM_CONS_IN 0x00000001 +#define SBBC_SRAM_CONS_OUT 0x00000002 +#define SBBC_SRAM_CONS_BRK 0x00000004 +#define SBBC_SRAM_CONS_SPACE_IN 0x00000008 +#define SBBC_SRAM_CONS_SPACE_OUT 0x00000010 + +#define SBBC_TAG_KEY_SIZE 8 +#define SBBC_TAG_KEY_SCSOLIE "SCSOLIE" /* SC -> OS int. enable */ +#define SBBC_TAG_KEY_SCSOLIR "SCSOLIR" /* SC -> OS int. reason */ +#define SBBC_TAG_KEY_SOLCONS "SOLCONS" /* OS console buffer */ +#define SBBC_TAG_KEY_SOLSCIE "SOLSCIE" /* OS -> SC int. enable */ +#define SBBC_TAG_KEY_SOLSCIR "SOLSCIR" /* OS -> SC int. reason */ +#define SBBC_TAG_KEY_TODDATA "TODDATA" /* OS TOD struct */ +#define SBBC_TAG_OFF(x) offsetof(struct sbbc_sram_tag, x) + +struct sbbc_sram_tag { + char tag_key[SBBC_TAG_KEY_SIZE]; + uint32_t tag_size; + uint32_t tag_offset; +} __packed; + +#define SBBC_TOC_MAGIC "TOCSRAM" +#define SBBC_TOC_MAGIC_SIZE 8 +#define SBBC_TOC_TAGS_MAX 32 +#define SBBC_TOC_OFF(x) offsetof(struct sbbc_sram_toc, x) + +struct sbbc_sram_toc { + char toc_magic[SBBC_TOC_MAGIC_SIZE]; + uint8_t toc_reserved; + uint8_t toc_type; + uint16_t toc_version; + uint32_t toc_ntags; + struct sbbc_sram_tag toc_tag[SBBC_TOC_TAGS_MAX]; +} __packed; + +#define SBBC_TOD_MAGIC 0x54443100 /* "TD1" */ +#define SBBC_TOD_VERSION 1 +#define SBBC_TOD_OFF(x) offsetof(struct sbbc_sram_tod, x) + +struct sbbc_sram_tod { + uint32_t tod_magic; + uint32_t tod_version; + uint64_t tod_time; + uint64_t tod_skew; + uint32_t tod_reserved; + uint32_t tod_heartbeat; + uint32_t tod_timeout; +} __packed; + +#define SBBC_CONS_MAGIC 0x434f4e00 /* "CON" */ +#define SBBC_CONS_VERSION 1 +#define SBBC_CONS_OFF(x) offsetof(struct sbbc_sram_cons, x) + +struct sbbc_sram_cons { + uint32_t cons_magic; + uint32_t cons_version; + uint32_t cons_size; + + uint32_t cons_in_begin; + uint32_t cons_in_end; + uint32_t cons_in_rdptr; + uint32_t cons_in_wrptr; + + uint32_t cons_out_begin; + uint32_t cons_out_end; + uint32_t cons_out_rdptr; + uint32_t cons_out_wrptr; +} __packed; + +struct sbbc_softc { + struct resource *sc_res; +}; + +#define SBBC_READ_N(wdth, offs) \ + bus_space_read_ ## wdth((bst), (bsh), (offs)) +#define SBBC_WRITE_N(wdth, offs, val) \ + bus_space_write_ ## wdth((bst), (bsh), (offs), (val)) + +#define SBBC_READ_1(offs) \ + SBBC_READ_N(1, (offs)) +#define SBBC_READ_2(offs) \ + bswap16(SBBC_READ_N(2, (offs))) +#define SBBC_READ_4(offs) \ + bswap32(SBBC_READ_N(4, (offs))) +#define SBBC_READ_8(offs) \ + bswap64(SBBC_READ_N(8, (offs))) +#define SBBC_WRITE_1(offs, val) \ + SBBC_WRITE_N(1, (offs), (val)) +#define SBBC_WRITE_2(offs, val) \ + SBBC_WRITE_N(2, (offs), bswap16(val)) +#define SBBC_WRITE_4(offs, val) \ + SBBC_WRITE_N(4, (offs), bswap32(val)) +#define SBBC_WRITE_8(offs, val) \ + SBBC_WRITE_N(8, (offs), bswap64(val)) + +#define SBBC_REGS_READ_1(offs) \ + SBBC_READ_1((offs) + SBBC_REGS_OFFSET) +#define SBBC_REGS_READ_2(offs) \ + SBBC_READ_2((offs) + SBBC_REGS_OFFSET) +#define SBBC_REGS_READ_4(offs) \ + SBBC_READ_4((offs) + SBBC_REGS_OFFSET) +#define SBBC_REGS_READ_8(offs) \ + SBBC_READ_8((offs) + SBBC_REGS_OFFSET) +#define SBBC_REGS_WRITE_1(offs, val) \ + SBBC_WRITE_1((offs) + SBBC_REGS_OFFSET, (val)) +#define SBBC_REGS_WRITE_2(offs, val) \ + SBBC_WRITE_2((offs) + SBBC_REGS_OFFSET, (val)) +#define SBBC_REGS_WRITE_4(offs, val) \ + SBBC_WRITE_4((offs) + SBBC_REGS_OFFSET, (val)) +#define SBBC_REGS_WRITE_8(offs, val) \ + SBBC_WRITE_8((offs) + SBBC_REGS_OFFSET, (val)) + +#define SBBC_EPLD_READ_1(offs) \ + SBBC_READ_1((offs) + SBBC_EPLD_OFFSET) +#define SBBC_EPLD_READ_2(offs) \ + SBBC_READ_2((offs) + SBBC_EPLD_OFFSET) +#define SBBC_EPLD_READ_4(offs) \ + SBBC_READ_4((offs) + SBBC_EPLD_OFFSET) +#define SBBC_EPLD_READ_8(offs) \ + SBBC_READ_8((offs) + SBBC_EPLD_OFFSET) +#define SBBC_EPLD_WRITE_1(offs, val) \ + SBBC_WRITE_1((offs) + SBBC_EPLD_OFFSET, (val)) +#define SBBC_EPLD_WRITE_2(offs, val) \ + SBBC_WRITE_2((offs) + SBBC_EPLD_OFFSET, (val)) +#define SBBC_EPLD_WRITE_4(offs, val) \ + SBBC_WRITE_4((offs) + SBBC_EPLD_OFFSET, (val)) +#define SBBC_EPLD_WRITE_8(offs, val) \ + SBBC_WRITE_8((offs) + SBBC_EPLD_OFFSET, (val)) + +#define SBBC_SRAM_READ_1(offs) \ + SBBC_READ_1((offs) + SBBC_SRAM_OFFSET) +#define SBBC_SRAM_READ_2(offs) \ + SBBC_READ_2((offs) + SBBC_SRAM_OFFSET) +#define SBBC_SRAM_READ_4(offs) \ + SBBC_READ_4((offs) + SBBC_SRAM_OFFSET) +#define SBBC_SRAM_READ_8(offs) \ + SBBC_READ_8((offs) + SBBC_SRAM_OFFSET) +#define SBBC_SRAM_WRITE_1(offs, val) \ + SBBC_WRITE_1((offs) + SBBC_SRAM_OFFSET, (val)) +#define SBBC_SRAM_WRITE_2(offs, val) \ + SBBC_WRITE_2((offs) + SBBC_SRAM_OFFSET, (val)) +#define SBBC_SRAM_WRITE_4(offs, val) \ + SBBC_WRITE_4((offs) + SBBC_SRAM_OFFSET, (val)) +#define SBBC_SRAM_WRITE_8(offs, val) \ + SBBC_WRITE_8((offs) + SBBC_SRAM_OFFSET, (val)) + +#define SUNW_SETCONSINPUT "SUNW,set-console-input" +#define SUNW_SETCONSINPUT_CLNT "CON_CLNT" +#define SUNW_SETCONSINPUT_OBP "CON_OBP" + +static u_int sbbc_console; + +static uint32_t sbbc_scsolie; +static uint32_t sbbc_scsolir; +static uint32_t sbbc_solcons; +static uint32_t sbbc_solscie; +static uint32_t sbbc_solscir; +static uint32_t sbbc_toddata; + +/* + * internal helpers + */ +static int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh); +static inline void sbbc_send_intr(bus_space_tag_t bst, + bus_space_handle_t bsh); +static const char *sbbc_serengeti_set_console_input(char *new); + +/* + * SBBC PCI interface + */ +static bus_alloc_resource_t sbbc_bus_alloc_resource; +static bus_release_resource_t sbbc_bus_release_resource; +static bus_get_resource_list_t sbbc_bus_get_resource_list; +static bus_setup_intr_t sbbc_bus_setup_intr; +static bus_teardown_intr_t sbbc_bus_teardown_intr; + +static device_attach_t sbbc_pci_attach; +static device_probe_t sbbc_pci_probe; + +static clock_gettime_t sbbc_tod_gettime; +static clock_settime_t sbbc_tod_settime; + +static device_method_t sbbc_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbbc_pci_probe), + DEVMETHOD(device_attach, sbbc_pci_attach), + + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, sbbc_bus_alloc_resource), + DEVMETHOD(bus_release_resource, sbbc_bus_release_resource), + DEVMETHOD(bus_setup_intr, sbbc_bus_setup_intr), + DEVMETHOD(bus_teardown_intr, sbbc_bus_teardown_intr), + DEVMETHOD(bus_get_resource_list, sbbc_bus_get_resource_list), + + /* clock interface */ + DEVMETHOD(clock_gettime, sbbc_tod_gettime), + DEVMETHOD(clock_settime, sbbc_tod_settime), + + KOBJMETHOD_END +}; + +static devclass_t sbbc_devclass; + +DEFINE_CLASS_0(sbbc, sbbc_driver, sbbc_pci_methods, sizeof(struct sbbc_softc)); +DRIVER_MODULE(sbbc, pci, sbbc_driver, sbbc_devclass, 0, 0); + +static int +sbbc_pci_probe(device_t dev) +{ + + if (pci_get_vendor(dev) == SBBC_PCI_VENDOR && + pci_get_device(dev) == SBBC_PCI_PRODUCT) { + device_set_desc(dev, "Sun BootBus controller"); + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +static int +sbbc_pci_attach(device_t dev) +{ + struct sbbc_softc *sc; + struct timespec ts; + device_t child; + bus_space_tag_t bst; + bus_space_handle_t bsh; + phandle_t node; + int error, rid; + uint32_t val; + + /* Nothing to to if we're not the chosen one. */ + if ((node = OF_finddevice("/chosen")) == -1) { + device_printf(dev, "failed to find /chosen\n"); + return (ENXIO); + } + if (OF_getprop(node, "iosram", &node, sizeof(node)) == -1) { + device_printf(dev, "failed to get iosram\n"); + return (ENXIO); + } + if (node != ofw_bus_get_node(dev)) + return (0); + + sc = device_get_softc(dev); + rid = SBBC_PCI_BAR; + /* + * Note that we don't activate the resource so it's not mapped twice + * but only once by the the firmware. + */ + sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 0); + if (sc->sc_res == NULL) { + device_printf(dev, "failed to allocate resources\n"); + return (ENXIO); + } + bst = rman_get_bustag(sc->sc_res); + bsh = rman_get_bushandle(sc->sc_res); + if (sbbc_console != 0) { + /* Once again the interrupt pin isn't set. */ + if (pci_get_intpin(dev) == 0) + pci_set_intpin(dev, 1); + child = device_add_child(dev, NULL, -1); + if (child == NULL) + device_printf(dev, "failed to add UART device\n"); + error = bus_generic_attach(dev); + if (error != 0) + device_printf(dev, "failed to attach UART device\n"); + } else { + error = sbbc_parse_toc(rman_get_bustag(sc->sc_res), + rman_get_bushandle(sc->sc_res)); + if (error != 0) { + device_printf(dev, "failed to parse TOC\n"); + if (sbbc_console != 0) { + bus_release_resource(dev, SYS_RES_MEMORY, rid, + sc->sc_res); + return (error); + } + } + } + if (sbbc_toddata != 0) { + if ((val = SBBC_SRAM_READ_4(sbbc_toddata + + SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC) + device_printf(dev, "invalid TOD magic %#x\n", val); + else if ((val = SBBC_SRAM_READ_4(sbbc_toddata + + SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION) + device_printf(dev, "invalid TOD version %#x\n", val); + else { + clock_register(dev, 1000000); /* 1 sec. resolution */ + if (bootverbose) { + sbbc_tod_gettime(dev, &ts); + device_printf(dev, + "current time: %ld.%09ld\n", + (long)ts.tv_sec, ts.tv_nsec); + } + } + } + return (0); +} + +/* + * Note that the bus methods don't pass-through the uart(4) requests but act + * as if they would come from sbbc(4) in order to avoid complications with + * pci(4) (actually, uart(4) isn't a real child but rather a function of + * sbbc(4) anyway). + */ + +static struct resource * +sbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type, + int *rid, u_long start, u_long end, u_long count, u_int flags) +{ + struct sbbc_softc *sc; + + sc = device_get_softc(dev); + switch (type) { + case SYS_RES_IRQ: + return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, type, + rid, start, end, count, flags)); + case SYS_RES_MEMORY: + return (sc->sc_res); + default: + return (NULL); + /* NOTREACHED */ + } +} + +static int +sbbc_bus_release_resource(device_t dev, device_t child __unused, int type, + int rid, struct resource *res) +{ + + if (type == SYS_RES_IRQ) + return (BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, + type, rid, res)); + return (0); +} + +static struct resource_list * +sbbc_bus_get_resource_list(device_t dev, device_t child __unused) +{ + + return (BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev)); +} + +static int +sbbc_bus_setup_intr(device_t dev, device_t child __unused, + struct resource *res, int flags, driver_filter_t *filt, + driver_intr_t *intr, void *arg, void **cookiep) +{ + + return (BUS_SETUP_INTR(device_get_parent(dev), dev, res, flags, filt, + intr, arg, cookiep)); +} + +static int +sbbc_bus_teardown_intr(device_t dev, device_t child __unused, + struct resource *res, void *cookie) +{ + + return (BUS_TEARDOWN_INTR(device_get_parent(dev), dev, res, cookie)); +} + +/* + * internal helpers + */ +static int +sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh) +{ + char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)]; + bus_size_t tag; + phandle_t node; + uint32_t off, sram_toc; + u_int i, tags; + + if ((node = OF_finddevice("/chosen")) == -1) + return (ENXIO); + /* SRAM TOC offset defaults to 0. */ + if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0) + sram_toc = 0; + + bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc + + SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE); + buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0'; + if (strcmp(buf, SBBC_TOC_MAGIC) != 0) + return (ENXIO); + + tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags)); + for (i = 0; i < tags; i++) { + tag = sram_toc + SBBC_TOC_OFF(toc_tag) + + i * sizeof(struct sbbc_sram_tag); + bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag + + SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE); + buf[SBBC_TAG_KEY_SIZE - 1] = '\0'; + off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset)); + if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0) + sbbc_scsolie = off; + else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0) + sbbc_scsolir = off; + else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0) + sbbc_solcons = off; + else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0) + sbbc_solscie = off; + else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0) + sbbc_solscir = off; + else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0) + sbbc_toddata = off; + } + return (0); +} + +static const char * +sbbc_serengeti_set_console_input(char *new) +{ + struct { + cell_t name; + cell_t nargs; + cell_t nreturns; + cell_t new; + cell_t old; + } args = { + (cell_t)SUNW_SETCONSINPUT, + 1, + 1, + }; + + args.new = (cell_t)new; + if (ofw_entry(&args) == -1) + return (NULL); + return ((const char *)args.old); +} + +static inline void +sbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh) +{ + + SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON); + bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); +} + +/* + * TOD interface + */ +static int +sbbc_tod_gettime(device_t dev, struct timespec *ts) +{ + struct sbbc_softc *sc; + bus_space_tag_t bst; + bus_space_handle_t bsh; + + sc = device_get_softc(dev); + bst = rman_get_bustag(sc->sc_res); + bsh = rman_get_bushandle(sc->sc_res); + + ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) + + SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew)); + ts->tv_nsec = 0; + return (0); +} + +static int +sbbc_tod_settime(device_t dev, struct timespec *ts) +{ + struct sbbc_softc *sc; + bus_space_tag_t bst; + bus_space_handle_t bsh; + + sc = device_get_softc(dev); + bst = rman_get_bustag(sc->sc_res); + bsh = rman_get_bushandle(sc->sc_res); + + SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec - + SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time))); + return (0); +} + +/* + * UART bus front-end + */ +static device_probe_t sbbc_uart_sbbc_probe; + +static device_method_t sbbc_uart_sbbc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbbc_uart_sbbc_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + + KOBJMETHOD_END +}; + +DEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods, + sizeof(struct uart_softc)); +DRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, 0, 0); + +static int +sbbc_uart_sbbc_probe(device_t dev) +{ + struct uart_softc *sc; + + sc = device_get_softc(dev); + sc->sc_class = &uart_sbbc_class; + device_set_desc(dev, "Serengeti console"); + return (uart_bus_probe(dev, 0, 0, SBBC_PCI_BAR, 0)); +} + +/* + * Low-level UART interface + */ +static int sbbc_uart_probe(struct uart_bas *bas); +static void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits, + int stopbits, int parity); +static void sbbc_uart_term(struct uart_bas *bas); +static void sbbc_uart_putc(struct uart_bas *bas, int c); +static int sbbc_uart_rxready(struct uart_bas *bas); +static int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx); + +static struct uart_ops sbbc_uart_ops = { + .probe = sbbc_uart_probe, + .init = sbbc_uart_init, + .term = sbbc_uart_term, + .putc = sbbc_uart_putc, + .rxready = sbbc_uart_rxready, + .getc = sbbc_uart_getc, +}; + +static int +sbbc_uart_probe(struct uart_bas *bas) +{ + bus_space_tag_t bst; + bus_space_handle_t bsh; + int error; + + sbbc_console = 1; + bst = bas->bst; + bsh = bas->bsh; + error = sbbc_parse_toc(bst, bsh); + if (error != 0) + return (error); + + if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 || + sbbc_solscie == 0 || sbbc_solscir == 0) + return (ENXIO); + + if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) != + SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION) + return (ENXIO); + return (0); +} + +static void +sbbc_uart_init(struct uart_bas *bas, int baudrate __unused, + int databits __unused, int stopbits __unused, int parity __unused) +{ + bus_space_tag_t bst; + bus_space_handle_t bsh; + + bst = bas->bst; + bsh = bas->bsh; + + /* Enable output to and space in from the SC interrupts. */ + SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) | + SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN); + uart_barrier(bas); + + /* Take over the console input. */ + sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT); +} + +static void +sbbc_uart_term(struct uart_bas *bas __unused) +{ + + /* Give back the console input. */ + sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); +} + +static void +sbbc_uart_putc(struct uart_bas *bas, int c) +{ + bus_space_tag_t bst; + bus_space_handle_t bsh; + uint32_t wrptr; + + bst = bas->bst; + bsh = bas->bsh; + + wrptr = SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_out_wrptr)); + SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c); + uart_barrier(bas); + if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_out_end))) + wrptr = SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_out_begin)); + SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr), + wrptr); + uart_barrier(bas); + + SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | + SBBC_SRAM_CONS_OUT); + uart_barrier(bas); + sbbc_send_intr(bst, bsh); +} + +static int +sbbc_uart_rxready(struct uart_bas *bas) +{ + bus_space_tag_t bst; + bus_space_handle_t bsh; + + bst = bas->bst; + bsh = bas->bsh; + + if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) == + SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr))) + return (0); + return (1); +} + +static int +sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) +{ + bus_space_tag_t bst; + bus_space_handle_t bsh; + int c; + uint32_t rdptr; + + bst = bas->bst; + bsh = bas->bsh; + + uart_lock(hwmtx); + + while (sbbc_uart_rxready(bas) == 0) { + uart_unlock(hwmtx); + DELAY(4); + uart_lock(hwmtx); + } + + rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)); + c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr); + uart_barrier(bas); + if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_in_end))) + rdptr = SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_in_begin)); + SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), + rdptr); + uart_barrier(bas); + SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | + SBBC_SRAM_CONS_SPACE_IN); + uart_barrier(bas); + sbbc_send_intr(bst, bsh); + + uart_unlock(hwmtx); + return (c); +} + +/* + * High-level UART interface + */ +static int sbbc_uart_bus_attach(struct uart_softc *sc); +static int sbbc_uart_bus_detach(struct uart_softc *sc); +static int sbbc_uart_bus_flush(struct uart_softc *sc, int what); +static int sbbc_uart_bus_getsig(struct uart_softc *sc); +static int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, + intptr_t data); +static int sbbc_uart_bus_ipend(struct uart_softc *sc); +static int sbbc_uart_bus_param(struct uart_softc *sc, int baudrate, + int databits, int stopbits, int parity); +static int sbbc_uart_bus_probe(struct uart_softc *sc); +static int sbbc_uart_bus_receive(struct uart_softc *sc); +static int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig); +static int sbbc_uart_bus_transmit(struct uart_softc *sc); + +static kobj_method_t sbbc_uart_methods[] = { + KOBJMETHOD(uart_attach, sbbc_uart_bus_attach), + KOBJMETHOD(uart_detach, sbbc_uart_bus_detach), + KOBJMETHOD(uart_flush, sbbc_uart_bus_flush), + KOBJMETHOD(uart_getsig, sbbc_uart_bus_getsig), + KOBJMETHOD(uart_ioctl, sbbc_uart_bus_ioctl), + KOBJMETHOD(uart_ipend, sbbc_uart_bus_ipend), + KOBJMETHOD(uart_param, sbbc_uart_bus_param), + KOBJMETHOD(uart_probe, sbbc_uart_bus_probe), + KOBJMETHOD(uart_receive, sbbc_uart_bus_receive), + KOBJMETHOD(uart_setsig, sbbc_uart_bus_setsig), + KOBJMETHOD(uart_transmit, sbbc_uart_bus_transmit), + + KOBJMETHOD_END +}; + +struct uart_class uart_sbbc_class = { + "sbbc", + sbbc_uart_methods, + sizeof(struct uart_softc), + .uc_ops = &sbbc_uart_ops, + .uc_range = 1, + .uc_rclk = 0x5bbc /* arbitrary */ +}; + +#define SIGCHG(c, i, s, d) \ + if ((c) != 0) { \ + i |= (((i) & (s)) != 0) ? (s) : (s) | (d); \ + } else { \ + i = (((i) & (s)) != 0) ? ((i) & ~(s)) | (d) : (i); \ + } + +static int +sbbc_uart_bus_attach(struct uart_softc *sc) +{ + struct uart_bas *bas; + bus_space_tag_t bst; + bus_space_handle_t bsh; + uint32_t wrptr; + + bas = &sc->sc_bas; + bst = bas->bst; + bsh = bas->bsh; + + sc->sc_rxfifosz = SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_in_end)) - SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_in_begin)) - 1; + sc->sc_txfifosz = SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_out_end)) - SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_out_begin)) - 1; + + uart_lock(sc->sc_hwmtx); + + /* + * Let the current output drain before enabling interrupts. Not + * doing so tends to cause lost output when turning them on. + */ + wrptr = SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_out_wrptr)); + while (SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_out_rdptr)) != wrptr); + cpu_spinwait(); + + /* Clear and acknowledge possibly outstanding interrupts. */ + SBBC_SRAM_WRITE_4(sbbc_scsolir, 0); + uart_barrier(bas); + SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, + SBBC_SRAM_READ_4(sbbc_scsolir)); + uart_barrier(bas); + /* Enable PCI interrupts. */ + SBBC_REGS_WRITE_4(SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A); + uart_barrier(bas); + /* Enable input from and output to SC as well as break interrupts. */ + SBBC_SRAM_WRITE_4(sbbc_scsolie, SBBC_SRAM_READ_4(sbbc_scsolie) | + SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK | + SBBC_SRAM_CONS_SPACE_OUT); + uart_barrier(bas); + + uart_unlock(sc->sc_hwmtx); + return (0); +} + +static int +sbbc_uart_bus_detach(struct uart_softc *sc) +{ + + /* Give back the console input. */ + sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); + return (0); +} + +static int +sbbc_uart_bus_flush(struct uart_softc *sc, int what) +{ + struct uart_bas *bas; + bus_space_tag_t bst; + bus_space_handle_t bsh; + + bas = &sc->sc_bas; + bst = bas->bst; + bsh = bas->bsh; + + if ((what & UART_FLUSH_TRANSMITTER) != 0) + return (ENODEV); + if ((what & UART_FLUSH_RECEIVER) != 0) { + SBBC_SRAM_WRITE_4(sbbc_solcons + + SBBC_CONS_OFF(cons_in_rdptr), + SBBC_SRAM_READ_4(sbbc_solcons + + SBBC_CONS_OFF(cons_in_wrptr))); + uart_barrier(bas); + } + return (0); +} + +static int +sbbc_uart_bus_getsig(struct uart_softc *sc) +{ + uint32_t dummy, new, old, sig; + + do { + old = sc->sc_hwsig; + sig = old; + dummy = 0; + SIGCHG(dummy, sig, SER_CTS, SER_DCTS); + SIGCHG(dummy, sig, SER_DCD, SER_DDCD); + SIGCHG(dummy, sig, SER_DSR, SER_DDSR); + new = sig & ~SER_MASK_DELTA; + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + return (sig); +} + +static int +sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) +{ + int error; + + error = 0; + uart_lock(sc->sc_hwmtx); + switch (request) { + case UART_IOCTL_BAUD: + *(int*)data = 9600; /* arbitrary */ + break; + default: + error = EINVAL; + break; + } + uart_unlock(sc->sc_hwmtx); + return (error); +} + +static int +sbbc_uart_bus_ipend(struct uart_softc *sc) +{ + struct uart_bas *bas; + bus_space_tag_t bst; + bus_space_handle_t bsh; + int ipend; + uint32_t reason, status; + + bas = &sc->sc_bas; + bst = bas->bst; + bsh = bas->bsh; + + uart_lock(sc->sc_hwmtx); + status = SBBC_REGS_READ_4(SBBC_PCI_INT_STATUS); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***