summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormarius <marius@FreeBSD.org>2010-04-10 11:52:12 +0000
committermarius <marius@FreeBSD.org>2010-04-10 11:52:12 +0000
commitaf63aa47ca8e507ea249380e9c4c7659a303ee6f (patch)
treef1b6758ebc22a7a36a90c075e73c4c3ebfa7a633
parentc48055dea1028997c98d1f43c73db7cb0d62ccf1 (diff)
downloadFreeBSD-src-af63aa47ca8e507ea249380e9c4c7659a303ee6f.zip
FreeBSD-src-af63aa47ca8e507ea249380e9c4c7659a303ee6f.tar.gz
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).
-rw-r--r--sys/conf/files.sparc641
-rw-r--r--sys/dev/uart/uart.h1
-rw-r--r--sys/dev/uart/uart_cpu_sparc64.c11
-rw-r--r--sys/sparc64/conf/GENERIC3
-rw-r--r--sys/sparc64/conf/NOTES1
-rw-r--r--sys/sparc64/pci/sbbc.c1074
6 files changed, 1091 insertions, 0 deletions
diff --git a/sys/conf/files.sparc64 b/sys/conf/files.sparc64
index 1f8c4a3..a822a0ed 100644
--- a/sys/conf/files.sparc64
+++ b/sys/conf/files.sparc64
@@ -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
diff --git a/sys/dev/uart/uart.h b/sys/dev/uart/uart.h
index eb66da0..bd7861d 100644
--- a/sys/dev/uart/uart.h
+++ b/sys/dev/uart/uart.h
@@ -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
diff --git a/sys/dev/uart/uart_cpu_sparc64.c b/sys/dev/uart/uart_cpu_sparc64.c
index 0f40cb7..c89b39f 100644
--- a/sys/dev/uart/uart_cpu_sparc64.c
+++ b/sys/dev/uart/uart_cpu_sparc64.c
@@ -133,6 +133,14 @@ uart_cpu_getdev_console(phandle_t options, char *dev, size_t devsz)
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_devinfo *di)
!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);
diff --git a/sys/sparc64/conf/GENERIC b/sys/sparc64/conf/GENERIC
index 0dc9f5c..be3cfd3 100644
--- a/sys/sparc64/conf/GENERIC
+++ b/sys/sparc64/conf/GENERIC
@@ -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
diff --git a/sys/sparc64/conf/NOTES b/sys/sparc64/conf/NOTES
index d9c5be5..5e02289 100644
--- a/sys/sparc64/conf/NOTES
+++ b/sys/sparc64/conf/NOTES
@@ -37,6 +37,7 @@ device eeprom # eeprom (really a front-end for the MK48Txx)
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:
diff --git a/sys/sparc64/pci/sbbc.c b/sys/sparc64/pci/sbbc.c
new file mode 100644
index 0000000..87bf6c9
--- /dev/null
+++ b/sys/sparc64/pci/sbbc.c
@@ -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 <marius@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/openfirm.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_bus.h>
+
+#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);
+ if (status == 0) {
+ uart_unlock(sc->sc_hwmtx);
+ return (0);
+ }
+
+ /*
+ * Unfortunately, we can't use compare and swap for non-cachable
+ * memory.
+ */
+ reason = SBBC_SRAM_READ_4(sbbc_scsolir);
+ SBBC_SRAM_WRITE_4(sbbc_scsolir, 0);
+ uart_barrier(bas);
+ /* Acknowledge the interrupt. */
+ SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, status);
+ uart_barrier(bas);
+
+ uart_unlock(sc->sc_hwmtx);
+
+ ipend = 0;
+ if ((reason & SBBC_SRAM_CONS_IN) != 0)
+ ipend |= SER_INT_RXREADY;
+ if ((reason & SBBC_SRAM_CONS_BRK) != 0)
+ ipend |= SER_INT_BREAK;
+ if ((reason & SBBC_SRAM_CONS_SPACE_OUT) != 0 &&
+ SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) ==
+ SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)))
+ ipend |= SER_INT_TXIDLE;
+ return (ipend);
+}
+
+static int
+sbbc_uart_bus_param(struct uart_softc *sc __unused, int baudrate __unused,
+ int databits __unused, int stopbits __unused, int parity __unused)
+{
+
+ return (0);
+}
+
+static int
+sbbc_uart_bus_probe(struct uart_softc *sc __unused)
+{
+
+ if (sbbc_console != 0)
+ return (0);
+ return (ENXIO);
+}
+
+static int
+sbbc_uart_bus_receive(struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ int c;
+ uint32_t end, rdptr, wrptr;
+
+ bas = &sc->sc_bas;
+ bst = bas->bst;
+ bsh = bas->bsh;
+
+ uart_lock(sc->sc_hwmtx);
+
+ end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end));
+ rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
+ wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr));
+ while (rdptr != wrptr) {
+ if (uart_rx_full(sc) != 0) {
+ sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
+ break;
+ }
+ c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
+ uart_rx_put(sc, c);
+ if (++rdptr == end)
+ rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
+ SBBC_CONS_OFF(cons_in_begin));
+ }
+ uart_barrier(bas);
+ 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(sc->sc_hwmtx);
+ return (0);
+}
+
+static int
+sbbc_uart_bus_setsig(struct uart_softc *sc, int sig)
+{
+ struct uart_bas *bas;
+ uint32_t new, old;
+
+ bas = &sc->sc_bas;
+ do {
+ old = sc->sc_hwsig;
+ new = old;
+ if ((sig & SER_DDTR) != 0) {
+ SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
+ }
+ if ((sig & SER_DRTS) != 0) {
+ SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
+ }
+ } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
+ return (0);
+}
+
+static int
+sbbc_uart_bus_transmit(struct uart_softc *sc)
+{
+ struct uart_bas *bas;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ int i;
+ uint32_t end, wrptr;
+
+ bas = &sc->sc_bas;
+ bst = bas->bst;
+ bsh = bas->bsh;
+
+ uart_lock(sc->sc_hwmtx);
+
+ end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end));
+ wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
+ SBBC_CONS_OFF(cons_out_wrptr));
+ for (i = 0; i < sc->sc_txdatasz; i++) {
+ SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, sc->sc_txbuf[i]);
+ if (++wrptr == end)
+ wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
+ SBBC_CONS_OFF(cons_out_begin));
+ }
+ uart_barrier(bas);
+ 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);
+ sc->sc_txbusy = 1;
+
+ uart_unlock(sc->sc_hwmtx);
+ return (0);
+}
OpenPOWER on IntegriCloud