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