1258210Srpaulo/*	$OpenBSD: sbbc.c,v 1.7 2009/11/09 17:53:39 nicm Exp $	*/
2258210Srpaulo/*-
3327595Sian * Copyright (c) 2008 Mark Kettenis
4258210Srpaulo *
5258210Srpaulo * Permission to use, copy, modify, and distribute this software for any
6258210Srpaulo * purpose with or without fee is hereby granted, provided that the above
7258210Srpaulo * copyright notice and this permission notice appear in all copies.
8258210Srpaulo *
9258210Srpaulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10258210Srpaulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11258210Srpaulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12258210Srpaulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13258210Srpaulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14258210Srpaulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15258210Srpaulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16258210Srpaulo */
17258210Srpaulo/*-
18258210Srpaulo * Copyright (c) 2010 Marius Strobl <marius@FreeBSD.org>
19258210Srpaulo * All rights reserved.
20258210Srpaulo *
21258210Srpaulo * Redistribution and use in source and binary forms, with or without
22258210Srpaulo * modification, are permitted provided that the following conditions
23258210Srpaulo * are met:
24258210Srpaulo * 1. Redistributions of source code must retain the above copyright
25258210Srpaulo *    notice, this list of conditions and the following disclaimer.
26258210Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
27258210Srpaulo *    notice, this list of conditions and the following disclaimer in the
28258210Srpaulo *    documentation and/or other materials provided with the distribution.
29258210Srpaulo *
30327595Sian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
31327595Sian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32327595Sian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33258210Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
34258210Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35327595Sian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36258210Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37258210Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38258210Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39258210Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40258210Srpaulo * SUCH DAMAGE.
41258210Srpaulo */
42327595Sian
43327595Sian#include <sys/cdefs.h>
44258210Srpaulo__FBSDID("$FreeBSD$");
45258210Srpaulo
46258210Srpaulo#include <sys/param.h>
47258210Srpaulo#include <sys/systm.h>
48258210Srpaulo#include <sys/bus.h>
49258210Srpaulo#include <sys/clock.h>
50327595Sian#include <sys/endian.h>
51258210Srpaulo#include <sys/kernel.h>
52258210Srpaulo#include <sys/lock.h>
53258210Srpaulo#include <sys/module.h>
54258210Srpaulo#include <sys/mutex.h>
55258210Srpaulo#include <sys/resource.h>
56258210Srpaulo#include <sys/rman.h>
57258210Srpaulo
58258210Srpaulo#include <dev/ofw/ofw_bus.h>
59258210Srpaulo#include <dev/ofw/openfirm.h>
60258210Srpaulo
61258210Srpaulo#include <machine/bus.h>
62258210Srpaulo#include <machine/cpu.h>
63258210Srpaulo#include <machine/resource.h>
64258210Srpaulo
65258210Srpaulo#include <dev/pci/pcireg.h>
66258210Srpaulo#include <dev/pci/pcivar.h>
67258210Srpaulo#include <dev/uart/uart.h>
68258210Srpaulo#include <dev/uart/uart_cpu.h>
69327595Sian#include <dev/uart/uart_bus.h>
70327595Sian
71327595Sian#include "clock_if.h"
72327595Sian#include "uart_if.h"
73258210Srpaulo
74258210Srpaulo#define	SBBC_PCI_BAR		PCIR_BAR(0)
75258210Srpaulo#define	SBBC_PCI_VENDOR		0x108e
76258210Srpaulo#define	SBBC_PCI_PRODUCT	0xc416
77258210Srpaulo
78258210Srpaulo#define	SBBC_REGS_OFFSET	0x800000
79327595Sian#define	SBBC_REGS_SIZE		0x6230
80327595Sian#define	SBBC_EPLD_OFFSET	0x8e0000
81327595Sian#define	SBBC_EPLD_SIZE		0x20
82327595Sian#define	SBBC_SRAM_OFFSET	0x900000
83258210Srpaulo#define	SBBC_SRAM_SIZE		0x20000	/* 128KB SRAM */
84327595Sian
85327595Sian#define	SBBC_PCI_INT_STATUS	0x2320
86327595Sian#define	SBBC_PCI_INT_ENABLE	0x2330
87327595Sian#define	SBBC_PCI_ENABLE_INT_A	0x11
88327595Sian
89327595Sian#define	SBBC_EPLD_INTERRUPT	0x13
90283138Srpaulo#define	SBBC_EPLD_INTERRUPT_ON	0x01
91327595Sian
92327595Sian#define	SBBC_SRAM_CONS_IN		0x00000001
93327595Sian#define	SBBC_SRAM_CONS_OUT		0x00000002
94327595Sian#define	SBBC_SRAM_CONS_BRK		0x00000004
95327595Sian#define	SBBC_SRAM_CONS_SPACE_IN		0x00000008
96327595Sian#define	SBBC_SRAM_CONS_SPACE_OUT	0x00000010
97327595Sian
98327595Sian#define	SBBC_TAG_KEY_SIZE	8
99327595Sian#define	SBBC_TAG_KEY_SCSOLIE	"SCSOLIE"	/* SC -> OS int. enable */
100327595Sian#define	SBBC_TAG_KEY_SCSOLIR	"SCSOLIR"	/* SC -> OS int. reason */
101327595Sian#define	SBBC_TAG_KEY_SOLCONS	"SOLCONS"	/* OS console buffer */
102327595Sian#define	SBBC_TAG_KEY_SOLSCIE	"SOLSCIE"	/* OS -> SC int. enable */
103327595Sian#define	SBBC_TAG_KEY_SOLSCIR	"SOLSCIR"	/* OS -> SC int. reason */
104327595Sian#define	SBBC_TAG_KEY_TODDATA	"TODDATA"	/* OS TOD struct */
105327595Sian#define	SBBC_TAG_OFF(x)		offsetof(struct sbbc_sram_tag, x)
106327595Sian
107327595Sianstruct sbbc_sram_tag {
108327595Sian	char		tag_key[SBBC_TAG_KEY_SIZE];
109327595Sian	uint32_t	tag_size;
110327595Sian	uint32_t	tag_offset;
111327595Sian} __packed;
112327595Sian
113327595Sian#define	SBBC_TOC_MAGIC		"TOCSRAM"
114327595Sian#define	SBBC_TOC_MAGIC_SIZE	8
115327595Sian#define	SBBC_TOC_TAGS_MAX	32
116327595Sian#define	SBBC_TOC_OFF(x)		offsetof(struct sbbc_sram_toc, x)
117327595Sian
118327595Sianstruct sbbc_sram_toc {
119327595Sian	char			toc_magic[SBBC_TOC_MAGIC_SIZE];
120327595Sian	uint8_t			toc_reserved;
121327595Sian	uint8_t			toc_type;
122327595Sian	uint16_t		toc_version;
123327595Sian	uint32_t		toc_ntags;
124258210Srpaulo	struct sbbc_sram_tag	toc_tag[SBBC_TOC_TAGS_MAX];
125258210Srpaulo} __packed;
126258210Srpaulo
127327595Sian#define	SBBC_TOD_MAGIC		0x54443100	/* "TD1" */
128327595Sian#define	SBBC_TOD_VERSION	1
129327595Sian#define	SBBC_TOD_OFF(x)		offsetof(struct sbbc_sram_tod, x)
130258210Srpaulo
131258210Srpaulostruct sbbc_sram_tod {
132258210Srpaulo	uint32_t	tod_magic;
133258210Srpaulo	uint32_t	tod_version;
134327595Sian	uint64_t	tod_time;
135258210Srpaulo	uint64_t	tod_skew;
136258210Srpaulo	uint32_t	tod_reserved;
137258210Srpaulo	uint32_t	tod_heartbeat;
138258210Srpaulo	uint32_t	tod_timeout;
139258210Srpaulo} __packed;
140258210Srpaulo
141258210Srpaulo#define	SBBC_CONS_MAGIC		0x434f4e00	/* "CON" */
142258210Srpaulo#define	SBBC_CONS_VERSION	1
143258210Srpaulo#define	SBBC_CONS_OFF(x)	offsetof(struct sbbc_sram_cons, x)
144258210Srpaulo
145258210Srpaulostruct sbbc_sram_cons {
146258210Srpaulo	uint32_t cons_magic;
147258210Srpaulo	uint32_t cons_version;
148258210Srpaulo	uint32_t cons_size;
149258210Srpaulo
150258210Srpaulo	uint32_t cons_in_begin;
151258210Srpaulo	uint32_t cons_in_end;
152258210Srpaulo	uint32_t cons_in_rdptr;
153258210Srpaulo	uint32_t cons_in_wrptr;
154258210Srpaulo
155258210Srpaulo	uint32_t cons_out_begin;
156258210Srpaulo	uint32_t cons_out_end;
157258210Srpaulo	uint32_t cons_out_rdptr;
158258210Srpaulo	uint32_t cons_out_wrptr;
159258210Srpaulo} __packed;
160258210Srpaulo
161327595Sianstruct sbbc_softc {
162258210Srpaulo	struct resource *sc_res;
163258210Srpaulo};
164258210Srpaulo
165258210Srpaulo#define	SBBC_READ_N(wdth, offs)						\
166258210Srpaulo	bus_space_read_ ## wdth((bst), (bsh), (offs))
167258210Srpaulo#define	SBBC_WRITE_N(wdth, offs, val)					\
168258210Srpaulo	bus_space_write_ ## wdth((bst), (bsh), (offs), (val))
169258210Srpaulo
170258210Srpaulo#define	SBBC_READ_1(offs)						\
171258210Srpaulo	SBBC_READ_N(1, (offs))
172258210Srpaulo#define	SBBC_READ_2(offs)						\
173258210Srpaulo	bswap16(SBBC_READ_N(2, (offs)))
174327595Sian#define	SBBC_READ_4(offs)						\
175258210Srpaulo	bswap32(SBBC_READ_N(4, (offs)))
176327595Sian#define	SBBC_READ_8(offs)						\
177327595Sian	bswap64(SBBC_READ_N(8, (offs)))
178327595Sian#define	SBBC_WRITE_1(offs, val)						\
179327595Sian	SBBC_WRITE_N(1, (offs), (val))
180327595Sian#define	SBBC_WRITE_2(offs, val)						\
181327595Sian	SBBC_WRITE_N(2, (offs), bswap16(val))
182327595Sian#define	SBBC_WRITE_4(offs, val)						\
183327595Sian	SBBC_WRITE_N(4, (offs), bswap32(val))
184327595Sian#define	SBBC_WRITE_8(offs, val)						\
185327595Sian	SBBC_WRITE_N(8, (offs), bswap64(val))
186327595Sian
187327595Sian#define	SBBC_REGS_READ_1(offs)						\
188327595Sian	SBBC_READ_1((offs) + SBBC_REGS_OFFSET)
189327595Sian#define	SBBC_REGS_READ_2(offs)						\
190327595Sian	SBBC_READ_2((offs) + SBBC_REGS_OFFSET)
191327595Sian#define	SBBC_REGS_READ_4(offs)						\
192327595Sian	SBBC_READ_4((offs) + SBBC_REGS_OFFSET)
193327595Sian#define	SBBC_REGS_READ_8(offs)						\
194327595Sian	SBBC_READ_8((offs) + SBBC_REGS_OFFSET)
195327595Sian#define	SBBC_REGS_WRITE_1(offs, val)					\
196327595Sian	SBBC_WRITE_1((offs) + SBBC_REGS_OFFSET, (val))
197327595Sian#define	SBBC_REGS_WRITE_2(offs, val)					\
198327595Sian	SBBC_WRITE_2((offs) + SBBC_REGS_OFFSET, (val))
199327595Sian#define	SBBC_REGS_WRITE_4(offs, val)					\
200327595Sian	SBBC_WRITE_4((offs) + SBBC_REGS_OFFSET, (val))
201327595Sian#define	SBBC_REGS_WRITE_8(offs, val)					\
202327595Sian	SBBC_WRITE_8((offs) + SBBC_REGS_OFFSET, (val))
203327595Sian
204327595Sian#define	SBBC_EPLD_READ_1(offs)						\
205327595Sian	SBBC_READ_1((offs) + SBBC_EPLD_OFFSET)
206327595Sian#define	SBBC_EPLD_READ_2(offs)						\
207327595Sian	SBBC_READ_2((offs) + SBBC_EPLD_OFFSET)
208327595Sian#define	SBBC_EPLD_READ_4(offs)						\
209327595Sian	SBBC_READ_4((offs) + SBBC_EPLD_OFFSET)
210327595Sian#define	SBBC_EPLD_READ_8(offs)						\
211327595Sian	SBBC_READ_8((offs) + SBBC_EPLD_OFFSET)
212327595Sian#define	SBBC_EPLD_WRITE_1(offs, val)					\
213327595Sian	SBBC_WRITE_1((offs) + SBBC_EPLD_OFFSET, (val))
214327595Sian#define	SBBC_EPLD_WRITE_2(offs, val)					\
215327595Sian	SBBC_WRITE_2((offs) + SBBC_EPLD_OFFSET, (val))
216327595Sian#define	SBBC_EPLD_WRITE_4(offs, val)					\
217327595Sian	SBBC_WRITE_4((offs) + SBBC_EPLD_OFFSET, (val))
218327595Sian#define	SBBC_EPLD_WRITE_8(offs, val)					\
219327595Sian	SBBC_WRITE_8((offs) + SBBC_EPLD_OFFSET, (val))
220327595Sian
221327595Sian#define	SBBC_SRAM_READ_1(offs)						\
222327595Sian	SBBC_READ_1((offs) + SBBC_SRAM_OFFSET)
223327595Sian#define	SBBC_SRAM_READ_2(offs)						\
224327595Sian	SBBC_READ_2((offs) + SBBC_SRAM_OFFSET)
225327595Sian#define	SBBC_SRAM_READ_4(offs)						\
226327595Sian	SBBC_READ_4((offs) + SBBC_SRAM_OFFSET)
227327595Sian#define	SBBC_SRAM_READ_8(offs)						\
228327595Sian	SBBC_READ_8((offs) + SBBC_SRAM_OFFSET)
229327595Sian#define	SBBC_SRAM_WRITE_1(offs, val)					\
230327595Sian	SBBC_WRITE_1((offs) + SBBC_SRAM_OFFSET, (val))
231327595Sian#define	SBBC_SRAM_WRITE_2(offs, val)					\
232327595Sian	SBBC_WRITE_2((offs) + SBBC_SRAM_OFFSET, (val))
233327595Sian#define	SBBC_SRAM_WRITE_4(offs, val)					\
234327595Sian	SBBC_WRITE_4((offs) + SBBC_SRAM_OFFSET, (val))
235327595Sian#define	SBBC_SRAM_WRITE_8(offs, val)					\
236327595Sian	SBBC_WRITE_8((offs) + SBBC_SRAM_OFFSET, (val))
237327595Sian
238327595Sian#define	SUNW_SETCONSINPUT	"SUNW,set-console-input"
239327595Sian#define	SUNW_SETCONSINPUT_CLNT	"CON_CLNT"
240327595Sian#define	SUNW_SETCONSINPUT_OBP	"CON_OBP"
241327595Sian
242327595Sianstatic u_int sbbc_console;
243327595Sian
244327595Sianstatic uint32_t	sbbc_scsolie;
245327595Sianstatic uint32_t	sbbc_scsolir;
246327595Sianstatic uint32_t	sbbc_solcons;
247327595Sianstatic uint32_t	sbbc_solscie;
248327595Sianstatic uint32_t	sbbc_solscir;
249327595Sianstatic uint32_t	sbbc_toddata;
250327595Sian
251327595Sian/*
252327595Sian * internal helpers
253327595Sian */
254327595Sianstatic int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh);
255327595Sianstatic inline void sbbc_send_intr(bus_space_tag_t bst,
256327595Sian    bus_space_handle_t bsh);
257327595Sianstatic const char *sbbc_serengeti_set_console_input(char *new);
258327595Sian
259327595Sian/*
260327595Sian * SBBC PCI interface
261327595Sian */
262327595Sianstatic bus_activate_resource_t sbbc_bus_activate_resource;
263327595Sianstatic bus_adjust_resource_t sbbc_bus_adjust_resource;
264327595Sianstatic bus_deactivate_resource_t sbbc_bus_deactivate_resource;
265327595Sianstatic bus_alloc_resource_t sbbc_bus_alloc_resource;
266327595Sianstatic bus_release_resource_t sbbc_bus_release_resource;
267327595Sianstatic bus_get_resource_list_t sbbc_bus_get_resource_list;
268327595Sianstatic bus_setup_intr_t sbbc_bus_setup_intr;
269327595Sianstatic bus_teardown_intr_t sbbc_bus_teardown_intr;
270327595Sian
271327595Sianstatic device_attach_t sbbc_pci_attach;
272327595Sianstatic device_probe_t sbbc_pci_probe;
273327595Sian
274327595Sianstatic clock_gettime_t sbbc_tod_gettime;
275327595Sianstatic clock_settime_t sbbc_tod_settime;
276327595Sian
277327595Sianstatic device_method_t sbbc_pci_methods[] = {
278327595Sian	/* Device interface */
279327595Sian	DEVMETHOD(device_probe,		sbbc_pci_probe),
280258210Srpaulo	DEVMETHOD(device_attach,	sbbc_pci_attach),
281258210Srpaulo
282258210Srpaulo	DEVMETHOD(bus_alloc_resource,	sbbc_bus_alloc_resource),
283258210Srpaulo	DEVMETHOD(bus_activate_resource,sbbc_bus_activate_resource),
284258210Srpaulo	DEVMETHOD(bus_deactivate_resource,sbbc_bus_deactivate_resource),
285258210Srpaulo	DEVMETHOD(bus_adjust_resource,	sbbc_bus_adjust_resource),
286258210Srpaulo	DEVMETHOD(bus_release_resource,	sbbc_bus_release_resource),
287258210Srpaulo	DEVMETHOD(bus_setup_intr,	sbbc_bus_setup_intr),
288258210Srpaulo	DEVMETHOD(bus_teardown_intr,	sbbc_bus_teardown_intr),
289258210Srpaulo	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
290258210Srpaulo	DEVMETHOD(bus_get_resource_list, sbbc_bus_get_resource_list),
291258210Srpaulo
292258210Srpaulo	/* clock interface */
293258210Srpaulo	DEVMETHOD(clock_gettime,	sbbc_tod_gettime),
294258210Srpaulo	DEVMETHOD(clock_settime,	sbbc_tod_settime),
295258210Srpaulo
296258210Srpaulo	DEVMETHOD_END
297327595Sian};
298327595Sian
299327595Sianstatic devclass_t sbbc_devclass;
300327595Sian
301327595SianDEFINE_CLASS_0(sbbc, sbbc_driver, sbbc_pci_methods, sizeof(struct sbbc_softc));
302327595SianDRIVER_MODULE(sbbc, pci, sbbc_driver, sbbc_devclass, NULL, NULL);
303327595Sian
304327595Sianstatic int
305327595Siansbbc_pci_probe(device_t dev)
306327595Sian{
307327595Sian
308327595Sian	if (pci_get_vendor(dev) == SBBC_PCI_VENDOR &&
309327595Sian	    pci_get_device(dev) == SBBC_PCI_PRODUCT) {
310327595Sian		device_set_desc(dev, "Sun BootBus controller");
311327595Sian		return (BUS_PROBE_DEFAULT);
312327595Sian	}
313327595Sian	return (ENXIO);
314327595Sian}
315327595Sian
316327595Sianstatic int
317327595Siansbbc_pci_attach(device_t dev)
318327595Sian{
319327595Sian	struct sbbc_softc *sc;
320327595Sian	struct timespec ts;
321327595Sian	device_t child;
322327595Sian	bus_space_tag_t bst;
323327595Sian	bus_space_handle_t bsh;
324327595Sian	phandle_t node;
325327595Sian	int error, rid;
326327595Sian	uint32_t val;
327327595Sian
328327595Sian	/* Nothing to to if we're not the chosen one. */
329327595Sian	if ((node = OF_finddevice("/chosen")) == -1) {
330327595Sian		device_printf(dev, "failed to find /chosen\n");
331327595Sian		return (ENXIO);
332327595Sian	}
333327595Sian	if (OF_getprop(node, "iosram", &node, sizeof(node)) == -1) {
334327595Sian		device_printf(dev, "failed to get iosram\n");
335327595Sian		return (ENXIO);
336327595Sian	}
337327595Sian	if (node != ofw_bus_get_node(dev))
338327595Sian		return (0);
339327595Sian
340327595Sian	sc = device_get_softc(dev);
341327595Sian	rid = SBBC_PCI_BAR;
342327595Sian	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
343327595Sian	    RF_ACTIVE);
344327595Sian	if (sc->sc_res == NULL) {
345327595Sian		device_printf(dev, "failed to allocate resources\n");
346327595Sian		return (ENXIO);
347327595Sian	}
348327595Sian	bst = rman_get_bustag(sc->sc_res);
349327595Sian	bsh = rman_get_bushandle(sc->sc_res);
350327595Sian	if (sbbc_console != 0) {
351327595Sian		/* Once again the interrupt pin isn't set. */
352327595Sian		if (pci_get_intpin(dev) == 0)
353327595Sian			pci_set_intpin(dev, 1);
354327595Sian		child = device_add_child(dev, NULL, -1);
355327595Sian		if (child == NULL)
356258210Srpaulo			device_printf(dev, "failed to add UART device\n");
357327595Sian		error = bus_generic_attach(dev);
358327595Sian		if (error != 0)
359327595Sian			device_printf(dev, "failed to attach UART device\n");
360327595Sian	} else {
361327595Sian		error = sbbc_parse_toc(bst, bsh);
362327595Sian		if (error != 0) {
363327595Sian			device_printf(dev, "failed to parse TOC\n");
364327595Sian			if (sbbc_console != 0) {
365327595Sian				bus_release_resource(dev, SYS_RES_MEMORY, rid,
366327595Sian				    sc->sc_res);
367327595Sian				return (error);
368327595Sian			}
369327595Sian		}
370327595Sian	}
371327595Sian	if (sbbc_toddata != 0) {
372327595Sian		if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
373327595Sian		    SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC)
374327595Sian			device_printf(dev, "invalid TOD magic %#x\n", val);
375327595Sian		else if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
376327595Sian		    SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION)
377327595Sian			device_printf(dev, "invalid TOD version %#x\n", val);
378327595Sian		else {
379327595Sian			clock_register(dev, 1000000); /* 1 sec. resolution */
380327595Sian			if (bootverbose) {
381327595Sian				sbbc_tod_gettime(dev, &ts);
382327595Sian				device_printf(dev,
383327595Sian				    "current time: %ld.%09ld\n",
384327595Sian				    (long)ts.tv_sec, ts.tv_nsec);
385327595Sian			}
386327595Sian		}
387327595Sian	}
388327595Sian	return (0);
389327595Sian}
390327595Sian
391327595Sian/*
392327595Sian * Note that the bus methods don't pass-through the uart(4) requests but act
393327595Sian * as if they would come from sbbc(4) in order to avoid complications with
394327595Sian * pci(4) (actually, uart(4) isn't a real child but rather a function of
395327595Sian * sbbc(4) anyway).
396327595Sian */
397327595Sian
398327595Sianstatic struct resource *
399327595Siansbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type,
400327595Sian    int *rid, u_long start, u_long end, u_long count, u_int flags)
401327595Sian{
402327595Sian	struct sbbc_softc *sc;
403327595Sian
404327595Sian	sc = device_get_softc(dev);
405327595Sian	switch (type) {
406327595Sian	case SYS_RES_IRQ:
407327595Sian		return (bus_generic_alloc_resource(dev, dev, type, rid, start,
408327595Sian		    end, count, flags));
409327595Sian	case SYS_RES_MEMORY:
410327595Sian		return (sc->sc_res);
411327595Sian	default:
412327595Sian		return (NULL);
413327595Sian	}
414327595Sian}
415327595Sian
416327595Sianstatic int
417327595Siansbbc_bus_activate_resource(device_t bus, device_t child, int type, int rid,
418327595Sian    struct resource *res)
419327595Sian{
420327595Sian
421327595Sian	if (type == SYS_RES_MEMORY)
422327595Sian		return (0);
423327595Sian	return (bus_generic_activate_resource(bus, child, type, rid, res));
424327595Sian}
425327595Sian
426327595Sianstatic int
427327595Siansbbc_bus_deactivate_resource(device_t bus, device_t child, int type, int rid,
428327595Sian    struct resource *res)
429327595Sian{
430327595Sian
431327595Sian	if (type == SYS_RES_MEMORY)
432327595Sian		return (0);
433327595Sian	return (bus_generic_deactivate_resource(bus, child, type, rid, res));
434327595Sian}
435327595Sian
436327595Sianstatic int
437327595Siansbbc_bus_adjust_resource(device_t bus __unused, device_t child __unused,
438327595Sian    int type __unused, struct resource *res __unused, u_long start __unused,
439327595Sian    u_long end __unused)
440327595Sian{
441327595Sian
442327595Sian	return (ENXIO);
443327595Sian}
444327595Sian
445327595Sianstatic int
446327595Siansbbc_bus_release_resource(device_t dev, device_t child __unused, int type,
447327595Sian    int rid, struct resource *res)
448327595Sian{
449327595Sian
450327595Sian	if (type == SYS_RES_IRQ)
451327595Sian		return (bus_generic_release_resource(dev, dev, type, rid,
452327595Sian		    res));
453327595Sian	return (0);
454327595Sian}
455327595Sian
456327595Sianstatic struct resource_list *
457327595Siansbbc_bus_get_resource_list(device_t dev, device_t child __unused)
458327595Sian{
459327595Sian
460327595Sian	return (bus_generic_get_resource_list(dev, dev));
461327595Sian}
462327595Sian
463327595Sianstatic int
464327595Siansbbc_bus_setup_intr(device_t dev, device_t child __unused,
465327595Sian    struct resource *res, int flags, driver_filter_t *filt,
466327595Sian    driver_intr_t *intr, void *arg, void **cookiep)
467327595Sian{
468327595Sian
469327595Sian	return (bus_generic_setup_intr(dev, dev, res, flags, filt, intr, arg,
470327595Sian	    cookiep));
471327595Sian}
472327595Sian
473327595Sianstatic int
474327595Siansbbc_bus_teardown_intr(device_t dev, device_t child __unused,
475327595Sian    struct resource *res, void *cookie)
476327595Sian{
477327595Sian
478327595Sian	return (bus_generic_teardown_intr(dev, dev, res, cookie));
479327595Sian}
480327595Sian
481327595Sian/*
482327595Sian * internal helpers
483327595Sian */
484327595Sianstatic int
485327595Siansbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh)
486327595Sian{
487327595Sian	char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)];
488327595Sian	bus_size_t tag;
489327595Sian	phandle_t node;
490327595Sian	uint32_t off, sram_toc;
491327595Sian	u_int i, tags;
492327595Sian
493327595Sian	if ((node = OF_finddevice("/chosen")) == -1)
494327595Sian		return (ENXIO);
495327595Sian	/* SRAM TOC offset defaults to 0. */
496258210Srpaulo	if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0)
497258210Srpaulo		sram_toc = 0;
498261410Sian
499261410Sian	bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc +
500261410Sian	    SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE);
501261410Sian	buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0';
502258210Srpaulo	if (strcmp(buf, SBBC_TOC_MAGIC) != 0)
503258210Srpaulo		return (ENXIO);
504258210Srpaulo
505258210Srpaulo	tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags));
506258210Srpaulo	for (i = 0; i < tags; i++) {
507258210Srpaulo		tag = sram_toc + SBBC_TOC_OFF(toc_tag) +
508258210Srpaulo		    i * sizeof(struct sbbc_sram_tag);
509258210Srpaulo		bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag +
510258210Srpaulo		    SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE);
511258210Srpaulo		buf[SBBC_TAG_KEY_SIZE - 1] = '\0';
512258210Srpaulo		off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset));
513258210Srpaulo		if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0)
514258210Srpaulo			sbbc_scsolie = off;
515258210Srpaulo		else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0)
516258210Srpaulo			sbbc_scsolir = off;
517258210Srpaulo		else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0)
518258210Srpaulo			sbbc_solcons = off;
519258210Srpaulo		else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0)
520258210Srpaulo			sbbc_solscie = off;
521258210Srpaulo		else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0)
522258210Srpaulo			sbbc_solscir = off;
523261211Sjmg		else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0)
524258210Srpaulo			sbbc_toddata = off;
525258210Srpaulo	}
526258210Srpaulo	return (0);
527258210Srpaulo}
528258210Srpaulo
529258210Srpaulostatic const char *
530327595Siansbbc_serengeti_set_console_input(char *new)
531327595Sian{
532327595Sian	struct {
533327595Sian		cell_t name;
534327595Sian		cell_t nargs;
535327595Sian		cell_t nreturns;
536327595Sian		cell_t new;
537327595Sian		cell_t old;
538327595Sian	} args = {
539327595Sian		(cell_t)SUNW_SETCONSINPUT,
540327595Sian		1,
541327595Sian		1,
542327595Sian	};
543327595Sian
544327595Sian	args.new = (cell_t)new;
545327595Sian	if (ofw_entry(&args) == -1)
546327595Sian		return (NULL);
547327595Sian	return ((const char *)args.old);
548327595Sian}
549258210Srpaulo
550258210Srpaulostatic inline void
551258210Srpaulosbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh)
552258210Srpaulo{
553258210Srpaulo
554258210Srpaulo	SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON);
555258210Srpaulo	bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1,
556327595Sian	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
557327595Sian}
558327595Sian
559258210Srpaulo/*
560327595Sian * TOD interface
561327595Sian */
562327595Sianstatic int
563327595Siansbbc_tod_gettime(device_t dev, struct timespec *ts)
564327595Sian{
565327595Sian	struct sbbc_softc *sc;
566327595Sian	bus_space_tag_t bst;
567327595Sian	bus_space_handle_t bsh;
568327595Sian
569327595Sian	sc = device_get_softc(dev);
570327595Sian	bst = rman_get_bustag(sc->sc_res);
571327595Sian	bsh = rman_get_bushandle(sc->sc_res);
572327595Sian
573327595Sian	ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) +
574327595Sian	    SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew));
575327595Sian	ts->tv_nsec = 0;
576327595Sian	return (0);
577327595Sian}
578327595Sian
579327595Sianstatic int
580327595Siansbbc_tod_settime(device_t dev, struct timespec *ts)
581327595Sian{
582327595Sian	struct sbbc_softc *sc;
583327595Sian	bus_space_tag_t bst;
584327595Sian	bus_space_handle_t bsh;
585327595Sian
586327595Sian	sc = device_get_softc(dev);
587327595Sian	bst = rman_get_bustag(sc->sc_res);
588327595Sian	bsh = rman_get_bushandle(sc->sc_res);
589327595Sian
590327595Sian	SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec -
591327595Sian	    SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)));
592327595Sian	return (0);
593327595Sian}
594327595Sian
595258210Srpaulo/*
596258210Srpaulo * UART bus front-end
597327595Sian */
598327595Sianstatic device_probe_t sbbc_uart_sbbc_probe;
599258210Srpaulo
600258210Srpaulostatic device_method_t sbbc_uart_sbbc_methods[] = {
601258210Srpaulo	/* Device interface */
602258210Srpaulo	DEVMETHOD(device_probe,		sbbc_uart_sbbc_probe),
603258210Srpaulo	DEVMETHOD(device_attach,	uart_bus_attach),
604258210Srpaulo	DEVMETHOD(device_detach,	uart_bus_detach),
605327595Sian
606327595Sian	DEVMETHOD_END
607327595Sian};
608327595Sian
609327595SianDEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods,
610327595Sian    sizeof(struct uart_softc));
611327595SianDRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, NULL, NULL);
612327595Sian
613258210Srpaulostatic int
614258210Srpaulosbbc_uart_sbbc_probe(device_t dev)
615258210Srpaulo{
616258210Srpaulo	struct uart_softc *sc;
617258210Srpaulo
618258210Srpaulo	sc = device_get_softc(dev);
619327595Sian	sc->sc_class = &uart_sbbc_class;
620258210Srpaulo	device_set_desc(dev, "Serengeti console");
621327595Sian	return (uart_bus_probe(dev, 0, 0, SBBC_PCI_BAR, 0));
622327595Sian}
623327595Sian
624327595Sian/*
625327595Sian * Low-level UART interface
626258210Srpaulo */
627258210Srpaulostatic int sbbc_uart_probe(struct uart_bas *bas);
628258210Srpaulostatic void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits,
629275376Srpaulo    int stopbits, int parity);
630258210Srpaulostatic void sbbc_uart_term(struct uart_bas *bas);
631258210Srpaulostatic void sbbc_uart_putc(struct uart_bas *bas, int c);
632327595Sianstatic int sbbc_uart_rxready(struct uart_bas *bas);
633327595Sianstatic int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx);
634327595Sian
635327595Sianstatic struct uart_ops sbbc_uart_ops = {
636327595Sian	.probe = sbbc_uart_probe,
637327595Sian	.init = sbbc_uart_init,
638327595Sian	.term = sbbc_uart_term,
639258210Srpaulo	.putc = sbbc_uart_putc,
640327595Sian	.rxready = sbbc_uart_rxready,
641283138Srpaulo	.getc = sbbc_uart_getc,
642258210Srpaulo};
643258210Srpaulo
644258210Srpaulostatic int
645258210Srpaulosbbc_uart_probe(struct uart_bas *bas)
646258210Srpaulo{
647258210Srpaulo	bus_space_tag_t bst;
648258210Srpaulo	bus_space_handle_t bsh;
649258210Srpaulo	int error;
650258210Srpaulo
651258210Srpaulo	sbbc_console = 1;
652258210Srpaulo	bst = bas->bst;
653258210Srpaulo	bsh = bas->bsh;
654283138Srpaulo	error = sbbc_parse_toc(bst, bsh);
655283138Srpaulo	if (error != 0)
656283138Srpaulo		return (error);
657283138Srpaulo
658327595Sian	if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 ||
659283138Srpaulo	    sbbc_solscie == 0 || sbbc_solscir == 0)
660283138Srpaulo		return (ENXIO);
661327595Sian
662283138Srpaulo	if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) !=
663327595Sian	    SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons +
664327595Sian	    SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION)
665258210Srpaulo		return (ENXIO);
666327595Sian	return (0);
667283138Srpaulo}
668283138Srpaulo
669327595Sianstatic void
670327595Siansbbc_uart_init(struct uart_bas *bas, int baudrate __unused,
671327595Sian    int databits __unused, int stopbits __unused, int parity __unused)
672327595Sian{
673327595Sian	bus_space_tag_t bst;
674327595Sian	bus_space_handle_t bsh;
675327595Sian
676327595Sian	bst = bas->bst;
677327595Sian	bsh = bas->bsh;
678327595Sian
679327595Sian	/* Enable output to and space in from the SC interrupts. */
680327595Sian	SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) |
681327595Sian	    SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN);
682327595Sian	uart_barrier(bas);
683327595Sian
684327595Sian	/* Take over the console input. */
685327595Sian	sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT);
686327595Sian}
687327595Sian
688327595Sianstatic void
689327595Siansbbc_uart_term(struct uart_bas *bas __unused)
690258210Srpaulo{
691258210Srpaulo
692258210Srpaulo	/* Give back the console input. */
693275376Srpaulo	sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP);
694275376Srpaulo}
695258210Srpaulo
696258210Srpaulostatic void
697258210Srpaulosbbc_uart_putc(struct uart_bas *bas, int c)
698258210Srpaulo{
699258210Srpaulo	bus_space_tag_t bst;
700258210Srpaulo	bus_space_handle_t bsh;
701258210Srpaulo	uint32_t wrptr;
702258210Srpaulo
703258210Srpaulo	bst = bas->bst;
704258210Srpaulo	bsh = bas->bsh;
705258210Srpaulo
706258210Srpaulo	wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
707327595Sian	    SBBC_CONS_OFF(cons_out_wrptr));
708258210Srpaulo	SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c);
709277958Srpaulo	uart_barrier(bas);
710258210Srpaulo	if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons +
711258210Srpaulo	    SBBC_CONS_OFF(cons_out_end)))
712258210Srpaulo		wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
713258210Srpaulo		    SBBC_CONS_OFF(cons_out_begin));
714258210Srpaulo	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr),
715258210Srpaulo	    wrptr);
716327595Sian	uart_barrier(bas);
717327595Sian
718258210Srpaulo	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
719258210Srpaulo	    SBBC_SRAM_CONS_OUT);
720258210Srpaulo	uart_barrier(bas);
721327595Sian	sbbc_send_intr(bst, bsh);
722258210Srpaulo}
723327595Sian
724258210Srpaulostatic int
725258210Srpaulosbbc_uart_rxready(struct uart_bas *bas)
726258210Srpaulo{
727258210Srpaulo	bus_space_tag_t bst;
728258210Srpaulo	bus_space_handle_t bsh;
729327595Sian
730258210Srpaulo	bst = bas->bst;
731327595Sian	bsh = bas->bsh;
732327595Sian
733258210Srpaulo	if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) ==
734327595Sian	    SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr)))
735327595Sian		return (0);
736327595Sian	return (1);
737327595Sian}
738327595Sian
739327595Sianstatic int
740327595Siansbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
741327595Sian{
742327595Sian	bus_space_tag_t bst;
743327595Sian	bus_space_handle_t bsh;
744327595Sian	int c;
745327595Sian	uint32_t rdptr;
746327595Sian
747258210Srpaulo	bst = bas->bst;
748258210Srpaulo	bsh = bas->bsh;
749258210Srpaulo
750327595Sian	uart_lock(hwmtx);
751258210Srpaulo
752327595Sian	while (sbbc_uart_rxready(bas) == 0) {
753258210Srpaulo		uart_unlock(hwmtx);
754258210Srpaulo		DELAY(4);
755258210Srpaulo		uart_lock(hwmtx);
756258210Srpaulo	}
757258210Srpaulo
758305572Sgonzo	rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
759258210Srpaulo	c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
760258210Srpaulo	uart_barrier(bas);
761258210Srpaulo	if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons +
762258210Srpaulo	    SBBC_CONS_OFF(cons_in_end)))
763258210Srpaulo		rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
764258210Srpaulo		    SBBC_CONS_OFF(cons_in_begin));
765258210Srpaulo	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr),
766	    rdptr);
767	uart_barrier(bas);
768	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
769	    SBBC_SRAM_CONS_SPACE_IN);
770	uart_barrier(bas);
771	sbbc_send_intr(bst, bsh);
772
773	uart_unlock(hwmtx);
774	return (c);
775}
776
777/*
778 * High-level UART interface
779 */
780static int sbbc_uart_bus_attach(struct uart_softc *sc);
781static int sbbc_uart_bus_detach(struct uart_softc *sc);
782static int sbbc_uart_bus_flush(struct uart_softc *sc, int what);
783static int sbbc_uart_bus_getsig(struct uart_softc *sc);
784static int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request,
785    intptr_t data);
786static int sbbc_uart_bus_ipend(struct uart_softc *sc);
787static int sbbc_uart_bus_param(struct uart_softc *sc, int baudrate,
788    int databits, int stopbits, int parity);
789static int sbbc_uart_bus_probe(struct uart_softc *sc);
790static int sbbc_uart_bus_receive(struct uart_softc *sc);
791static int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig);
792static int sbbc_uart_bus_transmit(struct uart_softc *sc);
793
794static kobj_method_t sbbc_uart_methods[] = {
795	KOBJMETHOD(uart_attach,		sbbc_uart_bus_attach),
796	KOBJMETHOD(uart_detach,		sbbc_uart_bus_detach),
797	KOBJMETHOD(uart_flush,		sbbc_uart_bus_flush),
798	KOBJMETHOD(uart_getsig,		sbbc_uart_bus_getsig),
799	KOBJMETHOD(uart_ioctl,		sbbc_uart_bus_ioctl),
800	KOBJMETHOD(uart_ipend,		sbbc_uart_bus_ipend),
801	KOBJMETHOD(uart_param,		sbbc_uart_bus_param),
802	KOBJMETHOD(uart_probe,		sbbc_uart_bus_probe),
803	KOBJMETHOD(uart_receive,	sbbc_uart_bus_receive),
804	KOBJMETHOD(uart_setsig,		sbbc_uart_bus_setsig),
805	KOBJMETHOD(uart_transmit,	sbbc_uart_bus_transmit),
806
807	DEVMETHOD_END
808};
809
810struct uart_class uart_sbbc_class = {
811	"sbbc",
812	sbbc_uart_methods,
813	sizeof(struct uart_softc),
814	.uc_ops = &sbbc_uart_ops,
815	.uc_range = 1,
816	.uc_rclk = 0x5bbc	/* arbitrary */
817};
818
819#define	SIGCHG(c, i, s, d)						\
820	if ((c) != 0) {							\
821		i |= (((i) & (s)) != 0) ? (s) : (s) | (d);		\
822	} else {							\
823		i = (((i) & (s)) != 0) ? ((i) & ~(s)) | (d) : (i);	\
824	}
825
826static int
827sbbc_uart_bus_attach(struct uart_softc *sc)
828{
829	struct uart_bas *bas;
830	bus_space_tag_t bst;
831	bus_space_handle_t bsh;
832	uint32_t wrptr;
833
834	bas = &sc->sc_bas;
835	bst = bas->bst;
836	bsh = bas->bsh;
837
838	uart_lock(sc->sc_hwmtx);
839
840	/*
841	 * Let the current output drain before enabling interrupts.  Not
842	 * doing so tends to cause lost output when turning them on.
843	 */
844	wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
845	    SBBC_CONS_OFF(cons_out_wrptr));
846	while (SBBC_SRAM_READ_4(sbbc_solcons +
847	    SBBC_CONS_OFF(cons_out_rdptr)) != wrptr);
848		cpu_spinwait();
849
850	/* Clear and acknowledge possibly outstanding interrupts. */
851	SBBC_SRAM_WRITE_4(sbbc_scsolir, 0);
852	uart_barrier(bas);
853	SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS,
854	    SBBC_SRAM_READ_4(sbbc_scsolir));
855	uart_barrier(bas);
856	/* Enable PCI interrupts. */
857	SBBC_REGS_WRITE_4(SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A);
858	uart_barrier(bas);
859	/* Enable input from and output to SC as well as break interrupts. */
860	SBBC_SRAM_WRITE_4(sbbc_scsolie, SBBC_SRAM_READ_4(sbbc_scsolie) |
861	    SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK |
862	    SBBC_SRAM_CONS_SPACE_OUT);
863	uart_barrier(bas);
864
865	uart_unlock(sc->sc_hwmtx);
866	return (0);
867}
868
869static int
870sbbc_uart_bus_detach(struct uart_softc *sc)
871{
872
873	/* Give back the console input. */
874	sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP);
875	return (0);
876}
877
878static int
879sbbc_uart_bus_flush(struct uart_softc *sc, int what)
880{
881	struct uart_bas *bas;
882	bus_space_tag_t bst;
883	bus_space_handle_t bsh;
884
885	bas = &sc->sc_bas;
886	bst = bas->bst;
887	bsh = bas->bsh;
888
889	if ((what & UART_FLUSH_TRANSMITTER) != 0)
890		return (ENODEV);
891	if ((what & UART_FLUSH_RECEIVER) != 0) {
892		SBBC_SRAM_WRITE_4(sbbc_solcons +
893		    SBBC_CONS_OFF(cons_in_rdptr),
894		    SBBC_SRAM_READ_4(sbbc_solcons +
895		    SBBC_CONS_OFF(cons_in_wrptr)));
896		uart_barrier(bas);
897	}
898	return (0);
899}
900
901static int
902sbbc_uart_bus_getsig(struct uart_softc *sc)
903{
904	uint32_t dummy, new, old, sig;
905
906	do {
907		old = sc->sc_hwsig;
908		sig = old;
909		dummy = 0;
910		SIGCHG(dummy, sig, SER_CTS, SER_DCTS);
911		SIGCHG(dummy, sig, SER_DCD, SER_DDCD);
912		SIGCHG(dummy, sig, SER_DSR, SER_DDSR);
913		new = sig & ~SER_MASK_DELTA;
914	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
915	return (sig);
916}
917
918static int
919sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
920{
921	int error;
922
923	error = 0;
924	uart_lock(sc->sc_hwmtx);
925	switch (request) {
926	case UART_IOCTL_BAUD:
927		*(int*)data = 9600;	/* arbitrary */
928		break;
929	default:
930		error = EINVAL;
931		break;
932	}
933	uart_unlock(sc->sc_hwmtx);
934	return (error);
935}
936
937static int
938sbbc_uart_bus_ipend(struct uart_softc *sc)
939{
940	struct uart_bas *bas;
941	bus_space_tag_t bst;
942	bus_space_handle_t bsh;
943	int ipend;
944	uint32_t reason, status;
945
946	bas = &sc->sc_bas;
947	bst = bas->bst;
948	bsh = bas->bsh;
949
950	uart_lock(sc->sc_hwmtx);
951	status = SBBC_REGS_READ_4(SBBC_PCI_INT_STATUS);
952	if (status == 0) {
953		uart_unlock(sc->sc_hwmtx);
954		return (0);
955	}
956
957	/*
958	 * Unfortunately, we can't use compare and swap for non-cachable
959	 * memory.
960	 */
961	reason = SBBC_SRAM_READ_4(sbbc_scsolir);
962	SBBC_SRAM_WRITE_4(sbbc_scsolir, 0);
963	uart_barrier(bas);
964	/* Acknowledge the interrupt. */
965	SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, status);
966	uart_barrier(bas);
967
968	uart_unlock(sc->sc_hwmtx);
969
970	ipend = 0;
971	if ((reason & SBBC_SRAM_CONS_IN) != 0)
972		ipend |= SER_INT_RXREADY;
973	if ((reason & SBBC_SRAM_CONS_BRK) != 0)
974		ipend |= SER_INT_BREAK;
975	if ((reason & SBBC_SRAM_CONS_SPACE_OUT) != 0 &&
976	    SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) ==
977	    SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)))
978		ipend |= SER_INT_TXIDLE;
979	return (ipend);
980}
981
982static int
983sbbc_uart_bus_param(struct uart_softc *sc __unused, int baudrate __unused,
984    int databits __unused, int stopbits __unused, int parity __unused)
985{
986
987	return (0);
988}
989
990static int
991sbbc_uart_bus_probe(struct uart_softc *sc)
992{
993	struct uart_bas *bas;
994	bus_space_tag_t bst;
995	bus_space_handle_t bsh;
996
997	if (sbbc_console != 0) {
998		bas = &sc->sc_bas;
999		bst = bas->bst;
1000		bsh = bas->bsh;
1001		sc->sc_rxfifosz = SBBC_SRAM_READ_4(sbbc_solcons +
1002		    SBBC_CONS_OFF(cons_in_end)) - SBBC_SRAM_READ_4(sbbc_solcons +
1003		    SBBC_CONS_OFF(cons_in_begin)) - 1;
1004		sc->sc_txfifosz = SBBC_SRAM_READ_4(sbbc_solcons +
1005		    SBBC_CONS_OFF(cons_out_end)) - SBBC_SRAM_READ_4(sbbc_solcons +
1006		    SBBC_CONS_OFF(cons_out_begin)) - 1;
1007		return (0);
1008	}
1009	return (ENXIO);
1010}
1011
1012static int
1013sbbc_uart_bus_receive(struct uart_softc *sc)
1014{
1015	struct uart_bas *bas;
1016	bus_space_tag_t bst;
1017	bus_space_handle_t bsh;
1018	int c;
1019	uint32_t end, rdptr, wrptr;
1020
1021	bas = &sc->sc_bas;
1022	bst = bas->bst;
1023	bsh = bas->bsh;
1024
1025	uart_lock(sc->sc_hwmtx);
1026
1027	end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end));
1028	rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
1029	wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr));
1030	while (rdptr != wrptr) {
1031		if (uart_rx_full(sc) != 0) {
1032			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
1033			break;
1034		}
1035		c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
1036		uart_rx_put(sc, c);
1037		if (++rdptr == end)
1038			rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
1039			    SBBC_CONS_OFF(cons_in_begin));
1040	}
1041	uart_barrier(bas);
1042	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr),
1043	    rdptr);
1044	uart_barrier(bas);
1045	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
1046	    SBBC_SRAM_CONS_SPACE_IN);
1047	uart_barrier(bas);
1048	sbbc_send_intr(bst, bsh);
1049
1050	uart_unlock(sc->sc_hwmtx);
1051	return (0);
1052}
1053
1054static int
1055sbbc_uart_bus_setsig(struct uart_softc *sc, int sig)
1056{
1057	struct uart_bas *bas;
1058	uint32_t new, old;
1059
1060	bas = &sc->sc_bas;
1061	do {
1062		old = sc->sc_hwsig;
1063		new = old;
1064		if ((sig & SER_DDTR) != 0) {
1065			SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
1066		}
1067		if ((sig & SER_DRTS) != 0) {
1068			SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
1069		}
1070	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
1071	return (0);
1072}
1073
1074static int
1075sbbc_uart_bus_transmit(struct uart_softc *sc)
1076{
1077	struct uart_bas *bas;
1078	bus_space_tag_t bst;
1079	bus_space_handle_t bsh;
1080	int i;
1081	uint32_t end, wrptr;
1082
1083	bas = &sc->sc_bas;
1084	bst = bas->bst;
1085	bsh = bas->bsh;
1086
1087	uart_lock(sc->sc_hwmtx);
1088
1089	end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end));
1090	wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
1091	    SBBC_CONS_OFF(cons_out_wrptr));
1092	for (i = 0; i < sc->sc_txdatasz; i++) {
1093		SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, sc->sc_txbuf[i]);
1094		if (++wrptr == end)
1095			wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
1096			    SBBC_CONS_OFF(cons_out_begin));
1097	}
1098	uart_barrier(bas);
1099	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr),
1100	    wrptr);
1101	uart_barrier(bas);
1102	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
1103	    SBBC_SRAM_CONS_OUT);
1104	uart_barrier(bas);
1105	sbbc_send_intr(bst, bsh);
1106	sc->sc_txbusy = 1;
1107
1108	uart_unlock(sc->sc_hwmtx);
1109	return (0);
1110}
1111