1 /*	$OpenBSD: com_puc.c,v 1.7 2004/08/19 21:47:54 miod Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 1999, Jason Downs.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name(s) of the author(s) nor the name OpenBSD
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/ioctl.h>
34 #include <sys/select.h>
35 #include <sys/tty.h>
36 #include <sys/proc.h>
37 #include <sys/user.h>
38 #include <sys/conf.h>
39 #include <sys/file.h>
40 #include <sys/uio.h>
41 #include <sys/kernel.h>
42 #include <sys/syslog.h>
43 #include <sys/types.h>
44 #include <sys/device.h>
45 
46 #include <machine/intr.h>
47 #include <machine/bus.h>
48 
49 #include <dev/pci/pcivar.h>
50 #include <dev/pci/pucvar.h>
51 
52 #ifdef i386
53 #include <dev/isa/isavar.h>	/* XXX */
54 #endif
55 
56 #include "com.h"
57 #ifdef i386
58 #include "pccom.h"
59 #endif
60 
61 #include <dev/ic/comreg.h>
62 #if NPCCOM > 0
63 #include <i386/isa/pccomvar.h>
64 #endif
65 #if NCOM > 0
66 #include <dev/ic/comvar.h>
67 #endif
68 #include <dev/ic/ns16550reg.h>
69 
70 #define	com_lcr		com_cfcr
71 
72 int com_puc_match(struct device *, void *, void *);
73 void com_puc_attach(struct device *, struct device *, void *);
74 
75 #if NCOM_PUC
76 struct cfattach com_puc_ca = {
77 	sizeof(struct com_softc), com_puc_match, com_puc_attach
78 };
79 #endif
80 
81 #if NPCCOM_PUC
82 struct cfattach pccom_puc_ca = {
83 	sizeof(struct com_softc), com_puc_match, com_puc_attach
84 };
85 #endif
86 
87 void com_puc_attach2(struct com_softc *);
88 
89 int
com_puc_match(parent,match,aux)90 com_puc_match(parent, match, aux)
91 	struct device *parent;
92 	void *match, *aux;
93 {
94 	struct puc_attach_args *pa = aux;
95 
96 	if (pa->type == PUC_PORT_TYPE_COM)
97 		return(1);
98 
99 	return(0);
100 }
101 
102 void
com_puc_attach(parent,self,aux)103 com_puc_attach(parent, self, aux)
104 	struct device *parent, *self;
105 	void *aux;
106 {
107 	struct com_softc *sc = (void *)self;
108 	struct puc_attach_args *pa = aux;
109 	const char *intrstr;
110 
111 	/* Grab a PCI interrupt. */
112 	intrstr = pci_intr_string(pa->pc, pa->intrhandle);
113 	sc->sc_ih = pci_intr_establish(pa->pc, pa->intrhandle,
114 			IPL_HIGH, comintr, sc,
115 			sc->sc_dev.dv_xname);
116 	if (sc->sc_ih == NULL) {
117 		printf(": couldn't establish interrupt");
118 		if (intrstr != NULL)
119 			printf(" at %s", intrstr);
120 		printf("\n");
121 		return;
122 	}
123 	printf(" %s", intrstr);
124 
125 	sc->sc_iot = pa->t;
126 	sc->sc_ioh = pa->h;
127 	sc->sc_iobase = pa->a;
128 	sc->sc_frequency = COM_FREQ;
129 
130 	if (pa->flags)
131 		sc->sc_frequency = pa->flags & PUC_COM_CLOCKMASK;
132 
133 	com_puc_attach2(sc);
134 }
135 
136 /*
137  * XXX This should be handled by a generic attach
138  */
139 void
com_puc_attach2(sc)140 com_puc_attach2(sc)
141 	struct com_softc *sc;
142 {
143 	bus_space_tag_t iot = sc->sc_iot;
144 	bus_space_handle_t ioh = sc->sc_ioh;
145 	u_int8_t lcr;
146 
147 	sc->sc_hwflags = 0;
148 	sc->sc_swflags = 0;
149 
150 	/*
151 	 * Probe for all known forms of UART.
152 	 */
153 	lcr = bus_space_read_1(iot, ioh, com_lcr);
154 
155 	bus_space_write_1(iot, ioh, com_lcr, LCR_EFR);
156 	bus_space_write_1(iot, ioh, com_efr, 0);
157 	bus_space_write_1(iot, ioh, com_lcr, 0);
158 
159 	bus_space_write_1(iot, ioh, com_fifo, FIFO_ENABLE);
160 	delay(100);
161 
162 	switch(bus_space_read_1(iot, ioh, com_iir) >> 6) {
163 	case 0:
164 		sc->sc_uarttype = COM_UART_16450;
165 		break;
166 	case 2:
167 		sc->sc_uarttype = COM_UART_16550;
168 		break;
169 	case 3:
170 		sc->sc_uarttype = COM_UART_16550A;
171 		break;
172 	default:
173 		sc->sc_uarttype = COM_UART_UNKNOWN;
174 		break;
175 	}
176 
177 	if (sc->sc_uarttype == COM_UART_16550A) { /* Probe for ST16650s */
178 		bus_space_write_1(iot, ioh, com_lcr, lcr | LCR_DLAB);
179 		if (bus_space_read_1(iot, ioh, com_efr) == 0) {
180 			sc->sc_uarttype = COM_UART_ST16650;
181 		} else {
182 			bus_space_write_1(iot, ioh, com_lcr, LCR_EFR);
183 			if (bus_space_read_1(iot, ioh, com_efr) == 0)
184 				sc->sc_uarttype = COM_UART_ST16650V2;
185 		}
186 	}
187 
188 #if NPCCOM > 0
189 #ifdef i386
190 	if (sc->sc_uarttype == COM_UART_ST16650V2) {	/* Probe for XR16850s */
191 		u_int8_t dlbl, dlbh;
192 
193 		/* Enable latch access and get the current values. */
194 		bus_space_write_1(iot, ioh, com_lcr, lcr | LCR_DLAB);
195 		dlbl = bus_space_read_1(iot, ioh, com_dlbl);
196 		dlbh = bus_space_read_1(iot, ioh, com_dlbh);
197 
198 		/* Zero out the latch divisors */
199 		bus_space_write_1(iot, ioh, com_dlbl, 0);
200 		bus_space_write_1(iot, ioh, com_dlbh, 0);
201 
202 		if (bus_space_read_1(iot, ioh, com_dlbh) == 0x10) {
203 			sc->sc_uarttype = COM_UART_XR16850;
204 			sc->sc_uartrev = bus_space_read_1(iot, ioh, com_dlbl);
205 		}
206 
207 		/* Reset to original. */
208 		bus_space_write_1(iot, ioh, com_dlbl, dlbl);
209 		bus_space_write_1(iot, ioh, com_dlbh, dlbh);
210 	}
211 #endif
212 #endif
213 
214 	/* Reset the LCR (latch access is probably enabled). */
215 	bus_space_write_1(iot, ioh, com_lcr, lcr);
216 	if (sc->sc_uarttype == COM_UART_16450) { /* Probe for 8250 */
217 		u_int8_t scr0, scr1, scr2;
218 
219 		scr0 = bus_space_read_1(iot, ioh, com_scratch);
220 		bus_space_write_1(iot, ioh, com_scratch, 0xa5);
221 		scr1 = bus_space_read_1(iot, ioh, com_scratch);
222 		bus_space_write_1(iot, ioh, com_scratch, 0x5a);
223 		scr2 = bus_space_read_1(iot, ioh, com_scratch);
224 		bus_space_write_1(iot, ioh, com_scratch, scr0);
225 
226 		if ((scr1 != 0xa5) || (scr2 != 0x5a))
227 			sc->sc_uarttype = COM_UART_8250;
228 	}
229 
230 	/*
231 	 * Print UART type and initialize ourself.
232 	 */
233 	sc->sc_fifolen = 1;	/* default */
234 	switch (sc->sc_uarttype) {
235 	case COM_UART_UNKNOWN:
236 		printf(": unknown uart\n");
237 		break;
238 	case COM_UART_8250:
239 		printf(": ns8250, no fifo\n");
240 		break;
241 	case COM_UART_16450:
242 		printf(": ns16450, no fifo\n");
243 		break;
244 	case COM_UART_16550:
245 		printf(": ns16550, no working fifo\n");
246 		break;
247 	case COM_UART_16550A:
248 		printf(": ns16550a, 16 byte fifo\n");
249 		SET(sc->sc_hwflags, COM_HW_FIFO);
250 		sc->sc_fifolen = 16;
251 		break;
252 	case COM_UART_ST16650:
253 		printf(": st16650, no working fifo\n");
254 		break;
255 	case COM_UART_ST16650V2:
256 		printf(": st16650, 32 byte fifo\n");
257 		SET(sc->sc_hwflags, COM_HW_FIFO);
258 		sc->sc_fifolen = 32;
259 		break;
260 	case COM_UART_TI16750:
261 		printf(": ti16750, 64 byte fifo\n");
262 		SET(sc->sc_hwflags, COM_HW_FIFO);
263 		sc->sc_fifolen = 64;
264 		break;
265 #if NPCCOM > 0
266 #ifdef i386
267 	case COM_UART_XR16850:
268 		printf(": xr16850 (rev %d), 128 byte fifo\n", sc->sc_uartrev);
269 		SET(sc->sc_hwflags, COM_HW_FIFO);
270 		sc->sc_fifolen = 128;
271 		break;
272 #endif
273 #endif
274 	default:
275 		panic("com_puc_attach2: bad fifo type");
276 	}
277 
278 	/* clear and disable fifo */
279 	bus_space_write_1(iot, ioh, com_fifo, FIFO_RCV_RST | FIFO_XMT_RST);
280 	(void)bus_space_read_1(iot, ioh, com_data);
281 	bus_space_write_1(iot, ioh, com_fifo, 0);
282 
283 	sc->sc_mcr = 0;
284 	bus_space_write_1(iot, ioh, com_mcr, sc->sc_mcr);
285 
286 	timeout_set(&sc->sc_diag_tmo, comdiag, sc);
287 	timeout_set(&sc->sc_dtr_tmo, com_raisedtr, sc);
288 #if NCOM > 0
289 #ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
290 	sc->sc_si = softintr_establish(IPL_TTY, comsoft, sc);
291 	if (sc->sc_si == NULL)
292 		panic("%s: can't establish soft interrupt.",
293 		    sc->sc_dev.dv_xname);
294 #else
295 	timeout_set(&sc->sc_comsoft_tmo, comsoft, sc);
296 #endif
297 #endif
298 
299 	/*
300 	 * If there are no enable/disable functions, assume the device
301 	 * is always enabled.
302 	 */
303 	if (!sc->enable)
304 		sc->enabled = 1;
305 }
306