pcmcom.c revision 1.5 1 /* $NetBSD: pcmcom.c,v 1.5 2000/02/04 01:27:14 cgd Exp $ */
2
3 /*-
4 * Copyright (c) 1998, 2000 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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Device driver for multi-port PCMCIA serial cards, written by
41 * Jason R. Thorpe for RedBack Networks, Inc.
42 *
43 * Most of these cards are simply multiple UARTs sharing a single interrupt
44 * line, and rely on the fact that PCMCIA level-triggered interrupts can
45 * be shared. There are no special interrupt registers on them, as there
46 * are on most ISA multi-port serial cards.
47 *
48 * If there are other cards that have interrupt registers, they should not
49 * be glued into this driver. Rather, separate drivers should be written
50 * for those devices, as we have in the ISA multi-port serial card case.
51 */
52
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/device.h>
56 #include <sys/termios.h>
57 #include <sys/malloc.h>
58
59 #include <machine/bus.h>
60 #include <machine/intr.h>
61
62 #include <dev/ic/comreg.h>
63 #include <dev/ic/comvar.h>
64
65 #include <dev/pcmcia/pcmciavar.h>
66 #include <dev/pcmcia/pcmciareg.h>
67 #include <dev/pcmcia/pcmciadevs.h>
68
69 #include "com.h"
70 #include "pcmcom.h"
71
72 #include "locators.h"
73
74 struct pcmcom_slave_info {
75 struct pcmcia_io_handle psi_pcioh; /* PCMCIA i/o space info */
76 int psi_io_window; /* our i/o window */
77 struct device *psi_child; /* child's device */
78 };
79
80 struct pcmcom_softc {
81 struct device sc_dev; /* generic device glue */
82
83 struct pcmcia_function *sc_pf; /* our PCMCIA function */
84 void *sc_ih; /* interrupt handle */
85 int sc_enabled_count; /* enabled count */
86
87 struct pcmcom_slave_info *sc_slaves; /* slave info */
88 int sc_nslaves; /* slave count */
89 };
90
91 struct pcmcom_attach_args {
92 bus_space_tag_t pca_iot; /* I/O tag */
93 bus_space_handle_t pca_ioh; /* I/O handle */
94 int pca_slave; /* slave # */
95 };
96
97 int pcmcom_match __P((struct device *, struct cfdata *, void *));
98 void pcmcom_attach __P((struct device *, struct device *, void *));
99 int pcmcom_detach __P((struct device *, int));
100 int pcmcom_activate __P((struct device *, enum devact));
101
102 struct cfattach pcmcom_ca = {
103 sizeof(struct pcmcom_softc), pcmcom_match, pcmcom_attach,
104 pcmcom_detach, pcmcom_activate
105 };
106
107 const struct pcmcom_product {
108 struct pcmcia_product pp_product;
109 int pp_nslaves; /* number of slaves */
110 } pcmcom_products[] = {
111 { { PCMCIA_STR_SOCKET_DUAL_RS232, PCMCIA_VENDOR_SOCKET,
112 PCMCIA_PRODUCT_SOCKET_DUAL_RS232, 0 },
113 2 },
114
115 { { NULL } }
116 };
117
118 int pcmcom_print __P((void *, const char *));
119 int pcmcom_submatch __P((struct device *, struct cfdata *, void *));
120
121 int pcmcom_check_cfe __P((struct pcmcom_softc *,
122 struct pcmcia_config_entry *));
123 void pcmcom_attach_slave __P((struct pcmcom_softc *, int));
124
125 int pcmcom_enable __P((struct pcmcom_softc *));
126 void pcmcom_disable __P((struct pcmcom_softc *));
127
128 int pcmcom_intr __P((void *));
129
130 int
131 pcmcom_match(parent, cf, aux)
132 struct device *parent;
133 struct cfdata *cf;
134 void *aux;
135 {
136 struct pcmcia_attach_args *pa = aux;
137
138 if (pcmcia_product_lookup(pa,
139 (const struct pcmcia_product *)pcmcom_products,
140 sizeof pcmcom_products[0], NULL) != NULL)
141 return (10); /* beat com_pcmcia */
142 return (0);
143 }
144
145 void
146 pcmcom_attach(parent, self, aux)
147 struct device *parent, *self;
148 void *aux;
149 {
150 struct pcmcom_softc *sc = (struct pcmcom_softc *)self;
151 struct pcmcia_attach_args *pa = aux;
152 struct pcmcia_config_entry *cfe;
153 const struct pcmcom_product *pp;
154 size_t size;
155 int i;
156
157 sc->sc_pf = pa->pf;
158
159 pp = (const struct pcmcom_product *)pcmcia_product_lookup(pa,
160 (const struct pcmcia_product *)pcmcom_products,
161 sizeof pcmcom_products[0], NULL);
162 if (pp == NULL) {
163 printf("\n");
164 panic("pcmcom_attach: impossible");
165 }
166
167 printf(": %s\n", pp->pp_product.pp_name);
168
169 /* Allocate the slave info. */
170 sc->sc_nslaves = pp->pp_nslaves;
171 size = sizeof(struct pcmcom_slave_info) * sc->sc_nslaves;
172 sc->sc_slaves = malloc(size, M_DEVBUF, M_NOWAIT);
173 if (sc->sc_slaves == NULL) {
174 printf("%s: unable to allocate slave info\n",
175 sc->sc_dev.dv_xname);
176 return;
177 }
178 memset(sc->sc_slaves, 0, size);
179
180 /*
181 * The address decoders on these cards are stupid. They decode
182 * (usually) 10 bits of address, so you need to allocate the
183 * regions they request in order for the card to differentiate
184 * between serial ports.
185 */
186 for (cfe = pa->pf->cfe_head.sqh_first; cfe != NULL;
187 cfe = cfe->cfe_list.sqe_next) {
188 if (pcmcom_check_cfe(sc, cfe)) {
189 /* Found one! */
190 break;
191 }
192 }
193 if (cfe == NULL) {
194 printf("%s: unable to find a suitable config table entry\n",
195 sc->sc_dev.dv_xname);
196 return;
197 }
198
199 /* Enable the card. */
200 pcmcia_function_init(pa->pf, cfe);
201 if (pcmcia_function_enable(sc->sc_pf)) {
202 printf("%s: function enable failed\n", sc->sc_dev.dv_xname);
203 return;
204 }
205
206 sc->sc_enabled_count = 1;
207
208 /* Attach the children. */
209 for (i = 0; i < sc->sc_nslaves; i++)
210 pcmcom_attach_slave(sc, i);
211
212 sc->sc_enabled_count = 0;
213 pcmcia_function_disable(sc->sc_pf);
214 }
215
216 int
217 pcmcom_check_cfe(sc, cfe)
218 struct pcmcom_softc *sc;
219 struct pcmcia_config_entry *cfe;
220 {
221 struct pcmcom_slave_info *psi;
222 int slave;
223
224 /* We need to have the same number of I/O spaces as we do slaves. */
225 if (cfe->num_iospace != sc->sc_nslaves)
226 return (0);
227
228 for (slave = 0; slave < sc->sc_nslaves; slave++) {
229 psi = &sc->sc_slaves[slave];
230 if (pcmcia_io_alloc(sc->sc_pf,
231 cfe->iospace[slave].start,
232 cfe->iospace[slave].length,
233 cfe->iospace[slave].length,
234 &psi->psi_pcioh))
235 goto release;
236 }
237
238 /* If we got here, we were able to allocate space for all slaves! */
239 return (1);
240
241 release:
242 /* Release the i/o spaces we've allocated. */
243 for (slave--; slave >= 0; slave--) {
244 psi = &sc->sc_slaves[slave];
245 pcmcia_io_free(sc->sc_pf, &psi->psi_pcioh);
246 }
247 return (0);
248 }
249
250 void
251 pcmcom_attach_slave(sc, slave)
252 struct pcmcom_softc *sc;
253 int slave;
254 {
255 struct pcmcom_slave_info *psi = &sc->sc_slaves[slave];
256 struct pcmcom_attach_args pca;
257
258 printf("%s: slave %d", sc->sc_dev.dv_xname, slave);
259
260 if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_IO8, 0, psi->psi_pcioh.size,
261 &psi->psi_pcioh, &psi->psi_io_window)) {
262 printf(": can't map i/o space\n");
263 return;
264 }
265
266 printf("\n");
267
268 pca.pca_iot = psi->psi_pcioh.iot;
269 pca.pca_ioh = psi->psi_pcioh.ioh;
270 pca.pca_slave = slave;
271
272 psi->psi_child = config_found_sm(&sc->sc_dev, &pca, pcmcom_print,
273 pcmcom_submatch);
274 }
275
276 int
277 pcmcom_detach(self, flags)
278 struct device *self;
279 int flags;
280 {
281 struct pcmcom_softc *sc = (struct pcmcom_softc *)self;
282 struct pcmcom_slave_info *psi;
283 int slave, error;
284
285 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) {
286 psi = &sc->sc_slaves[slave];
287 if (psi->psi_child == NULL)
288 continue;
289
290 /* Detach the child. */
291 if ((error = config_detach(self, flags)) != 0)
292 return (error);
293 psi->psi_child = NULL;
294
295 /* Unmap the i/o window. */
296 pcmcia_io_unmap(sc->sc_pf, psi->psi_io_window);
297
298 /* Free the i/o space. */
299 pcmcia_io_free(sc->sc_pf, &psi->psi_pcioh);
300 }
301 return (0);
302 }
303
304 int
305 pcmcom_activate(self, act)
306 struct device *self;
307 enum devact act;
308 {
309 struct pcmcom_softc *sc = (struct pcmcom_softc *)self;
310 struct pcmcom_slave_info *psi;
311 int slave, error = 0, s;
312
313 s = splserial();
314 switch (act) {
315 case DVACT_ACTIVATE:
316 error = EOPNOTSUPP;
317 break;
318
319 case DVACT_DEACTIVATE:
320 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) {
321 psi = &sc->sc_slaves[slave];
322 if (psi->psi_child == NULL)
323 continue;
324
325 /*
326 * Deactivate the child. Doing so will cause our
327 * own enabled count to drop to 0, once all children
328 * are deactivated.
329 */
330 if ((error = config_deactivate(psi->psi_child)) != 0)
331 break;
332 }
333 break;
334 }
335 splx(s);
336 return (error);
337 }
338
339 int
340 pcmcom_print(aux, pnp)
341 void *aux;
342 const char *pnp;
343 {
344 struct pcmcom_attach_args *pca = aux;
345
346 /* only com's can attach to pcmcom's; easy... */
347 if (pnp)
348 printf("com at %s", pnp);
349
350 printf(" slave %d", pca->pca_slave);
351
352 return (UNCONF);
353 }
354
355 int
356 pcmcom_submatch(parent, cf, aux)
357 struct device *parent;
358 struct cfdata *cf;
359 void *aux;
360 {
361 struct pcmcom_attach_args *pca = aux;
362
363 if (cf->cf_loc[PCMCOMCF_SLAVE] != pca->pca_slave &&
364 cf->cf_loc[PCMCOMCF_SLAVE] != PCMCOMCF_SLAVE_DEFAULT)
365 return (0);
366
367 return ((*cf->cf_attach->ca_match)(parent, cf, aux));
368 }
369
370 int
371 pcmcom_intr(arg)
372 void *arg;
373 {
374 #if NCOM > 0
375 struct pcmcom_softc *sc = arg;
376 int i, rval = 0;
377
378 if (sc->sc_enabled_count == 0)
379 return (0);
380
381 for (i = 0; i < sc->sc_nslaves; i++) {
382 if (sc->sc_slaves[i].psi_child != NULL)
383 rval |= comintr(sc->sc_slaves[i].psi_child);
384 }
385
386 return (rval);
387 #else
388 return (0);
389 #endif /* NCOM > 0 */
390 }
391
392 int
393 pcmcom_enable(sc)
394 struct pcmcom_softc *sc;
395 {
396
397 sc->sc_enabled_count++;
398 if (sc->sc_enabled_count > 1)
399 return (0);
400
401 /* Establish the interrupt. */
402 sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_SERIAL,
403 pcmcom_intr, sc);
404 if (sc->sc_ih == NULL) {
405 printf("%s: couldn't establish interrupt\n",
406 sc->sc_dev.dv_xname);
407 return (1);
408 }
409
410 return (pcmcia_function_enable(sc->sc_pf));
411 }
412
413 void
414 pcmcom_disable(sc)
415 struct pcmcom_softc *sc;
416 {
417
418 sc->sc_enabled_count--;
419 if (sc->sc_enabled_count != 0)
420 return;
421
422 pcmcia_function_disable(sc->sc_pf);
423 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
424 }
425
426 /****** Here begins the com attachment code. ******/
427
428 #if NCOM_PCMCOM > 0
429 int com_pcmcom_match __P((struct device *, struct cfdata *, void *));
430 void com_pcmcom_attach __P((struct device *, struct device *, void *));
431
432 /* No pcmcom-specific goo in the softc; it's all in the parent. */
433 struct cfattach com_pcmcom_ca = {
434 sizeof(struct com_softc), com_pcmcom_match, com_pcmcom_attach,
435 com_detach, com_activate
436 };
437
438 int com_pcmcom_enable __P((struct com_softc *));
439 void com_pcmcom_disable __P((struct com_softc *));
440
441 int
442 com_pcmcom_match(parent, cf, aux)
443 struct device *parent;
444 struct cfdata *cf;
445 void *aux;
446 {
447
448 /* Device is always present. */
449 return (1);
450 }
451
452 void
453 com_pcmcom_attach(parent, self, aux)
454 struct device *parent, *self;
455 void *aux;
456 {
457 struct com_softc *sc = (struct com_softc *)self;
458 struct pcmcom_attach_args *pca = aux;
459
460 sc->sc_iot = pca->pca_iot;
461 sc->sc_ioh = pca->pca_ioh;
462
463 sc->enabled = 1;
464
465 sc->sc_iobase = -1;
466 sc->sc_frequency = COM_FREQ;
467
468 sc->enable = com_pcmcom_enable;
469 sc->disable = com_pcmcom_disable;
470
471 com_attach_subr(sc);
472
473 sc->enabled = 0;
474 }
475
476 int
477 com_pcmcom_enable(sc)
478 struct com_softc *sc;
479 {
480
481 return (pcmcom_enable((struct pcmcom_softc *)sc->sc_dev.dv_parent));
482 }
483
484 void
485 com_pcmcom_disable(sc)
486 struct com_softc *sc;
487 {
488
489 pcmcom_disable((struct pcmcom_softc *)sc->sc_dev.dv_parent);
490 }
491 #endif /* NCOM_PCMCOM > 0 */
492