Commit | Line | Data |
---|---|---|
635d2b00 GKH |
1 | /* |
2 | * --------------------------------------------------------------------------- | |
3 | * | |
4 | * FILE: sdio_mmc.c | |
5 | * | |
6 | * PURPOSE: SDIO driver interface for generic MMC stack. | |
7 | * | |
8 | * Copyright (C) 2008-2009 by Cambridge Silicon Radio Ltd. | |
9 | * | |
10 | * --------------------------------------------------------------------------- | |
11 | */ | |
12 | #include <linux/module.h> | |
13 | #include <linux/init.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/mutex.h> | |
16 | #include <linux/gfp.h> | |
635d2b00 GKH |
17 | #include <linux/mmc/core.h> |
18 | #include <linux/mmc/card.h> | |
19 | #include <linux/mmc/host.h> | |
20 | #include <linux/mmc/sdio_func.h> | |
21 | #include <linux/mmc/sdio_ids.h> | |
22 | #include <linux/mmc/sdio.h> | |
95edd09e | 23 | #include <linux/suspend.h> |
635d2b00 GKH |
24 | |
25 | #include "unifi_priv.h" | |
26 | ||
95edd09e GKH |
27 | #ifdef ANDROID_BUILD |
28 | struct wake_lock unifi_sdio_wake_lock; /* wakelock to prevent suspend while resuming */ | |
29 | #endif | |
635d2b00 GKH |
30 | |
31 | static CsrSdioFunctionDriver *sdio_func_drv; | |
32 | ||
95edd09e GKH |
33 | #ifdef CONFIG_PM |
34 | static int uf_sdio_mmc_power_event(struct notifier_block *this, unsigned long event, void *ptr); | |
35 | #endif | |
36 | ||
635d2b00 GKH |
37 | /* |
38 | * We need to keep track of the power on/off because we can not call | |
39 | * mmc_power_restore_host() when the card is already powered. | |
40 | * Even then, we need to patch the MMC driver to add a power_restore handler | |
95edd09e GKH |
41 | * in the mmc_sdio_ops structure. If the MMC driver before 2.6.37 is not patched, |
42 | * mmc_power_save_host() and mmc_power_restore_host() are no-ops in the kernel, | |
43 | * returning immediately (at least on x86). | |
635d2b00 GKH |
44 | */ |
45 | static int card_is_powered = 1; | |
635d2b00 GKH |
46 | |
47 | /* MMC uses ENOMEDIUM to indicate card gone away */ | |
48 | ||
49 | static CsrResult | |
50 | ConvertSdioToCsrSdioResult(int r) | |
51 | { | |
52 | CsrResult csrResult = CSR_RESULT_FAILURE; | |
53 | ||
54 | switch (r) { | |
55 | case 0: | |
56 | csrResult = CSR_RESULT_SUCCESS; | |
57 | break; | |
58 | case -EIO: | |
59 | case -EILSEQ: | |
60 | csrResult = CSR_SDIO_RESULT_CRC_ERROR; | |
61 | break; | |
62 | /* Timeout errors */ | |
63 | case -ETIMEDOUT: | |
64 | case -EBUSY: | |
65 | csrResult = CSR_SDIO_RESULT_TIMEOUT; | |
66 | break; | |
67 | case -ENODEV: | |
68 | case -ENOMEDIUM: | |
69 | csrResult = CSR_SDIO_RESULT_NO_DEVICE; | |
70 | break; | |
71 | case -EINVAL: | |
72 | csrResult = CSR_SDIO_RESULT_INVALID_VALUE; | |
73 | break; | |
74 | case -ENOMEM: | |
75 | case -ENOSYS: | |
76 | case -ERANGE: | |
77 | case -ENXIO: | |
78 | csrResult = CSR_RESULT_FAILURE; | |
79 | break; | |
80 | default: | |
81 | unifi_warning(NULL, "Unrecognised SDIO error code: %d\n", r); | |
82 | break; | |
83 | } | |
84 | ||
85 | return csrResult; | |
86 | } | |
87 | ||
88 | ||
89 | static int | |
90 | csr_io_rw_direct(struct mmc_card *card, int write, uint8_t fn, | |
91 | uint32_t addr, uint8_t in, uint8_t* out) | |
92 | { | |
93 | struct mmc_command cmd; | |
94 | int err; | |
95 | ||
96 | BUG_ON(!card); | |
97 | BUG_ON(fn > 7); | |
98 | ||
99 | memset(&cmd, 0, sizeof(struct mmc_command)); | |
100 | ||
101 | cmd.opcode = SD_IO_RW_DIRECT; | |
102 | cmd.arg = write ? 0x80000000 : 0x00000000; | |
103 | cmd.arg |= fn << 28; | |
104 | cmd.arg |= (write && out) ? 0x08000000 : 0x00000000; | |
105 | cmd.arg |= addr << 9; | |
106 | cmd.arg |= in; | |
107 | cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; | |
108 | ||
109 | err = mmc_wait_for_cmd(card->host, &cmd, 0); | |
110 | if (err) | |
111 | return err; | |
112 | ||
113 | /* this function is not exported, so we will need to sort it out here | |
114 | * for now, lets hard code it to sdio */ | |
115 | if (0) { | |
116 | /* old arg (mmc_host_is_spi(card->host)) { */ | |
117 | /* host driver already reported errors */ | |
118 | } else { | |
119 | if (cmd.resp[0] & R5_ERROR) { | |
120 | printk(KERN_ERR "%s: r5 error 0x%02x\n", | |
121 | __FUNCTION__, cmd.resp[0]); | |
122 | return -EIO; | |
123 | } | |
124 | if (cmd.resp[0] & R5_FUNCTION_NUMBER) | |
125 | return -EINVAL; | |
126 | if (cmd.resp[0] & R5_OUT_OF_RANGE) | |
127 | return -ERANGE; | |
128 | } | |
129 | ||
130 | if (out) { | |
131 | if (0) { /* old argument (mmc_host_is_spi(card->host)) */ | |
132 | *out = (cmd.resp[0] >> 8) & 0xFF; | |
133 | } | |
134 | else { | |
135 | *out = cmd.resp[0] & 0xFF; | |
136 | } | |
137 | } | |
138 | ||
139 | return CSR_RESULT_SUCCESS; | |
140 | } | |
141 | ||
142 | ||
143 | CsrResult | |
26a6b2e1 | 144 | CsrSdioRead8(CsrSdioFunction *function, u32 address, u8 *data) |
635d2b00 GKH |
145 | { |
146 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
147 | int err = 0; | |
148 | ||
149 | _sdio_claim_host(func); | |
150 | *data = sdio_readb(func, address, &err); | |
151 | _sdio_release_host(func); | |
152 | ||
153 | if (err) { | |
635d2b00 GKH |
154 | return ConvertSdioToCsrSdioResult(err); |
155 | } | |
156 | ||
157 | return CSR_RESULT_SUCCESS; | |
158 | } /* CsrSdioRead8() */ | |
159 | ||
160 | CsrResult | |
26a6b2e1 | 161 | CsrSdioWrite8(CsrSdioFunction *function, u32 address, u8 data) |
635d2b00 GKH |
162 | { |
163 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
164 | int err = 0; | |
165 | ||
166 | _sdio_claim_host(func); | |
167 | sdio_writeb(func, data, address, &err); | |
168 | _sdio_release_host(func); | |
169 | ||
170 | if (err) { | |
635d2b00 GKH |
171 | return ConvertSdioToCsrSdioResult(err); |
172 | } | |
173 | ||
174 | return CSR_RESULT_SUCCESS; | |
175 | } /* CsrSdioWrite8() */ | |
176 | ||
177 | CsrResult | |
26a6b2e1 | 178 | CsrSdioRead16(CsrSdioFunction *function, u32 address, u16 *data) |
635d2b00 GKH |
179 | { |
180 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
181 | int err; | |
182 | uint8_t b0, b1; | |
183 | ||
184 | _sdio_claim_host(func); | |
185 | b0 = sdio_readb(func, address, &err); | |
186 | if (err) { | |
187 | _sdio_release_host(func); | |
188 | return ConvertSdioToCsrSdioResult(err); | |
189 | } | |
190 | ||
191 | b1 = sdio_readb(func, address+1, &err); | |
192 | if (err) { | |
193 | _sdio_release_host(func); | |
194 | return ConvertSdioToCsrSdioResult(err); | |
195 | } | |
196 | _sdio_release_host(func); | |
197 | ||
198 | *data = ((uint16_t)b1 << 8) | b0; | |
199 | ||
200 | return CSR_RESULT_SUCCESS; | |
201 | } /* CsrSdioRead16() */ | |
202 | ||
203 | ||
204 | CsrResult | |
26a6b2e1 | 205 | CsrSdioWrite16(CsrSdioFunction *function, u32 address, u16 data) |
635d2b00 GKH |
206 | { |
207 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
208 | int err; | |
209 | uint8_t b0, b1; | |
210 | ||
211 | _sdio_claim_host(func); | |
212 | b1 = (data >> 8) & 0xFF; | |
213 | sdio_writeb(func, b1, address+1, &err); | |
214 | if (err) { | |
215 | _sdio_release_host(func); | |
216 | return ConvertSdioToCsrSdioResult(err); | |
217 | } | |
218 | ||
219 | b0 = data & 0xFF; | |
220 | sdio_writeb(func, b0, address, &err); | |
221 | if (err) { | |
222 | _sdio_release_host(func); | |
223 | return ConvertSdioToCsrSdioResult(err); | |
224 | } | |
225 | ||
226 | _sdio_release_host(func); | |
227 | return CSR_RESULT_SUCCESS; | |
228 | } /* CsrSdioWrite16() */ | |
229 | ||
230 | ||
231 | CsrResult | |
26a6b2e1 | 232 | CsrSdioF0Read8(CsrSdioFunction *function, u32 address, u8 *data) |
635d2b00 GKH |
233 | { |
234 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
235 | int err = 0; | |
236 | ||
237 | _sdio_claim_host(func); | |
238 | #ifdef MMC_QUIRK_LENIENT_FN0 | |
239 | *data = sdio_f0_readb(func, address, &err); | |
240 | #else | |
241 | err = csr_io_rw_direct(func->card, 0, 0, address, 0, data); | |
242 | #endif | |
243 | _sdio_release_host(func); | |
244 | ||
245 | if (err) { | |
635d2b00 GKH |
246 | return ConvertSdioToCsrSdioResult(err); |
247 | } | |
248 | ||
249 | return CSR_RESULT_SUCCESS; | |
250 | } /* CsrSdioF0Read8() */ | |
251 | ||
252 | CsrResult | |
26a6b2e1 | 253 | CsrSdioF0Write8(CsrSdioFunction *function, u32 address, u8 data) |
635d2b00 GKH |
254 | { |
255 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
256 | int err = 0; | |
257 | ||
258 | _sdio_claim_host(func); | |
259 | #ifdef MMC_QUIRK_LENIENT_FN0 | |
260 | sdio_f0_writeb(func, data, address, &err); | |
261 | #else | |
262 | err = csr_io_rw_direct(func->card, 1, 0, address, data, NULL); | |
263 | #endif | |
264 | _sdio_release_host(func); | |
265 | ||
266 | if (err) { | |
635d2b00 GKH |
267 | return ConvertSdioToCsrSdioResult(err); |
268 | } | |
269 | ||
270 | return CSR_RESULT_SUCCESS; | |
271 | } /* CsrSdioF0Write8() */ | |
272 | ||
273 | ||
274 | CsrResult | |
26a6b2e1 | 275 | CsrSdioRead(CsrSdioFunction *function, u32 address, void *data, u32 length) |
635d2b00 GKH |
276 | { |
277 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
278 | int err; | |
279 | ||
280 | _sdio_claim_host(func); | |
281 | err = sdio_readsb(func, data, address, length); | |
282 | _sdio_release_host(func); | |
283 | ||
284 | if (err) { | |
635d2b00 GKH |
285 | return ConvertSdioToCsrSdioResult(err); |
286 | } | |
287 | ||
288 | return CSR_RESULT_SUCCESS; | |
289 | } /* CsrSdioRead() */ | |
290 | ||
291 | CsrResult | |
26a6b2e1 | 292 | CsrSdioWrite(CsrSdioFunction *function, u32 address, const void *data, u32 length) |
635d2b00 GKH |
293 | { |
294 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
295 | int err; | |
296 | ||
297 | _sdio_claim_host(func); | |
298 | err = sdio_writesb(func, address, (void*)data, length); | |
299 | _sdio_release_host(func); | |
300 | ||
301 | if (err) { | |
635d2b00 GKH |
302 | return ConvertSdioToCsrSdioResult(err); |
303 | } | |
304 | ||
305 | return CSR_RESULT_SUCCESS; | |
306 | } /* CsrSdioWrite() */ | |
307 | ||
308 | ||
309 | static int | |
310 | csr_sdio_enable_hs(struct mmc_card *card) | |
311 | { | |
312 | int ret; | |
313 | u8 speed; | |
314 | ||
95edd09e GKH |
315 | if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) { |
316 | /* We've asked for HS clock rates, but controller doesn't | |
317 | * claim to support it. We should limit the clock | |
318 | * to 25MHz via module parameter. | |
319 | */ | |
320 | printk(KERN_INFO "unifi: request HS but not MMC_CAP_SD_HIGHSPEED"); | |
635d2b00 | 321 | return 0; |
95edd09e | 322 | } |
635d2b00 GKH |
323 | |
324 | if (!card->cccr.high_speed) | |
325 | return 0; | |
326 | ||
95edd09e | 327 | #if 1 |
635d2b00 GKH |
328 | ret = csr_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed); |
329 | if (ret) | |
330 | return ret; | |
331 | ||
332 | speed |= SDIO_SPEED_EHS; | |
95edd09e GKH |
333 | #else |
334 | /* Optimisation: Eliminate read by always assuming SHS and that reserved bits can be zero */ | |
335 | speed = SDIO_SPEED_EHS | SDIO_SPEED_SHS; | |
336 | #endif | |
635d2b00 GKH |
337 | |
338 | ret = csr_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL); | |
339 | if (ret) | |
340 | return ret; | |
341 | ||
342 | mmc_card_set_highspeed(card); | |
343 | card->host->ios.timing = MMC_TIMING_SD_HS; | |
344 | card->host->ops->set_ios(card->host, &card->host->ios); | |
345 | ||
346 | return 0; | |
347 | } | |
348 | ||
349 | static int | |
350 | csr_sdio_disable_hs(struct mmc_card *card) | |
351 | { | |
352 | int ret; | |
353 | u8 speed; | |
354 | ||
355 | if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) | |
356 | return 0; | |
357 | ||
358 | if (!card->cccr.high_speed) | |
359 | return 0; | |
95edd09e | 360 | #if 1 |
635d2b00 GKH |
361 | ret = csr_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed); |
362 | if (ret) | |
363 | return ret; | |
364 | ||
365 | speed &= ~SDIO_SPEED_EHS; | |
95edd09e GKH |
366 | #else |
367 | /* Optimisation: Eliminate read by always assuming SHS and that reserved bits can be zero */ | |
368 | speed = SDIO_SPEED_SHS; /* clear SDIO_SPEED_EHS */ | |
369 | #endif | |
635d2b00 GKH |
370 | |
371 | ret = csr_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL); | |
372 | if (ret) | |
373 | return ret; | |
374 | ||
375 | card->state &= ~MMC_STATE_HIGHSPEED; | |
376 | card->host->ios.timing = MMC_TIMING_LEGACY; | |
377 | card->host->ops->set_ios(card->host, &card->host->ios); | |
378 | ||
379 | return 0; | |
380 | } | |
381 | ||
382 | ||
383 | /* | |
384 | * --------------------------------------------------------------------------- | |
385 | * CsrSdioMaxBusClockFrequencySet | |
386 | * | |
387 | * Set the maximum SDIO bus clock speed to use. | |
388 | * | |
389 | * Arguments: | |
390 | * sdio SDIO context pointer | |
391 | * maxFrequency maximum clock speed in Hz | |
392 | * | |
393 | * Returns: | |
394 | * an error code. | |
395 | * --------------------------------------------------------------------------- | |
396 | */ | |
397 | CsrResult | |
26a6b2e1 | 398 | CsrSdioMaxBusClockFrequencySet(CsrSdioFunction *function, u32 maxFrequency) |
635d2b00 GKH |
399 | { |
400 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
401 | struct mmc_host *host = func->card->host; | |
402 | struct mmc_ios *ios = &host->ios; | |
403 | unsigned int max_hz; | |
404 | int err; | |
26a6b2e1 | 405 | u32 max_khz = maxFrequency/1000; |
635d2b00 GKH |
406 | |
407 | if (!max_khz || max_khz > sdio_clock) { | |
408 | max_khz = sdio_clock; | |
409 | } | |
410 | ||
411 | _sdio_claim_host(func); | |
412 | max_hz = 1000 * max_khz; | |
413 | if (max_hz > host->f_max) { | |
414 | max_hz = host->f_max; | |
415 | } | |
416 | ||
417 | if (max_hz > 25000000) { | |
418 | err = csr_sdio_enable_hs(func->card); | |
419 | } else { | |
420 | err = csr_sdio_disable_hs(func->card); | |
421 | } | |
422 | if (err) { | |
423 | printk(KERN_ERR "SDIO warning: Failed to configure SDIO clock mode\n"); | |
424 | _sdio_release_host(func); | |
425 | return CSR_RESULT_SUCCESS; | |
426 | } | |
427 | ||
428 | ios->clock = max_hz; | |
429 | host->ops->set_ios(host, ios); | |
430 | ||
431 | _sdio_release_host(func); | |
432 | ||
433 | return CSR_RESULT_SUCCESS; | |
434 | } /* CsrSdioMaxBusClockFrequencySet() */ | |
435 | ||
436 | ||
437 | /* | |
438 | * --------------------------------------------------------------------------- | |
439 | * CsrSdioInterruptEnable | |
440 | * CsrSdioInterruptDisable | |
441 | * | |
442 | * Enable or disable the SDIO interrupt. | |
443 | * The driver disables the SDIO interrupt until the i/o thread can | |
444 | * process it. | |
445 | * The SDIO interrupt can be disabled by modifying the SDIO_INT_ENABLE | |
446 | * register in the Card Common Control Register block, but this requires | |
447 | * two CMD52 operations. A better solution is to mask the interrupt at | |
448 | * the host controller. | |
449 | * | |
450 | * Arguments: | |
451 | * sdio SDIO context pointer | |
452 | * | |
453 | * Returns: | |
454 | * Zero on success or a UniFi driver error code. | |
455 | * | |
456 | * --------------------------------------------------------------------------- | |
457 | */ | |
458 | CsrResult | |
459 | CsrSdioInterruptEnable(CsrSdioFunction *function) | |
460 | { | |
461 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
462 | int err = 0; | |
463 | ||
464 | #ifdef CSR_CONFIG_MMC_INT_BYPASS_KSOFTIRQD | |
465 | sdio_unblock_card_irq(func); | |
466 | #else | |
467 | _sdio_claim_host(func); | |
468 | /* Write the Int Enable in CCCR block */ | |
469 | #ifdef MMC_QUIRK_LENIENT_FN0 | |
470 | sdio_f0_writeb(func, 0x3, SDIO_CCCR_IENx, &err); | |
471 | #else | |
472 | err = csr_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, 0x03, NULL); | |
473 | #endif | |
474 | _sdio_release_host(func); | |
475 | ||
635d2b00 | 476 | if (err) { |
95edd09e | 477 | printk(KERN_ERR "unifi: %s: error %d writing IENx\n", __FUNCTION__, err); |
635d2b00 GKH |
478 | return ConvertSdioToCsrSdioResult(err); |
479 | } | |
480 | #endif | |
481 | return CSR_RESULT_SUCCESS; | |
482 | } /* CsrSdioInterruptEnable() */ | |
483 | ||
484 | CsrResult | |
485 | CsrSdioInterruptDisable(CsrSdioFunction *function) | |
486 | { | |
487 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
488 | int err = 0; | |
489 | ||
490 | #ifdef CSR_CONFIG_MMC_INT_BYPASS_KSOFTIRQD | |
491 | sdio_block_card_irq(func); | |
492 | #else | |
493 | _sdio_claim_host(func); | |
494 | /* Write the Int Enable in CCCR block */ | |
495 | #ifdef MMC_QUIRK_LENIENT_FN0 | |
496 | sdio_f0_writeb(func, 0, SDIO_CCCR_IENx, &err); | |
497 | #else | |
498 | err = csr_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, 0x00, NULL); | |
499 | #endif | |
500 | _sdio_release_host(func); | |
501 | ||
635d2b00 | 502 | if (err) { |
95edd09e | 503 | printk(KERN_ERR "unifi: %s: error %d writing IENx\n", __FUNCTION__, err); |
635d2b00 GKH |
504 | return ConvertSdioToCsrSdioResult(err); |
505 | } | |
506 | #endif | |
507 | return CSR_RESULT_SUCCESS; | |
508 | } /* CsrSdioInterruptDisable() */ | |
509 | ||
510 | ||
511 | void CsrSdioInterruptAcknowledge(CsrSdioFunction *function) | |
512 | { | |
513 | } | |
514 | ||
515 | ||
516 | /* | |
517 | * --------------------------------------------------------------------------- | |
518 | * CsrSdioFunctionEnable | |
519 | * | |
520 | * Enable i/o on function 1. | |
521 | * | |
522 | * Arguments: | |
523 | * sdio SDIO context pointer | |
524 | * | |
525 | * Returns: | |
526 | * UniFi driver error code. | |
527 | * --------------------------------------------------------------------------- | |
528 | */ | |
529 | CsrResult | |
530 | CsrSdioFunctionEnable(CsrSdioFunction *function) | |
531 | { | |
532 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
533 | int err; | |
534 | ||
635d2b00 GKH |
535 | /* Enable UniFi function 1 (the 802.11 part). */ |
536 | _sdio_claim_host(func); | |
537 | err = sdio_enable_func(func); | |
538 | _sdio_release_host(func); | |
539 | if (err) { | |
540 | unifi_error(NULL, "Failed to enable SDIO function %d\n", func->num); | |
541 | } | |
542 | ||
635d2b00 GKH |
543 | return ConvertSdioToCsrSdioResult(err); |
544 | } /* CsrSdioFunctionEnable() */ | |
545 | ||
546 | ||
547 | /* | |
548 | * --------------------------------------------------------------------------- | |
549 | * CsrSdioFunctionDisable | |
550 | * | |
551 | * Enable i/o on function 1. | |
552 | * | |
553 | * Arguments: | |
554 | * sdio SDIO context pointer | |
555 | * | |
556 | * Returns: | |
557 | * UniFi driver error code. | |
558 | * --------------------------------------------------------------------------- | |
559 | */ | |
560 | CsrResult | |
561 | CsrSdioFunctionDisable(CsrSdioFunction *function) | |
562 | { | |
563 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
564 | int err; | |
565 | ||
635d2b00 GKH |
566 | /* Disable UniFi function 1 (the 802.11 part). */ |
567 | _sdio_claim_host(func); | |
568 | err = sdio_disable_func(func); | |
569 | _sdio_release_host(func); | |
570 | if (err) { | |
571 | unifi_error(NULL, "Failed to disable SDIO function %d\n", func->num); | |
572 | } | |
573 | ||
635d2b00 GKH |
574 | return ConvertSdioToCsrSdioResult(err); |
575 | } /* CsrSdioFunctionDisable() */ | |
576 | ||
577 | ||
578 | /* | |
579 | * --------------------------------------------------------------------------- | |
580 | * CsrSdioFunctionActive | |
581 | * | |
582 | * No-op as the bus goes to an active state at the start of every | |
583 | * command. | |
584 | * | |
585 | * Arguments: | |
586 | * sdio SDIO context pointer | |
587 | * --------------------------------------------------------------------------- | |
588 | */ | |
589 | void | |
590 | CsrSdioFunctionActive(CsrSdioFunction *function) | |
591 | { | |
592 | } /* CsrSdioFunctionActive() */ | |
593 | ||
594 | /* | |
595 | * --------------------------------------------------------------------------- | |
596 | * CsrSdioFunctionIdle | |
597 | * | |
598 | * Set the function as idle. | |
599 | * | |
600 | * Arguments: | |
601 | * sdio SDIO context pointer | |
602 | * --------------------------------------------------------------------------- | |
603 | */ | |
604 | void | |
605 | CsrSdioFunctionIdle(CsrSdioFunction *function) | |
606 | { | |
607 | } /* CsrSdioFunctionIdle() */ | |
608 | ||
609 | ||
610 | /* | |
611 | * --------------------------------------------------------------------------- | |
612 | * CsrSdioPowerOn | |
613 | * | |
614 | * Power on UniFi. | |
615 | * | |
616 | * Arguments: | |
617 | * sdio SDIO context pointer | |
618 | * --------------------------------------------------------------------------- | |
619 | */ | |
620 | CsrResult | |
621 | CsrSdioPowerOn(CsrSdioFunction *function) | |
622 | { | |
635d2b00 GKH |
623 | struct sdio_func *func = (struct sdio_func *)function->priv; |
624 | struct mmc_host *host = func->card->host; | |
625 | ||
626 | _sdio_claim_host(func); | |
627 | if (!card_is_powered) { | |
628 | mmc_power_restore_host(host); | |
629 | card_is_powered = 1; | |
630 | } else { | |
631 | printk(KERN_INFO "SDIO: Skip power on; card is already powered.\n"); | |
632 | } | |
633 | _sdio_release_host(func); | |
635d2b00 GKH |
634 | |
635 | return CSR_RESULT_SUCCESS; | |
636 | } /* CsrSdioPowerOn() */ | |
637 | ||
638 | /* | |
639 | * --------------------------------------------------------------------------- | |
640 | * CsrSdioPowerOff | |
641 | * | |
642 | * Power off UniFi. | |
643 | * | |
644 | * Arguments: | |
645 | * sdio SDIO context pointer | |
646 | * --------------------------------------------------------------------------- | |
647 | */ | |
648 | void | |
649 | CsrSdioPowerOff(CsrSdioFunction *function) | |
650 | { | |
635d2b00 GKH |
651 | struct sdio_func *func = (struct sdio_func *)function->priv; |
652 | struct mmc_host *host = func->card->host; | |
653 | ||
654 | _sdio_claim_host(func); | |
655 | if (card_is_powered) { | |
656 | mmc_power_save_host(host); | |
657 | card_is_powered = 0; | |
658 | } else { | |
659 | printk(KERN_INFO "SDIO: Skip power off; card is already powered off.\n"); | |
660 | } | |
661 | _sdio_release_host(func); | |
635d2b00 GKH |
662 | } /* CsrSdioPowerOff() */ |
663 | ||
664 | ||
665 | static int | |
666 | sdio_set_block_size_ignore_first_error(struct sdio_func *func, unsigned blksz) | |
667 | { | |
668 | int ret; | |
669 | ||
670 | if (blksz > func->card->host->max_blk_size) | |
671 | return -EINVAL; | |
672 | ||
673 | if (blksz == 0) { | |
674 | blksz = min(func->max_blksize, func->card->host->max_blk_size); | |
675 | blksz = min(blksz, 512u); | |
676 | } | |
677 | ||
678 | /* | |
679 | * Ignore -ERANGE (OUT_OF_RANGE in R5) on the first byte as | |
680 | * the block size may be invalid until both bytes are written. | |
681 | */ | |
682 | ret = csr_io_rw_direct(func->card, 1, 0, | |
683 | SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE, | |
684 | blksz & 0xff, NULL); | |
685 | if (ret && ret != -ERANGE) | |
686 | return ret; | |
687 | ret = csr_io_rw_direct(func->card, 1, 0, | |
688 | SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1, | |
689 | (blksz >> 8) & 0xff, NULL); | |
690 | if (ret) | |
691 | return ret; | |
692 | func->cur_blksize = blksz; | |
693 | ||
694 | return 0; | |
695 | } | |
696 | ||
697 | CsrResult | |
8c87f69a | 698 | CsrSdioBlockSizeSet(CsrSdioFunction *function, u16 blockSize) |
635d2b00 GKH |
699 | { |
700 | struct sdio_func *func = (struct sdio_func *)function->priv; | |
701 | int r = 0; | |
702 | ||
703 | /* Module parameter overrides */ | |
704 | if (sdio_block_size > -1) { | |
705 | blockSize = sdio_block_size; | |
706 | } | |
707 | ||
708 | unifi_trace(NULL, UDBG1, "Set SDIO function block size to %d\n", | |
709 | blockSize); | |
710 | ||
711 | _sdio_claim_host(func); | |
712 | r = sdio_set_block_size(func, blockSize); | |
713 | _sdio_release_host(func); | |
714 | ||
715 | /* | |
716 | * The MMC driver for kernels prior to 2.6.32 may fail this request | |
717 | * with -ERANGE. In this case use our workaround. | |
718 | */ | |
719 | if (r == -ERANGE) { | |
720 | _sdio_claim_host(func); | |
721 | r = sdio_set_block_size_ignore_first_error(func, blockSize); | |
722 | _sdio_release_host(func); | |
723 | } | |
724 | if (r) { | |
725 | unifi_error(NULL, "Error %d setting block size\n", r); | |
726 | } | |
727 | ||
728 | /* Determine the achieved block size to pass to the core */ | |
729 | function->blockSize = func->cur_blksize; | |
730 | ||
731 | return ConvertSdioToCsrSdioResult(r); | |
732 | } /* CsrSdioBlockSizeSet() */ | |
733 | ||
734 | ||
735 | /* | |
736 | * --------------------------------------------------------------------------- | |
737 | * CsrSdioHardReset | |
738 | * | |
739 | * Hard Resets UniFi is possible. | |
740 | * | |
741 | * Arguments: | |
742 | * sdio SDIO context pointer | |
743 | * --------------------------------------------------------------------------- | |
744 | */ | |
745 | CsrResult | |
746 | CsrSdioHardReset(CsrSdioFunction *function) | |
747 | { | |
748 | return CSR_RESULT_FAILURE; | |
749 | } /* CsrSdioHardReset() */ | |
750 | ||
751 | ||
752 | ||
753 | /* | |
754 | * --------------------------------------------------------------------------- | |
755 | * uf_glue_sdio_int_handler | |
756 | * | |
757 | * Interrupt callback function for SDIO interrupts. | |
758 | * This is called in kernel context (i.e. not interrupt context). | |
759 | * | |
760 | * Arguments: | |
761 | * func SDIO context pointer | |
762 | * | |
763 | * Returns: | |
764 | * None. | |
765 | * | |
766 | * Note: Called with host already claimed. | |
767 | * --------------------------------------------------------------------------- | |
768 | */ | |
769 | static void | |
770 | uf_glue_sdio_int_handler(struct sdio_func *func) | |
771 | { | |
772 | CsrSdioFunction *sdio_ctx; | |
773 | CsrSdioInterruptDsrCallback func_dsr_callback; | |
774 | int r; | |
775 | ||
776 | sdio_ctx = sdio_get_drvdata(func); | |
777 | if (!sdio_ctx) { | |
778 | return; | |
779 | } | |
95edd09e | 780 | |
635d2b00 GKH |
781 | #ifndef CSR_CONFIG_MMC_INT_BYPASS_KSOFTIRQD |
782 | /* | |
783 | * Normally, we are not allowed to do any SDIO commands here. | |
784 | * However, this is called in a thread context and with the SDIO lock | |
785 | * so we disable the interrupts here instead of trying to do complicated | |
786 | * things with the SDIO lock. | |
787 | */ | |
788 | #ifdef MMC_QUIRK_LENIENT_FN0 | |
789 | sdio_f0_writeb(func, 0, SDIO_CCCR_IENx, &r); | |
790 | #else | |
791 | r = csr_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, 0x00, NULL); | |
792 | #endif | |
793 | if (r) { | |
95edd09e | 794 | printk(KERN_ERR "UniFi MMC Int handler: Failed to disable interrupts %d\n", r); |
635d2b00 GKH |
795 | } |
796 | #endif | |
797 | ||
798 | /* If the function driver has registered a handler, call it */ | |
799 | if (sdio_func_drv && sdio_func_drv->intr) { | |
800 | ||
801 | func_dsr_callback = sdio_func_drv->intr(sdio_ctx); | |
802 | ||
803 | /* If interrupt handle returns a DSR handle, call it */ | |
804 | if (func_dsr_callback) { | |
805 | func_dsr_callback(sdio_ctx); | |
806 | } | |
807 | } | |
808 | ||
809 | } /* uf_glue_sdio_int_handler() */ | |
810 | ||
811 | ||
812 | ||
813 | /* | |
814 | * --------------------------------------------------------------------------- | |
815 | * csr_sdio_linux_remove_irq | |
816 | * | |
817 | * Unregister the interrupt handler. | |
818 | * This means that the linux layer can not process interrupts any more. | |
819 | * | |
820 | * Arguments: | |
821 | * sdio SDIO context pointer | |
822 | * | |
823 | * Returns: | |
824 | * Status of the removal. | |
825 | * --------------------------------------------------------------------------- | |
826 | */ | |
2dd58c9d | 827 | int csr_sdio_linux_remove_irq(CsrSdioFunction *function) |
635d2b00 | 828 | { |
2dd58c9d DN |
829 | struct sdio_func *func = (struct sdio_func *)function->priv; |
830 | int r; | |
635d2b00 | 831 | |
2dd58c9d | 832 | unifi_trace(NULL, UDBG1, "csr_sdio_linux_remove_irq\n"); |
95edd09e | 833 | |
2dd58c9d DN |
834 | sdio_claim_host(func); |
835 | r = sdio_release_irq(func); | |
836 | sdio_release_host(func); | |
635d2b00 | 837 | |
2dd58c9d | 838 | return r; |
635d2b00 GKH |
839 | |
840 | } /* csr_sdio_linux_remove_irq() */ | |
841 | ||
842 | ||
843 | /* | |
844 | * --------------------------------------------------------------------------- | |
845 | * csr_sdio_linux_install_irq | |
846 | * | |
847 | * Register the interrupt handler. | |
848 | * This means that the linux layer can process interrupts. | |
849 | * | |
850 | * Arguments: | |
851 | * sdio SDIO context pointer | |
852 | * | |
853 | * Returns: | |
854 | * Status of the removal. | |
855 | * --------------------------------------------------------------------------- | |
856 | */ | |
2dd58c9d | 857 | int csr_sdio_linux_install_irq(CsrSdioFunction *function) |
635d2b00 | 858 | { |
2dd58c9d DN |
859 | struct sdio_func *func = (struct sdio_func *)function->priv; |
860 | int r; | |
635d2b00 | 861 | |
2dd58c9d | 862 | unifi_trace(NULL, UDBG1, "csr_sdio_linux_install_irq\n"); |
95edd09e | 863 | |
2dd58c9d DN |
864 | /* Register our interrupt handle */ |
865 | sdio_claim_host(func); | |
866 | r = sdio_claim_irq(func, uf_glue_sdio_int_handler); | |
867 | sdio_release_host(func); | |
635d2b00 | 868 | |
2dd58c9d DN |
869 | /* If the interrupt was installed earlier, is fine */ |
870 | if (r == -EBUSY) | |
871 | r = 0; | |
635d2b00 | 872 | |
2dd58c9d | 873 | return r; |
635d2b00 GKH |
874 | } /* csr_sdio_linux_install_irq() */ |
875 | ||
95edd09e GKH |
876 | #ifdef CONFIG_PM |
877 | ||
878 | /* | |
879 | * Power Management notifier | |
880 | */ | |
881 | struct uf_sdio_mmc_pm_notifier | |
882 | { | |
883 | struct list_head list; | |
884 | ||
885 | CsrSdioFunction *sdio_ctx; | |
886 | struct notifier_block pm_notifier; | |
887 | }; | |
888 | ||
889 | /* PM notifier list head */ | |
890 | static struct uf_sdio_mmc_pm_notifier uf_sdio_mmc_pm_notifiers = { | |
891 | .sdio_ctx = NULL, | |
892 | }; | |
893 | ||
894 | /* | |
895 | * --------------------------------------------------------------------------- | |
896 | * uf_sdio_mmc_register_pm_notifier | |
897 | * uf_sdio_mmc_unregister_pm_notifier | |
898 | * | |
899 | * Register/unregister for power management events. A list is used to | |
900 | * allow multiple card instances to be supported. | |
901 | * | |
902 | * Arguments: | |
903 | * sdio_ctx - CSR SDIO context to associate PM notifier to | |
904 | * | |
905 | * Returns: | |
906 | * Register function returns NULL on error | |
907 | * --------------------------------------------------------------------------- | |
908 | */ | |
909 | static struct uf_sdio_mmc_pm_notifier * | |
910 | uf_sdio_mmc_register_pm_notifier(CsrSdioFunction *sdio_ctx) | |
911 | { | |
912 | /* Allocate notifier context for this card instance */ | |
913 | struct uf_sdio_mmc_pm_notifier *notifier_ctx = kmalloc(sizeof(struct uf_sdio_mmc_pm_notifier), GFP_KERNEL); | |
914 | ||
915 | if (notifier_ctx) | |
916 | { | |
917 | notifier_ctx->sdio_ctx = sdio_ctx; | |
918 | notifier_ctx->pm_notifier.notifier_call = uf_sdio_mmc_power_event; | |
919 | ||
920 | list_add(¬ifier_ctx->list, &uf_sdio_mmc_pm_notifiers.list); | |
921 | ||
922 | if (register_pm_notifier(¬ifier_ctx->pm_notifier)) { | |
923 | printk(KERN_ERR "unifi: register_pm_notifier failed\n"); | |
924 | } | |
925 | } | |
926 | ||
927 | return notifier_ctx; | |
928 | } | |
929 | ||
930 | static void | |
931 | uf_sdio_mmc_unregister_pm_notifier(CsrSdioFunction *sdio_ctx) | |
932 | { | |
933 | struct uf_sdio_mmc_pm_notifier *notifier_ctx; | |
934 | struct list_head *node, *q; | |
935 | ||
936 | list_for_each_safe(node, q, &uf_sdio_mmc_pm_notifiers.list) { | |
937 | notifier_ctx = list_entry(node, struct uf_sdio_mmc_pm_notifier, list); | |
938 | ||
939 | /* If it matches, unregister and free the notifier context */ | |
940 | if (notifier_ctx && notifier_ctx->sdio_ctx == sdio_ctx) | |
941 | { | |
942 | if (unregister_pm_notifier(¬ifier_ctx->pm_notifier)) { | |
943 | printk(KERN_ERR "unifi: unregister_pm_notifier failed\n"); | |
944 | } | |
945 | ||
946 | /* Remove from list */ | |
947 | notifier_ctx->sdio_ctx = NULL; | |
948 | list_del(node); | |
949 | kfree(notifier_ctx); | |
950 | } | |
951 | } | |
952 | } | |
953 | ||
954 | /* | |
955 | * --------------------------------------------------------------------------- | |
956 | * uf_sdio_mmc_power_event | |
957 | * | |
958 | * Handler for power management events. | |
959 | * | |
960 | * We need to handle suspend/resume events while the userspace is unsuspended | |
961 | * to allow the SME to run its suspend/resume state machines. | |
962 | * | |
963 | * Arguments: | |
964 | * event event ID | |
965 | * | |
966 | * Returns: | |
967 | * Status of the event handling | |
968 | * --------------------------------------------------------------------------- | |
969 | */ | |
970 | static int | |
971 | uf_sdio_mmc_power_event(struct notifier_block *this, unsigned long event, void *ptr) | |
972 | { | |
973 | struct uf_sdio_mmc_pm_notifier *notifier_ctx = container_of(this, | |
974 | struct uf_sdio_mmc_pm_notifier, | |
975 | pm_notifier); | |
976 | ||
977 | /* Call the CSR SDIO function driver's suspend/resume method | |
978 | * while the userspace is unsuspended. | |
979 | */ | |
980 | switch (event) { | |
981 | case PM_POST_HIBERNATION: | |
982 | case PM_POST_SUSPEND: | |
983 | printk(KERN_INFO "%s:%d resume\n", __FUNCTION__, __LINE__ ); | |
984 | if (sdio_func_drv && sdio_func_drv->resume) { | |
985 | sdio_func_drv->resume(notifier_ctx->sdio_ctx); | |
986 | } | |
987 | break; | |
988 | ||
989 | case PM_HIBERNATION_PREPARE: | |
990 | case PM_SUSPEND_PREPARE: | |
991 | printk(KERN_INFO "%s:%d suspend\n", __FUNCTION__, __LINE__ ); | |
992 | if (sdio_func_drv && sdio_func_drv->suspend) { | |
993 | sdio_func_drv->suspend(notifier_ctx->sdio_ctx); | |
994 | } | |
995 | break; | |
996 | } | |
997 | return NOTIFY_DONE; | |
998 | } | |
635d2b00 | 999 | |
95edd09e | 1000 | #endif /* CONFIG_PM */ |
635d2b00 GKH |
1001 | |
1002 | /* | |
1003 | * --------------------------------------------------------------------------- | |
1004 | * uf_glue_sdio_probe | |
1005 | * | |
1006 | * Card insert callback. | |
1007 | * | |
1008 | * Arguments: | |
1009 | * func Our (glue layer) context pointer. | |
1010 | * | |
1011 | * Returns: | |
1012 | * UniFi driver error code. | |
1013 | * --------------------------------------------------------------------------- | |
1014 | */ | |
1015 | static int | |
1016 | uf_glue_sdio_probe(struct sdio_func *func, | |
1017 | const struct sdio_device_id *id) | |
1018 | { | |
1019 | int instance; | |
1020 | CsrSdioFunction *sdio_ctx; | |
1021 | ||
635d2b00 GKH |
1022 | /* First of all claim the SDIO driver */ |
1023 | sdio_claim_host(func); | |
1024 | ||
635d2b00 GKH |
1025 | /* Assume that the card is already powered */ |
1026 | card_is_powered = 1; | |
635d2b00 GKH |
1027 | |
1028 | /* Assumes one card per host, which is true for SDIO */ | |
1029 | instance = func->card->host->index; | |
1030 | printk("sdio bus_id: %16s - UniFi card 0x%X inserted\n", | |
1031 | sdio_func_id(func), instance); | |
1032 | ||
1033 | /* Allocate context */ | |
1034 | sdio_ctx = (CsrSdioFunction *)kmalloc(sizeof(CsrSdioFunction), | |
1035 | GFP_KERNEL); | |
1036 | if (sdio_ctx == NULL) { | |
1037 | sdio_release_host(func); | |
1038 | return -ENOMEM; | |
1039 | } | |
1040 | ||
1041 | /* Initialise the context */ | |
1042 | sdio_ctx->sdioId.manfId = func->vendor; | |
1043 | sdio_ctx->sdioId.cardId = func->device; | |
1044 | sdio_ctx->sdioId.sdioFunction = func->num; | |
1045 | sdio_ctx->sdioId.sdioInterface = func->class; | |
1046 | sdio_ctx->blockSize = func->cur_blksize; | |
1047 | sdio_ctx->priv = (void *)func; | |
1048 | sdio_ctx->features = 0; | |
1049 | ||
1050 | /* Module parameter enables byte mode */ | |
1051 | if (sdio_byte_mode) { | |
1052 | sdio_ctx->features |= CSR_SDIO_FEATURE_BYTE_MODE; | |
1053 | } | |
1054 | ||
95edd09e GKH |
1055 | if (func->card->host->caps & MMC_CAP_SD_HIGHSPEED) { |
1056 | unifi_trace(NULL, UDBG1, "MMC_CAP_SD_HIGHSPEED is available\n"); | |
1057 | } | |
1058 | ||
635d2b00 GKH |
1059 | #ifdef MMC_QUIRK_LENIENT_FN0 |
1060 | func->card->quirks |= MMC_QUIRK_LENIENT_FN0; | |
1061 | #endif | |
1062 | ||
1063 | /* Pass context to the SDIO driver */ | |
1064 | sdio_set_drvdata(func, sdio_ctx); | |
1065 | ||
95edd09e GKH |
1066 | #ifdef CONFIG_PM |
1067 | /* Register to get PM events */ | |
1068 | if (uf_sdio_mmc_register_pm_notifier(sdio_ctx) == NULL) { | |
1069 | unifi_error(NULL, "%s: Failed to register for PM events\n", __FUNCTION__); | |
1070 | } | |
95edd09e GKH |
1071 | #endif |
1072 | ||
635d2b00 GKH |
1073 | /* Register this device with the SDIO function driver */ |
1074 | /* Call the main UniFi driver inserted handler */ | |
1075 | if (sdio_func_drv && sdio_func_drv->inserted) { | |
1076 | uf_add_os_device(instance, &func->dev); | |
1077 | sdio_func_drv->inserted(sdio_ctx); | |
1078 | } | |
1079 | ||
1080 | /* We have finished, so release the SDIO driver */ | |
1081 | sdio_release_host(func); | |
1082 | ||
95edd09e GKH |
1083 | #ifdef ANDROID_BUILD |
1084 | /* Take the wakelock */ | |
1085 | unifi_trace(NULL, UDBG1, "probe: take wake lock\n"); | |
1086 | wake_lock(&unifi_sdio_wake_lock); | |
1087 | #endif | |
1088 | ||
635d2b00 GKH |
1089 | return 0; |
1090 | } /* uf_glue_sdio_probe() */ | |
1091 | ||
1092 | ||
1093 | /* | |
1094 | * --------------------------------------------------------------------------- | |
1095 | * uf_glue_sdio_remove | |
1096 | * | |
1097 | * Card removal callback. | |
1098 | * | |
1099 | * Arguments: | |
1100 | * func Our (glue layer) context pointer. | |
1101 | * | |
1102 | * Returns: | |
1103 | * UniFi driver error code. | |
1104 | * --------------------------------------------------------------------------- | |
1105 | */ | |
1106 | static void | |
1107 | uf_glue_sdio_remove(struct sdio_func *func) | |
1108 | { | |
1109 | CsrSdioFunction *sdio_ctx; | |
1110 | ||
1111 | sdio_ctx = sdio_get_drvdata(func); | |
1112 | if (!sdio_ctx) { | |
1113 | return; | |
1114 | } | |
1115 | ||
635d2b00 GKH |
1116 | unifi_info(NULL, "UniFi card removed\n"); |
1117 | ||
1118 | /* Clean up the SDIO function driver */ | |
1119 | if (sdio_func_drv && sdio_func_drv->removed) { | |
1120 | uf_remove_os_device(func->card->host->index); | |
1121 | sdio_func_drv->removed(sdio_ctx); | |
1122 | } | |
1123 | ||
95edd09e GKH |
1124 | #ifdef CONFIG_PM |
1125 | /* Unregister for PM events */ | |
1126 | uf_sdio_mmc_unregister_pm_notifier(sdio_ctx); | |
95edd09e GKH |
1127 | #endif |
1128 | ||
635d2b00 GKH |
1129 | kfree(sdio_ctx); |
1130 | ||
635d2b00 GKH |
1131 | } /* uf_glue_sdio_remove */ |
1132 | ||
1133 | ||
1134 | /* | |
1135 | * SDIO ids *must* be statically declared, so we can't take | |
1136 | * them from the list passed in csr_sdio_register_driver(). | |
1137 | */ | |
1138 | static const struct sdio_device_id unifi_ids[] = { | |
1139 | { SDIO_DEVICE(SDIO_MANF_ID_CSR,SDIO_CARD_ID_UNIFI_3) }, | |
1140 | { SDIO_DEVICE(SDIO_MANF_ID_CSR,SDIO_CARD_ID_UNIFI_4) }, | |
1141 | { /* end: all zeroes */ }, | |
1142 | }; | |
1143 | ||
1144 | MODULE_DEVICE_TABLE(sdio, unifi_ids); | |
1145 | ||
635d2b00 GKH |
1146 | #ifdef CONFIG_PM |
1147 | ||
1148 | /* | |
1149 | * --------------------------------------------------------------------------- | |
1150 | * uf_glue_sdio_suspend | |
1151 | * | |
95edd09e | 1152 | * Card suspend callback. The userspace will already be suspended. |
635d2b00 GKH |
1153 | * |
1154 | * Arguments: | |
1155 | * dev The struct device owned by the MMC driver | |
1156 | * | |
1157 | * Returns: | |
1158 | * None | |
1159 | * --------------------------------------------------------------------------- | |
1160 | */ | |
1161 | static int | |
1162 | uf_glue_sdio_suspend(struct device *dev) | |
1163 | { | |
95edd09e | 1164 | unifi_trace(NULL, UDBG1, "uf_glue_sdio_suspend"); |
635d2b00 | 1165 | |
635d2b00 GKH |
1166 | return 0; |
1167 | } /* uf_glue_sdio_suspend */ | |
1168 | ||
1169 | ||
1170 | /* | |
1171 | * --------------------------------------------------------------------------- | |
1172 | * uf_glue_sdio_resume | |
1173 | * | |
95edd09e | 1174 | * Card resume callback. The userspace will still be suspended. |
635d2b00 GKH |
1175 | * |
1176 | * Arguments: | |
1177 | * dev The struct device owned by the MMC driver | |
1178 | * | |
1179 | * Returns: | |
1180 | * None | |
1181 | * --------------------------------------------------------------------------- | |
1182 | */ | |
1183 | static int | |
1184 | uf_glue_sdio_resume(struct device *dev) | |
1185 | { | |
95edd09e | 1186 | unifi_trace(NULL, UDBG1, "uf_glue_sdio_resume"); |
635d2b00 | 1187 | |
95edd09e GKH |
1188 | #ifdef ANDROID_BUILD |
1189 | unifi_trace(NULL, UDBG1, "resume: take wakelock\n"); | |
1190 | wake_lock(&unifi_sdio_wake_lock); | |
1191 | #endif | |
635d2b00 | 1192 | |
635d2b00 GKH |
1193 | return 0; |
1194 | ||
1195 | } /* uf_glue_sdio_resume */ | |
1196 | ||
1197 | static struct dev_pm_ops unifi_pm_ops = { | |
1198 | .suspend = uf_glue_sdio_suspend, | |
1199 | .resume = uf_glue_sdio_resume, | |
1200 | }; | |
1201 | ||
1202 | #define UNIFI_PM_OPS (&unifi_pm_ops) | |
1203 | ||
1204 | #else | |
1205 | ||
1206 | #define UNIFI_PM_OPS NULL | |
1207 | ||
1208 | #endif /* CONFIG_PM */ | |
635d2b00 GKH |
1209 | |
1210 | static struct sdio_driver unifi_driver = { | |
1211 | .probe = uf_glue_sdio_probe, | |
1212 | .remove = uf_glue_sdio_remove, | |
1213 | .name = "unifi", | |
1214 | .id_table = unifi_ids, | |
635d2b00 | 1215 | .drv.pm = UNIFI_PM_OPS, |
635d2b00 GKH |
1216 | }; |
1217 | ||
1218 | ||
1219 | /* | |
1220 | * --------------------------------------------------------------------------- | |
1221 | * CsrSdioFunctionDriverRegister | |
1222 | * CsrSdioFunctionDriverUnregister | |
1223 | * | |
1224 | * These functions are called from the main module load and unload | |
1225 | * functions. They perform the appropriate operations for the | |
1226 | * linux MMC/SDIO driver. | |
1227 | * | |
1228 | * Arguments: | |
1229 | * sdio_drv Pointer to the function driver's SDIO structure. | |
1230 | * | |
1231 | * Returns: | |
1232 | * None. | |
1233 | * --------------------------------------------------------------------------- | |
1234 | */ | |
1235 | CsrResult | |
1236 | CsrSdioFunctionDriverRegister(CsrSdioFunctionDriver *sdio_drv) | |
1237 | { | |
1238 | int r; | |
1239 | ||
1240 | printk("UniFi: Using native Linux MMC driver for SDIO.\n"); | |
1241 | ||
1242 | if (sdio_func_drv) { | |
1243 | unifi_error(NULL, "sdio_mmc: UniFi driver already registered\n"); | |
1244 | return CSR_SDIO_RESULT_INVALID_VALUE; | |
1245 | } | |
1246 | ||
95edd09e GKH |
1247 | #ifdef ANDROID_BUILD |
1248 | wake_lock_init(&unifi_sdio_wake_lock, WAKE_LOCK_SUSPEND, "unifi_sdio_work"); | |
1249 | #endif | |
1250 | ||
635d2b00 GKH |
1251 | /* Save the registered driver description */ |
1252 | /* | |
1253 | * FIXME: | |
1254 | * Need a table here to handle a call to register for just one function. | |
1255 | * mmc only allows us to register for the whole device | |
1256 | */ | |
1257 | sdio_func_drv = sdio_drv; | |
1258 | ||
95edd09e GKH |
1259 | #ifdef CONFIG_PM |
1260 | /* Initialise PM notifier list */ | |
1261 | INIT_LIST_HEAD(&uf_sdio_mmc_pm_notifiers.list); | |
95edd09e GKH |
1262 | #endif |
1263 | ||
635d2b00 GKH |
1264 | /* Register ourself with mmc_core */ |
1265 | r = sdio_register_driver(&unifi_driver); | |
1266 | if (r) { | |
1267 | printk(KERN_ERR "unifi_sdio: Failed to register UniFi SDIO driver: %d\n", r); | |
1268 | return ConvertSdioToCsrSdioResult(r); | |
1269 | } | |
1270 | ||
1271 | return CSR_RESULT_SUCCESS; | |
1272 | } /* CsrSdioFunctionDriverRegister() */ | |
1273 | ||
1274 | ||
1275 | ||
1276 | void | |
1277 | CsrSdioFunctionDriverUnregister(CsrSdioFunctionDriver *sdio_drv) | |
1278 | { | |
1279 | printk(KERN_INFO "UniFi: unregister from MMC sdio\n"); | |
95edd09e GKH |
1280 | |
1281 | #ifdef ANDROID_BUILD | |
1282 | wake_lock_destroy(&unifi_sdio_wake_lock); | |
1283 | #endif | |
635d2b00 GKH |
1284 | sdio_unregister_driver(&unifi_driver); |
1285 | ||
1286 | sdio_func_drv = NULL; | |
1287 | ||
1288 | } /* CsrSdioFunctionDriverUnregister() */ | |
1289 |