pcmcom.c revision 1.4 1 /* $NetBSD: pcmcom.c,v 1.4 1998/11/20 05:20:20 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 1998 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 struct pcmcom_product {
108 u_int32_t pp_vendor; /* PCMCIA vendor ID */
109 u_int32_t pp_product; /* PCMCIA product ID */
110 int pp_expfunc; /* expected function number */
111 int pp_nslaves; /* number of slaves */
112 const char *pp_name; /* device name */
113 } pcmcom_products[] = {
114 { PCMCIA_VENDOR_SOCKET, PCMCIA_PRODUCT_SOCKET_DUAL_RS232,
115 0, 2,
116 PCMCIA_STR_SOCKET_DUAL_RS232 },
117
118 { 0, 0,
119 0, 0,
120 NULL },
121 };
122
123 int pcmcom_print __P((void *, const char *));
124 int pcmcom_submatch __P((struct device *, struct cfdata *, void *));
125
126 int pcmcom_check_cfe __P((struct pcmcom_softc *,
127 struct pcmcia_config_entry *));
128 void pcmcom_attach_slave __P((struct pcmcom_softc *, int));
129
130 int pcmcom_enable __P((struct pcmcom_softc *));
131 void pcmcom_disable __P((struct pcmcom_softc *));
132
133 int pcmcom_intr __P((void *));
134
135 struct pcmcom_product *pcmcom_lookup __P((struct pcmcia_attach_args *));
136
137 struct pcmcom_product *
138 pcmcom_lookup(pa)
139 struct pcmcia_attach_args *pa;
140 {
141 struct pcmcom_product *pp;
142
143 for (pp = pcmcom_products; pp->pp_name != NULL; pp++)
144 if (pa->manufacturer == pp->pp_vendor &&
145 pa->product == pp->pp_product &&
146 pa->pf->number == pp->pp_expfunc)
147 return (pp);
148
149 return (NULL);
150 }
151
152 int
153 pcmcom_match(parent, cf, aux)
154 struct device *parent;
155 struct cfdata *cf;
156 void *aux;
157 {
158 struct pcmcia_attach_args *pa = aux;
159
160 if (pcmcom_lookup(pa) != NULL)
161 return (10); /* beat com_pcmcia */
162 return (0);
163 }
164
165 void
166 pcmcom_attach(parent, self, aux)
167 struct device *parent, *self;
168 void *aux;
169 {
170 struct pcmcom_softc *sc = (struct pcmcom_softc *)self;
171 struct pcmcia_attach_args *pa = aux;
172 struct pcmcia_config_entry *cfe;
173 struct pcmcom_product *pp;
174 size_t size;
175 int i;
176
177 sc->sc_pf = pa->pf;
178
179 pp = pcmcom_lookup(pa);
180 if (pp == NULL) {
181 printf("\n");
182 panic("pcmcom_attach: impossible");
183 }
184
185 printf(": %s\n", pp->pp_name);
186
187 /* Allocate the slave info. */
188 sc->sc_nslaves = pp->pp_nslaves;
189 size = sizeof(struct pcmcom_slave_info) * sc->sc_nslaves;
190 sc->sc_slaves = malloc(size, M_DEVBUF, M_NOWAIT);
191 if (sc->sc_slaves == NULL) {
192 printf("%s: unable to allocate slave info\n",
193 sc->sc_dev.dv_xname);
194 return;
195 }
196 memset(sc->sc_slaves, 0, size);
197
198 /*
199 * The address decoders on these cards are stupid. They decode
200 * (usually) 10 bits of address, so you need to allocate the
201 * regions they request in order for the card to differentiate
202 * between serial ports.
203 */
204 for (cfe = pa->pf->cfe_head.sqh_first; cfe != NULL;
205 cfe = cfe->cfe_list.sqe_next) {
206 if (pcmcom_check_cfe(sc, cfe)) {
207 /* Found one! */
208 break;
209 }
210 }
211 if (cfe == NULL) {
212 printf("%s: unable to find a suitable config table entry\n",
213 sc->sc_dev.dv_xname);
214 return;
215 }
216
217 /* Enable the card. */
218 pcmcia_function_init(pa->pf, cfe);
219 if (pcmcia_function_enable(sc->sc_pf)) {
220 printf("%s: function enable failed\n", sc->sc_dev.dv_xname);
221 return;
222 }
223
224 sc->sc_enabled_count = 1;
225
226 /* Attach the children. */
227 for (i = 0; i < sc->sc_nslaves; i++)
228 pcmcom_attach_slave(sc, i);
229
230 sc->sc_enabled_count = 0;
231 pcmcia_function_disable(sc->sc_pf);
232 }
233
234 int
235 pcmcom_check_cfe(sc, cfe)
236 struct pcmcom_softc *sc;
237 struct pcmcia_config_entry *cfe;
238 {
239 struct pcmcom_slave_info *psi;
240 int slave;
241
242 /* We need to have the same number of I/O spaces as we do slaves. */
243 if (cfe->num_iospace != sc->sc_nslaves)
244 return (0);
245
246 for (slave = 0; slave < sc->sc_nslaves; slave++) {
247 psi = &sc->sc_slaves[slave];
248 if (pcmcia_io_alloc(sc->sc_pf,
249 cfe->iospace[slave].start,
250 cfe->iospace[slave].length,
251 cfe->iospace[slave].length,
252 &psi->psi_pcioh))
253 goto release;
254 }
255
256 /* If we got here, we were able to allocate space for all slaves! */
257 return (1);
258
259 release:
260 /* Release the i/o spaces we've allocated. */
261 for (slave--; slave >= 0; slave--) {
262 psi = &sc->sc_slaves[slave];
263 pcmcia_io_free(sc->sc_pf, &psi->psi_pcioh);
264 }
265 return (0);
266 }
267
268 void
269 pcmcom_attach_slave(sc, slave)
270 struct pcmcom_softc *sc;
271 int slave;
272 {
273 struct pcmcom_slave_info *psi = &sc->sc_slaves[slave];
274 struct pcmcom_attach_args pca;
275
276 printf("%s: slave %d", sc->sc_dev.dv_xname, slave);
277
278 if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_IO8, 0, psi->psi_pcioh.size,
279 &psi->psi_pcioh, &psi->psi_io_window)) {
280 printf(": can't map i/o space\n");
281 return;
282 }
283
284 printf("\n");
285
286 pca.pca_iot = psi->psi_pcioh.iot;
287 pca.pca_ioh = psi->psi_pcioh.ioh;
288 pca.pca_slave = slave;
289
290 psi->psi_child = config_found_sm(&sc->sc_dev, &pca, pcmcom_print,
291 pcmcom_submatch);
292 }
293
294 int
295 pcmcom_detach(self, flags)
296 struct device *self;
297 int flags;
298 {
299 struct pcmcom_softc *sc = (struct pcmcom_softc *)self;
300 struct pcmcom_slave_info *psi;
301 int slave, error;
302
303 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) {
304 psi = &sc->sc_slaves[slave];
305 if (psi->psi_child == NULL)
306 continue;
307
308 /* Detach the child. */
309 if ((error = config_detach(self, flags)) != 0)
310 return (error);
311 psi->psi_child = NULL;
312
313 /* Unmap the i/o window. */
314 pcmcia_io_unmap(sc->sc_pf, psi->psi_io_window);
315
316 /* Free the i/o space. */
317 pcmcia_io_free(sc->sc_pf, &psi->psi_pcioh);
318 }
319 return (0);
320 }
321
322 int
323 pcmcom_activate(self, act)
324 struct device *self;
325 enum devact act;
326 {
327 struct pcmcom_softc *sc = (struct pcmcom_softc *)self;
328 struct pcmcom_slave_info *psi;
329 int slave, error = 0, s;
330
331 s = splserial();
332 switch (act) {
333 case DVACT_ACTIVATE:
334 error = EOPNOTSUPP;
335 break;
336
337 case DVACT_DEACTIVATE:
338 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) {
339 psi = &sc->sc_slaves[slave];
340 if (psi->psi_child == NULL)
341 continue;
342
343 /*
344 * Deactivate the child. Doing so will cause our
345 * own enabled count to drop to 0, once all children
346 * are deactivated.
347 */
348 if ((error = config_deactivate(psi->psi_child)) != 0)
349 break;
350 }
351 break;
352 }
353 splx(s);
354 return (error);
355 }
356
357 int
358 pcmcom_print(aux, pnp)
359 void *aux;
360 const char *pnp;
361 {
362 struct pcmcom_attach_args *pca = aux;
363
364 /* only com's can attach to pcmcom's; easy... */
365 if (pnp)
366 printf("com at %s", pnp);
367
368 printf(" slave %d", pca->pca_slave);
369
370 return (UNCONF);
371 }
372
373 int
374 pcmcom_submatch(parent, cf, aux)
375 struct device *parent;
376 struct cfdata *cf;
377 void *aux;
378 {
379 struct pcmcom_attach_args *pca = aux;
380
381 if (cf->cf_loc[PCMCOMCF_SLAVE] != pca->pca_slave &&
382 cf->cf_loc[PCMCOMCF_SLAVE] != PCMCOMCF_SLAVE_DEFAULT)
383 return (0);
384
385 return ((*cf->cf_attach->ca_match)(parent, cf, aux));
386 }
387
388 int
389 pcmcom_intr(arg)
390 void *arg;
391 {
392 #if NCOM > 0
393 struct pcmcom_softc *sc = arg;
394 int i, rval = 0;
395
396 if (sc->sc_enabled_count == 0)
397 return (0);
398
399 for (i = 0; i < sc->sc_nslaves; i++) {
400 if (sc->sc_slaves[i].psi_child != NULL)
401 rval |= comintr(sc->sc_slaves[i].psi_child);
402 }
403
404 return (rval);
405 #else
406 return (0);
407 #endif /* NCOM > 0 */
408 }
409
410 int
411 pcmcom_enable(sc)
412 struct pcmcom_softc *sc;
413 {
414
415 sc->sc_enabled_count++;
416 if (sc->sc_enabled_count > 1)
417 return (0);
418
419 /* Establish the interrupt. */
420 sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_SERIAL,
421 pcmcom_intr, sc);
422 if (sc->sc_ih == NULL) {
423 printf("%s: couldn't establish interrupt\n",
424 sc->sc_dev.dv_xname);
425 return (1);
426 }
427
428 return (pcmcia_function_enable(sc->sc_pf));
429 }
430
431 void
432 pcmcom_disable(sc)
433 struct pcmcom_softc *sc;
434 {
435
436 sc->sc_enabled_count--;
437 if (sc->sc_enabled_count != 0)
438 return;
439
440 pcmcia_function_disable(sc->sc_pf);
441 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
442 }
443
444 /****** Here begins the com attachment code. ******/
445
446 #if NCOM_PCMCOM > 0
447 int com_pcmcom_match __P((struct device *, struct cfdata *, void *));
448 void com_pcmcom_attach __P((struct device *, struct device *, void *));
449
450 /* No pcmcom-specific goo in the softc; it's all in the parent. */
451 struct cfattach com_pcmcom_ca = {
452 sizeof(struct com_softc), com_pcmcom_match, com_pcmcom_attach,
453 com_detach, com_activate
454 };
455
456 int com_pcmcom_enable __P((struct com_softc *));
457 void com_pcmcom_disable __P((struct com_softc *));
458
459 int
460 com_pcmcom_match(parent, cf, aux)
461 struct device *parent;
462 struct cfdata *cf;
463 void *aux;
464 {
465
466 /* Device is always present. */
467 return (1);
468 }
469
470 void
471 com_pcmcom_attach(parent, self, aux)
472 struct device *parent, *self;
473 void *aux;
474 {
475 struct com_softc *sc = (struct com_softc *)self;
476 struct pcmcom_attach_args *pca = aux;
477
478 sc->sc_iot = pca->pca_iot;
479 sc->sc_ioh = pca->pca_ioh;
480
481 sc->enabled = 1;
482
483 sc->sc_iobase = -1;
484 sc->sc_frequency = COM_FREQ;
485
486 sc->enable = com_pcmcom_enable;
487 sc->disable = com_pcmcom_disable;
488
489 com_attach_subr(sc);
490
491 sc->enabled = 0;
492 }
493
494 int
495 com_pcmcom_enable(sc)
496 struct com_softc *sc;
497 {
498
499 return (pcmcom_enable((struct pcmcom_softc *)sc->sc_dev.dv_parent));
500 }
501
502 void
503 com_pcmcom_disable(sc)
504 struct com_softc *sc;
505 {
506
507 pcmcom_disable((struct pcmcom_softc *)sc->sc_dev.dv_parent);
508 }
509 #endif /* NCOM_PCMCOM > 0 */
510