uart_core.c revision 151792
1139749Simp/*-
2119815Smarcel * Copyright (c) 2003 Marcel Moolenaar
3119815Smarcel * All rights reserved.
4119815Smarcel *
5119815Smarcel * Redistribution and use in source and binary forms, with or without
6119815Smarcel * modification, are permitted provided that the following conditions
7119815Smarcel * are met:
8119815Smarcel *
9119815Smarcel * 1. Redistributions of source code must retain the above copyright
10119815Smarcel *    notice, this list of conditions and the following disclaimer.
11119815Smarcel * 2. Redistributions in binary form must reproduce the above copyright
12119815Smarcel *    notice, this list of conditions and the following disclaimer in the
13119815Smarcel *    documentation and/or other materials provided with the distribution.
14119815Smarcel *
15119815Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16119815Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17119815Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18119815Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19119815Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20119815Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21119815Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22119815Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23119815Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24119815Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25119815Smarcel */
26119815Smarcel
27119815Smarcel#include <sys/cdefs.h>
28119815Smarcel__FBSDID("$FreeBSD: head/sys/dev/uart/uart_core.c 151792 2005-10-28 06:30:39Z marcel $");
29119815Smarcel
30119815Smarcel#ifndef KLD_MODULE
31119815Smarcel#include "opt_comconsole.h"
32119815Smarcel#endif
33119815Smarcel
34119815Smarcel#include <sys/param.h>
35119815Smarcel#include <sys/systm.h>
36119815Smarcel#include <sys/bus.h>
37119815Smarcel#include <sys/conf.h>
38119815Smarcel#include <sys/cons.h>
39119815Smarcel#include <sys/fcntl.h>
40119815Smarcel#include <sys/interrupt.h>
41131921Smarcel#include <sys/kdb.h>
42119815Smarcel#include <sys/kernel.h>
43119815Smarcel#include <sys/malloc.h>
44119815Smarcel#include <sys/queue.h>
45119815Smarcel#include <sys/reboot.h>
46119815Smarcel#include <machine/bus.h>
47119815Smarcel#include <sys/rman.h>
48119815Smarcel#include <sys/termios.h>
49119815Smarcel#include <sys/tty.h>
50119815Smarcel#include <machine/resource.h>
51119815Smarcel#include <machine/stdarg.h>
52119815Smarcel
53119815Smarcel#include <dev/uart/uart.h>
54119815Smarcel#include <dev/uart/uart_bus.h>
55119815Smarcel#include <dev/uart/uart_cpu.h>
56119815Smarcel
57119815Smarcel#include "uart_if.h"
58119815Smarcel
59119815Smarceldevclass_t uart_devclass;
60119815Smarcelchar uart_driver_name[] = "uart";
61119815Smarcel
62119815SmarcelSLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs =
63119815Smarcel    SLIST_HEAD_INITIALIZER(uart_sysdevs);
64119815Smarcel
65119815SmarcelMALLOC_DEFINE(M_UART, "UART", "UART driver");
66119815Smarcel
67119815Smarcelvoid
68119815Smarceluart_add_sysdev(struct uart_devinfo *di)
69119815Smarcel{
70119815Smarcel	SLIST_INSERT_HEAD(&uart_sysdevs, di, next);
71119815Smarcel}
72119815Smarcel
73119815Smarcel/*
74119815Smarcel * A break condition has been detected. We treat the break condition as
75119815Smarcel * a special case that should not happen during normal operation. When
76119815Smarcel * the break condition is to be passed to higher levels in the form of
77119815Smarcel * a NUL character, we really want the break to be in the right place in
78119815Smarcel * the input stream. The overhead to achieve that is not in relation to
79119815Smarcel * the exceptional nature of the break condition, so we permit ourselves
80119815Smarcel * to be sloppy.
81119815Smarcel */
82119815Smarcelstatic void
83119815Smarceluart_intr_break(struct uart_softc *sc)
84119815Smarcel{
85119815Smarcel
86131921Smarcel#if defined(KDB) && defined(BREAK_TO_DEBUGGER)
87119815Smarcel	if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
88131921Smarcel		kdb_enter("Line break on console");
89119815Smarcel		return;
90119815Smarcel	}
91119815Smarcel#endif
92119815Smarcel	if (sc->sc_opened)
93119815Smarcel		atomic_set_32(&sc->sc_ttypend, UART_IPEND_BREAK);
94119815Smarcel}
95119815Smarcel
96119815Smarcel/*
97119815Smarcel * Handle a receiver overrun situation. We lost at least 1 byte in the
98119815Smarcel * input stream and it's our job to contain the situation. We grab as
99119815Smarcel * much of the data we can, but otherwise flush the receiver FIFO to
100119815Smarcel * create some breathing room. The net effect is that we avoid the
101119815Smarcel * overrun condition to happen for the next X characters, where X is
102119815Smarcel * related to the FIFO size at the cost of loosing data right away.
103119815Smarcel * So, instead of having multiple overrun interrupts in close proximity
104119815Smarcel * to each other and possibly pessimizing UART interrupt latency for
105119815Smarcel * other UARTs in a multiport configuration, we create a longer segment
106119815Smarcel * of missing characters by freeing up the FIFO.
107119815Smarcel * Each overrun condition is marked in the input buffer by a token. The
108119815Smarcel * token represents the loss of at least one, but possible more bytes in
109119815Smarcel * the input stream.
110119815Smarcel */
111119815Smarcelstatic void
112119815Smarceluart_intr_overrun(struct uart_softc *sc)
113119815Smarcel{
114119815Smarcel
115119815Smarcel	if (sc->sc_opened) {
116119815Smarcel		UART_RECEIVE(sc);
117119815Smarcel		if (uart_rx_put(sc, UART_STAT_OVERRUN))
118119815Smarcel			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
119119815Smarcel		atomic_set_32(&sc->sc_ttypend, UART_IPEND_RXREADY);
120119815Smarcel	}
121119815Smarcel	UART_FLUSH(sc, UART_FLUSH_RECEIVER);
122119815Smarcel}
123119815Smarcel
124119815Smarcel/*
125119815Smarcel * Received data ready.
126119815Smarcel */
127119815Smarcelstatic void
128119815Smarceluart_intr_rxready(struct uart_softc *sc)
129119815Smarcel{
130119815Smarcel	int rxp;
131119815Smarcel
132119815Smarcel	rxp = sc->sc_rxput;
133119815Smarcel	UART_RECEIVE(sc);
134131921Smarcel#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
135119815Smarcel	if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
136119815Smarcel		while (rxp != sc->sc_rxput) {
137131921Smarcel			if (kdb_alt_break(sc->sc_rxbuf[rxp++], &sc->sc_altbrk))
138131921Smarcel				kdb_enter("Break sequence on console");
139119815Smarcel			if (rxp == sc->sc_rxbufsz)
140119815Smarcel				rxp = 0;
141119815Smarcel		}
142119815Smarcel	}
143119815Smarcel#endif
144119815Smarcel	if (sc->sc_opened)
145119815Smarcel		atomic_set_32(&sc->sc_ttypend, UART_IPEND_RXREADY);
146119815Smarcel	else
147119815Smarcel		sc->sc_rxput = sc->sc_rxget;	/* Ignore received data. */
148119815Smarcel}
149119815Smarcel
150119815Smarcel/*
151119815Smarcel * Line or modem status change (OOB signalling).
152119815Smarcel * We pass the signals to the software interrupt handler for further
153119815Smarcel * processing. Note that we merge the delta bits, but set the state
154119815Smarcel * bits. This is to avoid loosing state transitions due to having more
155119815Smarcel * than 1 hardware interrupt between software interrupts.
156119815Smarcel */
157119815Smarcelstatic void
158119815Smarceluart_intr_sigchg(struct uart_softc *sc)
159119815Smarcel{
160119815Smarcel	int new, old, sig;
161119815Smarcel
162119815Smarcel	sig = UART_GETSIG(sc);
163119996Smarcel
164119996Smarcel	if (sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) {
165119996Smarcel		if (sig & UART_SIG_DPPS) {
166119996Smarcel			pps_capture(&sc->sc_pps);
167119996Smarcel			pps_event(&sc->sc_pps, (sig & UART_SIG_PPS) ?
168119996Smarcel			    PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
169119996Smarcel		}
170119996Smarcel	}
171119996Smarcel
172119815Smarcel	do {
173119815Smarcel		old = sc->sc_ttypend;
174119815Smarcel		new = old & ~UART_SIGMASK_STATE;
175119815Smarcel		new |= sig & UART_IPEND_SIGMASK;
176119815Smarcel		new |= UART_IPEND_SIGCHG;
177119815Smarcel	} while (!atomic_cmpset_32(&sc->sc_ttypend, old, new));
178119815Smarcel}
179119815Smarcel
180119815Smarcel/*
181119815Smarcel * The transmitter can accept more data.
182119815Smarcel */
183119815Smarcelstatic void
184119815Smarceluart_intr_txidle(struct uart_softc *sc)
185119815Smarcel{
186119815Smarcel	if (sc->sc_txbusy) {
187119815Smarcel		sc->sc_txbusy = 0;
188119815Smarcel		atomic_set_32(&sc->sc_ttypend, UART_IPEND_TXIDLE);
189119815Smarcel	}
190119815Smarcel}
191119815Smarcel
192119815Smarcelstatic void
193119815Smarceluart_intr(void *arg)
194119815Smarcel{
195119815Smarcel	struct uart_softc *sc = arg;
196119815Smarcel	int ipend;
197119815Smarcel
198119815Smarcel	if (sc->sc_leaving)
199119815Smarcel		return;
200119815Smarcel
201120146Smarcel	do {
202120146Smarcel		ipend = UART_IPEND(sc);
203120146Smarcel		if (ipend == 0)
204120146Smarcel			break;
205120146Smarcel		if (ipend & UART_IPEND_OVERRUN)
206120146Smarcel			uart_intr_overrun(sc);
207120146Smarcel		if (ipend & UART_IPEND_BREAK)
208120146Smarcel			uart_intr_break(sc);
209120146Smarcel		if (ipend & UART_IPEND_RXREADY)
210120146Smarcel			uart_intr_rxready(sc);
211120146Smarcel		if (ipend & UART_IPEND_SIGCHG)
212120146Smarcel			uart_intr_sigchg(sc);
213120146Smarcel		if (ipend & UART_IPEND_TXIDLE)
214120146Smarcel			uart_intr_txidle(sc);
215120146Smarcel	} while (1);
216119815Smarcel
217119815Smarcel	if (sc->sc_opened && sc->sc_ttypend != 0)
218119815Smarcel		swi_sched(sc->sc_softih, 0);
219119815Smarcel}
220119815Smarcel
221119815Smarcelint
222120452Smarceluart_bus_probe(device_t dev, int regshft, int rclk, int rid, int chan)
223119815Smarcel{
224119815Smarcel	struct uart_softc *sc;
225119815Smarcel	struct uart_devinfo *sysdev;
226119815Smarcel	int error;
227119815Smarcel
228119815Smarcel	/*
229119815Smarcel	 * Initialize the instance. Note that the instance (=softc) does
230119815Smarcel	 * not necessarily match the hardware specific softc. We can't do
231119815Smarcel	 * anything about it now, because we may not attach to the device.
232119815Smarcel	 * Hardware drivers cannot use any of the class specific fields
233119815Smarcel	 * while probing.
234119815Smarcel	 */
235119815Smarcel	sc = device_get_softc(dev);
236119815Smarcel	kobj_init((kobj_t)sc, (kobj_class_t)sc->sc_class);
237119815Smarcel	sc->sc_dev = dev;
238119815Smarcel	if (device_get_desc(dev) == NULL)
239119815Smarcel		device_set_desc(dev, sc->sc_class->name);
240119815Smarcel
241119815Smarcel	/*
242119815Smarcel	 * Allocate the register resource. We assume that all UARTs have
243119815Smarcel	 * a single register window in either I/O port space or memory
244119815Smarcel	 * mapped I/O space. Any UART that needs multiple windows will
245119815Smarcel	 * consequently not be supported by this driver as-is. We try I/O
246119815Smarcel	 * port space first because that's the common case.
247119815Smarcel	 */
248119815Smarcel	sc->sc_rrid = rid;
249119815Smarcel	sc->sc_rtype = SYS_RES_IOPORT;
250119815Smarcel	sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid,
251119815Smarcel	    0, ~0, sc->sc_class->uc_range, RF_ACTIVE);
252119815Smarcel	if (sc->sc_rres == NULL) {
253119815Smarcel		sc->sc_rrid = rid;
254119815Smarcel		sc->sc_rtype = SYS_RES_MEMORY;
255119815Smarcel		sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype,
256119815Smarcel		    &sc->sc_rrid, 0, ~0, sc->sc_class->uc_range, RF_ACTIVE);
257119815Smarcel		if (sc->sc_rres == NULL)
258119815Smarcel			return (ENXIO);
259119815Smarcel	}
260119815Smarcel
261119815Smarcel	/*
262119815Smarcel	 * Fill in the bus access structure and compare this device with
263119815Smarcel	 * a possible console device and/or a debug port. We set the flags
264119815Smarcel	 * in the softc so that the hardware dependent probe can adjust
265119815Smarcel	 * accordingly. In general, you don't want to permanently disrupt
266119815Smarcel	 * console I/O.
267119815Smarcel	 */
268119815Smarcel	sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres);
269119815Smarcel	sc->sc_bas.bst = rman_get_bustag(sc->sc_rres);
270120452Smarcel	sc->sc_bas.chan = chan;
271119815Smarcel	sc->sc_bas.regshft = regshft;
272119815Smarcel	sc->sc_bas.rclk = (rclk == 0) ? sc->sc_class->uc_rclk : rclk;
273119815Smarcel
274119815Smarcel	SLIST_FOREACH(sysdev, &uart_sysdevs, next) {
275120452Smarcel		if (chan == sysdev->bas.chan &&
276120452Smarcel		    uart_cpu_eqres(&sc->sc_bas, &sysdev->bas)) {
277119815Smarcel			/* XXX check if ops matches class. */
278119815Smarcel			sc->sc_sysdev = sysdev;
279119815Smarcel			break;
280119815Smarcel		}
281119815Smarcel	}
282119815Smarcel
283119815Smarcel	error = UART_PROBE(sc);
284119815Smarcel	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
285151792Smarcel	return ((error) ? error : BUS_PROBE_DEFAULT);
286119815Smarcel}
287119815Smarcel
288119815Smarcelint
289119815Smarceluart_bus_attach(device_t dev)
290119815Smarcel{
291119815Smarcel	struct uart_softc *sc, *sc0;
292119815Smarcel	const char *sep;
293119815Smarcel	int error;
294119815Smarcel
295119815Smarcel	/*
296119815Smarcel	 * The sc_class field defines the type of UART we're going to work
297119815Smarcel	 * with and thus the size of the softc. Replace the generic softc
298119815Smarcel	 * with one that matches the UART now that we're certain we handle
299119815Smarcel	 * the device.
300119815Smarcel	 */
301119815Smarcel	sc0 = device_get_softc(dev);
302119815Smarcel	if (sc0->sc_class->size > sizeof(*sc)) {
303119815Smarcel		sc = malloc(sc0->sc_class->size, M_UART, M_WAITOK|M_ZERO);
304119815Smarcel		bcopy(sc0, sc, sizeof(*sc));
305119815Smarcel		device_set_softc(dev, sc);
306119815Smarcel	} else
307119815Smarcel		sc = sc0;
308119815Smarcel
309119815Smarcel	/*
310119815Smarcel	 * Protect ourselves against interrupts while we're not completely
311119815Smarcel	 * finished attaching and initializing. We don't expect interrupts
312119815Smarcel	 * until after UART_ATTACH() though.
313119815Smarcel	 */
314119815Smarcel	sc->sc_leaving = 1;
315119815Smarcel
316120143Smarcel	mtx_init(&sc->sc_hwmtx, "uart_hwmtx", NULL, MTX_SPIN);
317120143Smarcel
318119815Smarcel	/*
319119815Smarcel	 * Re-allocate. We expect that the softc contains the information
320119815Smarcel	 * collected by uart_bus_probe() intact.
321119815Smarcel	 */
322119815Smarcel	sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid,
323119815Smarcel	    0, ~0, sc->sc_class->uc_range, RF_ACTIVE);
324143025Smarius	if (sc->sc_rres == NULL) {
325143025Smarius		mtx_destroy(&sc->sc_hwmtx);
326119815Smarcel		return (ENXIO);
327143025Smarius	}
328120380Snyan	sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres);
329120380Snyan	sc->sc_bas.bst = rman_get_bustag(sc->sc_rres);
330120380Snyan
331119815Smarcel	sc->sc_irid = 0;
332127135Snjl	sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid,
333143025Smarius	    RF_ACTIVE | RF_SHAREABLE);
334119815Smarcel	if (sc->sc_ires != NULL) {
335119815Smarcel		error = BUS_SETUP_INTR(device_get_parent(dev), dev,
336119815Smarcel		    sc->sc_ires, INTR_TYPE_TTY | INTR_FAST, uart_intr,
337119815Smarcel		    sc, &sc->sc_icookie);
338119815Smarcel		if (error)
339119815Smarcel			error = BUS_SETUP_INTR(device_get_parent(dev), dev,
340128909Smarcel			    sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE,
341128909Smarcel			    uart_intr, sc, &sc->sc_icookie);
342119815Smarcel		else
343119815Smarcel			sc->sc_fastintr = 1;
344119815Smarcel
345119815Smarcel		if (error) {
346119815Smarcel			device_printf(dev, "could not activate interrupt\n");
347119815Smarcel			bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
348119815Smarcel			    sc->sc_ires);
349119815Smarcel			sc->sc_ires = NULL;
350119815Smarcel		}
351119815Smarcel	}
352119815Smarcel	if (sc->sc_ires == NULL) {
353119815Smarcel		/* XXX no interrupt resource. Force polled mode. */
354119815Smarcel		sc->sc_polled = 1;
355119815Smarcel	}
356119815Smarcel
357119815Smarcel	sc->sc_rxbufsz = IBUFSIZ;
358119815Smarcel	sc->sc_rxbuf = malloc(sc->sc_rxbufsz * sizeof(*sc->sc_rxbuf),
359119815Smarcel	    M_UART, M_WAITOK);
360119815Smarcel	sc->sc_txbuf = malloc(sc->sc_txfifosz * sizeof(*sc->sc_txbuf),
361119815Smarcel	    M_UART, M_WAITOK);
362119815Smarcel
363119815Smarcel	error = UART_ATTACH(sc);
364119815Smarcel	if (error)
365119815Smarcel		goto fail;
366119815Smarcel
367119815Smarcel	if (sc->sc_hwiflow || sc->sc_hwoflow) {
368119815Smarcel		sep = "";
369119815Smarcel		device_print_prettyname(dev);
370119815Smarcel		if (sc->sc_hwiflow) {
371119815Smarcel			printf("%sRTS iflow", sep);
372119815Smarcel			sep = ", ";
373119815Smarcel		}
374119815Smarcel		if (sc->sc_hwoflow) {
375119815Smarcel			printf("%sCTS oflow", sep);
376119815Smarcel			sep = ", ";
377119815Smarcel		}
378119815Smarcel		printf("\n");
379119815Smarcel	}
380119815Smarcel
381119815Smarcel	if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) {
382119815Smarcel		sep = "";
383119815Smarcel		device_print_prettyname(dev);
384119815Smarcel		if (sc->sc_fastintr) {
385119815Smarcel			printf("%sfast interrupt", sep);
386119815Smarcel			sep = ", ";
387119815Smarcel		}
388119815Smarcel		if (sc->sc_polled) {
389119815Smarcel			printf("%spolled mode", sep);
390119815Smarcel			sep = ", ";
391119815Smarcel		}
392119815Smarcel		printf("\n");
393119815Smarcel	}
394119815Smarcel
395119815Smarcel	if (sc->sc_sysdev != NULL) {
396137706Smarcel		if (sc->sc_sysdev->baudrate == 0) {
397137706Smarcel			if (UART_IOCTL(sc, UART_IOCTL_BAUD,
398137706Smarcel			    (intptr_t)&sc->sc_sysdev->baudrate) != 0)
399137706Smarcel				sc->sc_sysdev->baudrate = -1;
400137706Smarcel		}
401119815Smarcel		switch (sc->sc_sysdev->type) {
402119815Smarcel		case UART_DEV_CONSOLE:
403119815Smarcel			device_printf(dev, "console");
404119815Smarcel			break;
405119815Smarcel		case UART_DEV_DBGPORT:
406119815Smarcel			device_printf(dev, "debug port");
407119815Smarcel			break;
408119815Smarcel		case UART_DEV_KEYBOARD:
409119815Smarcel			device_printf(dev, "keyboard");
410119815Smarcel			break;
411119815Smarcel		default:
412119815Smarcel			device_printf(dev, "unknown system device");
413119815Smarcel			break;
414119815Smarcel		}
415119815Smarcel		printf(" (%d,%c,%d,%d)\n", sc->sc_sysdev->baudrate,
416119815Smarcel		    "noems"[sc->sc_sysdev->parity], sc->sc_sysdev->databits,
417119815Smarcel		    sc->sc_sysdev->stopbits);
418119815Smarcel	}
419119815Smarcel
420119996Smarcel	sc->sc_pps.ppscap = PPS_CAPTUREBOTH;
421119996Smarcel	pps_init(&sc->sc_pps);
422119996Smarcel
423119815Smarcel	error = (sc->sc_sysdev != NULL && sc->sc_sysdev->attach != NULL)
424119815Smarcel	    ? (*sc->sc_sysdev->attach)(sc) : uart_tty_attach(sc);
425119815Smarcel	if (error)
426119815Smarcel		goto fail;
427119815Smarcel
428119815Smarcel	sc->sc_leaving = 0;
429119815Smarcel	uart_intr(sc);
430119815Smarcel	return (0);
431119815Smarcel
432119815Smarcel fail:
433119815Smarcel	free(sc->sc_txbuf, M_UART);
434119815Smarcel	free(sc->sc_rxbuf, M_UART);
435119815Smarcel
436119815Smarcel	if (sc->sc_ires != NULL) {
437119815Smarcel		bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
438119815Smarcel		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
439119815Smarcel		    sc->sc_ires);
440119815Smarcel	}
441119815Smarcel	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
442119815Smarcel
443143025Smarius	mtx_destroy(&sc->sc_hwmtx);
444143025Smarius
445119815Smarcel	return (error);
446119815Smarcel}
447119815Smarcel
448119815Smarcelint
449119815Smarceluart_bus_detach(device_t dev)
450119815Smarcel{
451119815Smarcel	struct uart_softc *sc;
452119815Smarcel
453119815Smarcel	sc = device_get_softc(dev);
454119815Smarcel
455119815Smarcel	sc->sc_leaving = 1;
456119815Smarcel
457119815Smarcel	UART_DETACH(sc);
458119815Smarcel
459119815Smarcel	if (sc->sc_sysdev != NULL && sc->sc_sysdev->detach != NULL)
460119815Smarcel		(*sc->sc_sysdev->detach)(sc);
461119815Smarcel	else
462119815Smarcel		uart_tty_detach(sc);
463119815Smarcel
464119815Smarcel	free(sc->sc_txbuf, M_UART);
465119815Smarcel	free(sc->sc_rxbuf, M_UART);
466119815Smarcel
467119815Smarcel	if (sc->sc_ires != NULL) {
468119815Smarcel		bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
469119815Smarcel		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
470119815Smarcel		    sc->sc_ires);
471119815Smarcel	}
472119815Smarcel	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
473119815Smarcel
474143025Smarius	mtx_destroy(&sc->sc_hwmtx);
475143025Smarius
476119815Smarcel	if (sc->sc_class->size > sizeof(*sc)) {
477119815Smarcel		device_set_softc(dev, NULL);
478119815Smarcel		free(sc, M_UART);
479119815Smarcel	} else
480119815Smarcel		device_set_softc(dev, NULL);
481119815Smarcel
482119815Smarcel	return (0);
483119815Smarcel}
484