FreeBSD/Linux Kernel Cross Reference
sys/dev/sx/sx_util.c
1 /*
2 * Device driver for Specialix I/O8+ multiport serial card.
3 *
4 * Copyright 2003 Frank Mayhar <frank@exit.com>
5 *
6 * Derived from the "si" driver by Peter Wemm <peter@netplex.com.au>, using
7 * lots of information from the Linux "specialix" driver by Roger Wolff
8 * <R.E.Wolff@BitWizard.nl> and from the Intel CD1865 "Intelligent Eight-
9 * Channel Communications Controller" datasheet. Roger was also nice
10 * enough to answer numerous questions about stuff specific to the I/O8+
11 * not covered by the CD1865 datasheet.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notices, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notices, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
25 * NO EVENT SHALL THE AUTHORS BE LIABLE.
26 *
27 * $FreeBSD: releng/5.3/sys/dev/sx/sx_util.c 128133 2004-04-11 19:32:20Z imp $
28 */
29
30 #include "opt_debug_sx.h"
31
32 /* Utility and support routines for the Specialix I/O8+ driver. */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37 #include <sys/tty.h>
38 #include <machine/resource.h>
39 #include <machine/bus.h>
40 #include <machine/clock.h>
41 #include <sys/rman.h>
42
43 #include <dev/sx/cd1865.h>
44 #include <dev/sx/sxvar.h>
45 #include <dev/sx/sx.h>
46 #include <dev/sx/sx_util.h>
47
48 /*
49 * sx_probe_io8()
50 * Probe the board to verify that it is a Specialix I/O8+.
51 *
52 * Description:
53 * This is called by sx_pci_attach() (and possibly in the future by
54 * sx_isa_attach()) to verify that the card we're attaching to is
55 * indeed a Specialix I/O8+. To do this, we check for the Prescaler
56 * Period Register of the CD1865 chip and for the Specialix signature
57 * on the DSR input line of each channel. These lines, along with the
58 * RTS output lines, are wired down in hardware.
59 */
60 int
61 sx_probe_io8(
62 device_t dev)
63 {
64 struct sx_softc *sc;
65 unsigned char val1, val2;
66 int i;
67
68 sc = device_get_softc(dev);
69 /*
70 * Try to write the Prescaler Period Register, then read it back,
71 * twice. If this fails, it's not an I/O8+.
72 */
73 sx_cd1865_out(sc, CD1865_PPRL, 0x5a);
74 DELAY(1);
75 val1 = sx_cd1865_in(sc, CD1865_PPRL);
76
77 sx_cd1865_out(sc, CD1865_PPRL, 0xa5);
78 DELAY(1);
79 val2 = sx_cd1865_in(sc, CD1865_PPRL);
80
81 if ((val1 != 0x5a) || (val2 != 0xa5))
82 return(1);
83
84 /*
85 * Check the lines that Specialix uses as board identification.
86 * These are the DSR input and the RTS output, which are wired
87 * down.
88 */
89 val1 = 0;
90 for (i = 0; i < 8; i++) {
91 sx_cd1865_out(sc, CD1865_CAR, i); /* Select channel. */
92 if (sx_cd1865_in(sc, CD1865_MSVR) & CD1865_MSVR_DSR) /* Set? */
93 val1 |= 1 << i; /* OR it in. */
94 }
95 #ifdef notdef
96 val2 = 0;
97 for (i = 0; i < 8; i++) {
98 sx_cd1865_out(sc, CD1865_CAR, i); /* Select channel. */
99 if (sx_cd1865_in(sc, CD1865_MSVR) & CD1865_MSVR_RTS) /* Set? */
100 val2 |= 1 << i; /* OR it in. */
101 }
102 /*
103 * They managed to switch the bit order between the docs and
104 * the IO8+ card. The new PCI card now conforms to old docs.
105 * They changed the PCI docs to reflect the situation on the
106 * old card.
107 */
108 val2 = (bp->flags & SX_BOARD_IS_PCI) ? 0x4d : 0xb2;
109 #endif /* notdef */
110 if (val1 != 0x4d) {
111 if (bootverbose)
112 device_printf(dev,
113 "Specialix I/O8+ ID 0x4d not found (0x%02x).\n",
114 val1);
115 return(1);
116 }
117 return(0); /* Probed successfully. */
118 }
119
120 /*
121 * sx_init_CD1865()
122 * Hard-reset and initialize the I/O8+ CD1865 processor.
123 *
124 * Description:
125 * This routine does a hard reset of the CD1865 chip and waits for it
126 * to complete. (The reset should complete after 500us; we wait 1ms
127 * and fail if we time out.) We then initialize the CD1865 processor.
128 */
129 int
130 sx_init_cd1865(
131 struct sx_softc *sc,
132 int unit)
133 {
134 int s;
135 unsigned int to;
136
137 s = spltty();
138 disable_intr();
139 sx_cd1865_out(sc, CD1865_GSVR, 0x00); /* Clear the GSVR. */
140 sx_cd1865_wait_CCR(sc, 0); /* Wait for the CCR to clear. */
141 sx_cd1865_out(sc, CD1865_CCR, CD1865_CCR_HARDRESET); /* Reset CD1865. */
142 enable_intr();
143 to = SX_GSVR_TIMEOUT/5;
144 while (to-- > 0) {
145 if (sx_cd1865_in(sc, CD1865_GSVR) == 0xff)
146 break;
147 DELAY(5);
148 }
149 if (to == 0) {
150 splx(s);
151 printf("sx%d: Timeout waiting for reset.\n", unit);
152 return(EIO);
153 }
154 /*
155 * The high five bits of the Global Interrupt Vector Register is
156 * used to identify daisy-chained CD1865 chips. The I/O8+ isn't
157 * daisy chained, but we have to initialize the field anyway.
158 */
159 sx_cd1865_out(sc, CD1865_GIVR, SX_CD1865_ID);
160 /* Clear the Global Interrupting Channel register. */
161 sx_cd1865_out(sc, CD1865_GICR, 0);
162 /*
163 * Set the Service Match Registers to the appropriate values. See
164 * the cd1865.h include file for more information.
165 */
166 sx_cd1865_out(sc, CD1865_MSMR, CD1865_ACK_MINT); /* Modem. */
167 sx_cd1865_out(sc, CD1865_TSMR, CD1865_ACK_TINT); /* Transmit. */
168 sx_cd1865_out(sc, CD1865_RSMR, CD1865_ACK_RINT); /* Receive. */
169 /*
170 * Set RegAckEn in the Service Request Configuration Register;
171 * we'll be acknowledging service requests in software, not
172 * hardware.
173 */
174 sx_cd1865_bis(sc, CD1865_SRCR, CD1865_SRCR_REGACKEN);
175 /*
176 * Set the CD1865 timer tick rate. The value here is the processor
177 * clock rate (in MHz) divided by the rate in ticks per second. See
178 * commentary in sx.h.
179 */
180 sx_cd1865_out(sc, CD1865_PPRH, SX_CD1865_PRESCALE >> 8);
181 sx_cd1865_out(sc, CD1865_PPRL, SX_CD1865_PRESCALE & 0xff);
182
183 splx(s);
184 return(0);
185 }
186
187 #ifdef notyet
188 /*
189 * Set the IRQ using the RTS lines that run to the PAL on the board....
190 *
191 * This is a placeholder for ISA support, if that's ever implemented. This
192 * should _only_ be called from sx_isa_attach().
193 */
194 int
195 sx_set_irq(
196 struct sx_softc *sc,
197 int unit,
198 int irq)
199 {
200 register int virq;
201 register int i, j;
202
203 switch (irq) {
204 /* In the same order as in the docs... */
205 case 15:
206 virq = 0;
207 break;
208 case 12:
209 virq = 1;
210 break;
211 case 11:
212 virq = 2;
213 break;
214 case 9:
215 virq = 3;
216 break;
217 default:
218 printf("sx%d: Illegal irq %d.\n", unit, irq);
219 return(0);
220 }
221 for (i = 0; i < 2; i++) {
222 sx_cd1865_out(sc, CD1865_CAR, i); /* Select channel. */
223 j = ((virq >> i) & 0x1) ? MSVR_RTS : 0;
224 sx_cd1865_out(sc, CD1865_MSVRTS, j);
225 }
226 return(1);
227 }
228
229 #endif /* notyet */
230
231 /*
232 * sx_int_port()
233 * Determine the port that interrupted us.
234 *
235 * Description:
236 * This routine checks the Global Interrupting Channel Register (GICR)
237 * to find the port that caused an interrupt. It returns a pointer to
238 * the sx_port structure of the interrupting port, or NULL if there was
239 * none.
240 *
241 * XXX - check type/validity of interrupt?
242 */
243 struct sx_port *
244 sx_int_port(
245 struct sx_softc *sc,
246 int unit)
247 {
248 unsigned char chan;
249 struct sx_port *pp;
250
251 chan = (sx_cd1865_in(sc, CD1865_GSCR2|SX_EI) & CD1865_GICR_CHAN_MASK)
252 >> CD1865_GICR_CHAN_SHIFT;
253 DPRINT((NULL, DBG_INTR, "Intr chan %d\n", chan));
254 if (chan < CD1865_NUMCHAN) {
255 pp = sc->sc_ports + (int)chan;
256 return(pp);
257 }
258 printf("sx%d: False interrupt on port %d.\n", unit, chan);
259 return(NULL);
260 }
Cache object: fb48933ed23bdbf0f5e9ab60d4a9c27e
|