1 | /* $NetBSD: pcmcia.c,v 1.94 2011/07/26 22:24:36 dyoung Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2004 Charles M. Hannum. All rights reserved. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * 3. All advertising materials mentioning features or use of this software |
15 | * must display the following acknowledgement: |
16 | * This product includes software developed by Charles M. Hannum. |
17 | * 4. The name of the author may not be used to endorse or promote products |
18 | * derived from this software without specific prior written permission. |
19 | */ |
20 | |
21 | /* |
22 | * Copyright (c) 1997 Marc Horowitz. All rights reserved. |
23 | * |
24 | * Redistribution and use in source and binary forms, with or without |
25 | * modification, are permitted provided that the following conditions |
26 | * are met: |
27 | * 1. Redistributions of source code must retain the above copyright |
28 | * notice, this list of conditions and the following disclaimer. |
29 | * 2. Redistributions in binary form must reproduce the above copyright |
30 | * notice, this list of conditions and the following disclaimer in the |
31 | * documentation and/or other materials provided with the distribution. |
32 | * 3. All advertising materials mentioning features or use of this software |
33 | * must display the following acknowledgement: |
34 | * This product includes software developed by Marc Horowitz. |
35 | * 4. The name of the author may not be used to endorse or promote products |
36 | * derived from this software without specific prior written permission. |
37 | * |
38 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
39 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
40 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
41 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
42 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
43 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
44 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
45 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
46 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
47 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
48 | */ |
49 | |
50 | #include <sys/cdefs.h> |
51 | __KERNEL_RCSID(0, "$NetBSD: pcmcia.c,v 1.94 2011/07/26 22:24:36 dyoung Exp $" ); |
52 | |
53 | #include "opt_pcmciaverbose.h" |
54 | |
55 | #include <sys/param.h> |
56 | #include <sys/systm.h> |
57 | #include <sys/device.h> |
58 | |
59 | #include <net/if.h> |
60 | |
61 | #include <dev/pcmcia/pcmciareg.h> |
62 | #include <dev/pcmcia/pcmciachip.h> |
63 | #include <dev/pcmcia/pcmciavar.h> |
64 | #ifdef IT8368E_LEGACY_MODE /* XXX -uch */ |
65 | #include <arch/hpcmips/dev/it8368var.h> |
66 | #endif |
67 | |
68 | #include "locators.h" |
69 | |
70 | #ifdef PCMCIADEBUG |
71 | int pcmcia_debug = 0; |
72 | #define DPRINTF(arg) if (pcmcia_debug) printf arg |
73 | #else |
74 | #define DPRINTF(arg) |
75 | #endif |
76 | |
77 | #ifdef PCMCIAVERBOSE |
78 | int pcmcia_verbose = 1; |
79 | #else |
80 | int pcmcia_verbose = 0; |
81 | #endif |
82 | |
83 | int pcmcia_match(device_t, cfdata_t, void *); |
84 | void pcmcia_attach(device_t, device_t, void *); |
85 | int pcmcia_detach(device_t, int); |
86 | int pcmcia_rescan(device_t, const char *, const int *); |
87 | void pcmcia_childdetached(device_t, device_t); |
88 | int pcmcia_print(void *, const char *); |
89 | |
90 | CFATTACH_DECL3_NEW(pcmcia, sizeof(struct pcmcia_softc), |
91 | pcmcia_match, pcmcia_attach, pcmcia_detach, NULL, |
92 | pcmcia_rescan, pcmcia_childdetached, DVF_DETACH_SHUTDOWN); |
93 | |
94 | int |
95 | pcmcia_ccr_read(struct pcmcia_function *pf, int ccr) |
96 | { |
97 | |
98 | return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, |
99 | pf->pf_ccr_offset + ccr * 2)); |
100 | } |
101 | |
102 | void |
103 | pcmcia_ccr_write(struct pcmcia_function *pf, int ccr, int val) |
104 | { |
105 | |
106 | if (pf->ccr_mask & (1 << ccr)) { |
107 | bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, |
108 | pf->pf_ccr_offset + ccr * 2, val); |
109 | } |
110 | } |
111 | |
112 | int |
113 | pcmcia_match(device_t parent, cfdata_t match, void *aux) |
114 | { |
115 | struct pcmciabus_attach_args *paa = aux; |
116 | |
117 | if (strcmp(paa->paa_busname, match->cf_name)) { |
118 | return 0; |
119 | } |
120 | /* if the autoconfiguration got this far, there's a socket here */ |
121 | return (1); |
122 | } |
123 | |
124 | void |
125 | pcmcia_attach(device_t parent, device_t self, void *aux) |
126 | { |
127 | struct pcmciabus_attach_args *paa = aux; |
128 | struct pcmcia_softc *sc = device_private(self); |
129 | |
130 | aprint_naive("\n" ); |
131 | aprint_normal("\n" ); |
132 | |
133 | sc->dev = self; |
134 | sc->pct = paa->pct; |
135 | sc->pch = paa->pch; |
136 | |
137 | sc->ih = NULL; |
138 | |
139 | if (!pmf_device_register(self, NULL, NULL)) |
140 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
141 | } |
142 | |
143 | int |
144 | pcmcia_detach(device_t self, int flags) |
145 | { |
146 | int rc; |
147 | |
148 | if ((rc = config_detach_children(self, flags)) != 0) |
149 | return rc; |
150 | |
151 | pmf_device_deregister(self); |
152 | return 0; |
153 | } |
154 | |
155 | int |
156 | pcmcia_card_attach(device_t dev) |
157 | { |
158 | struct pcmcia_softc *sc = device_private(dev); |
159 | struct pcmcia_function *pf; |
160 | int error; |
161 | static const int wildcard[PCMCIACF_NLOCS] = { |
162 | PCMCIACF_FUNCTION_DEFAULT |
163 | }; |
164 | |
165 | /* |
166 | * this is here so that when socket_enable calls gettype, trt happens |
167 | */ |
168 | SIMPLEQ_FIRST(&sc->card.pf_head) = NULL; |
169 | |
170 | pcmcia_socket_enable(dev); |
171 | |
172 | pcmcia_read_cis(sc); |
173 | pcmcia_check_cis_quirks(sc); |
174 | |
175 | #if 1 /* XXX remove this, done below ??? */ |
176 | /* |
177 | * bail now if the card has no functions, or if there was an error in |
178 | * the cis. |
179 | */ |
180 | if (sc->card.error || |
181 | SIMPLEQ_EMPTY(&sc->card.pf_head)) { |
182 | printf("%s: card appears to have bogus CIS\n" , |
183 | device_xname(sc->dev)); |
184 | error = EIO; |
185 | goto done; |
186 | } |
187 | #endif |
188 | |
189 | if (pcmcia_verbose) |
190 | pcmcia_print_cis(sc); |
191 | |
192 | SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) { |
193 | if (SIMPLEQ_EMPTY(&pf->cfe_head)) |
194 | continue; |
195 | |
196 | #ifdef DIAGNOSTIC |
197 | if (pf->child != NULL) { |
198 | printf("%s: %s still attached to function %d!\n" , |
199 | device_xname(sc->dev), device_xname(pf->child), |
200 | pf->number); |
201 | panic("pcmcia_card_attach" ); |
202 | } |
203 | #endif |
204 | pf->sc = sc; |
205 | pf->child = NULL; |
206 | pf->cfe = NULL; |
207 | pf->pf_ih = NULL; |
208 | } |
209 | |
210 | error = pcmcia_rescan(dev, "pcmcia" , wildcard); |
211 | done: |
212 | pcmcia_socket_disable(dev); |
213 | return (error); |
214 | } |
215 | |
216 | int |
217 | pcmcia_rescan(device_t self, const char *ifattr, |
218 | const int *locators) |
219 | { |
220 | struct pcmcia_softc *sc = device_private(self); |
221 | struct pcmcia_function *pf; |
222 | struct pcmcia_attach_args paa; |
223 | int locs[PCMCIACF_NLOCS]; |
224 | |
225 | if (sc->card.error || |
226 | SIMPLEQ_EMPTY(&sc->card.pf_head)) { |
227 | /* XXX silently ignore if no card present? */ |
228 | return (EIO); |
229 | } |
230 | |
231 | SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) { |
232 | if (SIMPLEQ_EMPTY(&pf->cfe_head)) |
233 | continue; |
234 | |
235 | if ((locators[PCMCIACF_FUNCTION] != PCMCIACF_FUNCTION_DEFAULT) |
236 | && (locators[PCMCIACF_FUNCTION] != pf->number)) |
237 | continue; |
238 | |
239 | if (pf->child) |
240 | continue; |
241 | |
242 | locs[PCMCIACF_FUNCTION] = pf->number; |
243 | |
244 | paa.manufacturer = sc->card.manufacturer; |
245 | paa.product = sc->card.product; |
246 | paa.card = &sc->card; |
247 | paa.pf = pf; |
248 | |
249 | pf->child = config_found_sm_loc(self, "pcmcia" , locs, &paa, |
250 | pcmcia_print, |
251 | config_stdsubmatch); |
252 | } |
253 | |
254 | return (0); |
255 | } |
256 | |
257 | void |
258 | pcmcia_card_detach(device_t dev, int flags) |
259 | /* flags: DETACH_* flags */ |
260 | { |
261 | struct pcmcia_softc *sc = device_private(dev); |
262 | struct pcmcia_function *pf; |
263 | int error; |
264 | |
265 | /* |
266 | * We are running on either the PCMCIA socket's event thread |
267 | * or in user context detaching a device by user request. |
268 | */ |
269 | SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) { |
270 | pf->pf_flags |= PFF_DETACHED; |
271 | if (SIMPLEQ_EMPTY(&pf->cfe_head)) |
272 | continue; |
273 | if (pf->child == NULL) |
274 | continue; |
275 | DPRINTF(("%s: detaching %s (function %d)\n" , |
276 | device_xname(sc->dev), device_xname(pf->child), pf->number)); |
277 | if ((error = config_detach(pf->child, flags)) != 0) { |
278 | printf("%s: error %d detaching %s (function %d)\n" , |
279 | device_xname(sc->dev), error, device_xname(pf->child), |
280 | pf->number); |
281 | } |
282 | } |
283 | |
284 | if (sc->sc_enabled_count != 0) { |
285 | #ifdef DIAGNOSTIC |
286 | printf("pcmcia_card_detach: enabled_count should be 0 here??\n" ); |
287 | #endif |
288 | pcmcia_chip_socket_disable(sc->pct, sc->pch); |
289 | sc->sc_enabled_count = 0; |
290 | } |
291 | } |
292 | |
293 | void |
294 | pcmcia_childdetached(device_t self, device_t child) |
295 | { |
296 | struct pcmcia_softc *sc = device_private(self); |
297 | struct pcmcia_function *pf; |
298 | |
299 | SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) { |
300 | if (SIMPLEQ_EMPTY(&pf->cfe_head)) |
301 | continue; |
302 | if (pf->child == child) { |
303 | KASSERT(device_locator(child, PCMCIACF_FUNCTION) |
304 | == pf->number); |
305 | pf->child = NULL; |
306 | return; |
307 | } |
308 | } |
309 | |
310 | aprint_error_dev(self, "pcmcia_childdetached: %s not found\n" , |
311 | device_xname(child)); |
312 | } |
313 | |
314 | void |
315 | pcmcia_card_deactivate(device_t dev) |
316 | { |
317 | struct pcmcia_softc *sc = device_private(dev); |
318 | struct pcmcia_function *pf; |
319 | |
320 | /* |
321 | * We're in the chip's card removal interrupt handler. |
322 | * Deactivate the child driver. The PCMCIA socket's |
323 | * event thread will run later to finish the detach. |
324 | */ |
325 | SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) { |
326 | if (SIMPLEQ_EMPTY(&pf->cfe_head)) |
327 | continue; |
328 | if (pf->child == NULL) |
329 | continue; |
330 | DPRINTF(("%s: deactivating %s (function %d)\n" , |
331 | device_xname(sc->dev), device_xname(pf->child), pf->number)); |
332 | config_deactivate(pf->child); |
333 | } |
334 | } |
335 | |
336 | int |
337 | pcmcia_print(void *arg, const char *pnp) |
338 | { |
339 | struct pcmcia_attach_args *pa = arg; |
340 | struct pcmcia_softc *sc = pa->pf->sc; |
341 | struct pcmcia_card *card = &sc->card; |
342 | char devinfo[256]; |
343 | |
344 | if (pnp) |
345 | aprint_normal("%s" , pnp); |
346 | |
347 | pcmcia_devinfo(card, !!pnp, devinfo, sizeof(devinfo)); |
348 | |
349 | aprint_normal(" function %d: %s\n" , pa->pf->number, devinfo); |
350 | |
351 | return (UNCONF); |
352 | } |
353 | |
354 | void |
355 | pcmcia_devinfo(struct pcmcia_card *card, int showhex, char *cp, size_t cplen) |
356 | { |
357 | int i, n; |
358 | |
359 | if (cplen > 1) { |
360 | *cp++ = '<'; |
361 | *cp = '\0'; |
362 | cplen--; |
363 | } |
364 | |
365 | for (i = 0; i < 4 && card->cis1_info[i] != NULL && cplen > 1; i++) { |
366 | n = snprintf(cp, cplen, "%s%s" , i ? ", " : "" , |
367 | card->cis1_info[i]); |
368 | cp += n; |
369 | if (cplen < n) |
370 | return; |
371 | cplen -= n; |
372 | } |
373 | |
374 | if (cplen > 1) { |
375 | *cp++ = '>'; |
376 | *cp = '\0'; |
377 | cplen--; |
378 | } |
379 | |
380 | if (showhex && cplen > 1) |
381 | snprintf(cp, cplen, " (manufacturer 0x%04x, product 0x%04x)" , |
382 | card->manufacturer, card->product); |
383 | } |
384 | |
385 | const void * |
386 | pcmcia_product_lookup(struct pcmcia_attach_args *pa, const void *tab, size_t nent, size_t ent_size, pcmcia_product_match_fn matchfn) |
387 | { |
388 | const struct pcmcia_product *pp; |
389 | int n; |
390 | int matches; |
391 | |
392 | #ifdef DIAGNOSTIC |
393 | if (sizeof *pp > ent_size) |
394 | panic("pcmcia_product_lookup: bogus ent_size %ld" , |
395 | (long) ent_size); |
396 | #endif |
397 | |
398 | for (pp = tab, n = nent; n; pp = (const struct pcmcia_product *) |
399 | ((const char *)pp + ent_size), n--) { |
400 | /* see if it matches vendor/product */ |
401 | matches = 0; |
402 | if ((pp->pp_vendor != PCMCIA_VENDOR_INVALID && |
403 | pp->pp_vendor == pa->manufacturer) && |
404 | (pp->pp_product != PCMCIA_PRODUCT_INVALID && |
405 | pp->pp_product == pa->product)) |
406 | matches = 1; |
407 | if ((pp->pp_cisinfo[0] && pa->card->cis1_info[0] && |
408 | !strcmp(pp->pp_cisinfo[0], pa->card->cis1_info[0])) && |
409 | (pp->pp_cisinfo[1] && pa->card->cis1_info[1] && |
410 | !strcmp(pp->pp_cisinfo[1], pa->card->cis1_info[1])) && |
411 | (!pp->pp_cisinfo[2] || (pa->card->cis1_info[2] && |
412 | !strcmp(pp->pp_cisinfo[2], pa->card->cis1_info[2]))) && |
413 | (!pp->pp_cisinfo[3] || (pa->card->cis1_info[3] && |
414 | !strcmp(pp->pp_cisinfo[3], pa->card->cis1_info[3])))) |
415 | matches = 1; |
416 | |
417 | /* if a separate match function is given, let it override */ |
418 | if (matchfn) |
419 | matches = (*matchfn)(pa, pp, matches); |
420 | |
421 | if (matches) |
422 | return (pp); |
423 | } |
424 | return (0); |
425 | } |
426 | |
427 | void |
428 | pcmcia_socket_settype(device_t dev, int type) |
429 | { |
430 | struct pcmcia_softc *sc = device_private(dev); |
431 | |
432 | pcmcia_chip_socket_settype(sc->pct, sc->pch, type); |
433 | } |
434 | |
435 | /* |
436 | * Initialize a PCMCIA function. May be called as long as the function is |
437 | * disabled. |
438 | */ |
439 | void |
440 | pcmcia_function_init(struct pcmcia_function *pf, struct pcmcia_config_entry *cfe) |
441 | { |
442 | if (pf->pf_flags & PFF_ENABLED) |
443 | panic("pcmcia_function_init: function is enabled" ); |
444 | |
445 | /* Remember which configuration entry we are using. */ |
446 | pf->cfe = cfe; |
447 | } |
448 | |
449 | void |
450 | pcmcia_socket_enable(device_t dev) |
451 | { |
452 | struct pcmcia_softc *sc = device_private(dev); |
453 | |
454 | if (sc->sc_enabled_count++ == 0) |
455 | pcmcia_chip_socket_enable(sc->pct, sc->pch); |
456 | DPRINTF(("%s: ++enabled_count = %d\n" , device_xname(sc->dev), |
457 | sc->sc_enabled_count)); |
458 | } |
459 | |
460 | void |
461 | pcmcia_socket_disable(device_t dev) |
462 | { |
463 | struct pcmcia_softc *sc = device_private(dev); |
464 | |
465 | if (--sc->sc_enabled_count == 0) |
466 | pcmcia_chip_socket_disable(sc->pct, sc->pch); |
467 | DPRINTF(("%s: --enabled_count = %d\n" , device_xname(sc->dev), |
468 | sc->sc_enabled_count)); |
469 | } |
470 | |
471 | /* Enable a PCMCIA function */ |
472 | int |
473 | pcmcia_function_enable(struct pcmcia_function *pf) |
474 | { |
475 | struct pcmcia_softc *sc = pf->sc; |
476 | struct pcmcia_function *tmp; |
477 | int reg; |
478 | int error; |
479 | |
480 | if (pf->cfe == NULL) |
481 | panic("pcmcia_function_enable: function not initialized" ); |
482 | |
483 | /* |
484 | * Increase the reference count on the socket, enabling power, if |
485 | * necessary. |
486 | */ |
487 | pcmcia_socket_enable(sc->dev); |
488 | pcmcia_socket_settype(sc->dev, pf->cfe->iftype); |
489 | |
490 | if (pf->pf_flags & PFF_ENABLED) { |
491 | /* |
492 | * Don't do anything if we're already enabled. |
493 | */ |
494 | return (0); |
495 | } |
496 | |
497 | /* |
498 | * it's possible for different functions' CCRs to be in the same |
499 | * underlying page. Check for that. |
500 | */ |
501 | |
502 | SIMPLEQ_FOREACH(tmp, &sc->card.pf_head, pf_list) { |
503 | if ((tmp->pf_flags & PFF_ENABLED) && |
504 | (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && |
505 | ((pf->ccr_base + PCMCIA_CCR_SIZE) <= |
506 | (tmp->ccr_base - tmp->pf_ccr_offset + |
507 | tmp->pf_ccr_realsize))) { |
508 | pf->pf_ccrt = tmp->pf_ccrt; |
509 | pf->pf_ccrh = tmp->pf_ccrh; |
510 | pf->pf_ccr_realsize = tmp->pf_ccr_realsize; |
511 | |
512 | /* |
513 | * pf->pf_ccr_offset = (tmp->pf_ccr_offset - |
514 | * tmp->ccr_base) + pf->ccr_base; |
515 | */ |
516 | pf->pf_ccr_offset = |
517 | (tmp->pf_ccr_offset + pf->ccr_base) - |
518 | tmp->ccr_base; |
519 | pf->pf_ccr_window = tmp->pf_ccr_window; |
520 | break; |
521 | } |
522 | } |
523 | |
524 | if (tmp == NULL) { |
525 | error = pcmcia_mem_alloc(pf, PCMCIA_CCR_SIZE, &pf->pf_pcmh); |
526 | if (error) |
527 | goto bad; |
528 | |
529 | error = pcmcia_mem_map(pf, PCMCIA_MEM_ATTR, pf->ccr_base, |
530 | PCMCIA_CCR_SIZE, &pf->pf_pcmh, &pf->pf_ccr_offset, |
531 | &pf->pf_ccr_window); |
532 | if (error) { |
533 | pcmcia_mem_free(pf, &pf->pf_pcmh); |
534 | goto bad; |
535 | } |
536 | } |
537 | |
538 | if (pcmcia_mfc(sc) || 1) { |
539 | pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE0, |
540 | (pf->pf_mfc_iobase >> 0) & 0xff); |
541 | pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE1, |
542 | (pf->pf_mfc_iobase >> 8) & 0xff); |
543 | pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE2, |
544 | (pf->pf_mfc_iobase >> 16) & 0xff); |
545 | pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE3, |
546 | (pf->pf_mfc_iobase >> 24) & 0xff); |
547 | pcmcia_ccr_write(pf, PCMCIA_CCR_IOLIMIT, |
548 | pf->pf_mfc_iomax - pf->pf_mfc_iobase); |
549 | } |
550 | |
551 | reg = 0; |
552 | if (pf->cfe->flags & PCMCIA_CFE_AUDIO) |
553 | reg |= PCMCIA_CCR_STATUS_AUDIO; |
554 | pcmcia_ccr_write(pf, PCMCIA_CCR_STATUS, reg); |
555 | |
556 | pcmcia_ccr_write(pf, PCMCIA_CCR_SOCKETCOPY, 0); |
557 | |
558 | reg = (pf->cfe->number & PCMCIA_CCR_OPTION_CFINDEX); |
559 | reg |= PCMCIA_CCR_OPTION_LEVIREQ; |
560 | if (pcmcia_mfc(sc)) { |
561 | reg |= (PCMCIA_CCR_OPTION_FUNC_ENABLE | |
562 | PCMCIA_CCR_OPTION_ADDR_DECODE); |
563 | if (pf->pf_ih) |
564 | reg |= PCMCIA_CCR_OPTION_IREQ_ENABLE; |
565 | |
566 | } |
567 | pcmcia_ccr_write(pf, PCMCIA_CCR_OPTION, reg); |
568 | |
569 | #ifdef PCMCIADEBUG |
570 | if (pcmcia_debug) { |
571 | SIMPLEQ_FOREACH(tmp, &sc->card.pf_head, pf_list) { |
572 | printf("%s: function %d CCR at %d offset %lx: " |
573 | "%x %x %x %x, %x %x %x %x, %x\n" , |
574 | device_xname(tmp->sc->dev), tmp->number, |
575 | tmp->pf_ccr_window, |
576 | (unsigned long) tmp->pf_ccr_offset, |
577 | pcmcia_ccr_read(tmp, 0), |
578 | pcmcia_ccr_read(tmp, 1), |
579 | pcmcia_ccr_read(tmp, 2), |
580 | pcmcia_ccr_read(tmp, 3), |
581 | |
582 | pcmcia_ccr_read(tmp, 5), |
583 | pcmcia_ccr_read(tmp, 6), |
584 | pcmcia_ccr_read(tmp, 7), |
585 | pcmcia_ccr_read(tmp, 8), |
586 | |
587 | pcmcia_ccr_read(tmp, 9)); |
588 | } |
589 | } |
590 | #endif |
591 | |
592 | #ifdef IT8368E_LEGACY_MODE |
593 | /* return to I/O mode */ |
594 | it8368_mode(pf, IT8368_IO_MODE, IT8368_WIDTH_16); |
595 | #endif |
596 | |
597 | pf->pf_flags |= PFF_ENABLED; |
598 | return (0); |
599 | |
600 | bad: |
601 | /* |
602 | * Decrement the reference count, and power down the socket, if |
603 | * necessary. |
604 | */ |
605 | printf("%s: couldn't map the CCR\n" , device_xname(pf->child)); |
606 | pcmcia_socket_disable(sc->dev); |
607 | |
608 | return (error); |
609 | } |
610 | |
611 | /* Disable PCMCIA function. */ |
612 | void |
613 | pcmcia_function_disable(struct pcmcia_function *pf) |
614 | { |
615 | struct pcmcia_softc *sc = pf->sc; |
616 | struct pcmcia_function *tmp; |
617 | int reg; |
618 | |
619 | if (pf->cfe == NULL) |
620 | panic("pcmcia_function_enable: function not initialized" ); |
621 | |
622 | if ((pf->pf_flags & PFF_ENABLED) == 0) { |
623 | /* |
624 | * Don't do anything but decrement if we're already disabled. |
625 | */ |
626 | goto out; |
627 | } |
628 | |
629 | if (pcmcia_mfc(sc) && |
630 | (pf->pf_flags & PFF_DETACHED) == 0) { |
631 | reg = pcmcia_ccr_read(pf, PCMCIA_CCR_OPTION); |
632 | reg &= ~(PCMCIA_CCR_OPTION_FUNC_ENABLE| |
633 | PCMCIA_CCR_OPTION_ADDR_DECODE| |
634 | PCMCIA_CCR_OPTION_IREQ_ENABLE); |
635 | pcmcia_ccr_write(pf, PCMCIA_CCR_OPTION, reg); |
636 | } |
637 | |
638 | /* |
639 | * it's possible for different functions' CCRs to be in the same |
640 | * underlying page. Check for that. Note we mark us as disabled |
641 | * first to avoid matching ourself. |
642 | */ |
643 | |
644 | pf->pf_flags &= ~PFF_ENABLED; |
645 | SIMPLEQ_FOREACH(tmp, &sc->card.pf_head, pf_list) { |
646 | if ((tmp->pf_flags & PFF_ENABLED) && |
647 | (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && |
648 | ((pf->ccr_base + PCMCIA_CCR_SIZE) <= |
649 | (tmp->ccr_base - tmp->pf_ccr_offset + tmp->pf_ccr_realsize))) |
650 | break; |
651 | } |
652 | |
653 | /* Not used by anyone else; unmap the CCR. */ |
654 | if (tmp == NULL) { |
655 | pcmcia_mem_unmap(pf, pf->pf_ccr_window); |
656 | pcmcia_mem_free(pf, &pf->pf_pcmh); |
657 | } |
658 | |
659 | out: |
660 | /* |
661 | * Decrement the reference count, and power down the socket, if |
662 | * necessary. |
663 | */ |
664 | pcmcia_socket_disable(sc->dev); |
665 | } |
666 | |
667 | int |
668 | pcmcia_io_map(struct pcmcia_function *pf, int width, struct pcmcia_io_handle *pcihp, int *windowp) |
669 | { |
670 | struct pcmcia_softc *sc = pf->sc; |
671 | int error; |
672 | |
673 | if (pf->pf_flags & PFF_ENABLED) |
674 | printf("pcmcia_io_map: function is enabled!\n" ); |
675 | |
676 | error = pcmcia_chip_io_map(sc->pct, sc->pch, |
677 | width, 0, pcihp->size, pcihp, windowp); |
678 | if (error) |
679 | return (error); |
680 | |
681 | /* |
682 | * XXX in the multifunction multi-iospace-per-function case, this |
683 | * needs to cooperate with io_alloc to make sure that the spaces |
684 | * don't overlap, and that the ccr's are set correctly |
685 | */ |
686 | |
687 | if (pcmcia_mfc(sc) || 1) { |
688 | bus_addr_t iobase = pcihp->addr; |
689 | bus_addr_t iomax = pcihp->addr + pcihp->size - 1; |
690 | |
691 | DPRINTF(("window iobase %lx iomax %lx\n" , (long)iobase, |
692 | (long)iomax)); |
693 | if (pf->pf_mfc_iobase == 0) { |
694 | pf->pf_mfc_iobase = iobase; |
695 | pf->pf_mfc_iomax = iomax; |
696 | } else { |
697 | if (iobase < pf->pf_mfc_iobase) |
698 | pf->pf_mfc_iobase = iobase; |
699 | if (iomax > pf->pf_mfc_iomax) |
700 | pf->pf_mfc_iomax = iomax; |
701 | } |
702 | DPRINTF(("function iobase %lx iomax %lx\n" , |
703 | (long)pf->pf_mfc_iobase, (long)pf->pf_mfc_iomax)); |
704 | } |
705 | |
706 | return (0); |
707 | } |
708 | |
709 | void |
710 | pcmcia_io_unmap(struct pcmcia_function *pf, int window) |
711 | { |
712 | struct pcmcia_softc *sc = pf->sc; |
713 | |
714 | if (pf->pf_flags & PFF_ENABLED) |
715 | printf("pcmcia_io_unmap: function is enabled!\n" ); |
716 | |
717 | pcmcia_chip_io_unmap(sc->pct, sc->pch, window); |
718 | } |
719 | |
720 | void * |
721 | pcmcia_intr_establish(struct pcmcia_function *pf, int ipl, |
722 | int (*ih_fct)(void *), void *ih_arg) |
723 | { |
724 | |
725 | if (pf->pf_flags & PFF_ENABLED) |
726 | printf("pcmcia_intr_establish: function is enabled!\n" ); |
727 | if (pf->pf_ih) |
728 | panic("pcmcia_intr_establish: already done\n" ); |
729 | |
730 | pf->pf_ih = pcmcia_chip_intr_establish(pf->sc->pct, pf->sc->pch, |
731 | pf, ipl, ih_fct, ih_arg); |
732 | if (!pf->pf_ih) |
733 | aprint_error_dev(pf->child, "interrupt establish failed\n" ); |
734 | return (pf->pf_ih); |
735 | } |
736 | |
737 | void |
738 | pcmcia_intr_disestablish(struct pcmcia_function *pf, void *ih) |
739 | { |
740 | |
741 | if (pf->pf_flags & PFF_ENABLED) |
742 | printf("pcmcia_intr_disestablish: function is enabled!\n" ); |
743 | if (!pf->pf_ih) |
744 | panic("pcmcia_intr_distestablish: already done\n" ); |
745 | |
746 | pcmcia_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, ih); |
747 | pf->pf_ih = 0; |
748 | } |
749 | |
750 | int |
751 | pcmcia_config_alloc(struct pcmcia_function *pf, struct pcmcia_config_entry *cfe) |
752 | { |
753 | int error = 0; |
754 | int n, m; |
755 | |
756 | for (n = 0; n < cfe->num_iospace; n++) { |
757 | bus_addr_t start = cfe->iospace[n].start; |
758 | bus_size_t length = cfe->iospace[n].length; |
759 | bus_size_t align = cfe->iomask ? (1 << cfe->iomask) : |
760 | length; |
761 | bus_size_t skew = start & (align - 1); |
762 | |
763 | if ((start - skew) == 0 && align < 0x400) { |
764 | if (skew) |
765 | printf("Drats! I need a skew!\n" ); |
766 | start = 0; |
767 | } |
768 | |
769 | DPRINTF(("pcmcia_config_alloc: io %d start=%lx length=%lx align=%lx skew=%lx\n" , |
770 | n, (long)start, (long)length, (long)align, (long)skew)); |
771 | |
772 | error = pcmcia_io_alloc(pf, start, length, align, |
773 | &cfe->iospace[n].handle); |
774 | if (error) |
775 | break; |
776 | } |
777 | if (n < cfe->num_iospace) { |
778 | for (m = 0; m < n; m++) |
779 | pcmcia_io_free(pf, &cfe->iospace[m].handle); |
780 | return (error); |
781 | } |
782 | |
783 | for (n = 0; n < cfe->num_memspace; n++) { |
784 | bus_size_t length = cfe->memspace[n].length; |
785 | |
786 | DPRINTF(("pcmcia_config_alloc: mem %d length %lx\n" , n, |
787 | (long)length)); |
788 | |
789 | error = pcmcia_mem_alloc(pf, length, &cfe->memspace[n].handle); |
790 | if (error) |
791 | break; |
792 | } |
793 | if (n < cfe->num_memspace) { |
794 | for (m = 0; m < cfe->num_iospace; m++) |
795 | pcmcia_io_free(pf, &cfe->iospace[m].handle); |
796 | for (m = 0; m < n; m++) |
797 | pcmcia_mem_free(pf, &cfe->memspace[m].handle); |
798 | return (error); |
799 | } |
800 | |
801 | /* This one's good! */ |
802 | return (error); |
803 | } |
804 | |
805 | void |
806 | pcmcia_config_free(struct pcmcia_function *pf) |
807 | { |
808 | struct pcmcia_config_entry *cfe = pf->cfe; |
809 | int m; |
810 | |
811 | for (m = 0; m < cfe->num_iospace; m++) |
812 | pcmcia_io_free(pf, &cfe->iospace[m].handle); |
813 | for (m = 0; m < cfe->num_memspace; m++) |
814 | pcmcia_mem_free(pf, &cfe->memspace[m].handle); |
815 | } |
816 | |
817 | int |
818 | pcmcia_config_map(struct pcmcia_function *pf) |
819 | { |
820 | struct pcmcia_config_entry *cfe = pf->cfe; |
821 | int error = 0; |
822 | int n, m; |
823 | |
824 | for (n = 0; n < cfe->num_iospace; n++) { |
825 | int width; |
826 | |
827 | if (cfe->flags & PCMCIA_CFE_IO16) |
828 | width = PCMCIA_WIDTH_AUTO; |
829 | else |
830 | width = PCMCIA_WIDTH_IO8; |
831 | error = pcmcia_io_map(pf, width, &cfe->iospace[n].handle, |
832 | &cfe->iospace[n].window); |
833 | if (error) |
834 | break; |
835 | } |
836 | if (n < cfe->num_iospace) { |
837 | for (m = 0; m < n; m++) |
838 | pcmcia_io_unmap(pf, cfe->iospace[m].window); |
839 | return (error); |
840 | } |
841 | |
842 | for (n = 0; n < cfe->num_memspace; n++) { |
843 | bus_size_t length = cfe->memspace[n].length; |
844 | int width; |
845 | |
846 | DPRINTF(("pcmcia_config_alloc: mem %d length %lx\n" , n, |
847 | (long)length)); |
848 | |
849 | /*XXX*/ |
850 | width = PCMCIA_WIDTH_MEM8|PCMCIA_MEM_COMMON; |
851 | error = pcmcia_mem_map(pf, width, 0, length, |
852 | &cfe->memspace[n].handle, &cfe->memspace[n].offset, |
853 | &cfe->memspace[n].window); |
854 | if (error) |
855 | break; |
856 | } |
857 | if (n < cfe->num_memspace) { |
858 | for (m = 0; m < cfe->num_iospace; m++) |
859 | pcmcia_io_unmap(pf, cfe->iospace[m].window); |
860 | for (m = 0; m < n; m++) |
861 | pcmcia_mem_unmap(pf, cfe->memspace[m].window); |
862 | return (error); |
863 | } |
864 | |
865 | /* This one's good! */ |
866 | return (error); |
867 | } |
868 | |
869 | void |
870 | pcmcia_config_unmap(struct pcmcia_function *pf) |
871 | { |
872 | struct pcmcia_config_entry *cfe = pf->cfe; |
873 | int m; |
874 | |
875 | for (m = 0; m < cfe->num_iospace; m++) |
876 | pcmcia_io_unmap(pf, cfe->iospace[m].window); |
877 | for (m = 0; m < cfe->num_memspace; m++) |
878 | pcmcia_mem_unmap(pf, cfe->memspace[m].window); |
879 | } |
880 | |
881 | int |
882 | pcmcia_function_configure(struct pcmcia_function *pf, |
883 | int (*validator)(struct pcmcia_config_entry *)) |
884 | { |
885 | struct pcmcia_config_entry *cfe; |
886 | int error = ENOENT; |
887 | |
888 | SIMPLEQ_FOREACH(cfe, &pf->cfe_head, cfe_list) { |
889 | error = validator(cfe); |
890 | if (error) |
891 | continue; |
892 | error = pcmcia_config_alloc(pf, cfe); |
893 | if (!error) |
894 | break; |
895 | } |
896 | if (!cfe) { |
897 | DPRINTF(("pcmcia_function_configure: no config entry found, error=%d\n" , |
898 | error)); |
899 | return (error); |
900 | } |
901 | |
902 | /* Remember which configuration entry we are using. */ |
903 | pf->cfe = cfe; |
904 | |
905 | error = pcmcia_config_map(pf); |
906 | if (error) { |
907 | DPRINTF(("pcmcia_function_configure: map failed, error=%d\n" , |
908 | error)); |
909 | return (error); |
910 | } |
911 | |
912 | return (0); |
913 | } |
914 | |
915 | void |
916 | pcmcia_function_unconfigure(struct pcmcia_function *pf) |
917 | { |
918 | |
919 | pcmcia_config_unmap(pf); |
920 | pcmcia_config_free(pf); |
921 | } |
922 | |