1/*	$OpenBSD: sbbc.c,v 1.7 2009/11/09 17:53:39 nicm Exp $	*/
2/*-
3 * SPDX-License-Identifier: (ISC AND BSD-2-Clause-FreeBSD)
4 *
5 * Copyright (c) 2008 Mark Kettenis
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19/*-
20 * Copyright (c) 2010 Marius Strobl <marius@FreeBSD.org>
21 * All rights reserved.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 * 1. Redistributions of source code must retain the above copyright
27 *    notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 *    notice, this list of conditions and the following disclaimer in the
30 *    documentation and/or other materials provided with the distribution.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * SUCH DAMAGE.
43 */
44
45#include <sys/cdefs.h>
46__FBSDID("$FreeBSD$");
47
48#include <sys/param.h>
49#include <sys/systm.h>
50#include <sys/bus.h>
51#include <sys/clock.h>
52#include <sys/endian.h>
53#include <sys/kernel.h>
54#include <sys/lock.h>
55#include <sys/module.h>
56#include <sys/mutex.h>
57#include <sys/resource.h>
58#include <sys/rman.h>
59
60#include <dev/ofw/ofw_bus.h>
61#include <dev/ofw/openfirm.h>
62
63#include <machine/bus.h>
64#include <machine/cpu.h>
65#include <machine/resource.h>
66
67#include <dev/pci/pcireg.h>
68#include <dev/pci/pcivar.h>
69#include <dev/uart/uart.h>
70#include <dev/uart/uart_cpu.h>
71#include <dev/uart/uart_bus.h>
72
73#include "clock_if.h"
74#include "uart_if.h"
75
76#define	SBBC_PCI_BAR		PCIR_BAR(0)
77#define	SBBC_PCI_VENDOR		0x108e
78#define	SBBC_PCI_PRODUCT	0xc416
79
80#define	SBBC_REGS_OFFSET	0x800000
81#define	SBBC_REGS_SIZE		0x6230
82#define	SBBC_EPLD_OFFSET	0x8e0000
83#define	SBBC_EPLD_SIZE		0x20
84#define	SBBC_SRAM_OFFSET	0x900000
85#define	SBBC_SRAM_SIZE		0x20000	/* 128KB SRAM */
86
87#define	SBBC_PCI_INT_STATUS	0x2320
88#define	SBBC_PCI_INT_ENABLE	0x2330
89#define	SBBC_PCI_ENABLE_INT_A	0x11
90
91#define	SBBC_EPLD_INTERRUPT	0x13
92#define	SBBC_EPLD_INTERRUPT_ON	0x01
93
94#define	SBBC_SRAM_CONS_IN		0x00000001
95#define	SBBC_SRAM_CONS_OUT		0x00000002
96#define	SBBC_SRAM_CONS_BRK		0x00000004
97#define	SBBC_SRAM_CONS_SPACE_IN		0x00000008
98#define	SBBC_SRAM_CONS_SPACE_OUT	0x00000010
99
100#define	SBBC_TAG_KEY_SIZE	8
101#define	SBBC_TAG_KEY_SCSOLIE	"SCSOLIE"	/* SC -> OS int. enable */
102#define	SBBC_TAG_KEY_SCSOLIR	"SCSOLIR"	/* SC -> OS int. reason */
103#define	SBBC_TAG_KEY_SOLCONS	"SOLCONS"	/* OS console buffer */
104#define	SBBC_TAG_KEY_SOLSCIE	"SOLSCIE"	/* OS -> SC int. enable */
105#define	SBBC_TAG_KEY_SOLSCIR	"SOLSCIR"	/* OS -> SC int. reason */
106#define	SBBC_TAG_KEY_TODDATA	"TODDATA"	/* OS TOD struct */
107#define	SBBC_TAG_OFF(x)		offsetof(struct sbbc_sram_tag, x)
108
109struct sbbc_sram_tag {
110	char		tag_key[SBBC_TAG_KEY_SIZE];
111	uint32_t	tag_size;
112	uint32_t	tag_offset;
113} __packed;
114
115#define	SBBC_TOC_MAGIC		"TOCSRAM"
116#define	SBBC_TOC_MAGIC_SIZE	8
117#define	SBBC_TOC_TAGS_MAX	32
118#define	SBBC_TOC_OFF(x)		offsetof(struct sbbc_sram_toc, x)
119
120struct sbbc_sram_toc {
121	char			toc_magic[SBBC_TOC_MAGIC_SIZE];
122	uint8_t			toc_reserved;
123	uint8_t			toc_type;
124	uint16_t		toc_version;
125	uint32_t		toc_ntags;
126	struct sbbc_sram_tag	toc_tag[SBBC_TOC_TAGS_MAX];
127} __packed;
128
129#define	SBBC_TOD_MAGIC		0x54443100	/* "TD1" */
130#define	SBBC_TOD_VERSION	1
131#define	SBBC_TOD_OFF(x)		offsetof(struct sbbc_sram_tod, x)
132
133struct sbbc_sram_tod {
134	uint32_t	tod_magic;
135	uint32_t	tod_version;
136	uint64_t	tod_time;
137	uint64_t	tod_skew;
138	uint32_t	tod_reserved;
139	uint32_t	tod_heartbeat;
140	uint32_t	tod_timeout;
141} __packed;
142
143#define	SBBC_CONS_MAGIC		0x434f4e00	/* "CON" */
144#define	SBBC_CONS_VERSION	1
145#define	SBBC_CONS_OFF(x)	offsetof(struct sbbc_sram_cons, x)
146
147struct sbbc_sram_cons {
148	uint32_t cons_magic;
149	uint32_t cons_version;
150	uint32_t cons_size;
151
152	uint32_t cons_in_begin;
153	uint32_t cons_in_end;
154	uint32_t cons_in_rdptr;
155	uint32_t cons_in_wrptr;
156
157	uint32_t cons_out_begin;
158	uint32_t cons_out_end;
159	uint32_t cons_out_rdptr;
160	uint32_t cons_out_wrptr;
161} __packed;
162
163struct sbbc_softc {
164	struct resource *sc_res;
165};
166
167#define	SBBC_READ_N(wdth, offs)						\
168	bus_space_read_ ## wdth((bst), (bsh), (offs))
169#define	SBBC_WRITE_N(wdth, offs, val)					\
170	bus_space_write_ ## wdth((bst), (bsh), (offs), (val))
171
172#define	SBBC_READ_1(offs)						\
173	SBBC_READ_N(1, (offs))
174#define	SBBC_READ_2(offs)						\
175	bswap16(SBBC_READ_N(2, (offs)))
176#define	SBBC_READ_4(offs)						\
177	bswap32(SBBC_READ_N(4, (offs)))
178#define	SBBC_READ_8(offs)						\
179	bswap64(SBBC_READ_N(8, (offs)))
180#define	SBBC_WRITE_1(offs, val)						\
181	SBBC_WRITE_N(1, (offs), (val))
182#define	SBBC_WRITE_2(offs, val)						\
183	SBBC_WRITE_N(2, (offs), bswap16(val))
184#define	SBBC_WRITE_4(offs, val)						\
185	SBBC_WRITE_N(4, (offs), bswap32(val))
186#define	SBBC_WRITE_8(offs, val)						\
187	SBBC_WRITE_N(8, (offs), bswap64(val))
188
189#define	SBBC_REGS_READ_1(offs)						\
190	SBBC_READ_1((offs) + SBBC_REGS_OFFSET)
191#define	SBBC_REGS_READ_2(offs)						\
192	SBBC_READ_2((offs) + SBBC_REGS_OFFSET)
193#define	SBBC_REGS_READ_4(offs)						\
194	SBBC_READ_4((offs) + SBBC_REGS_OFFSET)
195#define	SBBC_REGS_READ_8(offs)						\
196	SBBC_READ_8((offs) + SBBC_REGS_OFFSET)
197#define	SBBC_REGS_WRITE_1(offs, val)					\
198	SBBC_WRITE_1((offs) + SBBC_REGS_OFFSET, (val))
199#define	SBBC_REGS_WRITE_2(offs, val)					\
200	SBBC_WRITE_2((offs) + SBBC_REGS_OFFSET, (val))
201#define	SBBC_REGS_WRITE_4(offs, val)					\
202	SBBC_WRITE_4((offs) + SBBC_REGS_OFFSET, (val))
203#define	SBBC_REGS_WRITE_8(offs, val)					\
204	SBBC_WRITE_8((offs) + SBBC_REGS_OFFSET, (val))
205
206#define	SBBC_EPLD_READ_1(offs)						\
207	SBBC_READ_1((offs) + SBBC_EPLD_OFFSET)
208#define	SBBC_EPLD_READ_2(offs)						\
209	SBBC_READ_2((offs) + SBBC_EPLD_OFFSET)
210#define	SBBC_EPLD_READ_4(offs)						\
211	SBBC_READ_4((offs) + SBBC_EPLD_OFFSET)
212#define	SBBC_EPLD_READ_8(offs)						\
213	SBBC_READ_8((offs) + SBBC_EPLD_OFFSET)
214#define	SBBC_EPLD_WRITE_1(offs, val)					\
215	SBBC_WRITE_1((offs) + SBBC_EPLD_OFFSET, (val))
216#define	SBBC_EPLD_WRITE_2(offs, val)					\
217	SBBC_WRITE_2((offs) + SBBC_EPLD_OFFSET, (val))
218#define	SBBC_EPLD_WRITE_4(offs, val)					\
219	SBBC_WRITE_4((offs) + SBBC_EPLD_OFFSET, (val))
220#define	SBBC_EPLD_WRITE_8(offs, val)					\
221	SBBC_WRITE_8((offs) + SBBC_EPLD_OFFSET, (val))
222
223#define	SBBC_SRAM_READ_1(offs)						\
224	SBBC_READ_1((offs) + SBBC_SRAM_OFFSET)
225#define	SBBC_SRAM_READ_2(offs)						\
226	SBBC_READ_2((offs) + SBBC_SRAM_OFFSET)
227#define	SBBC_SRAM_READ_4(offs)						\
228	SBBC_READ_4((offs) + SBBC_SRAM_OFFSET)
229#define	SBBC_SRAM_READ_8(offs)						\
230	SBBC_READ_8((offs) + SBBC_SRAM_OFFSET)
231#define	SBBC_SRAM_WRITE_1(offs, val)					\
232	SBBC_WRITE_1((offs) + SBBC_SRAM_OFFSET, (val))
233#define	SBBC_SRAM_WRITE_2(offs, val)					\
234	SBBC_WRITE_2((offs) + SBBC_SRAM_OFFSET, (val))
235#define	SBBC_SRAM_WRITE_4(offs, val)					\
236	SBBC_WRITE_4((offs) + SBBC_SRAM_OFFSET, (val))
237#define	SBBC_SRAM_WRITE_8(offs, val)					\
238	SBBC_WRITE_8((offs) + SBBC_SRAM_OFFSET, (val))
239
240#define	SUNW_SETCONSINPUT	"SUNW,set-console-input"
241#define	SUNW_SETCONSINPUT_CLNT	"CON_CLNT"
242#define	SUNW_SETCONSINPUT_OBP	"CON_OBP"
243
244static u_int sbbc_console;
245
246static uint32_t	sbbc_scsolie;
247static uint32_t	sbbc_scsolir;
248static uint32_t	sbbc_solcons;
249static uint32_t	sbbc_solscie;
250static uint32_t	sbbc_solscir;
251static uint32_t	sbbc_toddata;
252
253/*
254 * internal helpers
255 */
256static int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh);
257static inline void sbbc_send_intr(bus_space_tag_t bst,
258    bus_space_handle_t bsh);
259static const char *sbbc_serengeti_set_console_input(char *new);
260
261/*
262 * SBBC PCI interface
263 */
264static bus_activate_resource_t sbbc_bus_activate_resource;
265static bus_adjust_resource_t sbbc_bus_adjust_resource;
266static bus_deactivate_resource_t sbbc_bus_deactivate_resource;
267static bus_alloc_resource_t sbbc_bus_alloc_resource;
268static bus_release_resource_t sbbc_bus_release_resource;
269static bus_get_resource_list_t sbbc_bus_get_resource_list;
270static bus_setup_intr_t sbbc_bus_setup_intr;
271static bus_teardown_intr_t sbbc_bus_teardown_intr;
272
273static device_attach_t sbbc_pci_attach;
274static device_probe_t sbbc_pci_probe;
275
276static clock_gettime_t sbbc_tod_gettime;
277static clock_settime_t sbbc_tod_settime;
278
279static device_method_t sbbc_pci_methods[] = {
280	/* Device interface */
281	DEVMETHOD(device_probe,		sbbc_pci_probe),
282	DEVMETHOD(device_attach,	sbbc_pci_attach),
283
284	DEVMETHOD(bus_alloc_resource,	sbbc_bus_alloc_resource),
285	DEVMETHOD(bus_activate_resource,sbbc_bus_activate_resource),
286	DEVMETHOD(bus_deactivate_resource,sbbc_bus_deactivate_resource),
287	DEVMETHOD(bus_adjust_resource,	sbbc_bus_adjust_resource),
288	DEVMETHOD(bus_release_resource,	sbbc_bus_release_resource),
289	DEVMETHOD(bus_setup_intr,	sbbc_bus_setup_intr),
290	DEVMETHOD(bus_teardown_intr,	sbbc_bus_teardown_intr),
291	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
292	DEVMETHOD(bus_get_resource_list, sbbc_bus_get_resource_list),
293
294	/* clock interface */
295	DEVMETHOD(clock_gettime,	sbbc_tod_gettime),
296	DEVMETHOD(clock_settime,	sbbc_tod_settime),
297
298	DEVMETHOD_END
299};
300
301static devclass_t sbbc_devclass;
302
303DEFINE_CLASS_0(sbbc, sbbc_driver, sbbc_pci_methods, sizeof(struct sbbc_softc));
304DRIVER_MODULE(sbbc, pci, sbbc_driver, sbbc_devclass, NULL, NULL);
305
306static int
307sbbc_pci_probe(device_t dev)
308{
309
310	if (pci_get_vendor(dev) == SBBC_PCI_VENDOR &&
311	    pci_get_device(dev) == SBBC_PCI_PRODUCT) {
312		device_set_desc(dev, "Sun BootBus controller");
313		return (BUS_PROBE_DEFAULT);
314	}
315	return (ENXIO);
316}
317
318static int
319sbbc_pci_attach(device_t dev)
320{
321	struct sbbc_softc *sc;
322	struct timespec ts;
323	device_t child;
324	bus_space_tag_t bst;
325	bus_space_handle_t bsh;
326	phandle_t node;
327	int error, rid;
328	uint32_t val;
329
330	/* Nothing to do if we're not the chosen one. */
331	if ((node = OF_finddevice("/chosen")) == -1) {
332		device_printf(dev, "failed to find /chosen\n");
333		return (ENXIO);
334	}
335	if (OF_getprop(node, "iosram", &node, sizeof(node)) == -1) {
336		device_printf(dev, "failed to get iosram\n");
337		return (ENXIO);
338	}
339	if (node != ofw_bus_get_node(dev))
340		return (0);
341
342	sc = device_get_softc(dev);
343	rid = SBBC_PCI_BAR;
344	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
345	    RF_ACTIVE);
346	if (sc->sc_res == NULL) {
347		device_printf(dev, "failed to allocate resources\n");
348		return (ENXIO);
349	}
350	bst = rman_get_bustag(sc->sc_res);
351	bsh = rman_get_bushandle(sc->sc_res);
352	if (sbbc_console != 0) {
353		/* Once again the interrupt pin isn't set. */
354		if (pci_get_intpin(dev) == 0)
355			pci_set_intpin(dev, 1);
356		child = device_add_child(dev, NULL, -1);
357		if (child == NULL)
358			device_printf(dev, "failed to add UART device\n");
359		error = bus_generic_attach(dev);
360		if (error != 0)
361			device_printf(dev, "failed to attach UART device\n");
362	} else {
363		error = sbbc_parse_toc(bst, bsh);
364		if (error != 0) {
365			device_printf(dev, "failed to parse TOC\n");
366			if (sbbc_console != 0) {
367				bus_release_resource(dev, SYS_RES_MEMORY, rid,
368				    sc->sc_res);
369				return (error);
370			}
371		}
372	}
373	if (sbbc_toddata != 0) {
374		if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
375		    SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC)
376			device_printf(dev, "invalid TOD magic %#x\n", val);
377		else if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
378		    SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION)
379			device_printf(dev, "invalid TOD version %#x\n", val);
380		else {
381			clock_register(dev, 1000000); /* 1 sec. resolution */
382			if (bootverbose) {
383				sbbc_tod_gettime(dev, &ts);
384				device_printf(dev,
385				    "current time: %ld.%09ld\n",
386				    (long)ts.tv_sec, ts.tv_nsec);
387			}
388		}
389	}
390	return (0);
391}
392
393/*
394 * Note that the bus methods don't pass-through the uart(4) requests but act
395 * as if they would come from sbbc(4) in order to avoid complications with
396 * pci(4) (actually, uart(4) isn't a real child but rather a function of
397 * sbbc(4) anyway).
398 */
399
400static struct resource *
401sbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type,
402    int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
403{
404	struct sbbc_softc *sc;
405
406	sc = device_get_softc(dev);
407	switch (type) {
408	case SYS_RES_IRQ:
409		return (bus_generic_alloc_resource(dev, dev, type, rid, start,
410		    end, count, flags));
411	case SYS_RES_MEMORY:
412		return (sc->sc_res);
413	default:
414		return (NULL);
415	}
416}
417
418static int
419sbbc_bus_activate_resource(device_t bus, device_t child, int type, int rid,
420    struct resource *res)
421{
422
423	if (type == SYS_RES_MEMORY)
424		return (0);
425	return (bus_generic_activate_resource(bus, child, type, rid, res));
426}
427
428static int
429sbbc_bus_deactivate_resource(device_t bus, device_t child, int type, int rid,
430    struct resource *res)
431{
432
433	if (type == SYS_RES_MEMORY)
434		return (0);
435	return (bus_generic_deactivate_resource(bus, child, type, rid, res));
436}
437
438static int
439sbbc_bus_adjust_resource(device_t bus __unused, device_t child __unused,
440    int type __unused, struct resource *res __unused, rman_res_t start __unused,
441    rman_res_t end __unused)
442{
443
444	return (ENXIO);
445}
446
447static int
448sbbc_bus_release_resource(device_t dev, device_t child __unused, int type,
449    int rid, struct resource *res)
450{
451
452	if (type == SYS_RES_IRQ)
453		return (bus_generic_release_resource(dev, dev, type, rid,
454		    res));
455	return (0);
456}
457
458static struct resource_list *
459sbbc_bus_get_resource_list(device_t dev, device_t child __unused)
460{
461
462	return (bus_generic_get_resource_list(dev, dev));
463}
464
465static int
466sbbc_bus_setup_intr(device_t dev, device_t child __unused,
467    struct resource *res, int flags, driver_filter_t *filt,
468    driver_intr_t *intr, void *arg, void **cookiep)
469{
470
471	return (bus_generic_setup_intr(dev, dev, res, flags, filt, intr, arg,
472	    cookiep));
473}
474
475static int
476sbbc_bus_teardown_intr(device_t dev, device_t child __unused,
477    struct resource *res, void *cookie)
478{
479
480	return (bus_generic_teardown_intr(dev, dev, res, cookie));
481}
482
483/*
484 * internal helpers
485 */
486static int
487sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh)
488{
489	char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)];
490	bus_size_t tag;
491	phandle_t node;
492	uint32_t off, sram_toc;
493	u_int i, tags;
494
495	if ((node = OF_finddevice("/chosen")) == -1)
496		return (ENXIO);
497	/* SRAM TOC offset defaults to 0. */
498	if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0)
499		sram_toc = 0;
500
501	bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc +
502	    SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE);
503	buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0';
504	if (strcmp(buf, SBBC_TOC_MAGIC) != 0)
505		return (ENXIO);
506
507	tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags));
508	for (i = 0; i < tags; i++) {
509		tag = sram_toc + SBBC_TOC_OFF(toc_tag) +
510		    i * sizeof(struct sbbc_sram_tag);
511		bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag +
512		    SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE);
513		buf[SBBC_TAG_KEY_SIZE - 1] = '\0';
514		off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset));
515		if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0)
516			sbbc_scsolie = off;
517		else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0)
518			sbbc_scsolir = off;
519		else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0)
520			sbbc_solcons = off;
521		else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0)
522			sbbc_solscie = off;
523		else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0)
524			sbbc_solscir = off;
525		else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0)
526			sbbc_toddata = off;
527	}
528	return (0);
529}
530
531static const char *
532sbbc_serengeti_set_console_input(char *new)
533{
534	struct {
535		cell_t name;
536		cell_t nargs;
537		cell_t nreturns;
538		cell_t new;
539		cell_t old;
540	} args = {
541		(cell_t)SUNW_SETCONSINPUT,
542		1,
543		1,
544	};
545
546	args.new = (cell_t)new;
547	if (ofw_entry(&args) == -1)
548		return (NULL);
549	return ((const char *)args.old);
550}
551
552static inline void
553sbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh)
554{
555
556	SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON);
557	bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1,
558	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
559}
560
561/*
562 * TOD interface
563 */
564static int
565sbbc_tod_gettime(device_t dev, struct timespec *ts)
566{
567	struct sbbc_softc *sc;
568	bus_space_tag_t bst;
569	bus_space_handle_t bsh;
570
571	sc = device_get_softc(dev);
572	bst = rman_get_bustag(sc->sc_res);
573	bsh = rman_get_bushandle(sc->sc_res);
574
575	ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) +
576	    SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew));
577	ts->tv_nsec = 0;
578	return (0);
579}
580
581static int
582sbbc_tod_settime(device_t dev, struct timespec *ts)
583{
584	struct sbbc_softc *sc;
585	bus_space_tag_t bst;
586	bus_space_handle_t bsh;
587
588	sc = device_get_softc(dev);
589	bst = rman_get_bustag(sc->sc_res);
590	bsh = rman_get_bushandle(sc->sc_res);
591
592	SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec -
593	    SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)));
594	return (0);
595}
596
597/*
598 * UART bus front-end
599 */
600static device_probe_t sbbc_uart_sbbc_probe;
601
602static device_method_t sbbc_uart_sbbc_methods[] = {
603	/* Device interface */
604	DEVMETHOD(device_probe,		sbbc_uart_sbbc_probe),
605	DEVMETHOD(device_attach,	uart_bus_attach),
606	DEVMETHOD(device_detach,	uart_bus_detach),
607
608	DEVMETHOD_END
609};
610
611DEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods,
612    sizeof(struct uart_softc));
613DRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, NULL, NULL);
614
615static int
616sbbc_uart_sbbc_probe(device_t dev)
617{
618	struct uart_softc *sc;
619
620	sc = device_get_softc(dev);
621	sc->sc_class = &uart_sbbc_class;
622	device_set_desc(dev, "Serengeti console");
623	return (uart_bus_probe(dev, 0, 0, 0, SBBC_PCI_BAR, 0, 0));
624}
625
626/*
627 * Low-level UART interface
628 */
629static int sbbc_uart_probe(struct uart_bas *bas);
630static void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits,
631    int stopbits, int parity);
632static void sbbc_uart_term(struct uart_bas *bas);
633static void sbbc_uart_putc(struct uart_bas *bas, int c);
634static int sbbc_uart_rxready(struct uart_bas *bas);
635static int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx);
636
637static struct uart_ops sbbc_uart_ops = {
638	.probe = sbbc_uart_probe,
639	.init = sbbc_uart_init,
640	.term = sbbc_uart_term,
641	.putc = sbbc_uart_putc,
642	.rxready = sbbc_uart_rxready,
643	.getc = sbbc_uart_getc,
644};
645
646static int
647sbbc_uart_probe(struct uart_bas *bas)
648{
649	bus_space_tag_t bst;
650	bus_space_handle_t bsh;
651	int error;
652
653	sbbc_console = 1;
654	bst = bas->bst;
655	bsh = bas->bsh;
656	error = sbbc_parse_toc(bst, bsh);
657	if (error != 0)
658		return (error);
659
660	if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 ||
661	    sbbc_solscie == 0 || sbbc_solscir == 0)
662		return (ENXIO);
663
664	if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) !=
665	    SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons +
666	    SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION)
667		return (ENXIO);
668	return (0);
669}
670
671static void
672sbbc_uart_init(struct uart_bas *bas, int baudrate __unused,
673    int databits __unused, int stopbits __unused, int parity __unused)
674{
675	bus_space_tag_t bst;
676	bus_space_handle_t bsh;
677
678	bst = bas->bst;
679	bsh = bas->bsh;
680
681	/* Enable output to and space in from the SC interrupts. */
682	SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) |
683	    SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN);
684	uart_barrier(bas);
685
686	/* Take over the console input. */
687	sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT);
688}
689
690static void
691sbbc_uart_term(struct uart_bas *bas __unused)
692{
693
694	/* Give back the console input. */
695	sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP);
696}
697
698static void
699sbbc_uart_putc(struct uart_bas *bas, int c)
700{
701	bus_space_tag_t bst;
702	bus_space_handle_t bsh;
703	uint32_t wrptr;
704
705	bst = bas->bst;
706	bsh = bas->bsh;
707
708	wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
709	    SBBC_CONS_OFF(cons_out_wrptr));
710	SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c);
711	uart_barrier(bas);
712	if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons +
713	    SBBC_CONS_OFF(cons_out_end)))
714		wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
715		    SBBC_CONS_OFF(cons_out_begin));
716	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr),
717	    wrptr);
718	uart_barrier(bas);
719
720	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
721	    SBBC_SRAM_CONS_OUT);
722	uart_barrier(bas);
723	sbbc_send_intr(bst, bsh);
724}
725
726static int
727sbbc_uart_rxready(struct uart_bas *bas)
728{
729	bus_space_tag_t bst;
730	bus_space_handle_t bsh;
731
732	bst = bas->bst;
733	bsh = bas->bsh;
734
735	if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) ==
736	    SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr)))
737		return (0);
738	return (1);
739}
740
741static int
742sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
743{
744	bus_space_tag_t bst;
745	bus_space_handle_t bsh;
746	int c;
747	uint32_t rdptr;
748
749	bst = bas->bst;
750	bsh = bas->bsh;
751
752	uart_lock(hwmtx);
753
754	while (sbbc_uart_rxready(bas) == 0) {
755		uart_unlock(hwmtx);
756		DELAY(4);
757		uart_lock(hwmtx);
758	}
759
760	rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
761	c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
762	uart_barrier(bas);
763	if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons +
764	    SBBC_CONS_OFF(cons_in_end)))
765		rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
766		    SBBC_CONS_OFF(cons_in_begin));
767	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr),
768	    rdptr);
769	uart_barrier(bas);
770	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
771	    SBBC_SRAM_CONS_SPACE_IN);
772	uart_barrier(bas);
773	sbbc_send_intr(bst, bsh);
774
775	uart_unlock(hwmtx);
776	return (c);
777}
778
779/*
780 * High-level UART interface
781 */
782static int sbbc_uart_bus_attach(struct uart_softc *sc);
783static int sbbc_uart_bus_detach(struct uart_softc *sc);
784static int sbbc_uart_bus_flush(struct uart_softc *sc, int what);
785static int sbbc_uart_bus_getsig(struct uart_softc *sc);
786static int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request,
787    intptr_t data);
788static int sbbc_uart_bus_ipend(struct uart_softc *sc);
789static int sbbc_uart_bus_param(struct uart_softc *sc, int baudrate,
790    int databits, int stopbits, int parity);
791static int sbbc_uart_bus_probe(struct uart_softc *sc);
792static int sbbc_uart_bus_receive(struct uart_softc *sc);
793static int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig);
794static int sbbc_uart_bus_transmit(struct uart_softc *sc);
795
796static kobj_method_t sbbc_uart_methods[] = {
797	KOBJMETHOD(uart_attach,		sbbc_uart_bus_attach),
798	KOBJMETHOD(uart_detach,		sbbc_uart_bus_detach),
799	KOBJMETHOD(uart_flush,		sbbc_uart_bus_flush),
800	KOBJMETHOD(uart_getsig,		sbbc_uart_bus_getsig),
801	KOBJMETHOD(uart_ioctl,		sbbc_uart_bus_ioctl),
802	KOBJMETHOD(uart_ipend,		sbbc_uart_bus_ipend),
803	KOBJMETHOD(uart_param,		sbbc_uart_bus_param),
804	KOBJMETHOD(uart_probe,		sbbc_uart_bus_probe),
805	KOBJMETHOD(uart_receive,	sbbc_uart_bus_receive),
806	KOBJMETHOD(uart_setsig,		sbbc_uart_bus_setsig),
807	KOBJMETHOD(uart_transmit,	sbbc_uart_bus_transmit),
808
809	DEVMETHOD_END
810};
811
812struct uart_class uart_sbbc_class = {
813	"sbbc",
814	sbbc_uart_methods,
815	sizeof(struct uart_softc),
816	.uc_ops = &sbbc_uart_ops,
817	.uc_range = 1,
818	.uc_rclk = 0x5bbc,	/* arbitrary */
819	.uc_rshift = 0
820};
821
822#define	SIGCHG(c, i, s, d)						\
823	if ((c) != 0) {							\
824		i |= (((i) & (s)) != 0) ? (s) : (s) | (d);		\
825	} else {							\
826		i = (((i) & (s)) != 0) ? ((i) & ~(s)) | (d) : (i);	\
827	}
828
829static int
830sbbc_uart_bus_attach(struct uart_softc *sc)
831{
832	struct uart_bas *bas;
833	bus_space_tag_t bst;
834	bus_space_handle_t bsh;
835	uint32_t wrptr;
836
837	bas = &sc->sc_bas;
838	bst = bas->bst;
839	bsh = bas->bsh;
840
841	uart_lock(sc->sc_hwmtx);
842
843	/*
844	 * Let the current output drain before enabling interrupts.  Not
845	 * doing so tends to cause lost output when turning them on.
846	 */
847	wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
848	    SBBC_CONS_OFF(cons_out_wrptr));
849	while (SBBC_SRAM_READ_4(sbbc_solcons +
850	    SBBC_CONS_OFF(cons_out_rdptr)) != wrptr);
851		cpu_spinwait();
852
853	/* Clear and acknowledge possibly outstanding interrupts. */
854	SBBC_SRAM_WRITE_4(sbbc_scsolir, 0);
855	uart_barrier(bas);
856	SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS,
857	    SBBC_SRAM_READ_4(sbbc_scsolir));
858	uart_barrier(bas);
859	/* Enable PCI interrupts. */
860	SBBC_REGS_WRITE_4(SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A);
861	uart_barrier(bas);
862	/* Enable input from and output to SC as well as break interrupts. */
863	SBBC_SRAM_WRITE_4(sbbc_scsolie, SBBC_SRAM_READ_4(sbbc_scsolie) |
864	    SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK |
865	    SBBC_SRAM_CONS_SPACE_OUT);
866	uart_barrier(bas);
867
868	uart_unlock(sc->sc_hwmtx);
869	return (0);
870}
871
872static int
873sbbc_uart_bus_detach(struct uart_softc *sc)
874{
875
876	/* Give back the console input. */
877	sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP);
878	return (0);
879}
880
881static int
882sbbc_uart_bus_flush(struct uart_softc *sc, int what)
883{
884	struct uart_bas *bas;
885	bus_space_tag_t bst;
886	bus_space_handle_t bsh;
887
888	bas = &sc->sc_bas;
889	bst = bas->bst;
890	bsh = bas->bsh;
891
892	if ((what & UART_FLUSH_TRANSMITTER) != 0)
893		return (ENODEV);
894	if ((what & UART_FLUSH_RECEIVER) != 0) {
895		SBBC_SRAM_WRITE_4(sbbc_solcons +
896		    SBBC_CONS_OFF(cons_in_rdptr),
897		    SBBC_SRAM_READ_4(sbbc_solcons +
898		    SBBC_CONS_OFF(cons_in_wrptr)));
899		uart_barrier(bas);
900	}
901	return (0);
902}
903
904static int
905sbbc_uart_bus_getsig(struct uart_softc *sc)
906{
907	uint32_t dummy, new, old, sig;
908
909	do {
910		old = sc->sc_hwsig;
911		sig = old;
912		dummy = 0;
913		SIGCHG(dummy, sig, SER_CTS, SER_DCTS);
914		SIGCHG(dummy, sig, SER_DCD, SER_DDCD);
915		SIGCHG(dummy, sig, SER_DSR, SER_DDSR);
916		new = sig & ~SER_MASK_DELTA;
917	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
918	return (sig);
919}
920
921static int
922sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
923{
924	int error;
925
926	error = 0;
927	uart_lock(sc->sc_hwmtx);
928	switch (request) {
929	case UART_IOCTL_BAUD:
930		*(int*)data = 9600;	/* arbitrary */
931		break;
932	default:
933		error = EINVAL;
934		break;
935	}
936	uart_unlock(sc->sc_hwmtx);
937	return (error);
938}
939
940static int
941sbbc_uart_bus_ipend(struct uart_softc *sc)
942{
943	struct uart_bas *bas;
944	bus_space_tag_t bst;
945	bus_space_handle_t bsh;
946	int ipend;
947	uint32_t reason, status;
948
949	bas = &sc->sc_bas;
950	bst = bas->bst;
951	bsh = bas->bsh;
952
953	uart_lock(sc->sc_hwmtx);
954	status = SBBC_REGS_READ_4(SBBC_PCI_INT_STATUS);
955	if (status == 0) {
956		uart_unlock(sc->sc_hwmtx);
957		return (0);
958	}
959
960	/*
961	 * Unfortunately, we can't use compare and swap for non-cachable
962	 * memory.
963	 */
964	reason = SBBC_SRAM_READ_4(sbbc_scsolir);
965	SBBC_SRAM_WRITE_4(sbbc_scsolir, 0);
966	uart_barrier(bas);
967	/* Acknowledge the interrupt. */
968	SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, status);
969	uart_barrier(bas);
970
971	uart_unlock(sc->sc_hwmtx);
972
973	ipend = 0;
974	if ((reason & SBBC_SRAM_CONS_IN) != 0)
975		ipend |= SER_INT_RXREADY;
976	if ((reason & SBBC_SRAM_CONS_BRK) != 0)
977		ipend |= SER_INT_BREAK;
978	if ((reason & SBBC_SRAM_CONS_SPACE_OUT) != 0 &&
979	    SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) ==
980	    SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)))
981		ipend |= SER_INT_TXIDLE;
982	return (ipend);
983}
984
985static int
986sbbc_uart_bus_param(struct uart_softc *sc __unused, int baudrate __unused,
987    int databits __unused, int stopbits __unused, int parity __unused)
988{
989
990	return (0);
991}
992
993static int
994sbbc_uart_bus_probe(struct uart_softc *sc)
995{
996	struct uart_bas *bas;
997	bus_space_tag_t bst;
998	bus_space_handle_t bsh;
999
1000	if (sbbc_console != 0) {
1001		bas = &sc->sc_bas;
1002		bst = bas->bst;
1003		bsh = bas->bsh;
1004		sc->sc_rxfifosz = SBBC_SRAM_READ_4(sbbc_solcons +
1005		    SBBC_CONS_OFF(cons_in_end)) - SBBC_SRAM_READ_4(sbbc_solcons +
1006		    SBBC_CONS_OFF(cons_in_begin)) - 1;
1007		sc->sc_txfifosz = SBBC_SRAM_READ_4(sbbc_solcons +
1008		    SBBC_CONS_OFF(cons_out_end)) - SBBC_SRAM_READ_4(sbbc_solcons +
1009		    SBBC_CONS_OFF(cons_out_begin)) - 1;
1010		return (0);
1011	}
1012	return (ENXIO);
1013}
1014
1015static int
1016sbbc_uart_bus_receive(struct uart_softc *sc)
1017{
1018	struct uart_bas *bas;
1019	bus_space_tag_t bst;
1020	bus_space_handle_t bsh;
1021	int c;
1022	uint32_t end, rdptr, wrptr;
1023
1024	bas = &sc->sc_bas;
1025	bst = bas->bst;
1026	bsh = bas->bsh;
1027
1028	uart_lock(sc->sc_hwmtx);
1029
1030	end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end));
1031	rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
1032	wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr));
1033	while (rdptr != wrptr) {
1034		if (uart_rx_full(sc) != 0) {
1035			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
1036			break;
1037		}
1038		c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
1039		uart_rx_put(sc, c);
1040		if (++rdptr == end)
1041			rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
1042			    SBBC_CONS_OFF(cons_in_begin));
1043	}
1044	uart_barrier(bas);
1045	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr),
1046	    rdptr);
1047	uart_barrier(bas);
1048	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
1049	    SBBC_SRAM_CONS_SPACE_IN);
1050	uart_barrier(bas);
1051	sbbc_send_intr(bst, bsh);
1052
1053	uart_unlock(sc->sc_hwmtx);
1054	return (0);
1055}
1056
1057static int
1058sbbc_uart_bus_setsig(struct uart_softc *sc, int sig)
1059{
1060	struct uart_bas *bas;
1061	uint32_t new, old;
1062
1063	bas = &sc->sc_bas;
1064	do {
1065		old = sc->sc_hwsig;
1066		new = old;
1067		if ((sig & SER_DDTR) != 0) {
1068			SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
1069		}
1070		if ((sig & SER_DRTS) != 0) {
1071			SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
1072		}
1073	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
1074	return (0);
1075}
1076
1077static int
1078sbbc_uart_bus_transmit(struct uart_softc *sc)
1079{
1080	struct uart_bas *bas;
1081	bus_space_tag_t bst;
1082	bus_space_handle_t bsh;
1083	int i;
1084	uint32_t end, wrptr;
1085
1086	bas = &sc->sc_bas;
1087	bst = bas->bst;
1088	bsh = bas->bsh;
1089
1090	uart_lock(sc->sc_hwmtx);
1091
1092	end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end));
1093	wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
1094	    SBBC_CONS_OFF(cons_out_wrptr));
1095	for (i = 0; i < sc->sc_txdatasz; i++) {
1096		SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, sc->sc_txbuf[i]);
1097		if (++wrptr == end)
1098			wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
1099			    SBBC_CONS_OFF(cons_out_begin));
1100	}
1101	uart_barrier(bas);
1102	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr),
1103	    wrptr);
1104	uart_barrier(bas);
1105	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
1106	    SBBC_SRAM_CONS_OUT);
1107	uart_barrier(bas);
1108	sbbc_send_intr(bst, bsh);
1109	sc->sc_txbusy = 1;
1110
1111	uart_unlock(sc->sc_hwmtx);
1112	return (0);
1113}
1114