aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c')
-rw-r--r--drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c350
1 files changed, 350 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c
new file mode 100644
index 00000000000..1a370862b33
--- /dev/null
+++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c
@@ -0,0 +1,350 @@
1/*
2 * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
3 *
4 * Copyright (C) 1999-2011, Broadcom Corporation
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 * $Id: bcmsdh_sdmmc_linux.c,v 1.8.6.2 2011-02-01 18:38:36 Exp $
25 */
26
27#include <typedefs.h>
28#include <bcmutils.h>
29#include <sdio.h> /* SDIO Device and Protocol Specs */
30#include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
31#include <sdiovar.h> /* to get msglevel bit values */
32
33#include <linux/sched.h> /* request_irq() */
34
35#include <linux/mmc/core.h>
36#include <linux/mmc/card.h>
37#include <linux/mmc/sdio_func.h>
38#include <linux/mmc/sdio_ids.h>
39
40#if !defined(SDIO_VENDOR_ID_BROADCOM)
41#define SDIO_VENDOR_ID_BROADCOM 0x02d0
42#endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */
43
44#define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000
45
46#if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)
47#define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB 0x0492 /* BCM94325SDGWB */
48#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */
49#if !defined(SDIO_DEVICE_ID_BROADCOM_4325)
50#define SDIO_DEVICE_ID_BROADCOM_4325 0x0493
51#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */
52#if !defined(SDIO_DEVICE_ID_BROADCOM_4319)
53#define SDIO_DEVICE_ID_BROADCOM_4319 0x4319
54#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4319) */
55#if !defined(SDIO_DEVICE_ID_BROADCOM_4330)
56#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330
57#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4330) */
58
59#include <bcmsdh_sdmmc.h>
60
61#include <dhd_dbg.h>
62
63#ifdef WL_CFG80211
64extern void wl_cfg80211_set_sdio_func(void *func);
65#endif
66
67extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd);
68extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd);
69extern int dhd_os_check_wakelock(void *dhdp);
70extern int dhd_os_check_if_up(void *dhdp);
71extern void *bcmsdh_get_drvdata(void);
72
73int sdio_function_init(void);
74void sdio_function_cleanup(void);
75
76#define DESCRIPTION "bcmsdh_sdmmc Driver"
77#define AUTHOR "Broadcom Corporation"
78
79/* module param defaults */
80static int clockoverride = 0;
81
82module_param(clockoverride, int, 0644);
83MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
84
85PBCMSDH_SDMMC_INSTANCE gInstance;
86
87/* Maximum number of bcmsdh_sdmmc devices supported by driver */
88#define BCMSDH_SDMMC_MAX_DEVICES 1
89
90extern int bcmsdh_probe_bcmdhd(struct device *dev);
91extern int bcmsdh_remove_bcmdhd(struct device *dev);
92
93extern volatile bool dhd_mmc_suspend;
94
95static int bcmsdh_sdmmc_probe(struct sdio_func *func,
96 const struct sdio_device_id *id)
97{
98 int ret = 0;
99 static struct sdio_func sdio_func_0;
100 sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
101 sd_trace(("sdio_bcmsdh: func->class=%x\n", func->class));
102 sd_trace(("sdio_vendor: 0x%04x\n", func->vendor));
103 sd_trace(("sdio_device: 0x%04x\n", func->device));
104 sd_trace(("Function#: 0x%04x\n", func->num));
105
106 if (func->num == 1) {
107 sdio_func_0.num = 0;
108 sdio_func_0.card = func->card;
109 gInstance->func[0] = &sdio_func_0;
110 if(func->device == 0x4) { /* 4318 */
111 gInstance->func[2] = NULL;
112 sd_trace(("NIC found, calling bcmsdh_probe_bcmdhd...\n"));
113 ret = bcmsdh_probe_bcmdhd(&func->dev);
114 }
115 }
116
117 gInstance->func[func->num] = func;
118
119 if (func->num == 2) {
120#ifdef WL_CFG80211
121 wl_cfg80211_set_sdio_func(func);
122#endif
123 sd_trace(("F2 found, calling bcmsdh_probe_bcmdhd...\n"));
124 ret = bcmsdh_probe_bcmdhd(&func->dev);
125 }
126
127 return ret;
128}
129
130static void bcmsdh_sdmmc_remove(struct sdio_func *func)
131{
132 sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
133 sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
134 sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
135 sd_info(("sdio_device: 0x%04x\n", func->device));
136 sd_info(("Function#: 0x%04x\n", func->num));
137
138 if (func->num == 2) {
139 sd_trace(("F2 found, calling bcmsdh_remove_bcmdhd...\n"));
140 bcmsdh_remove_bcmdhd(&func->dev);
141 } else if (func->num == 1) {
142 sdio_claim_host(func);
143 sdio_disable_func(func);
144 sdio_release_host(func);
145 gInstance->func[1] = NULL;
146 }
147}
148
149/* devices we support, null terminated */
150static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
151 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) },
152 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) },
153 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) },
154 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) },
155 { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330) },
156 { /* end: all zeroes */ },
157};
158
159MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
160
161#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
162static int bcmsdh_sdmmc_suspend(struct device *pdev)
163{
164 struct sdio_func *func = dev_to_sdio_func(pdev);
165 mmc_pm_flag_t sdio_flags;
166 int ret = 0;
167
168 if (func->num != 2)
169 return 0;
170 if (dhd_os_check_wakelock(bcmsdh_get_drvdata()))
171 return -EBUSY;
172#if defined(OOB_INTR_ONLY)
173 bcmsdh_oob_intr_set(0);
174#endif
175 smp_mb();
176
177 sdio_flags = sdio_get_host_pm_caps(func);
178
179 if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
180 sd_err(("can't keep power while host "
181 "is suspended\n", __FUNCTION__));
182 ret = -EINVAL;
183 goto out;
184 }
185
186 /* keep power while host suspended */
187 ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
188 if (ret) {
189 sd_err(("error while trying to keep power\n", __FUNCTION__));
190 goto out;
191 }
192
193 dhd_mmc_suspend = TRUE;
194
195out:
196 return ret;
197}
198
199static int bcmsdh_sdmmc_resume(struct device *pdev)
200{
201 struct sdio_func *func = dev_to_sdio_func(pdev);
202
203 if (func->num != 2)
204 return 0;
205 dhd_mmc_suspend = FALSE;
206#if defined(OOB_INTR_ONLY)
207 if (dhd_os_check_if_up(bcmsdh_get_drvdata()))
208 bcmsdh_oob_intr_set(1);
209#endif
210 smp_mb();
211 return 0;
212}
213
214static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops = {
215 .suspend = bcmsdh_sdmmc_suspend,
216 .resume = bcmsdh_sdmmc_resume,
217};
218#endif
219
220static struct sdio_driver bcmsdh_sdmmc_driver = {
221 .probe = bcmsdh_sdmmc_probe,
222 .remove = bcmsdh_sdmmc_remove,
223 .name = "bcmsdh_sdmmc",
224 .id_table = bcmsdh_sdmmc_ids,
225#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
226 .drv = {
227 .pm = &bcmsdh_sdmmc_pm_ops,
228 },
229#endif
230};
231
232struct sdos_info {
233 sdioh_info_t *sd;
234 spinlock_t lock;
235};
236
237
238int
239sdioh_sdmmc_osinit(sdioh_info_t *sd)
240{
241 struct sdos_info *sdos;
242
243 sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info));
244 sd->sdos_info = (void*)sdos;
245 if (sdos == NULL)
246 return BCME_NOMEM;
247
248 sdos->sd = sd;
249 spin_lock_init(&sdos->lock);
250 return BCME_OK;
251}
252
253void
254sdioh_sdmmc_osfree(sdioh_info_t *sd)
255{
256 struct sdos_info *sdos;
257 ASSERT(sd && sd->sdos_info);
258
259 sdos = (struct sdos_info *)sd->sdos_info;
260 MFREE(sd->osh, sdos, sizeof(struct sdos_info));
261}
262
263/* Interrupt enable/disable */
264SDIOH_API_RC
265sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
266{
267 ulong flags;
268 struct sdos_info *sdos;
269
270 sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
271
272 sdos = (struct sdos_info *)sd->sdos_info;
273 ASSERT(sdos);
274
275#if !defined(OOB_INTR_ONLY)
276 if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
277 sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
278 return SDIOH_API_RC_FAIL;
279 }
280#endif /* !defined(OOB_INTR_ONLY) */
281
282 /* Ensure atomicity for enable/disable calls */
283 spin_lock_irqsave(&sdos->lock, flags);
284
285 sd->client_intr_enabled = enable;
286 if (enable) {
287 sdioh_sdmmc_devintr_on(sd);
288 } else {
289 sdioh_sdmmc_devintr_off(sd);
290 }
291
292 spin_unlock_irqrestore(&sdos->lock, flags);
293
294 return SDIOH_API_RC_SUCCESS;
295}
296
297
298#ifdef BCMSDH_MODULE
299static int __init
300bcmsdh_module_init(void)
301{
302 int error = 0;
303 sdio_function_init();
304 return error;
305}
306
307static void __exit
308bcmsdh_module_cleanup(void)
309{
310 sdio_function_cleanup();
311}
312
313module_init(bcmsdh_module_init);
314module_exit(bcmsdh_module_cleanup);
315
316MODULE_LICENSE("GPL v2");
317MODULE_DESCRIPTION(DESCRIPTION);
318MODULE_AUTHOR(AUTHOR);
319
320#endif /* BCMSDH_MODULE */
321/*
322 * module init
323*/
324int sdio_function_init(void)
325{
326 int error = 0;
327 sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
328
329 gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL);
330 if (!gInstance)
331 return -ENOMEM;
332
333 error = sdio_register_driver(&bcmsdh_sdmmc_driver);
334
335 return error;
336}
337
338/*
339 * module cleanup
340*/
341void sdio_function_cleanup(void)
342{
343 sd_trace(("%s Enter\n", __FUNCTION__));
344
345
346 sdio_unregister_driver(&bcmsdh_sdmmc_driver);
347
348 if (gInstance)
349 kfree(gInstance);
350}