]>
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/types.h> |
18 | #include <linux/netdevice.h> | |
19 | #include <linux/mmc/sdio.h> | |
20 | #include <linux/mmc/core.h> | |
21 | #include <linux/mmc/sdio_func.h> | |
22 | #include <linux/mmc/sdio_ids.h> | |
23 | #include <linux/mmc/card.h> | |
71201496 | 24 | #include <linux/mmc/host.h> |
5b435de0 AS |
25 | #include <linux/suspend.h> |
26 | #include <linux/errno.h> | |
27 | #include <linux/sched.h> /* request_irq() */ | |
b7a57e76 | 28 | #include <linux/module.h> |
ba89bf19 | 29 | #include <linux/platform_device.h> |
668761ac | 30 | #include <linux/platform_data/brcmfmac-sdio.h> |
5b435de0 AS |
31 | #include <net/cfg80211.h> |
32 | ||
33 | #include <defs.h> | |
34 | #include <brcm_hw_ids.h> | |
35 | #include <brcmu_utils.h> | |
36 | #include <brcmu_wifi.h> | |
37 | #include "sdio_host.h" | |
fe040158 | 38 | #include "sdio_chip.h" |
5b435de0 | 39 | #include "dhd_dbg.h" |
a8a363ac | 40 | #include "dhd_bus.h" |
5b435de0 AS |
41 | |
42 | #define SDIO_VENDOR_ID_BROADCOM 0x02d0 | |
43 | ||
44 | #define DMA_ALIGN_MASK 0x03 | |
45 | ||
5b435de0 AS |
46 | #define SDIO_FUNC1_BLOCKSIZE 64 |
47 | #define SDIO_FUNC2_BLOCKSIZE 512 | |
48 | ||
49 | /* devices we support, null terminated */ | |
50 | static const struct sdio_device_id brcmf_sdmmc_ids[] = { | |
369508c5 | 51 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)}, |
4a1c02ce | 52 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)}, |
5b435de0 | 53 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, |
ce2d7d7e | 54 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)}, |
85a4a1c3 | 55 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)}, |
fe040158 FL |
56 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, |
57 | SDIO_DEVICE_ID_BROADCOM_4335_4339)}, | |
5b435de0 AS |
58 | { /* end: all zeroes */ }, |
59 | }; | |
60 | MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); | |
61 | ||
668761ac HM |
62 | static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; |
63 | ||
ba89bf19 | 64 | |
78b3f1c5 | 65 | bool |
5b435de0 AS |
66 | brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev) |
67 | { | |
68 | bool is_err = false; | |
69 | #ifdef CONFIG_PM_SLEEP | |
70 | is_err = atomic_read(&sdiodev->suspend); | |
71 | #endif | |
72 | return is_err; | |
73 | } | |
74 | ||
78b3f1c5 | 75 | void |
5b435de0 AS |
76 | brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq) |
77 | { | |
78 | #ifdef CONFIG_PM_SLEEP | |
79 | int retry = 0; | |
80 | while (atomic_read(&sdiodev->suspend) && retry++ != 30) | |
81 | wait_event_timeout(*wq, false, HZ/100); | |
82 | #endif | |
83 | } | |
84 | ||
85 | static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev, | |
86 | uint regaddr, u8 *byte) | |
87 | { | |
88 | struct sdio_func *sdfunc = sdiodev->func[0]; | |
89 | int err_ret; | |
90 | ||
91 | /* | |
92 | * Can only directly write to some F0 registers. | |
93 | * Handle F2 enable/disable and Abort command | |
94 | * as a special case. | |
95 | */ | |
96 | if (regaddr == SDIO_CCCR_IOEx) { | |
97 | sdfunc = sdiodev->func[2]; | |
98 | if (sdfunc) { | |
5b435de0 AS |
99 | if (*byte & SDIO_FUNC_ENABLE_2) { |
100 | /* Enable Function 2 */ | |
101 | err_ret = sdio_enable_func(sdfunc); | |
102 | if (err_ret) | |
5e8149f5 | 103 | brcmf_err("enable F2 failed:%d\n", |
5b435de0 AS |
104 | err_ret); |
105 | } else { | |
106 | /* Disable Function 2 */ | |
107 | err_ret = sdio_disable_func(sdfunc); | |
108 | if (err_ret) | |
5e8149f5 | 109 | brcmf_err("Disable F2 failed:%d\n", |
5b435de0 AS |
110 | err_ret); |
111 | } | |
5b435de0 | 112 | } |
ba89bf19 FL |
113 | } else if ((regaddr == SDIO_CCCR_ABORT) || |
114 | (regaddr == SDIO_CCCR_IENx)) { | |
1cc26990 FL |
115 | sdfunc = kmemdup(sdiodev->func[0], sizeof(struct sdio_func), |
116 | GFP_KERNEL); | |
117 | if (!sdfunc) | |
118 | return -ENOMEM; | |
119 | sdfunc->num = 0; | |
5b435de0 | 120 | sdio_writeb(sdfunc, *byte, regaddr, &err_ret); |
1cc26990 | 121 | kfree(sdfunc); |
5b435de0 | 122 | } else if (regaddr < 0xF0) { |
5e8149f5 | 123 | brcmf_err("F0 Wr:0x%02x: write disallowed\n", regaddr); |
5b435de0 AS |
124 | err_ret = -EPERM; |
125 | } else { | |
5b435de0 | 126 | sdio_f0_writeb(sdfunc, *byte, regaddr, &err_ret); |
5b435de0 AS |
127 | } |
128 | ||
129 | return err_ret; | |
130 | } | |
131 | ||
132 | int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint func, | |
133 | uint regaddr, u8 *byte) | |
134 | { | |
135 | int err_ret; | |
136 | ||
c3203374 | 137 | brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr); |
5b435de0 AS |
138 | |
139 | brcmf_pm_resume_wait(sdiodev, &sdiodev->request_byte_wait); | |
140 | if (brcmf_pm_resume_error(sdiodev)) | |
141 | return -EIO; | |
142 | ||
143 | if (rw && func == 0) { | |
144 | /* handle F0 separately */ | |
145 | err_ret = brcmf_sdioh_f0_write_byte(sdiodev, regaddr, byte); | |
146 | } else { | |
5b435de0 AS |
147 | if (rw) /* CMD52 Write */ |
148 | sdio_writeb(sdiodev->func[func], *byte, regaddr, | |
149 | &err_ret); | |
150 | else if (func == 0) { | |
151 | *byte = sdio_f0_readb(sdiodev->func[func], regaddr, | |
152 | &err_ret); | |
153 | } else { | |
154 | *byte = sdio_readb(sdiodev->func[func], regaddr, | |
155 | &err_ret); | |
156 | } | |
5b435de0 AS |
157 | } |
158 | ||
159 | if (err_ret) | |
5e8149f5 | 160 | brcmf_err("Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n", |
5b435de0 AS |
161 | rw ? "write" : "read", func, regaddr, *byte, err_ret); |
162 | ||
163 | return err_ret; | |
164 | } | |
165 | ||
166 | int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, | |
167 | uint rw, uint func, uint addr, u32 *word, | |
168 | uint nbytes) | |
169 | { | |
170 | int err_ret = -EIO; | |
171 | ||
172 | if (func == 0) { | |
5e8149f5 | 173 | brcmf_err("Only CMD52 allowed to F0\n"); |
5b435de0 AS |
174 | return -EINVAL; |
175 | } | |
176 | ||
c3203374 | 177 | brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", |
5b435de0 AS |
178 | rw, func, addr, nbytes); |
179 | ||
180 | brcmf_pm_resume_wait(sdiodev, &sdiodev->request_word_wait); | |
181 | if (brcmf_pm_resume_error(sdiodev)) | |
182 | return -EIO; | |
5b435de0 AS |
183 | |
184 | if (rw) { /* CMD52 Write */ | |
185 | if (nbytes == 4) | |
186 | sdio_writel(sdiodev->func[func], *word, addr, | |
187 | &err_ret); | |
188 | else if (nbytes == 2) | |
189 | sdio_writew(sdiodev->func[func], (*word & 0xFFFF), | |
190 | addr, &err_ret); | |
191 | else | |
5e8149f5 | 192 | brcmf_err("Invalid nbytes: %d\n", nbytes); |
5b435de0 AS |
193 | } else { /* CMD52 Read */ |
194 | if (nbytes == 4) | |
195 | *word = sdio_readl(sdiodev->func[func], addr, &err_ret); | |
196 | else if (nbytes == 2) | |
197 | *word = sdio_readw(sdiodev->func[func], addr, | |
198 | &err_ret) & 0xFFFF; | |
199 | else | |
5e8149f5 | 200 | brcmf_err("Invalid nbytes: %d\n", nbytes); |
5b435de0 AS |
201 | } |
202 | ||
5b435de0 | 203 | if (err_ret) |
5e8149f5 | 204 | brcmf_err("Failed to %s word, Err: 0x%08x\n", |
5b435de0 AS |
205 | rw ? "write" : "read", err_ret); |
206 | ||
207 | return err_ret; | |
208 | } | |
209 | ||
5b435de0 AS |
210 | static int brcmf_sdioh_get_cisaddr(struct brcmf_sdio_dev *sdiodev, u32 regaddr) |
211 | { | |
212 | /* read 24 bits and return valid 17 bit addr */ | |
d8b3fc59 | 213 | int i, ret; |
5b435de0 AS |
214 | u32 scratch, regdata; |
215 | __le32 scratch_le; | |
216 | u8 *ptr = (u8 *)&scratch_le; | |
217 | ||
218 | for (i = 0; i < 3; i++) { | |
d8b3fc59 FL |
219 | regdata = brcmf_sdio_regrl(sdiodev, regaddr, &ret); |
220 | if (ret != 0) | |
5e8149f5 | 221 | brcmf_err("Can't read!\n"); |
5b435de0 AS |
222 | |
223 | *ptr++ = (u8) regdata; | |
224 | regaddr++; | |
225 | } | |
226 | ||
227 | /* Only the lower 17-bits are valid */ | |
228 | scratch = le32_to_cpu(scratch_le); | |
229 | scratch &= 0x0001FFFF; | |
230 | return scratch; | |
231 | } | |
232 | ||
233 | static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev) | |
234 | { | |
235 | int err_ret; | |
236 | u32 fbraddr; | |
237 | u8 func; | |
238 | ||
c3203374 | 239 | brcmf_dbg(SDIO, "\n"); |
5b435de0 AS |
240 | |
241 | /* Get the Card's common CIS address */ | |
242 | sdiodev->func_cis_ptr[0] = brcmf_sdioh_get_cisaddr(sdiodev, | |
243 | SDIO_CCCR_CIS); | |
c3203374 | 244 | brcmf_dbg(SDIO, "Card's Common CIS Ptr = 0x%x\n", |
5b435de0 AS |
245 | sdiodev->func_cis_ptr[0]); |
246 | ||
247 | /* Get the Card's function CIS (for each function) */ | |
248 | for (fbraddr = SDIO_FBR_BASE(1), func = 1; | |
249 | func <= sdiodev->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) { | |
250 | sdiodev->func_cis_ptr[func] = | |
251 | brcmf_sdioh_get_cisaddr(sdiodev, SDIO_FBR_CIS + fbraddr); | |
c3203374 | 252 | brcmf_dbg(SDIO, "Function %d CIS Ptr = 0x%x\n", |
5b435de0 AS |
253 | func, sdiodev->func_cis_ptr[func]); |
254 | } | |
255 | ||
256 | /* Enable Function 1 */ | |
5b435de0 | 257 | err_ret = sdio_enable_func(sdiodev->func[1]); |
5b435de0 | 258 | if (err_ret) |
5e8149f5 | 259 | brcmf_err("Failed to enable F1 Err: 0x%08x\n", err_ret); |
5b435de0 AS |
260 | |
261 | return false; | |
262 | } | |
263 | ||
264 | /* | |
265 | * Public entry points & extern's | |
266 | */ | |
267 | int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev) | |
268 | { | |
269 | int err_ret = 0; | |
270 | ||
c3203374 | 271 | brcmf_dbg(SDIO, "\n"); |
5b435de0 AS |
272 | |
273 | sdiodev->num_funcs = 2; | |
274 | ||
275 | sdio_claim_host(sdiodev->func[1]); | |
38b0b0dd | 276 | |
5b435de0 | 277 | err_ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE); |
5b435de0 | 278 | if (err_ret) { |
5e8149f5 | 279 | brcmf_err("Failed to set F1 blocksize\n"); |
5b435de0 AS |
280 | goto out; |
281 | } | |
282 | ||
5b435de0 | 283 | err_ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE); |
5b435de0 | 284 | if (err_ret) { |
5e8149f5 | 285 | brcmf_err("Failed to set F2 blocksize\n"); |
5b435de0 AS |
286 | goto out; |
287 | } | |
288 | ||
289 | brcmf_sdioh_enablefuncs(sdiodev); | |
290 | ||
291 | out: | |
38b0b0dd | 292 | sdio_release_host(sdiodev->func[1]); |
c3203374 | 293 | brcmf_dbg(SDIO, "Done\n"); |
5b435de0 AS |
294 | return err_ret; |
295 | } | |
296 | ||
297 | void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev) | |
298 | { | |
c3203374 | 299 | brcmf_dbg(SDIO, "\n"); |
5b435de0 AS |
300 | |
301 | /* Disable Function 2 */ | |
302 | sdio_claim_host(sdiodev->func[2]); | |
303 | sdio_disable_func(sdiodev->func[2]); | |
304 | sdio_release_host(sdiodev->func[2]); | |
305 | ||
306 | /* Disable Function 1 */ | |
307 | sdio_claim_host(sdiodev->func[1]); | |
308 | sdio_disable_func(sdiodev->func[1]); | |
309 | sdio_release_host(sdiodev->func[1]); | |
310 | ||
311 | } | |
312 | ||
313 | static int brcmf_ops_sdio_probe(struct sdio_func *func, | |
5b3c1832 | 314 | const struct sdio_device_id *id) |
5b435de0 | 315 | { |
5b3c1832 | 316 | int err; |
5b435de0 | 317 | struct brcmf_sdio_dev *sdiodev; |
655713be | 318 | struct brcmf_bus *bus_if; |
71201496 AS |
319 | struct mmc_host *host; |
320 | uint max_blocks; | |
ba89bf19 | 321 | |
c3203374 AS |
322 | brcmf_dbg(SDIO, "Enter\n"); |
323 | brcmf_dbg(SDIO, "Class=%x\n", func->class); | |
324 | brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); | |
325 | brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); | |
326 | brcmf_dbg(SDIO, "Function#: %d\n", func->num); | |
5b3c1832 HM |
327 | |
328 | /* Consume func num 1 but dont do anything with it. */ | |
329 | if (func->num == 1) | |
330 | return 0; | |
331 | ||
332 | /* Ignore anything but func 2 */ | |
333 | if (func->num != 2) | |
334 | return -ENODEV; | |
335 | ||
336 | bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL); | |
337 | if (!bus_if) | |
338 | return -ENOMEM; | |
339 | sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL); | |
340 | if (!sdiodev) { | |
341 | kfree(bus_if); | |
342 | return -ENOMEM; | |
5b435de0 AS |
343 | } |
344 | ||
5b3c1832 HM |
345 | sdiodev->func[0] = func->card->sdio_func[0]; |
346 | sdiodev->func[1] = func->card->sdio_func[0]; | |
347 | sdiodev->func[2] = func; | |
348 | ||
349 | sdiodev->bus_if = bus_if; | |
350 | bus_if->bus_priv.sdio = sdiodev; | |
5b3c1832 HM |
351 | dev_set_drvdata(&func->dev, bus_if); |
352 | dev_set_drvdata(&sdiodev->func[1]->dev, bus_if); | |
353 | sdiodev->dev = &sdiodev->func[1]->dev; | |
668761ac | 354 | sdiodev->pdata = brcmfmac_sdio_pdata; |
5b3c1832 HM |
355 | |
356 | atomic_set(&sdiodev->suspend, false); | |
357 | init_waitqueue_head(&sdiodev->request_byte_wait); | |
358 | init_waitqueue_head(&sdiodev->request_word_wait); | |
5b3c1832 | 359 | init_waitqueue_head(&sdiodev->request_buffer_wait); |
5b3c1832 | 360 | |
c3203374 | 361 | brcmf_dbg(SDIO, "F2 found, calling brcmf_sdio_probe...\n"); |
5b3c1832 HM |
362 | err = brcmf_sdio_probe(sdiodev); |
363 | if (err) { | |
5e8149f5 | 364 | brcmf_err("F2 error, probe failed %d...\n", err); |
5b3c1832 | 365 | goto fail; |
5b435de0 | 366 | } |
71201496 AS |
367 | |
368 | /* | |
369 | * determine host related variables after brcmf_sdio_probe() | |
370 | * as func->cur_blksize is properly set and F2 init has been | |
371 | * completed successfully. | |
372 | */ | |
373 | host = func->card->host; | |
374 | sdiodev->sg_support = host->max_segs > 1; | |
375 | max_blocks = min_t(uint, host->max_blk_count, 511u); | |
376 | sdiodev->max_request_size = min_t(uint, host->max_req_size, | |
377 | max_blocks * func->cur_blksize); | |
378 | sdiodev->max_segment_count = min_t(uint, host->max_segs, | |
379 | SG_MAX_SINGLE_ALLOC); | |
380 | sdiodev->max_segment_size = host->max_seg_size; | |
c3203374 | 381 | brcmf_dbg(SDIO, "F2 init completed...\n"); |
5b3c1832 | 382 | return 0; |
5b435de0 | 383 | |
5b3c1832 HM |
384 | fail: |
385 | dev_set_drvdata(&func->dev, NULL); | |
386 | dev_set_drvdata(&sdiodev->func[1]->dev, NULL); | |
387 | kfree(sdiodev); | |
388 | kfree(bus_if); | |
389 | return err; | |
5b435de0 AS |
390 | } |
391 | ||
392 | static void brcmf_ops_sdio_remove(struct sdio_func *func) | |
393 | { | |
655713be | 394 | struct brcmf_bus *bus_if; |
5b435de0 | 395 | struct brcmf_sdio_dev *sdiodev; |
5b3c1832 | 396 | |
c3203374 AS |
397 | brcmf_dbg(SDIO, "Enter\n"); |
398 | brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); | |
399 | brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); | |
400 | brcmf_dbg(SDIO, "Function: %d\n", func->num); | |
5b435de0 | 401 | |
5b3c1832 HM |
402 | if (func->num != 1 && func->num != 2) |
403 | return; | |
404 | ||
405 | bus_if = dev_get_drvdata(&func->dev); | |
406 | if (bus_if) { | |
0a332e46 | 407 | sdiodev = bus_if->bus_priv.sdio; |
5b435de0 | 408 | brcmf_sdio_remove(sdiodev); |
5b3c1832 HM |
409 | |
410 | dev_set_drvdata(&sdiodev->func[1]->dev, NULL); | |
411 | dev_set_drvdata(&sdiodev->func[2]->dev, NULL); | |
412 | ||
655713be | 413 | kfree(bus_if); |
5b435de0 AS |
414 | kfree(sdiodev); |
415 | } | |
5b3c1832 | 416 | |
c3203374 | 417 | brcmf_dbg(SDIO, "Exit\n"); |
5b435de0 AS |
418 | } |
419 | ||
420 | #ifdef CONFIG_PM_SLEEP | |
421 | static int brcmf_sdio_suspend(struct device *dev) | |
422 | { | |
423 | mmc_pm_flag_t sdio_flags; | |
5b3c1832 HM |
424 | struct brcmf_bus *bus_if = dev_get_drvdata(dev); |
425 | struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; | |
5b435de0 AS |
426 | int ret = 0; |
427 | ||
c3203374 | 428 | brcmf_dbg(SDIO, "\n"); |
5b435de0 | 429 | |
5b435de0 AS |
430 | atomic_set(&sdiodev->suspend, true); |
431 | ||
432 | sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]); | |
433 | if (!(sdio_flags & MMC_PM_KEEP_POWER)) { | |
5e8149f5 | 434 | brcmf_err("Host can't keep power while suspended\n"); |
5b435de0 AS |
435 | return -EINVAL; |
436 | } | |
437 | ||
438 | ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER); | |
439 | if (ret) { | |
5e8149f5 | 440 | brcmf_err("Failed to set pm_flags\n"); |
5b435de0 AS |
441 | return ret; |
442 | } | |
443 | ||
444 | brcmf_sdio_wdtmr_enable(sdiodev, false); | |
445 | ||
446 | return ret; | |
447 | } | |
448 | ||
449 | static int brcmf_sdio_resume(struct device *dev) | |
450 | { | |
5b3c1832 HM |
451 | struct brcmf_bus *bus_if = dev_get_drvdata(dev); |
452 | struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; | |
5b435de0 | 453 | |
5b435de0 AS |
454 | brcmf_sdio_wdtmr_enable(sdiodev, true); |
455 | atomic_set(&sdiodev->suspend, false); | |
456 | return 0; | |
457 | } | |
458 | ||
459 | static const struct dev_pm_ops brcmf_sdio_pm_ops = { | |
460 | .suspend = brcmf_sdio_suspend, | |
461 | .resume = brcmf_sdio_resume, | |
462 | }; | |
463 | #endif /* CONFIG_PM_SLEEP */ | |
464 | ||
465 | static struct sdio_driver brcmf_sdmmc_driver = { | |
466 | .probe = brcmf_ops_sdio_probe, | |
467 | .remove = brcmf_ops_sdio_remove, | |
668761ac | 468 | .name = BRCMFMAC_SDIO_PDATA_NAME, |
5b435de0 AS |
469 | .id_table = brcmf_sdmmc_ids, |
470 | #ifdef CONFIG_PM_SLEEP | |
471 | .drv = { | |
472 | .pm = &brcmf_sdio_pm_ops, | |
473 | }, | |
474 | #endif /* CONFIG_PM_SLEEP */ | |
475 | }; | |
476 | ||
ba89bf19 FL |
477 | static int brcmf_sdio_pd_probe(struct platform_device *pdev) |
478 | { | |
668761ac | 479 | int ret; |
ba89bf19 | 480 | |
668761ac | 481 | brcmf_dbg(SDIO, "Enter\n"); |
ba89bf19 | 482 | |
f4116702 | 483 | brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev); |
ba89bf19 | 484 | |
668761ac HM |
485 | if (brcmfmac_sdio_pdata->power_on) |
486 | brcmfmac_sdio_pdata->power_on(); | |
ba89bf19 FL |
487 | |
488 | ret = sdio_register_driver(&brcmf_sdmmc_driver); | |
ba89bf19 | 489 | if (ret) |
5e8149f5 | 490 | brcmf_err("sdio_register_driver failed: %d\n", ret); |
ba89bf19 FL |
491 | |
492 | return ret; | |
493 | } | |
494 | ||
668761ac | 495 | static int brcmf_sdio_pd_remove(struct platform_device *pdev) |
ba89bf19 | 496 | { |
c3203374 | 497 | brcmf_dbg(SDIO, "Enter\n"); |
ba89bf19 | 498 | |
668761ac HM |
499 | if (brcmfmac_sdio_pdata->power_off) |
500 | brcmfmac_sdio_pdata->power_off(); | |
501 | ||
ba89bf19 FL |
502 | sdio_unregister_driver(&brcmf_sdmmc_driver); |
503 | ||
668761ac | 504 | return 0; |
ba89bf19 FL |
505 | } |
506 | ||
668761ac HM |
507 | static struct platform_driver brcmf_sdio_pd = { |
508 | .remove = brcmf_sdio_pd_remove, | |
509 | .driver = { | |
f91f5f05 LC |
510 | .name = BRCMFMAC_SDIO_PDATA_NAME, |
511 | .owner = THIS_MODULE, | |
668761ac HM |
512 | } |
513 | }; | |
ba89bf19 | 514 | |
f3d7cdc3 | 515 | void brcmf_sdio_exit(void) |
5b435de0 | 516 | { |
c3203374 | 517 | brcmf_dbg(SDIO, "Enter\n"); |
5b435de0 | 518 | |
668761ac HM |
519 | if (brcmfmac_sdio_pdata) |
520 | platform_driver_unregister(&brcmf_sdio_pd); | |
521 | else | |
522 | sdio_unregister_driver(&brcmf_sdmmc_driver); | |
5b435de0 AS |
523 | } |
524 | ||
549040ab | 525 | void brcmf_sdio_init(void) |
5b435de0 | 526 | { |
3392c888 FL |
527 | int ret; |
528 | ||
c3203374 | 529 | brcmf_dbg(SDIO, "Enter\n"); |
5b435de0 | 530 | |
668761ac HM |
531 | ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe); |
532 | if (ret == -ENODEV) { | |
533 | brcmf_dbg(SDIO, "No platform data available, registering without.\n"); | |
534 | ret = sdio_register_driver(&brcmf_sdmmc_driver); | |
535 | } | |
3392c888 FL |
536 | |
537 | if (ret) | |
668761ac | 538 | brcmf_err("driver registration failed: %d\n", ret); |
5b435de0 | 539 | } |