| /****************************************************************************** |
| * |
| * Copyright(c) 2015 - 2017 Realtek Corporation. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| *****************************************************************************/ |
| #define _RTL8822BS_IO_C_ |
| |
| #include <drv_types.h> /* PADAPTER and etc. */ |
| #include <hal_data.h> /* HAL_DATA_TYPE */ |
| #include <rtw_sdio.h> /* rtw_sdio_write_cmd53() */ |
| #include <sdio_ops_linux.h> /* SDIO_ERR_VAL8 and etc. */ |
| #include "rtl8822bs.h" /* rtl8822bs_get_interrupt(), rtl8822bs_clear_interrupt() and etc. */ |
| #include "../../hal_halmac.h" /* rtw_halmac_sdio_get_rx_addr() */ |
| |
| |
| /* |
| * Align size to guarantee I/O would be done in one command, |
| * only align TX and RX FIFO size. |
| */ |
| static size_t sdio_cmd53_align_size(size_t len) |
| { |
| u32 domain; |
| |
| |
| if (len <= 512) |
| return len; |
| |
| len = ((len + 511) / 512) * 512; |
| |
| return len; |
| } |
| |
| /* |
| * For Core I/O API |
| */ |
| |
| static u8 sdio_f0_read8(struct intf_hdl *pintfhdl, u32 addr) |
| { |
| struct dvobj_priv *d; |
| u8 val = 0; |
| u8 ret; |
| |
| |
| d = pintfhdl->pintf_dev; |
| ret = rtw_sdio_f0_read(d, addr, &val, 1); |
| if (_FAIL == ret) |
| RTW_ERR("%s: Read f0 register(0x%x) FAIL!\n", |
| __FUNCTION__, addr); |
| |
| return val; |
| } |
| |
| /* |
| * Description: |
| * Read from RX FIFO |
| * Round read size to block size, |
| * and make sure data transfer will be done in one command. |
| * |
| * Parameters: |
| * d a pointer of dvobj_priv |
| * addr not use |
| * cnt size to write |
| * mem buffer to write |
| * |
| * Return: |
| * _SUCCESS(1) Success |
| * _FAIL(0) Fail |
| */ |
| u32 rtl8822bs_read_port(struct dvobj_priv *d, u32 cnt, u8 *mem) |
| { |
| struct _ADAPTER *adapter; |
| struct hal_com_data *hal; |
| u32 rxaddr; |
| void *buf; |
| size_t buflen; |
| u32 ret; |
| |
| |
| adapter = dvobj_get_primary_adapter(d); |
| hal = GET_HAL_DATA(adapter); |
| |
| rxaddr = rtw_halmac_sdio_get_rx_addr(d, &hal->SdioRxFIFOCnt); |
| buf = mem; |
| |
| /* align size to guarantee I/O would be done in one command */ |
| buflen = sdio_cmd53_align_size(cnt); |
| if (buflen != cnt) { |
| buf = rtw_zmalloc(buflen); |
| if (!buf) |
| return _FAIL; |
| } |
| |
| ret = rtw_sdio_read_cmd53(d, rxaddr, buf, buflen); |
| |
| if (buflen != cnt) { |
| _rtw_memcpy(mem, buf, cnt); |
| rtw_mfree(buf, buflen); |
| } |
| |
| return ret; |
| } |
| |
| /* |
| * Description: |
| * Read from RX FIFO |
| * Round read size to block size, |
| * and make sure data transfer will be done in one command. |
| * |
| * Parameters: |
| * pintfhdl a pointer of intf_hdl |
| * addr port ID |
| * cnt size to read |
| * mem struct recv_buf* |
| * |
| * Return: |
| * _SUCCESS(1) Success |
| * _FAIL(0) Fail |
| */ |
| static u32 sdio_read_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *mem) |
| { |
| struct recv_buf *recvbuf; |
| |
| |
| recvbuf = (struct recv_buf *)mem; |
| return rtl8822bs_read_port(pintfhdl->pintf_dev, cnt, recvbuf->pbuf); |
| } |
| |
| /* |
| * Description: |
| * Write to TX FIFO |
| * Align write size to block size, |
| * and check enough FIFO size to write. |
| * |
| * Parameters: |
| * d a pointer of dvobj_priv |
| * addr not use |
| * cnt size to write |
| * mem buffer to write |
| * |
| * Return: |
| * _SUCCESS(1) Success |
| * _FAIL(0) Fail |
| */ |
| u32 rtl8822bs_write_port(struct dvobj_priv *d, u32 cnt, u8 *mem) |
| { |
| u32 txaddr, txsize; |
| u32 ret = _FAIL; |
| |
| |
| txaddr = rtw_halmac_sdio_get_tx_addr(d, mem, cnt); |
| if (!txaddr) |
| goto exit; |
| /* |
| * Align size to SDIO IC excpeted, |
| * and this would be done by calling halmac function later. |
| */ |
| cnt = _RND4(cnt); |
| |
| /* align size to guarantee I/O would be done in one command */ |
| txsize = sdio_cmd53_align_size(cnt); |
| |
| ret = rtw_sdio_write_cmd53(d, txaddr, mem, txsize); |
| |
| exit: |
| |
| return ret; |
| } |
| |
| /* |
| * Description: |
| * Write to TX FIFO |
| * Align write size to block size, |
| * and check enough FIFO size to write. |
| * |
| * Parameters: |
| * pintfhdl a pointer of intf_hdl |
| * addr not use |
| * cnt size to write |
| * mem struct xmit_buf* |
| * |
| * Return: |
| * _SUCCESS(1) Success |
| * _FAIL(0) Fail |
| */ |
| static u32 sdio_write_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *mem) |
| { |
| struct dvobj_priv *d; |
| PADAPTER adapter; |
| struct xmit_buf *xmitbuf; |
| u32 txaddr, txsize; |
| u32 ret = _FAIL; |
| |
| |
| d = pintfhdl->pintf_dev; |
| adapter = pintfhdl->padapter; |
| xmitbuf = (struct xmit_buf *)mem; |
| |
| #if 0 /* who will call this when hardware not be initialized? */ |
| if (!rtw_is_hw_init_completed(adapter)) { |
| RTW_INFO("%s [addr=0x%x cnt=%d] adapter->hw_init_completed == _FALSE\n", |
| __FUNCTION__, addr, cnt); |
| goto exit; |
| } |
| #endif |
| |
| ret = rtl8822bs_write_port(d, cnt, xmitbuf->pdata); |
| |
| exit: |
| rtw_sctx_done_err(&xmitbuf->sctx, |
| (_FAIL == ret) ? RTW_SCTX_DONE_WRITE_PORT_ERR : RTW_SCTX_DONE_SUCCESS); |
| |
| return ret; |
| } |
| |
| void sdio_set_intf_ops(PADAPTER adapter, struct _io_ops *pops) |
| { |
| pops->_read8 = rtw_halmac_read8; |
| pops->_read16 = rtw_halmac_read16; |
| pops->_read32 = rtw_halmac_read32; |
| pops->_read_mem = rtw_halmac_read_mem; |
| pops->_read_port = sdio_read_port; |
| |
| pops->_write8 = rtw_halmac_write8; |
| pops->_write16 = rtw_halmac_write16; |
| pops->_write32 = rtw_halmac_write32; |
| pops->_writeN = NULL; |
| pops->_write_mem = NULL; |
| pops->_write_port = sdio_write_port; |
| |
| pops->_sd_f0_read8 = sdio_f0_read8; |
| |
| #ifdef CONFIG_SDIO_INDIRECT_ACCESS |
| pops->_sd_iread8 = rtw_halmac_iread8; |
| pops->_sd_iread16 = rtw_halmac_iread16; |
| pops->_sd_iread32 = rtw_halmac_iread32; |
| pops->_sd_iwrite8 = rtw_halmac_write8; |
| pops->_sd_iwrite16 = rtw_halmac_write16; |
| pops->_sd_iwrite32 = rtw_halmac_write32; |
| #endif /* CONFIG_SDIO_INDIRECT_ACCESS */ |
| } |
| |
| static struct recv_buf *sd_recv_rxfifo(PADAPTER adapter, u32 size) |
| { |
| struct recv_priv *recvpriv; |
| struct recv_buf *recvbuf; |
| u32 readsz, blksz, bufsz; |
| u8 *rbuf; |
| _pkt *pkt; |
| s32 ret; |
| |
| |
| /* |
| * Patch for some SDIO Host 4 bytes issue |
| * ex. RK3188 |
| */ |
| readsz = RND4(size); |
| |
| /* round to block size */ |
| blksz = adapter_to_dvobj(adapter)->intf_data.block_transfer_len; |
| if (readsz > blksz) |
| bufsz = _RND(readsz, blksz); |
| else |
| bufsz = readsz; |
| |
| /* 1. alloc recvbuf */ |
| recvpriv = &adapter->recvpriv; |
| recvbuf = rtw_dequeue_recvbuf(&recvpriv->free_recv_buf_queue); |
| if (recvbuf == NULL) { |
| #ifndef CONFIG_RECV_THREAD_MODE |
| RTW_WARN("%s:alloc recvbuf FAIL!\n", __FUNCTION__); |
| #endif /* !CONFIG_RECV_THREAD_MODE */ |
| return NULL; |
| } |
| |
| /* 2. alloc skb */ |
| pkt = rtl8822bs_alloc_recvbuf_skb(recvbuf, bufsz); |
| if (!pkt) { |
| RTW_ERR("%s: alloc_skb fail! alloc=%d read=%d\n", __FUNCTION__, bufsz, size); |
| rtw_enqueue_recvbuf(recvbuf, &recvpriv->free_recv_buf_queue); |
| return NULL; |
| } |
| |
| /* 3. read data from rxfifo */ |
| rbuf = skb_put(pkt, size); |
| ret = rtl8822bs_read_port(adapter_to_dvobj(adapter), bufsz, rbuf); |
| if (_FAIL == ret) { |
| RTW_ERR("%s: read port FAIL!\n", __FUNCTION__); |
| rtl8822bs_free_recvbuf_skb(recvbuf); |
| rtw_enqueue_recvbuf(recvbuf, &recvpriv->free_recv_buf_queue); |
| return NULL; |
| } |
| |
| /* 4. init recvbuf */ |
| recvbuf->len = pkt->len; |
| recvbuf->phead = pkt->head; |
| recvbuf->pdata = pkt->data; |
| recvbuf->ptail = skb_tail_pointer(pkt); |
| recvbuf->pend = skb_end_pointer(pkt); |
| |
| return recvbuf; |
| } |
| |
| static u32 sdio_recv_and_drop(PADAPTER adapter, u32 size) |
| { |
| u32 readsz, blksz, bufsz; |
| u8 *rbuf; |
| s32 ret = _SUCCESS; |
| |
| /* |
| * Patch for some SDIO Host 4 bytes issue |
| * ex. RK3188 |
| */ |
| readsz = RND4(size); |
| |
| /* round to block size */ |
| blksz = adapter_to_dvobj(adapter)->intf_data.block_transfer_len; |
| if (readsz > blksz) |
| bufsz = _RND(readsz, blksz); |
| else |
| bufsz = readsz; |
| |
| rbuf = rtw_zmalloc(bufsz); |
| if (NULL == rbuf) { |
| ret = _FAIL; |
| goto _exit; |
| } |
| |
| ret = rtl8822bs_read_port(adapter_to_dvobj(adapter), bufsz, rbuf); |
| if (_FAIL == ret) |
| RTW_ERR("%s: read port FAIL!\n", __FUNCTION__); |
| |
| if (NULL != rbuf) |
| rtw_mfree(rbuf, bufsz); |
| |
| _exit: |
| return ret; |
| } |
| |
| void sd_int_dpc(PADAPTER adapter) |
| { |
| PHAL_DATA_TYPE phal; |
| struct dvobj_priv *dvobj; |
| struct pwrctrl_priv *pwrctl; |
| |
| |
| phal = GET_HAL_DATA(adapter); |
| dvobj = adapter_to_dvobj(adapter); |
| pwrctl = dvobj_to_pwrctl(dvobj); |
| |
| #ifdef CONFIG_SDIO_TX_ENABLE_AVAL_INT |
| if (phal->sdio_hisr & BIT_SDIO_AVAL_8822B) |
| _rtw_up_sema(&adapter->xmitpriv.xmit_sema); |
| |
| #endif /* CONFIG_SDIO_TX_ENABLE_AVAL_INT */ |
| |
| if (phal->sdio_hisr & BIT_SDIO_CPWM1_8822B) { |
| struct reportpwrstate_parm report; |
| |
| #ifdef CONFIG_LPS_RPWM_TIMER |
| _cancel_timer_ex(&pwrctl->pwr_rpwm_timer); |
| #endif /* CONFIG_LPS_RPWM_TIMER */ |
| |
| report.state = rtw_read8(adapter, REG_SDIO_HCPWM1_V2_8822B); |
| |
| #ifdef CONFIG_LPS_LCLK |
| _set_workitem(&(pwrctl->cpwm_event)); |
| #endif /* CONFIG_LPS_LCLK */ |
| } |
| |
| if (phal->sdio_hisr & BIT_SDIO_TXERR_8822B) { |
| u32 status; |
| u32 addr; |
| |
| addr = REG_TXDMA_STATUS_8822B; |
| status = rtw_read32(adapter, addr); |
| rtw_write32(adapter, addr, status); |
| |
| RTW_INFO("%s: SDIO_HISR_TXERR (0x%08x)\n", __FUNCTION__, status); |
| } |
| |
| if (phal->sdio_hisr & BIT_SDIO_TXBCNOK_8822B) |
| RTW_INFO("%s: SDIO_HISR_TXBCNOK\n", __FUNCTION__); |
| |
| if (phal->sdio_hisr & BIT_SDIO_TXBCNERR_8822B) |
| RTW_INFO("%s: SDIO_HISR_TXBCNERR\n", __FUNCTION__); |
| |
| if (phal->sdio_hisr & BIT_SDIO_RXFOVW_8822B) |
| RTW_INFO("%s: Rx Overflow\n", __FUNCTION__); |
| |
| if (phal->sdio_hisr & BIT_SDIO_RXERR_8822B) |
| RTW_INFO("%s: Rx Error\n", __FUNCTION__); |
| |
| if (phal->sdio_hisr & BIT_RX_REQUEST_8822B) { |
| struct recv_buf *precvbuf; |
| int rx_fail_time = 0; |
| u16 rx_len; |
| |
| |
| /* No need to write 1 clear for RX_REQUEST */ |
| phal->sdio_hisr ^= BIT_RX_REQUEST_8822B; |
| |
| rx_len = phal->SdioRxFIFOSize; |
| do { |
| if (!rx_len) |
| break; |
| |
| precvbuf = sd_recv_rxfifo(adapter, rx_len); |
| if (precvbuf) { |
| rtl8822bs_rxhandler(adapter, precvbuf); |
| } else { |
| rx_fail_time++; |
| #ifdef CONFIG_RECV_THREAD_MODE |
| if (rx_fail_time >= 10) { |
| if (_FAIL == sdio_recv_and_drop(adapter, rx_len)) |
| break; |
| |
| rx_fail_time = 0; |
| } else { |
| rtw_msleep_os(1); |
| continue; |
| } |
| #else /* !CONFIG_RECV_THREAD_MODE */ |
| RTW_WARN("%s: recv fail!(time=%d)\n", __FUNCTION__, rx_fail_time); |
| if (rx_fail_time >= 10) |
| break; |
| #endif /* !CONFIG_RECV_THREAD_MODE */ |
| } |
| |
| rx_len = 0; |
| rtl8822bs_get_interrupt(adapter, NULL, &rx_len); |
| } while (1); |
| |
| if (rx_fail_time == 10) |
| RTW_ERR("%s: exit because recv failed more than 10 times!\n", __FUNCTION__); |
| } |
| } |
| |
| void sd_int_hdl(PADAPTER adapter) |
| { |
| PHAL_DATA_TYPE phal; |
| |
| |
| if (RTW_CANNOT_RUN(adapter)) |
| return; |
| |
| phal = GET_HAL_DATA(adapter); |
| |
| if (!phal->sdio_himr) { |
| RTW_WARN("%s: unexpected interrupt!\n", __FUNCTION__); |
| return; |
| } |
| |
| rtl8822bs_get_interrupt(adapter, &phal->sdio_hisr, &phal->SdioRxFIFOSize); |
| if (phal->sdio_hisr & phal->sdio_himr) { |
| phal->sdio_hisr &= phal->sdio_himr; |
| sd_int_dpc(adapter); |
| rtl8822bs_clear_interrupt(adapter, phal->sdio_hisr); |
| } |
| #if 0 |
| else { |
| RTW_INFO("%s: HISR(0x%08x) and HIMR(0x%08x) no match!\n", |
| __FUNCTION__, phal->sdio_hisr, phal->sdio_himr); |
| } |
| #endif |
| } |
| |
| #if defined(CONFIG_WOWLAN) || defined(CONFIG_AP_WOWLAN) |
| u8 rtw_hal_enable_cpwm2(_adapter *adapter) |
| { |
| rtl8822bs_disable_interrupt_but_cpwm2(adapter); |
| return _SUCCESS; |
| } |
| |
| u8 RecvOnePkt(PADAPTER adapter) |
| { |
| struct recv_buf *precvbuf; |
| struct dvobj_priv *psddev; |
| PSDIO_DATA psdio_data; |
| PHAL_DATA_TYPE phal; |
| struct sdio_func *func; |
| u8 res = _TRUE; |
| u32 len = 0; |
| |
| if (adapter == NULL) { |
| RTW_ERR("%s: adapter is NULL!\n", __func__); |
| return _FALSE; |
| } |
| |
| psddev = adapter->dvobj; |
| psdio_data = &psddev->intf_data; |
| func = psdio_data->func; |
| phal = GET_HAL_DATA(adapter); |
| |
| rtl8822bs_get_interrupt(adapter, &phal->sdio_hisr, |
| &phal->SdioRxFIFOSize); |
| |
| len = phal->SdioRxFIFOSize; |
| |
| RTW_DBG("+%s: hisr: %08x size=%d+\n", |
| __func__, phal->sdio_hisr, phal->SdioRxFIFOSize); |
| |
| if (len) { |
| sdio_claim_host(func); |
| precvbuf = sd_recv_rxfifo(adapter, len); |
| if (precvbuf) |
| rtl8822bs_rxhandler(adapter, precvbuf); |
| else |
| res = _FALSE; |
| sdio_release_host(func); |
| } |
| return res; |
| } |
| #endif /* CONFIG_WOWLAN */ |
| |