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