pcmcom.c revision 1.31.16.2 1 /* $NetBSD: pcmcom.c,v 1.31.16.2 2008/06/02 13:23:47 mjf Exp $ */
2
3 /*-
4 * Copyright (c) 1998, 2000, 2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by RedBack Networks, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Device driver for multi-port PCMCIA serial cards, written by
34 * Jason R. Thorpe for RedBack Networks, Inc.
35 *
36 * Most of these cards are simply multiple UARTs sharing a single interrupt
37 * line, and rely on the fact that PCMCIA level-triggered interrupts can
38 * be shared. There are no special interrupt registers on them, as there
39 * are on most ISA multi-port serial cards.
40 *
41 * If there are other cards that have interrupt registers, they should not
42 * be glued into this driver. Rather, separate drivers should be written
43 * for those devices, as we have in the ISA multi-port serial card case.
44 */
45
46 #include <sys/cdefs.h>
47 __KERNEL_RCSID(0, "$NetBSD: pcmcom.c,v 1.31.16.2 2008/06/02 13:23:47 mjf Exp $");
48
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/device.h>
52 #include <sys/termios.h>
53 #include <sys/malloc.h>
54
55 #include <sys/bus.h>
56 #include <sys/intr.h>
57
58 #include <dev/ic/comreg.h>
59 #include <dev/ic/comvar.h>
60
61 #include <dev/pcmcia/pcmciavar.h>
62 #include <dev/pcmcia/pcmciareg.h>
63 #include <dev/pcmcia/pcmciadevs.h>
64
65 #include "com.h"
66 #include "pcmcom.h"
67
68 #include "locators.h"
69
70 struct pcmcom_softc {
71 struct device sc_dev; /* generic device glue */
72
73 struct pcmcia_function *sc_pf; /* our PCMCIA function */
74 void *sc_ih; /* interrupt handle */
75 int sc_enabled_count; /* enabled count */
76
77 #define NSLAVES 8
78 struct device *sc_slaves[NSLAVES]; /* slave info */
79 int sc_nslaves; /* slave count */
80
81 int sc_state;
82 #define PCMCOM_ATTACHED 3
83 };
84
85 struct pcmcom_attach_args {
86 bus_space_tag_t pca_iot; /* I/O tag */
87 bus_space_handle_t pca_ioh; /* I/O handle */
88 int pca_slave; /* slave # */
89 };
90
91 int pcmcom_match(struct device *, struct cfdata *, void *);
92 int pcmcom_validate_config(struct pcmcia_config_entry *);
93 void pcmcom_attach(struct device *, struct device *, void *);
94 int pcmcom_detach(struct device *, int);
95 int pcmcom_activate(struct device *, enum devact);
96
97 CFATTACH_DECL(pcmcom, sizeof(struct pcmcom_softc),
98 pcmcom_match, pcmcom_attach, pcmcom_detach, pcmcom_activate);
99
100 const struct pcmcia_product pcmcom_products[] = {
101 { PCMCIA_VENDOR_SOCKET, PCMCIA_PRODUCT_SOCKET_DUAL_RS232,
102 PCMCIA_CIS_INVALID },
103 #if 0 /* does not work */
104 { PCMCIA_VENDOR_SOCKET, PCMCIA_PRODUCT_SOCKET_DUAL_RS232_A,
105 PCMCIA_CIS_INVALID },
106 #endif
107 };
108 const size_t pcmcom_nproducts =
109 sizeof(pcmcom_products) / sizeof(pcmcom_products[0]);
110
111 int pcmcom_print(void *, const char *);
112
113 int pcmcom_enable(struct pcmcom_softc *);
114 void pcmcom_disable(struct pcmcom_softc *);
115
116 int pcmcom_intr(void *);
117
118 int
119 pcmcom_match(struct device *parent, struct cfdata *cf,
120 void *aux)
121 {
122 struct pcmcia_attach_args *pa = aux;
123
124 if (pcmcia_product_lookup(pa, pcmcom_products, pcmcom_nproducts,
125 sizeof(pcmcom_products[0]), NULL))
126 return (2); /* beat com_pcmcia */
127 return (0);
128 }
129
130 int
131 pcmcom_validate_config(cfe)
132 struct pcmcia_config_entry *cfe;
133 {
134 if (cfe->iftype != PCMCIA_IFTYPE_IO ||
135 cfe->num_iospace < 1 || cfe->num_iospace > NSLAVES)
136 return (EINVAL);
137 return (0);
138 }
139
140 void
141 pcmcom_attach(struct device *parent, struct device *self, void *aux)
142 {
143 struct pcmcom_softc *sc = (void *)self;
144 struct pcmcia_attach_args *pa = aux;
145 struct pcmcia_config_entry *cfe;
146 int slave;
147 int error;
148 int locs[PCMCOMCF_NLOCS];
149
150 sc->sc_pf = pa->pf;
151
152 error = pcmcia_function_configure(pa->pf, pcmcom_validate_config);
153 if (error) {
154 aprint_error_dev(self, "configure failed, error=%d\n",
155 error);
156 return;
157 }
158
159 cfe = pa->pf->cfe;
160 sc->sc_nslaves = cfe->num_iospace;
161
162 error = pcmcom_enable(sc);
163 if (error)
164 goto fail;
165
166 /* Attach the children. */
167 for (slave = 0; slave < sc->sc_nslaves; slave++) {
168 struct pcmcom_attach_args pca;
169
170 printf("%s: slave %d\n", device_xname(self), slave);
171
172 pca.pca_iot = cfe->iospace[slave].handle.iot;
173 pca.pca_ioh = cfe->iospace[slave].handle.ioh;
174 pca.pca_slave = slave;
175
176 locs[PCMCOMCF_SLAVE] = slave;
177
178 sc->sc_slaves[slave] = config_found_sm_loc(&sc->sc_dev,
179 "pcmcom", locs,
180 &pca, pcmcom_print, config_stdsubmatch);
181 }
182
183 pcmcom_disable(sc);
184 sc->sc_state = PCMCOM_ATTACHED;
185 return;
186
187 fail:
188 pcmcia_function_unconfigure(pa->pf);
189 }
190
191 int
192 pcmcom_detach(self, flags)
193 struct device *self;
194 int flags;
195 {
196 struct pcmcom_softc *sc = (void *)self;
197 int slave, error;
198
199 if (sc->sc_state != PCMCOM_ATTACHED)
200 return (0);
201
202 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) {
203 if (sc->sc_slaves[slave]) {
204 /* Detach the child. */
205 error = config_detach(sc->sc_slaves[slave], flags);
206 if (error)
207 return (error);
208 sc->sc_slaves[slave] = 0;
209 }
210 }
211
212 pcmcia_function_unconfigure(sc->sc_pf);
213
214 return (0);
215 }
216
217 int
218 pcmcom_activate(self, act)
219 struct device *self;
220 enum devact act;
221 {
222 struct pcmcom_softc *sc = (void *)self;
223 int slave, error = 0, s;
224
225 s = splserial();
226 switch (act) {
227 case DVACT_ACTIVATE:
228 error = EOPNOTSUPP;
229 break;
230
231 case DVACT_DEACTIVATE:
232 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) {
233 if (sc->sc_slaves[slave]) {
234 /*
235 * Deactivate the child. Doing so will cause
236 * our own enabled count to drop to 0, once all
237 * children are deactivated.
238 */
239 error = config_deactivate(sc->sc_slaves[slave]);
240 if (error)
241 break;
242 }
243 }
244 break;
245 }
246 splx(s);
247 return (error);
248 }
249
250 int
251 pcmcom_print(aux, pnp)
252 void *aux;
253 const char *pnp;
254 {
255 struct pcmcom_attach_args *pca = aux;
256
257 /* only com's can attach to pcmcom's; easy... */
258 if (pnp)
259 aprint_normal("com at %s", pnp);
260
261 aprint_normal(" slave %d", pca->pca_slave);
262
263 return (UNCONF);
264 }
265
266 int
267 pcmcom_intr(arg)
268 void *arg;
269 {
270 #if NCOM > 0
271 struct pcmcom_softc *sc = arg;
272 int i, rval = 0;
273
274 if (sc->sc_enabled_count == 0)
275 return (0);
276
277 for (i = 0; i < sc->sc_nslaves; i++) {
278 if (sc->sc_slaves[i])
279 rval |= comintr(sc->sc_slaves[i]);
280 }
281
282 return (rval);
283 #else
284 return (0);
285 #endif /* NCOM > 0 */
286 }
287
288 int
289 pcmcom_enable(sc)
290 struct pcmcom_softc *sc;
291 {
292 int error;
293
294 if (sc->sc_enabled_count++ != 0)
295 return (0);
296
297 /* Establish the interrupt. */
298 sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_SERIAL,
299 pcmcom_intr, sc);
300 if (!sc->sc_ih)
301 return (EIO);
302
303 error = pcmcia_function_enable(sc->sc_pf);
304 if (error) {
305 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
306 sc->sc_ih = 0;
307 }
308
309 return (error);
310 }
311
312 void
313 pcmcom_disable(sc)
314 struct pcmcom_softc *sc;
315 {
316
317 if (--sc->sc_enabled_count != 0)
318 return;
319
320 pcmcia_function_disable(sc->sc_pf);
321 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
322 sc->sc_ih = 0;
323 }
324
325 /****** Here begins the com attachment code. ******/
326
327 #if NCOM_PCMCOM > 0
328 int com_pcmcom_match(device_t, cfdata_t , void *);
329 void com_pcmcom_attach(device_t, device_t, void *);
330
331 /* No pcmcom-specific goo in the softc; it's all in the parent. */
332 CFATTACH_DECL(com_pcmcom, sizeof(struct com_softc),
333 com_pcmcom_match, com_pcmcom_attach, com_detach, com_activate);
334
335 int com_pcmcom_enable(struct com_softc *);
336 void com_pcmcom_disable(struct com_softc *);
337
338 int
339 com_pcmcom_match(device_t parent, cfdata_t cf, void *aux)
340 {
341
342 /* Device is always present. */
343 return (1);
344 }
345
346 void
347 com_pcmcom_attach(device_t parent, device_t self, void *aux)
348 {
349 struct com_softc *sc = device_private(self);
350 struct pcmcom_attach_args *pca = aux;
351
352 sc->sc_dev = self;
353 COM_INIT_REGS(sc->sc_regs, pca->pca_iot, pca->pca_ioh, -1);
354 sc->enabled = 1;
355
356 sc->sc_frequency = COM_FREQ;
357
358 sc->enable = com_pcmcom_enable;
359 sc->disable = com_pcmcom_disable;
360
361 com_attach_subr(sc);
362
363 sc->enabled = 0;
364 }
365
366 int
367 com_pcmcom_enable(struct com_softc *sc)
368 {
369
370 return (pcmcom_enable(device_private(device_parent(sc->sc_dev))));
371 }
372
373 void
374 com_pcmcom_disable(sc)
375 struct com_softc *sc;
376 {
377
378 pcmcom_disable(device_private(device_parent(sc->sc_dev)));
379 }
380 #endif /* NCOM_PCMCOM > 0 */
381