]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sx/sx_util.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / 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$
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 }