blob: 99385596a65f491c40954d8208cd4a1f622b6fac [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/libra_sdioif.h>
15#include <linux/delay.h>
16#include <linux/mmc/sdio.h>
17#include <linux/mmc/mmc.h>
18#include <linux/mmc/host.h>
19#include <linux/mmc/card.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070020#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070021
22/* Libra SDIO function device */
23static struct sdio_func *libra_sdio_func;
24static struct mmc_host *libra_mmc_host;
25static int libra_mmc_host_index;
26
27/* SDIO Card ID / Device ID */
28static unsigned short libra_sdio_card_id;
29
Deepthi Gowrib968a7c2012-08-29 15:09:08 +053030/* completion variables */
31struct completion gCard_rem_event_var;
32EXPORT_SYMBOL(gCard_rem_event_var);
33struct completion gShutdown_event_var;
34EXPORT_SYMBOL(gShutdown_event_var);
35
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036static suspend_handler_t *libra_suspend_hldr;
37static resume_handler_t *libra_resume_hldr;
Pavan Kumar89f18c32012-03-21 14:55:22 +053038static notify_card_removal_t *libra_notify_card_removal_hdlr;
Pavan Kumarc2ee83a2012-03-30 13:39:35 +053039static shutdown_handler_t *libra_sdio_shutdown_hdlr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070040
Pavan Kumar8832b232012-03-05 19:32:29 +053041int libra_enable_sdio_irq_in_chip(struct sdio_func *func, u8 enable)
42{
43 unsigned char reg = 0;
44 int err = 0;
45
46 sdio_claim_host(func);
47
48 /* Read the value into reg */
49 libra_sdiocmd52(func, SDIO_CCCR_IENx, &reg, 0, &err);
50 if (err)
51 printk(KERN_ERR "%s: Could not read SDIO_CCCR_IENx register "
52 "err=%d\n", __func__, err);
53
54 if (libra_mmc_host) {
55 if (enable) {
56 reg |= 1 << func->num;
57 reg |= 1;
58 } else {
59 reg &= ~(1 << func->num);
60 }
61 libra_sdiocmd52(func, SDIO_CCCR_IENx, &reg, 1, &err);
62 if (err)
63 printk(KERN_ERR "%s: Could not enable/disable irq "
64 "err=%d\n", __func__, err);
65 }
66 sdio_release_host(func);
67
68 return err;
69}
70EXPORT_SYMBOL(libra_enable_sdio_irq_in_chip);
71
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072/**
73 * libra_sdio_configure() - Function to configure the SDIO device param
74 * @libra_sdio_rxhandler Rx handler
75 * @func_drv_fn Function driver function for special setup
76 * @funcdrv_timeout Function Enable timeout
77 * @blksize Block size
78 *
79 * Configure SDIO device, enable function and set block size
80 */
81int libra_sdio_configure(sdio_irq_handler_t libra_sdio_rxhandler,
82 void (*func_drv_fn)(int *status),
83 unsigned int funcdrv_timeout, unsigned int blksize)
84{
85 int err_ret = 0;
86 struct sdio_func *func = libra_sdio_func;
87
88 if (libra_sdio_func == NULL) {
89 printk(KERN_ERR "%s: Error SDIO card not detected\n", __func__);
90 goto cfg_error;
91 }
92
93 sdio_claim_host(func);
94
95 /* Currently block sizes are set here. */
96 func->max_blksize = blksize;
97 if (sdio_set_block_size(func, blksize)) {
98 printk(KERN_ERR "%s: Unable to set the block size.\n",
99 __func__);
100 sdio_release_host(func);
101 goto cfg_error;
102 }
103
104 /* Function driver specific configuration. */
105 if (func_drv_fn) {
106 (*func_drv_fn)(&err_ret);
107 if (err_ret) {
108 printk(KERN_ERR "%s: function driver provided configure function error=%d\n",
109 __func__, err_ret);
110 sdio_release_host(func);
111 goto cfg_error;
112 }
113 }
114
115 /* We set this based on the function card. */
116 func->enable_timeout = funcdrv_timeout;
117 err_ret = sdio_enable_func(func);
118 if (err_ret != 0) {
119 printk(KERN_ERR "%s: Unable to enable function %d\n",
120 __func__, err_ret);
121 sdio_release_host(func);
122 goto cfg_error;
123 }
124
125 if (sdio_claim_irq(func, libra_sdio_rxhandler)) {
126 sdio_disable_func(func);
127 printk(KERN_ERR "%s: Unable to claim irq.\n", __func__);
128 sdio_release_host(func);
129 goto cfg_error;
130 }
131
Pavan Kumar8832b232012-03-05 19:32:29 +0530132 libra_enable_sdio_irq_in_chip(func, 0);
133
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134 sdio_release_host(func);
135
136 return 0;
137
138cfg_error:
139 return -1;
140
141}
142EXPORT_SYMBOL(libra_sdio_configure);
143
144int libra_sdio_configure_suspend_resume(
145 suspend_handler_t *libra_sdio_suspend_hdlr,
146 resume_handler_t *libra_sdio_resume_hdlr)
147{
148 libra_suspend_hldr = libra_sdio_suspend_hdlr;
149 libra_resume_hldr = libra_sdio_resume_hdlr;
150 return 0;
151}
152EXPORT_SYMBOL(libra_sdio_configure_suspend_resume);
153
154/*
155 * libra_sdio_deconfigure() - Function to reset the SDIO device param
156 */
157void libra_sdio_deconfigure(struct sdio_func *func)
158{
159 if (NULL == libra_sdio_func)
160 return;
161
162 sdio_claim_host(func);
163 sdio_release_irq(func);
164 sdio_disable_func(func);
165 sdio_release_host(func);
166}
167EXPORT_SYMBOL(libra_sdio_deconfigure);
168
169int libra_enable_sdio_irq(struct sdio_func *func, u8 enable)
170{
171 if (libra_mmc_host && libra_mmc_host->ops &&
172 libra_mmc_host->ops->enable_sdio_irq) {
173 libra_mmc_host->ops->enable_sdio_irq(libra_mmc_host, enable);
174 return 0;
175 }
176
177 printk(KERN_ERR "%s: Could not enable disable irq\n", __func__);
178 return -EINVAL;
179}
180EXPORT_SYMBOL(libra_enable_sdio_irq);
181
182int libra_disable_sdio_irq_capability(struct sdio_func *func, u8 disable)
183{
184 if (libra_mmc_host) {
185 if (disable)
186 libra_mmc_host->caps &= ~MMC_CAP_SDIO_IRQ;
187 else
188 libra_mmc_host->caps |= MMC_CAP_SDIO_IRQ;
189 return 0;
190 }
191 printk(KERN_ERR "%s: Could not change sdio capabilities to polling\n",
192 __func__);
193 return -EINVAL;
194}
195EXPORT_SYMBOL(libra_disable_sdio_irq_capability);
196
197/*
198 * libra_sdio_release_irq() - Function to release IRQ
199 */
200void libra_sdio_release_irq(struct sdio_func *func)
201{
202 if (NULL == libra_sdio_func)
203 return;
204
205 sdio_release_irq(func);
206}
207EXPORT_SYMBOL(libra_sdio_release_irq);
208
209/*
210 * libra_sdio_disable_func() - Function to disable sdio func
211 */
212void libra_sdio_disable_func(struct sdio_func *func)
213{
214 if (NULL == libra_sdio_func)
215 return;
216
217 sdio_disable_func(func);
218}
219EXPORT_SYMBOL(libra_sdio_disable_func);
220
221/*
222 * Return the SDIO Function device
223 */
224struct sdio_func *libra_getsdio_funcdev(void)
225{
226 return libra_sdio_func;
227}
228EXPORT_SYMBOL(libra_getsdio_funcdev);
229
230/*
231 * Set function driver as the private data for the function device
232 */
233void libra_sdio_setprivdata(struct sdio_func *sdio_func_dev,
234 void *padapter)
235{
236 if (NULL == libra_sdio_func)
237 return;
238
239 sdio_set_drvdata(sdio_func_dev, padapter);
240}
241EXPORT_SYMBOL(libra_sdio_setprivdata);
242
243/*
244 * Return private data of the function device.
245 */
246void *libra_sdio_getprivdata(struct sdio_func *sdio_func_dev)
247{
248 return sdio_get_drvdata(sdio_func_dev);
249}
250EXPORT_SYMBOL(libra_sdio_getprivdata);
251
252/*
253 * Function driver claims the SDIO device
254 */
255void libra_claim_host(struct sdio_func *sdio_func_dev,
256 pid_t *curr_claimed, pid_t current_pid, atomic_t *claim_count)
257{
258 if (NULL == libra_sdio_func)
259 return;
260
261 if (*curr_claimed == current_pid) {
262 atomic_inc(claim_count);
263 return;
264 }
265
266 /* Go ahead and claim the host if not locked by anybody. */
267 sdio_claim_host(sdio_func_dev);
268
269 *curr_claimed = current_pid;
270 atomic_inc(claim_count);
271
272}
273EXPORT_SYMBOL(libra_claim_host);
274
275/*
276 * Function driver releases the SDIO device
277 */
278void libra_release_host(struct sdio_func *sdio_func_dev,
279 pid_t *curr_claimed, pid_t current_pid, atomic_t *claim_count)
280{
281
282 if (NULL == libra_sdio_func)
283 return;
284
285 if (*curr_claimed != current_pid) {
286 /* Dont release */
287 return;
288 }
289
290 atomic_dec(claim_count);
291 if (atomic_read(claim_count) == 0) {
292 *curr_claimed = 0;
293 sdio_release_host(sdio_func_dev);
294 }
295}
296EXPORT_SYMBOL(libra_release_host);
297
298void libra_sdiocmd52(struct sdio_func *sdio_func_dev, unsigned int addr,
299 u8 *byte_var, int write, int *err_ret)
300{
301 if (write)
302 sdio_writeb(sdio_func_dev, byte_var[0], addr, err_ret);
303 else
304 byte_var[0] = sdio_readb(sdio_func_dev, addr, err_ret);
305}
306EXPORT_SYMBOL(libra_sdiocmd52);
307
308u8 libra_sdio_readsb(struct sdio_func *func, void *dst,
309 unsigned int addr, int count)
310{
311 return sdio_readsb(func, dst, addr, count);
312}
313EXPORT_SYMBOL(libra_sdio_readsb);
314
315int libra_sdio_memcpy_fromio(struct sdio_func *func,
316 void *dst, unsigned int addr, int count)
317{
318 return sdio_memcpy_fromio(func, dst, addr, count);
319}
320EXPORT_SYMBOL(libra_sdio_memcpy_fromio);
321
322int libra_sdio_writesb(struct sdio_func *func,
323 unsigned int addr, void *src, int count)
324{
325 return sdio_writesb(func, addr, src, count);
326}
327EXPORT_SYMBOL(libra_sdio_writesb);
328
329int libra_sdio_memcpy_toio(struct sdio_func *func,
330 unsigned int addr, void *src, int count)
331{
332 return sdio_memcpy_toio(func, addr, src, count);
333}
334EXPORT_SYMBOL(libra_sdio_memcpy_toio);
335
336int libra_detect_card_change(void)
337{
338 if (libra_mmc_host) {
339 if (!strcmp(libra_mmc_host->class_dev.class->name, "mmc_host")
340 && (libra_mmc_host_index == libra_mmc_host->index)) {
341 mmc_detect_change(libra_mmc_host, 0);
342 return 0;
343 }
344 }
345
346 printk(KERN_ERR "%s: Could not trigger card change\n", __func__);
347 return -EINVAL;
348}
349EXPORT_SYMBOL(libra_detect_card_change);
350
351int libra_sdio_enable_polling(void)
352{
353 if (libra_mmc_host) {
354 if (!strcmp(libra_mmc_host->class_dev.class->name, "mmc_host")
355 && (libra_mmc_host_index == libra_mmc_host->index)) {
356 libra_mmc_host->caps |= MMC_CAP_NEEDS_POLL;
357 mmc_detect_change(libra_mmc_host, 0);
358 return 0;
359 }
360 }
361
362 printk(KERN_ERR "%s: Could not trigger SDIO scan\n", __func__);
363 return -1;
364}
365EXPORT_SYMBOL(libra_sdio_enable_polling);
366
367void libra_sdio_set_clock(struct sdio_func *func, unsigned int clk_freq)
368{
369 struct mmc_host *host = func->card->host;
370 host->ios.clock = clk_freq;
371 host->ops->set_ios(host, &host->ios);
372
373}
374EXPORT_SYMBOL(libra_sdio_set_clock);
375
376/*
377 * API to get SDIO Device Card ID
378 */
379void libra_sdio_get_card_id(struct sdio_func *func, unsigned short *card_id)
380{
381 if (card_id)
382 *card_id = libra_sdio_card_id;
383}
384EXPORT_SYMBOL(libra_sdio_get_card_id);
385
386/*
387 * SDIO Probe
388 */
389static int libra_sdio_probe(struct sdio_func *func,
390 const struct sdio_device_id *sdio_dev_id)
391{
392 libra_mmc_host = func->card->host;
393 libra_mmc_host_index = libra_mmc_host->index;
394 libra_sdio_func = func;
395 libra_sdio_card_id = sdio_dev_id->device;
396
397 printk(KERN_INFO "%s: success with block size of %d device_id=0x%x\n",
398 __func__,
399 func->cur_blksize,
400 sdio_dev_id->device);
401
402 /* Turn off SDIO polling from now on */
403 libra_mmc_host->caps &= ~MMC_CAP_NEEDS_POLL;
404 return 0;
405}
406
407static void libra_sdio_remove(struct sdio_func *func)
408{
Pavan Kumar89f18c32012-03-21 14:55:22 +0530409 if (libra_notify_card_removal_hdlr)
410 libra_notify_card_removal_hdlr();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411 libra_sdio_func = NULL;
412
413 printk(KERN_INFO "%s : Module removed.\n", __func__);
414}
415
416#ifdef CONFIG_PM
417static int libra_sdio_suspend(struct device *dev)
418{
419 struct sdio_func *func = dev_to_sdio_func(dev);
420 int ret = 0;
421
422 ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
423
424 if (ret) {
425 printk(KERN_ERR "%s: Error Host doesn't support the keep power capability\n" ,
426 __func__);
427 return ret;
428 }
429 if (libra_suspend_hldr) {
430 /* Disable SDIO IRQ when driver is being suspended */
431 libra_enable_sdio_irq(func, 0);
432 ret = libra_suspend_hldr(func);
433 if (ret) {
434 printk(KERN_ERR
435 "%s: Libra driver is not able to suspend\n" , __func__);
436 /* Error - Restore SDIO IRQ */
437 libra_enable_sdio_irq(func, 1);
438 return ret;
439 }
440 }
441
442
443 return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
444}
445
446static int libra_sdio_resume(struct device *dev)
447{
448 struct sdio_func *func = dev_to_sdio_func(dev);
449
450 if (libra_resume_hldr) {
451 libra_resume_hldr(func);
452 /* Restore SDIO IRQ */
453 libra_enable_sdio_irq(func, 1);
454 }
455
456 return 0;
457}
458#else
459#define libra_sdio_suspend 0
460#define libra_sdio_resume 0
461#endif
462
Pavan Kumarc2ee83a2012-03-30 13:39:35 +0530463static void libra_sdio_shutdown(struct device *dev)
464{
465 if (libra_sdio_shutdown_hdlr) {
466 libra_sdio_shutdown_hdlr();
467 printk(KERN_INFO "%s : Notified shutdown event to Libra driver.\n",
468 __func__);
469 }
470}
471
472int libra_sdio_register_shutdown_hdlr(
473 shutdown_handler_t *libra_shutdown_hdlr)
474{
475 libra_sdio_shutdown_hdlr = libra_shutdown_hdlr;
476 return 0;
477}
478EXPORT_SYMBOL(libra_sdio_register_shutdown_hdlr);
479
Pavan Kumar89f18c32012-03-21 14:55:22 +0530480int libra_sdio_notify_card_removal(
481 notify_card_removal_t *libra_sdio_notify_card_removal_hdlr)
482{
483 libra_notify_card_removal_hdlr = libra_sdio_notify_card_removal_hdlr;
484 return 0;
485}
486EXPORT_SYMBOL(libra_sdio_notify_card_removal);
487
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700488static struct sdio_device_id libra_sdioid[] = {
489 {.class = 0, .vendor = LIBRA_MAN_ID, .device = LIBRA_REV_1_0_CARD_ID},
490 {.class = 0, .vendor = VOLANS_MAN_ID, .device = VOLANS_REV_2_0_CARD_ID},
491 {}
492};
493
494static const struct dev_pm_ops libra_sdio_pm_ops = {
495 .suspend = libra_sdio_suspend,
496 .resume = libra_sdio_resume,
497};
498
499static struct sdio_driver libra_sdiofn_driver = {
Pavan Kumarc2ee83a2012-03-30 13:39:35 +0530500 .name = "libra_sdiofn",
501 .id_table = libra_sdioid,
502 .probe = libra_sdio_probe,
503 .remove = libra_sdio_remove,
504 .drv.pm = &libra_sdio_pm_ops,
505 .drv.shutdown = libra_sdio_shutdown,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700506};
507
508static int __init libra_sdioif_init(void)
509{
510 libra_sdio_func = NULL;
511 libra_mmc_host = NULL;
512 libra_mmc_host_index = -1;
513 libra_suspend_hldr = NULL;
514 libra_resume_hldr = NULL;
Pavan Kumar89f18c32012-03-21 14:55:22 +0530515 libra_notify_card_removal_hdlr = NULL;
Pavan Kumarc2ee83a2012-03-30 13:39:35 +0530516 libra_sdio_shutdown_hdlr = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700517
518 sdio_register_driver(&libra_sdiofn_driver);
519
520 printk(KERN_INFO "%s: Loaded Successfully\n", __func__);
521
522 return 0;
523}
524
525static void __exit libra_sdioif_exit(void)
526{
527 unsigned int attempts = 0;
528
529 if (!libra_detect_card_change()) {
530 do {
531 ++attempts;
532 msleep(500);
533 } while (libra_sdio_func != NULL && attempts < 3);
534 }
535
536 if (libra_sdio_func != NULL)
537 printk(KERN_ERR "%s: Card removal not detected\n", __func__);
538
539 sdio_unregister_driver(&libra_sdiofn_driver);
540
541 libra_sdio_func = NULL;
542 libra_mmc_host = NULL;
543 libra_mmc_host_index = -1;
544
545 printk(KERN_INFO "%s: Unloaded Successfully\n", __func__);
546}
547
548module_init(libra_sdioif_init);
549module_exit(libra_sdioif_exit);
550
551MODULE_LICENSE("GPL v2");
552MODULE_VERSION("1.0");
553MODULE_DESCRIPTION("WLAN SDIODriver");