1206451Smarius/*	$OpenBSD: sbbc.c,v 1.7 2009/11/09 17:53:39 nicm Exp $	*/
2206453Smarius/*-
3206451Smarius * Copyright (c) 2008 Mark Kettenis
4206451Smarius *
5206451Smarius * Permission to use, copy, modify, and distribute this software for any
6206451Smarius * purpose with or without fee is hereby granted, provided that the above
7206451Smarius * copyright notice and this permission notice appear in all copies.
8206451Smarius *
9206451Smarius * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10206451Smarius * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11206451Smarius * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12206451Smarius * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13206451Smarius * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14206451Smarius * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15206451Smarius * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16206451Smarius */
17206451Smarius/*-
18206451Smarius * Copyright (c) 2010 Marius Strobl <marius@FreeBSD.org>
19206451Smarius * All rights reserved.
20206451Smarius *
21206451Smarius * Redistribution and use in source and binary forms, with or without
22206451Smarius * modification, are permitted provided that the following conditions
23206451Smarius * are met:
24206451Smarius * 1. Redistributions of source code must retain the above copyright
25206451Smarius *    notice, this list of conditions and the following disclaimer.
26206451Smarius * 2. Redistributions in binary form must reproduce the above copyright
27206451Smarius *    notice, this list of conditions and the following disclaimer in the
28206451Smarius *    documentation and/or other materials provided with the distribution.
29206451Smarius *
30206451Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
31206451Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32206451Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33206451Smarius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
34206451Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35206451Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36206451Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37206451Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38206451Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39206451Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40206451Smarius * SUCH DAMAGE.
41206451Smarius */
42206451Smarius
43206451Smarius#include <sys/cdefs.h>
44206451Smarius__FBSDID("$FreeBSD: stable/11/sys/sparc64/pci/sbbc.c 340145 2018-11-04 23:28:56Z mmacy $");
45206451Smarius
46206451Smarius#include <sys/param.h>
47206451Smarius#include <sys/systm.h>
48206451Smarius#include <sys/bus.h>
49206451Smarius#include <sys/clock.h>
50206451Smarius#include <sys/endian.h>
51206451Smarius#include <sys/kernel.h>
52206451Smarius#include <sys/lock.h>
53206451Smarius#include <sys/module.h>
54206451Smarius#include <sys/mutex.h>
55206451Smarius#include <sys/resource.h>
56206451Smarius#include <sys/rman.h>
57206451Smarius
58206451Smarius#include <dev/ofw/ofw_bus.h>
59206451Smarius#include <dev/ofw/openfirm.h>
60206451Smarius
61206451Smarius#include <machine/bus.h>
62206451Smarius#include <machine/cpu.h>
63206451Smarius#include <machine/resource.h>
64206451Smarius
65206451Smarius#include <dev/pci/pcireg.h>
66206451Smarius#include <dev/pci/pcivar.h>
67206451Smarius#include <dev/uart/uart.h>
68206451Smarius#include <dev/uart/uart_cpu.h>
69206451Smarius#include <dev/uart/uart_bus.h>
70206451Smarius
71206451Smarius#include "clock_if.h"
72206451Smarius#include "uart_if.h"
73206451Smarius
74206451Smarius#define	SBBC_PCI_BAR		PCIR_BAR(0)
75206451Smarius#define	SBBC_PCI_VENDOR		0x108e
76206451Smarius#define	SBBC_PCI_PRODUCT	0xc416
77206451Smarius
78206451Smarius#define	SBBC_REGS_OFFSET	0x800000
79206451Smarius#define	SBBC_REGS_SIZE		0x6230
80206451Smarius#define	SBBC_EPLD_OFFSET	0x8e0000
81206451Smarius#define	SBBC_EPLD_SIZE		0x20
82206451Smarius#define	SBBC_SRAM_OFFSET	0x900000
83206451Smarius#define	SBBC_SRAM_SIZE		0x20000	/* 128KB SRAM */
84206451Smarius
85206451Smarius#define	SBBC_PCI_INT_STATUS	0x2320
86206451Smarius#define	SBBC_PCI_INT_ENABLE	0x2330
87206451Smarius#define	SBBC_PCI_ENABLE_INT_A	0x11
88206451Smarius
89206451Smarius#define	SBBC_EPLD_INTERRUPT	0x13
90206451Smarius#define	SBBC_EPLD_INTERRUPT_ON	0x01
91206451Smarius
92206451Smarius#define	SBBC_SRAM_CONS_IN		0x00000001
93206451Smarius#define	SBBC_SRAM_CONS_OUT		0x00000002
94206451Smarius#define	SBBC_SRAM_CONS_BRK		0x00000004
95206451Smarius#define	SBBC_SRAM_CONS_SPACE_IN		0x00000008
96206451Smarius#define	SBBC_SRAM_CONS_SPACE_OUT	0x00000010
97206451Smarius
98206451Smarius#define	SBBC_TAG_KEY_SIZE	8
99206451Smarius#define	SBBC_TAG_KEY_SCSOLIE	"SCSOLIE"	/* SC -> OS int. enable */
100206451Smarius#define	SBBC_TAG_KEY_SCSOLIR	"SCSOLIR"	/* SC -> OS int. reason */
101206451Smarius#define	SBBC_TAG_KEY_SOLCONS	"SOLCONS"	/* OS console buffer */
102206451Smarius#define	SBBC_TAG_KEY_SOLSCIE	"SOLSCIE"	/* OS -> SC int. enable */
103206451Smarius#define	SBBC_TAG_KEY_SOLSCIR	"SOLSCIR"	/* OS -> SC int. reason */
104206451Smarius#define	SBBC_TAG_KEY_TODDATA	"TODDATA"	/* OS TOD struct */
105206451Smarius#define	SBBC_TAG_OFF(x)		offsetof(struct sbbc_sram_tag, x)
106206451Smarius
107206451Smariusstruct sbbc_sram_tag {
108206451Smarius	char		tag_key[SBBC_TAG_KEY_SIZE];
109206451Smarius	uint32_t	tag_size;
110206451Smarius	uint32_t	tag_offset;
111206451Smarius} __packed;
112206451Smarius
113206451Smarius#define	SBBC_TOC_MAGIC		"TOCSRAM"
114206451Smarius#define	SBBC_TOC_MAGIC_SIZE	8
115206451Smarius#define	SBBC_TOC_TAGS_MAX	32
116206451Smarius#define	SBBC_TOC_OFF(x)		offsetof(struct sbbc_sram_toc, x)
117206451Smarius
118206451Smariusstruct sbbc_sram_toc {
119206451Smarius	char			toc_magic[SBBC_TOC_MAGIC_SIZE];
120206451Smarius	uint8_t			toc_reserved;
121206451Smarius	uint8_t			toc_type;
122206451Smarius	uint16_t		toc_version;
123206451Smarius	uint32_t		toc_ntags;
124206451Smarius	struct sbbc_sram_tag	toc_tag[SBBC_TOC_TAGS_MAX];
125206451Smarius} __packed;
126206451Smarius
127206451Smarius#define	SBBC_TOD_MAGIC		0x54443100	/* "TD1" */
128206451Smarius#define	SBBC_TOD_VERSION	1
129206451Smarius#define	SBBC_TOD_OFF(x)		offsetof(struct sbbc_sram_tod, x)
130206451Smarius
131206451Smariusstruct sbbc_sram_tod {
132206451Smarius	uint32_t	tod_magic;
133206451Smarius	uint32_t	tod_version;
134206451Smarius	uint64_t	tod_time;
135206451Smarius	uint64_t	tod_skew;
136206451Smarius	uint32_t	tod_reserved;
137206451Smarius	uint32_t	tod_heartbeat;
138206451Smarius	uint32_t	tod_timeout;
139206451Smarius} __packed;
140206451Smarius
141206451Smarius#define	SBBC_CONS_MAGIC		0x434f4e00	/* "CON" */
142206451Smarius#define	SBBC_CONS_VERSION	1
143206451Smarius#define	SBBC_CONS_OFF(x)	offsetof(struct sbbc_sram_cons, x)
144206451Smarius
145206451Smariusstruct sbbc_sram_cons {
146206451Smarius	uint32_t cons_magic;
147206451Smarius	uint32_t cons_version;
148206451Smarius	uint32_t cons_size;
149206451Smarius
150206451Smarius	uint32_t cons_in_begin;
151206451Smarius	uint32_t cons_in_end;
152206451Smarius	uint32_t cons_in_rdptr;
153206451Smarius	uint32_t cons_in_wrptr;
154206451Smarius
155206451Smarius	uint32_t cons_out_begin;
156206451Smarius	uint32_t cons_out_end;
157206451Smarius	uint32_t cons_out_rdptr;
158206451Smarius	uint32_t cons_out_wrptr;
159206451Smarius} __packed;
160206451Smarius
161206451Smariusstruct sbbc_softc {
162206451Smarius	struct resource *sc_res;
163206451Smarius};
164206451Smarius
165206451Smarius#define	SBBC_READ_N(wdth, offs)						\
166206451Smarius	bus_space_read_ ## wdth((bst), (bsh), (offs))
167206451Smarius#define	SBBC_WRITE_N(wdth, offs, val)					\
168206451Smarius	bus_space_write_ ## wdth((bst), (bsh), (offs), (val))
169206451Smarius
170206451Smarius#define	SBBC_READ_1(offs)						\
171206451Smarius	SBBC_READ_N(1, (offs))
172206451Smarius#define	SBBC_READ_2(offs)						\
173206451Smarius	bswap16(SBBC_READ_N(2, (offs)))
174206451Smarius#define	SBBC_READ_4(offs)						\
175206451Smarius	bswap32(SBBC_READ_N(4, (offs)))
176206451Smarius#define	SBBC_READ_8(offs)						\
177206451Smarius	bswap64(SBBC_READ_N(8, (offs)))
178206451Smarius#define	SBBC_WRITE_1(offs, val)						\
179206451Smarius	SBBC_WRITE_N(1, (offs), (val))
180206451Smarius#define	SBBC_WRITE_2(offs, val)						\
181206451Smarius	SBBC_WRITE_N(2, (offs), bswap16(val))
182206451Smarius#define	SBBC_WRITE_4(offs, val)						\
183206451Smarius	SBBC_WRITE_N(4, (offs), bswap32(val))
184206451Smarius#define	SBBC_WRITE_8(offs, val)						\
185206451Smarius	SBBC_WRITE_N(8, (offs), bswap64(val))
186206451Smarius
187206451Smarius#define	SBBC_REGS_READ_1(offs)						\
188206451Smarius	SBBC_READ_1((offs) + SBBC_REGS_OFFSET)
189206451Smarius#define	SBBC_REGS_READ_2(offs)						\
190206451Smarius	SBBC_READ_2((offs) + SBBC_REGS_OFFSET)
191206451Smarius#define	SBBC_REGS_READ_4(offs)						\
192206451Smarius	SBBC_READ_4((offs) + SBBC_REGS_OFFSET)
193206451Smarius#define	SBBC_REGS_READ_8(offs)						\
194206451Smarius	SBBC_READ_8((offs) + SBBC_REGS_OFFSET)
195206451Smarius#define	SBBC_REGS_WRITE_1(offs, val)					\
196206451Smarius	SBBC_WRITE_1((offs) + SBBC_REGS_OFFSET, (val))
197206451Smarius#define	SBBC_REGS_WRITE_2(offs, val)					\
198206451Smarius	SBBC_WRITE_2((offs) + SBBC_REGS_OFFSET, (val))
199206451Smarius#define	SBBC_REGS_WRITE_4(offs, val)					\
200206451Smarius	SBBC_WRITE_4((offs) + SBBC_REGS_OFFSET, (val))
201206451Smarius#define	SBBC_REGS_WRITE_8(offs, val)					\
202206451Smarius	SBBC_WRITE_8((offs) + SBBC_REGS_OFFSET, (val))
203206451Smarius
204206451Smarius#define	SBBC_EPLD_READ_1(offs)						\
205206451Smarius	SBBC_READ_1((offs) + SBBC_EPLD_OFFSET)
206206451Smarius#define	SBBC_EPLD_READ_2(offs)						\
207206451Smarius	SBBC_READ_2((offs) + SBBC_EPLD_OFFSET)
208206451Smarius#define	SBBC_EPLD_READ_4(offs)						\
209206451Smarius	SBBC_READ_4((offs) + SBBC_EPLD_OFFSET)
210206451Smarius#define	SBBC_EPLD_READ_8(offs)						\
211206451Smarius	SBBC_READ_8((offs) + SBBC_EPLD_OFFSET)
212206451Smarius#define	SBBC_EPLD_WRITE_1(offs, val)					\
213206451Smarius	SBBC_WRITE_1((offs) + SBBC_EPLD_OFFSET, (val))
214206451Smarius#define	SBBC_EPLD_WRITE_2(offs, val)					\
215206451Smarius	SBBC_WRITE_2((offs) + SBBC_EPLD_OFFSET, (val))
216206451Smarius#define	SBBC_EPLD_WRITE_4(offs, val)					\
217206451Smarius	SBBC_WRITE_4((offs) + SBBC_EPLD_OFFSET, (val))
218206451Smarius#define	SBBC_EPLD_WRITE_8(offs, val)					\
219206451Smarius	SBBC_WRITE_8((offs) + SBBC_EPLD_OFFSET, (val))
220206451Smarius
221206451Smarius#define	SBBC_SRAM_READ_1(offs)						\
222206451Smarius	SBBC_READ_1((offs) + SBBC_SRAM_OFFSET)
223206451Smarius#define	SBBC_SRAM_READ_2(offs)						\
224206451Smarius	SBBC_READ_2((offs) + SBBC_SRAM_OFFSET)
225206451Smarius#define	SBBC_SRAM_READ_4(offs)						\
226206451Smarius	SBBC_READ_4((offs) + SBBC_SRAM_OFFSET)
227206451Smarius#define	SBBC_SRAM_READ_8(offs)						\
228206451Smarius	SBBC_READ_8((offs) + SBBC_SRAM_OFFSET)
229206451Smarius#define	SBBC_SRAM_WRITE_1(offs, val)					\
230206451Smarius	SBBC_WRITE_1((offs) + SBBC_SRAM_OFFSET, (val))
231206451Smarius#define	SBBC_SRAM_WRITE_2(offs, val)					\
232206451Smarius	SBBC_WRITE_2((offs) + SBBC_SRAM_OFFSET, (val))
233206451Smarius#define	SBBC_SRAM_WRITE_4(offs, val)					\
234206451Smarius	SBBC_WRITE_4((offs) + SBBC_SRAM_OFFSET, (val))
235206451Smarius#define	SBBC_SRAM_WRITE_8(offs, val)					\
236206451Smarius	SBBC_WRITE_8((offs) + SBBC_SRAM_OFFSET, (val))
237206451Smarius
238206451Smarius#define	SUNW_SETCONSINPUT	"SUNW,set-console-input"
239206451Smarius#define	SUNW_SETCONSINPUT_CLNT	"CON_CLNT"
240206451Smarius#define	SUNW_SETCONSINPUT_OBP	"CON_OBP"
241206451Smarius
242206451Smariusstatic u_int sbbc_console;
243206451Smarius
244206451Smariusstatic uint32_t	sbbc_scsolie;
245206451Smariusstatic uint32_t	sbbc_scsolir;
246206451Smariusstatic uint32_t	sbbc_solcons;
247206451Smariusstatic uint32_t	sbbc_solscie;
248206451Smariusstatic uint32_t	sbbc_solscir;
249206451Smariusstatic uint32_t	sbbc_toddata;
250206451Smarius
251206451Smarius/*
252206451Smarius * internal helpers
253206451Smarius */
254206451Smariusstatic int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh);
255206451Smariusstatic inline void sbbc_send_intr(bus_space_tag_t bst,
256206451Smarius    bus_space_handle_t bsh);
257206451Smariusstatic const char *sbbc_serengeti_set_console_input(char *new);
258206451Smarius
259206451Smarius/*
260206451Smarius * SBBC PCI interface
261206451Smarius */
262225931Smariusstatic bus_activate_resource_t sbbc_bus_activate_resource;
263225931Smariusstatic bus_adjust_resource_t sbbc_bus_adjust_resource;
264225931Smariusstatic bus_deactivate_resource_t sbbc_bus_deactivate_resource;
265206451Smariusstatic bus_alloc_resource_t sbbc_bus_alloc_resource;
266206451Smariusstatic bus_release_resource_t sbbc_bus_release_resource;
267206451Smariusstatic bus_get_resource_list_t sbbc_bus_get_resource_list;
268206451Smariusstatic bus_setup_intr_t sbbc_bus_setup_intr;
269206451Smariusstatic bus_teardown_intr_t sbbc_bus_teardown_intr;
270206451Smarius
271206451Smariusstatic device_attach_t sbbc_pci_attach;
272206451Smariusstatic device_probe_t sbbc_pci_probe;
273206451Smarius
274206451Smariusstatic clock_gettime_t sbbc_tod_gettime;
275206451Smariusstatic clock_settime_t sbbc_tod_settime;
276206451Smarius
277206451Smariusstatic device_method_t sbbc_pci_methods[] = {
278206451Smarius	/* Device interface */
279206451Smarius	DEVMETHOD(device_probe,		sbbc_pci_probe),
280206451Smarius	DEVMETHOD(device_attach,	sbbc_pci_attach),
281206451Smarius
282206451Smarius	DEVMETHOD(bus_alloc_resource,	sbbc_bus_alloc_resource),
283225931Smarius	DEVMETHOD(bus_activate_resource,sbbc_bus_activate_resource),
284225931Smarius	DEVMETHOD(bus_deactivate_resource,sbbc_bus_deactivate_resource),
285225931Smarius	DEVMETHOD(bus_adjust_resource,	sbbc_bus_adjust_resource),
286206451Smarius	DEVMETHOD(bus_release_resource,	sbbc_bus_release_resource),
287206451Smarius	DEVMETHOD(bus_setup_intr,	sbbc_bus_setup_intr),
288206451Smarius	DEVMETHOD(bus_teardown_intr,	sbbc_bus_teardown_intr),
289225931Smarius	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
290206451Smarius	DEVMETHOD(bus_get_resource_list, sbbc_bus_get_resource_list),
291206451Smarius
292206451Smarius	/* clock interface */
293206451Smarius	DEVMETHOD(clock_gettime,	sbbc_tod_gettime),
294206451Smarius	DEVMETHOD(clock_settime,	sbbc_tod_settime),
295206451Smarius
296227843Smarius	DEVMETHOD_END
297206451Smarius};
298206451Smarius
299206451Smariusstatic devclass_t sbbc_devclass;
300206451Smarius
301206451SmariusDEFINE_CLASS_0(sbbc, sbbc_driver, sbbc_pci_methods, sizeof(struct sbbc_softc));
302247574SmariusDRIVER_MODULE(sbbc, pci, sbbc_driver, sbbc_devclass, NULL, NULL);
303206451Smarius
304206451Smariusstatic int
305206451Smariussbbc_pci_probe(device_t dev)
306206451Smarius{
307206451Smarius
308206451Smarius	if (pci_get_vendor(dev) == SBBC_PCI_VENDOR &&
309206451Smarius	    pci_get_device(dev) == SBBC_PCI_PRODUCT) {
310206451Smarius		device_set_desc(dev, "Sun BootBus controller");
311206451Smarius		return (BUS_PROBE_DEFAULT);
312206451Smarius	}
313206451Smarius	return (ENXIO);
314206451Smarius}
315206451Smarius
316206451Smariusstatic int
317206451Smariussbbc_pci_attach(device_t dev)
318206451Smarius{
319206451Smarius	struct sbbc_softc *sc;
320206451Smarius	struct timespec ts;
321206451Smarius	device_t child;
322206451Smarius	bus_space_tag_t bst;
323206451Smarius	bus_space_handle_t bsh;
324206451Smarius	phandle_t node;
325206451Smarius	int error, rid;
326206451Smarius	uint32_t val;
327206451Smarius
328330446Seadler	/* Nothing to do if we're not the chosen one. */
329206451Smarius	if ((node = OF_finddevice("/chosen")) == -1) {
330206451Smarius		device_printf(dev, "failed to find /chosen\n");
331206451Smarius		return (ENXIO);
332206451Smarius	}
333206451Smarius	if (OF_getprop(node, "iosram", &node, sizeof(node)) == -1) {
334206451Smarius		device_printf(dev, "failed to get iosram\n");
335206451Smarius		return (ENXIO);
336206451Smarius	}
337206451Smarius	if (node != ofw_bus_get_node(dev))
338206451Smarius		return (0);
339206451Smarius
340206451Smarius	sc = device_get_softc(dev);
341206451Smarius	rid = SBBC_PCI_BAR;
342225931Smarius	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
343225931Smarius	    RF_ACTIVE);
344206451Smarius	if (sc->sc_res == NULL) {
345206451Smarius		device_printf(dev, "failed to allocate resources\n");
346206451Smarius		return (ENXIO);
347206451Smarius	}
348206451Smarius	bst = rman_get_bustag(sc->sc_res);
349206451Smarius	bsh = rman_get_bushandle(sc->sc_res);
350206451Smarius	if (sbbc_console != 0) {
351206451Smarius		/* Once again the interrupt pin isn't set. */
352206451Smarius		if (pci_get_intpin(dev) == 0)
353206451Smarius			pci_set_intpin(dev, 1);
354206451Smarius		child = device_add_child(dev, NULL, -1);
355206451Smarius		if (child == NULL)
356206451Smarius			device_printf(dev, "failed to add UART device\n");
357206451Smarius		error = bus_generic_attach(dev);
358206451Smarius		if (error != 0)
359206451Smarius			device_printf(dev, "failed to attach UART device\n");
360206451Smarius	} else {
361247574Smarius		error = sbbc_parse_toc(bst, bsh);
362206451Smarius		if (error != 0) {
363206451Smarius			device_printf(dev, "failed to parse TOC\n");
364206451Smarius			if (sbbc_console != 0) {
365206451Smarius				bus_release_resource(dev, SYS_RES_MEMORY, rid,
366206451Smarius				    sc->sc_res);
367206451Smarius				return (error);
368206451Smarius			}
369206451Smarius		}
370206451Smarius	}
371206451Smarius	if (sbbc_toddata != 0) {
372206451Smarius		if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
373206451Smarius		    SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC)
374206451Smarius			device_printf(dev, "invalid TOD magic %#x\n", val);
375206451Smarius		else if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
376206451Smarius		    SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION)
377206451Smarius			device_printf(dev, "invalid TOD version %#x\n", val);
378206451Smarius		else {
379206451Smarius			clock_register(dev, 1000000); /* 1 sec. resolution */
380206451Smarius			if (bootverbose) {
381206451Smarius				sbbc_tod_gettime(dev, &ts);
382206451Smarius				device_printf(dev,
383206451Smarius				    "current time: %ld.%09ld\n",
384206451Smarius				    (long)ts.tv_sec, ts.tv_nsec);
385206451Smarius			}
386206451Smarius		}
387206451Smarius	}
388206451Smarius	return (0);
389206451Smarius}
390206451Smarius
391206451Smarius/*
392206451Smarius * Note that the bus methods don't pass-through the uart(4) requests but act
393206451Smarius * as if they would come from sbbc(4) in order to avoid complications with
394206451Smarius * pci(4) (actually, uart(4) isn't a real child but rather a function of
395206451Smarius * sbbc(4) anyway).
396206451Smarius */
397206451Smarius
398206451Smariusstatic struct resource *
399206451Smariussbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type,
400294883Sjhibbits    int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
401206451Smarius{
402206451Smarius	struct sbbc_softc *sc;
403206451Smarius
404206451Smarius	sc = device_get_softc(dev);
405206451Smarius	switch (type) {
406206451Smarius	case SYS_RES_IRQ:
407225931Smarius		return (bus_generic_alloc_resource(dev, dev, type, rid, start,
408225931Smarius		    end, count, flags));
409206451Smarius	case SYS_RES_MEMORY:
410206451Smarius		return (sc->sc_res);
411206451Smarius	default:
412206451Smarius		return (NULL);
413206451Smarius	}
414206451Smarius}
415206451Smarius
416206451Smariusstatic int
417225931Smariussbbc_bus_activate_resource(device_t bus, device_t child, int type, int rid,
418225931Smarius    struct resource *res)
419225931Smarius{
420225931Smarius
421225931Smarius	if (type == SYS_RES_MEMORY)
422225931Smarius		return (0);
423225931Smarius	return (bus_generic_activate_resource(bus, child, type, rid, res));
424225931Smarius}
425225931Smarius
426225931Smariusstatic int
427225931Smariussbbc_bus_deactivate_resource(device_t bus, device_t child, int type, int rid,
428225931Smarius    struct resource *res)
429225931Smarius{
430225931Smarius
431225931Smarius	if (type == SYS_RES_MEMORY)
432225931Smarius		return (0);
433225931Smarius	return (bus_generic_deactivate_resource(bus, child, type, rid, res));
434225931Smarius}
435225931Smarius
436225931Smariusstatic int
437225931Smariussbbc_bus_adjust_resource(device_t bus __unused, device_t child __unused,
438294883Sjhibbits    int type __unused, struct resource *res __unused, rman_res_t start __unused,
439294883Sjhibbits    rman_res_t end __unused)
440225931Smarius{
441225931Smarius
442225931Smarius	return (ENXIO);
443225931Smarius}
444225931Smarius
445225931Smariusstatic int
446206451Smariussbbc_bus_release_resource(device_t dev, device_t child __unused, int type,
447206451Smarius    int rid, struct resource *res)
448206451Smarius{
449206451Smarius
450206451Smarius	if (type == SYS_RES_IRQ)
451225931Smarius		return (bus_generic_release_resource(dev, dev, type, rid,
452225931Smarius		    res));
453206451Smarius	return (0);
454206451Smarius}
455206451Smarius
456206451Smariusstatic struct resource_list *
457206451Smariussbbc_bus_get_resource_list(device_t dev, device_t child __unused)
458206451Smarius{
459206451Smarius
460225931Smarius	return (bus_generic_get_resource_list(dev, dev));
461206451Smarius}
462206451Smarius
463206451Smariusstatic int
464206451Smariussbbc_bus_setup_intr(device_t dev, device_t child __unused,
465206451Smarius    struct resource *res, int flags, driver_filter_t *filt,
466206451Smarius    driver_intr_t *intr, void *arg, void **cookiep)
467206451Smarius{
468206451Smarius
469225931Smarius	return (bus_generic_setup_intr(dev, dev, res, flags, filt, intr, arg,
470225931Smarius	    cookiep));
471206451Smarius}
472206451Smarius
473206451Smariusstatic int
474206451Smariussbbc_bus_teardown_intr(device_t dev, device_t child __unused,
475206451Smarius    struct resource *res, void *cookie)
476206451Smarius{
477206451Smarius
478225931Smarius	return (bus_generic_teardown_intr(dev, dev, res, cookie));
479206451Smarius}
480206451Smarius
481206451Smarius/*
482206451Smarius * internal helpers
483206451Smarius */
484206451Smariusstatic int
485206451Smariussbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh)
486206451Smarius{
487206451Smarius	char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)];
488206451Smarius	bus_size_t tag;
489206451Smarius	phandle_t node;
490206451Smarius	uint32_t off, sram_toc;
491206451Smarius	u_int i, tags;
492206451Smarius
493206451Smarius	if ((node = OF_finddevice("/chosen")) == -1)
494206451Smarius		return (ENXIO);
495206451Smarius	/* SRAM TOC offset defaults to 0. */
496206451Smarius	if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0)
497206451Smarius		sram_toc = 0;
498206451Smarius
499206451Smarius	bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc +
500206451Smarius	    SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE);
501206451Smarius	buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0';
502206451Smarius	if (strcmp(buf, SBBC_TOC_MAGIC) != 0)
503206451Smarius		return (ENXIO);
504206451Smarius
505206451Smarius	tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags));
506206451Smarius	for (i = 0; i < tags; i++) {
507206451Smarius		tag = sram_toc + SBBC_TOC_OFF(toc_tag) +
508206451Smarius		    i * sizeof(struct sbbc_sram_tag);
509206451Smarius		bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag +
510206451Smarius		    SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE);
511206451Smarius		buf[SBBC_TAG_KEY_SIZE - 1] = '\0';
512206451Smarius		off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset));
513206451Smarius		if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0)
514206451Smarius			sbbc_scsolie = off;
515206451Smarius		else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0)
516206451Smarius			sbbc_scsolir = off;
517206451Smarius		else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0)
518206451Smarius			sbbc_solcons = off;
519206451Smarius		else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0)
520206451Smarius			sbbc_solscie = off;
521206451Smarius		else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0)
522206451Smarius			sbbc_solscir = off;
523206451Smarius		else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0)
524206451Smarius			sbbc_toddata = off;
525206451Smarius	}
526206451Smarius	return (0);
527206451Smarius}
528206451Smarius
529206451Smariusstatic const char *
530206451Smariussbbc_serengeti_set_console_input(char *new)
531206451Smarius{
532206451Smarius	struct {
533206451Smarius		cell_t name;
534206451Smarius		cell_t nargs;
535206451Smarius		cell_t nreturns;
536206451Smarius		cell_t new;
537206451Smarius		cell_t old;
538206451Smarius	} args = {
539206451Smarius		(cell_t)SUNW_SETCONSINPUT,
540206451Smarius		1,
541206451Smarius		1,
542206451Smarius	};
543206451Smarius
544206451Smarius	args.new = (cell_t)new;
545206451Smarius	if (ofw_entry(&args) == -1)
546206451Smarius		return (NULL);
547206451Smarius	return ((const char *)args.old);
548206451Smarius}
549206451Smarius
550206451Smariusstatic inline void
551206451Smariussbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh)
552206451Smarius{
553206451Smarius
554206451Smarius	SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON);
555206451Smarius	bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1,
556206451Smarius	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
557206451Smarius}
558206451Smarius
559206451Smarius/*
560206451Smarius * TOD interface
561206451Smarius */
562206451Smariusstatic int
563206451Smariussbbc_tod_gettime(device_t dev, struct timespec *ts)
564206451Smarius{
565206451Smarius	struct sbbc_softc *sc;
566206451Smarius	bus_space_tag_t bst;
567206451Smarius	bus_space_handle_t bsh;
568206451Smarius
569206451Smarius	sc = device_get_softc(dev);
570206451Smarius	bst = rman_get_bustag(sc->sc_res);
571206451Smarius	bsh = rman_get_bushandle(sc->sc_res);
572206451Smarius
573206451Smarius	ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) +
574206451Smarius	    SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew));
575206451Smarius	ts->tv_nsec = 0;
576206451Smarius	return (0);
577206451Smarius}
578206451Smarius
579206451Smariusstatic int
580206451Smariussbbc_tod_settime(device_t dev, struct timespec *ts)
581206451Smarius{
582206451Smarius	struct sbbc_softc *sc;
583206451Smarius	bus_space_tag_t bst;
584206451Smarius	bus_space_handle_t bsh;
585206451Smarius
586206451Smarius	sc = device_get_softc(dev);
587206451Smarius	bst = rman_get_bustag(sc->sc_res);
588206451Smarius	bsh = rman_get_bushandle(sc->sc_res);
589206451Smarius
590206451Smarius	SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec -
591206451Smarius	    SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)));
592206451Smarius	return (0);
593206451Smarius}
594206451Smarius
595206451Smarius/*
596206451Smarius * UART bus front-end
597206451Smarius */
598206451Smariusstatic device_probe_t sbbc_uart_sbbc_probe;
599206451Smarius
600206451Smariusstatic device_method_t sbbc_uart_sbbc_methods[] = {
601206451Smarius	/* Device interface */
602206451Smarius	DEVMETHOD(device_probe,		sbbc_uart_sbbc_probe),
603206451Smarius	DEVMETHOD(device_attach,	uart_bus_attach),
604206451Smarius	DEVMETHOD(device_detach,	uart_bus_detach),
605206451Smarius
606227843Smarius	DEVMETHOD_END
607206451Smarius};
608206451Smarius
609206451SmariusDEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods,
610206451Smarius    sizeof(struct uart_softc));
611247574SmariusDRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, NULL, NULL);
612206451Smarius
613206451Smariusstatic int
614206451Smariussbbc_uart_sbbc_probe(device_t dev)
615206451Smarius{
616206451Smarius	struct uart_softc *sc;
617206451Smarius
618206451Smarius	sc = device_get_softc(dev);
619206451Smarius	sc->sc_class = &uart_sbbc_class;
620206451Smarius	device_set_desc(dev, "Serengeti console");
621340145Smmacy	return (uart_bus_probe(dev, 0, 0, 0, SBBC_PCI_BAR, 0, 0));
622206451Smarius}
623206451Smarius
624206451Smarius/*
625206451Smarius * Low-level UART interface
626206451Smarius */
627206451Smariusstatic int sbbc_uart_probe(struct uart_bas *bas);
628206451Smariusstatic void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits,
629206451Smarius    int stopbits, int parity);
630206451Smariusstatic void sbbc_uart_term(struct uart_bas *bas);
631206451Smariusstatic void sbbc_uart_putc(struct uart_bas *bas, int c);
632206451Smariusstatic int sbbc_uart_rxready(struct uart_bas *bas);
633206451Smariusstatic int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx);
634206451Smarius
635206451Smariusstatic struct uart_ops sbbc_uart_ops = {
636206451Smarius	.probe = sbbc_uart_probe,
637206451Smarius	.init = sbbc_uart_init,
638206451Smarius	.term = sbbc_uart_term,
639206451Smarius	.putc = sbbc_uart_putc,
640206451Smarius	.rxready = sbbc_uart_rxready,
641206451Smarius	.getc = sbbc_uart_getc,
642206451Smarius};
643206451Smarius
644206451Smariusstatic int
645206451Smariussbbc_uart_probe(struct uart_bas *bas)
646206451Smarius{
647206451Smarius	bus_space_tag_t bst;
648206451Smarius	bus_space_handle_t bsh;
649206451Smarius	int error;
650206451Smarius
651206451Smarius	sbbc_console = 1;
652206451Smarius	bst = bas->bst;
653206451Smarius	bsh = bas->bsh;
654206451Smarius	error = sbbc_parse_toc(bst, bsh);
655206451Smarius	if (error != 0)
656206451Smarius		return (error);
657206451Smarius
658206451Smarius	if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 ||
659206451Smarius	    sbbc_solscie == 0 || sbbc_solscir == 0)
660206451Smarius		return (ENXIO);
661206451Smarius
662206451Smarius	if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) !=
663206451Smarius	    SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons +
664206451Smarius	    SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION)
665206451Smarius		return (ENXIO);
666206451Smarius	return (0);
667206451Smarius}
668206451Smarius
669206451Smariusstatic void
670206451Smariussbbc_uart_init(struct uart_bas *bas, int baudrate __unused,
671206451Smarius    int databits __unused, int stopbits __unused, int parity __unused)
672206451Smarius{
673206451Smarius	bus_space_tag_t bst;
674206451Smarius	bus_space_handle_t bsh;
675206451Smarius
676206451Smarius	bst = bas->bst;
677206451Smarius	bsh = bas->bsh;
678206451Smarius
679206451Smarius	/* Enable output to and space in from the SC interrupts. */
680206451Smarius	SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) |
681206451Smarius	    SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN);
682206451Smarius	uart_barrier(bas);
683206451Smarius
684206451Smarius	/* Take over the console input. */
685206451Smarius	sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT);
686206451Smarius}
687206451Smarius
688206451Smariusstatic void
689206451Smariussbbc_uart_term(struct uart_bas *bas __unused)
690206451Smarius{
691206451Smarius
692206451Smarius	/* Give back the console input. */
693206451Smarius	sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP);
694206451Smarius}
695206451Smarius
696206451Smariusstatic void
697206451Smariussbbc_uart_putc(struct uart_bas *bas, int c)
698206451Smarius{
699206451Smarius	bus_space_tag_t bst;
700206451Smarius	bus_space_handle_t bsh;
701206451Smarius	uint32_t wrptr;
702206451Smarius
703206451Smarius	bst = bas->bst;
704206451Smarius	bsh = bas->bsh;
705206451Smarius
706206451Smarius	wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
707206451Smarius	    SBBC_CONS_OFF(cons_out_wrptr));
708206451Smarius	SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c);
709206451Smarius	uart_barrier(bas);
710206451Smarius	if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons +
711206451Smarius	    SBBC_CONS_OFF(cons_out_end)))
712206451Smarius		wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
713206451Smarius		    SBBC_CONS_OFF(cons_out_begin));
714206451Smarius	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr),
715206451Smarius	    wrptr);
716206451Smarius	uart_barrier(bas);
717206451Smarius
718206451Smarius	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
719206451Smarius	    SBBC_SRAM_CONS_OUT);
720206451Smarius	uart_barrier(bas);
721206451Smarius	sbbc_send_intr(bst, bsh);
722206451Smarius}
723206451Smarius
724206451Smariusstatic int
725206451Smariussbbc_uart_rxready(struct uart_bas *bas)
726206451Smarius{
727206451Smarius	bus_space_tag_t bst;
728206451Smarius	bus_space_handle_t bsh;
729206451Smarius
730206451Smarius	bst = bas->bst;
731206451Smarius	bsh = bas->bsh;
732206451Smarius
733206451Smarius	if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) ==
734206451Smarius	    SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr)))
735206451Smarius		return (0);
736206451Smarius	return (1);
737206451Smarius}
738206451Smarius
739206451Smariusstatic int
740206451Smariussbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
741206451Smarius{
742206451Smarius	bus_space_tag_t bst;
743206451Smarius	bus_space_handle_t bsh;
744206451Smarius	int c;
745206451Smarius	uint32_t rdptr;
746206451Smarius
747206451Smarius	bst = bas->bst;
748206451Smarius	bsh = bas->bsh;
749206451Smarius
750206451Smarius	uart_lock(hwmtx);
751206451Smarius
752206451Smarius	while (sbbc_uart_rxready(bas) == 0) {
753206451Smarius		uart_unlock(hwmtx);
754206451Smarius		DELAY(4);
755206451Smarius		uart_lock(hwmtx);
756206451Smarius	}
757206451Smarius
758206451Smarius	rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
759206451Smarius	c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
760206451Smarius	uart_barrier(bas);
761206451Smarius	if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons +
762206451Smarius	    SBBC_CONS_OFF(cons_in_end)))
763206451Smarius		rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
764206451Smarius		    SBBC_CONS_OFF(cons_in_begin));
765206451Smarius	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr),
766206451Smarius	    rdptr);
767206451Smarius	uart_barrier(bas);
768206451Smarius	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
769206451Smarius	    SBBC_SRAM_CONS_SPACE_IN);
770206451Smarius	uart_barrier(bas);
771206451Smarius	sbbc_send_intr(bst, bsh);
772206451Smarius
773206451Smarius	uart_unlock(hwmtx);
774206451Smarius	return (c);
775206451Smarius}
776206451Smarius
777206451Smarius/*
778206451Smarius * High-level UART interface
779206451Smarius */
780206451Smariusstatic int sbbc_uart_bus_attach(struct uart_softc *sc);
781206451Smariusstatic int sbbc_uart_bus_detach(struct uart_softc *sc);
782206451Smariusstatic int sbbc_uart_bus_flush(struct uart_softc *sc, int what);
783206451Smariusstatic int sbbc_uart_bus_getsig(struct uart_softc *sc);
784206451Smariusstatic int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request,
785206451Smarius    intptr_t data);
786206451Smariusstatic int sbbc_uart_bus_ipend(struct uart_softc *sc);
787206451Smariusstatic int sbbc_uart_bus_param(struct uart_softc *sc, int baudrate,
788206451Smarius    int databits, int stopbits, int parity);
789206451Smariusstatic int sbbc_uart_bus_probe(struct uart_softc *sc);
790206451Smariusstatic int sbbc_uart_bus_receive(struct uart_softc *sc);
791206451Smariusstatic int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig);
792206451Smariusstatic int sbbc_uart_bus_transmit(struct uart_softc *sc);
793206451Smarius
794206451Smariusstatic kobj_method_t sbbc_uart_methods[] = {
795206451Smarius	KOBJMETHOD(uart_attach,		sbbc_uart_bus_attach),
796206451Smarius	KOBJMETHOD(uart_detach,		sbbc_uart_bus_detach),
797206451Smarius	KOBJMETHOD(uart_flush,		sbbc_uart_bus_flush),
798206451Smarius	KOBJMETHOD(uart_getsig,		sbbc_uart_bus_getsig),
799206451Smarius	KOBJMETHOD(uart_ioctl,		sbbc_uart_bus_ioctl),
800206451Smarius	KOBJMETHOD(uart_ipend,		sbbc_uart_bus_ipend),
801206451Smarius	KOBJMETHOD(uart_param,		sbbc_uart_bus_param),
802206451Smarius	KOBJMETHOD(uart_probe,		sbbc_uart_bus_probe),
803206451Smarius	KOBJMETHOD(uart_receive,	sbbc_uart_bus_receive),
804206451Smarius	KOBJMETHOD(uart_setsig,		sbbc_uart_bus_setsig),
805206451Smarius	KOBJMETHOD(uart_transmit,	sbbc_uart_bus_transmit),
806206451Smarius
807227843Smarius	DEVMETHOD_END
808206451Smarius};
809206451Smarius
810206451Smariusstruct uart_class uart_sbbc_class = {
811206451Smarius	"sbbc",
812206451Smarius	sbbc_uart_methods,
813206451Smarius	sizeof(struct uart_softc),
814206451Smarius	.uc_ops = &sbbc_uart_ops,
815206451Smarius	.uc_range = 1,
816281438Sandrew	.uc_rclk = 0x5bbc,	/* arbitrary */
817281438Sandrew	.uc_rshift = 0
818206451Smarius};
819206451Smarius
820206451Smarius#define	SIGCHG(c, i, s, d)						\
821206451Smarius	if ((c) != 0) {							\
822206451Smarius		i |= (((i) & (s)) != 0) ? (s) : (s) | (d);		\
823206451Smarius	} else {							\
824206451Smarius		i = (((i) & (s)) != 0) ? ((i) & ~(s)) | (d) : (i);	\
825206451Smarius	}
826206451Smarius
827206451Smariusstatic int
828206451Smariussbbc_uart_bus_attach(struct uart_softc *sc)
829206451Smarius{
830206451Smarius	struct uart_bas *bas;
831206451Smarius	bus_space_tag_t bst;
832206451Smarius	bus_space_handle_t bsh;
833206451Smarius	uint32_t wrptr;
834206451Smarius
835206451Smarius	bas = &sc->sc_bas;
836206451Smarius	bst = bas->bst;
837206451Smarius	bsh = bas->bsh;
838206451Smarius
839206451Smarius	uart_lock(sc->sc_hwmtx);
840206451Smarius
841206451Smarius	/*
842206451Smarius	 * Let the current output drain before enabling interrupts.  Not
843206451Smarius	 * doing so tends to cause lost output when turning them on.
844206451Smarius	 */
845206451Smarius	wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
846206451Smarius	    SBBC_CONS_OFF(cons_out_wrptr));
847206451Smarius	while (SBBC_SRAM_READ_4(sbbc_solcons +
848206451Smarius	    SBBC_CONS_OFF(cons_out_rdptr)) != wrptr);
849206451Smarius		cpu_spinwait();
850206451Smarius
851206451Smarius	/* Clear and acknowledge possibly outstanding interrupts. */
852206451Smarius	SBBC_SRAM_WRITE_4(sbbc_scsolir, 0);
853206451Smarius	uart_barrier(bas);
854206451Smarius	SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS,
855206451Smarius	    SBBC_SRAM_READ_4(sbbc_scsolir));
856206451Smarius	uart_barrier(bas);
857206451Smarius	/* Enable PCI interrupts. */
858206451Smarius	SBBC_REGS_WRITE_4(SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A);
859206451Smarius	uart_barrier(bas);
860206451Smarius	/* Enable input from and output to SC as well as break interrupts. */
861206451Smarius	SBBC_SRAM_WRITE_4(sbbc_scsolie, SBBC_SRAM_READ_4(sbbc_scsolie) |
862206451Smarius	    SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK |
863206451Smarius	    SBBC_SRAM_CONS_SPACE_OUT);
864206451Smarius	uart_barrier(bas);
865206451Smarius
866206451Smarius	uart_unlock(sc->sc_hwmtx);
867206451Smarius	return (0);
868206451Smarius}
869206451Smarius
870206451Smariusstatic int
871206451Smariussbbc_uart_bus_detach(struct uart_softc *sc)
872206451Smarius{
873206451Smarius
874206451Smarius	/* Give back the console input. */
875206451Smarius	sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP);
876206451Smarius	return (0);
877206451Smarius}
878206451Smarius
879206451Smariusstatic int
880206451Smariussbbc_uart_bus_flush(struct uart_softc *sc, int what)
881206451Smarius{
882206451Smarius	struct uart_bas *bas;
883206451Smarius	bus_space_tag_t bst;
884206451Smarius	bus_space_handle_t bsh;
885206451Smarius
886206451Smarius	bas = &sc->sc_bas;
887206451Smarius	bst = bas->bst;
888206451Smarius	bsh = bas->bsh;
889206451Smarius
890206451Smarius	if ((what & UART_FLUSH_TRANSMITTER) != 0)
891206451Smarius		return (ENODEV);
892206451Smarius	if ((what & UART_FLUSH_RECEIVER) != 0) {
893206451Smarius		SBBC_SRAM_WRITE_4(sbbc_solcons +
894206451Smarius		    SBBC_CONS_OFF(cons_in_rdptr),
895206451Smarius		    SBBC_SRAM_READ_4(sbbc_solcons +
896206451Smarius		    SBBC_CONS_OFF(cons_in_wrptr)));
897206451Smarius		uart_barrier(bas);
898206451Smarius	}
899206451Smarius	return (0);
900206451Smarius}
901206451Smarius
902206451Smariusstatic int
903206451Smariussbbc_uart_bus_getsig(struct uart_softc *sc)
904206451Smarius{
905206451Smarius	uint32_t dummy, new, old, sig;
906206451Smarius
907206451Smarius	do {
908206451Smarius		old = sc->sc_hwsig;
909206451Smarius		sig = old;
910206451Smarius		dummy = 0;
911206451Smarius		SIGCHG(dummy, sig, SER_CTS, SER_DCTS);
912206451Smarius		SIGCHG(dummy, sig, SER_DCD, SER_DDCD);
913206451Smarius		SIGCHG(dummy, sig, SER_DSR, SER_DDSR);
914206451Smarius		new = sig & ~SER_MASK_DELTA;
915206451Smarius	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
916206451Smarius	return (sig);
917206451Smarius}
918206451Smarius
919206451Smariusstatic int
920206451Smariussbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
921206451Smarius{
922206451Smarius	int error;
923206451Smarius
924206451Smarius	error = 0;
925206451Smarius	uart_lock(sc->sc_hwmtx);
926206451Smarius	switch (request) {
927206451Smarius	case UART_IOCTL_BAUD:
928206451Smarius		*(int*)data = 9600;	/* arbitrary */
929206451Smarius		break;
930206451Smarius	default:
931206451Smarius		error = EINVAL;
932206451Smarius		break;
933206451Smarius	}
934206451Smarius	uart_unlock(sc->sc_hwmtx);
935206451Smarius	return (error);
936206451Smarius}
937206451Smarius
938206451Smariusstatic int
939206451Smariussbbc_uart_bus_ipend(struct uart_softc *sc)
940206451Smarius{
941206451Smarius	struct uart_bas *bas;
942206451Smarius	bus_space_tag_t bst;
943206451Smarius	bus_space_handle_t bsh;
944206451Smarius	int ipend;
945206451Smarius	uint32_t reason, status;
946206451Smarius
947206451Smarius	bas = &sc->sc_bas;
948206451Smarius	bst = bas->bst;
949206451Smarius	bsh = bas->bsh;
950206451Smarius
951206451Smarius	uart_lock(sc->sc_hwmtx);
952206451Smarius	status = SBBC_REGS_READ_4(SBBC_PCI_INT_STATUS);
953206451Smarius	if (status == 0) {
954206451Smarius		uart_unlock(sc->sc_hwmtx);
955206451Smarius		return (0);
956206451Smarius	}
957206451Smarius
958206451Smarius	/*
959206451Smarius	 * Unfortunately, we can't use compare and swap for non-cachable
960206451Smarius	 * memory.
961206451Smarius	 */
962206451Smarius	reason = SBBC_SRAM_READ_4(sbbc_scsolir);
963206451Smarius	SBBC_SRAM_WRITE_4(sbbc_scsolir, 0);
964206451Smarius	uart_barrier(bas);
965206451Smarius	/* Acknowledge the interrupt. */
966206451Smarius	SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, status);
967206451Smarius	uart_barrier(bas);
968206451Smarius
969206451Smarius	uart_unlock(sc->sc_hwmtx);
970206451Smarius
971206451Smarius	ipend = 0;
972206451Smarius	if ((reason & SBBC_SRAM_CONS_IN) != 0)
973206451Smarius		ipend |= SER_INT_RXREADY;
974206451Smarius	if ((reason & SBBC_SRAM_CONS_BRK) != 0)
975206451Smarius		ipend |= SER_INT_BREAK;
976206451Smarius	if ((reason & SBBC_SRAM_CONS_SPACE_OUT) != 0 &&
977206451Smarius	    SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) ==
978206451Smarius	    SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)))
979206451Smarius		ipend |= SER_INT_TXIDLE;
980206451Smarius	return (ipend);
981206451Smarius}
982206451Smarius
983206451Smariusstatic int
984206451Smariussbbc_uart_bus_param(struct uart_softc *sc __unused, int baudrate __unused,
985206451Smarius    int databits __unused, int stopbits __unused, int parity __unused)
986206451Smarius{
987206451Smarius
988206451Smarius	return (0);
989206451Smarius}
990206451Smarius
991206451Smariusstatic int
992248965Siansbbc_uart_bus_probe(struct uart_softc *sc)
993206451Smarius{
994248965Sian	struct uart_bas *bas;
995248965Sian	bus_space_tag_t bst;
996248965Sian	bus_space_handle_t bsh;
997206451Smarius
998248965Sian	if (sbbc_console != 0) {
999248965Sian		bas = &sc->sc_bas;
1000248965Sian		bst = bas->bst;
1001248965Sian		bsh = bas->bsh;
1002248965Sian		sc->sc_rxfifosz = SBBC_SRAM_READ_4(sbbc_solcons +
1003248965Sian		    SBBC_CONS_OFF(cons_in_end)) - SBBC_SRAM_READ_4(sbbc_solcons +
1004248965Sian		    SBBC_CONS_OFF(cons_in_begin)) - 1;
1005248965Sian		sc->sc_txfifosz = SBBC_SRAM_READ_4(sbbc_solcons +
1006248965Sian		    SBBC_CONS_OFF(cons_out_end)) - SBBC_SRAM_READ_4(sbbc_solcons +
1007248965Sian		    SBBC_CONS_OFF(cons_out_begin)) - 1;
1008206451Smarius		return (0);
1009248965Sian	}
1010206451Smarius	return (ENXIO);
1011206451Smarius}
1012206451Smarius
1013206451Smariusstatic int
1014206451Smariussbbc_uart_bus_receive(struct uart_softc *sc)
1015206451Smarius{
1016206451Smarius	struct uart_bas *bas;
1017206451Smarius	bus_space_tag_t bst;
1018206451Smarius	bus_space_handle_t bsh;
1019206451Smarius	int c;
1020206451Smarius	uint32_t end, rdptr, wrptr;
1021206451Smarius
1022206451Smarius	bas = &sc->sc_bas;
1023206451Smarius	bst = bas->bst;
1024206451Smarius	bsh = bas->bsh;
1025206451Smarius
1026206451Smarius	uart_lock(sc->sc_hwmtx);
1027206451Smarius
1028206451Smarius	end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end));
1029206451Smarius	rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
1030206451Smarius	wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr));
1031206451Smarius	while (rdptr != wrptr) {
1032206451Smarius		if (uart_rx_full(sc) != 0) {
1033206451Smarius			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
1034206451Smarius			break;
1035206451Smarius		}
1036206451Smarius		c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
1037206451Smarius		uart_rx_put(sc, c);
1038206451Smarius		if (++rdptr == end)
1039206451Smarius			rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
1040206451Smarius			    SBBC_CONS_OFF(cons_in_begin));
1041206451Smarius	}
1042206451Smarius	uart_barrier(bas);
1043206451Smarius	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr),
1044206451Smarius	    rdptr);
1045206451Smarius	uart_barrier(bas);
1046206451Smarius	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
1047206451Smarius	    SBBC_SRAM_CONS_SPACE_IN);
1048206451Smarius	uart_barrier(bas);
1049206451Smarius	sbbc_send_intr(bst, bsh);
1050206451Smarius
1051206451Smarius	uart_unlock(sc->sc_hwmtx);
1052206451Smarius	return (0);
1053206451Smarius}
1054206451Smarius
1055206451Smariusstatic int
1056206451Smariussbbc_uart_bus_setsig(struct uart_softc *sc, int sig)
1057206451Smarius{
1058206451Smarius	struct uart_bas *bas;
1059206451Smarius	uint32_t new, old;
1060206451Smarius
1061206451Smarius	bas = &sc->sc_bas;
1062206451Smarius	do {
1063206451Smarius		old = sc->sc_hwsig;
1064206451Smarius		new = old;
1065206451Smarius		if ((sig & SER_DDTR) != 0) {
1066206451Smarius			SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
1067206451Smarius		}
1068206451Smarius		if ((sig & SER_DRTS) != 0) {
1069206451Smarius			SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
1070206451Smarius		}
1071206451Smarius	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
1072206451Smarius	return (0);
1073206451Smarius}
1074206451Smarius
1075206451Smariusstatic int
1076206451Smariussbbc_uart_bus_transmit(struct uart_softc *sc)
1077206451Smarius{
1078206451Smarius	struct uart_bas *bas;
1079206451Smarius	bus_space_tag_t bst;
1080206451Smarius	bus_space_handle_t bsh;
1081206451Smarius	int i;
1082206451Smarius	uint32_t end, wrptr;
1083206451Smarius
1084206451Smarius	bas = &sc->sc_bas;
1085206451Smarius	bst = bas->bst;
1086206451Smarius	bsh = bas->bsh;
1087206451Smarius
1088206451Smarius	uart_lock(sc->sc_hwmtx);
1089206451Smarius
1090206451Smarius	end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end));
1091206451Smarius	wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
1092206451Smarius	    SBBC_CONS_OFF(cons_out_wrptr));
1093206451Smarius	for (i = 0; i < sc->sc_txdatasz; i++) {
1094206451Smarius		SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, sc->sc_txbuf[i]);
1095206451Smarius		if (++wrptr == end)
1096206451Smarius			wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
1097206451Smarius			    SBBC_CONS_OFF(cons_out_begin));
1098206451Smarius	}
1099206451Smarius	uart_barrier(bas);
1100206451Smarius	SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr),
1101206451Smarius	    wrptr);
1102206451Smarius	uart_barrier(bas);
1103206451Smarius	SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
1104206451Smarius	    SBBC_SRAM_CONS_OUT);
1105206451Smarius	uart_barrier(bas);
1106206451Smarius	sbbc_send_intr(bst, bsh);
1107206451Smarius	sc->sc_txbusy = 1;
1108206451Smarius
1109206451Smarius	uart_unlock(sc->sc_hwmtx);
1110206451Smarius	return (0);
1111206451Smarius}
1112