1 /******************************************************************************
2 *
3 * Copyright(c) 2015 - 2017 Realtek Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 *****************************************************************************/
15 #define _RTL8822CS_IO_C_
16
17 #include <drv_types.h> /* PADAPTER and etc. */
18 #include <hal_data.h> /* HAL_DATA_TYPE */
19 #include <rtw_sdio.h> /* rtw_sdio_write_cmd53() */
20 #include <sdio_ops_linux.h> /* SDIO_ERR_VAL8 and etc. */
21 #include "rtl8822cs.h" /* rtl8822cs_get_interrupt(), rtl8822cs_clear_interrupt() and etc. */
22 #include "../../hal_halmac.h" /* rtw_halmac_sdio_get_rx_addr() */
23
24
25 /*
26 * For Core I/O API
27 */
28
sdio_f0_read8(struct intf_hdl * pintfhdl,u32 addr)29 static u8 sdio_f0_read8(struct intf_hdl *pintfhdl, u32 addr)
30 {
31 struct dvobj_priv *d;
32 u8 val = 0;
33 u8 ret;
34
35
36 d = pintfhdl->pintf_dev;
37 ret = rtw_sdio_f0_read(d, addr, &val, 1);
38 if (_FAIL == ret)
39 RTW_ERR("%s: Read f0 register(0x%x) FAIL!\n",
40 __FUNCTION__, addr);
41
42 return val;
43 }
44
45 /*
46 * Description:
47 * Read from RX FIFO
48 * Round read size to block size,
49 * and make sure data transfer will be done in one command.
50 *
51 * Parameters:
52 * d a pointer of dvobj_priv
53 * addr not use
54 * cnt size to write
55 * mem buffer to write
56 *
57 * Return:
58 * _SUCCESS(1) Success
59 * _FAIL(0) Fail
60 */
rtl8822cs_read_port(struct dvobj_priv * d,u32 cnt,u8 * mem)61 u32 rtl8822cs_read_port(struct dvobj_priv *d, u32 cnt, u8 *mem)
62 {
63 struct _ADAPTER *adapter;
64 struct hal_com_data *hal;
65 u32 rxaddr;
66 void *buf;
67 size_t buflen;
68 u32 ret;
69
70
71 adapter = dvobj_get_primary_adapter(d);
72 hal = GET_HAL_DATA(adapter);
73
74 rxaddr = rtw_halmac_sdio_get_rx_addr(d, &hal->SdioRxFIFOCnt);
75 buf = mem;
76
77 /* align size to guarantee I/O would be done in one command */
78 buflen = rtw_sdio_cmd53_align_size(d, cnt);
79 if (buflen != cnt) {
80 buf = rtw_zmalloc(buflen);
81 if (!buf)
82 return _FAIL;
83 }
84
85 ret = rtw_sdio_read_cmd53(d, rxaddr, buf, buflen);
86
87 if (buflen != cnt) {
88 _rtw_memcpy(mem, buf, cnt);
89 rtw_mfree(buf, buflen);
90 }
91
92 return ret;
93 }
94
95 /*
96 * Description:
97 * Read from RX FIFO
98 * Round read size to block size,
99 * and make sure data transfer will be done in one command.
100 *
101 * Parameters:
102 * pintfhdl a pointer of intf_hdl
103 * addr port ID
104 * cnt size to read
105 * mem struct recv_buf*
106 *
107 * Return:
108 * _SUCCESS(1) Success
109 * _FAIL(0) Fail
110 */
sdio_read_port(struct intf_hdl * pintfhdl,u32 addr,u32 cnt,u8 * mem)111 static u32 sdio_read_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *mem)
112 {
113 struct recv_buf *recvbuf;
114
115
116 recvbuf = (struct recv_buf *)mem;
117 return rtl8822cs_read_port(pintfhdl->pintf_dev, cnt, recvbuf->pbuf);
118 }
119
120 /*
121 * Description:
122 * Write to TX FIFO
123 * Align write size to block size,
124 * and check enough FIFO size to write.
125 *
126 * Parameters:
127 * d a pointer of dvobj_priv
128 * addr not use
129 * cnt size to write
130 * mem buffer to write
131 *
132 * Return:
133 * _SUCCESS(1) Success
134 * _FAIL(0) Fail
135 */
rtl8822cs_write_port(struct dvobj_priv * d,u32 cnt,u8 * mem)136 u32 rtl8822cs_write_port(struct dvobj_priv *d, u32 cnt, u8 *mem)
137 {
138 u32 txaddr, txsize;
139 u32 ret = _FAIL;
140
141
142 txaddr = rtw_halmac_sdio_get_tx_addr(d, mem, cnt);
143 if (!txaddr)
144 goto exit;
145 /*
146 * Align size to SDIO IC excpeted,
147 * and this would be done by calling halmac function later.
148 */
149 cnt = _RND4(cnt);
150
151 /* align size to guarantee I/O would be done in one command */
152 txsize = rtw_sdio_cmd53_align_size(d, cnt);
153
154 ret = rtw_sdio_write_cmd53(d, txaddr, mem, txsize);
155
156 exit:
157
158 return ret;
159 }
160
161 /*
162 * Description:
163 * Write to TX FIFO
164 * Align write size to block size,
165 * and check enough FIFO size to write.
166 *
167 * Parameters:
168 * pintfhdl a pointer of intf_hdl
169 * addr not use
170 * cnt size to write
171 * mem struct xmit_buf*
172 *
173 * Return:
174 * _SUCCESS(1) Success
175 * _FAIL(0) Fail
176 */
sdio_write_port(struct intf_hdl * pintfhdl,u32 addr,u32 cnt,u8 * mem)177 static u32 sdio_write_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *mem)
178 {
179 struct dvobj_priv *d;
180 PADAPTER adapter;
181 struct xmit_buf *xmitbuf;
182 u32 txaddr, txsize;
183 u32 ret = _FAIL;
184
185
186 d = pintfhdl->pintf_dev;
187 adapter = pintfhdl->padapter;
188 xmitbuf = (struct xmit_buf *)mem;
189
190 #if 0 /* who will call this when hardware not be initialized? */
191 if (!rtw_is_hw_init_completed(adapter)) {
192 RTW_INFO("%s [addr=0x%x cnt=%d] adapter->hw_init_completed == _FALSE\n",
193 __FUNCTION__, addr, cnt);
194 goto exit;
195 }
196 #endif
197
198 ret = rtl8822cs_write_port(d, cnt, xmitbuf->pdata);
199
200 rtw_sctx_done_err(&xmitbuf->sctx,
201 (_FAIL == ret) ? RTW_SCTX_DONE_WRITE_PORT_ERR : RTW_SCTX_DONE_SUCCESS);
202
203 return ret;
204 }
205
sdio_set_intf_ops(PADAPTER adapter,struct _io_ops * pops)206 void sdio_set_intf_ops(PADAPTER adapter, struct _io_ops *pops)
207 {
208 pops->_read8 = rtw_halmac_read8;
209 pops->_read16 = rtw_halmac_read16;
210 pops->_read32 = rtw_halmac_read32;
211 pops->_read_mem = rtw_halmac_read_mem;
212 pops->_read_port = sdio_read_port;
213
214 pops->_write8 = rtw_halmac_write8;
215 pops->_write16 = rtw_halmac_write16;
216 pops->_write32 = rtw_halmac_write32;
217 pops->_writeN = NULL;
218 pops->_write_mem = NULL;
219 pops->_write_port = sdio_write_port;
220
221 pops->_sd_f0_read8 = sdio_f0_read8;
222
223 #ifdef CONFIG_SDIO_INDIRECT_ACCESS
224 pops->_sd_iread8 = rtw_halmac_iread8;
225 pops->_sd_iread16 = rtw_halmac_iread16;
226 pops->_sd_iread32 = rtw_halmac_iread32;
227 pops->_sd_iwrite8 = rtw_halmac_write8;
228 pops->_sd_iwrite16 = rtw_halmac_write16;
229 pops->_sd_iwrite32 = rtw_halmac_write32;
230 #endif /* CONFIG_SDIO_INDIRECT_ACCESS */
231 }
232
sd_recv_rxfifo(PADAPTER adapter,u32 size)233 static struct recv_buf *sd_recv_rxfifo(PADAPTER adapter, u32 size)
234 {
235 struct dvobj_priv *d;
236 struct recv_priv *recvpriv;
237 struct recv_buf *recvbuf;
238 u32 readsz, bufsz;
239 u8 *rbuf;
240 _pkt *pkt;
241 s32 ret;
242
243
244 d = adapter_to_dvobj(adapter);
245
246 /*
247 * Patch for some SDIO Host 4 bytes issue
248 * ex. RK3188
249 */
250 readsz = RND4(size);
251
252 /* round to block size */
253 bufsz = rtw_sdio_cmd53_align_size(d, readsz);
254
255 /* 1. alloc recvbuf */
256 recvpriv = &adapter->recvpriv;
257 recvbuf = rtw_dequeue_recvbuf(&recvpriv->free_recv_buf_queue);
258 if (recvbuf == NULL) {
259 #ifndef CONFIG_RECV_THREAD_MODE
260 RTW_WARN("%s:alloc recvbuf FAIL!\n", __FUNCTION__);
261 #endif /* !CONFIG_RECV_THREAD_MODE */
262 return NULL;
263 }
264
265 /* 2. alloc skb */
266 pkt = rtl8822cs_alloc_recvbuf_skb(recvbuf, bufsz);
267 if (!pkt) {
268 RTW_ERR("%s: alloc_skb fail! alloc=%d read=%d\n", __FUNCTION__, bufsz, size);
269 rtw_enqueue_recvbuf(recvbuf, &recvpriv->free_recv_buf_queue);
270 return NULL;
271 }
272
273 /* 3. read data from rxfifo */
274 rbuf = skb_put(pkt, size);
275 ret = rtl8822cs_read_port(d, bufsz, rbuf);
276 if (_FAIL == ret) {
277 RTW_ERR("%s: read port FAIL!\n", __FUNCTION__);
278 rtl8822cs_free_recvbuf_skb(recvbuf);
279 rtw_enqueue_recvbuf(recvbuf, &recvpriv->free_recv_buf_queue);
280 return NULL;
281 }
282
283 /* 4. init recvbuf */
284 recvbuf->len = pkt->len;
285 recvbuf->phead = pkt->head;
286 recvbuf->pdata = pkt->data;
287 recvbuf->ptail = skb_tail_pointer(pkt);
288 recvbuf->pend = skb_end_pointer(pkt);
289
290 return recvbuf;
291 }
292 #ifdef CONFIG_RECV_THREAD_MODE
sdio_recv_and_drop(PADAPTER adapter,u32 size)293 static u32 sdio_recv_and_drop(PADAPTER adapter, u32 size)
294 {
295 struct dvobj_priv *d;
296 u32 readsz, bufsz;
297 u8 *rbuf;
298 s32 ret = _SUCCESS;
299
300
301 d = adapter_to_dvobj(adapter);
302
303 /*
304 * Patch for some SDIO Host 4 bytes issue
305 * ex. RK3188
306 */
307 readsz = RND4(size);
308
309 /* round to block size */
310 bufsz = rtw_sdio_cmd53_align_size(d, readsz);
311
312 rbuf = rtw_zmalloc(bufsz);
313 if (NULL == rbuf) {
314 ret = _FAIL;
315 goto _exit;
316 }
317
318 ret = rtl8822cs_read_port(d, bufsz, rbuf);
319 if (_FAIL == ret)
320 RTW_ERR("%s: read port FAIL!\n", __FUNCTION__);
321
322 if (NULL != rbuf)
323 rtw_mfree(rbuf, bufsz);
324
325 _exit:
326 return ret;
327 }
328 #endif
329
sd_int_dpc(PADAPTER adapter)330 void sd_int_dpc(PADAPTER adapter)
331 {
332 PHAL_DATA_TYPE phal;
333 struct dvobj_priv *dvobj;
334 struct pwrctrl_priv *pwrctl;
335
336
337 phal = GET_HAL_DATA(adapter);
338 dvobj = adapter_to_dvobj(adapter);
339 pwrctl = dvobj_to_pwrctl(dvobj);
340
341 #ifdef CONFIG_SDIO_TX_ENABLE_AVAL_INT
342 if (phal->sdio_hisr & BIT_SDIO_AVAL_8822C)
343 _rtw_up_sema(&adapter->xmitpriv.xmit_sema);
344
345 #endif /* CONFIG_SDIO_TX_ENABLE_AVAL_INT */
346
347 if (phal->sdio_hisr & BIT_SDIO_CPWM1_8822C) {
348 struct reportpwrstate_parm report;
349
350 #ifdef CONFIG_LPS_RPWM_TIMER
351 _cancel_timer_ex(&pwrctl->pwr_rpwm_timer);
352 #endif /* CONFIG_LPS_RPWM_TIMER */
353
354 report.state = rtw_read8(adapter, REG_SDIO_HCPWM1_V2_8822C);
355
356 #ifdef CONFIG_LPS_LCLK
357 _set_workitem(&(pwrctl->cpwm_event));
358 #endif /* CONFIG_LPS_LCLK */
359 }
360
361 if (phal->sdio_hisr & BIT_SDIO_TXERR_8822C) {
362 u32 status;
363 u32 addr;
364
365 addr = REG_TXDMA_STATUS_8822C;
366 status = rtw_read32(adapter, addr);
367 rtw_write32(adapter, addr, status);
368
369 RTW_INFO("%s: SDIO_HISR_TXERR (0x%08x)\n", __FUNCTION__, status);
370 }
371
372 if (phal->sdio_hisr & BIT_SDIO_TXBCNOK_8822C)
373 RTW_INFO("%s: SDIO_HISR_TXBCNOK\n", __FUNCTION__);
374
375 if (phal->sdio_hisr & BIT_SDIO_TXBCNERR_8822C)
376 RTW_INFO("%s: SDIO_HISR_TXBCNERR\n", __FUNCTION__);
377
378 if (phal->sdio_hisr & BIT_SDIO_RXFOVW_8822C)
379 RTW_INFO("%s: Rx Overflow\n", __FUNCTION__);
380
381 if (phal->sdio_hisr & BIT_SDIO_RXERR_8822C)
382 RTW_INFO("%s: Rx Error\n", __FUNCTION__);
383 #ifdef CONFIG_SDIO_MULTI_FUNCTION_COEX
384 #if DBG_SDIO_MULTI_FUNCTION_COEX
385 if (phal->sdio_hisr & BIT_BT_INT_8822C)
386 RTW_INFO("%s: SDIO bus resume to available\n", __FUNCTION__);
387 #endif
388 #endif
389
390 if (phal->sdio_hisr & BIT_RX_REQUEST_8822C) {
391 struct recv_buf *precvbuf;
392 int rx_fail_time = 0;
393 u32 rx_len;
394
395 #ifdef CONFIG_SDIO_MULTI_FUNCTION_COEX
396 if (!ex_hal_sdio_multi_if_bus_available(adapter)
397 && !(phal->sdio_hisr & BIT_BT_INT_8822C)
398 ) {
399 #if DBG_SDIO_MULTI_FUNCTION_COEX
400 RTW_INFO("%s: RX entry, SDIO_MULTI_BT && not resume\n", __FUNCTION__);
401 #endif
402 goto rx_end;
403 }
404 #endif
405
406 /* No need to write 1 clear for RX_REQUEST */
407 phal->sdio_hisr ^= BIT_RX_REQUEST_8822C;
408
409 rx_len = phal->SdioRxFIFOSize;
410 do {
411 if (!rx_len)
412 break;
413
414 if (rx_len > MAX_RECVBUF_SZ) {
415 RTW_ERR("%s : rx drop for invalid len %d\n", __func__, rx_len);
416 break;
417 }
418
419 precvbuf = sd_recv_rxfifo(adapter, rx_len);
420 if (precvbuf) {
421 rtl8822cs_rxhandler(adapter, precvbuf);
422
423 } else {
424 rx_fail_time++;
425 #ifdef CONFIG_RECV_THREAD_MODE
426 if (rx_fail_time >= 10) {
427 if (_FAIL == sdio_recv_and_drop(adapter, rx_len))
428 break;
429
430 rx_fail_time = 0;
431 } else if (1
432 #ifdef CONFIG_SDIO_MULTI_FUNCTION_COEX
433 && ex_hal_sdio_multi_if_bus_available(adapter)
434 #endif
435 ) {
436 rtw_msleep_os(1);
437 continue;
438 }
439 #else /* !CONFIG_RECV_THREAD_MODE */
440 RTW_WARN("%s: recv fail!(time=%d)\n", __FUNCTION__, rx_fail_time);
441 if (rx_fail_time >= 10)
442 break;
443 #endif /* !CONFIG_RECV_THREAD_MODE */
444 }
445
446 #ifdef CONFIG_SDIO_MULTI_FUNCTION_COEX
447 if (!ex_hal_sdio_multi_if_bus_available(adapter)) {
448 #if DBG_SDIO_MULTI_FUNCTION_COEX
449 RTW_INFO("%s: RX, SDIO_MULTI_BT\n", __FUNCTION__);
450 #endif
451 break;
452 }
453 #endif
454
455 rx_len = 0;
456 rtl8822cs_get_interrupt(adapter, NULL, &rx_len);
457 } while (1);
458
459 if (rx_fail_time == 10)
460 RTW_ERR("%s: exit because recv failed more than 10 times!\n", __FUNCTION__);
461
462 #ifdef CONFIG_SDIO_MULTI_FUNCTION_COEX
463 rx_end:
464 ;
465 #endif
466 }
467 }
468
sd_int_hdl(PADAPTER adapter)469 void sd_int_hdl(PADAPTER adapter)
470 {
471 PHAL_DATA_TYPE phal;
472 u8 pwr;
473
474
475 if (RTW_CANNOT_RUN(adapter))
476 return;
477
478 phal = GET_HAL_DATA(adapter);
479
480 rtw_hal_get_hwreg(adapter, HW_VAR_APFM_ON_MAC, &pwr);
481 if (pwr != _TRUE) {
482 RTW_WARN("%s: unexpected interrupt!\n", __FUNCTION__);
483 return;
484 }
485
486 rtl8822cs_get_interrupt(adapter, &phal->sdio_hisr, &phal->SdioRxFIFOSize);
487 if (phal->sdio_hisr & phal->sdio_himr) {
488 phal->sdio_hisr &= phal->sdio_himr;
489 sd_int_dpc(adapter);
490 rtl8822cs_clear_interrupt(adapter, phal->sdio_hisr);
491 } else {
492 RTW_DBG("%s: HISR(0x%08x) and HIMR(0x%08x) no match!\n",
493 __FUNCTION__, phal->sdio_hisr, phal->sdio_himr);
494 }
495 }
496
497 #if defined(CONFIG_WOWLAN) || defined(CONFIG_AP_WOWLAN)
rtw_hal_enable_cpwm2(_adapter * adapter)498 u8 rtw_hal_enable_cpwm2(_adapter *adapter)
499 {
500 rtl8822cs_disable_interrupt_but_cpwm2(adapter);
501 return _SUCCESS;
502 }
503
RecvOnePkt(PADAPTER adapter)504 u8 RecvOnePkt(PADAPTER adapter)
505 {
506 struct recv_buf *precvbuf;
507 struct dvobj_priv *psddev;
508 PSDIO_DATA psdio_data;
509 PHAL_DATA_TYPE phal;
510 struct sdio_func *func;
511 u8 res = _TRUE;
512 u32 len = 0;
513
514 if (adapter == NULL) {
515 RTW_ERR("%s: adapter is NULL!\n", __func__);
516 return _FALSE;
517 }
518
519 psddev = adapter->dvobj;
520 psdio_data = &psddev->intf_data;
521 func = psdio_data->func;
522 phal = GET_HAL_DATA(adapter);
523
524 rtl8822cs_get_interrupt(adapter, &phal->sdio_hisr,
525 &phal->SdioRxFIFOSize);
526
527 len = phal->SdioRxFIFOSize;
528
529 RTW_DBG("+%s: hisr: %08x size=%d+\n",
530 __func__, phal->sdio_hisr, phal->SdioRxFIFOSize);
531
532 if (len) {
533 sdio_claim_host(func);
534 precvbuf = sd_recv_rxfifo(adapter, len);
535 if (precvbuf)
536 rtl8822cs_rxhandler(adapter, precvbuf);
537 else
538 res = _FALSE;
539 sdio_release_host(func);
540 }
541 return res;
542 }
543 #endif /* CONFIG_WOWLAN */
544
545