1 | /* $NetBSD: cardslot.c,v 1.56 2016/09/24 23:54:49 mrg Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 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: cardslot.c,v 1.56 2016/09/24 23:54:49 mrg Exp $" ); |
31 | |
32 | #include "opt_cardslot.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/kthread.h> |
41 | |
42 | #include <sys/bus.h> |
43 | |
44 | #include <dev/cardbus/cardslotvar.h> |
45 | #include <dev/cardbus/cardbusvar.h> |
46 | #include <dev/pcmcia/pcmciavar.h> |
47 | #include <dev/pcmcia/pcmciachip.h> |
48 | |
49 | #include "locators.h" |
50 | |
51 | #if defined CARDSLOT_DEBUG |
52 | #define STATIC |
53 | #define DPRINTF(a) printf a |
54 | #else |
55 | #define STATIC static |
56 | #define DPRINTF(a) |
57 | #endif |
58 | |
59 | int pcmcia_error(device_t); |
60 | int |
61 | pcmcia_error(device_t dev) |
62 | { |
63 | |
64 | return 1; |
65 | } |
66 | __weak_alias(pcmcia_card_attach, pcmcia_error); |
67 | __weak_alias(pcmcia_card_deactivate, pcmcia_error); |
68 | __weak_alias(pcmcia_card_detach, pcmcia_error); |
69 | |
70 | |
71 | STATIC void cardslotchilddet(device_t, device_t); |
72 | STATIC void cardslotattach(device_t, device_t, void *); |
73 | STATIC int cardslotdetach(device_t, int); |
74 | |
75 | STATIC int cardslotmatch(device_t, cfdata_t, void *); |
76 | static void cardslot_event_thread(void *arg); |
77 | |
78 | STATIC int cardslot_cb_print(void *aux, const char *pcic); |
79 | static int cardslot_16_print(void *, const char *); |
80 | static int cardslot_16_submatch(device_t, cfdata_t, const int *, void *); |
81 | |
82 | CFATTACH_DECL3_NEW(cardslot, sizeof(struct cardslot_softc), |
83 | cardslotmatch, cardslotattach, cardslotdetach, NULL, NULL, cardslotchilddet, |
84 | DVF_DETACH_SHUTDOWN); |
85 | |
86 | STATIC int |
87 | cardslotmatch(device_t parent, cfdata_t cf, |
88 | void *aux) |
89 | { |
90 | struct cardslot_attach_args *caa = aux; |
91 | |
92 | if (caa->caa_cb_attach == NULL && caa->caa_16_attach == NULL) { |
93 | /* Neither CardBus nor 16-bit PCMCIA are defined. */ |
94 | return 0; |
95 | } |
96 | |
97 | return 1; |
98 | } |
99 | |
100 | STATIC void |
101 | cardslotchilddet(device_t self, device_t child) |
102 | { |
103 | struct cardslot_softc *sc = device_private(self); |
104 | |
105 | KASSERT(sc->sc_cb_softc == device_private(child) || |
106 | sc->sc_16_softc == child); |
107 | |
108 | if (sc->sc_cb_softc == device_private(child)) |
109 | sc->sc_cb_softc = NULL; |
110 | else if (sc->sc_16_softc == child) |
111 | sc->sc_16_softc = NULL; |
112 | } |
113 | |
114 | STATIC void |
115 | cardslotattach(device_t parent, device_t self, void *aux) |
116 | { |
117 | struct cardslot_softc *sc = device_private(self); |
118 | struct cardslot_attach_args *caa = aux; |
119 | |
120 | struct cbslot_attach_args *cba = caa->caa_cb_attach; |
121 | struct pcmciabus_attach_args *pa = caa->caa_16_attach; |
122 | |
123 | struct cardbus_softc *csc = NULL; |
124 | struct pcmcia_softc *psc = NULL; |
125 | |
126 | sc->sc_dev = self; |
127 | |
128 | sc->sc_cb_softc = NULL; |
129 | sc->sc_16_softc = NULL; |
130 | SIMPLEQ_INIT(&sc->sc_events); |
131 | sc->sc_th_enable = 0; |
132 | |
133 | aprint_naive("\n" ); |
134 | aprint_normal("\n" ); |
135 | |
136 | DPRINTF(("%s attaching CardBus bus...\n" , device_xname(self))); |
137 | if (cba != NULL) { |
138 | csc = device_private(config_found_ia(self, "cbbus" , cba, |
139 | cardslot_cb_print)); |
140 | if (csc) { |
141 | /* cardbus found */ |
142 | DPRINTF(("%s: found cardbus on %s\n" , __func__, |
143 | device_xname(self))); |
144 | sc->sc_cb_softc = csc; |
145 | } |
146 | } |
147 | |
148 | if (pa != NULL) { |
149 | sc->sc_16_softc = config_found_sm_loc(self, "pcmciabus" , NULL, |
150 | pa, cardslot_16_print, |
151 | cardslot_16_submatch); |
152 | if (sc->sc_16_softc) { |
153 | /* pcmcia 16-bit bus found */ |
154 | DPRINTF(("%s: found 16-bit pcmcia bus\n" , __func__)); |
155 | psc = device_private(sc->sc_16_softc); |
156 | } |
157 | } |
158 | |
159 | if (csc != NULL || psc != NULL) { |
160 | config_pending_incr(self); |
161 | if (kthread_create(PRI_NONE, 0, NULL, cardslot_event_thread, |
162 | sc, &sc->sc_event_thread, "%s" , device_xname(self))) { |
163 | aprint_error_dev(sc->sc_dev, |
164 | "unable to create thread\n" ); |
165 | panic("cardslotattach" ); |
166 | } |
167 | sc->sc_th_enable = 1; |
168 | } |
169 | |
170 | if (csc && (csc->sc_cf->cardbus_ctrl)(csc->sc_cc, CARDBUS_CD)) { |
171 | DPRINTF(("%s: CardBus card found\n" , __func__)); |
172 | /* attach deferred */ |
173 | cardslot_event_throw(sc, CARDSLOT_EVENT_INSERTION_CB); |
174 | } |
175 | |
176 | if (psc && (psc->pct->card_detect)(psc->pch)) { |
177 | DPRINTF(("%s: 16-bit card found\n" , __func__)); |
178 | /* attach deferred */ |
179 | cardslot_event_throw(sc, CARDSLOT_EVENT_INSERTION_16); |
180 | } |
181 | |
182 | if (!pmf_device_register(self, NULL, NULL)) |
183 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
184 | } |
185 | |
186 | STATIC int |
187 | cardslotdetach(device_t self, int flags) |
188 | { |
189 | int rc; |
190 | struct cardslot_softc *sc = device_private(self); |
191 | |
192 | if ((rc = config_detach_children(self, flags)) != 0) |
193 | return rc; |
194 | |
195 | sc->sc_th_enable = 0; |
196 | wakeup(&sc->sc_events); |
197 | while (sc->sc_event_thread != NULL) |
198 | (void)tsleep(sc, PWAIT, "cardslotthd" , 0); |
199 | |
200 | if (!SIMPLEQ_EMPTY(&sc->sc_events)) |
201 | aprint_error_dev(self, "events outstanding" ); |
202 | |
203 | pmf_device_deregister(self); |
204 | return 0; |
205 | } |
206 | |
207 | STATIC int |
208 | cardslot_cb_print(void *aux, const char *pnp) |
209 | { |
210 | struct cbslot_attach_args *cba = aux; |
211 | |
212 | if (pnp != NULL) { |
213 | aprint_normal("cardbus at %s subordinate bus %d" , |
214 | pnp, cba->cba_bus); |
215 | } |
216 | |
217 | return UNCONF; |
218 | } |
219 | |
220 | |
221 | static int |
222 | cardslot_16_submatch(device_t parent, cfdata_t cf, |
223 | const int *ldesc, void *aux) |
224 | { |
225 | |
226 | if (cf->cf_loc[PCMCIABUSCF_CONTROLLER] != PCMCIABUSCF_CONTROLLER_DEFAULT |
227 | && cf->cf_loc[PCMCIABUSCF_CONTROLLER] != 0) { |
228 | return 0; |
229 | } |
230 | |
231 | if (cf->cf_loc[PCMCIABUSCF_CONTROLLER] == PCMCIABUSCF_CONTROLLER_DEFAULT) { |
232 | return (config_match(parent, cf, aux)); |
233 | } |
234 | |
235 | return 0; |
236 | } |
237 | |
238 | |
239 | |
240 | static int |
241 | cardslot_16_print(void *arg, const char *pnp) |
242 | { |
243 | |
244 | if (pnp != NULL) { |
245 | aprint_normal("pcmciabus at %s" , pnp); |
246 | } |
247 | |
248 | return UNCONF; |
249 | } |
250 | |
251 | |
252 | /* |
253 | * void cardslot_event_throw(struct cardslot_softc *sc, int ev) |
254 | * |
255 | * This function throws an event to the event handler. If the state |
256 | * of a slot is changed, it should be noticed using this function. |
257 | */ |
258 | void |
259 | cardslot_event_throw(struct cardslot_softc *sc, int ev) |
260 | { |
261 | struct cardslot_event *ce; |
262 | |
263 | DPRINTF(("cardslot_event_throw: an event %s comes\n" , |
264 | ev == CARDSLOT_EVENT_INSERTION_CB ? "CardBus Card inserted" : |
265 | ev == CARDSLOT_EVENT_INSERTION_16 ? "16-bit Card inserted" : |
266 | ev == CARDSLOT_EVENT_REMOVAL_CB ? "CardBus Card removed" : |
267 | ev == CARDSLOT_EVENT_REMOVAL_16 ? "16-bit Card removed" : "???" )); |
268 | |
269 | if (NULL == (ce = (struct cardslot_event *)malloc(sizeof (struct cardslot_event), M_TEMP, M_NOWAIT))) { |
270 | panic("cardslot_enevt" ); |
271 | } |
272 | |
273 | ce->ce_type = ev; |
274 | |
275 | { |
276 | int s = spltty(); |
277 | SIMPLEQ_INSERT_TAIL(&sc->sc_events, ce, ce_q); |
278 | splx(s); |
279 | } |
280 | |
281 | wakeup(&sc->sc_events); |
282 | |
283 | return; |
284 | } |
285 | |
286 | |
287 | /* |
288 | * static void cardslot_event_thread(void *arg) |
289 | * |
290 | * This function is the main routine handing cardslot events such as |
291 | * insertions and removals. |
292 | * |
293 | */ |
294 | static void |
295 | cardslot_event_thread(void *arg) |
296 | { |
297 | struct cardslot_softc *sc = arg; |
298 | struct cardslot_event *ce; |
299 | int s, first = 1; |
300 | static int antonym_ev[4] = { |
301 | CARDSLOT_EVENT_REMOVAL_16, CARDSLOT_EVENT_INSERTION_16, |
302 | CARDSLOT_EVENT_REMOVAL_CB, CARDSLOT_EVENT_INSERTION_CB |
303 | }; |
304 | |
305 | while (sc->sc_th_enable) { |
306 | s = spltty(); |
307 | if ((ce = SIMPLEQ_FIRST(&sc->sc_events)) == NULL) { |
308 | splx(s); |
309 | if (first) { |
310 | first = 0; |
311 | config_pending_decr(sc->sc_dev); |
312 | } |
313 | (void) tsleep(&sc->sc_events, PWAIT, "cardslotev" , 0); |
314 | continue; |
315 | } |
316 | SIMPLEQ_REMOVE_HEAD(&sc->sc_events, ce_q); |
317 | splx(s); |
318 | |
319 | if (IS_CARDSLOT_INSERT_REMOVE_EV(ce->ce_type)) { |
320 | /* Chattering suppression */ |
321 | s = spltty(); |
322 | while (1) { |
323 | struct cardslot_event *ce1, *ce2; |
324 | |
325 | if ((ce1 = SIMPLEQ_FIRST(&sc->sc_events)) == NULL) { |
326 | break; |
327 | } |
328 | if (ce1->ce_type != antonym_ev[ce->ce_type]) { |
329 | break; |
330 | } |
331 | if ((ce2 = SIMPLEQ_NEXT(ce1, ce_q)) == NULL) { |
332 | break; |
333 | } |
334 | if (ce2->ce_type == ce->ce_type) { |
335 | SIMPLEQ_REMOVE_HEAD(&sc->sc_events, |
336 | ce_q); |
337 | free(ce1, M_TEMP); |
338 | SIMPLEQ_REMOVE_HEAD(&sc->sc_events, |
339 | ce_q); |
340 | free(ce2, M_TEMP); |
341 | } |
342 | } |
343 | splx(s); |
344 | } |
345 | |
346 | switch (ce->ce_type) { |
347 | case CARDSLOT_EVENT_INSERTION_CB: |
348 | if ((CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_CB) |
349 | || (CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_16)) { |
350 | if (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING) { |
351 | /* |
352 | * A card has already been |
353 | * inserted and works. |
354 | */ |
355 | break; |
356 | } |
357 | } |
358 | |
359 | if (sc->sc_cb_softc) { |
360 | CARDSLOT_SET_CARDTYPE(sc->sc_status, |
361 | CARDSLOT_STATUS_CARD_CB); |
362 | if (cardbus_attach_card(sc->sc_cb_softc) > 0) { |
363 | /* at least one function works */ |
364 | CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_WORKING); |
365 | } else { |
366 | /* |
367 | * no functions work or this |
368 | * card is not known |
369 | */ |
370 | CARDSLOT_SET_WORK(sc->sc_status, |
371 | CARDSLOT_STATUS_NOTWORK); |
372 | } |
373 | } else { |
374 | panic("no cardbus on %s" , |
375 | device_xname(sc->sc_dev)); |
376 | } |
377 | |
378 | break; |
379 | |
380 | case CARDSLOT_EVENT_INSERTION_16: |
381 | if ((CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_CB) |
382 | || (CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_16)) { |
383 | if (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING) { |
384 | /* |
385 | * A card has already been |
386 | * inserted and work. |
387 | */ |
388 | break; |
389 | } |
390 | } |
391 | if (sc->sc_16_softc) { |
392 | CARDSLOT_SET_CARDTYPE(sc->sc_status, CARDSLOT_STATUS_CARD_16); |
393 | if (pcmcia_card_attach(sc->sc_16_softc)) { |
394 | /* Do not attach */ |
395 | CARDSLOT_SET_WORK(sc->sc_status, |
396 | CARDSLOT_STATUS_NOTWORK); |
397 | } else { |
398 | /* working */ |
399 | CARDSLOT_SET_WORK(sc->sc_status, |
400 | CARDSLOT_STATUS_WORKING); |
401 | } |
402 | } else { |
403 | panic("no 16-bit pcmcia on %s" , |
404 | device_xname(sc->sc_dev)); |
405 | } |
406 | |
407 | break; |
408 | |
409 | case CARDSLOT_EVENT_REMOVAL_CB: |
410 | if (CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_CB) { |
411 | /* CardBus card has not been inserted. */ |
412 | if (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING) { |
413 | cardbus_detach_card(sc->sc_cb_softc); |
414 | CARDSLOT_SET_WORK(sc->sc_status, |
415 | CARDSLOT_STATUS_NOTWORK); |
416 | CARDSLOT_SET_WORK(sc->sc_status, |
417 | CARDSLOT_STATUS_CARD_NONE); |
418 | } |
419 | CARDSLOT_SET_CARDTYPE(sc->sc_status, |
420 | CARDSLOT_STATUS_CARD_NONE); |
421 | } else if (CARDSLOT_CARDTYPE(sc->sc_status) != CARDSLOT_STATUS_CARD_16) { |
422 | /* Unknown card... */ |
423 | CARDSLOT_SET_CARDTYPE(sc->sc_status, |
424 | CARDSLOT_STATUS_CARD_NONE); |
425 | } |
426 | CARDSLOT_SET_WORK(sc->sc_status, |
427 | CARDSLOT_STATUS_NOTWORK); |
428 | break; |
429 | |
430 | case CARDSLOT_EVENT_REMOVAL_16: |
431 | DPRINTF(("%s: removal event\n" , device_xname(sc->sc_dev))); |
432 | if (CARDSLOT_CARDTYPE(sc->sc_status) != CARDSLOT_STATUS_CARD_16) { |
433 | /* 16-bit card has not been inserted. */ |
434 | break; |
435 | } |
436 | if ((sc->sc_16_softc != NULL) |
437 | && (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING)) { |
438 | struct pcmcia_softc *psc = |
439 | device_private(sc->sc_16_softc); |
440 | |
441 | pcmcia_card_deactivate(sc->sc_16_softc); |
442 | pcmcia_chip_socket_disable(psc->pct, psc->pch); |
443 | pcmcia_card_detach(sc->sc_16_softc, |
444 | DETACH_FORCE); |
445 | } |
446 | CARDSLOT_SET_CARDTYPE(sc->sc_status, CARDSLOT_STATUS_CARD_NONE); |
447 | CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_NOTWORK); |
448 | break; |
449 | |
450 | default: |
451 | panic("cardslot_event_thread: unknown event %d" , ce->ce_type); |
452 | } |
453 | free(ce, M_TEMP); |
454 | } |
455 | |
456 | sc->sc_event_thread = NULL; |
457 | |
458 | /* In case the parent device is waiting for us to exit. */ |
459 | wakeup(sc); |
460 | |
461 | kthread_exit(0); |
462 | } |
463 | |