Commit | Line | Data |
---|---|---|
cf2b4488 HP |
1 | /* |
2 | * Copyright (c) 2010 Broadcom Corporation | |
3 | * | |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
11 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | |
13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
a1c16ed2 GKH |
16 | #include <linux/types.h> |
17 | #include <linux/sched.h> /* request_irq() */ | |
c6ac24e9 | 18 | #include <linux/netdevice.h> |
a1c16ed2 | 19 | #include <bcmdefs.h> |
cf2b4488 HP |
20 | #include <bcmutils.h> |
21 | #include <sdio.h> /* SDIO Specs */ | |
22 | #include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */ | |
23 | #include <sdiovar.h> /* to get msglevel bit values */ | |
24 | ||
cf2b4488 HP |
25 | #include <linux/mmc/core.h> |
26 | #include <linux/mmc/card.h> | |
27 | #include <linux/mmc/sdio_func.h> | |
28 | #include <linux/mmc/sdio_ids.h> | |
29 | ||
30 | #if !defined(SDIO_VENDOR_ID_BROADCOM) | |
31 | #define SDIO_VENDOR_ID_BROADCOM 0x02d0 | |
32 | #endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */ | |
33 | ||
34 | #define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000 | |
35 | ||
36 | #if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) | |
37 | #define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB 0x0492 /* BCM94325SDGWB */ | |
38 | #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */ | |
39 | #if !defined(SDIO_DEVICE_ID_BROADCOM_4325) | |
40 | #define SDIO_DEVICE_ID_BROADCOM_4325 0x0493 | |
41 | #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */ | |
42 | #if !defined(SDIO_DEVICE_ID_BROADCOM_4329) | |
43 | #define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 | |
44 | #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */ | |
45 | #if !defined(SDIO_DEVICE_ID_BROADCOM_4319) | |
46 | #define SDIO_DEVICE_ID_BROADCOM_4319 0x4319 | |
47 | #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */ | |
48 | ||
49 | #include <bcmsdh_sdmmc.h> | |
50 | ||
51 | #include <dhd_dbg.h> | |
cf2b4488 | 52 | #include <wl_cfg80211.h> |
cf2b4488 HP |
53 | |
54 | extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd); | |
55 | extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd); | |
56 | ||
57 | int sdio_function_init(void); | |
58 | void sdio_function_cleanup(void); | |
59 | ||
60 | /* module param defaults */ | |
5f782dee | 61 | static int clockoverride; |
cf2b4488 HP |
62 | |
63 | module_param(clockoverride, int, 0644); | |
64 | MODULE_PARM_DESC(clockoverride, "SDIO card clock override"); | |
65 | ||
66 | PBCMSDH_SDMMC_INSTANCE gInstance; | |
67 | ||
68 | /* Maximum number of bcmsdh_sdmmc devices supported by driver */ | |
69 | #define BCMSDH_SDMMC_MAX_DEVICES 1 | |
70 | ||
71 | extern int bcmsdh_probe(struct device *dev); | |
72 | extern int bcmsdh_remove(struct device *dev); | |
73 | struct device sdmmc_dev; | |
74 | ||
75 | static int bcmsdh_sdmmc_probe(struct sdio_func *func, | |
76 | const struct sdio_device_id *id) | |
77 | { | |
78 | int ret = 0; | |
79 | static struct sdio_func sdio_func_0; | |
80 | sd_trace(("bcmsdh_sdmmc: %s Enter\n", __func__)); | |
81 | sd_trace(("sdio_bcmsdh: func->class=%x\n", func->class)); | |
82 | sd_trace(("sdio_vendor: 0x%04x\n", func->vendor)); | |
83 | sd_trace(("sdio_device: 0x%04x\n", func->device)); | |
84 | sd_trace(("Function#: 0x%04x\n", func->num)); | |
85 | ||
86 | if (func->num == 1) { | |
87 | sdio_func_0.num = 0; | |
88 | sdio_func_0.card = func->card; | |
89 | gInstance->func[0] = &sdio_func_0; | |
90 | if (func->device == 0x4) { /* 4318 */ | |
91 | gInstance->func[2] = NULL; | |
92 | sd_trace(("NIC found, calling bcmsdh_probe...\n")); | |
93 | ret = bcmsdh_probe(&sdmmc_dev); | |
94 | } | |
95 | } | |
96 | ||
97 | gInstance->func[func->num] = func; | |
98 | ||
99 | if (func->num == 2) { | |
cf2b4488 | 100 | wl_cfg80211_sdio_func(func); |
cf2b4488 HP |
101 | sd_trace(("F2 found, calling bcmsdh_probe...\n")); |
102 | ret = bcmsdh_probe(&sdmmc_dev); | |
103 | } | |
104 | ||
105 | return ret; | |
106 | } | |
107 | ||
108 | static void bcmsdh_sdmmc_remove(struct sdio_func *func) | |
109 | { | |
110 | sd_trace(("bcmsdh_sdmmc: %s Enter\n", __func__)); | |
111 | sd_info(("sdio_bcmsdh: func->class=%x\n", func->class)); | |
112 | sd_info(("sdio_vendor: 0x%04x\n", func->vendor)); | |
113 | sd_info(("sdio_device: 0x%04x\n", func->device)); | |
114 | sd_info(("Function#: 0x%04x\n", func->num)); | |
115 | ||
116 | if (func->num == 2) { | |
117 | sd_trace(("F2 found, calling bcmsdh_remove...\n")); | |
118 | bcmsdh_remove(&sdmmc_dev); | |
119 | } | |
120 | } | |
121 | ||
122 | /* devices we support, null terminated */ | |
123 | static const struct sdio_device_id bcmsdh_sdmmc_ids[] = { | |
124 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT)}, | |
125 | {SDIO_DEVICE | |
126 | (SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)}, | |
127 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325)}, | |
128 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, | |
129 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319)}, | |
130 | { /* end: all zeroes */ }, | |
131 | }; | |
132 | ||
133 | MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids); | |
134 | ||
135 | static struct sdio_driver bcmsdh_sdmmc_driver = { | |
136 | .probe = bcmsdh_sdmmc_probe, | |
137 | .remove = bcmsdh_sdmmc_remove, | |
138 | .name = "brcmfmac", | |
139 | .id_table = bcmsdh_sdmmc_ids, | |
140 | }; | |
141 | ||
142 | struct sdos_info { | |
143 | sdioh_info_t *sd; | |
144 | spinlock_t lock; | |
145 | }; | |
146 | ||
147 | int sdioh_sdmmc_osinit(sdioh_info_t *sd) | |
148 | { | |
149 | struct sdos_info *sdos; | |
150 | ||
5fcc1fcb | 151 | sdos = kmalloc(sizeof(struct sdos_info), GFP_ATOMIC); |
cf2b4488 HP |
152 | sd->sdos_info = (void *)sdos; |
153 | if (sdos == NULL) | |
154 | return BCME_NOMEM; | |
155 | ||
156 | sdos->sd = sd; | |
157 | spin_lock_init(&sdos->lock); | |
158 | return BCME_OK; | |
159 | } | |
160 | ||
161 | void sdioh_sdmmc_osfree(sdioh_info_t *sd) | |
162 | { | |
163 | struct sdos_info *sdos; | |
164 | ASSERT(sd && sd->sdos_info); | |
165 | ||
166 | sdos = (struct sdos_info *)sd->sdos_info; | |
182acb3c | 167 | kfree(sdos); |
cf2b4488 HP |
168 | } |
169 | ||
170 | /* Interrupt enable/disable */ | |
171 | SDIOH_API_RC sdioh_interrupt_set(sdioh_info_t *sd, bool enable) | |
172 | { | |
3deea904 | 173 | unsigned long flags; |
cf2b4488 HP |
174 | struct sdos_info *sdos; |
175 | ||
176 | sd_trace(("%s: %s\n", __func__, enable ? "Enabling" : "Disabling")); | |
177 | ||
178 | sdos = (struct sdos_info *)sd->sdos_info; | |
179 | ASSERT(sdos); | |
180 | ||
181 | #if !defined(OOB_INTR_ONLY) | |
182 | if (enable && !(sd->intr_handler && sd->intr_handler_arg)) { | |
183 | sd_err(("%s: no handler registered, will not enable\n", | |
184 | __func__)); | |
185 | return SDIOH_API_RC_FAIL; | |
186 | } | |
187 | #endif /* !defined(OOB_INTR_ONLY) */ | |
188 | ||
189 | /* Ensure atomicity for enable/disable calls */ | |
190 | spin_lock_irqsave(&sdos->lock, flags); | |
191 | ||
192 | sd->client_intr_enabled = enable; | |
193 | if (enable) | |
194 | sdioh_sdmmc_devintr_on(sd); | |
195 | else | |
196 | sdioh_sdmmc_devintr_off(sd); | |
197 | ||
198 | spin_unlock_irqrestore(&sdos->lock, flags); | |
199 | ||
200 | return SDIOH_API_RC_SUCCESS; | |
201 | } | |
202 | ||
203 | /* | |
204 | * module init | |
205 | */ | |
206 | int sdio_function_init(void) | |
207 | { | |
208 | int error = 0; | |
209 | sd_trace(("bcmsdh_sdmmc: %s Enter\n", __func__)); | |
210 | ||
211 | gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL); | |
212 | if (!gInstance) | |
213 | return -ENOMEM; | |
214 | ||
215 | bzero(&sdmmc_dev, sizeof(sdmmc_dev)); | |
216 | error = sdio_register_driver(&bcmsdh_sdmmc_driver); | |
217 | ||
218 | return error; | |
219 | } | |
220 | ||
221 | /* | |
222 | * module cleanup | |
223 | */ | |
224 | extern int bcmsdh_remove(struct device *dev); | |
225 | void sdio_function_cleanup(void) | |
226 | { | |
227 | sd_trace(("%s Enter\n", __func__)); | |
228 | ||
229 | sdio_unregister_driver(&bcmsdh_sdmmc_driver); | |
230 | ||
231 | kfree(gInstance); | |
232 | } |