1/*	$NetBSD: oboe.c,v 1.51 2021/09/26 14:56:36 thorpej Exp $	*/
2
3/*	XXXXFVDL THIS DRIVER IS BROKEN FOR NON-i386 -- vtophys() usage	*/
4
5/*-
6 * Copyright (c) 2001 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation
10 * by Jan Sparud.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/*
35 * Toshiba OBOE IrDA SIR/FIR driver.
36 *
37 * Based on information from the Linux driver, thus the magic hex numbers.
38 */
39
40#include <sys/cdefs.h>
41__KERNEL_RCSID(0, "$NetBSD: oboe.c,v 1.51 2021/09/26 14:56:36 thorpej Exp $");
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/kernel.h>
46#include <sys/device.h>
47#include <sys/malloc.h>
48#include <sys/tty.h>
49#include <sys/vnode.h>
50#include <sys/poll.h>
51#include <sys/proc.h>
52
53#include <dev/ir/ir.h>
54#include <dev/ir/irdaio.h>
55#include <dev/ir/irframevar.h>
56#include <dev/ir/sir.h>
57
58#include <dev/pci/pcidevs.h>
59#include <dev/pci/pcivar.h>
60
61#include <sys/bus.h>
62#include <sys/intr.h>
63
64#include <dev/pci/oboereg.h>
65
66static int oboe_match(device_t parent, cfdata_t match, void *aux);
67static void oboe_attach(device_t parent, device_t self, void *aux);
68static int oboe_detach(device_t self, int flags);
69
70static int oboe_open(void *h, int flag, int mode, struct lwp *l);
71static int oboe_close(void *h, int flag, int mode, struct lwp *l);
72static int oboe_read(void *h, struct uio *uio, int flag);
73static int oboe_write(void *h, struct uio *uio, int flag);
74static int oboe_set_params(void *h, struct irda_params *params);
75static int oboe_get_speeds(void *h, int *speeds);
76static int oboe_get_turnarounds(void *h, int *times);
77static int oboe_poll(void *h, int events, struct lwp *l);
78static int oboe_kqfilter(void *h, struct knote *kn);
79
80#ifdef OBOE_DEBUG
81#define DPRINTF(x)	if (oboedebug) printf x
82int oboedebug = 1;
83#else
84#define DPRINTF(x)
85#endif
86
87struct oboe_dma;
88
89struct oboe_softc {
90	device_t		sc_child;
91	struct pci_attach_args	sc_pa;
92	pci_intr_handle_t *	sc_ih;
93	unsigned int		sc_revision;	/* PCI Revision ID */
94	/* I/O Base device */
95	bus_space_tag_t		sc_iot;
96	bus_space_handle_t	sc_ioh;
97	bus_dma_tag_t		sc_dmatag;
98	struct selinfo		sc_rsel;
99
100	int			sc_state;
101#define	OBOE_RSLP		0x01	/* waiting for data (read) */
102#define	OBOE_WSLP		0x02	/* waiting for data (write) */
103#define OBOE_CLOSING		0x04	/* waiting for output to drain */
104
105	int			sc_speeds;
106	int			sc_flags;
107	int			sc_speed;
108	int			sc_ebofs;
109
110	struct oboe_dma		*sc_dmas;
111	struct OboeTaskFile	*sc_taskfile;    /* The taskfile   */
112	u_char *		sc_xmit_bufs[TX_SLOTS];
113	u_char *		sc_recv_bufs[RX_SLOTS];
114	void *			sc_xmit_stores[TX_SLOTS];
115	void *			sc_recv_stores[RX_SLOTS];
116	int			sc_txs; /* Current transmit slot number */
117	int			sc_rxs; /* Current receive slot number */
118	int			sc_saved; /* number of saved frames */
119	int			sc_lens[RX_SLOTS];
120
121	int			sc_txpending;
122
123	/* Statistics */
124	int			sc_txpackets;
125	int			sc_rxpackets;
126	int			sc_txerrors;
127	int			sc_rxerrors;
128};
129
130static int oboe_intr(void *handle);
131static int oboe_reset(struct oboe_softc *);
132
133struct oboe_dma {
134	bus_dmamap_t map;
135	void *addr;
136	bus_dma_segment_t segs[1];
137	int nsegs;
138	size_t size;
139	struct oboe_dma *next;
140};
141
142#define DMAADDR(p) ((p)->map->dm_segs[0].ds_addr)
143#define KERNADDR(p) ((void *)((p)->addr))
144
145static int oboe_alloc_taskfile(struct oboe_softc *);
146static void oboe_init_taskfile(struct oboe_softc *);
147static void oboe_startchip(struct oboe_softc *);
148static void oboe_stopchip(struct oboe_softc *);
149static int oboe_setbaud(struct oboe_softc *, int);
150
151CFATTACH_DECL_NEW(oboe, sizeof(struct oboe_softc),
152    oboe_match, oboe_attach, oboe_detach, NULL);
153
154static struct irframe_methods oboe_methods = {
155	oboe_open, oboe_close, oboe_read, oboe_write, oboe_poll,
156	oboe_kqfilter, oboe_set_params, oboe_get_speeds, oboe_get_turnarounds
157};
158
159static int
160oboe_match(device_t parent, cfdata_t match, void *aux)
161{
162	struct pci_attach_args *pa = aux;
163
164	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_TOSHIBA2 &&
165	    (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_TOSHIBA2_OBOE ||
166	     PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_TOSHIBA2_DONAUOBOE))
167		return (1);
168	return 0;
169}
170
171static void
172oboe_attach(device_t parent, device_t self, void *aux)
173{
174	struct oboe_softc *sc = device_private(self);
175	struct pci_attach_args *pa = aux;
176	pci_intr_handle_t ih;
177	struct ir_attach_args ia;
178	const char *intrstring;
179	char intrbuf[PCI_INTRSTR_LEN];
180
181	sc->sc_revision = PCI_REVISION(pa->pa_class);
182	printf(": Toshiba Fast Infrared Type O, revision %d\n",
183	       sc->sc_revision);
184
185	/* Map I/O registers. */
186	if (pci_mapreg_map(pa, IO_BAR, PCI_MAPREG_TYPE_IO, 0,
187	    &sc->sc_iot, &sc->sc_ioh, NULL, NULL)) {
188		aprint_error_dev(self, "can't map I/O space\n");
189		return;
190	}
191
192	sc->sc_dmatag = pa->pa_dmat;
193
194	ia.ia_type = IR_TYPE_IRFRAME;
195	ia.ia_methods = &oboe_methods;
196	ia.ia_handle = sc;
197
198	sc->sc_state = 0;
199	sc->sc_speed = IRDA_SPEED_9600;
200
201	/* Enable the device */
202	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
203	    pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG) |
204	    PCI_COMMAND_MASTER_ENABLE);
205
206	/* Reset the device; bail out upon failure. */
207	if (oboe_reset(sc) != 0) {
208		aprint_error_dev(self, "can't reset\n");
209		return;
210	}
211	/* Map and establish the interrupt. */
212	if (pci_intr_map(pa, &ih)) {
213		aprint_error_dev(self, "couldn't map interrupt\n");
214		return;
215	}
216	intrstring = pci_intr_string(pa->pa_pc, ih, intrbuf, sizeof(intrbuf));
217	sc->sc_ih  = pci_intr_establish_xname(pa->pa_pc, ih, IPL_IR, oboe_intr,
218	    sc, device_xname(self));
219	if (sc->sc_ih == NULL) {
220		aprint_error_dev(self, "couldn't establish interrupt");
221		if (intrstring != NULL)
222			aprint_error(" at %s", intrstring);
223		aprint_error("\n");
224		return;
225	}
226	aprint_normal_dev(self, "interrupting at %s\n", intrstring);
227
228	selinit(&sc->sc_rsel);
229
230	sc->sc_txs = 0;
231	sc->sc_rxs = 0;
232
233	sc->sc_speeds =
234		IRDA_SPEED_2400   | IRDA_SPEED_9600    | IRDA_SPEED_19200 |
235		IRDA_SPEED_38400  | IRDA_SPEED_57600   | IRDA_SPEED_115200 |
236		IRDA_SPEED_576000 | IRDA_SPEED_1152000 | IRDA_SPEED_4000000;
237
238	oboe_alloc_taskfile(sc);
239
240	sc->sc_child = config_found(self, &ia, ir_print, CFARGS_NONE);
241}
242
243static int
244oboe_detach(device_t self, int flags)
245{
246	struct oboe_softc *sc = device_private(self);
247
248#ifdef OBOE_DEBUG
249	/* XXX needs reference counting for proper detach. */
250	DPRINTF(("%s: sc=%p\n", __func__, sc));
251#endif
252	seldestroy(&sc->sc_rsel);
253	return (0);
254}
255
256static int
257oboe_open(void *h, int flag, int mode, struct lwp *l)
258{
259	struct oboe_softc *sc = h;
260
261	DPRINTF(("%s: sc=%p\n", __func__, sc));
262
263	sc->sc_state = 0;
264	sc->sc_saved = 0;
265	oboe_init_taskfile(sc);
266	oboe_startchip(sc);
267
268	return (0);
269}
270
271static int
272oboe_close(void *h, int flag, int mode,
273    struct lwp *l)
274{
275	struct oboe_softc *sc = h;
276	int error = 0;
277	int s = splir();
278
279	DPRINTF(("%s: sc=%p\n", __func__, sc));
280	/* Wait for output to drain */
281
282	if (sc->sc_txpending > 0) {
283		sc->sc_state |= OBOE_CLOSING;
284		error = tsleep(&sc->sc_state, PZERO | PCATCH, "oboecl", hz/10);
285	}
286	splx(s);
287
288	oboe_stopchip(sc);
289	return (error);
290}
291
292static int
293oboe_read(void *h, struct uio *uio, int flag)
294{
295	struct oboe_softc *sc = h;
296	int error = 0;
297	int s;
298	int slot;
299
300	DPRINTF(("%s: resid=%zu, iovcnt=%d, offset=%jd\n",
301		 __func__, uio->uio_resid, uio->uio_iovcnt,
302		 (intmax_t)uio->uio_offset));
303
304	s = splir();
305	while (sc->sc_saved == 0) {
306		if (flag & IO_NDELAY) {
307			splx(s);
308			return (EWOULDBLOCK);
309		}
310		sc->sc_state |= OBOE_RSLP;
311		DPRINTF(("oboe_read: sleep\n"));
312		error = tsleep(&sc->sc_rxs, PZERO | PCATCH, "oboerd", 0);
313		DPRINTF(("oboe_read: woke, error=%d\n", error));
314		if (error) {
315			sc->sc_state &= ~OBOE_RSLP;
316			break;
317		}
318	}
319
320	/* Do just one frame transfer per read */
321
322	if (!error) {
323		slot = (sc->sc_rxs - sc->sc_saved + RX_SLOTS) % RX_SLOTS;
324		if (uio->uio_resid < sc->sc_lens[slot]) {
325			DPRINTF(("oboe_read: uio buffer smaller than frame size"
326			    "(%zu < %d)\n", uio->uio_resid, sc->sc_lens[slot]));
327			error = EINVAL;
328		} else {
329			DPRINTF(("oboe_read: moving %d bytes from %p\n",
330				 sc->sc_lens[slot],
331				 sc->sc_recv_stores[slot]));
332			error = uiomove(sc->sc_recv_stores[slot],
333					sc->sc_lens[slot], uio);
334		}
335	}
336	sc->sc_saved--;
337	splx(s);
338
339	return (error);
340}
341
342static int
343oboe_write(void *h, struct uio *uio, int flag)
344{
345	struct oboe_softc *sc = h;
346	int error = 0;
347	int n;
348	int s = splir();
349
350	DPRINTF(("%s: sc=%p\n", __func__, sc));
351	while (sc->sc_txpending == TX_SLOTS) {
352		if (flag & IO_NDELAY) {
353			splx(s);
354			return (EWOULDBLOCK);
355		}
356		sc->sc_state |= OBOE_WSLP;
357		DPRINTF(("oboe_write: sleep\n"));
358		error = tsleep(&sc->sc_txs, PZERO | PCATCH, "oboewr", 0);
359		DPRINTF(("oboe_write: woke up, error=%d\n", error));
360		if (error) {
361			sc->sc_state &= ~OBOE_WSLP;
362			break;
363		}
364	}
365	if (error)
366		goto err;
367	if (sc->sc_taskfile->xmit[sc->sc_txs].control) {
368		DPRINTF(("oboe_write: slot overrun\n"));
369	}
370
371	n = irda_sir_frame(sc->sc_xmit_bufs[sc->sc_txs], TX_BUF_SZ, uio,
372			   sc->sc_ebofs);
373	if (n < 0) {
374		error = -n;
375		goto err;
376	}
377	sc->sc_taskfile->xmit[sc->sc_txs].len = n;
378
379	OUTB(sc, 0, OBOE_RST);
380	OUTB(sc, 0x1e, OBOE_REG_11);
381
382	sc->sc_taskfile->xmit[sc->sc_txs].control = 0x84;
383
384	/* XXX Need delay here??? */
385	delay(1000);
386
387	sc->sc_txpending++;
388	OUTB(sc, 0x80, OBOE_RST);
389	OUTB(sc, 1, OBOE_REG_9);
390	sc->sc_txs++;
391	sc->sc_txs %= TX_SLOTS;
392
393 err:
394	splx(s);
395	return (error);
396}
397
398static int
399oboe_set_params(void *h, struct irda_params *p)
400{
401	struct oboe_softc *sc = h;
402	int error;
403
404	if (p->speed > 0) {
405		error = oboe_setbaud(sc, p->speed);
406		if (error)
407			return (error);
408	}
409	sc->sc_ebofs = p->ebofs;
410
411	/* XXX ignore ebofs and maxsize for now */
412	return (0);
413}
414
415static int
416oboe_get_speeds(void *h, int *speeds)
417{
418	struct oboe_softc *sc = h;
419	*speeds = sc->sc_speeds;
420	return (0);
421}
422
423static int
424oboe_get_turnarounds(void *h, int *turnarounds)
425{
426#ifdef OBOE_DEBUG
427	struct oboe_softc *sc = h;
428	DPRINTF(("%s: sc=%p\n", __func__, sc));
429#endif
430
431	/* XXX Linux driver sets all bits */
432	*turnarounds = IRDA_TURNT_10000; /* 10ms */
433
434	return (0);
435}
436
437static int
438oboe_poll(void *h, int events, struct lwp *l)
439{
440	struct oboe_softc *sc = h;
441	int revents = 0;
442	int s;
443
444	DPRINTF(("%s: sc=%p\n", __func__, sc));
445
446	s = splir();
447	if (events & (POLLOUT | POLLWRNORM))
448		revents |= events & (POLLOUT | POLLWRNORM);
449	if (events & (POLLIN | POLLRDNORM)) {
450		if (sc->sc_saved > 0) {
451			DPRINTF(("%s: have data\n", __func__));
452			revents |= events & (POLLIN | POLLRDNORM);
453		} else {
454			DPRINTF(("%s: recording select\n", __func__));
455			selrecord(l, &sc->sc_rsel);
456		}
457	}
458	splx(s);
459
460	return (revents);
461}
462
463static void
464filt_oboerdetach(struct knote *kn)
465{
466	struct oboe_softc *sc = kn->kn_hook;
467	int s;
468
469	s = splir();
470	selremove_knote(&sc->sc_rsel, kn);
471	splx(s);
472}
473
474static int
475filt_oboeread(struct knote *kn, long hint)
476{
477	struct oboe_softc *sc = kn->kn_hook;
478
479	kn->kn_data = sc->sc_saved;
480	return (kn->kn_data > 0);
481}
482
483static const struct filterops oboeread_filtops = {
484	.f_flags = FILTEROP_ISFD,
485	.f_attach = NULL,
486	.f_detach = filt_oboerdetach,
487	.f_event = filt_oboeread,
488};
489
490static int
491oboe_kqfilter(void *h, struct knote *kn)
492{
493	struct oboe_softc *sc = h;
494	int s;
495
496	switch (kn->kn_filter) {
497	case EVFILT_READ:
498		kn->kn_fop = &oboeread_filtops;
499		kn->kn_hook = sc;
500		s = splir();
501		selrecord_knote(&sc->sc_rsel, kn);
502		splx(s);
503		break;
504
505	case EVFILT_WRITE:
506		kn->kn_fop = &seltrue_filtops;
507		break;
508	default:
509		return (EINVAL);
510	}
511
512	return (0);
513}
514
515static int
516oboe_reset(struct oboe_softc *sc)
517{
518#if 0
519	OUTB(sc, 0x00, OBOE_RST);
520	OUTB(sc, 0x80, OBOE_RST);
521#endif
522	return 0;
523}
524
525static int
526oboe_intr(void *p)
527{
528	struct oboe_softc *sc = p;
529	uint8_t irqstat	= INB(sc, OBOE_ISR);
530
531	if (!(irqstat & 0xf8))
532		return (0); /* Not for me? */
533
534	DPRINTF(("oboe_intr stat=0x%x\n", irqstat));
535
536	OUTB(sc, irqstat, OBOE_ISR);
537
538	if (irqstat & OBOE_ISR_RXDONE) {
539		while (sc->sc_taskfile->recv[sc->sc_rxs].control == 0) {
540			int len = sc->sc_taskfile->recv[sc->sc_rxs].len;
541			if (sc->sc_saved == RX_SLOTS) {
542				DPRINTF(("oboe_intr: all buffers filled\n"));
543				return 0;
544			}
545
546			if (len > 2)
547				len -= 2; /* JSP: skip check sum? */
548
549			DPRINTF(("oboe_intr: moving %d bytes to %p\n", len,
550				 sc->sc_recv_stores[sc->sc_rxs]));
551			memcpy(sc->sc_recv_stores[sc->sc_rxs],
552			       sc->sc_recv_bufs[sc->sc_rxs],
553			       len);
554			sc->sc_lens[sc->sc_rxs] = len;
555			sc->sc_saved++;
556#if 0
557			(void)b_to_q(sc->sc_recv_bufs[sc->sc_rxs],
558				     len, &sc->sc_q);
559#endif
560			sc->sc_taskfile->recv[sc->sc_rxs].control = 0x83;
561			sc->sc_taskfile->recv[sc->sc_rxs].len = 0x0;
562			sc->sc_rxs = (sc->sc_rxs + 1) % RX_SLOTS;
563			DPRINTF(("oboe_intr new rxs=%d\n", sc->sc_rxs));
564		}
565		DPRINTF(("oboe_intr no more frames available\n"));
566		if (sc->sc_state & OBOE_RSLP) {
567			DPRINTF(("oboe_intr changing state to ~OBOE_RSLP\n"));
568			sc->sc_state &= ~OBOE_RSLP;
569			DPRINTF(("oboe_intr: waking up reader\n"));
570			wakeup(&sc->sc_rxs);
571		}
572		selnotify(&sc->sc_rsel, 0, 0);
573		DPRINTF(("oboe_intr returning\n"));
574	}
575	if (irqstat & OBOE_ISR_TXDONE) {
576	        DPRINTF(("oboe_intr: write done\n"));
577		sc->sc_txpending--;
578		sc->sc_txpackets++;
579
580		if ((sc->sc_state & OBOE_CLOSING) && sc->sc_txpending == 0) {
581			wakeup(&sc->sc_state);
582			return 1;
583		}
584
585		if (sc->sc_state & OBOE_WSLP) {
586			DPRINTF(("oboe_intr changing state to ~OBOE_WSLP\n"));
587			sc->sc_state &= ~OBOE_WSLP;
588			DPRINTF(("oboe_intr: waking up writer\n"));
589			wakeup(&sc->sc_txs);
590		}
591	}
592	return (1);
593}
594
595/* XXX vtophys must go! */
596static void
597oboe_init_taskfile(struct oboe_softc *sc)
598{
599	int i;
600	int s = splir();
601
602	for (i = 0; i < TX_SLOTS; ++i) {
603		sc->sc_taskfile->xmit[i].len = 0;
604		sc->sc_taskfile->xmit[i].control = 0x00;
605		sc->sc_taskfile->xmit[i].buffer =
606		    vtophys((uintptr_t)sc->sc_xmit_bufs[i]);
607	}
608
609	for (i = 0; i < RX_SLOTS; ++i) {
610		sc->sc_taskfile->recv[i].len = 0;
611		sc->sc_taskfile->recv[i].control = 0x83;
612		sc->sc_taskfile->recv[i].buffer =
613		    vtophys((uintptr_t)sc->sc_recv_bufs[i]);
614	}
615
616	sc->sc_txpending = 0;
617
618	splx(s);
619}
620
621static int
622oboe_alloc_taskfile(struct oboe_softc *sc)
623{
624	int i;
625	/* XXX */
626	uintptr_t addr =
627	    (uintptr_t)malloc(OBOE_TASK_BUF_LEN, M_DEVBUF, M_WAITOK);
628
629	addr &= ~(sizeof (struct OboeTaskFile) - 1);
630	addr += sizeof (struct OboeTaskFile);
631	sc->sc_taskfile = (struct OboeTaskFile *) addr;
632
633	for (i = 0; i < TX_SLOTS; ++i) {
634		sc->sc_xmit_bufs[i] =
635			malloc(TX_BUF_SZ, M_DEVBUF, M_WAITOK);
636		sc->sc_xmit_stores[i] =
637			malloc(TX_BUF_SZ, M_DEVBUF, M_WAITOK);
638	}
639	for (i = 0; i < RX_SLOTS; ++i) {
640		sc->sc_recv_bufs[i] =
641			malloc(RX_BUF_SZ, M_DEVBUF, M_WAITOK);
642		sc->sc_recv_stores[i] =
643			malloc(RX_BUF_SZ, M_DEVBUF, M_WAITOK);
644	}
645
646	return 0;
647}
648
649static void
650oboe_startchip (struct oboe_softc *sc)
651{
652	uint32_t physaddr;
653
654	OUTB(sc, 0, OBOE_LOCK);
655	OUTB(sc, 0, OBOE_RST);
656	OUTB(sc, OBOE_NTR_VAL, OBOE_NTR);
657	OUTB(sc, 0xf0, OBOE_REG_D);
658	OUTB(sc, 0xff, OBOE_ISR);
659	OUTB(sc, 0x0f, OBOE_REG_1A);
660	OUTB(sc, 0xff, OBOE_REG_1B);
661
662	physaddr = vtophys((uintptr_t)sc->sc_taskfile);
663
664	OUTB(sc, (physaddr >> 0x0a) & 0xff, OBOE_TFP0);
665	OUTB(sc, (physaddr >> 0x12) & 0xff, OBOE_TFP1);
666	OUTB(sc, (physaddr >> 0x1a) & 0x3f, OBOE_TFP2);
667
668	OUTB(sc, 0x0e, OBOE_REG_11);
669	OUTB(sc, 0x80, OBOE_RST);
670
671	(void)oboe_setbaud(sc, 9600);
672
673	sc->sc_rxs = INB(sc, OBOE_RCVT);
674	if (sc->sc_rxs < 0 || sc->sc_rxs >= RX_SLOTS)
675		sc->sc_rxs = 0;
676	sc->sc_txs = INB(sc, OBOE_XMTT) - OBOE_XMTT_OFFSET;
677	if (sc->sc_txs < 0 || sc->sc_txs >= TX_SLOTS)
678		sc->sc_txs = 0;
679}
680
681static void
682oboe_stopchip (struct oboe_softc *sc)
683{
684	OUTB(sc, 0x0e, OBOE_REG_11);
685	OUTB(sc, 0x00, OBOE_RST);
686	OUTB(sc, 0x3f, OBOE_TFP2);     /* Write the taskfile address */
687	OUTB(sc, 0xff, OBOE_TFP1);
688	OUTB(sc, 0xff, OBOE_TFP0);
689	OUTB(sc, 0x0f, OBOE_REG_1B);
690	OUTB(sc, 0xff, OBOE_REG_1A);
691	OUTB(sc, 0x00, OBOE_ISR); /* XXX: should i do this to disable ints? */
692	OUTB(sc, 0x80, OBOE_RST);
693	OUTB(sc, 0x0e, OBOE_LOCK);
694}
695
696#define SPEEDCASE(speed, type, divisor) \
697case speed: \
698OUTB(sc, OBOE_PMDL_##type, OBOE_PMDL); \
699OUTB(sc, OBOE_SMDL_##type, OBOE_SMDL); \
700OUTB(sc, divisor, OBOE_UDIV); \
701break
702
703static int
704oboe_setbaud(struct oboe_softc *sc, int baud)
705{
706	int s;
707
708	DPRINTF(("oboe: setting baud to %d\n", baud));
709
710	s = splir();
711
712	switch (baud) {
713	SPEEDCASE(   2400, SIR, 0xbf);
714	SPEEDCASE(   9600, SIR, 0x2f);
715	SPEEDCASE(  19200, SIR, 0x17);
716	SPEEDCASE(  38400, SIR, 0x0b);
717	SPEEDCASE(  57600, SIR, 0x07);
718	SPEEDCASE( 115200, SIR, 0x03);
719	SPEEDCASE(1152000, MIR, 0x01);
720	SPEEDCASE(4000000, FIR, 0x00);
721	default:
722		DPRINTF(("oboe: cannot set speed to %d\n", baud));
723		splx(s);
724		return (EINVAL);
725	}
726
727	OUTB(sc, 0x00, OBOE_RST);
728	OUTB(sc, 0x80, OBOE_RST);
729	OUTB(sc, 0x01, OBOE_REG_9);
730
731	sc->sc_speed = baud;
732
733	splx(s);
734
735	return (0);
736}
737