1/*	$OpenBSD: sbbc.c,v 1.15 2022/10/12 13:39:50 kettenis 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#include <sys/param.h>
19#include <sys/conf.h>
20#include <sys/device.h>
21#include <sys/malloc.h>
22#include <sys/proc.h>
23#include <sys/timeout.h>
24#include <sys/tty.h>
25#include <sys/systm.h>
26
27#ifdef DDB
28#include <ddb/db_var.h>
29#endif
30
31#include <machine/autoconf.h>
32#include <machine/conf.h>
33#include <machine/openfirm.h>
34#include <machine/sparc64.h>
35
36#include <dev/cons.h>
37
38#include <dev/pci/pcidevs.h>
39#include <dev/pci/pcireg.h>
40#include <dev/pci/pcivar.h>
41
42#include <dev/clock_subr.h>
43
44extern todr_chip_handle_t todr_handle;
45
46#define SBBC_PCI_BAR	PCI_MAPREG_START
47
48#define SBBC_REGS_OFFSET	0x800000
49#define SBBC_REGS_SIZE		0x6230
50#define SBBC_EPLD_OFFSET	0x8e0000
51#define SBBC_EPLD_SIZE		0x20
52#define SBBC_SRAM_OFFSET	0x900000
53#define SBBC_SRAM_SIZE		0x20000	/* 128KB SRAM */
54
55#define SBBC_PCI_INT_STATUS	0x2320
56#define SBBC_PCI_INT_ENABLE	0x2330
57#define SBBC_PCI_ENABLE_INT_A	0x11
58
59#define SBBC_EPLD_INTERRUPT	0x13
60#define SBBC_EPLD_INTERRUPT_ON	0x01
61
62#define SBBC_SRAM_CONS_IN		0x00000001
63#define SBBC_SRAM_CONS_OUT		0x00000002
64#define SBBC_SRAM_CONS_BRK		0x00000004
65#define SBBC_SRAM_CONS_SPACE_IN		0x00000008
66#define SBBC_SRAM_CONS_SPACE_OUT	0x00000010
67
68#define SBBC_MAX_TAGS	32
69
70struct sbbc_sram_tag {
71	char		tag_key[8];
72	uint32_t	tag_size;
73	uint32_t	tag_offset;
74};
75
76struct sbbc_sram_toc {
77	char			toc_magic[8];
78	uint8_t			toc_reserved;
79	uint8_t			toc_type;
80	uint16_t		toc_version;
81	uint32_t		toc_ntags;
82	struct sbbc_sram_tag 	toc_tag[SBBC_MAX_TAGS];
83};
84
85/* Time of day service. */
86struct sbbc_sram_tod {
87	uint32_t	tod_magic;
88	uint32_t	tod_version;
89	uint64_t	tod_time;
90	uint64_t	tod_skew;
91	uint32_t	tod_reserved;
92	uint32_t	tod_heartbeat;
93	uint32_t	tod_timeout;
94};
95
96#define SBBC_TOD_MAGIC		0x54443100 /* "TD1" */
97#define SBBC_TOD_VERSION	1
98
99/* Console service. */
100struct sbbc_sram_cons {
101	uint32_t cons_magic;
102	uint32_t cons_version;
103	uint32_t cons_size;
104
105	uint32_t cons_in_begin;
106	uint32_t cons_in_end;
107	uint32_t cons_in_rdptr;
108	uint32_t cons_in_wrptr;
109
110	uint32_t cons_out_begin;
111	uint32_t cons_out_end;
112	uint32_t cons_out_rdptr;
113	uint32_t cons_out_wrptr;
114};
115
116#define SBBC_CONS_MAGIC		0x434f4e00 /* "CON" */
117#define SBBC_CONS_VERSION	1
118
119struct sbbc_softc {
120	struct device		sc_dv;
121	bus_space_tag_t		sc_iot;
122	bus_space_handle_t	sc_regs_ioh;
123	bus_space_handle_t	sc_epld_ioh;
124	bus_space_handle_t	sc_sram_ioh;
125	caddr_t			sc_sram;
126	uint32_t		sc_sram_toc;
127	void *			sc_ih;
128
129	struct sparc_bus_space_tag sc_bbt;
130
131	struct tty		*sc_tty;
132	caddr_t			sc_sram_cons;
133	uint32_t		*sc_sram_solscie;
134	uint32_t		*sc_sram_solscir;
135	uint32_t		*sc_sram_scsolie;
136	uint32_t		*sc_sram_scsolir;
137	void			*sc_cons_si;
138};
139
140struct sbbc_softc *sbbc_cons_input;
141struct sbbc_softc *sbbc_cons_output;
142
143int	sbbc_match(struct device *, void *, void *);
144void	sbbc_attach(struct device *, struct device *, void *);
145
146const struct cfattach sbbc_ca = {
147	sizeof(struct sbbc_softc), sbbc_match, sbbc_attach
148};
149
150struct cfdriver sbbc_cd = {
151	NULL, "sbbc", DV_DULL
152};
153
154int	sbbc_intr(void *);
155void	sbbc_send_intr(struct sbbc_softc *sc);
156
157void	sbbc_attach_tod(struct sbbc_softc *, uint32_t);
158int	sbbc_tod_gettime(todr_chip_handle_t, struct timeval *);
159int	sbbc_tod_settime(todr_chip_handle_t, struct timeval *);
160
161void	sbbc_attach_cons(struct sbbc_softc *, uint32_t);
162void	sbbc_intr_cons(struct sbbc_softc *, uint32_t);
163void	sbbc_softintr_cons(void *);
164int	sbbc_cnlookc(dev_t, int *);
165int	sbbc_cngetc(dev_t);
166void	sbbc_cnputc(dev_t, int);
167void	sbbcstart(struct tty *);
168int	sbbcparam(struct tty *, struct termios *);
169
170int
171sbbc_match(struct device *parent, void *match, void *aux)
172{
173	struct pci_attach_args *pa = aux;
174
175	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SUN &&
176	    (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SUN_SBBC))
177		return (1);
178
179	return (0);
180}
181
182void
183sbbc_attach(struct device *parent, struct device *self, void *aux)
184{
185	struct sbbc_softc *sc = (void *)self;
186	struct pci_attach_args *pa = aux;
187	struct sbbc_sram_toc *toc;
188	bus_addr_t base;
189	bus_size_t size;
190	pci_intr_handle_t ih;
191	int chosen, iosram;
192	int i;
193
194	/* XXX Don't byteswap. */
195	sc->sc_bbt = *pa->pa_memt;
196	sc->sc_bbt.sasi = ASI_PRIMARY;
197	sc->sc_iot = &sc->sc_bbt;
198
199	if (pci_mapreg_info(pa->pa_pc, pa->pa_tag, SBBC_PCI_BAR,
200	    PCI_MAPREG_TYPE_MEM, &base, &size, NULL)) {
201		printf(": can't find register space\n");
202		return;
203	}
204
205	if (bus_space_map(sc->sc_iot, base + SBBC_REGS_OFFSET,
206	    SBBC_REGS_SIZE, 0, &sc->sc_regs_ioh)) {
207		printf(": can't map register space\n");
208		return;
209	}
210
211	if (bus_space_map(sc->sc_iot, base + SBBC_EPLD_OFFSET,
212	    SBBC_EPLD_SIZE, 0, &sc->sc_epld_ioh)) {
213		printf(": can't map EPLD registers\n");
214		goto unmap_regs;
215	}
216
217	if (bus_space_map(sc->sc_iot, base + SBBC_SRAM_OFFSET,
218	    SBBC_SRAM_SIZE, 0, &sc->sc_sram_ioh)) {
219		printf(": can't map SRAM\n");
220		goto unmap_epld;
221	}
222
223	if (pci_intr_map(pa, &ih)) {
224		printf(": unable to map interrupt\n");
225		goto unmap_sram;
226	}
227	printf(": %s\n", pci_intr_string(pa->pa_pc, ih));
228
229	sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_TTY,
230	    sbbc_intr, sc, sc->sc_dv.dv_xname);
231	if (sc->sc_ih == NULL) {
232		printf("%s: unable to establish interrupt\n", sc->sc_dv.dv_xname);
233		goto unmap_sram;
234	}
235
236	bus_space_write_4(sc->sc_iot, sc->sc_regs_ioh,
237	    SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A);
238
239	/* Check if we are the chosen one. */
240	chosen = OF_finddevice("/chosen");
241	if (OF_getprop(chosen, "iosram", &iosram, sizeof(iosram)) <= 0 ||
242	    PCITAG_NODE(pa->pa_tag) != iosram)
243		return;
244
245	/* SRAM TOC offset defaults to 0. */
246	if (OF_getprop(chosen, "iosram-toc", &sc->sc_sram_toc,
247	    sizeof(sc->sc_sram_toc)) <= 0)
248		sc->sc_sram_toc = 0;
249
250	sc->sc_sram = bus_space_vaddr(sc->sc_iot, sc->sc_sram_ioh);
251	toc = (struct sbbc_sram_toc *)(sc->sc_sram + sc->sc_sram_toc);
252
253	for (i = 0; i < toc->toc_ntags; i++) {
254		if (strcmp(toc->toc_tag[i].tag_key, "SOLSCIE") == 0)
255			sc->sc_sram_solscie = (uint32_t *)
256			    (sc->sc_sram + toc->toc_tag[i].tag_offset);
257		if (strcmp(toc->toc_tag[i].tag_key, "SOLSCIR") == 0)
258			sc->sc_sram_solscir = (uint32_t *)
259			    (sc->sc_sram + toc->toc_tag[i].tag_offset);
260		if (strcmp(toc->toc_tag[i].tag_key, "SCSOLIE") == 0)
261			sc->sc_sram_scsolie = (uint32_t *)
262			    (sc->sc_sram + toc->toc_tag[i].tag_offset);
263		if (strcmp(toc->toc_tag[i].tag_key, "SCSOLIR") == 0)
264			sc->sc_sram_scsolir = (uint32_t *)
265			    (sc->sc_sram + toc->toc_tag[i].tag_offset);
266	}
267
268	for (i = 0; i < toc->toc_ntags; i++) {
269		if (strcmp(toc->toc_tag[i].tag_key, "TODDATA") == 0)
270			sbbc_attach_tod(sc, toc->toc_tag[i].tag_offset);
271		if (strcmp(toc->toc_tag[i].tag_key, "SOLCONS") == 0)
272			sbbc_attach_cons(sc, toc->toc_tag[i].tag_offset);
273	}
274
275	return;
276
277 unmap_sram:
278	bus_space_unmap(sc->sc_iot, sc->sc_sram_ioh, SBBC_SRAM_SIZE);
279 unmap_epld:
280	bus_space_unmap(sc->sc_iot, sc->sc_sram_ioh, SBBC_EPLD_SIZE);
281 unmap_regs:
282	bus_space_unmap(sc->sc_iot, sc->sc_sram_ioh, SBBC_REGS_SIZE);
283}
284
285int
286sbbc_intr(void *arg)
287{
288	struct sbbc_softc *sc = arg;
289	uint32_t status, reason;
290
291	status = bus_space_read_4(sc->sc_iot, sc->sc_regs_ioh,
292	    SBBC_PCI_INT_STATUS);
293	if (status == 0)
294		return (0);
295
296	/* Sigh, we cannot use compare and swap for non-cachable memory. */
297	reason = *sc->sc_sram_scsolir;
298	*sc->sc_sram_scsolir = 0;
299
300	sbbc_intr_cons(sc, reason);
301
302	/* Ack interrupt. */
303	bus_space_write_4(sc->sc_iot, sc->sc_regs_ioh,
304	    SBBC_PCI_INT_STATUS, status);
305	return (1);
306}
307
308void
309sbbc_send_intr(struct sbbc_softc *sc)
310{
311	bus_space_write_1(sc->sc_iot, sc->sc_epld_ioh,
312	    SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON);
313}
314
315void
316sbbc_attach_tod(struct sbbc_softc *sc, uint32_t offset)
317{
318	struct sbbc_sram_tod *tod;
319	todr_chip_handle_t handle;
320
321	tod = (struct sbbc_sram_tod *)(sc->sc_sram + offset);
322	if (tod->tod_magic != SBBC_TOD_MAGIC ||
323	    tod->tod_version < SBBC_TOD_VERSION)
324		return;
325
326	handle = malloc(sizeof(struct todr_chip_handle), M_DEVBUF, M_NOWAIT);
327	if (handle == NULL)
328		panic("couldn't allocate todr_handle");
329
330	handle->cookie = tod;
331	handle->todr_gettime = sbbc_tod_gettime;
332	handle->todr_settime = sbbc_tod_settime;
333	handle->bus_cookie = NULL;
334	handle->todr_setwen = NULL;
335	handle->todr_quality = 0;
336	todr_handle = handle;
337}
338
339int
340sbbc_tod_gettime(todr_chip_handle_t handle, struct timeval *tv)
341{
342	struct sbbc_sram_tod *tod = handle->cookie;
343
344	tv->tv_sec = tod->tod_time + tod->tod_skew;
345	tv->tv_usec = 0;
346	return (0);
347}
348
349int
350sbbc_tod_settime(todr_chip_handle_t handle, struct timeval *tv)
351{
352	struct sbbc_sram_tod *tod = handle->cookie;
353
354	tod->tod_skew = tv->tv_sec - tod->tod_time;
355	return (0);
356}
357
358void
359sbbc_attach_cons(struct sbbc_softc *sc, uint32_t offset)
360{
361	struct sbbc_sram_cons *cons;
362	int sgcn_is_input, sgcn_is_output, node, maj;
363	char buf[32];
364
365	if (sc->sc_sram_solscie == NULL || sc->sc_sram_solscir == NULL ||
366	    sc->sc_sram_scsolie == NULL || sc->sc_sram_scsolir == NULL)
367		return;
368
369	cons = (struct sbbc_sram_cons *)(sc->sc_sram + offset);
370	if (cons->cons_magic != SBBC_CONS_MAGIC ||
371	    cons->cons_version < SBBC_CONS_VERSION)
372		return;
373
374	sc->sc_sram_cons = sc->sc_sram + offset;
375	sbbc_cons_input = sbbc_cons_output = sc;
376	sgcn_is_input = sgcn_is_output = 0;
377
378	sc->sc_cons_si = softintr_establish(IPL_TTY, sbbc_softintr_cons, sc);
379	if (sc->sc_cons_si == NULL)
380		panic("%s: can't establish soft interrupt",
381		    sc->sc_dv.dv_xname);
382
383	*sc->sc_sram_solscie |= SBBC_SRAM_CONS_OUT;
384	*sc->sc_sram_scsolie |= SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK;
385
386	/* Take over console input. */
387	prom_serengeti_set_console_input("CON_CLNT");
388
389	/* Check for console input. */
390	node = OF_instance_to_package(OF_stdin());
391	if (OF_getprop(node, "name", buf, sizeof(buf)) > 0)
392		sgcn_is_input = (strcmp(buf, "sgcn") == 0);
393
394	/* Check for console output. */
395	node = OF_instance_to_package(OF_stdout());
396	if (OF_getprop(node, "name", buf, sizeof(buf)) > 0)
397		sgcn_is_output = (strcmp(buf, "sgcn") == 0);
398
399	if (sgcn_is_input) {
400		cn_tab->cn_pollc = nullcnpollc;
401		cn_tab->cn_getc = sbbc_cngetc;
402	}
403
404	if (sgcn_is_output)
405		cn_tab->cn_putc = sbbc_cnputc;
406
407	if (sgcn_is_input || sgcn_is_output) {
408		/* Locate the major number. */
409		for (maj = 0; maj < nchrdev; maj++)
410			if (cdevsw[maj].d_open == sbbcopen)
411				break;
412		cn_tab->cn_dev = makedev(maj, sc->sc_dv.dv_unit);
413
414		/* Let current output drain. */
415		DELAY(2000000);
416
417		printf("%s: console\n", sc->sc_dv.dv_xname);
418	}
419}
420
421void
422sbbc_intr_cons(struct sbbc_softc *sc, uint32_t reason)
423{
424#ifdef DDB
425	if ((reason & SBBC_SRAM_CONS_BRK) && sc == sbbc_cons_input) {
426		if (db_console)
427			db_enter();
428	}
429#endif
430
431	if ((reason & SBBC_SRAM_CONS_IN) && sc->sc_tty)
432		softintr_schedule(sc->sc_cons_si);
433}
434
435void
436sbbc_softintr_cons(void *arg)
437{
438	struct sbbc_softc *sc = arg;
439	struct sbbc_sram_cons *cons = (void *)sc->sc_sram_cons;
440	uint32_t rdptr = cons->cons_in_rdptr;
441	struct tty *tp = sc->sc_tty;
442	int c;
443
444	while (rdptr != cons->cons_in_wrptr) {
445		if (tp->t_state & TS_ISOPEN) {
446			c = *(sc->sc_sram_cons + rdptr);
447			(*linesw[tp->t_line].l_rint)(c, tp);
448		}
449
450		if (++rdptr == cons->cons_in_end)
451			rdptr = cons->cons_in_begin;
452	}
453
454	cons->cons_in_rdptr = rdptr;
455}
456
457int
458sbbc_cnlookc(dev_t dev, int *cp)
459{
460	struct sbbc_softc *sc = sbbc_cons_input;
461	struct sbbc_sram_cons *cons = (void *)sc->sc_sram_cons;
462	uint32_t rdptr = cons->cons_in_rdptr;
463
464	if (rdptr == cons->cons_in_wrptr)
465		return (0);
466
467	*cp = *(sc->sc_sram_cons + rdptr);
468	if (++rdptr == cons->cons_in_end)
469		rdptr = cons->cons_in_begin;
470	cons->cons_in_rdptr = rdptr;
471
472	return (1);
473}
474
475int
476sbbc_cngetc(dev_t dev)
477{
478	int c;
479
480	while(!sbbc_cnlookc(dev, &c))
481		;
482
483	return (c);
484}
485
486void
487sbbc_cnputc(dev_t dev, int c)
488{
489	struct sbbc_softc *sc = sbbc_cons_output;
490	struct sbbc_sram_cons *cons = (void *)sc->sc_sram_cons;
491	uint32_t wrptr = cons->cons_out_wrptr;
492
493	*(sc->sc_sram_cons + wrptr) = c;
494	if (++wrptr == cons->cons_out_end)
495		wrptr = cons->cons_out_begin;
496	cons->cons_out_wrptr = wrptr;
497
498	*sc->sc_sram_solscir |= SBBC_SRAM_CONS_OUT;
499	sbbc_send_intr(sc);
500}
501
502int
503sbbcopen(dev_t dev, int flag, int mode, struct proc *p)
504{
505	struct sbbc_softc *sc;
506	struct tty *tp;
507	int unit = minor(dev);
508
509	if (unit >= sbbc_cd.cd_ndevs)
510		return (ENXIO);
511	sc = sbbc_cd.cd_devs[unit];
512	if (sc == NULL)
513		return (ENXIO);
514
515	if (sc->sc_tty)
516		tp = sc->sc_tty;
517	else
518		tp = sc->sc_tty = ttymalloc(0);
519
520	tp->t_oproc = sbbcstart;
521	tp->t_param = sbbcparam;
522	tp->t_dev = dev;
523	if ((tp->t_state & TS_ISOPEN) == 0) {
524		ttychars(tp);
525		tp->t_iflag = TTYDEF_IFLAG;
526		tp->t_oflag = TTYDEF_OFLAG;
527		tp->t_cflag = TTYDEF_CFLAG;
528		tp->t_lflag = TTYDEF_LFLAG;
529		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
530		ttsetwater(tp);
531	} else if ((tp->t_state & TS_XCLUDE) && suser(p))
532		return (EBUSY);
533	tp->t_state |= TS_CARR_ON;
534
535	return ((*linesw[tp->t_line].l_open)(dev, tp, p));
536}
537
538int
539sbbcclose(dev_t dev, int flag, int mode, struct proc *p)
540{
541	struct sbbc_softc *sc;
542	struct tty *tp;
543	int unit = minor(dev);
544
545	if (unit >= sbbc_cd.cd_ndevs)
546		return (ENXIO);
547	sc = sbbc_cd.cd_devs[unit];
548	if (sc == NULL)
549		return (ENXIO);
550
551	tp = sc->sc_tty;
552	(*linesw[tp->t_line].l_close)(tp, flag, p);
553	ttyclose(tp);
554	return (0);
555}
556
557int
558sbbcread(dev_t dev, struct uio *uio, int flag)
559{
560	struct sbbc_softc *sc;
561	struct tty *tp;
562	int unit = minor(dev);
563
564	if (unit >= sbbc_cd.cd_ndevs)
565		return (ENXIO);
566	sc = sbbc_cd.cd_devs[unit];
567	if (sc == NULL)
568		return (ENXIO);
569
570	tp = sc->sc_tty;
571	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
572}
573
574int
575sbbcwrite(dev_t dev, struct uio *uio, int flag)
576{
577	struct sbbc_softc *sc;
578	struct tty *tp;
579	int unit = minor(dev);
580
581	if (unit >= sbbc_cd.cd_ndevs)
582		return (ENXIO);
583	sc = sbbc_cd.cd_devs[unit];
584	if (sc == NULL)
585		return (ENXIO);
586
587	tp = sc->sc_tty;
588	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
589}
590
591int
592sbbcioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
593{
594	struct sbbc_softc *sc;
595	struct tty *tp;
596	int unit = minor(dev);
597	int error;
598
599	if (unit >= sbbc_cd.cd_ndevs)
600		return (ENXIO);
601	sc = sbbc_cd.cd_devs[unit];
602	if (sc == NULL)
603		return (ENXIO);
604
605	tp = sc->sc_tty;
606	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
607	if (error >= 0)
608		return error;
609	error = ttioctl(tp, cmd, data, flag, p);
610	if (error >= 0)
611		return (error);
612
613	return (ENOTTY);
614}
615
616void
617sbbcstart(struct tty *tp)
618{
619	int s;
620
621	s = spltty();
622	if (tp->t_state & (TS_TTSTOP | TS_BUSY)) {
623		splx(s);
624		return;
625	}
626	ttwakeupwr(tp);
627	tp->t_state |= TS_BUSY;
628	while (tp->t_outq.c_cc != 0)
629		sbbc_cnputc(tp->t_dev, getc(&tp->t_outq));
630	tp->t_state &= ~TS_BUSY;
631	splx(s);
632}
633
634int
635sbbcstop(struct tty *tp, int flag)
636{
637	int s;
638
639	s = spltty();
640	if (tp->t_state & TS_BUSY)
641		if ((tp->t_state & TS_TTSTOP) == 0)
642			tp->t_state |= TS_FLUSH;
643	splx(s);
644	return (0);
645}
646
647struct tty *
648sbbctty(dev_t dev)
649{
650	struct sbbc_softc *sc;
651	int unit = minor(dev);
652
653	if (unit >= sbbc_cd.cd_ndevs)
654		return (NULL);
655	sc = sbbc_cd.cd_devs[unit];
656	if (sc == NULL)
657		return (NULL);
658
659	return sc->sc_tty;
660}
661
662int
663sbbcparam(struct tty *tp, struct termios *t)
664{
665	tp->t_ispeed = t->c_ispeed;
666	tp->t_ospeed = t->c_ospeed;
667	tp->t_cflag = t->c_cflag;
668	return (0);
669}
670