Commit | Line | Data |
---|---|---|
5b435de0 AS |
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 | */ | |
02f77195 | 16 | |
5b435de0 AS |
17 | #include <linux/kernel.h> |
18 | #include <linux/string.h> | |
5b435de0 | 19 | #include <linux/netdevice.h> |
5dc3b7b9 | 20 | #include <linux/module.h> |
fdd0bd88 | 21 | #include <linux/firmware.h> |
5b435de0 AS |
22 | #include <brcmu_wifi.h> |
23 | #include <brcmu_utils.h> | |
122d3d04 | 24 | #include "core.h" |
d14f78b9 | 25 | #include "bus.h" |
a8e8ed34 | 26 | #include "debug.h" |
0af29bf7 | 27 | #include "fwil.h" |
6f5838a4 | 28 | #include "fwil_types.h" |
e5483576 | 29 | #include "tracepoint.h" |
6b89dcb3 | 30 | #include "common.h" |
8ea56be0 | 31 | #include "of.h" |
fdd0bd88 | 32 | #include "firmware.h" |
6b89dcb3 | 33 | |
d84d99e0 HM |
34 | MODULE_AUTHOR("Broadcom Corporation"); |
35 | MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); | |
36 | MODULE_LICENSE("Dual BSD/GPL"); | |
37 | ||
6b89dcb3 | 38 | const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; |
5b435de0 | 39 | |
0af29bf7 HM |
40 | #define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 |
41 | #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 | |
5b435de0 | 42 | |
7705ba6f | 43 | /* default boost value for RSSI_DELTA in preferred join selection */ |
e09cc63d DK |
44 | #define BRCMF_JOIN_PREF_RSSI_BOOST 8 |
45 | ||
7d34b056 HM |
46 | #define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ |
47 | ||
48 | static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; | |
49 | module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); | |
50 | MODULE_PARM_DESC(txglomsz, "Maximum tx packet chain size [SDIO]"); | |
51 | ||
52 | /* Debug level configuration. See debug.h for bits, sysfs modifiable */ | |
53 | int brcmf_msg_level; | |
54 | module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); | |
55 | MODULE_PARM_DESC(debug, "Level of debug output"); | |
56 | ||
57 | static int brcmf_p2p_enable; | |
58 | module_param_named(p2pon, brcmf_p2p_enable, int, 0); | |
59 | MODULE_PARM_DESC(p2pon, "Enable legacy p2p management functionality"); | |
60 | ||
61 | static int brcmf_feature_disable; | |
62 | module_param_named(feature_disable, brcmf_feature_disable, int, 0); | |
63 | MODULE_PARM_DESC(feature_disable, "Disable features"); | |
64 | ||
65 | static char brcmf_firmware_path[BRCMF_FW_ALTPATH_LEN]; | |
66 | module_param_string(alternative_fw_path, brcmf_firmware_path, | |
67 | BRCMF_FW_ALTPATH_LEN, S_IRUSR); | |
68 | MODULE_PARM_DESC(alternative_fw_path, "Alternative firmware path"); | |
69 | ||
70 | static int brcmf_fcmode; | |
71 | module_param_named(fcmode, brcmf_fcmode, int, 0); | |
72 | MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control"); | |
73 | ||
74 | static int brcmf_roamoff; | |
75 | module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); | |
76 | MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine"); | |
77 | ||
8ba83d4d | 78 | #ifdef DEBUG |
e8cd4750 | 79 | /* always succeed brcmf_bus_started() */ |
8ba83d4d AS |
80 | static int brcmf_ignore_probe_fail; |
81 | module_param_named(ignore_probe_fail, brcmf_ignore_probe_fail, int, 0); | |
82 | MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging"); | |
83 | #endif | |
84 | ||
4d792895 | 85 | static struct brcmfmac_platform_data *brcmfmac_pdata; |
7d34b056 HM |
86 | struct brcmf_mp_global_t brcmf_mp_global; |
87 | ||
7705ba6f AS |
88 | void brcmf_c_set_joinpref_default(struct brcmf_if *ifp) |
89 | { | |
90 | struct brcmf_join_pref_params join_pref_params[2]; | |
91 | int err; | |
92 | ||
93 | /* Setup join_pref to select target by RSSI (boost on 5GHz) */ | |
94 | join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA; | |
95 | join_pref_params[0].len = 2; | |
96 | join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST; | |
97 | join_pref_params[0].band = WLC_BAND_5G; | |
98 | ||
99 | join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI; | |
100 | join_pref_params[1].len = 2; | |
101 | join_pref_params[1].rssi_gain = 0; | |
102 | join_pref_params[1].band = 0; | |
103 | err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params, | |
104 | sizeof(join_pref_params)); | |
105 | if (err) | |
106 | brcmf_err("Set join_pref error (%d)\n", err); | |
107 | } | |
108 | ||
fdd0bd88 CHH |
109 | static int brcmf_c_download(struct brcmf_if *ifp, u16 flag, |
110 | struct brcmf_dload_data_le *dload_buf, | |
111 | u32 len) | |
112 | { | |
113 | s32 err; | |
114 | ||
115 | flag |= (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT); | |
116 | dload_buf->flag = cpu_to_le16(flag); | |
117 | dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM); | |
118 | dload_buf->len = cpu_to_le32(len); | |
119 | dload_buf->crc = cpu_to_le32(0); | |
120 | len = sizeof(*dload_buf) + len - 1; | |
121 | ||
122 | err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, len); | |
123 | ||
124 | return err; | |
125 | } | |
126 | ||
127 | static int brcmf_c_get_clm_name(struct brcmf_if *ifp, u8 *clm_name) | |
128 | { | |
129 | struct brcmf_bus *bus = ifp->drvr->bus_if; | |
130 | struct brcmf_rev_info *ri = &ifp->drvr->revinfo; | |
131 | u8 fw_name[BRCMF_FW_NAME_LEN]; | |
132 | u8 *ptr; | |
133 | size_t len; | |
134 | s32 err; | |
135 | ||
136 | memset(fw_name, 0, BRCMF_FW_NAME_LEN); | |
137 | err = brcmf_bus_get_fwname(bus, ri->chipnum, ri->chiprev, fw_name); | |
138 | if (err) { | |
139 | brcmf_err("get firmware name failed (%d)\n", err); | |
140 | goto done; | |
141 | } | |
142 | ||
143 | /* generate CLM blob file name */ | |
144 | ptr = strrchr(fw_name, '.'); | |
145 | if (!ptr) { | |
146 | err = -ENOENT; | |
147 | goto done; | |
148 | } | |
149 | ||
150 | len = ptr - fw_name + 1; | |
151 | if (len + strlen(".clm_blob") > BRCMF_FW_NAME_LEN) { | |
152 | err = -E2BIG; | |
153 | } else { | |
154 | strlcpy(clm_name, fw_name, len); | |
155 | strlcat(clm_name, ".clm_blob", BRCMF_FW_NAME_LEN); | |
156 | } | |
157 | done: | |
158 | return err; | |
159 | } | |
160 | ||
161 | static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) | |
162 | { | |
163 | struct device *dev = ifp->drvr->bus_if->dev; | |
164 | struct brcmf_dload_data_le *chunk_buf; | |
165 | const struct firmware *clm = NULL; | |
166 | u8 clm_name[BRCMF_FW_NAME_LEN]; | |
167 | u32 chunk_len; | |
168 | u32 datalen; | |
169 | u32 cumulative_len; | |
170 | u16 dl_flag = DL_BEGIN; | |
171 | u32 status; | |
172 | s32 err; | |
173 | ||
174 | brcmf_dbg(TRACE, "Enter\n"); | |
175 | ||
176 | memset(clm_name, 0, BRCMF_FW_NAME_LEN); | |
177 | err = brcmf_c_get_clm_name(ifp, clm_name); | |
178 | if (err) { | |
179 | brcmf_err("get CLM blob file name failed (%d)\n", err); | |
180 | return err; | |
181 | } | |
182 | ||
183 | err = request_firmware(&clm, clm_name, dev); | |
184 | if (err) { | |
cc124d5c WF |
185 | brcmf_info("no clm_blob available(err=%d), device may have limited channels available\n", |
186 | err); | |
187 | return 0; | |
fdd0bd88 CHH |
188 | } |
189 | ||
190 | chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN - 1, GFP_KERNEL); | |
191 | if (!chunk_buf) { | |
192 | err = -ENOMEM; | |
193 | goto done; | |
194 | } | |
195 | ||
196 | datalen = clm->size; | |
197 | cumulative_len = 0; | |
198 | do { | |
199 | if (datalen > MAX_CHUNK_LEN) { | |
200 | chunk_len = MAX_CHUNK_LEN; | |
201 | } else { | |
202 | chunk_len = datalen; | |
203 | dl_flag |= DL_END; | |
204 | } | |
205 | memcpy(chunk_buf->data, clm->data + cumulative_len, chunk_len); | |
206 | ||
207 | err = brcmf_c_download(ifp, dl_flag, chunk_buf, chunk_len); | |
208 | ||
209 | dl_flag &= ~DL_BEGIN; | |
210 | ||
211 | cumulative_len += chunk_len; | |
212 | datalen -= chunk_len; | |
213 | } while ((datalen > 0) && (err == 0)); | |
214 | ||
215 | if (err) { | |
216 | brcmf_err("clmload (%zu byte file) failed (%d); ", | |
217 | clm->size, err); | |
218 | /* Retrieve clmload_status and print */ | |
219 | err = brcmf_fil_iovar_int_get(ifp, "clmload_status", &status); | |
220 | if (err) | |
221 | brcmf_err("get clmload_status failed (%d)\n", err); | |
222 | else | |
223 | brcmf_dbg(INFO, "clmload_status=%d\n", status); | |
224 | err = -EIO; | |
225 | } | |
226 | ||
227 | kfree(chunk_buf); | |
228 | done: | |
229 | release_firmware(clm); | |
230 | return err; | |
231 | } | |
232 | ||
0af29bf7 | 233 | int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) |
5b435de0 | 234 | { |
0af29bf7 HM |
235 | s8 eventmask[BRCMF_EVENTING_MASK_LEN]; |
236 | u8 buf[BRCMF_DCMD_SMLEN]; | |
48622903 AS |
237 | struct brcmf_rev_info_le revinfo; |
238 | struct brcmf_rev_info *ri; | |
fdd0bd88 | 239 | char *clmver; |
0af29bf7 HM |
240 | char *ptr; |
241 | s32 err; | |
5b435de0 | 242 | |
0af29bf7 HM |
243 | /* retreive mac address */ |
244 | err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, | |
245 | sizeof(ifp->mac_addr)); | |
246 | if (err < 0) { | |
48622903 | 247 | brcmf_err("Retreiving cur_etheraddr failed, %d\n", err); |
0af29bf7 | 248 | goto done; |
5b435de0 | 249 | } |
0af29bf7 | 250 | memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac)); |
5b435de0 | 251 | |
48622903 AS |
252 | err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, |
253 | &revinfo, sizeof(revinfo)); | |
7f52c81d | 254 | ri = &ifp->drvr->revinfo; |
48622903 AS |
255 | if (err < 0) { |
256 | brcmf_err("retrieving revision info failed, %d\n", err); | |
257 | } else { | |
48622903 AS |
258 | ri->vendorid = le32_to_cpu(revinfo.vendorid); |
259 | ri->deviceid = le32_to_cpu(revinfo.deviceid); | |
260 | ri->radiorev = le32_to_cpu(revinfo.radiorev); | |
261 | ri->chiprev = le32_to_cpu(revinfo.chiprev); | |
262 | ri->corerev = le32_to_cpu(revinfo.corerev); | |
263 | ri->boardid = le32_to_cpu(revinfo.boardid); | |
264 | ri->boardvendor = le32_to_cpu(revinfo.boardvendor); | |
265 | ri->boardrev = le32_to_cpu(revinfo.boardrev); | |
266 | ri->driverrev = le32_to_cpu(revinfo.driverrev); | |
267 | ri->ucoderev = le32_to_cpu(revinfo.ucoderev); | |
268 | ri->bus = le32_to_cpu(revinfo.bus); | |
269 | ri->chipnum = le32_to_cpu(revinfo.chipnum); | |
270 | ri->phytype = le32_to_cpu(revinfo.phytype); | |
271 | ri->phyrev = le32_to_cpu(revinfo.phyrev); | |
272 | ri->anarev = le32_to_cpu(revinfo.anarev); | |
273 | ri->chippkg = le32_to_cpu(revinfo.chippkg); | |
274 | ri->nvramrev = le32_to_cpu(revinfo.nvramrev); | |
275 | } | |
7f52c81d | 276 | ri->result = err; |
48622903 | 277 | |
fdd0bd88 CHH |
278 | /* Do any CLM downloading */ |
279 | err = brcmf_c_process_clm_blob(ifp); | |
280 | if (err < 0) { | |
281 | brcmf_err("download CLM blob file failed, %d\n", err); | |
282 | goto done; | |
283 | } | |
284 | ||
5b435de0 AS |
285 | /* query for 'ver' to get version info from firmware */ |
286 | memset(buf, 0, sizeof(buf)); | |
0af29bf7 HM |
287 | strcpy(buf, "ver"); |
288 | err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf)); | |
289 | if (err < 0) { | |
5e8149f5 | 290 | brcmf_err("Retreiving version information failed, %d\n", |
0af29bf7 HM |
291 | err); |
292 | goto done; | |
293 | } | |
294 | ptr = (char *)buf; | |
5b435de0 | 295 | strsep(&ptr, "\n"); |
55685b8f | 296 | |
5b435de0 | 297 | /* Print fw version info */ |
d79fe4cb | 298 | brcmf_info("Firmware version = %s\n", buf); |
5b435de0 | 299 | |
55685b8f AS |
300 | /* locate firmware version number for ethtool */ |
301 | ptr = strrchr(buf, ' ') + 1; | |
302 | strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver)); | |
303 | ||
fdd0bd88 CHH |
304 | /* Query for 'clmver' to get CLM version info from firmware */ |
305 | memset(buf, 0, sizeof(buf)); | |
306 | err = brcmf_fil_iovar_data_get(ifp, "clmver", buf, sizeof(buf)); | |
307 | if (err) { | |
308 | brcmf_dbg(TRACE, "retrieving clmver failed, %d\n", err); | |
309 | } else { | |
310 | clmver = (char *)buf; | |
311 | /* store CLM version for adding it to revinfo debugfs file */ | |
312 | memcpy(ifp->drvr->clmver, clmver, sizeof(ifp->drvr->clmver)); | |
313 | ||
314 | /* Replace all newline/linefeed characters with space | |
315 | * character | |
316 | */ | |
317 | ptr = clmver; | |
318 | while ((ptr = strnchr(ptr, '\n', sizeof(buf))) != NULL) | |
319 | *ptr = ' '; | |
320 | ||
321 | brcmf_dbg(INFO, "CLM version = %s\n", clmver); | |
322 | } | |
323 | ||
5e787f75 DK |
324 | /* set mpc */ |
325 | err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); | |
326 | if (err) { | |
327 | brcmf_err("failed setting mpc\n"); | |
328 | goto done; | |
329 | } | |
330 | ||
7705ba6f | 331 | brcmf_c_set_joinpref_default(ifp); |
e09cc63d | 332 | |
0af29bf7 HM |
333 | /* Setup event_msgs, enable E_IF */ |
334 | err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask, | |
335 | BRCMF_EVENTING_MASK_LEN); | |
336 | if (err) { | |
5e8149f5 | 337 | brcmf_err("Get event_msgs error (%d)\n", err); |
0af29bf7 HM |
338 | goto done; |
339 | } | |
340 | setbit(eventmask, BRCMF_E_IF); | |
341 | err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask, | |
342 | BRCMF_EVENTING_MASK_LEN); | |
343 | if (err) { | |
5e8149f5 | 344 | brcmf_err("Set event_msgs error (%d)\n", err); |
0af29bf7 HM |
345 | goto done; |
346 | } | |
347 | ||
348 | /* Setup default scan channel time */ | |
349 | err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, | |
350 | BRCMF_DEFAULT_SCAN_CHANNEL_TIME); | |
351 | if (err) { | |
5e8149f5 | 352 | brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n", |
0af29bf7 HM |
353 | err); |
354 | goto done; | |
355 | } | |
356 | ||
357 | /* Setup default scan unassoc time */ | |
358 | err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, | |
359 | BRCMF_DEFAULT_SCAN_UNASSOC_TIME); | |
360 | if (err) { | |
5e8149f5 | 361 | brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n", |
0af29bf7 HM |
362 | err); |
363 | goto done; | |
364 | } | |
365 | ||
7bf65aa9 HM |
366 | /* Enable tx beamforming, errors can be ignored (not supported) */ |
367 | (void)brcmf_fil_iovar_int_set(ifp, "txbf", 1); | |
368 | ||
cf458287 AS |
369 | /* do bus specific preinit here */ |
370 | err = brcmf_bus_preinit(ifp->drvr->bus_if); | |
0af29bf7 HM |
371 | done: |
372 | return err; | |
5b435de0 | 373 | } |
e5483576 | 374 | |
087fa712 RM |
375 | #ifndef CONFIG_BRCM_TRACING |
376 | void __brcmf_err(const char *func, const char *fmt, ...) | |
377 | { | |
378 | struct va_format vaf; | |
379 | va_list args; | |
380 | ||
381 | va_start(args, fmt); | |
382 | ||
383 | vaf.fmt = fmt; | |
384 | vaf.va = &args; | |
385 | pr_err("%s: %pV", func, &vaf); | |
386 | ||
387 | va_end(args); | |
388 | } | |
389 | #endif | |
390 | ||
e5483576 AS |
391 | #if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG) |
392 | void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) | |
393 | { | |
394 | struct va_format vaf = { | |
395 | .fmt = fmt, | |
396 | }; | |
397 | va_list args; | |
398 | ||
399 | va_start(args, fmt); | |
400 | vaf.va = &args; | |
401 | if (brcmf_msg_level & level) | |
402 | pr_debug("%s %pV", func, &vaf); | |
403 | trace_brcmf_dbg(level, func, &vaf); | |
404 | va_end(args); | |
405 | } | |
406 | #endif | |
7d34b056 | 407 | |
d84d99e0 | 408 | static void brcmf_mp_attach(void) |
7d34b056 | 409 | { |
4d792895 HM |
410 | /* If module param firmware path is set then this will always be used, |
411 | * if not set then if available use the platform data version. To make | |
412 | * sure it gets initialized at all, always copy the module param version | |
413 | */ | |
7d34b056 HM |
414 | strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path, |
415 | BRCMF_FW_ALTPATH_LEN); | |
4d792895 HM |
416 | if ((brcmfmac_pdata) && (brcmfmac_pdata->fw_alternative_path) && |
417 | (brcmf_mp_global.firmware_path[0] == '\0')) { | |
418 | strlcpy(brcmf_mp_global.firmware_path, | |
419 | brcmfmac_pdata->fw_alternative_path, | |
420 | BRCMF_FW_ALTPATH_LEN); | |
421 | } | |
7d34b056 HM |
422 | } |
423 | ||
af5b5e62 HM |
424 | struct brcmf_mp_device *brcmf_get_module_param(struct device *dev, |
425 | enum brcmf_bus_type bus_type, | |
426 | u32 chip, u32 chiprev) | |
8ea56be0 | 427 | { |
af5b5e62 | 428 | struct brcmf_mp_device *settings; |
4d792895 | 429 | struct brcmfmac_pd_device *device_pd; |
af5b5e62 | 430 | bool found; |
4d792895 HM |
431 | int i; |
432 | ||
af5b5e62 HM |
433 | brcmf_dbg(INFO, "Enter, bus=%d, chip=%d, rev=%d\n", bus_type, chip, |
434 | chiprev); | |
435 | settings = kzalloc(sizeof(*settings), GFP_ATOMIC); | |
436 | if (!settings) | |
437 | return NULL; | |
438 | ||
439 | /* start by using the module paramaters */ | |
440 | settings->p2p_enable = !!brcmf_p2p_enable; | |
441 | settings->feature_disable = brcmf_feature_disable; | |
442 | settings->fcmode = brcmf_fcmode; | |
443 | settings->roamoff = !!brcmf_roamoff; | |
444 | #ifdef DEBUG | |
445 | settings->ignore_probe_fail = !!brcmf_ignore_probe_fail; | |
446 | #endif | |
447 | ||
448 | if (bus_type == BRCMF_BUSTYPE_SDIO) | |
449 | settings->bus.sdio.txglomsz = brcmf_sdiod_txglomsz; | |
450 | ||
451 | /* See if there is any device specific platform data configured */ | |
452 | found = false; | |
4d792895 HM |
453 | if (brcmfmac_pdata) { |
454 | for (i = 0; i < brcmfmac_pdata->device_count; i++) { | |
455 | device_pd = &brcmfmac_pdata->devices[i]; | |
456 | if ((device_pd->bus_type == bus_type) && | |
457 | (device_pd->id == chip) && | |
458 | ((device_pd->rev == chiprev) || | |
459 | (device_pd->rev == -1))) { | |
460 | brcmf_dbg(INFO, "Platform data for device found\n"); | |
af5b5e62 HM |
461 | settings->country_codes = |
462 | device_pd->country_codes; | |
4d792895 | 463 | if (device_pd->bus_type == BRCMF_BUSTYPE_SDIO) |
af5b5e62 HM |
464 | memcpy(&settings->bus.sdio, |
465 | &device_pd->bus.sdio, | |
466 | sizeof(settings->bus.sdio)); | |
467 | found = true; | |
4d792895 HM |
468 | break; |
469 | } | |
470 | } | |
471 | } | |
e457a8a0 RM |
472 | if (!found) { |
473 | /* No platform data for this device, try OF (Open Firwmare) */ | |
474 | brcmf_of_probe(dev, bus_type, settings); | |
af5b5e62 HM |
475 | } |
476 | return settings; | |
7d34b056 HM |
477 | } |
478 | ||
af5b5e62 | 479 | void brcmf_release_module_param(struct brcmf_mp_device *module_param) |
7d34b056 | 480 | { |
af5b5e62 | 481 | kfree(module_param); |
7d34b056 HM |
482 | } |
483 | ||
8ea56be0 HM |
484 | static int __init brcmf_common_pd_probe(struct platform_device *pdev) |
485 | { | |
486 | brcmf_dbg(INFO, "Enter\n"); | |
487 | ||
488 | brcmfmac_pdata = dev_get_platdata(&pdev->dev); | |
489 | ||
490 | if (brcmfmac_pdata->power_on) | |
491 | brcmfmac_pdata->power_on(); | |
492 | ||
493 | return 0; | |
494 | } | |
495 | ||
496 | static int brcmf_common_pd_remove(struct platform_device *pdev) | |
497 | { | |
498 | brcmf_dbg(INFO, "Enter\n"); | |
499 | ||
500 | if (brcmfmac_pdata->power_off) | |
501 | brcmfmac_pdata->power_off(); | |
502 | ||
503 | return 0; | |
504 | } | |
505 | ||
506 | static struct platform_driver brcmf_pd = { | |
507 | .remove = brcmf_common_pd_remove, | |
508 | .driver = { | |
4d792895 | 509 | .name = BRCMFMAC_PDATA_NAME, |
8ea56be0 HM |
510 | } |
511 | }; | |
512 | ||
d84d99e0 HM |
513 | static int __init brcmfmac_module_init(void) |
514 | { | |
515 | int err; | |
516 | ||
517 | /* Initialize debug system first */ | |
518 | brcmf_debugfs_init(); | |
519 | ||
8ea56be0 HM |
520 | /* Get the platform data (if available) for our devices */ |
521 | err = platform_driver_probe(&brcmf_pd, brcmf_common_pd_probe); | |
522 | if (err == -ENODEV) | |
523 | brcmf_dbg(INFO, "No platform data available.\n"); | |
524 | ||
d84d99e0 HM |
525 | /* Initialize global module paramaters */ |
526 | brcmf_mp_attach(); | |
527 | ||
528 | /* Continue the initialization by registering the different busses */ | |
529 | err = brcmf_core_init(); | |
8ea56be0 | 530 | if (err) { |
d84d99e0 | 531 | brcmf_debugfs_exit(); |
8ea56be0 HM |
532 | if (brcmfmac_pdata) |
533 | platform_driver_unregister(&brcmf_pd); | |
534 | } | |
d84d99e0 HM |
535 | |
536 | return err; | |
537 | } | |
538 | ||
539 | static void __exit brcmfmac_module_exit(void) | |
540 | { | |
541 | brcmf_core_exit(); | |
8ea56be0 HM |
542 | if (brcmfmac_pdata) |
543 | platform_driver_unregister(&brcmf_pd); | |
d84d99e0 HM |
544 | brcmf_debugfs_exit(); |
545 | } | |
546 | ||
547 | module_init(brcmfmac_module_init); | |
548 | module_exit(brcmfmac_module_exit); | |
549 |