Deleted Added
full compact
sbbc.c (248965) sbbc.c (281438)
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>
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 248965 2013-04-01 00:44:20Z ian $");
44__FBSDID("$FreeBSD: head/sys/sparc64/pci/sbbc.c 281438 2015-04-11 17:16:23Z andrew $");
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, NULL, NULL);
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(bst, bsh);
362 if (error != 0) {
363 device_printf(dev, "failed to parse TOC\n");
364 if (sbbc_console != 0) {
365 bus_release_resource(dev, SYS_RES_MEMORY, rid,
366 sc->sc_res);
367 return (error);
368 }
369 }
370 }
371 if (sbbc_toddata != 0) {
372 if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
373 SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC)
374 device_printf(dev, "invalid TOD magic %#x\n", val);
375 else if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
376 SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION)
377 device_printf(dev, "invalid TOD version %#x\n", val);
378 else {
379 clock_register(dev, 1000000); /* 1 sec. resolution */
380 if (bootverbose) {
381 sbbc_tod_gettime(dev, &ts);
382 device_printf(dev,
383 "current time: %ld.%09ld\n",
384 (long)ts.tv_sec, ts.tv_nsec);
385 }
386 }
387 }
388 return (0);
389}
390
391/*
392 * Note that the bus methods don't pass-through the uart(4) requests but act
393 * as if they would come from sbbc(4) in order to avoid complications with
394 * pci(4) (actually, uart(4) isn't a real child but rather a function of
395 * sbbc(4) anyway).
396 */
397
398static struct resource *
399sbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type,
400 int *rid, u_long start, u_long end, u_long count, u_int flags)
401{
402 struct sbbc_softc *sc;
403
404 sc = device_get_softc(dev);
405 switch (type) {
406 case SYS_RES_IRQ:
407 return (bus_generic_alloc_resource(dev, dev, type, rid, start,
408 end, count, flags));
409 case SYS_RES_MEMORY:
410 return (sc->sc_res);
411 default:
412 return (NULL);
413 }
414}
415
416static int
417sbbc_bus_activate_resource(device_t bus, device_t child, int type, int rid,
418 struct resource *res)
419{
420
421 if (type == SYS_RES_MEMORY)
422 return (0);
423 return (bus_generic_activate_resource(bus, child, type, rid, res));
424}
425
426static int
427sbbc_bus_deactivate_resource(device_t bus, device_t child, int type, int rid,
428 struct resource *res)
429{
430
431 if (type == SYS_RES_MEMORY)
432 return (0);
433 return (bus_generic_deactivate_resource(bus, child, type, rid, res));
434}
435
436static int
437sbbc_bus_adjust_resource(device_t bus __unused, device_t child __unused,
438 int type __unused, struct resource *res __unused, u_long start __unused,
439 u_long end __unused)
440{
441
442 return (ENXIO);
443}
444
445static int
446sbbc_bus_release_resource(device_t dev, device_t child __unused, int type,
447 int rid, struct resource *res)
448{
449
450 if (type == SYS_RES_IRQ)
451 return (bus_generic_release_resource(dev, dev, type, rid,
452 res));
453 return (0);
454}
455
456static struct resource_list *
457sbbc_bus_get_resource_list(device_t dev, device_t child __unused)
458{
459
460 return (bus_generic_get_resource_list(dev, dev));
461}
462
463static int
464sbbc_bus_setup_intr(device_t dev, device_t child __unused,
465 struct resource *res, int flags, driver_filter_t *filt,
466 driver_intr_t *intr, void *arg, void **cookiep)
467{
468
469 return (bus_generic_setup_intr(dev, dev, res, flags, filt, intr, arg,
470 cookiep));
471}
472
473static int
474sbbc_bus_teardown_intr(device_t dev, device_t child __unused,
475 struct resource *res, void *cookie)
476{
477
478 return (bus_generic_teardown_intr(dev, dev, res, cookie));
479}
480
481/*
482 * internal helpers
483 */
484static int
485sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh)
486{
487 char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)];
488 bus_size_t tag;
489 phandle_t node;
490 uint32_t off, sram_toc;
491 u_int i, tags;
492
493 if ((node = OF_finddevice("/chosen")) == -1)
494 return (ENXIO);
495 /* SRAM TOC offset defaults to 0. */
496 if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0)
497 sram_toc = 0;
498
499 bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc +
500 SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE);
501 buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0';
502 if (strcmp(buf, SBBC_TOC_MAGIC) != 0)
503 return (ENXIO);
504
505 tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags));
506 for (i = 0; i < tags; i++) {
507 tag = sram_toc + SBBC_TOC_OFF(toc_tag) +
508 i * sizeof(struct sbbc_sram_tag);
509 bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag +
510 SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE);
511 buf[SBBC_TAG_KEY_SIZE - 1] = '\0';
512 off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset));
513 if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0)
514 sbbc_scsolie = off;
515 else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0)
516 sbbc_scsolir = off;
517 else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0)
518 sbbc_solcons = off;
519 else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0)
520 sbbc_solscie = off;
521 else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0)
522 sbbc_solscir = off;
523 else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0)
524 sbbc_toddata = off;
525 }
526 return (0);
527}
528
529static const char *
530sbbc_serengeti_set_console_input(char *new)
531{
532 struct {
533 cell_t name;
534 cell_t nargs;
535 cell_t nreturns;
536 cell_t new;
537 cell_t old;
538 } args = {
539 (cell_t)SUNW_SETCONSINPUT,
540 1,
541 1,
542 };
543
544 args.new = (cell_t)new;
545 if (ofw_entry(&args) == -1)
546 return (NULL);
547 return ((const char *)args.old);
548}
549
550static inline void
551sbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh)
552{
553
554 SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON);
555 bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1,
556 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
557}
558
559/*
560 * TOD interface
561 */
562static int
563sbbc_tod_gettime(device_t dev, struct timespec *ts)
564{
565 struct sbbc_softc *sc;
566 bus_space_tag_t bst;
567 bus_space_handle_t bsh;
568
569 sc = device_get_softc(dev);
570 bst = rman_get_bustag(sc->sc_res);
571 bsh = rman_get_bushandle(sc->sc_res);
572
573 ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) +
574 SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew));
575 ts->tv_nsec = 0;
576 return (0);
577}
578
579static int
580sbbc_tod_settime(device_t dev, struct timespec *ts)
581{
582 struct sbbc_softc *sc;
583 bus_space_tag_t bst;
584 bus_space_handle_t bsh;
585
586 sc = device_get_softc(dev);
587 bst = rman_get_bustag(sc->sc_res);
588 bsh = rman_get_bushandle(sc->sc_res);
589
590 SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec -
591 SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)));
592 return (0);
593}
594
595/*
596 * UART bus front-end
597 */
598static device_probe_t sbbc_uart_sbbc_probe;
599
600static device_method_t sbbc_uart_sbbc_methods[] = {
601 /* Device interface */
602 DEVMETHOD(device_probe, sbbc_uart_sbbc_probe),
603 DEVMETHOD(device_attach, uart_bus_attach),
604 DEVMETHOD(device_detach, uart_bus_detach),
605
606 DEVMETHOD_END
607};
608
609DEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods,
610 sizeof(struct uart_softc));
611DRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, NULL, NULL);
612
613static int
614sbbc_uart_sbbc_probe(device_t dev)
615{
616 struct uart_softc *sc;
617
618 sc = device_get_softc(dev);
619 sc->sc_class = &uart_sbbc_class;
620 device_set_desc(dev, "Serengeti console");
621 return (uart_bus_probe(dev, 0, 0, SBBC_PCI_BAR, 0));
622}
623
624/*
625 * Low-level UART interface
626 */
627static int sbbc_uart_probe(struct uart_bas *bas);
628static void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits,
629 int stopbits, int parity);
630static void sbbc_uart_term(struct uart_bas *bas);
631static void sbbc_uart_putc(struct uart_bas *bas, int c);
632static int sbbc_uart_rxready(struct uart_bas *bas);
633static int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx);
634
635static struct uart_ops sbbc_uart_ops = {
636 .probe = sbbc_uart_probe,
637 .init = sbbc_uart_init,
638 .term = sbbc_uart_term,
639 .putc = sbbc_uart_putc,
640 .rxready = sbbc_uart_rxready,
641 .getc = sbbc_uart_getc,
642};
643
644static int
645sbbc_uart_probe(struct uart_bas *bas)
646{
647 bus_space_tag_t bst;
648 bus_space_handle_t bsh;
649 int error;
650
651 sbbc_console = 1;
652 bst = bas->bst;
653 bsh = bas->bsh;
654 error = sbbc_parse_toc(bst, bsh);
655 if (error != 0)
656 return (error);
657
658 if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 ||
659 sbbc_solscie == 0 || sbbc_solscir == 0)
660 return (ENXIO);
661
662 if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) !=
663 SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons +
664 SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION)
665 return (ENXIO);
666 return (0);
667}
668
669static void
670sbbc_uart_init(struct uart_bas *bas, int baudrate __unused,
671 int databits __unused, int stopbits __unused, int parity __unused)
672{
673 bus_space_tag_t bst;
674 bus_space_handle_t bsh;
675
676 bst = bas->bst;
677 bsh = bas->bsh;
678
679 /* Enable output to and space in from the SC interrupts. */
680 SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) |
681 SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN);
682 uart_barrier(bas);
683
684 /* Take over the console input. */
685 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT);
686}
687
688static void
689sbbc_uart_term(struct uart_bas *bas __unused)
690{
691
692 /* Give back the console input. */
693 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP);
694}
695
696static void
697sbbc_uart_putc(struct uart_bas *bas, int c)
698{
699 bus_space_tag_t bst;
700 bus_space_handle_t bsh;
701 uint32_t wrptr;
702
703 bst = bas->bst;
704 bsh = bas->bsh;
705
706 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
707 SBBC_CONS_OFF(cons_out_wrptr));
708 SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c);
709 uart_barrier(bas);
710 if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons +
711 SBBC_CONS_OFF(cons_out_end)))
712 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
713 SBBC_CONS_OFF(cons_out_begin));
714 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr),
715 wrptr);
716 uart_barrier(bas);
717
718 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
719 SBBC_SRAM_CONS_OUT);
720 uart_barrier(bas);
721 sbbc_send_intr(bst, bsh);
722}
723
724static int
725sbbc_uart_rxready(struct uart_bas *bas)
726{
727 bus_space_tag_t bst;
728 bus_space_handle_t bsh;
729
730 bst = bas->bst;
731 bsh = bas->bsh;
732
733 if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) ==
734 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr)))
735 return (0);
736 return (1);
737}
738
739static int
740sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
741{
742 bus_space_tag_t bst;
743 bus_space_handle_t bsh;
744 int c;
745 uint32_t rdptr;
746
747 bst = bas->bst;
748 bsh = bas->bsh;
749
750 uart_lock(hwmtx);
751
752 while (sbbc_uart_rxready(bas) == 0) {
753 uart_unlock(hwmtx);
754 DELAY(4);
755 uart_lock(hwmtx);
756 }
757
758 rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
759 c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
760 uart_barrier(bas);
761 if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons +
762 SBBC_CONS_OFF(cons_in_end)))
763 rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
764 SBBC_CONS_OFF(cons_in_begin));
765 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,
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, NULL, NULL);
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(bst, bsh);
362 if (error != 0) {
363 device_printf(dev, "failed to parse TOC\n");
364 if (sbbc_console != 0) {
365 bus_release_resource(dev, SYS_RES_MEMORY, rid,
366 sc->sc_res);
367 return (error);
368 }
369 }
370 }
371 if (sbbc_toddata != 0) {
372 if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
373 SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC)
374 device_printf(dev, "invalid TOD magic %#x\n", val);
375 else if ((val = SBBC_SRAM_READ_4(sbbc_toddata +
376 SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION)
377 device_printf(dev, "invalid TOD version %#x\n", val);
378 else {
379 clock_register(dev, 1000000); /* 1 sec. resolution */
380 if (bootverbose) {
381 sbbc_tod_gettime(dev, &ts);
382 device_printf(dev,
383 "current time: %ld.%09ld\n",
384 (long)ts.tv_sec, ts.tv_nsec);
385 }
386 }
387 }
388 return (0);
389}
390
391/*
392 * Note that the bus methods don't pass-through the uart(4) requests but act
393 * as if they would come from sbbc(4) in order to avoid complications with
394 * pci(4) (actually, uart(4) isn't a real child but rather a function of
395 * sbbc(4) anyway).
396 */
397
398static struct resource *
399sbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type,
400 int *rid, u_long start, u_long end, u_long count, u_int flags)
401{
402 struct sbbc_softc *sc;
403
404 sc = device_get_softc(dev);
405 switch (type) {
406 case SYS_RES_IRQ:
407 return (bus_generic_alloc_resource(dev, dev, type, rid, start,
408 end, count, flags));
409 case SYS_RES_MEMORY:
410 return (sc->sc_res);
411 default:
412 return (NULL);
413 }
414}
415
416static int
417sbbc_bus_activate_resource(device_t bus, device_t child, int type, int rid,
418 struct resource *res)
419{
420
421 if (type == SYS_RES_MEMORY)
422 return (0);
423 return (bus_generic_activate_resource(bus, child, type, rid, res));
424}
425
426static int
427sbbc_bus_deactivate_resource(device_t bus, device_t child, int type, int rid,
428 struct resource *res)
429{
430
431 if (type == SYS_RES_MEMORY)
432 return (0);
433 return (bus_generic_deactivate_resource(bus, child, type, rid, res));
434}
435
436static int
437sbbc_bus_adjust_resource(device_t bus __unused, device_t child __unused,
438 int type __unused, struct resource *res __unused, u_long start __unused,
439 u_long end __unused)
440{
441
442 return (ENXIO);
443}
444
445static int
446sbbc_bus_release_resource(device_t dev, device_t child __unused, int type,
447 int rid, struct resource *res)
448{
449
450 if (type == SYS_RES_IRQ)
451 return (bus_generic_release_resource(dev, dev, type, rid,
452 res));
453 return (0);
454}
455
456static struct resource_list *
457sbbc_bus_get_resource_list(device_t dev, device_t child __unused)
458{
459
460 return (bus_generic_get_resource_list(dev, dev));
461}
462
463static int
464sbbc_bus_setup_intr(device_t dev, device_t child __unused,
465 struct resource *res, int flags, driver_filter_t *filt,
466 driver_intr_t *intr, void *arg, void **cookiep)
467{
468
469 return (bus_generic_setup_intr(dev, dev, res, flags, filt, intr, arg,
470 cookiep));
471}
472
473static int
474sbbc_bus_teardown_intr(device_t dev, device_t child __unused,
475 struct resource *res, void *cookie)
476{
477
478 return (bus_generic_teardown_intr(dev, dev, res, cookie));
479}
480
481/*
482 * internal helpers
483 */
484static int
485sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh)
486{
487 char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)];
488 bus_size_t tag;
489 phandle_t node;
490 uint32_t off, sram_toc;
491 u_int i, tags;
492
493 if ((node = OF_finddevice("/chosen")) == -1)
494 return (ENXIO);
495 /* SRAM TOC offset defaults to 0. */
496 if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0)
497 sram_toc = 0;
498
499 bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc +
500 SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE);
501 buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0';
502 if (strcmp(buf, SBBC_TOC_MAGIC) != 0)
503 return (ENXIO);
504
505 tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags));
506 for (i = 0; i < tags; i++) {
507 tag = sram_toc + SBBC_TOC_OFF(toc_tag) +
508 i * sizeof(struct sbbc_sram_tag);
509 bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag +
510 SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE);
511 buf[SBBC_TAG_KEY_SIZE - 1] = '\0';
512 off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset));
513 if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0)
514 sbbc_scsolie = off;
515 else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0)
516 sbbc_scsolir = off;
517 else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0)
518 sbbc_solcons = off;
519 else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0)
520 sbbc_solscie = off;
521 else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0)
522 sbbc_solscir = off;
523 else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0)
524 sbbc_toddata = off;
525 }
526 return (0);
527}
528
529static const char *
530sbbc_serengeti_set_console_input(char *new)
531{
532 struct {
533 cell_t name;
534 cell_t nargs;
535 cell_t nreturns;
536 cell_t new;
537 cell_t old;
538 } args = {
539 (cell_t)SUNW_SETCONSINPUT,
540 1,
541 1,
542 };
543
544 args.new = (cell_t)new;
545 if (ofw_entry(&args) == -1)
546 return (NULL);
547 return ((const char *)args.old);
548}
549
550static inline void
551sbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh)
552{
553
554 SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON);
555 bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1,
556 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
557}
558
559/*
560 * TOD interface
561 */
562static int
563sbbc_tod_gettime(device_t dev, struct timespec *ts)
564{
565 struct sbbc_softc *sc;
566 bus_space_tag_t bst;
567 bus_space_handle_t bsh;
568
569 sc = device_get_softc(dev);
570 bst = rman_get_bustag(sc->sc_res);
571 bsh = rman_get_bushandle(sc->sc_res);
572
573 ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) +
574 SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew));
575 ts->tv_nsec = 0;
576 return (0);
577}
578
579static int
580sbbc_tod_settime(device_t dev, struct timespec *ts)
581{
582 struct sbbc_softc *sc;
583 bus_space_tag_t bst;
584 bus_space_handle_t bsh;
585
586 sc = device_get_softc(dev);
587 bst = rman_get_bustag(sc->sc_res);
588 bsh = rman_get_bushandle(sc->sc_res);
589
590 SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec -
591 SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)));
592 return (0);
593}
594
595/*
596 * UART bus front-end
597 */
598static device_probe_t sbbc_uart_sbbc_probe;
599
600static device_method_t sbbc_uart_sbbc_methods[] = {
601 /* Device interface */
602 DEVMETHOD(device_probe, sbbc_uart_sbbc_probe),
603 DEVMETHOD(device_attach, uart_bus_attach),
604 DEVMETHOD(device_detach, uart_bus_detach),
605
606 DEVMETHOD_END
607};
608
609DEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods,
610 sizeof(struct uart_softc));
611DRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, NULL, NULL);
612
613static int
614sbbc_uart_sbbc_probe(device_t dev)
615{
616 struct uart_softc *sc;
617
618 sc = device_get_softc(dev);
619 sc->sc_class = &uart_sbbc_class;
620 device_set_desc(dev, "Serengeti console");
621 return (uart_bus_probe(dev, 0, 0, SBBC_PCI_BAR, 0));
622}
623
624/*
625 * Low-level UART interface
626 */
627static int sbbc_uart_probe(struct uart_bas *bas);
628static void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits,
629 int stopbits, int parity);
630static void sbbc_uart_term(struct uart_bas *bas);
631static void sbbc_uart_putc(struct uart_bas *bas, int c);
632static int sbbc_uart_rxready(struct uart_bas *bas);
633static int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx);
634
635static struct uart_ops sbbc_uart_ops = {
636 .probe = sbbc_uart_probe,
637 .init = sbbc_uart_init,
638 .term = sbbc_uart_term,
639 .putc = sbbc_uart_putc,
640 .rxready = sbbc_uart_rxready,
641 .getc = sbbc_uart_getc,
642};
643
644static int
645sbbc_uart_probe(struct uart_bas *bas)
646{
647 bus_space_tag_t bst;
648 bus_space_handle_t bsh;
649 int error;
650
651 sbbc_console = 1;
652 bst = bas->bst;
653 bsh = bas->bsh;
654 error = sbbc_parse_toc(bst, bsh);
655 if (error != 0)
656 return (error);
657
658 if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 ||
659 sbbc_solscie == 0 || sbbc_solscir == 0)
660 return (ENXIO);
661
662 if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) !=
663 SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons +
664 SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION)
665 return (ENXIO);
666 return (0);
667}
668
669static void
670sbbc_uart_init(struct uart_bas *bas, int baudrate __unused,
671 int databits __unused, int stopbits __unused, int parity __unused)
672{
673 bus_space_tag_t bst;
674 bus_space_handle_t bsh;
675
676 bst = bas->bst;
677 bsh = bas->bsh;
678
679 /* Enable output to and space in from the SC interrupts. */
680 SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) |
681 SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN);
682 uart_barrier(bas);
683
684 /* Take over the console input. */
685 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT);
686}
687
688static void
689sbbc_uart_term(struct uart_bas *bas __unused)
690{
691
692 /* Give back the console input. */
693 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP);
694}
695
696static void
697sbbc_uart_putc(struct uart_bas *bas, int c)
698{
699 bus_space_tag_t bst;
700 bus_space_handle_t bsh;
701 uint32_t wrptr;
702
703 bst = bas->bst;
704 bsh = bas->bsh;
705
706 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
707 SBBC_CONS_OFF(cons_out_wrptr));
708 SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c);
709 uart_barrier(bas);
710 if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons +
711 SBBC_CONS_OFF(cons_out_end)))
712 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
713 SBBC_CONS_OFF(cons_out_begin));
714 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr),
715 wrptr);
716 uart_barrier(bas);
717
718 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
719 SBBC_SRAM_CONS_OUT);
720 uart_barrier(bas);
721 sbbc_send_intr(bst, bsh);
722}
723
724static int
725sbbc_uart_rxready(struct uart_bas *bas)
726{
727 bus_space_tag_t bst;
728 bus_space_handle_t bsh;
729
730 bst = bas->bst;
731 bsh = bas->bsh;
732
733 if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) ==
734 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr)))
735 return (0);
736 return (1);
737}
738
739static int
740sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
741{
742 bus_space_tag_t bst;
743 bus_space_handle_t bsh;
744 int c;
745 uint32_t rdptr;
746
747 bst = bas->bst;
748 bsh = bas->bsh;
749
750 uart_lock(hwmtx);
751
752 while (sbbc_uart_rxready(bas) == 0) {
753 uart_unlock(hwmtx);
754 DELAY(4);
755 uart_lock(hwmtx);
756 }
757
758 rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
759 c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
760 uart_barrier(bas);
761 if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons +
762 SBBC_CONS_OFF(cons_in_end)))
763 rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
764 SBBC_CONS_OFF(cons_in_begin));
765 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 */
816 .uc_rclk = 0x5bbc, /* arbitrary */
817 .uc_rshift = 0
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}
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 uart_lock(sc->sc_hwmtx);
840
841 /*
842 * Let the current output drain before enabling interrupts. Not
843 * doing so tends to cause lost output when turning them on.
844 */
845 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
846 SBBC_CONS_OFF(cons_out_wrptr));
847 while (SBBC_SRAM_READ_4(sbbc_solcons +
848 SBBC_CONS_OFF(cons_out_rdptr)) != wrptr);
849 cpu_spinwait();
850
851 /* Clear and acknowledge possibly outstanding interrupts. */
852 SBBC_SRAM_WRITE_4(sbbc_scsolir, 0);
853 uart_barrier(bas);
854 SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS,
855 SBBC_SRAM_READ_4(sbbc_scsolir));
856 uart_barrier(bas);
857 /* Enable PCI interrupts. */
858 SBBC_REGS_WRITE_4(SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A);
859 uart_barrier(bas);
860 /* Enable input from and output to SC as well as break interrupts. */
861 SBBC_SRAM_WRITE_4(sbbc_scsolie, SBBC_SRAM_READ_4(sbbc_scsolie) |
862 SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK |
863 SBBC_SRAM_CONS_SPACE_OUT);
864 uart_barrier(bas);
865
866 uart_unlock(sc->sc_hwmtx);
867 return (0);
868}
869
870static int
871sbbc_uart_bus_detach(struct uart_softc *sc)
872{
873
874 /* Give back the console input. */
875 sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP);
876 return (0);
877}
878
879static int
880sbbc_uart_bus_flush(struct uart_softc *sc, int what)
881{
882 struct uart_bas *bas;
883 bus_space_tag_t bst;
884 bus_space_handle_t bsh;
885
886 bas = &sc->sc_bas;
887 bst = bas->bst;
888 bsh = bas->bsh;
889
890 if ((what & UART_FLUSH_TRANSMITTER) != 0)
891 return (ENODEV);
892 if ((what & UART_FLUSH_RECEIVER) != 0) {
893 SBBC_SRAM_WRITE_4(sbbc_solcons +
894 SBBC_CONS_OFF(cons_in_rdptr),
895 SBBC_SRAM_READ_4(sbbc_solcons +
896 SBBC_CONS_OFF(cons_in_wrptr)));
897 uart_barrier(bas);
898 }
899 return (0);
900}
901
902static int
903sbbc_uart_bus_getsig(struct uart_softc *sc)
904{
905 uint32_t dummy, new, old, sig;
906
907 do {
908 old = sc->sc_hwsig;
909 sig = old;
910 dummy = 0;
911 SIGCHG(dummy, sig, SER_CTS, SER_DCTS);
912 SIGCHG(dummy, sig, SER_DCD, SER_DDCD);
913 SIGCHG(dummy, sig, SER_DSR, SER_DDSR);
914 new = sig & ~SER_MASK_DELTA;
915 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
916 return (sig);
917}
918
919static int
920sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
921{
922 int error;
923
924 error = 0;
925 uart_lock(sc->sc_hwmtx);
926 switch (request) {
927 case UART_IOCTL_BAUD:
928 *(int*)data = 9600; /* arbitrary */
929 break;
930 default:
931 error = EINVAL;
932 break;
933 }
934 uart_unlock(sc->sc_hwmtx);
935 return (error);
936}
937
938static int
939sbbc_uart_bus_ipend(struct uart_softc *sc)
940{
941 struct uart_bas *bas;
942 bus_space_tag_t bst;
943 bus_space_handle_t bsh;
944 int ipend;
945 uint32_t reason, status;
946
947 bas = &sc->sc_bas;
948 bst = bas->bst;
949 bsh = bas->bsh;
950
951 uart_lock(sc->sc_hwmtx);
952 status = SBBC_REGS_READ_4(SBBC_PCI_INT_STATUS);
953 if (status == 0) {
954 uart_unlock(sc->sc_hwmtx);
955 return (0);
956 }
957
958 /*
959 * Unfortunately, we can't use compare and swap for non-cachable
960 * memory.
961 */
962 reason = SBBC_SRAM_READ_4(sbbc_scsolir);
963 SBBC_SRAM_WRITE_4(sbbc_scsolir, 0);
964 uart_barrier(bas);
965 /* Acknowledge the interrupt. */
966 SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, status);
967 uart_barrier(bas);
968
969 uart_unlock(sc->sc_hwmtx);
970
971 ipend = 0;
972 if ((reason & SBBC_SRAM_CONS_IN) != 0)
973 ipend |= SER_INT_RXREADY;
974 if ((reason & SBBC_SRAM_CONS_BRK) != 0)
975 ipend |= SER_INT_BREAK;
976 if ((reason & SBBC_SRAM_CONS_SPACE_OUT) != 0 &&
977 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) ==
978 SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)))
979 ipend |= SER_INT_TXIDLE;
980 return (ipend);
981}
982
983static int
984sbbc_uart_bus_param(struct uart_softc *sc __unused, int baudrate __unused,
985 int databits __unused, int stopbits __unused, int parity __unused)
986{
987
988 return (0);
989}
990
991static int
992sbbc_uart_bus_probe(struct uart_softc *sc)
993{
994 struct uart_bas *bas;
995 bus_space_tag_t bst;
996 bus_space_handle_t bsh;
997
998 if (sbbc_console != 0) {
999 bas = &sc->sc_bas;
1000 bst = bas->bst;
1001 bsh = bas->bsh;
1002 sc->sc_rxfifosz = SBBC_SRAM_READ_4(sbbc_solcons +
1003 SBBC_CONS_OFF(cons_in_end)) - SBBC_SRAM_READ_4(sbbc_solcons +
1004 SBBC_CONS_OFF(cons_in_begin)) - 1;
1005 sc->sc_txfifosz = SBBC_SRAM_READ_4(sbbc_solcons +
1006 SBBC_CONS_OFF(cons_out_end)) - SBBC_SRAM_READ_4(sbbc_solcons +
1007 SBBC_CONS_OFF(cons_out_begin)) - 1;
1008 return (0);
1009 }
1010 return (ENXIO);
1011}
1012
1013static int
1014sbbc_uart_bus_receive(struct uart_softc *sc)
1015{
1016 struct uart_bas *bas;
1017 bus_space_tag_t bst;
1018 bus_space_handle_t bsh;
1019 int c;
1020 uint32_t end, rdptr, wrptr;
1021
1022 bas = &sc->sc_bas;
1023 bst = bas->bst;
1024 bsh = bas->bsh;
1025
1026 uart_lock(sc->sc_hwmtx);
1027
1028 end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end));
1029 rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr));
1030 wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr));
1031 while (rdptr != wrptr) {
1032 if (uart_rx_full(sc) != 0) {
1033 sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
1034 break;
1035 }
1036 c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr);
1037 uart_rx_put(sc, c);
1038 if (++rdptr == end)
1039 rdptr = SBBC_SRAM_READ_4(sbbc_solcons +
1040 SBBC_CONS_OFF(cons_in_begin));
1041 }
1042 uart_barrier(bas);
1043 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr),
1044 rdptr);
1045 uart_barrier(bas);
1046 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
1047 SBBC_SRAM_CONS_SPACE_IN);
1048 uart_barrier(bas);
1049 sbbc_send_intr(bst, bsh);
1050
1051 uart_unlock(sc->sc_hwmtx);
1052 return (0);
1053}
1054
1055static int
1056sbbc_uart_bus_setsig(struct uart_softc *sc, int sig)
1057{
1058 struct uart_bas *bas;
1059 uint32_t new, old;
1060
1061 bas = &sc->sc_bas;
1062 do {
1063 old = sc->sc_hwsig;
1064 new = old;
1065 if ((sig & SER_DDTR) != 0) {
1066 SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
1067 }
1068 if ((sig & SER_DRTS) != 0) {
1069 SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
1070 }
1071 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
1072 return (0);
1073}
1074
1075static int
1076sbbc_uart_bus_transmit(struct uart_softc *sc)
1077{
1078 struct uart_bas *bas;
1079 bus_space_tag_t bst;
1080 bus_space_handle_t bsh;
1081 int i;
1082 uint32_t end, wrptr;
1083
1084 bas = &sc->sc_bas;
1085 bst = bas->bst;
1086 bsh = bas->bsh;
1087
1088 uart_lock(sc->sc_hwmtx);
1089
1090 end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end));
1091 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
1092 SBBC_CONS_OFF(cons_out_wrptr));
1093 for (i = 0; i < sc->sc_txdatasz; i++) {
1094 SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, sc->sc_txbuf[i]);
1095 if (++wrptr == end)
1096 wrptr = SBBC_SRAM_READ_4(sbbc_solcons +
1097 SBBC_CONS_OFF(cons_out_begin));
1098 }
1099 uart_barrier(bas);
1100 SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr),
1101 wrptr);
1102 uart_barrier(bas);
1103 SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) |
1104 SBBC_SRAM_CONS_OUT);
1105 uart_barrier(bas);
1106 sbbc_send_intr(bst, bsh);
1107 sc->sc_txbusy = 1;
1108
1109 uart_unlock(sc->sc_hwmtx);
1110 return (0);
1111}