1 | /* $NetBSD: cardbus.c,v 1.108 2011/08/01 11:20:27 drochner Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1997, 1998, 1999 and 2000 |
5 | * HAYAKAWA Koichi. All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
20 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
22 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
24 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
25 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: cardbus.c,v 1.108 2011/08/01 11:20:27 drochner Exp $" ); |
31 | |
32 | #include "opt_cardbus.h" |
33 | |
34 | #include <sys/param.h> |
35 | #include <sys/systm.h> |
36 | #include <sys/device.h> |
37 | #include <sys/malloc.h> |
38 | #include <sys/kernel.h> |
39 | #include <sys/syslog.h> |
40 | #include <sys/proc.h> |
41 | #include <sys/reboot.h> /* for AB_* needed by bootverbose */ |
42 | |
43 | #include <sys/bus.h> |
44 | |
45 | #include <dev/cardbus/cardbusvar.h> |
46 | #include <dev/pci/pcidevs.h> |
47 | |
48 | #include <dev/cardbus/cardbus_exrom.h> |
49 | |
50 | #include <dev/pci/pcivar.h> /* XXX */ |
51 | #include <dev/pci/pcireg.h> /* XXX */ |
52 | |
53 | #include <dev/pcmcia/pcmciareg.h> |
54 | |
55 | #include "locators.h" |
56 | |
57 | #if defined CARDBUS_DEBUG |
58 | #define STATIC |
59 | #define DPRINTF(a) printf a |
60 | #else |
61 | #define STATIC static |
62 | #define DPRINTF(a) |
63 | #endif |
64 | |
65 | |
66 | STATIC void cardbusattach(device_t, device_t, void *); |
67 | STATIC int cardbusdetach(device_t, int); |
68 | STATIC int cardbusmatch(device_t, cfdata_t, void *); |
69 | int cardbus_rescan(device_t, const char *, const int *); |
70 | void cardbus_childdetached(device_t, device_t); |
71 | static int cardbusprint(void *, const char *); |
72 | |
73 | typedef void (*tuple_decode_func)(u_int8_t*, int, void*); |
74 | |
75 | static int decode_tuples(u_int8_t *, int, tuple_decode_func, void*); |
76 | #ifdef CARDBUS_DEBUG |
77 | static void print_tuple(u_int8_t*, int, void*); |
78 | #endif |
79 | |
80 | static int cardbus_read_tuples(struct cardbus_attach_args *, |
81 | pcireg_t, u_int8_t *, size_t); |
82 | |
83 | static void enable_function(struct cardbus_softc *, int, int); |
84 | static void disable_function(struct cardbus_softc *, int); |
85 | |
86 | static bool cardbus_child_register(device_t); |
87 | |
88 | CFATTACH_DECL3_NEW(cardbus, sizeof(struct cardbus_softc), |
89 | cardbusmatch, cardbusattach, cardbusdetach, NULL, |
90 | cardbus_rescan, cardbus_childdetached, DVF_DETACH_SHUTDOWN); |
91 | |
92 | #ifndef __NetBSD_Version__ |
93 | struct cfdriver cardbus_cd = { |
94 | NULL, "cardbus" , DV_DULL |
95 | }; |
96 | #endif |
97 | |
98 | |
99 | STATIC int |
100 | cardbusmatch(device_t parent, cfdata_t cf, void *aux) |
101 | { |
102 | |
103 | return (1); |
104 | } |
105 | |
106 | STATIC void |
107 | cardbusattach(device_t parent, device_t self, void *aux) |
108 | { |
109 | struct cardbus_softc *sc = device_private(self); |
110 | struct cbslot_attach_args *cba = aux; |
111 | |
112 | sc->sc_dev = self; |
113 | |
114 | sc->sc_bus = cba->cba_bus; |
115 | sc->sc_cacheline = cba->cba_cacheline; |
116 | sc->sc_max_lattimer = MIN(0xf8, cba->cba_max_lattimer); |
117 | |
118 | aprint_naive("\n" ); |
119 | aprint_normal(": bus %d" , sc->sc_bus); |
120 | if (bootverbose) |
121 | aprint_normal(" cacheline 0x%x, lattimer 0x%x" , |
122 | sc->sc_cacheline, sc->sc_max_lattimer); |
123 | aprint_normal("\n" ); |
124 | |
125 | sc->sc_iot = cba->cba_iot; /* CardBus I/O space tag */ |
126 | sc->sc_memt = cba->cba_memt; /* CardBus MEM space tag */ |
127 | sc->sc_dmat = cba->cba_dmat; /* DMA tag */ |
128 | sc->sc_cc = cba->cba_cc; |
129 | sc->sc_cf = cba->cba_cf; |
130 | |
131 | sc->sc_rbus_iot = cba->cba_rbus_iot; |
132 | sc->sc_rbus_memt = cba->cba_rbus_memt; |
133 | |
134 | if (!pmf_device_register(self, NULL, NULL)) |
135 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
136 | } |
137 | |
138 | STATIC int |
139 | cardbusdetach(device_t self, int flags) |
140 | { |
141 | int rc; |
142 | |
143 | if ((rc = config_detach_children(self, flags)) != 0) |
144 | return rc; |
145 | |
146 | pmf_device_deregister(self); |
147 | return 0; |
148 | } |
149 | |
150 | static int |
151 | cardbus_read_tuples(struct cardbus_attach_args *ca, pcireg_t cis_ptr, |
152 | u_int8_t *tuples, size_t len) |
153 | { |
154 | struct cardbus_softc *sc = ca->ca_ct->ct_sc; |
155 | cardbus_chipset_tag_t cc = ca->ca_ct->ct_cc; |
156 | cardbus_function_tag_t cf = ca->ca_ct->ct_cf; |
157 | pcitag_t tag = ca->ca_tag; |
158 | pcireg_t command; |
159 | bus_space_tag_t bar_tag; |
160 | bus_space_handle_t bar_memh; |
161 | bus_size_t bar_size; |
162 | bus_addr_t bar_addr; |
163 | pcireg_t reg; |
164 | int found = 0; |
165 | int cardbus_space = cis_ptr & CARDBUS_CIS_ASIMASK; |
166 | int i, j; |
167 | |
168 | memset(tuples, 0, len); |
169 | |
170 | cis_ptr = cis_ptr & CARDBUS_CIS_ADDRMASK; |
171 | |
172 | switch (cardbus_space) { |
173 | case CARDBUS_CIS_ASI_TUPLE: |
174 | DPRINTF(("%s: reading CIS data from configuration space\n" , |
175 | device_xname(sc->sc_dev))); |
176 | for (i = cis_ptr, j = 0; i < 0xff; i += 4) { |
177 | u_int32_t e = (*cf->cardbus_conf_read)(cc, tag, i); |
178 | tuples[j] = 0xff & e; |
179 | e >>= 8; |
180 | tuples[j + 1] = 0xff & e; |
181 | e >>= 8; |
182 | tuples[j + 2] = 0xff & e; |
183 | e >>= 8; |
184 | tuples[j + 3] = 0xff & e; |
185 | j += 4; |
186 | } |
187 | found++; |
188 | break; |
189 | |
190 | case CARDBUS_CIS_ASI_BAR0: |
191 | case CARDBUS_CIS_ASI_BAR1: |
192 | case CARDBUS_CIS_ASI_BAR2: |
193 | case CARDBUS_CIS_ASI_BAR3: |
194 | case CARDBUS_CIS_ASI_BAR4: |
195 | case CARDBUS_CIS_ASI_BAR5: |
196 | case CARDBUS_CIS_ASI_ROM: |
197 | if (cardbus_space == CARDBUS_CIS_ASI_ROM) { |
198 | reg = CARDBUS_ROM_REG; |
199 | DPRINTF(("%s: reading CIS data from ROM\n" , |
200 | device_xname(sc->sc_dev))); |
201 | } else { |
202 | reg = CARDBUS_CIS_ASI_BAR(cardbus_space); |
203 | DPRINTF(("%s: reading CIS data from BAR%d\n" , |
204 | device_xname(sc->sc_dev), cardbus_space - 1)); |
205 | } |
206 | |
207 | /* |
208 | * XXX zero register so mapreg_map doesn't get confused by old |
209 | * contents. |
210 | */ |
211 | cardbus_conf_write(cc, cf, tag, reg, 0); |
212 | if (Cardbus_mapreg_map(ca->ca_ct, reg, |
213 | PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, |
214 | 0, &bar_tag, &bar_memh, &bar_addr, &bar_size)) { |
215 | aprint_error_dev(sc->sc_dev, "failed to map memory\n" ); |
216 | return (1); |
217 | } |
218 | aprint_debug_dev(sc->sc_dev, "mapped %ju bytes at 0x%jx\n" , |
219 | (uintmax_t)bar_size, (uintmax_t)bar_addr); |
220 | |
221 | if (cardbus_space == CARDBUS_CIS_ASI_ROM) { |
222 | pcireg_t exrom; |
223 | int save; |
224 | struct cardbus_rom_image_head rom_image; |
225 | struct cardbus_rom_image *p; |
226 | |
227 | save = splhigh(); |
228 | /* enable rom address decoder */ |
229 | exrom = cardbus_conf_read(cc, cf, tag, reg); |
230 | cardbus_conf_write(cc, cf, tag, reg, exrom | 1); |
231 | |
232 | command = cardbus_conf_read(cc, cf, tag, |
233 | PCI_COMMAND_STATUS_REG); |
234 | cardbus_conf_write(cc, cf, tag, |
235 | PCI_COMMAND_STATUS_REG, |
236 | command | PCI_COMMAND_MEM_ENABLE); |
237 | |
238 | if (cardbus_read_exrom(bar_tag, bar_memh, &rom_image)) |
239 | goto out; |
240 | |
241 | SIMPLEQ_FOREACH(p, &rom_image, next) { |
242 | if (p->rom_image == |
243 | CARDBUS_CIS_ASI_ROM_IMAGE(cis_ptr)) { |
244 | bus_space_read_region_1(p->romt, |
245 | p->romh, CARDBUS_CIS_ADDR(cis_ptr), |
246 | tuples, MIN(p->image_size, len)); |
247 | found++; |
248 | break; |
249 | } |
250 | } |
251 | while ((p = SIMPLEQ_FIRST(&rom_image)) != NULL) { |
252 | SIMPLEQ_REMOVE_HEAD(&rom_image, next); |
253 | free(p, M_DEVBUF); |
254 | } |
255 | out: |
256 | exrom = cardbus_conf_read(cc, cf, tag, reg); |
257 | cardbus_conf_write(cc, cf, tag, reg, exrom & ~1); |
258 | splx(save); |
259 | } else { |
260 | command = cardbus_conf_read(cc, cf, tag, |
261 | PCI_COMMAND_STATUS_REG); |
262 | cardbus_conf_write(cc, cf, tag, |
263 | PCI_COMMAND_STATUS_REG, |
264 | command | PCI_COMMAND_MEM_ENABLE); |
265 | /* XXX byte order? */ |
266 | bus_space_read_region_1(bar_tag, bar_memh, |
267 | cis_ptr, tuples, |
268 | MIN(bar_size - MIN(bar_size, cis_ptr), len)); |
269 | found++; |
270 | } |
271 | command = cardbus_conf_read(cc, cf, tag, |
272 | PCI_COMMAND_STATUS_REG); |
273 | cardbus_conf_write(cc, cf, tag, PCI_COMMAND_STATUS_REG, |
274 | command & ~PCI_COMMAND_MEM_ENABLE); |
275 | cardbus_conf_write(cc, cf, tag, reg, 0); |
276 | |
277 | Cardbus_mapreg_unmap(ca->ca_ct, reg, bar_tag, bar_memh, |
278 | bar_size); |
279 | break; |
280 | |
281 | #ifdef DIAGNOSTIC |
282 | default: |
283 | panic("%s: bad CIS space (%d)" , device_xname(sc->sc_dev), |
284 | cardbus_space); |
285 | #endif |
286 | } |
287 | return (!found); |
288 | } |
289 | |
290 | static void |
291 | parse_tuple(u_int8_t *tuple, int len, void *data) |
292 | { |
293 | struct cardbus_cis_info *cis = data; |
294 | char *p; |
295 | int i, bar_index; |
296 | |
297 | switch (tuple[0]) { |
298 | case PCMCIA_CISTPL_MANFID: |
299 | if (tuple[1] != 4) { |
300 | DPRINTF(("%s: wrong length manufacturer id (%d)\n" , |
301 | __func__, tuple[1])); |
302 | break; |
303 | } |
304 | cis->manufacturer = tuple[2] | (tuple[3] << 8); |
305 | cis->product = tuple[4] | (tuple[5] << 8); |
306 | break; |
307 | |
308 | case PCMCIA_CISTPL_VERS_1: |
309 | memcpy(cis->cis1_info_buf, tuple + 2, tuple[1]); |
310 | i = 0; |
311 | p = cis->cis1_info_buf + 2; |
312 | while (i < |
313 | sizeof(cis->cis1_info) / sizeof(cis->cis1_info[0])) { |
314 | if (p >= cis->cis1_info_buf + tuple[1] || *p == '\xff') |
315 | break; |
316 | cis->cis1_info[i++] = p; |
317 | while (*p != '\0' && *p != '\xff') |
318 | p++; |
319 | if (*p == '\0') |
320 | p++; |
321 | } |
322 | break; |
323 | |
324 | case PCMCIA_CISTPL_BAR: |
325 | if (tuple[1] != 6) { |
326 | DPRINTF(("%s: BAR with short length (%d)\n" , |
327 | __func__, tuple[1])); |
328 | break; |
329 | } |
330 | bar_index = tuple[2] & 7; |
331 | if (bar_index == 0) { |
332 | DPRINTF(("%s: invalid ASI in BAR tuple\n" , __func__)); |
333 | break; |
334 | } |
335 | bar_index--; |
336 | cis->bar[bar_index].flags = tuple[2]; |
337 | cis->bar[bar_index].size = |
338 | (tuple[4] << 0) | |
339 | (tuple[5] << 8) | |
340 | (tuple[6] << 16) | |
341 | (tuple[7] << 24); |
342 | break; |
343 | |
344 | case PCMCIA_CISTPL_FUNCID: |
345 | cis->funcid = tuple[2]; |
346 | break; |
347 | |
348 | case PCMCIA_CISTPL_FUNCE: |
349 | switch (cis->funcid) { |
350 | case PCMCIA_FUNCTION_SERIAL: |
351 | if (tuple[1] >= 2 && |
352 | /* XXX PCMCIA_TPLFE_TYPE_SERIAL_??? */ |
353 | tuple[2] == 0) { |
354 | cis->funce.serial.uart_type = tuple[3] & 0x1f; |
355 | cis->funce.serial.uart_present = 1; |
356 | } |
357 | break; |
358 | |
359 | case PCMCIA_FUNCTION_NETWORK: |
360 | if (tuple[1] >= 8 && |
361 | tuple[2] == PCMCIA_TPLFE_TYPE_LAN_NID) { |
362 | if (tuple[3] > |
363 | sizeof(cis->funce.network.netid)) { |
364 | DPRINTF(("%s: unknown network id type " |
365 | "(len = %d)\n" , |
366 | __func__, tuple[3])); |
367 | } else { |
368 | cis->funce.network.netid_present = 1; |
369 | memcpy(cis->funce.network.netid, |
370 | tuple + 4, tuple[3]); |
371 | } |
372 | } |
373 | break; |
374 | } |
375 | break; |
376 | } |
377 | } |
378 | |
379 | /* |
380 | * int cardbus_attach_card(struct cardbus_softc *sc) |
381 | * |
382 | * This function attaches the card on the slot: turns on power, |
383 | * reads and analyses tuple, sets configuration index. |
384 | * |
385 | * This function returns the number of recognised device functions. |
386 | * If no functions are recognised, return 0. |
387 | */ |
388 | int |
389 | cardbus_attach_card(struct cardbus_softc *sc) |
390 | { |
391 | cardbus_chipset_tag_t cc; |
392 | cardbus_function_tag_t cf; |
393 | int cdstatus; |
394 | static int wildcard[CARDBUSCF_NLOCS] = { |
395 | CARDBUSCF_FUNCTION_DEFAULT |
396 | }; |
397 | |
398 | cc = sc->sc_cc; |
399 | cf = sc->sc_cf; |
400 | |
401 | DPRINTF(("cardbus_attach_card: cb%d start\n" , |
402 | device_unit(sc->sc_dev))); |
403 | |
404 | /* inspect initial voltage */ |
405 | if ((cdstatus = (*cf->cardbus_ctrl)(cc, CARDBUS_CD)) == 0) { |
406 | DPRINTF(("%s: no CardBus card on cb%d\n" , __func__, |
407 | device_unit(sc->sc_dev))); |
408 | return (0); |
409 | } |
410 | |
411 | device_pmf_driver_set_child_register(sc->sc_dev, cardbus_child_register); |
412 | cardbus_rescan(sc->sc_dev, "cardbus" , wildcard); |
413 | return (1); /* XXX */ |
414 | } |
415 | |
416 | int |
417 | cardbus_rescan(device_t self, const char *ifattr, |
418 | const int *locators) |
419 | { |
420 | struct cardbus_softc *sc = device_private(self); |
421 | cardbus_chipset_tag_t cc; |
422 | cardbus_function_tag_t cf; |
423 | pcitag_t tag; |
424 | pcireg_t id, class, cis_ptr; |
425 | pcireg_t bhlc, icr, lattimer; |
426 | int cdstatus; |
427 | int function, nfunction; |
428 | device_t csc; |
429 | cardbus_devfunc_t ct; |
430 | |
431 | cc = sc->sc_cc; |
432 | cf = sc->sc_cf; |
433 | |
434 | /* inspect initial voltage */ |
435 | if ((cdstatus = (*cf->cardbus_ctrl)(cc, CARDBUS_CD)) == 0) { |
436 | DPRINTF(("%s: no CardBus card on cb%d\n" , __func__, |
437 | device_unit(sc->sc_dev))); |
438 | return (0); |
439 | } |
440 | |
441 | /* |
442 | * XXX use fake function 8 to keep power on during whole |
443 | * configuration. |
444 | */ |
445 | enable_function(sc, cdstatus, 8); |
446 | function = 0; |
447 | |
448 | tag = cardbus_make_tag(cc, cf, sc->sc_bus, function); |
449 | |
450 | /* |
451 | * Wait until power comes up. Maxmum 500 ms. |
452 | * |
453 | * XXX What is this for? The bridge driver ought to have waited |
454 | * XXX already. |
455 | */ |
456 | { |
457 | int i; |
458 | |
459 | for (i = 0; i < 5; ++i) { |
460 | id = cardbus_conf_read(cc, cf, tag, PCI_ID_REG); |
461 | if (id != 0xffffffff && id != 0) { |
462 | break; |
463 | } |
464 | if (cold) { /* before kernel thread invoked */ |
465 | delay(100 * 1000); |
466 | } else { /* thread context */ |
467 | if (tsleep((void *)sc, PCATCH, "cardbus" , |
468 | hz / 10) != EWOULDBLOCK) { |
469 | break; |
470 | } |
471 | } |
472 | } |
473 | aprint_debug_dev(self, "id reg valid in %d iterations\n" , i); |
474 | if (i == 5) { |
475 | return (EIO); |
476 | } |
477 | } |
478 | |
479 | bhlc = cardbus_conf_read(cc, cf, tag, PCI_BHLC_REG); |
480 | DPRINTF(("%s bhlc 0x%08x -> " , device_xname(sc->sc_dev), bhlc)); |
481 | nfunction = PCI_HDRTYPE_MULTIFN(bhlc) ? 8 : 1; |
482 | |
483 | for (function = 0; function < nfunction; function++) { |
484 | struct cardbus_attach_args ca; |
485 | int locs[CARDBUSCF_NLOCS]; |
486 | |
487 | if (locators[CARDBUSCF_FUNCTION] != |
488 | CARDBUSCF_FUNCTION_DEFAULT && |
489 | locators[CARDBUSCF_FUNCTION] != function) |
490 | continue; |
491 | |
492 | if (sc->sc_funcs[function]) |
493 | continue; |
494 | |
495 | tag = cardbus_make_tag(cc, cf, sc->sc_bus, function); |
496 | |
497 | id = cardbus_conf_read(cc, cf, tag, PCI_ID_REG); |
498 | class = cardbus_conf_read(cc, cf, tag, PCI_CLASS_REG); |
499 | cis_ptr = cardbus_conf_read(cc, cf, tag, CARDBUS_CIS_REG); |
500 | |
501 | /* Invalid vendor ID value? */ |
502 | if (PCI_VENDOR(id) == PCI_VENDOR_INVALID) { |
503 | continue; |
504 | } |
505 | |
506 | DPRINTF(("cardbus_attach_card: " |
507 | "Vendor 0x%x, Product 0x%x, CIS 0x%x\n" , |
508 | PCI_VENDOR(id), PCI_PRODUCT(id), cis_ptr)); |
509 | |
510 | enable_function(sc, cdstatus, function); |
511 | |
512 | /* clean up every BAR */ |
513 | cardbus_conf_write(cc, cf, tag, PCI_BAR0, 0); |
514 | cardbus_conf_write(cc, cf, tag, PCI_BAR1, 0); |
515 | cardbus_conf_write(cc, cf, tag, PCI_BAR2, 0); |
516 | cardbus_conf_write(cc, cf, tag, PCI_BAR3, 0); |
517 | cardbus_conf_write(cc, cf, tag, PCI_BAR4, 0); |
518 | cardbus_conf_write(cc, cf, tag, PCI_BAR5, 0); |
519 | cardbus_conf_write(cc, cf, tag, CARDBUS_ROM_REG, 0); |
520 | |
521 | /* set initial latency and cacheline size */ |
522 | bhlc = cardbus_conf_read(cc, cf, tag, PCI_BHLC_REG); |
523 | icr = cardbus_conf_read(cc, cf, tag, PCI_INTERRUPT_REG); |
524 | DPRINTF(("%s func%d icr 0x%08x bhlc 0x%08x -> " , |
525 | device_xname(sc->sc_dev), function, icr, bhlc)); |
526 | bhlc &= ~(PCI_CACHELINE_MASK << PCI_CACHELINE_SHIFT); |
527 | bhlc |= (sc->sc_cacheline & PCI_CACHELINE_MASK) << |
528 | PCI_CACHELINE_SHIFT; |
529 | /* |
530 | * Set the initial value of the Latency Timer. |
531 | * |
532 | * While a PCI device owns the bus, its Latency |
533 | * Timer counts down bus cycles from its initial |
534 | * value to 0. Minimum Grant tells for how long |
535 | * the device wants to own the bus once it gets |
536 | * access, in units of 250ns. |
537 | * |
538 | * On a 33 MHz bus, there are 8 cycles per 250ns. |
539 | * So I multiply the Minimum Grant by 8 to find |
540 | * out the initial value of the Latency Timer. |
541 | * |
542 | * Avoid setting a Latency Timer less than 0x10, |
543 | * since the old code did not do that. |
544 | */ |
545 | lattimer = |
546 | MIN(sc->sc_max_lattimer, MAX(0x10, 8 * PCI_MIN_GNT(icr))); |
547 | if (PCI_LATTIMER(bhlc) < lattimer) { |
548 | bhlc &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); |
549 | bhlc |= (lattimer << PCI_LATTIMER_SHIFT); |
550 | } |
551 | |
552 | cardbus_conf_write(cc, cf, tag, PCI_BHLC_REG, bhlc); |
553 | bhlc = cardbus_conf_read(cc, cf, tag, PCI_BHLC_REG); |
554 | DPRINTF(("0x%08x\n" , bhlc)); |
555 | |
556 | /* |
557 | * We need to allocate the ct here, since we might |
558 | * need it when reading the CIS |
559 | */ |
560 | if ((ct = malloc(sizeof(struct cardbus_devfunc), |
561 | M_DEVBUF, M_NOWAIT)) == NULL) { |
562 | panic("no room for cardbus_tag" ); |
563 | } |
564 | |
565 | ct->ct_bhlc = bhlc; |
566 | ct->ct_cc = sc->sc_cc; |
567 | ct->ct_cf = sc->sc_cf; |
568 | ct->ct_bus = sc->sc_bus; |
569 | ct->ct_func = function; |
570 | ct->ct_sc = sc; |
571 | sc->sc_funcs[function] = ct; |
572 | |
573 | memset(&ca, 0, sizeof(ca)); |
574 | |
575 | ca.ca_ct = ct; |
576 | |
577 | ca.ca_iot = sc->sc_iot; |
578 | ca.ca_memt = sc->sc_memt; |
579 | ca.ca_dmat = sc->sc_dmat; |
580 | |
581 | ca.ca_rbus_iot = sc->sc_rbus_iot; |
582 | ca.ca_rbus_memt= sc->sc_rbus_memt; |
583 | |
584 | ca.ca_tag = tag; |
585 | ca.ca_bus = sc->sc_bus; |
586 | ca.ca_function = function; |
587 | ca.ca_id = id; |
588 | ca.ca_class = class; |
589 | |
590 | if (cis_ptr != 0) { |
591 | #define TUPLESIZE 2048 |
592 | u_int8_t *tuple = malloc(TUPLESIZE, M_DEVBUF, M_WAITOK); |
593 | if (cardbus_read_tuples(&ca, cis_ptr, |
594 | tuple, TUPLESIZE)) { |
595 | printf("cardbus_attach_card: " |
596 | "failed to read CIS\n" ); |
597 | } else { |
598 | #ifdef CARDBUS_DEBUG |
599 | decode_tuples(tuple, TUPLESIZE, |
600 | print_tuple, NULL); |
601 | #endif |
602 | decode_tuples(tuple, TUPLESIZE, |
603 | parse_tuple, &ca.ca_cis); |
604 | } |
605 | free(tuple, M_DEVBUF); |
606 | } |
607 | |
608 | locs[CARDBUSCF_FUNCTION] = function; |
609 | |
610 | if ((csc = config_found_sm_loc(sc->sc_dev, "cardbus" , locs, |
611 | &ca, cardbusprint, config_stdsubmatch)) == NULL) { |
612 | /* do not match */ |
613 | disable_function(sc, function); |
614 | sc->sc_funcs[function] = NULL; |
615 | free(ct, M_DEVBUF); |
616 | } else { |
617 | /* found */ |
618 | ct->ct_device = csc; |
619 | } |
620 | } |
621 | /* |
622 | * XXX power down pseudo function 8 (this will power down the card |
623 | * if no functions were attached). |
624 | */ |
625 | disable_function(sc, 8); |
626 | |
627 | return (0); |
628 | } |
629 | |
630 | static int |
631 | cardbusprint(void *aux, const char *pnp) |
632 | { |
633 | struct cardbus_attach_args *ca = aux; |
634 | char devinfo[256]; |
635 | int i; |
636 | |
637 | if (pnp) { |
638 | pci_devinfo(ca->ca_id, ca->ca_class, 1, devinfo, |
639 | sizeof(devinfo)); |
640 | for (i = 0; i < 4; i++) { |
641 | if (ca->ca_cis.cis1_info[i] == NULL) |
642 | break; |
643 | if (i) |
644 | aprint_normal(", " ); |
645 | aprint_normal("%s" , ca->ca_cis.cis1_info[i]); |
646 | } |
647 | aprint_verbose("%s(manufacturer 0x%x, product 0x%x)" , |
648 | i ? " " : "" , |
649 | ca->ca_cis.manufacturer, ca->ca_cis.product); |
650 | aprint_normal(" %s at %s" , devinfo, pnp); |
651 | } |
652 | aprint_normal(" function %d" , ca->ca_function); |
653 | |
654 | return (UNCONF); |
655 | } |
656 | |
657 | /* |
658 | * void cardbus_detach_card(struct cardbus_softc *sc) |
659 | * |
660 | * This function detaches the card on the slot: detach device data |
661 | * structure and turns off the power. |
662 | * |
663 | * This function must not be called under interrupt context. |
664 | */ |
665 | void |
666 | cardbus_detach_card(struct cardbus_softc *sc) |
667 | { |
668 | int f; |
669 | struct cardbus_devfunc *ct; |
670 | |
671 | for (f = 0; f < 8; f++) { |
672 | ct = sc->sc_funcs[f]; |
673 | if (!ct) |
674 | continue; |
675 | |
676 | DPRINTF(("%s: detaching %s\n" , device_xname(sc->sc_dev), |
677 | device_xname(ct->ct_device))); |
678 | /* call device detach function */ |
679 | |
680 | if (config_detach(ct->ct_device, 0) != 0) { |
681 | aprint_error_dev(sc->sc_dev, |
682 | "cannot detach dev %s, function %d\n" , |
683 | device_xname(ct->ct_device), ct->ct_func); |
684 | } |
685 | } |
686 | |
687 | sc->sc_poweron_func = 0; |
688 | (*sc->sc_cf->cardbus_power)(sc->sc_cc, |
689 | CARDBUS_VCC_0V | CARDBUS_VPP_0V); |
690 | } |
691 | |
692 | void |
693 | cardbus_childdetached(device_t self, device_t child) |
694 | { |
695 | struct cardbus_softc *sc = device_private(self); |
696 | struct cardbus_devfunc *ct; |
697 | |
698 | ct = sc->sc_funcs[device_locator(child, CARDBUSCF_FUNCTION)]; |
699 | KASSERT(ct->ct_device == child); |
700 | |
701 | sc->sc_poweron_func &= ~(1 << ct->ct_func); |
702 | sc->sc_funcs[ct->ct_func] = NULL; |
703 | free(ct, M_DEVBUF); |
704 | } |
705 | |
706 | void * |
707 | Cardbus_intr_establish(cardbus_devfunc_t ct, |
708 | int level, int (*func)(void *), void *arg) |
709 | { |
710 | return cardbus_intr_establish(ct->ct_cc, ct->ct_cf, level, func, |
711 | arg); |
712 | } |
713 | |
714 | /* |
715 | * void *cardbus_intr_establish(cc, cf, irq, level, func, arg) |
716 | * Interrupt handler of pccard. |
717 | * args: |
718 | * cardbus_chipset_tag_t *cc |
719 | * int irq: |
720 | */ |
721 | void * |
722 | cardbus_intr_establish(cardbus_chipset_tag_t cc, cardbus_function_tag_t cf, |
723 | int level, int (*func)(void *), void *arg) |
724 | { |
725 | |
726 | DPRINTF(("- cardbus_intr_establish\n" )); |
727 | return ((*cf->cardbus_intr_establish)(cc, level, func, arg)); |
728 | } |
729 | |
730 | void |
731 | Cardbus_intr_disestablish(cardbus_devfunc_t ct, void *handler) |
732 | { |
733 | cardbus_intr_disestablish(ct->ct_cc, ct->ct_cf, handler); |
734 | } |
735 | |
736 | /* |
737 | * void cardbus_intr_disestablish(cc, cf, handler) |
738 | * Interrupt handler of pccard. |
739 | * args: |
740 | * cardbus_chipset_tag_t *cc |
741 | */ |
742 | void |
743 | cardbus_intr_disestablish(cardbus_chipset_tag_t cc, cardbus_function_tag_t cf, |
744 | void *handler) |
745 | { |
746 | |
747 | DPRINTF(("- pccard_intr_disestablish\n" )); |
748 | (*cf->cardbus_intr_disestablish)(cc, handler); |
749 | } |
750 | |
751 | /* |
752 | * XXX this should be merged with cardbus_function_{enable,disable}, |
753 | * but we don't have a ct when these functions are called. |
754 | */ |
755 | static void |
756 | enable_function(struct cardbus_softc *sc, int cdstatus, int function) |
757 | { |
758 | |
759 | if (sc->sc_poweron_func == 0) { |
760 | /* switch to 3V and/or wait for power to stabilize */ |
761 | if (cdstatus & CARDBUS_3V_CARD) { |
762 | /* |
763 | * sc_poweron_func must be substituted before |
764 | * entering sleep, in order to avoid turn on |
765 | * power twice. |
766 | */ |
767 | sc->sc_poweron_func |= (1 << function); |
768 | (*sc->sc_cf->cardbus_power)(sc->sc_cc, CARDBUS_VCC_3V); |
769 | } else { |
770 | /* No cards other than 3.3V cards. */ |
771 | return; |
772 | } |
773 | (*sc->sc_cf->cardbus_ctrl)(sc->sc_cc, CARDBUS_RESET); |
774 | } |
775 | sc->sc_poweron_func |= (1 << function); |
776 | } |
777 | |
778 | static void |
779 | disable_function(struct cardbus_softc *sc, int function) |
780 | { |
781 | bool powerdown; |
782 | cardbus_devfunc_t ct; |
783 | device_t dv; |
784 | int i; |
785 | |
786 | sc->sc_poweron_func &= ~(1 << function); |
787 | if (sc->sc_poweron_func != 0) |
788 | return; |
789 | for (i = 0; i < __arraycount(sc->sc_funcs); i++) { |
790 | if ((ct = sc->sc_funcs[i]) == NULL) |
791 | continue; |
792 | dv = ct->ct_device; |
793 | if (prop_dictionary_get_bool(device_properties(dv), |
794 | "pmf-powerdown" , &powerdown) && !powerdown) |
795 | return; |
796 | } |
797 | /* power-off because no functions are enabled */ |
798 | (*sc->sc_cf->cardbus_power)(sc->sc_cc, CARDBUS_VCC_0V); |
799 | } |
800 | |
801 | /* |
802 | * int cardbus_function_enable(struct cardbus_softc *sc, int func) |
803 | * |
804 | * This function enables a function on a card. When no power is |
805 | * applied on the card, power will be applied on it. |
806 | */ |
807 | int |
808 | cardbus_function_enable(struct cardbus_softc *sc, int func) |
809 | { |
810 | cardbus_chipset_tag_t cc = sc->sc_cc; |
811 | cardbus_function_tag_t cf = sc->sc_cf; |
812 | cardbus_devfunc_t ct; |
813 | pcireg_t command; |
814 | pcitag_t tag; |
815 | |
816 | DPRINTF(("entering cardbus_function_enable... " )); |
817 | |
818 | /* entering critical area */ |
819 | |
820 | /* XXX: sc_vold should be used */ |
821 | enable_function(sc, CARDBUS_3V_CARD, func); |
822 | |
823 | /* exiting critical area */ |
824 | |
825 | tag = cardbus_make_tag(cc, cf, sc->sc_bus, func); |
826 | |
827 | command = cardbus_conf_read(cc, cf, tag, PCI_COMMAND_STATUS_REG); |
828 | command |= (PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_IO_ENABLE | |
829 | PCI_COMMAND_MASTER_ENABLE); /* XXX: good guess needed */ |
830 | |
831 | cardbus_conf_write(cc, cf, tag, PCI_COMMAND_STATUS_REG, command); |
832 | |
833 | if ((ct = sc->sc_funcs[func]) != NULL) |
834 | Cardbus_conf_write(ct, tag, PCI_BHLC_REG, ct->ct_bhlc); |
835 | |
836 | DPRINTF(("%x\n" , sc->sc_poweron_func)); |
837 | |
838 | return (0); |
839 | } |
840 | |
841 | /* |
842 | * int cardbus_function_disable(struct cardbus_softc *, int func) |
843 | * |
844 | * This function disable a function on a card. When no functions are |
845 | * enabled, it turns off the power. |
846 | */ |
847 | int |
848 | cardbus_function_disable(struct cardbus_softc *sc, int func) |
849 | { |
850 | |
851 | DPRINTF(("entering cardbus_function_disable... " )); |
852 | |
853 | disable_function(sc, func); |
854 | |
855 | return (0); |
856 | } |
857 | |
858 | /* |
859 | * int cardbus_get_capability(cardbus_chipset_tag_t cc, |
860 | * cardbus_function_tag_t cf, pcitag_t tag, int capid, int *offset, |
861 | * pcireg_t *value) |
862 | * |
863 | * Find the specified PCI capability. |
864 | */ |
865 | int |
866 | cardbus_get_capability(cardbus_chipset_tag_t cc, cardbus_function_tag_t cf, |
867 | pcitag_t tag, int capid, int *offset, pcireg_t *value) |
868 | { |
869 | pcireg_t reg; |
870 | unsigned int ofs; |
871 | |
872 | reg = cardbus_conf_read(cc, cf, tag, PCI_COMMAND_STATUS_REG); |
873 | if (!(reg & PCI_STATUS_CAPLIST_SUPPORT)) |
874 | return (0); |
875 | |
876 | ofs = PCI_CAPLIST_PTR(cardbus_conf_read(cc, cf, tag, |
877 | PCI_CAPLISTPTR_REG)); |
878 | while (ofs != 0) { |
879 | #ifdef DIAGNOSTIC |
880 | if ((ofs & 3) || (ofs < 0x40)) |
881 | panic("cardbus_get_capability" ); |
882 | #endif |
883 | reg = cardbus_conf_read(cc, cf, tag, ofs); |
884 | if (PCI_CAPLIST_CAP(reg) == capid) { |
885 | if (offset) |
886 | *offset = ofs; |
887 | if (value) |
888 | *value = reg; |
889 | return (1); |
890 | } |
891 | ofs = PCI_CAPLIST_NEXT(reg); |
892 | } |
893 | |
894 | return (0); |
895 | } |
896 | |
897 | /* |
898 | * below this line, there are some functions for decoding tuples. |
899 | * They should go out from this file. |
900 | */ |
901 | |
902 | static u_int8_t * |
903 | decode_tuple(u_int8_t *, u_int8_t *, tuple_decode_func, void *); |
904 | |
905 | static int |
906 | decode_tuples(u_int8_t *tuple, int buflen, tuple_decode_func func, void *data) |
907 | { |
908 | u_int8_t *tp = tuple; |
909 | |
910 | if (PCMCIA_CISTPL_LINKTARGET != *tuple) { |
911 | DPRINTF(("WRONG TUPLE: 0x%x\n" , *tuple)); |
912 | return (0); |
913 | } |
914 | |
915 | while ((tp = decode_tuple(tp, tuple + buflen, func, data)) != NULL) |
916 | ; |
917 | |
918 | return (1); |
919 | } |
920 | |
921 | static u_int8_t * |
922 | decode_tuple(u_int8_t *tuple, u_int8_t *end, |
923 | tuple_decode_func func, void *data) |
924 | { |
925 | u_int8_t type; |
926 | u_int8_t len; |
927 | |
928 | type = tuple[0]; |
929 | switch (type) { |
930 | case PCMCIA_CISTPL_NULL: |
931 | case PCMCIA_CISTPL_END: |
932 | len = 1; |
933 | break; |
934 | default: |
935 | if (tuple + 2 > end) |
936 | return (NULL); |
937 | len = tuple[1] + 2; |
938 | break; |
939 | } |
940 | |
941 | if (tuple + len > end) |
942 | return (NULL); |
943 | |
944 | (*func)(tuple, len, data); |
945 | |
946 | if (type == PCMCIA_CISTPL_END || tuple + len == end) |
947 | return (NULL); |
948 | |
949 | return (tuple + len); |
950 | } |
951 | |
952 | /* |
953 | * XXX: this is another reason why this code should be shared with PCI. |
954 | */ |
955 | static int |
956 | cardbus_get_powerstate_int(cardbus_devfunc_t ct, pcitag_t tag, |
957 | pcireg_t *state, int offset) |
958 | { |
959 | pcireg_t value, now; |
960 | cardbus_chipset_tag_t cc = ct->ct_cc; |
961 | cardbus_function_tag_t cf = ct->ct_cf; |
962 | |
963 | value = cardbus_conf_read(cc, cf, tag, offset + PCI_PMCSR); |
964 | now = value & PCI_PMCSR_STATE_MASK; |
965 | switch (now) { |
966 | case PCI_PMCSR_STATE_D0: |
967 | case PCI_PMCSR_STATE_D1: |
968 | case PCI_PMCSR_STATE_D2: |
969 | case PCI_PMCSR_STATE_D3: |
970 | *state = now; |
971 | return 0; |
972 | default: |
973 | return EINVAL; |
974 | } |
975 | } |
976 | |
977 | int |
978 | cardbus_get_powerstate(cardbus_devfunc_t ct, pcitag_t tag, pcireg_t *state) |
979 | { |
980 | cardbus_chipset_tag_t cc = ct->ct_cc; |
981 | cardbus_function_tag_t cf = ct->ct_cf; |
982 | int offset; |
983 | pcireg_t value; |
984 | |
985 | if (!cardbus_get_capability(cc, cf, tag, PCI_CAP_PWRMGMT, &offset, &value)) |
986 | return EOPNOTSUPP; |
987 | |
988 | return cardbus_get_powerstate_int(ct, tag, state, offset); |
989 | } |
990 | |
991 | static int |
992 | cardbus_set_powerstate_int(cardbus_devfunc_t ct, pcitag_t tag, |
993 | pcireg_t state, int offset, pcireg_t cap_reg) |
994 | { |
995 | cardbus_chipset_tag_t cc = ct->ct_cc; |
996 | cardbus_function_tag_t cf = ct->ct_cf; |
997 | |
998 | pcireg_t value, cap, now; |
999 | |
1000 | KASSERT((offset & 0x3) == 0); |
1001 | |
1002 | cap = cap_reg >> PCI_PMCR_SHIFT; |
1003 | value = cardbus_conf_read(cc, cf, tag, offset + PCI_PMCSR); |
1004 | now = value & PCI_PMCSR_STATE_MASK; |
1005 | value &= ~PCI_PMCSR_STATE_MASK; |
1006 | |
1007 | if (now == state) |
1008 | return 0; |
1009 | switch (state) { |
1010 | case PCI_PMCSR_STATE_D0: |
1011 | break; |
1012 | case PCI_PMCSR_STATE_D1: |
1013 | if (now == PCI_PMCSR_STATE_D2 || now == PCI_PMCSR_STATE_D3) { |
1014 | printf("invalid transition from %d to D1\n" , (int)now); |
1015 | return EINVAL; |
1016 | } |
1017 | if (!(cap & PCI_PMCR_D1SUPP)) { |
1018 | printf("D1 not supported\n" ); |
1019 | return EOPNOTSUPP; |
1020 | } |
1021 | break; |
1022 | case PCI_PMCSR_STATE_D2: |
1023 | if (now == PCI_PMCSR_STATE_D3) { |
1024 | printf("invalid transition from %d to D2\n" , (int)now); |
1025 | return EINVAL; |
1026 | } |
1027 | if (!(cap & PCI_PMCR_D2SUPP)) { |
1028 | printf("D2 not supported\n" ); |
1029 | return EOPNOTSUPP; |
1030 | } |
1031 | break; |
1032 | case PCI_PMCSR_STATE_D3: |
1033 | break; |
1034 | default: |
1035 | return EINVAL; |
1036 | } |
1037 | value |= state; |
1038 | cardbus_conf_write(cc, cf, tag, offset + PCI_PMCSR, value); |
1039 | if (state == PCI_PMCSR_STATE_D3 || now == PCI_PMCSR_STATE_D3) |
1040 | DELAY(10000); |
1041 | else if (state == PCI_PMCSR_STATE_D2 || now == PCI_PMCSR_STATE_D2) |
1042 | DELAY(200); |
1043 | |
1044 | return 0; |
1045 | } |
1046 | |
1047 | int |
1048 | cardbus_set_powerstate(cardbus_devfunc_t ct, pcitag_t tag, pcireg_t state) |
1049 | { |
1050 | cardbus_chipset_tag_t cc = ct->ct_cc; |
1051 | cardbus_function_tag_t cf = ct->ct_cf; |
1052 | int offset; |
1053 | pcireg_t value; |
1054 | |
1055 | if (!cardbus_get_capability(cc, cf, tag, PCI_CAP_PWRMGMT, &offset, |
1056 | &value)) |
1057 | return EOPNOTSUPP; |
1058 | |
1059 | return cardbus_set_powerstate_int(ct, tag, state, offset, value); |
1060 | } |
1061 | |
1062 | #ifdef CARDBUS_DEBUG |
1063 | static const char *tuple_name(int); |
1064 | static const char *tuple_names[] = { |
1065 | "TPL_NULL" , "TPL_DEVICE" , "Reserved" , "Reserved" , /* 0-3 */ |
1066 | "CONFIG_CB" , "CFTABLE_ENTRY_CB" , "Reserved" , "BAR" , /* 4-7 */ |
1067 | "Reserved" , "Reserved" , "Reserved" , "Reserved" , /* 8-B */ |
1068 | "Reserved" , "Reserved" , "Reserved" , "Reserved" , /* C-F */ |
1069 | "CHECKSUM" , "LONGLINK_A" , "LONGLINK_C" , "LINKTARGET" , /* 10-13 */ |
1070 | "NO_LINK" , "VERS_1" , "ALTSTR" , "DEVICE_A" , |
1071 | "JEDEC_C" , "JEDEC_A" , "CONFIG" , "CFTABLE_ENTRY" , |
1072 | "DEVICE_OC" , "DEVICE_OA" , "DEVICE_GEO" , "DEVICE_GEO_A" , |
1073 | "MANFID" , "FUNCID" , "FUNCE" , "SWIL" , /* 20-23 */ |
1074 | "Reserved" , "Reserved" , "Reserved" , "Reserved" , /* 24-27 */ |
1075 | "Reserved" , "Reserved" , "Reserved" , "Reserved" , /* 28-2B */ |
1076 | "Reserved" , "Reserved" , "Reserved" , "Reserved" , /* 2C-2F */ |
1077 | "Reserved" , "Reserved" , "Reserved" , "Reserved" , /* 30-33 */ |
1078 | "Reserved" , "Reserved" , "Reserved" , "Reserved" , /* 34-37 */ |
1079 | "Reserved" , "Reserved" , "Reserved" , "Reserved" , /* 38-3B */ |
1080 | "Reserved" , "Reserved" , "Reserved" , "Reserved" , /* 3C-3F */ |
1081 | "VERS_2" , "FORMAT" , "GEOMETRY" , "BYTEORDER" , |
1082 | "DATE" , "BATTERY" , "ORG" |
1083 | }; |
1084 | #define NAME_LEN(x) (sizeof x / sizeof(x[0])) |
1085 | |
1086 | static const char * |
1087 | tuple_name(int type) |
1088 | { |
1089 | |
1090 | if (0 <= type && type < NAME_LEN(tuple_names)) { |
1091 | return (tuple_names[type]); |
1092 | } else if (type == 0xff) { |
1093 | return ("END" ); |
1094 | } else { |
1095 | return ("Reserved" ); |
1096 | } |
1097 | } |
1098 | |
1099 | static void |
1100 | print_tuple(u_int8_t *tuple, int len, void *data) |
1101 | { |
1102 | int i; |
1103 | |
1104 | printf("tuple: %s len %d\n" , tuple_name(tuple[0]), len); |
1105 | |
1106 | for (i = 0; i < len; ++i) { |
1107 | if (i % 16 == 0) { |
1108 | printf(" 0x%2x:" , i); |
1109 | } |
1110 | printf(" %x" , tuple[i]); |
1111 | if (i % 16 == 15) { |
1112 | printf("\n" ); |
1113 | } |
1114 | } |
1115 | if (i % 16 != 0) { |
1116 | printf("\n" ); |
1117 | } |
1118 | } |
1119 | #endif |
1120 | |
1121 | void |
1122 | cardbus_conf_capture(cardbus_chipset_tag_t cc, cardbus_function_tag_t cf, |
1123 | pcitag_t tag, struct cardbus_conf_state *pcs) |
1124 | { |
1125 | int off; |
1126 | |
1127 | for (off = 0; off < 16; off++) |
1128 | pcs->reg[off] = cardbus_conf_read(cc, cf, tag, (off * 4)); |
1129 | } |
1130 | |
1131 | void |
1132 | cardbus_conf_restore(cardbus_chipset_tag_t cc, cardbus_function_tag_t cf, |
1133 | pcitag_t tag, struct cardbus_conf_state *pcs) |
1134 | { |
1135 | int off; |
1136 | pcireg_t val; |
1137 | |
1138 | for (off = 15; off >= 0; off--) { |
1139 | val = cardbus_conf_read(cc, cf, tag, (off * 4)); |
1140 | if (val != pcs->reg[off]) |
1141 | cardbus_conf_write(cc, cf,tag, (off * 4), pcs->reg[off]); |
1142 | } |
1143 | } |
1144 | |
1145 | struct cardbus_child_power { |
1146 | struct cardbus_conf_state p_cardbusconf; |
1147 | cardbus_devfunc_t p_ct; |
1148 | pcitag_t p_tag; |
1149 | cardbus_chipset_tag_t p_cc; |
1150 | cardbus_function_tag_t p_cf; |
1151 | pcireg_t p_pm_cap; |
1152 | bool p_has_pm; |
1153 | int p_pm_offset; |
1154 | }; |
1155 | |
1156 | static bool |
1157 | cardbus_child_suspend(device_t dv, const pmf_qual_t *qual) |
1158 | { |
1159 | struct cardbus_child_power *priv = device_pmf_bus_private(dv); |
1160 | |
1161 | cardbus_conf_capture(priv->p_cc, priv->p_cf, priv->p_tag, |
1162 | &priv->p_cardbusconf); |
1163 | |
1164 | if (priv->p_has_pm && |
1165 | cardbus_set_powerstate_int(priv->p_ct, priv->p_tag, |
1166 | PCI_PMCSR_STATE_D3, priv->p_pm_offset, priv->p_pm_cap)) { |
1167 | aprint_error_dev(dv, "unsupported state, continuing.\n" ); |
1168 | return false; |
1169 | } |
1170 | |
1171 | Cardbus_function_disable(priv->p_ct); |
1172 | |
1173 | return true; |
1174 | } |
1175 | |
1176 | static bool |
1177 | cardbus_child_resume(device_t dv, const pmf_qual_t *qual) |
1178 | { |
1179 | struct cardbus_child_power *priv = device_pmf_bus_private(dv); |
1180 | |
1181 | Cardbus_function_enable(priv->p_ct); |
1182 | |
1183 | if (priv->p_has_pm && |
1184 | cardbus_set_powerstate_int(priv->p_ct, priv->p_tag, |
1185 | PCI_PMCSR_STATE_D0, priv->p_pm_offset, priv->p_pm_cap)) { |
1186 | aprint_error_dev(dv, "unsupported state, continuing.\n" ); |
1187 | return false; |
1188 | } |
1189 | |
1190 | cardbus_conf_restore(priv->p_cc, priv->p_cf, priv->p_tag, |
1191 | &priv->p_cardbusconf); |
1192 | |
1193 | return true; |
1194 | } |
1195 | |
1196 | static void |
1197 | cardbus_child_deregister(device_t dv) |
1198 | { |
1199 | struct cardbus_child_power *priv = device_pmf_bus_private(dv); |
1200 | |
1201 | free(priv, M_DEVBUF); |
1202 | } |
1203 | |
1204 | static bool |
1205 | cardbus_child_register(device_t child) |
1206 | { |
1207 | device_t self = device_parent(child); |
1208 | struct cardbus_softc *sc = device_private(self); |
1209 | struct cardbus_devfunc *ct; |
1210 | struct cardbus_child_power *priv; |
1211 | int off; |
1212 | pcireg_t reg; |
1213 | |
1214 | ct = sc->sc_funcs[device_locator(child, CARDBUSCF_FUNCTION)]; |
1215 | |
1216 | priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK); |
1217 | |
1218 | priv->p_ct = ct; |
1219 | priv->p_cc = ct->ct_cc; |
1220 | priv->p_cf = ct->ct_cf; |
1221 | priv->p_tag = cardbus_make_tag(priv->p_cc, priv->p_cf, ct->ct_bus, |
1222 | ct->ct_func); |
1223 | |
1224 | if (cardbus_get_capability(priv->p_cc, priv->p_cf, priv->p_tag, |
1225 | PCI_CAP_PWRMGMT, &off, ®)) { |
1226 | priv->p_has_pm = true; |
1227 | priv->p_pm_offset = off; |
1228 | priv->p_pm_cap = reg; |
1229 | } else { |
1230 | priv->p_has_pm = false; |
1231 | priv->p_pm_offset = -1; |
1232 | } |
1233 | |
1234 | device_pmf_bus_register(child, priv, cardbus_child_suspend, |
1235 | cardbus_child_resume, 0, cardbus_child_deregister); |
1236 | |
1237 | return true; |
1238 | } |
1239 | |