1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /****************************************************************************** |
3 | * |
4 | * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. |
5 | * |
6 | ******************************************************************************/ |
7 | #include <drv_types.h> |
8 | #include <rtw_debug.h> |
9 | #include <hal_btcoex.h> |
10 | #include <linux/jiffies.h> |
11 | |
12 | #ifndef dev_to_sdio_func |
13 | #define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) |
14 | #endif |
15 | |
16 | static const struct sdio_device_id sdio_ids[] = { |
17 | { SDIO_DEVICE(0x024c, 0x0523), }, |
18 | { SDIO_DEVICE(0x024c, 0x0525), }, |
19 | { SDIO_DEVICE(0x024c, 0x0623), }, |
20 | { SDIO_DEVICE(0x024c, 0x0626), }, |
21 | { SDIO_DEVICE(0x024c, 0x0627), }, |
22 | { SDIO_DEVICE(0x024c, 0xb723), }, |
23 | { /* end: all zeroes */ }, |
24 | }; |
25 | MODULE_DEVICE_TABLE(sdio, sdio_ids); |
26 | |
27 | static int rtw_drv_init(struct sdio_func *func, const struct sdio_device_id *id); |
28 | static void rtw_dev_remove(struct sdio_func *func); |
29 | static int rtw_sdio_resume(struct device *dev); |
30 | static int rtw_sdio_suspend(struct device *dev); |
31 | |
32 | static const struct dev_pm_ops rtw_sdio_pm_ops = { |
33 | .suspend = rtw_sdio_suspend, |
34 | .resume = rtw_sdio_resume, |
35 | }; |
36 | |
37 | static struct sdio_driver rtl8723bs_sdio_driver = { |
38 | .probe = rtw_drv_init, |
39 | .remove = rtw_dev_remove, |
40 | .name = "rtl8723bs" , |
41 | .id_table = sdio_ids, |
42 | .drv = { |
43 | .pm = &rtw_sdio_pm_ops, |
44 | } |
45 | }; |
46 | |
47 | static void sd_sync_int_hdl(struct sdio_func *func) |
48 | { |
49 | struct dvobj_priv *psdpriv; |
50 | |
51 | |
52 | psdpriv = sdio_get_drvdata(func); |
53 | |
54 | if (!psdpriv->if1) |
55 | return; |
56 | |
57 | rtw_sdio_set_irq_thd(dvobj: psdpriv, current); |
58 | sd_int_hdl(padapter: psdpriv->if1); |
59 | rtw_sdio_set_irq_thd(dvobj: psdpriv, NULL); |
60 | } |
61 | |
62 | static int sdio_alloc_irq(struct dvobj_priv *dvobj) |
63 | { |
64 | struct sdio_data *psdio_data; |
65 | struct sdio_func *func; |
66 | int err; |
67 | |
68 | psdio_data = &dvobj->intf_data; |
69 | func = psdio_data->func; |
70 | |
71 | sdio_claim_host(func); |
72 | |
73 | err = sdio_claim_irq(func, handler: &sd_sync_int_hdl); |
74 | if (err) { |
75 | dvobj->drv_dbg.dbg_sdio_alloc_irq_error_cnt++; |
76 | printk(KERN_CRIT "%s: sdio_claim_irq FAIL(%d)!\n" , __func__, err); |
77 | } else { |
78 | dvobj->drv_dbg.dbg_sdio_alloc_irq_cnt++; |
79 | dvobj->irq_alloc = 1; |
80 | } |
81 | |
82 | sdio_release_host(func); |
83 | |
84 | return err?_FAIL:_SUCCESS; |
85 | } |
86 | |
87 | static void sdio_free_irq(struct dvobj_priv *dvobj) |
88 | { |
89 | struct sdio_data *psdio_data; |
90 | struct sdio_func *func; |
91 | int err; |
92 | |
93 | if (dvobj->irq_alloc) { |
94 | psdio_data = &dvobj->intf_data; |
95 | func = psdio_data->func; |
96 | |
97 | if (func) { |
98 | sdio_claim_host(func); |
99 | err = sdio_release_irq(func); |
100 | if (err) { |
101 | dvobj->drv_dbg.dbg_sdio_free_irq_error_cnt++; |
102 | netdev_err(dev: dvobj->if1->pnetdev, |
103 | format: "%s: sdio_release_irq FAIL(%d)!\n" , |
104 | __func__, err); |
105 | } else |
106 | dvobj->drv_dbg.dbg_sdio_free_irq_cnt++; |
107 | sdio_release_host(func); |
108 | } |
109 | dvobj->irq_alloc = 0; |
110 | } |
111 | } |
112 | |
113 | static u32 sdio_init(struct dvobj_priv *dvobj) |
114 | { |
115 | struct sdio_data *psdio_data; |
116 | struct sdio_func *func; |
117 | int err; |
118 | |
119 | psdio_data = &dvobj->intf_data; |
120 | func = psdio_data->func; |
121 | |
122 | /* 3 1. init SDIO bus */ |
123 | sdio_claim_host(func); |
124 | |
125 | err = sdio_enable_func(func); |
126 | if (err) { |
127 | dvobj->drv_dbg.dbg_sdio_init_error_cnt++; |
128 | goto release; |
129 | } |
130 | |
131 | err = sdio_set_block_size(func, blksz: 512); |
132 | if (err) { |
133 | dvobj->drv_dbg.dbg_sdio_init_error_cnt++; |
134 | goto release; |
135 | } |
136 | psdio_data->block_transfer_len = 512; |
137 | psdio_data->tx_block_mode = 1; |
138 | psdio_data->rx_block_mode = 1; |
139 | |
140 | release: |
141 | sdio_release_host(func); |
142 | |
143 | if (err) |
144 | return _FAIL; |
145 | return _SUCCESS; |
146 | } |
147 | |
148 | static void sdio_deinit(struct dvobj_priv *dvobj) |
149 | { |
150 | struct sdio_func *func; |
151 | int err; |
152 | |
153 | func = dvobj->intf_data.func; |
154 | |
155 | if (func) { |
156 | sdio_claim_host(func); |
157 | err = sdio_disable_func(func); |
158 | if (err) |
159 | dvobj->drv_dbg.dbg_sdio_deinit_error_cnt++; |
160 | |
161 | if (dvobj->irq_alloc) { |
162 | err = sdio_release_irq(func); |
163 | if (err) |
164 | dvobj->drv_dbg.dbg_sdio_free_irq_error_cnt++; |
165 | else |
166 | dvobj->drv_dbg.dbg_sdio_free_irq_cnt++; |
167 | } |
168 | |
169 | sdio_release_host(func); |
170 | } |
171 | } |
172 | static struct dvobj_priv *sdio_dvobj_init(struct sdio_func *func) |
173 | { |
174 | int status = _FAIL; |
175 | struct dvobj_priv *dvobj = NULL; |
176 | struct sdio_data *psdio; |
177 | |
178 | dvobj = devobj_init(); |
179 | if (!dvobj) |
180 | goto exit; |
181 | |
182 | sdio_set_drvdata(func, dvobj); |
183 | |
184 | psdio = &dvobj->intf_data; |
185 | psdio->func = func; |
186 | |
187 | if (sdio_init(dvobj) != _SUCCESS) |
188 | goto free_dvobj; |
189 | |
190 | rtw_reset_continual_io_error(dvobj); |
191 | status = _SUCCESS; |
192 | |
193 | free_dvobj: |
194 | if (status != _SUCCESS && dvobj) { |
195 | sdio_set_drvdata(func, NULL); |
196 | |
197 | devobj_deinit(pdvobj: dvobj); |
198 | |
199 | dvobj = NULL; |
200 | } |
201 | exit: |
202 | return dvobj; |
203 | } |
204 | |
205 | static void sdio_dvobj_deinit(struct sdio_func *func) |
206 | { |
207 | struct dvobj_priv *dvobj = sdio_get_drvdata(func); |
208 | |
209 | sdio_set_drvdata(func, NULL); |
210 | if (dvobj) { |
211 | sdio_deinit(dvobj); |
212 | devobj_deinit(pdvobj: dvobj); |
213 | } |
214 | } |
215 | |
216 | void rtw_set_hal_ops(struct adapter *padapter) |
217 | { |
218 | /* alloc memory for HAL DATA */ |
219 | rtw_hal_data_init(padapter); |
220 | |
221 | rtl8723bs_set_hal_ops(padapter); |
222 | } |
223 | |
224 | static void sd_intf_start(struct adapter *padapter) |
225 | { |
226 | if (!padapter) |
227 | return; |
228 | |
229 | /* hal dep */ |
230 | rtw_hal_enable_interrupt(padapter); |
231 | } |
232 | |
233 | static void sd_intf_stop(struct adapter *padapter) |
234 | { |
235 | if (!padapter) |
236 | return; |
237 | |
238 | /* hal dep */ |
239 | rtw_hal_disable_interrupt(padapter); |
240 | } |
241 | |
242 | |
243 | static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct sdio_device_id *pdid) |
244 | { |
245 | int status = _FAIL; |
246 | struct net_device *pnetdev; |
247 | struct adapter *padapter = NULL; |
248 | struct sdio_data *psdio = &dvobj->intf_data; |
249 | |
250 | padapter = vzalloc(size: sizeof(*padapter)); |
251 | if (!padapter) |
252 | goto exit; |
253 | |
254 | padapter->dvobj = dvobj; |
255 | dvobj->if1 = padapter; |
256 | |
257 | padapter->bDriverStopped = true; |
258 | |
259 | dvobj->padapters = padapter; |
260 | padapter->iface_id = 0; |
261 | |
262 | /* 3 1. init network device data */ |
263 | pnetdev = rtw_init_netdev(padapter); |
264 | if (!pnetdev) |
265 | goto free_adapter; |
266 | |
267 | SET_NETDEV_DEV(pnetdev, dvobj_to_dev(dvobj)); |
268 | |
269 | padapter = rtw_netdev_priv(netdev: pnetdev); |
270 | |
271 | /* 3 3. init driver special setting, interface, OS and hardware relative */ |
272 | |
273 | /* 4 3.1 set hardware operation functions */ |
274 | rtw_set_hal_ops(padapter); |
275 | |
276 | |
277 | /* 3 5. initialize Chip version */ |
278 | padapter->intf_start = &sd_intf_start; |
279 | padapter->intf_stop = &sd_intf_stop; |
280 | |
281 | padapter->intf_init = &sdio_init; |
282 | padapter->intf_deinit = &sdio_deinit; |
283 | padapter->intf_alloc_irq = &sdio_alloc_irq; |
284 | padapter->intf_free_irq = &sdio_free_irq; |
285 | |
286 | if (rtw_init_io_priv(padapter, set_intf_ops: sdio_set_intf_ops) == _FAIL) |
287 | goto free_hal_data; |
288 | |
289 | rtw_hal_read_chip_version(padapter); |
290 | |
291 | rtw_hal_chip_configure(padapter); |
292 | |
293 | hal_btcoex_Initialize(padapter: (void *) padapter); |
294 | |
295 | /* 3 6. read efuse/eeprom data */ |
296 | rtw_hal_read_chip_info(padapter); |
297 | |
298 | /* 3 7. init driver common data */ |
299 | if (rtw_init_drv_sw(padapter) == _FAIL) |
300 | goto free_hal_data; |
301 | |
302 | rtw_wdev_alloc(padapter, dev: dvobj_to_dev(dvobj)); |
303 | |
304 | /* 3 8. get WLan MAC address */ |
305 | /* set mac addr */ |
306 | rtw_macaddr_cfg(dev: &psdio->func->dev, mac_addr: padapter->eeprompriv.mac_addr); |
307 | |
308 | rtw_hal_disable_interrupt(padapter); |
309 | |
310 | status = _SUCCESS; |
311 | |
312 | free_hal_data: |
313 | if (status != _SUCCESS && padapter->HalData) |
314 | kfree(objp: padapter->HalData); |
315 | |
316 | if (status != _SUCCESS) { |
317 | rtw_wdev_unregister(wdev: padapter->rtw_wdev); |
318 | rtw_wdev_free(wdev: padapter->rtw_wdev); |
319 | } |
320 | |
321 | free_adapter: |
322 | if (status != _SUCCESS) { |
323 | if (pnetdev) |
324 | rtw_free_netdev(netdev: pnetdev); |
325 | else |
326 | vfree(addr: (u8 *)padapter); |
327 | padapter = NULL; |
328 | } |
329 | exit: |
330 | return padapter; |
331 | } |
332 | |
333 | static void rtw_sdio_if1_deinit(struct adapter *if1) |
334 | { |
335 | struct net_device *pnetdev = if1->pnetdev; |
336 | struct mlme_priv *pmlmepriv = &if1->mlmepriv; |
337 | |
338 | if (check_fwstate(pmlmepriv, _FW_LINKED)) |
339 | rtw_disassoc_cmd(padapter: if1, deauth_timeout_ms: 0, enqueue: false); |
340 | |
341 | free_mlme_ap_info(padapter: if1); |
342 | |
343 | rtw_cancel_all_timer(padapter: if1); |
344 | |
345 | rtw_dev_unload(padapter: if1); |
346 | |
347 | if (if1->rtw_wdev) |
348 | rtw_wdev_free(wdev: if1->rtw_wdev); |
349 | |
350 | rtw_free_drv_sw(padapter: if1); |
351 | |
352 | if (pnetdev) |
353 | rtw_free_netdev(netdev: pnetdev); |
354 | } |
355 | |
356 | /* |
357 | * drv_init() - a device potentially for us |
358 | * |
359 | * notes: drv_init() is called when the bus driver has located a card for us to support. |
360 | * We accept the new device by returning 0. |
361 | */ |
362 | static int rtw_drv_init( |
363 | struct sdio_func *func, |
364 | const struct sdio_device_id *id) |
365 | { |
366 | int status = _FAIL; |
367 | struct adapter *if1 = NULL; |
368 | struct dvobj_priv *dvobj; |
369 | |
370 | dvobj = sdio_dvobj_init(func); |
371 | if (!dvobj) |
372 | goto exit; |
373 | |
374 | if1 = rtw_sdio_if1_init(dvobj, pdid: id); |
375 | if (!if1) |
376 | goto free_dvobj; |
377 | |
378 | /* dev_alloc_name && register_netdev */ |
379 | status = rtw_drv_register_netdev(padapter: if1); |
380 | if (status != _SUCCESS) |
381 | goto free_if1; |
382 | |
383 | if (sdio_alloc_irq(dvobj) != _SUCCESS) |
384 | goto free_if1; |
385 | |
386 | rtw_ndev_notifier_register(); |
387 | status = _SUCCESS; |
388 | |
389 | free_if1: |
390 | if (status != _SUCCESS && if1) |
391 | rtw_sdio_if1_deinit(if1); |
392 | |
393 | free_dvobj: |
394 | if (status != _SUCCESS) |
395 | sdio_dvobj_deinit(func); |
396 | exit: |
397 | return status == _SUCCESS ? 0 : -ENODEV; |
398 | } |
399 | |
400 | static void rtw_dev_remove(struct sdio_func *func) |
401 | { |
402 | struct dvobj_priv *dvobj = sdio_get_drvdata(func); |
403 | struct adapter *padapter = dvobj->if1; |
404 | |
405 | dvobj->processing_dev_remove = true; |
406 | |
407 | rtw_unregister_netdevs(dvobj); |
408 | |
409 | if (!padapter->bSurpriseRemoved) { |
410 | int err; |
411 | |
412 | /* test surprise remove */ |
413 | sdio_claim_host(func); |
414 | sdio_readb(func, addr: 0, err_ret: &err); |
415 | sdio_release_host(func); |
416 | if (err == -ENOMEDIUM) |
417 | padapter->bSurpriseRemoved = true; |
418 | } |
419 | |
420 | rtw_ps_deny(padapter, reason: PS_DENY_DRV_REMOVE); |
421 | |
422 | rtw_pm_set_ips(padapter, mode: IPS_NONE); |
423 | rtw_pm_set_lps(padapter, mode: PS_MODE_ACTIVE); |
424 | |
425 | LeaveAllPowerSaveMode(Adapter: padapter); |
426 | |
427 | rtw_btcoex_HaltNotify(padapter); |
428 | |
429 | rtw_sdio_if1_deinit(if1: padapter); |
430 | |
431 | sdio_dvobj_deinit(func); |
432 | } |
433 | |
434 | static int rtw_sdio_suspend(struct device *dev) |
435 | { |
436 | struct sdio_func *func = dev_to_sdio_func(dev); |
437 | struct dvobj_priv *psdpriv = sdio_get_drvdata(func); |
438 | struct pwrctrl_priv *pwrpriv = dvobj_to_pwrctl(psdpriv); |
439 | struct adapter *padapter = psdpriv->if1; |
440 | struct debug_priv *pdbgpriv = &psdpriv->drv_dbg; |
441 | |
442 | if (padapter->bDriverStopped) |
443 | return 0; |
444 | |
445 | if (pwrpriv->bInSuspend) { |
446 | pdbgpriv->dbg_suspend_error_cnt++; |
447 | return 0; |
448 | } |
449 | |
450 | rtw_suspend_common(padapter); |
451 | |
452 | return 0; |
453 | } |
454 | |
455 | static int rtw_resume_process(struct adapter *padapter) |
456 | { |
457 | struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(padapter); |
458 | struct dvobj_priv *psdpriv = padapter->dvobj; |
459 | struct debug_priv *pdbgpriv = &psdpriv->drv_dbg; |
460 | |
461 | if (!pwrpriv->bInSuspend) { |
462 | pdbgpriv->dbg_resume_error_cnt++; |
463 | return -1; |
464 | } |
465 | |
466 | return rtw_resume_common(padapter); |
467 | } |
468 | |
469 | static int rtw_sdio_resume(struct device *dev) |
470 | { |
471 | struct sdio_func *func = dev_to_sdio_func(dev); |
472 | struct dvobj_priv *psdpriv = sdio_get_drvdata(func); |
473 | struct adapter *padapter = psdpriv->if1; |
474 | struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
475 | int ret = 0; |
476 | struct debug_priv *pdbgpriv = &psdpriv->drv_dbg; |
477 | |
478 | pdbgpriv->dbg_resume_cnt++; |
479 | |
480 | ret = rtw_resume_process(padapter); |
481 | |
482 | pmlmeext->last_scan_time = jiffies; |
483 | return ret; |
484 | } |
485 | |
486 | static int __init rtw_drv_entry(void) |
487 | { |
488 | int ret; |
489 | |
490 | ret = sdio_register_driver(&rtl8723bs_sdio_driver); |
491 | if (ret != 0) |
492 | rtw_ndev_notifier_unregister(); |
493 | |
494 | return ret; |
495 | } |
496 | |
497 | static void __exit rtw_drv_halt(void) |
498 | { |
499 | sdio_unregister_driver(&rtl8723bs_sdio_driver); |
500 | |
501 | rtw_ndev_notifier_unregister(); |
502 | } |
503 | |
504 | |
505 | module_init(rtw_drv_entry); |
506 | module_exit(rtw_drv_halt); |
507 | |