1 /* $OpenBSD: sbbc.c,v 1.7 2009/11/09 17:53:39 nicm Exp $ */
3 * Copyright (c) 2008 Mark Kettenis
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * Copyright (c) 2010 Marius Strobl <marius@FreeBSD.org>
19 * All rights reserved.
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions
24 * 1. Redistributions of source code must retain the above copyright
25 * notice, this list of conditions and the following disclaimer.
26 * 2. Redistributions in binary form must reproduce the above copyright
27 * notice, this list of conditions and the following disclaimer in the
28 * documentation and/or other materials provided with the distribution.
30 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
46 #include <sys/param.h>
47 #include <sys/systm.h>
49 #include <sys/clock.h>
50 #include <sys/endian.h>
51 #include <sys/kernel.h>
53 #include <sys/module.h>
54 #include <sys/mutex.h>
55 #include <sys/resource.h>
58 #include <dev/ofw/ofw_bus.h>
59 #include <dev/ofw/openfirm.h>
61 #include <machine/bus.h>
62 #include <machine/cpu.h>
63 #include <machine/resource.h>
65 #include <dev/pci/pcireg.h>
66 #include <dev/pci/pcivar.h>
67 #include <dev/uart/uart.h>
68 #include <dev/uart/uart_cpu.h>
69 #include <dev/uart/uart_bus.h>
74 #define SBBC_PCI_BAR PCIR_BAR(0)
75 #define SBBC_PCI_VENDOR 0x108e
76 #define SBBC_PCI_PRODUCT 0xc416
78 #define SBBC_REGS_OFFSET 0x800000
79 #define SBBC_REGS_SIZE 0x6230
80 #define SBBC_EPLD_OFFSET 0x8e0000
81 #define SBBC_EPLD_SIZE 0x20
82 #define SBBC_SRAM_OFFSET 0x900000
83 #define SBBC_SRAM_SIZE 0x20000 /* 128KB SRAM */
85 #define SBBC_PCI_INT_STATUS 0x2320
86 #define SBBC_PCI_INT_ENABLE 0x2330
87 #define SBBC_PCI_ENABLE_INT_A 0x11
89 #define SBBC_EPLD_INTERRUPT 0x13
90 #define SBBC_EPLD_INTERRUPT_ON 0x01
92 #define SBBC_SRAM_CONS_IN 0x00000001
93 #define SBBC_SRAM_CONS_OUT 0x00000002
94 #define SBBC_SRAM_CONS_BRK 0x00000004
95 #define SBBC_SRAM_CONS_SPACE_IN 0x00000008
96 #define SBBC_SRAM_CONS_SPACE_OUT 0x00000010
98 #define SBBC_TAG_KEY_SIZE 8
99 #define SBBC_TAG_KEY_SCSOLIE "SCSOLIE" /* SC -> OS int. enable */
100 #define SBBC_TAG_KEY_SCSOLIR "SCSOLIR" /* SC -> OS int. reason */
101 #define SBBC_TAG_KEY_SOLCONS "SOLCONS" /* OS console buffer */
102 #define SBBC_TAG_KEY_SOLSCIE "SOLSCIE" /* OS -> SC int. enable */
103 #define SBBC_TAG_KEY_SOLSCIR "SOLSCIR" /* OS -> SC int. reason */
104 #define SBBC_TAG_KEY_TODDATA "TODDATA" /* OS TOD struct */
105 #define SBBC_TAG_OFF(x) offsetof(struct sbbc_sram_tag, x)
107 struct sbbc_sram_tag {
108 char tag_key[SBBC_TAG_KEY_SIZE];
113 #define SBBC_TOC_MAGIC "TOCSRAM"
114 #define SBBC_TOC_MAGIC_SIZE 8
115 #define SBBC_TOC_TAGS_MAX 32
116 #define SBBC_TOC_OFF(x) offsetof(struct sbbc_sram_toc, x)
118 struct sbbc_sram_toc {
119 char toc_magic[SBBC_TOC_MAGIC_SIZE];
120 uint8_t toc_reserved;
122 uint16_t toc_version;
124 struct sbbc_sram_tag toc_tag[SBBC_TOC_TAGS_MAX];
127 #define SBBC_TOD_MAGIC 0x54443100 /* "TD1" */
128 #define SBBC_TOD_VERSION 1
129 #define SBBC_TOD_OFF(x) offsetof(struct sbbc_sram_tod, x)
131 struct sbbc_sram_tod {
133 uint32_t tod_version;
136 uint32_t tod_reserved;
137 uint32_t tod_heartbeat;
138 uint32_t tod_timeout;
141 #define SBBC_CONS_MAGIC 0x434f4e00 /* "CON" */
142 #define SBBC_CONS_VERSION 1
143 #define SBBC_CONS_OFF(x) offsetof(struct sbbc_sram_cons, x)
145 struct sbbc_sram_cons {
147 uint32_t cons_version;
150 uint32_t cons_in_begin;
151 uint32_t cons_in_end;
152 uint32_t cons_in_rdptr;
153 uint32_t cons_in_wrptr;
155 uint32_t cons_out_begin;
156 uint32_t cons_out_end;
157 uint32_t cons_out_rdptr;
158 uint32_t cons_out_wrptr;
162 struct resource *sc_res;
165 #define SBBC_READ_N(wdth, offs) \
166 bus_space_read_ ## wdth((bst), (bsh), (offs))
167 #define SBBC_WRITE_N(wdth, offs, val) \
168 bus_space_write_ ## wdth((bst), (bsh), (offs), (val))
170 #define SBBC_READ_1(offs) \
171 SBBC_READ_N(1, (offs))
172 #define SBBC_READ_2(offs) \
173 bswap16(SBBC_READ_N(2, (offs)))
174 #define SBBC_READ_4(offs) \
175 bswap32(SBBC_READ_N(4, (offs)))
176 #define SBBC_READ_8(offs) \
177 bswap64(SBBC_READ_N(8, (offs)))
178 #define SBBC_WRITE_1(offs, val) \
179 SBBC_WRITE_N(1, (offs), (val))
180 #define SBBC_WRITE_2(offs, val) \
181 SBBC_WRITE_N(2, (offs), bswap16(val))
182 #define SBBC_WRITE_4(offs, val) \
183 SBBC_WRITE_N(4, (offs), bswap32(val))
184 #define SBBC_WRITE_8(offs, val) \
185 SBBC_WRITE_N(8, (offs), bswap64(val))
187 #define SBBC_REGS_READ_1(offs) \
188 SBBC_READ_1((offs) + SBBC_REGS_OFFSET)
189 #define SBBC_REGS_READ_2(offs) \
190 SBBC_READ_2((offs) + SBBC_REGS_OFFSET)
191 #define SBBC_REGS_READ_4(offs) \
192 SBBC_READ_4((offs) + SBBC_REGS_OFFSET)
193 #define SBBC_REGS_READ_8(offs) \
194 SBBC_READ_8((offs) + SBBC_REGS_OFFSET)
195 #define SBBC_REGS_WRITE_1(offs, val) \
196 SBBC_WRITE_1((offs) + SBBC_REGS_OFFSET, (val))
197 #define SBBC_REGS_WRITE_2(offs, val) \
198 SBBC_WRITE_2((offs) + SBBC_REGS_OFFSET, (val))
199 #define SBBC_REGS_WRITE_4(offs, val) \
200 SBBC_WRITE_4((offs) + SBBC_REGS_OFFSET, (val))
201 #define SBBC_REGS_WRITE_8(offs, val) \
202 SBBC_WRITE_8((offs) + SBBC_REGS_OFFSET, (val))
204 #define SBBC_EPLD_READ_1(offs) \
205 SBBC_READ_1((offs) + SBBC_EPLD_OFFSET)
206 #define SBBC_EPLD_READ_2(offs) \
207 SBBC_READ_2((offs) + SBBC_EPLD_OFFSET)
208 #define SBBC_EPLD_READ_4(offs) \
209 SBBC_READ_4((offs) + SBBC_EPLD_OFFSET)
210 #define SBBC_EPLD_READ_8(offs) \
211 SBBC_READ_8((offs) + SBBC_EPLD_OFFSET)
212 #define SBBC_EPLD_WRITE_1(offs, val) \
213 SBBC_WRITE_1((offs) + SBBC_EPLD_OFFSET, (val))
214 #define SBBC_EPLD_WRITE_2(offs, val) \
215 SBBC_WRITE_2((offs) + SBBC_EPLD_OFFSET, (val))
216 #define SBBC_EPLD_WRITE_4(offs, val) \
217 SBBC_WRITE_4((offs) + SBBC_EPLD_OFFSET, (val))
218 #define SBBC_EPLD_WRITE_8(offs, val) \
219 SBBC_WRITE_8((offs) + SBBC_EPLD_OFFSET, (val))
221 #define SBBC_SRAM_READ_1(offs) \
222 SBBC_READ_1((offs) + SBBC_SRAM_OFFSET)
223 #define SBBC_SRAM_READ_2(offs) \
224 SBBC_READ_2((offs) + SBBC_SRAM_OFFSET)
225 #define SBBC_SRAM_READ_4(offs) \
226 SBBC_READ_4((offs) + SBBC_SRAM_OFFSET)
227 #define SBBC_SRAM_READ_8(offs) \
228 SBBC_READ_8((offs) + SBBC_SRAM_OFFSET)
229 #define SBBC_SRAM_WRITE_1(offs, val) \
230 SBBC_WRITE_1((offs) + SBBC_SRAM_OFFSET, (val))
231 #define SBBC_SRAM_WRITE_2(offs, val) \
232 SBBC_WRITE_2((offs) + SBBC_SRAM_OFFSET, (val))
233 #define SBBC_SRAM_WRITE_4(offs, val) \
234 SBBC_WRITE_4((offs) + SBBC_SRAM_OFFSET, (val))
235 #define SBBC_SRAM_WRITE_8(offs, val) \
236 SBBC_WRITE_8((offs) + SBBC_SRAM_OFFSET, (val))
238 #define SUNW_SETCONSINPUT "SUNW,set-console-input"
239 #define SUNW_SETCONSINPUT_CLNT "CON_CLNT"
240 #define SUNW_SETCONSINPUT_OBP "CON_OBP"
242 static u_int sbbc_console;
244 static uint32_t sbbc_scsolie;
245 static uint32_t sbbc_scsolir;
246 static uint32_t sbbc_solcons;
247 static uint32_t sbbc_solscie;
248 static uint32_t sbbc_solscir;
249 static uint32_t sbbc_toddata;
254 static int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh);
255 static inline void sbbc_send_intr(bus_space_tag_t bst,
256 bus_space_handle_t bsh);
257 static const char *sbbc_serengeti_set_console_input(char *new);
262 static bus_alloc_resource_t sbbc_bus_alloc_resource;
263 static bus_release_resource_t sbbc_bus_release_resource;
264 static bus_get_resource_list_t sbbc_bus_get_resource_list;
265 static bus_setup_intr_t sbbc_bus_setup_intr;
266 static bus_teardown_intr_t sbbc_bus_teardown_intr;
268 static device_attach_t sbbc_pci_attach;
269 static device_probe_t sbbc_pci_probe;
271 static clock_gettime_t sbbc_tod_gettime;
272 static clock_settime_t sbbc_tod_settime;
274 static device_method_t sbbc_pci_methods[] = {
275 /* Device interface */
276 DEVMETHOD(device_probe, sbbc_pci_probe),
277 DEVMETHOD(device_attach, sbbc_pci_attach),
279 DEVMETHOD(bus_print_child, bus_generic_print_child),
280 DEVMETHOD(bus_alloc_resource, sbbc_bus_alloc_resource),
281 DEVMETHOD(bus_release_resource, sbbc_bus_release_resource),
282 DEVMETHOD(bus_setup_intr, sbbc_bus_setup_intr),
283 DEVMETHOD(bus_teardown_intr, sbbc_bus_teardown_intr),
284 DEVMETHOD(bus_get_resource_list, sbbc_bus_get_resource_list),
286 /* clock interface */
287 DEVMETHOD(clock_gettime, sbbc_tod_gettime),
288 DEVMETHOD(clock_settime, sbbc_tod_settime),
293 static devclass_t sbbc_devclass;
295 DEFINE_CLASS_0(sbbc, sbbc_driver, sbbc_pci_methods, sizeof(struct sbbc_softc));
296 DRIVER_MODULE(sbbc, pci, sbbc_driver, sbbc_devclass, 0, 0);
299 sbbc_pci_probe(device_t dev)
302 if (pci_get_vendor(dev) == SBBC_PCI_VENDOR &&
303 pci_get_device(dev) == SBBC_PCI_PRODUCT) {
304 device_set_desc(dev, "Sun BootBus controller");
305 return (BUS_PROBE_DEFAULT);
311 sbbc_pci_attach(device_t dev)
313 struct sbbc_softc *sc;
317 bus_space_handle_t bsh;
322 /* Nothing to to if we're not the chosen one. */
323 if ((node = OF_finddevice("/chosen")) == -1) {
324 device_printf(dev, "failed to find /chosen\n");
327 if (OF_getprop(node, "iosram", &node, sizeof(node)) == -1) {
328 device_printf(dev, "failed to get iosram\n");
331 if (node != ofw_bus_get_node(dev))
334 sc = device_get_softc(dev);
337 * Note that we don't activate the resource so it's not mapped twice
338 * but only once by the firmware.
340 sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 0);
341 if (sc->sc_res == NULL) {
342 device_printf(dev, "failed to allocate resources\n");
345 bst = rman_get_bustag(sc->sc_res);
346 bsh = rman_get_bushandle(sc->sc_res);
347 if (sbbc_console != 0) {
348 /* Once again the interrupt pin isn't set. */
349 if (pci_get_intpin(dev) == 0)
350 pci_set_intpin(dev, 1);
351 child = device_add_child(dev, NULL, -1);
353 device_printf(dev, "failed to add UART device\n");
354 error = bus_generic_attach(dev);
356 device_printf(dev, "failed to attach UART device\n");
358 error = sbbc_parse_toc(rman_get_bustag(sc->sc_res),
359 rman_get_bushandle(sc->sc_res));
361 device_printf(dev, "failed to parse TOC\n");
362 if (sbbc_console != 0) {
363 bus_release_resource(dev, SYS_RES_MEMORY, rid,
369 if (sbbc_toddata != 0) {
370 if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
371 SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC)
372 device_printf(dev, "invalid TOD magic %#x\n", val);
373 else if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
374 SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION)
375 device_printf(dev, "invalid TOD version %#x\n", val);
377 clock_register(dev, 1000000); /* 1 sec. resolution */
379 sbbc_tod_gettime(dev, &ts);
381 "current time: %ld.%09ld\n",
382 (long)ts.tv_sec, ts.tv_nsec);
390 * Note that the bus methods don't pass-through the uart(4) requests but act
391 * as if they would come from sbbc(4) in order to avoid complications with
392 * pci(4) (actually, uart(4) isn't a real child but rather a function of
396 static struct resource *
397 sbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type,
398 int *rid, u_long start, u_long end, u_long count, u_int flags)
400 struct sbbc_softc *sc;
402 sc = device_get_softc(dev);
405 return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, type,
406 rid, start, end, count, flags));
416 sbbc_bus_release_resource(device_t dev, device_t child __unused, int type,
417 int rid, struct resource *res)
420 if (type == SYS_RES_IRQ)
421 return (BUS_RELEASE_RESOURCE(device_get_parent(dev), dev,
426 static struct resource_list *
427 sbbc_bus_get_resource_list(device_t dev, device_t child __unused)
430 return (BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev));
434 sbbc_bus_setup_intr(device_t dev, device_t child __unused,
435 struct resource *res, int flags, driver_filter_t *filt,
436 driver_intr_t *intr, void *arg, void **cookiep)
439 return (BUS_SETUP_INTR(device_get_parent(dev), dev, res, flags, filt,
440 intr, arg, cookiep));
444 sbbc_bus_teardown_intr(device_t dev, device_t child __unused,
445 struct resource *res, void *cookie)
448 return (BUS_TEARDOWN_INTR(device_get_parent(dev), dev, res, cookie));
455 sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh)
457 char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)];
460 uint32_t off, sram_toc;
463 if ((node = OF_finddevice("/chosen")) == -1)
465 /* SRAM TOC offset defaults to 0. */
466 if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0)
469 bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc +
470 SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE);
471 buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0';
472 if (strcmp(buf, SBBC_TOC_MAGIC) != 0)
475 tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags));
476 for (i = 0; i < tags; i++) {
477 tag = sram_toc + SBBC_TOC_OFF(toc_tag) +
478 i * sizeof(struct sbbc_sram_tag);
479 bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag +
480 SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE);
481 buf[SBBC_TAG_KEY_SIZE - 1] = '\0';
482 off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset));
483 if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0)
485 else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0)
487 else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0)
489 else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0)
491 else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0)
493 else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0)
500 sbbc_serengeti_set_console_input(char *new)
509 (cell_t)SUNW_SETCONSINPUT,
514 args.new = (cell_t)new;
515 if (ofw_entry(&args) == -1)
517 return ((const char *)args.old);
521 sbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh)
524 SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON);
525 bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1,
526 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
533 sbbc_tod_gettime(device_t dev, struct timespec *ts)
535 struct sbbc_softc *sc;
537 bus_space_handle_t bsh;
539 sc = device_get_softc(dev);
540 bst = rman_get_bustag(sc->sc_res);
541 bsh = rman_get_bushandle(sc->sc_res);
543 ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) +
544 SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew));
550 sbbc_tod_settime(device_t dev, struct timespec *ts)
552 struct sbbc_softc *sc;
554 bus_space_handle_t bsh;
556 sc = device_get_softc(dev);
557 bst = rman_get_bustag(sc->sc_res);
558 bsh = rman_get_bushandle(sc->sc_res);
560 SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec -
561 SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)));
568 static device_probe_t sbbc_uart_sbbc_probe;
570 static device_method_t sbbc_uart_sbbc_methods[] = {
571 /* Device interface */
572 DEVMETHOD(device_probe, sbbc_uart_sbbc_probe),
573 DEVMETHOD(device_attach, uart_bus_attach),
574 DEVMETHOD(device_detach, uart_bus_detach),
579 DEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods,
580 sizeof(struct uart_softc));
581 DRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, 0, 0);
584 sbbc_uart_sbbc_probe(device_t dev)
586 struct uart_softc *sc;
588 sc = device_get_softc(dev);
589 sc->sc_class = &uart_sbbc_class;
590 device_set_desc(dev, "Serengeti console");
591 return (uart_bus_probe(dev, 0, 0, SBBC_PCI_BAR, 0));
595 * Low-level UART interface
597 static int sbbc_uart_probe(struct uart_bas *bas);
598 static void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits,
599 int stopbits, int parity);
600 static void sbbc_uart_term(struct uart_bas *bas);
601 static void sbbc_uart_putc(struct uart_bas *bas, int c);
602 static int sbbc_uart_rxready(struct uart_bas *bas);
603 static int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx);
605 static struct uart_ops sbbc_uart_ops = {
606 .probe = sbbc_uart_probe,
607 .init = sbbc_uart_init,
608 .term = sbbc_uart_term,
609 .putc = sbbc_uart_putc,
610 .rxready = sbbc_uart_rxready,
611 .getc = sbbc_uart_getc,
615 sbbc_uart_probe(struct uart_bas *bas)
618 bus_space_handle_t bsh;
624 error = sbbc_parse_toc(bst, bsh);
628 if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 ||
629 sbbc_solscie == 0 || sbbc_solscir == 0)
632 if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) !=
633 SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons +
634 SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION)
640 sbbc_uart_init(struct uart_bas *bas, int baudrate __unused,
641 int databits __unused, int stopbits __unused, int parity __unused)
644 bus_space_handle_t bsh;
649 /* Enable output to and space in from the SC interrupts. */
650 SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) |
651 SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN);
654 /* Take over the console input. */
655 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT);
659 sbbc_uart_term(struct uart_bas *bas __unused)
662 /* Give back the console input. */
663 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP);
667 sbbc_uart_putc(struct uart_bas *bas, int c)
670 bus_space_handle_t bsh;
676 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
677 SBBC_CONS_OFF(cons_out_wrptr));
678 SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c);
680 if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons +
681 SBBC_CONS_OFF(cons_out_end)))
682 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
683 SBBC_CONS_OFF(cons_out_begin));
684 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr),
688 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
691 sbbc_send_intr(bst, bsh);
695 sbbc_uart_rxready(struct uart_bas *bas)
698 bus_space_handle_t bsh;
703 if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) ==
704 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr)))
710 sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
713 bus_space_handle_t bsh;
722 while (sbbc_uart_rxready(bas) == 0) {
728 rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
729 c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
731 if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons +
732 SBBC_CONS_OFF(cons_in_end)))
733 rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
734 SBBC_CONS_OFF(cons_in_begin));
735 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr),
738 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
739 SBBC_SRAM_CONS_SPACE_IN);
741 sbbc_send_intr(bst, bsh);
748 * High-level UART interface
750 static int sbbc_uart_bus_attach(struct uart_softc *sc);
751 static int sbbc_uart_bus_detach(struct uart_softc *sc);
752 static int sbbc_uart_bus_flush(struct uart_softc *sc, int what);
753 static int sbbc_uart_bus_getsig(struct uart_softc *sc);
754 static int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request,
756 static int sbbc_uart_bus_ipend(struct uart_softc *sc);
757 static int sbbc_uart_bus_param(struct uart_softc *sc, int baudrate,
758 int databits, int stopbits, int parity);
759 static int sbbc_uart_bus_probe(struct uart_softc *sc);
760 static int sbbc_uart_bus_receive(struct uart_softc *sc);
761 static int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig);
762 static int sbbc_uart_bus_transmit(struct uart_softc *sc);
764 static kobj_method_t sbbc_uart_methods[] = {
765 KOBJMETHOD(uart_attach, sbbc_uart_bus_attach),
766 KOBJMETHOD(uart_detach, sbbc_uart_bus_detach),
767 KOBJMETHOD(uart_flush, sbbc_uart_bus_flush),
768 KOBJMETHOD(uart_getsig, sbbc_uart_bus_getsig),
769 KOBJMETHOD(uart_ioctl, sbbc_uart_bus_ioctl),
770 KOBJMETHOD(uart_ipend, sbbc_uart_bus_ipend),
771 KOBJMETHOD(uart_param, sbbc_uart_bus_param),
772 KOBJMETHOD(uart_probe, sbbc_uart_bus_probe),
773 KOBJMETHOD(uart_receive, sbbc_uart_bus_receive),
774 KOBJMETHOD(uart_setsig, sbbc_uart_bus_setsig),
775 KOBJMETHOD(uart_transmit, sbbc_uart_bus_transmit),
780 struct uart_class uart_sbbc_class = {
783 sizeof(struct uart_softc),
784 .uc_ops = &sbbc_uart_ops,
786 .uc_rclk = 0x5bbc /* arbitrary */
789 #define SIGCHG(c, i, s, d) \
791 i |= (((i) & (s)) != 0) ? (s) : (s) | (d); \
793 i = (((i) & (s)) != 0) ? ((i) & ~(s)) | (d) : (i); \
797 sbbc_uart_bus_attach(struct uart_softc *sc)
799 struct uart_bas *bas;
801 bus_space_handle_t bsh;
808 sc->sc_rxfifosz = SBBC_SRAM_READ_4(sbbc_solcons +
809 SBBC_CONS_OFF(cons_in_end)) - SBBC_SRAM_READ_4(sbbc_solcons +
810 SBBC_CONS_OFF(cons_in_begin)) - 1;
811 sc->sc_txfifosz = SBBC_SRAM_READ_4(sbbc_solcons +
812 SBBC_CONS_OFF(cons_out_end)) - SBBC_SRAM_READ_4(sbbc_solcons +
813 SBBC_CONS_OFF(cons_out_begin)) - 1;
815 uart_lock(sc->sc_hwmtx);
818 * Let the current output drain before enabling interrupts. Not
819 * doing so tends to cause lost output when turning them on.
821 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
822 SBBC_CONS_OFF(cons_out_wrptr));
823 while (SBBC_SRAM_READ_4(sbbc_solcons +
824 SBBC_CONS_OFF(cons_out_rdptr)) != wrptr);
827 /* Clear and acknowledge possibly outstanding interrupts. */
828 SBBC_SRAM_WRITE_4(sbbc_scsolir, 0);
830 SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS,
831 SBBC_SRAM_READ_4(sbbc_scsolir));
833 /* Enable PCI interrupts. */
834 SBBC_REGS_WRITE_4(SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A);
836 /* Enable input from and output to SC as well as break interrupts. */
837 SBBC_SRAM_WRITE_4(sbbc_scsolie, SBBC_SRAM_READ_4(sbbc_scsolie) |
838 SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK |
839 SBBC_SRAM_CONS_SPACE_OUT);
842 uart_unlock(sc->sc_hwmtx);
847 sbbc_uart_bus_detach(struct uart_softc *sc)
850 /* Give back the console input. */
851 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP);
856 sbbc_uart_bus_flush(struct uart_softc *sc, int what)
858 struct uart_bas *bas;
860 bus_space_handle_t bsh;
866 if ((what & UART_FLUSH_TRANSMITTER) != 0)
868 if ((what & UART_FLUSH_RECEIVER) != 0) {
869 SBBC_SRAM_WRITE_4(sbbc_solcons +
870 SBBC_CONS_OFF(cons_in_rdptr),
871 SBBC_SRAM_READ_4(sbbc_solcons +
872 SBBC_CONS_OFF(cons_in_wrptr)));
879 sbbc_uart_bus_getsig(struct uart_softc *sc)
881 uint32_t dummy, new, old, sig;
887 SIGCHG(dummy, sig, SER_CTS, SER_DCTS);
888 SIGCHG(dummy, sig, SER_DCD, SER_DDCD);
889 SIGCHG(dummy, sig, SER_DSR, SER_DDSR);
890 new = sig & ~SER_MASK_DELTA;
891 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
896 sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
901 uart_lock(sc->sc_hwmtx);
903 case UART_IOCTL_BAUD:
904 *(int*)data = 9600; /* arbitrary */
910 uart_unlock(sc->sc_hwmtx);
915 sbbc_uart_bus_ipend(struct uart_softc *sc)
917 struct uart_bas *bas;
919 bus_space_handle_t bsh;
921 uint32_t reason, status;
927 uart_lock(sc->sc_hwmtx);
928 status = SBBC_REGS_READ_4(SBBC_PCI_INT_STATUS);
930 uart_unlock(sc->sc_hwmtx);
935 * Unfortunately, we can't use compare and swap for non-cachable
938 reason = SBBC_SRAM_READ_4(sbbc_scsolir);
939 SBBC_SRAM_WRITE_4(sbbc_scsolir, 0);
941 /* Acknowledge the interrupt. */
942 SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, status);
945 uart_unlock(sc->sc_hwmtx);
948 if ((reason & SBBC_SRAM_CONS_IN) != 0)
949 ipend |= SER_INT_RXREADY;
950 if ((reason & SBBC_SRAM_CONS_BRK) != 0)
951 ipend |= SER_INT_BREAK;
952 if ((reason & SBBC_SRAM_CONS_SPACE_OUT) != 0 &&
953 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) ==
954 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)))
955 ipend |= SER_INT_TXIDLE;
960 sbbc_uart_bus_param(struct uart_softc *sc __unused, int baudrate __unused,
961 int databits __unused, int stopbits __unused, int parity __unused)
968 sbbc_uart_bus_probe(struct uart_softc *sc __unused)
971 if (sbbc_console != 0)
977 sbbc_uart_bus_receive(struct uart_softc *sc)
979 struct uart_bas *bas;
981 bus_space_handle_t bsh;
983 uint32_t end, rdptr, wrptr;
989 uart_lock(sc->sc_hwmtx);
991 end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end));
992 rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
993 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr));
994 while (rdptr != wrptr) {
995 if (uart_rx_full(sc) != 0) {
996 sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
999 c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
1002 rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
1003 SBBC_CONS_OFF(cons_in_begin));
1006 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr),
1009 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
1010 SBBC_SRAM_CONS_SPACE_IN);
1012 sbbc_send_intr(bst, bsh);
1014 uart_unlock(sc->sc_hwmtx);
1019 sbbc_uart_bus_setsig(struct uart_softc *sc, int sig)
1021 struct uart_bas *bas;
1028 if ((sig & SER_DDTR) != 0) {
1029 SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
1031 if ((sig & SER_DRTS) != 0) {
1032 SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
1034 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
1039 sbbc_uart_bus_transmit(struct uart_softc *sc)
1041 struct uart_bas *bas;
1042 bus_space_tag_t bst;
1043 bus_space_handle_t bsh;
1045 uint32_t end, wrptr;
1051 uart_lock(sc->sc_hwmtx);
1053 end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end));
1054 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
1055 SBBC_CONS_OFF(cons_out_wrptr));
1056 for (i = 0; i < sc->sc_txdatasz; i++) {
1057 SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, sc->sc_txbuf[i]);
1059 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
1060 SBBC_CONS_OFF(cons_out_begin));
1063 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr),
1066 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
1067 SBBC_SRAM_CONS_OUT);
1069 sbbc_send_intr(bst, bsh);
1072 uart_unlock(sc->sc_hwmtx);