uart_core.c revision 127135
1119815Smarcel/*
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 127135 2004-03-17 17:50:55Z njl $");
29119815Smarcel
30119815Smarcel#ifndef KLD_MODULE
31119815Smarcel#include "opt_comconsole.h"
32119815Smarcel#include "opt_ddb.h"
33119815Smarcel#endif
34119815Smarcel
35119815Smarcel#include <sys/param.h>
36119815Smarcel#include <sys/systm.h>
37119815Smarcel#include <sys/bus.h>
38119815Smarcel#include <sys/conf.h>
39119815Smarcel#include <sys/cons.h>
40119815Smarcel#include <sys/fcntl.h>
41119815Smarcel#include <sys/interrupt.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 <ddb/ddb.h>
54119815Smarcel
55119815Smarcel#include <dev/uart/uart.h>
56119815Smarcel#include <dev/uart/uart_bus.h>
57119815Smarcel#include <dev/uart/uart_cpu.h>
58119815Smarcel
59119815Smarcel#include "uart_if.h"
60119815Smarcel
61119815Smarceldevclass_t uart_devclass;
62119815Smarcelchar uart_driver_name[] = "uart";
63119815Smarcel
64119815SmarcelSLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs =
65119815Smarcel    SLIST_HEAD_INITIALIZER(uart_sysdevs);
66119815Smarcel
67119815SmarcelMALLOC_DEFINE(M_UART, "UART", "UART driver");
68119815Smarcel
69119815Smarcelvoid
70119815Smarceluart_add_sysdev(struct uart_devinfo *di)
71119815Smarcel{
72119815Smarcel	SLIST_INSERT_HEAD(&uart_sysdevs, di, next);
73119815Smarcel}
74119815Smarcel
75119815Smarcel/*
76119815Smarcel * A break condition has been detected. We treat the break condition as
77119815Smarcel * a special case that should not happen during normal operation. When
78119815Smarcel * the break condition is to be passed to higher levels in the form of
79119815Smarcel * a NUL character, we really want the break to be in the right place in
80119815Smarcel * the input stream. The overhead to achieve that is not in relation to
81119815Smarcel * the exceptional nature of the break condition, so we permit ourselves
82119815Smarcel * to be sloppy.
83119815Smarcel */
84119815Smarcelstatic void
85119815Smarceluart_intr_break(struct uart_softc *sc)
86119815Smarcel{
87119815Smarcel
88119815Smarcel#if defined(DDB) && defined(BREAK_TO_DEBUGGER)
89119815Smarcel	if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
90119815Smarcel		breakpoint();
91119815Smarcel		return;
92119815Smarcel	}
93119815Smarcel#endif
94119815Smarcel	if (sc->sc_opened)
95119815Smarcel		atomic_set_32(&sc->sc_ttypend, UART_IPEND_BREAK);
96119815Smarcel}
97119815Smarcel
98119815Smarcel/*
99119815Smarcel * Handle a receiver overrun situation. We lost at least 1 byte in the
100119815Smarcel * input stream and it's our job to contain the situation. We grab as
101119815Smarcel * much of the data we can, but otherwise flush the receiver FIFO to
102119815Smarcel * create some breathing room. The net effect is that we avoid the
103119815Smarcel * overrun condition to happen for the next X characters, where X is
104119815Smarcel * related to the FIFO size at the cost of loosing data right away.
105119815Smarcel * So, instead of having multiple overrun interrupts in close proximity
106119815Smarcel * to each other and possibly pessimizing UART interrupt latency for
107119815Smarcel * other UARTs in a multiport configuration, we create a longer segment
108119815Smarcel * of missing characters by freeing up the FIFO.
109119815Smarcel * Each overrun condition is marked in the input buffer by a token. The
110119815Smarcel * token represents the loss of at least one, but possible more bytes in
111119815Smarcel * the input stream.
112119815Smarcel */
113119815Smarcelstatic void
114119815Smarceluart_intr_overrun(struct uart_softc *sc)
115119815Smarcel{
116119815Smarcel
117119815Smarcel	if (sc->sc_opened) {
118119815Smarcel		UART_RECEIVE(sc);
119119815Smarcel		if (uart_rx_put(sc, UART_STAT_OVERRUN))
120119815Smarcel			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
121119815Smarcel		atomic_set_32(&sc->sc_ttypend, UART_IPEND_RXREADY);
122119815Smarcel	}
123119815Smarcel	UART_FLUSH(sc, UART_FLUSH_RECEIVER);
124119815Smarcel}
125119815Smarcel
126119815Smarcel/*
127119815Smarcel * Received data ready.
128119815Smarcel */
129119815Smarcelstatic void
130119815Smarceluart_intr_rxready(struct uart_softc *sc)
131119815Smarcel{
132119815Smarcel	int rxp;
133119815Smarcel
134119815Smarcel	rxp = sc->sc_rxput;
135119815Smarcel	UART_RECEIVE(sc);
136119815Smarcel#if defined(DDB) && defined(ALT_BREAK_TO_DEBUGGER)
137119815Smarcel	if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
138119815Smarcel		while (rxp != sc->sc_rxput) {
139119815Smarcel			if (db_alt_break(sc->sc_rxbuf[rxp++], &sc->sc_altbrk))
140119815Smarcel				breakpoint();
141119815Smarcel			if (rxp == sc->sc_rxbufsz)
142119815Smarcel				rxp = 0;
143119815Smarcel		}
144119815Smarcel	}
145119815Smarcel#endif
146119815Smarcel	if (sc->sc_opened)
147119815Smarcel		atomic_set_32(&sc->sc_ttypend, UART_IPEND_RXREADY);
148119815Smarcel	else
149119815Smarcel		sc->sc_rxput = sc->sc_rxget;	/* Ignore received data. */
150119815Smarcel}
151119815Smarcel
152119815Smarcel/*
153119815Smarcel * Line or modem status change (OOB signalling).
154119815Smarcel * We pass the signals to the software interrupt handler for further
155119815Smarcel * processing. Note that we merge the delta bits, but set the state
156119815Smarcel * bits. This is to avoid loosing state transitions due to having more
157119815Smarcel * than 1 hardware interrupt between software interrupts.
158119815Smarcel */
159119815Smarcelstatic void
160119815Smarceluart_intr_sigchg(struct uart_softc *sc)
161119815Smarcel{
162119815Smarcel	int new, old, sig;
163119815Smarcel
164119815Smarcel	sig = UART_GETSIG(sc);
165119996Smarcel
166119996Smarcel	if (sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) {
167119996Smarcel		if (sig & UART_SIG_DPPS) {
168119996Smarcel			pps_capture(&sc->sc_pps);
169119996Smarcel			pps_event(&sc->sc_pps, (sig & UART_SIG_PPS) ?
170119996Smarcel			    PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
171119996Smarcel		}
172119996Smarcel	}
173119996Smarcel
174119815Smarcel	do {
175119815Smarcel		old = sc->sc_ttypend;
176119815Smarcel		new = old & ~UART_SIGMASK_STATE;
177119815Smarcel		new |= sig & UART_IPEND_SIGMASK;
178119815Smarcel		new |= UART_IPEND_SIGCHG;
179119815Smarcel	} while (!atomic_cmpset_32(&sc->sc_ttypend, old, new));
180119815Smarcel}
181119815Smarcel
182119815Smarcel/*
183119815Smarcel * The transmitter can accept more data.
184119815Smarcel */
185119815Smarcelstatic void
186119815Smarceluart_intr_txidle(struct uart_softc *sc)
187119815Smarcel{
188119815Smarcel	if (sc->sc_txbusy) {
189119815Smarcel		sc->sc_txbusy = 0;
190119815Smarcel		atomic_set_32(&sc->sc_ttypend, UART_IPEND_TXIDLE);
191119815Smarcel	}
192119815Smarcel}
193119815Smarcel
194119815Smarcelstatic void
195119815Smarceluart_intr(void *arg)
196119815Smarcel{
197119815Smarcel	struct uart_softc *sc = arg;
198119815Smarcel	int ipend;
199119815Smarcel
200119815Smarcel	if (sc->sc_leaving)
201119815Smarcel		return;
202119815Smarcel
203120146Smarcel	do {
204120146Smarcel		ipend = UART_IPEND(sc);
205120146Smarcel		if (ipend == 0)
206120146Smarcel			break;
207120146Smarcel		if (ipend & UART_IPEND_OVERRUN)
208120146Smarcel			uart_intr_overrun(sc);
209120146Smarcel		if (ipend & UART_IPEND_BREAK)
210120146Smarcel			uart_intr_break(sc);
211120146Smarcel		if (ipend & UART_IPEND_RXREADY)
212120146Smarcel			uart_intr_rxready(sc);
213120146Smarcel		if (ipend & UART_IPEND_SIGCHG)
214120146Smarcel			uart_intr_sigchg(sc);
215120146Smarcel		if (ipend & UART_IPEND_TXIDLE)
216120146Smarcel			uart_intr_txidle(sc);
217120146Smarcel	} while (1);
218119815Smarcel
219119815Smarcel	if (sc->sc_opened && sc->sc_ttypend != 0)
220119815Smarcel		swi_sched(sc->sc_softih, 0);
221119815Smarcel}
222119815Smarcel
223119815Smarcelint
224120452Smarceluart_bus_probe(device_t dev, int regshft, int rclk, int rid, int chan)
225119815Smarcel{
226119815Smarcel	struct uart_softc *sc;
227119815Smarcel	struct uart_devinfo *sysdev;
228119815Smarcel	int error;
229119815Smarcel
230119815Smarcel	/*
231119815Smarcel	 * Initialize the instance. Note that the instance (=softc) does
232119815Smarcel	 * not necessarily match the hardware specific softc. We can't do
233119815Smarcel	 * anything about it now, because we may not attach to the device.
234119815Smarcel	 * Hardware drivers cannot use any of the class specific fields
235119815Smarcel	 * while probing.
236119815Smarcel	 */
237119815Smarcel	sc = device_get_softc(dev);
238119815Smarcel	kobj_init((kobj_t)sc, (kobj_class_t)sc->sc_class);
239119815Smarcel	sc->sc_dev = dev;
240119815Smarcel	if (device_get_desc(dev) == NULL)
241119815Smarcel		device_set_desc(dev, sc->sc_class->name);
242119815Smarcel
243119815Smarcel	/*
244119815Smarcel	 * Allocate the register resource. We assume that all UARTs have
245119815Smarcel	 * a single register window in either I/O port space or memory
246119815Smarcel	 * mapped I/O space. Any UART that needs multiple windows will
247119815Smarcel	 * consequently not be supported by this driver as-is. We try I/O
248119815Smarcel	 * port space first because that's the common case.
249119815Smarcel	 */
250119815Smarcel	sc->sc_rrid = rid;
251119815Smarcel	sc->sc_rtype = SYS_RES_IOPORT;
252119815Smarcel	sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid,
253119815Smarcel	    0, ~0, sc->sc_class->uc_range, RF_ACTIVE);
254119815Smarcel	if (sc->sc_rres == NULL) {
255119815Smarcel		sc->sc_rrid = rid;
256119815Smarcel		sc->sc_rtype = SYS_RES_MEMORY;
257119815Smarcel		sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype,
258119815Smarcel		    &sc->sc_rrid, 0, ~0, sc->sc_class->uc_range, RF_ACTIVE);
259119815Smarcel		if (sc->sc_rres == NULL)
260119815Smarcel			return (ENXIO);
261119815Smarcel	}
262119815Smarcel
263119815Smarcel	/*
264119815Smarcel	 * Fill in the bus access structure and compare this device with
265119815Smarcel	 * a possible console device and/or a debug port. We set the flags
266119815Smarcel	 * in the softc so that the hardware dependent probe can adjust
267119815Smarcel	 * accordingly. In general, you don't want to permanently disrupt
268119815Smarcel	 * console I/O.
269119815Smarcel	 */
270119815Smarcel	sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres);
271119815Smarcel	sc->sc_bas.bst = rman_get_bustag(sc->sc_rres);
272120452Smarcel	sc->sc_bas.chan = chan;
273119815Smarcel	sc->sc_bas.regshft = regshft;
274119815Smarcel	sc->sc_bas.rclk = (rclk == 0) ? sc->sc_class->uc_rclk : rclk;
275119815Smarcel
276119815Smarcel	SLIST_FOREACH(sysdev, &uart_sysdevs, next) {
277120452Smarcel		if (chan == sysdev->bas.chan &&
278120452Smarcel		    uart_cpu_eqres(&sc->sc_bas, &sysdev->bas)) {
279119815Smarcel			/* XXX check if ops matches class. */
280119815Smarcel			sc->sc_sysdev = sysdev;
281119815Smarcel			break;
282119815Smarcel		}
283119815Smarcel	}
284119815Smarcel
285119815Smarcel	error = UART_PROBE(sc);
286119815Smarcel	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
287119815Smarcel	return (error);
288119815Smarcel}
289119815Smarcel
290119815Smarcelint
291119815Smarceluart_bus_attach(device_t dev)
292119815Smarcel{
293119815Smarcel	struct uart_softc *sc, *sc0;
294119815Smarcel	const char *sep;
295119815Smarcel	int error;
296119815Smarcel
297119815Smarcel	/*
298119815Smarcel	 * The sc_class field defines the type of UART we're going to work
299119815Smarcel	 * with and thus the size of the softc. Replace the generic softc
300119815Smarcel	 * with one that matches the UART now that we're certain we handle
301119815Smarcel	 * the device.
302119815Smarcel	 */
303119815Smarcel	sc0 = device_get_softc(dev);
304119815Smarcel	if (sc0->sc_class->size > sizeof(*sc)) {
305119815Smarcel		sc = malloc(sc0->sc_class->size, M_UART, M_WAITOK|M_ZERO);
306119815Smarcel		bcopy(sc0, sc, sizeof(*sc));
307119815Smarcel		device_set_softc(dev, sc);
308119815Smarcel	} else
309119815Smarcel		sc = sc0;
310119815Smarcel
311119815Smarcel	/*
312119815Smarcel	 * Protect ourselves against interrupts while we're not completely
313119815Smarcel	 * finished attaching and initializing. We don't expect interrupts
314119815Smarcel	 * until after UART_ATTACH() though.
315119815Smarcel	 */
316119815Smarcel	sc->sc_leaving = 1;
317119815Smarcel
318120143Smarcel	mtx_init(&sc->sc_hwmtx, "uart_hwmtx", NULL, MTX_SPIN);
319120143Smarcel
320119815Smarcel	/*
321119815Smarcel	 * Re-allocate. We expect that the softc contains the information
322119815Smarcel	 * collected by uart_bus_probe() intact.
323119815Smarcel	 */
324119815Smarcel	sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid,
325119815Smarcel	    0, ~0, sc->sc_class->uc_range, RF_ACTIVE);
326119815Smarcel	if (sc->sc_rres == NULL)
327119815Smarcel		return (ENXIO);
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,
333127135Snjl	    RF_ACTIVE);
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,
340119815Smarcel			    sc->sc_ires, INTR_TYPE_TTY, uart_intr, sc,
341119815Smarcel			    &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) {
396119815Smarcel		switch (sc->sc_sysdev->type) {
397119815Smarcel		case UART_DEV_CONSOLE:
398119815Smarcel			device_printf(dev, "console");
399119815Smarcel			break;
400119815Smarcel		case UART_DEV_DBGPORT:
401119815Smarcel			device_printf(dev, "debug port");
402119815Smarcel			break;
403119815Smarcel		case UART_DEV_KEYBOARD:
404119815Smarcel			device_printf(dev, "keyboard");
405119815Smarcel			break;
406119815Smarcel		default:
407119815Smarcel			device_printf(dev, "unknown system device");
408119815Smarcel			break;
409119815Smarcel		}
410119815Smarcel		printf(" (%d,%c,%d,%d)\n", sc->sc_sysdev->baudrate,
411119815Smarcel		    "noems"[sc->sc_sysdev->parity], sc->sc_sysdev->databits,
412119815Smarcel		    sc->sc_sysdev->stopbits);
413119815Smarcel	}
414119815Smarcel
415119996Smarcel	sc->sc_pps.ppscap = PPS_CAPTUREBOTH;
416119996Smarcel	pps_init(&sc->sc_pps);
417119996Smarcel
418119815Smarcel	error = (sc->sc_sysdev != NULL && sc->sc_sysdev->attach != NULL)
419119815Smarcel	    ? (*sc->sc_sysdev->attach)(sc) : uart_tty_attach(sc);
420119815Smarcel	if (error)
421119815Smarcel		goto fail;
422119815Smarcel
423119815Smarcel	sc->sc_leaving = 0;
424119815Smarcel	uart_intr(sc);
425119815Smarcel	return (0);
426119815Smarcel
427119815Smarcel fail:
428119815Smarcel	free(sc->sc_txbuf, M_UART);
429119815Smarcel	free(sc->sc_rxbuf, M_UART);
430119815Smarcel
431119815Smarcel	if (sc->sc_ires != NULL) {
432119815Smarcel		bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
433119815Smarcel		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
434119815Smarcel		    sc->sc_ires);
435119815Smarcel	}
436119815Smarcel	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
437119815Smarcel
438119815Smarcel	return (error);
439119815Smarcel}
440119815Smarcel
441119815Smarcelint
442119815Smarceluart_bus_detach(device_t dev)
443119815Smarcel{
444119815Smarcel	struct uart_softc *sc;
445119815Smarcel
446119815Smarcel	sc = device_get_softc(dev);
447119815Smarcel
448119815Smarcel	sc->sc_leaving = 1;
449119815Smarcel
450119815Smarcel	UART_DETACH(sc);
451119815Smarcel
452119815Smarcel	if (sc->sc_sysdev != NULL && sc->sc_sysdev->detach != NULL)
453119815Smarcel		(*sc->sc_sysdev->detach)(sc);
454119815Smarcel	else
455119815Smarcel		uart_tty_detach(sc);
456119815Smarcel
457119815Smarcel	free(sc->sc_txbuf, M_UART);
458119815Smarcel	free(sc->sc_rxbuf, M_UART);
459119815Smarcel
460119815Smarcel	if (sc->sc_ires != NULL) {
461119815Smarcel		bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
462119815Smarcel		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
463119815Smarcel		    sc->sc_ires);
464119815Smarcel	}
465119815Smarcel	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
466119815Smarcel
467119815Smarcel	if (sc->sc_class->size > sizeof(*sc)) {
468119815Smarcel		device_set_softc(dev, NULL);
469119815Smarcel		free(sc, M_UART);
470119815Smarcel	} else
471119815Smarcel		device_set_softc(dev, NULL);
472119815Smarcel
473119815Smarcel	return (0);
474119815Smarcel}
475