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