| /****************************************************************************** |
| * |
| * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. |
| * |
| * 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 _SDIO_OPS_C_ |
| |
| #include <drv_types.h> |
| #include <rtw_debug.h> |
| #include <rtl8723b_hal.h> |
| |
| /* define SDIO_DEBUG_IO 1 */ |
| |
| |
| /* */ |
| /* Description: */ |
| /* The following mapping is for SDIO host local register space. */ |
| /* */ |
| /* Creadted by Roger, 2011.01.31. */ |
| /* */ |
| static void HalSdioGetCmdAddr8723BSdio( |
| struct adapter *padapter, |
| u8 DeviceID, |
| u32 Addr, |
| u32 *pCmdAddr |
| ) |
| { |
| switch (DeviceID) { |
| case SDIO_LOCAL_DEVICE_ID: |
| *pCmdAddr = ((SDIO_LOCAL_DEVICE_ID << 13) | (Addr & SDIO_LOCAL_MSK)); |
| break; |
| |
| case WLAN_IOREG_DEVICE_ID: |
| *pCmdAddr = ((WLAN_IOREG_DEVICE_ID << 13) | (Addr & WLAN_IOREG_MSK)); |
| break; |
| |
| case WLAN_TX_HIQ_DEVICE_ID: |
| *pCmdAddr = ((WLAN_TX_HIQ_DEVICE_ID << 13) | (Addr & WLAN_FIFO_MSK)); |
| break; |
| |
| case WLAN_TX_MIQ_DEVICE_ID: |
| *pCmdAddr = ((WLAN_TX_MIQ_DEVICE_ID << 13) | (Addr & WLAN_FIFO_MSK)); |
| break; |
| |
| case WLAN_TX_LOQ_DEVICE_ID: |
| *pCmdAddr = ((WLAN_TX_LOQ_DEVICE_ID << 13) | (Addr & WLAN_FIFO_MSK)); |
| break; |
| |
| case WLAN_RX0FF_DEVICE_ID: |
| *pCmdAddr = ((WLAN_RX0FF_DEVICE_ID << 13) | (Addr & WLAN_RX0FF_MSK)); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| static u8 get_deviceid(u32 addr) |
| { |
| u8 devideId; |
| u16 pseudoId; |
| |
| |
| pseudoId = (u16)(addr >> 16); |
| switch (pseudoId) { |
| case 0x1025: |
| devideId = SDIO_LOCAL_DEVICE_ID; |
| break; |
| |
| case 0x1026: |
| devideId = WLAN_IOREG_DEVICE_ID; |
| break; |
| |
| /* case 0x1027: */ |
| /* devideId = SDIO_FIRMWARE_FIFO; */ |
| /* break; */ |
| |
| case 0x1031: |
| devideId = WLAN_TX_HIQ_DEVICE_ID; |
| break; |
| |
| case 0x1032: |
| devideId = WLAN_TX_MIQ_DEVICE_ID; |
| break; |
| |
| case 0x1033: |
| devideId = WLAN_TX_LOQ_DEVICE_ID; |
| break; |
| |
| case 0x1034: |
| devideId = WLAN_RX0FF_DEVICE_ID; |
| break; |
| |
| default: |
| /* devideId = (u8)((addr >> 13) & 0xF); */ |
| devideId = WLAN_IOREG_DEVICE_ID; |
| break; |
| } |
| |
| return devideId; |
| } |
| |
| /* |
| * Ref: |
| *HalSdioGetCmdAddr8723BSdio() |
| */ |
| static u32 _cvrt2ftaddr(const u32 addr, u8 *pdeviceId, u16 *poffset) |
| { |
| u8 deviceId; |
| u16 offset; |
| u32 ftaddr; |
| |
| |
| deviceId = get_deviceid(addr); |
| offset = 0; |
| |
| switch (deviceId) { |
| case SDIO_LOCAL_DEVICE_ID: |
| offset = addr & SDIO_LOCAL_MSK; |
| break; |
| |
| case WLAN_TX_HIQ_DEVICE_ID: |
| case WLAN_TX_MIQ_DEVICE_ID: |
| case WLAN_TX_LOQ_DEVICE_ID: |
| offset = addr & WLAN_FIFO_MSK; |
| break; |
| |
| case WLAN_RX0FF_DEVICE_ID: |
| offset = addr & WLAN_RX0FF_MSK; |
| break; |
| |
| case WLAN_IOREG_DEVICE_ID: |
| default: |
| deviceId = WLAN_IOREG_DEVICE_ID; |
| offset = addr & WLAN_IOREG_MSK; |
| break; |
| } |
| ftaddr = (deviceId << 13) | offset; |
| |
| if (pdeviceId) |
| *pdeviceId = deviceId; |
| if (poffset) |
| *poffset = offset; |
| |
| return ftaddr; |
| } |
| |
| static u8 sdio_read8(struct intf_hdl *pintfhdl, u32 addr) |
| { |
| u32 ftaddr; |
| u8 val; |
| |
| ftaddr = _cvrt2ftaddr(addr, NULL, NULL); |
| val = sd_read8(pintfhdl, ftaddr, NULL); |
| return val; |
| } |
| |
| static u16 sdio_read16(struct intf_hdl *pintfhdl, u32 addr) |
| { |
| u32 ftaddr; |
| u16 val; |
| __le16 le_tmp; |
| |
| ftaddr = _cvrt2ftaddr(addr, NULL, NULL); |
| sd_cmd52_read(pintfhdl, ftaddr, 2, (u8 *)&le_tmp); |
| val = le16_to_cpu(le_tmp); |
| return val; |
| } |
| |
| static u32 sdio_read32(struct intf_hdl *pintfhdl, u32 addr) |
| { |
| struct adapter *padapter; |
| u8 bMacPwrCtrlOn; |
| u8 deviceId; |
| u16 offset; |
| u32 ftaddr; |
| u8 shift; |
| u32 val; |
| s32 err; |
| __le32 le_tmp; |
| |
| padapter = pintfhdl->padapter; |
| ftaddr = _cvrt2ftaddr(addr, &deviceId, &offset); |
| |
| rtw_hal_get_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); |
| if ( |
| ((deviceId == WLAN_IOREG_DEVICE_ID) && (offset < 0x100)) || |
| (false == bMacPwrCtrlOn) || |
| (true == adapter_to_pwrctl(padapter)->bFwCurrentInPSMode) |
| ) { |
| err = sd_cmd52_read(pintfhdl, ftaddr, 4, (u8 *)&le_tmp); |
| #ifdef SDIO_DEBUG_IO |
| if (!err) { |
| #endif |
| val = le32_to_cpu(le_tmp); |
| return val; |
| #ifdef SDIO_DEBUG_IO |
| } |
| |
| DBG_8192C(KERN_ERR "%s: Mac Power off, Read FAIL(%d)! addr = 0x%x\n", __func__, err, addr); |
| return SDIO_ERR_VAL32; |
| #endif |
| } |
| |
| /* 4 bytes alignment */ |
| shift = ftaddr & 0x3; |
| if (shift == 0) { |
| val = sd_read32(pintfhdl, ftaddr, NULL); |
| } else { |
| u8 *ptmpbuf; |
| |
| ptmpbuf = (u8 *)rtw_malloc(8); |
| if (NULL == ptmpbuf) { |
| DBG_8192C(KERN_ERR "%s: Allocate memory FAIL!(size =8) addr = 0x%x\n", __func__, addr); |
| return SDIO_ERR_VAL32; |
| } |
| |
| ftaddr &= ~(u16)0x3; |
| sd_read(pintfhdl, ftaddr, 8, ptmpbuf); |
| memcpy(&le_tmp, ptmpbuf+shift, 4); |
| val = le32_to_cpu(le_tmp); |
| |
| kfree(ptmpbuf); |
| } |
| return val; |
| } |
| |
| static s32 sdio_readN(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *pbuf) |
| { |
| struct adapter *padapter; |
| u8 bMacPwrCtrlOn; |
| u8 deviceId; |
| u16 offset; |
| u32 ftaddr; |
| u8 shift; |
| s32 err; |
| |
| padapter = pintfhdl->padapter; |
| err = 0; |
| |
| ftaddr = _cvrt2ftaddr(addr, &deviceId, &offset); |
| |
| rtw_hal_get_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); |
| if ( |
| ((deviceId == WLAN_IOREG_DEVICE_ID) && (offset < 0x100)) || |
| (false == bMacPwrCtrlOn) || |
| (true == adapter_to_pwrctl(padapter)->bFwCurrentInPSMode) |
| ) { |
| err = sd_cmd52_read(pintfhdl, ftaddr, cnt, pbuf); |
| return err; |
| } |
| |
| /* 4 bytes alignment */ |
| shift = ftaddr & 0x3; |
| if (shift == 0) { |
| err = sd_read(pintfhdl, ftaddr, cnt, pbuf); |
| } else { |
| u8 *ptmpbuf; |
| u32 n; |
| |
| ftaddr &= ~(u16)0x3; |
| n = cnt + shift; |
| ptmpbuf = rtw_malloc(n); |
| if (NULL == ptmpbuf) |
| return -1; |
| |
| err = sd_read(pintfhdl, ftaddr, n, ptmpbuf); |
| if (!err) |
| memcpy(pbuf, ptmpbuf+shift, cnt); |
| kfree(ptmpbuf); |
| } |
| return err; |
| } |
| |
| static s32 sdio_write8(struct intf_hdl *pintfhdl, u32 addr, u8 val) |
| { |
| u32 ftaddr; |
| s32 err; |
| |
| ftaddr = _cvrt2ftaddr(addr, NULL, NULL); |
| sd_write8(pintfhdl, ftaddr, val, &err); |
| |
| return err; |
| } |
| |
| static s32 sdio_write16(struct intf_hdl *pintfhdl, u32 addr, u16 val) |
| { |
| u32 ftaddr; |
| s32 err; |
| __le16 le_tmp; |
| |
| ftaddr = _cvrt2ftaddr(addr, NULL, NULL); |
| le_tmp = cpu_to_le16(val); |
| err = sd_cmd52_write(pintfhdl, ftaddr, 2, (u8 *)&le_tmp); |
| |
| return err; |
| } |
| |
| static s32 sdio_write32(struct intf_hdl *pintfhdl, u32 addr, u32 val) |
| { |
| struct adapter *padapter; |
| u8 bMacPwrCtrlOn; |
| u8 deviceId; |
| u16 offset; |
| u32 ftaddr; |
| u8 shift; |
| s32 err; |
| __le32 le_tmp; |
| |
| padapter = pintfhdl->padapter; |
| err = 0; |
| |
| ftaddr = _cvrt2ftaddr(addr, &deviceId, &offset); |
| |
| rtw_hal_get_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); |
| if ( |
| ((deviceId == WLAN_IOREG_DEVICE_ID) && (offset < 0x100)) || |
| (!bMacPwrCtrlOn) || |
| (adapter_to_pwrctl(padapter)->bFwCurrentInPSMode) |
| ) { |
| le_tmp = cpu_to_le32(val); |
| err = sd_cmd52_write(pintfhdl, ftaddr, 4, (u8 *)&le_tmp); |
| return err; |
| } |
| |
| /* 4 bytes alignment */ |
| shift = ftaddr & 0x3; |
| if (shift == 0) { |
| sd_write32(pintfhdl, ftaddr, val, &err); |
| } else { |
| le_tmp = cpu_to_le32(val); |
| err = sd_cmd52_write(pintfhdl, ftaddr, 4, (u8 *)&le_tmp); |
| } |
| return err; |
| } |
| |
| static s32 sdio_writeN(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *pbuf) |
| { |
| struct adapter *padapter; |
| u8 bMacPwrCtrlOn; |
| u8 deviceId; |
| u16 offset; |
| u32 ftaddr; |
| u8 shift; |
| s32 err; |
| |
| padapter = pintfhdl->padapter; |
| err = 0; |
| |
| ftaddr = _cvrt2ftaddr(addr, &deviceId, &offset); |
| |
| rtw_hal_get_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); |
| if ( |
| ((deviceId == WLAN_IOREG_DEVICE_ID) && (offset < 0x100)) || |
| (false == bMacPwrCtrlOn) || |
| (true == adapter_to_pwrctl(padapter)->bFwCurrentInPSMode) |
| ) { |
| err = sd_cmd52_write(pintfhdl, ftaddr, cnt, pbuf); |
| return err; |
| } |
| |
| shift = ftaddr & 0x3; |
| if (shift == 0) { |
| err = sd_write(pintfhdl, ftaddr, cnt, pbuf); |
| } else { |
| u8 *ptmpbuf; |
| u32 n; |
| |
| ftaddr &= ~(u16)0x3; |
| n = cnt + shift; |
| ptmpbuf = rtw_malloc(n); |
| if (NULL == ptmpbuf) |
| return -1; |
| err = sd_read(pintfhdl, ftaddr, 4, ptmpbuf); |
| if (err) { |
| kfree(ptmpbuf); |
| return err; |
| } |
| memcpy(ptmpbuf+shift, pbuf, cnt); |
| err = sd_write(pintfhdl, ftaddr, n, ptmpbuf); |
| kfree(ptmpbuf); |
| } |
| return err; |
| } |
| |
| static u8 sdio_f0_read8(struct intf_hdl *pintfhdl, u32 addr) |
| { |
| return sd_f0_read8(pintfhdl, addr, NULL); |
| } |
| |
| static void sdio_read_mem( |
| struct intf_hdl *pintfhdl, |
| u32 addr, |
| u32 cnt, |
| u8 *rmem |
| ) |
| { |
| s32 err; |
| |
| err = sdio_readN(pintfhdl, addr, cnt, rmem); |
| /* TODO: Report error is err not zero */ |
| } |
| |
| static void sdio_write_mem( |
| struct intf_hdl *pintfhdl, |
| u32 addr, |
| u32 cnt, |
| u8 *wmem |
| ) |
| { |
| sdio_writeN(pintfhdl, addr, cnt, wmem); |
| } |
| |
| /* |
| * 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 |
| *rmem address to put data |
| * |
| * Return: |
| *_SUCCESS(1) Success |
| *_FAIL(0) Fail |
| */ |
| static u32 sdio_read_port( |
| struct intf_hdl *pintfhdl, |
| u32 addr, |
| u32 cnt, |
| u8 *mem |
| ) |
| { |
| struct adapter *padapter; |
| PSDIO_DATA psdio; |
| struct hal_com_data *phal; |
| u32 oldcnt; |
| #ifdef SDIO_DYNAMIC_ALLOC_MEM |
| u8 *oldmem; |
| #endif |
| s32 err; |
| |
| |
| padapter = pintfhdl->padapter; |
| psdio = &adapter_to_dvobj(padapter)->intf_data; |
| phal = GET_HAL_DATA(padapter); |
| |
| HalSdioGetCmdAddr8723BSdio(padapter, addr, phal->SdioRxFIFOCnt++, &addr); |
| |
| oldcnt = cnt; |
| if (cnt > psdio->block_transfer_len) |
| cnt = _RND(cnt, psdio->block_transfer_len); |
| /* cnt = sdio_align_size(cnt); */ |
| |
| if (oldcnt != cnt) { |
| #ifdef SDIO_DYNAMIC_ALLOC_MEM |
| oldmem = mem; |
| mem = rtw_malloc(cnt); |
| if (mem == NULL) { |
| DBG_8192C(KERN_WARNING "%s: allocate memory %d bytes fail!\n", __func__, cnt); |
| mem = oldmem; |
| oldmem == NULL; |
| } |
| #else |
| /* in this case, caller should gurante the buffer is big enough */ |
| /* to receive data after alignment */ |
| #endif |
| } |
| |
| err = _sd_read(pintfhdl, addr, cnt, mem); |
| |
| #ifdef SDIO_DYNAMIC_ALLOC_MEM |
| if ((oldcnt != cnt) && (oldmem)) { |
| memcpy(oldmem, mem, oldcnt); |
| kfree(mem); |
| } |
| #endif |
| |
| if (err) |
| return _FAIL; |
| return _SUCCESS; |
| } |
| |
| /* |
| * Description: |
| *Write to TX FIFO |
| *Align write size block size, |
| *and make sure data could be written in one command. |
| * |
| * Parameters: |
| *pintfhdl a pointer of intf_hdl |
| *addr port ID |
| *cnt size to write |
| *wmem data pointer to write |
| * |
| * Return: |
| *_SUCCESS(1) Success |
| *_FAIL(0) Fail |
| */ |
| static u32 sdio_write_port( |
| struct intf_hdl *pintfhdl, |
| u32 addr, |
| u32 cnt, |
| u8 *mem |
| ) |
| { |
| struct adapter *padapter; |
| PSDIO_DATA psdio; |
| s32 err; |
| struct xmit_buf *xmitbuf = (struct xmit_buf *)mem; |
| |
| padapter = pintfhdl->padapter; |
| psdio = &adapter_to_dvobj(padapter)->intf_data; |
| |
| if (padapter->hw_init_completed == false) { |
| DBG_871X("%s [addr = 0x%x cnt =%d] padapter->hw_init_completed == false\n", __func__, addr, cnt); |
| return _FAIL; |
| } |
| |
| cnt = _RND4(cnt); |
| HalSdioGetCmdAddr8723BSdio(padapter, addr, cnt >> 2, &addr); |
| |
| if (cnt > psdio->block_transfer_len) |
| cnt = _RND(cnt, psdio->block_transfer_len); |
| /* cnt = sdio_align_size(cnt); */ |
| |
| err = sd_write(pintfhdl, addr, cnt, xmitbuf->pdata); |
| |
| rtw_sctx_done_err( |
| &xmitbuf->sctx, |
| err ? RTW_SCTX_DONE_WRITE_PORT_ERR : RTW_SCTX_DONE_SUCCESS |
| ); |
| |
| if (err) |
| return _FAIL; |
| return _SUCCESS; |
| } |
| |
| void sdio_set_intf_ops(struct adapter *padapter, struct _io_ops *pops) |
| { |
| pops->_read8 = &sdio_read8; |
| pops->_read16 = &sdio_read16; |
| pops->_read32 = &sdio_read32; |
| pops->_read_mem = &sdio_read_mem; |
| pops->_read_port = &sdio_read_port; |
| |
| pops->_write8 = &sdio_write8; |
| pops->_write16 = &sdio_write16; |
| pops->_write32 = &sdio_write32; |
| pops->_writeN = &sdio_writeN; |
| pops->_write_mem = &sdio_write_mem; |
| pops->_write_port = &sdio_write_port; |
| |
| pops->_sd_f0_read8 = sdio_f0_read8; |
| } |
| |
| /* |
| * Todo: align address to 4 bytes. |
| */ |
| static s32 _sdio_local_read( |
| struct adapter *padapter, |
| u32 addr, |
| u32 cnt, |
| u8 *pbuf |
| ) |
| { |
| struct intf_hdl *pintfhdl; |
| u8 bMacPwrCtrlOn; |
| s32 err; |
| u8 *ptmpbuf; |
| u32 n; |
| |
| |
| pintfhdl = &padapter->iopriv.intf; |
| |
| HalSdioGetCmdAddr8723BSdio(padapter, SDIO_LOCAL_DEVICE_ID, addr, &addr); |
| |
| rtw_hal_get_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); |
| if (false == bMacPwrCtrlOn) { |
| err = _sd_cmd52_read(pintfhdl, addr, cnt, pbuf); |
| return err; |
| } |
| |
| n = RND4(cnt); |
| ptmpbuf = (u8 *)rtw_malloc(n); |
| if (!ptmpbuf) |
| return (-1); |
| |
| err = _sd_read(pintfhdl, addr, n, ptmpbuf); |
| if (!err) |
| memcpy(pbuf, ptmpbuf, cnt); |
| |
| kfree(ptmpbuf); |
| |
| return err; |
| } |
| |
| /* |
| * Todo: align address to 4 bytes. |
| */ |
| s32 sdio_local_read( |
| struct adapter *padapter, |
| u32 addr, |
| u32 cnt, |
| u8 *pbuf |
| ) |
| { |
| struct intf_hdl *pintfhdl; |
| u8 bMacPwrCtrlOn; |
| s32 err; |
| u8 *ptmpbuf; |
| u32 n; |
| |
| pintfhdl = &padapter->iopriv.intf; |
| |
| HalSdioGetCmdAddr8723BSdio(padapter, SDIO_LOCAL_DEVICE_ID, addr, &addr); |
| |
| rtw_hal_get_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); |
| if ( |
| (false == bMacPwrCtrlOn) || |
| (true == adapter_to_pwrctl(padapter)->bFwCurrentInPSMode) |
| ) { |
| err = sd_cmd52_read(pintfhdl, addr, cnt, pbuf); |
| return err; |
| } |
| |
| n = RND4(cnt); |
| ptmpbuf = (u8 *)rtw_malloc(n); |
| if (!ptmpbuf) |
| return (-1); |
| |
| err = sd_read(pintfhdl, addr, n, ptmpbuf); |
| if (!err) |
| memcpy(pbuf, ptmpbuf, cnt); |
| |
| kfree(ptmpbuf); |
| |
| return err; |
| } |
| |
| /* |
| * Todo: align address to 4 bytes. |
| */ |
| s32 sdio_local_write( |
| struct adapter *padapter, |
| u32 addr, |
| u32 cnt, |
| u8 *pbuf |
| ) |
| { |
| struct intf_hdl *pintfhdl; |
| u8 bMacPwrCtrlOn; |
| s32 err; |
| u8 *ptmpbuf; |
| |
| if (addr & 0x3) |
| DBG_8192C("%s, address must be 4 bytes alignment\n", __func__); |
| |
| if (cnt & 0x3) |
| DBG_8192C("%s, size must be the multiple of 4\n", __func__); |
| |
| pintfhdl = &padapter->iopriv.intf; |
| |
| HalSdioGetCmdAddr8723BSdio(padapter, SDIO_LOCAL_DEVICE_ID, addr, &addr); |
| |
| rtw_hal_get_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); |
| if ( |
| (false == bMacPwrCtrlOn) || |
| (true == adapter_to_pwrctl(padapter)->bFwCurrentInPSMode) |
| ) { |
| err = sd_cmd52_write(pintfhdl, addr, cnt, pbuf); |
| return err; |
| } |
| |
| ptmpbuf = (u8 *)rtw_malloc(cnt); |
| if (!ptmpbuf) |
| return (-1); |
| |
| memcpy(ptmpbuf, pbuf, cnt); |
| |
| err = sd_write(pintfhdl, addr, cnt, ptmpbuf); |
| |
| kfree(ptmpbuf); |
| |
| return err; |
| } |
| |
| u8 SdioLocalCmd52Read1Byte(struct adapter *padapter, u32 addr) |
| { |
| u8 val = 0; |
| struct intf_hdl *pintfhdl = &padapter->iopriv.intf; |
| |
| HalSdioGetCmdAddr8723BSdio(padapter, SDIO_LOCAL_DEVICE_ID, addr, &addr); |
| sd_cmd52_read(pintfhdl, addr, 1, &val); |
| |
| return val; |
| } |
| |
| static u16 SdioLocalCmd52Read2Byte(struct adapter *padapter, u32 addr) |
| { |
| __le16 val = 0; |
| struct intf_hdl *pintfhdl = &padapter->iopriv.intf; |
| |
| HalSdioGetCmdAddr8723BSdio(padapter, SDIO_LOCAL_DEVICE_ID, addr, &addr); |
| sd_cmd52_read(pintfhdl, addr, 2, (u8 *)&val); |
| |
| return le16_to_cpu(val); |
| } |
| |
| static u32 SdioLocalCmd53Read4Byte(struct adapter *padapter, u32 addr) |
| { |
| |
| u8 bMacPwrCtrlOn; |
| u32 val = 0; |
| struct intf_hdl *pintfhdl = &padapter->iopriv.intf; |
| __le32 le_tmp; |
| |
| HalSdioGetCmdAddr8723BSdio(padapter, SDIO_LOCAL_DEVICE_ID, addr, &addr); |
| rtw_hal_get_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); |
| if (!bMacPwrCtrlOn || adapter_to_pwrctl(padapter)->bFwCurrentInPSMode) { |
| sd_cmd52_read(pintfhdl, addr, 4, (u8 *)&le_tmp); |
| val = le32_to_cpu(le_tmp); |
| } else { |
| val = sd_read32(pintfhdl, addr, NULL); |
| } |
| return val; |
| } |
| |
| void SdioLocalCmd52Write1Byte(struct adapter *padapter, u32 addr, u8 v) |
| { |
| struct intf_hdl *pintfhdl = &padapter->iopriv.intf; |
| |
| HalSdioGetCmdAddr8723BSdio(padapter, SDIO_LOCAL_DEVICE_ID, addr, &addr); |
| sd_cmd52_write(pintfhdl, addr, 1, &v); |
| } |
| |
| static void SdioLocalCmd52Write4Byte(struct adapter *padapter, u32 addr, u32 v) |
| { |
| struct intf_hdl *pintfhdl = &padapter->iopriv.intf; |
| __le32 le_tmp; |
| |
| HalSdioGetCmdAddr8723BSdio(padapter, SDIO_LOCAL_DEVICE_ID, addr, &addr); |
| le_tmp = cpu_to_le32(v); |
| sd_cmd52_write(pintfhdl, addr, 4, (u8 *)&le_tmp); |
| } |
| |
| static s32 ReadInterrupt8723BSdio(struct adapter *padapter, u32 *phisr) |
| { |
| u32 hisr, himr; |
| u8 val8, hisr_len; |
| |
| |
| if (phisr == NULL) |
| return false; |
| |
| himr = GET_HAL_DATA(padapter)->sdio_himr; |
| |
| /* decide how many bytes need to be read */ |
| hisr_len = 0; |
| while (himr) { |
| hisr_len++; |
| himr >>= 8; |
| } |
| |
| hisr = 0; |
| while (hisr_len != 0) { |
| hisr_len--; |
| val8 = SdioLocalCmd52Read1Byte(padapter, SDIO_REG_HISR+hisr_len); |
| hisr |= (val8 << (8*hisr_len)); |
| } |
| |
| *phisr = hisr; |
| |
| return true; |
| } |
| |
| /* */ |
| /* Description: */ |
| /* Initialize SDIO Host Interrupt Mask configuration variables for future use. */ |
| /* */ |
| /* Assumption: */ |
| /* Using SDIO Local register ONLY for configuration. */ |
| /* */ |
| /* Created by Roger, 2011.02.11. */ |
| /* */ |
| void InitInterrupt8723BSdio(struct adapter *padapter) |
| { |
| struct hal_com_data *pHalData; |
| |
| |
| pHalData = GET_HAL_DATA(padapter); |
| pHalData->sdio_himr = (u32)( \ |
| SDIO_HIMR_RX_REQUEST_MSK | |
| SDIO_HIMR_AVAL_MSK | |
| /* SDIO_HIMR_TXERR_MSK | */ |
| /* SDIO_HIMR_RXERR_MSK | */ |
| /* SDIO_HIMR_TXFOVW_MSK | */ |
| /* SDIO_HIMR_RXFOVW_MSK | */ |
| /* SDIO_HIMR_TXBCNOK_MSK | */ |
| /* SDIO_HIMR_TXBCNERR_MSK | */ |
| /* SDIO_HIMR_BCNERLY_INT_MSK | */ |
| /* SDIO_HIMR_C2HCMD_MSK | */ |
| /* SDIO_HIMR_HSISR_IND_MSK | */ |
| /* SDIO_HIMR_GTINT3_IND_MSK | */ |
| /* SDIO_HIMR_GTINT4_IND_MSK | */ |
| /* SDIO_HIMR_PSTIMEOUT_MSK | */ |
| /* SDIO_HIMR_OCPINT_MSK | */ |
| /* SDIO_HIMR_ATIMEND_MSK | */ |
| /* SDIO_HIMR_ATIMEND_E_MSK | */ |
| /* SDIO_HIMR_CTWEND_MSK | */ |
| 0); |
| } |
| |
| /* */ |
| /* Description: */ |
| /* Initialize System Host Interrupt Mask configuration variables for future use. */ |
| /* */ |
| /* Created by Roger, 2011.08.03. */ |
| /* */ |
| void InitSysInterrupt8723BSdio(struct adapter *padapter) |
| { |
| struct hal_com_data *pHalData; |
| |
| |
| pHalData = GET_HAL_DATA(padapter); |
| |
| pHalData->SysIntrMask = ( \ |
| /* HSIMR_GPIO12_0_INT_EN | */ |
| /* HSIMR_SPS_OCP_INT_EN | */ |
| /* HSIMR_RON_INT_EN | */ |
| /* HSIMR_PDNINT_EN | */ |
| /* HSIMR_GPIO9_INT_EN | */ |
| 0); |
| } |
| |
| #ifdef CONFIG_WOWLAN |
| /* */ |
| /* Description: */ |
| /* Clear corresponding SDIO Host ISR interrupt service. */ |
| /* */ |
| /* Assumption: */ |
| /* Using SDIO Local register ONLY for configuration. */ |
| /* */ |
| /* Created by Roger, 2011.02.11. */ |
| /* */ |
| void ClearInterrupt8723BSdio(struct adapter *padapter) |
| { |
| struct hal_com_data *pHalData; |
| u8 *clear; |
| |
| |
| if (true == padapter->bSurpriseRemoved) |
| return; |
| |
| pHalData = GET_HAL_DATA(padapter); |
| clear = rtw_zmalloc(4); |
| |
| /* Clear corresponding HISR Content if needed */ |
| *(__le32 *)clear = cpu_to_le32(pHalData->sdio_hisr & MASK_SDIO_HISR_CLEAR); |
| if (*(__le32 *)clear) { |
| /* Perform write one clear operation */ |
| sdio_local_write(padapter, SDIO_REG_HISR, 4, clear); |
| } |
| |
| kfree(clear); |
| } |
| #endif |
| |
| /* */ |
| /* Description: */ |
| /* Enalbe SDIO Host Interrupt Mask configuration on SDIO local domain. */ |
| /* */ |
| /* Assumption: */ |
| /* 1. Using SDIO Local register ONLY for configuration. */ |
| /* 2. PASSIVE LEVEL */ |
| /* */ |
| /* Created by Roger, 2011.02.11. */ |
| /* */ |
| void EnableInterrupt8723BSdio(struct adapter *padapter) |
| { |
| struct hal_com_data *pHalData; |
| __le32 himr; |
| u32 tmp; |
| |
| pHalData = GET_HAL_DATA(padapter); |
| |
| himr = cpu_to_le32(pHalData->sdio_himr); |
| sdio_local_write(padapter, SDIO_REG_HIMR, 4, (u8 *)&himr); |
| |
| RT_TRACE( |
| _module_hci_ops_c_, |
| _drv_notice_, |
| ( |
| "%s: enable SDIO HIMR = 0x%08X\n", |
| __func__, |
| pHalData->sdio_himr |
| ) |
| ); |
| |
| /* Update current system IMR settings */ |
| tmp = rtw_read32(padapter, REG_HSIMR); |
| rtw_write32(padapter, REG_HSIMR, tmp | pHalData->SysIntrMask); |
| |
| RT_TRACE( |
| _module_hci_ops_c_, |
| _drv_notice_, |
| ( |
| "%s: enable HSIMR = 0x%08X\n", |
| __func__, |
| pHalData->SysIntrMask |
| ) |
| ); |
| |
| /* */ |
| /* <Roger_Notes> There are some C2H CMDs have been sent before system interrupt is enabled, e.g., C2H, CPWM. */ |
| /* So we need to clear all C2H events that FW has notified, otherwise FW won't schedule any commands anymore. */ |
| /* 2011.10.19. */ |
| /* */ |
| rtw_write8(padapter, REG_C2HEVT_CLEAR, C2H_EVT_HOST_CLOSE); |
| } |
| |
| /* */ |
| /* Description: */ |
| /* Disable SDIO Host IMR configuration to mask unnecessary interrupt service. */ |
| /* */ |
| /* Assumption: */ |
| /* Using SDIO Local register ONLY for configuration. */ |
| /* */ |
| /* Created by Roger, 2011.02.11. */ |
| /* */ |
| void DisableInterrupt8723BSdio(struct adapter *padapter) |
| { |
| __le32 himr; |
| |
| himr = cpu_to_le32(SDIO_HIMR_DISABLED); |
| sdio_local_write(padapter, SDIO_REG_HIMR, 4, (u8 *)&himr); |
| } |
| |
| /* */ |
| /* Description: */ |
| /* Using 0x100 to check the power status of FW. */ |
| /* */ |
| /* Assumption: */ |
| /* Using SDIO Local register ONLY for configuration. */ |
| /* */ |
| /* Created by Isaac, 2013.09.10. */ |
| /* */ |
| u8 CheckIPSStatus(struct adapter *padapter) |
| { |
| DBG_871X( |
| "%s(): Read 0x100 = 0x%02x 0x86 = 0x%02x\n", |
| __func__, |
| rtw_read8(padapter, 0x100), |
| rtw_read8(padapter, 0x86) |
| ); |
| |
| if (rtw_read8(padapter, 0x100) == 0xEA) |
| return true; |
| else |
| return false; |
| } |
| |
| static struct recv_buf *sd_recv_rxfifo(struct adapter *padapter, u32 size) |
| { |
| u32 readsize, ret; |
| u8 *preadbuf; |
| struct recv_priv *precvpriv; |
| struct recv_buf *precvbuf; |
| |
| |
| /* Patch for some SDIO Host 4 bytes issue */ |
| /* ex. RK3188 */ |
| readsize = RND4(size); |
| |
| /* 3 1. alloc recvbuf */ |
| precvpriv = &padapter->recvpriv; |
| precvbuf = rtw_dequeue_recvbuf(&precvpriv->free_recv_buf_queue); |
| if (precvbuf == NULL) { |
| DBG_871X_LEVEL(_drv_err_, "%s: alloc recvbuf FAIL!\n", __func__); |
| return NULL; |
| } |
| |
| /* 3 2. alloc skb */ |
| if (precvbuf->pskb == NULL) { |
| SIZE_PTR tmpaddr = 0; |
| SIZE_PTR alignment = 0; |
| |
| precvbuf->pskb = rtw_skb_alloc(MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ); |
| |
| if (precvbuf->pskb) { |
| precvbuf->pskb->dev = padapter->pnetdev; |
| |
| tmpaddr = (SIZE_PTR)precvbuf->pskb->data; |
| alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1); |
| skb_reserve(precvbuf->pskb, (RECVBUFF_ALIGN_SZ - alignment)); |
| } |
| |
| if (precvbuf->pskb == NULL) { |
| DBG_871X("%s: alloc_skb fail! read =%d\n", __func__, readsize); |
| return NULL; |
| } |
| } |
| |
| /* 3 3. read data from rxfifo */ |
| preadbuf = precvbuf->pskb->data; |
| ret = sdio_read_port(&padapter->iopriv.intf, WLAN_RX0FF_DEVICE_ID, readsize, preadbuf); |
| if (ret == _FAIL) { |
| RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("%s: read port FAIL!\n", __func__)); |
| return NULL; |
| } |
| |
| |
| /* 3 4. init recvbuf */ |
| precvbuf->len = size; |
| precvbuf->phead = precvbuf->pskb->head; |
| precvbuf->pdata = precvbuf->pskb->data; |
| skb_set_tail_pointer(precvbuf->pskb, size); |
| precvbuf->ptail = skb_tail_pointer(precvbuf->pskb); |
| precvbuf->pend = skb_end_pointer(precvbuf->pskb); |
| |
| return precvbuf; |
| } |
| |
| static void sd_rxhandler(struct adapter *padapter, struct recv_buf *precvbuf) |
| { |
| struct recv_priv *precvpriv; |
| struct __queue *ppending_queue; |
| |
| precvpriv = &padapter->recvpriv; |
| ppending_queue = &precvpriv->recv_buf_pending_queue; |
| |
| /* 3 1. enqueue recvbuf */ |
| rtw_enqueue_recvbuf(precvbuf, ppending_queue); |
| |
| /* 3 2. schedule tasklet */ |
| tasklet_schedule(&precvpriv->recv_tasklet); |
| } |
| |
| void sd_int_dpc(struct adapter *padapter) |
| { |
| struct hal_com_data *phal; |
| struct dvobj_priv *dvobj; |
| struct intf_hdl *pintfhdl = &padapter->iopriv.intf; |
| struct pwrctrl_priv *pwrctl; |
| |
| |
| phal = GET_HAL_DATA(padapter); |
| dvobj = adapter_to_dvobj(padapter); |
| pwrctl = dvobj_to_pwrctl(dvobj); |
| |
| if (phal->sdio_hisr & SDIO_HISR_AVAL) { |
| u8 freepage[4]; |
| |
| _sdio_local_read(padapter, SDIO_REG_FREE_TXPG, 4, freepage); |
| up(&(padapter->xmitpriv.xmit_sema)); |
| } |
| |
| if (phal->sdio_hisr & SDIO_HISR_CPWM1) { |
| struct reportpwrstate_parm report; |
| |
| u8 bcancelled; |
| _cancel_timer(&(pwrctl->pwr_rpwm_timer), &bcancelled); |
| |
| report.state = SdioLocalCmd52Read1Byte(padapter, SDIO_REG_HCPWM1_8723B); |
| |
| /* cpwm_int_hdl(padapter, &report); */ |
| _set_workitem(&(pwrctl->cpwm_event)); |
| } |
| |
| if (phal->sdio_hisr & SDIO_HISR_TXERR) { |
| u8 *status; |
| u32 addr; |
| |
| status = rtw_malloc(4); |
| if (status) { |
| addr = REG_TXDMA_STATUS; |
| HalSdioGetCmdAddr8723BSdio(padapter, WLAN_IOREG_DEVICE_ID, addr, &addr); |
| _sd_read(pintfhdl, addr, 4, status); |
| _sd_write(pintfhdl, addr, 4, status); |
| DBG_8192C("%s: SDIO_HISR_TXERR (0x%08x)\n", __func__, le32_to_cpu(*(u32 *)status)); |
| kfree(status); |
| } else { |
| DBG_8192C("%s: SDIO_HISR_TXERR, but can't allocate memory to read status!\n", __func__); |
| } |
| } |
| |
| if (phal->sdio_hisr & SDIO_HISR_TXBCNOK) { |
| DBG_8192C("%s: SDIO_HISR_TXBCNOK\n", __func__); |
| } |
| |
| if (phal->sdio_hisr & SDIO_HISR_TXBCNERR) { |
| DBG_8192C("%s: SDIO_HISR_TXBCNERR\n", __func__); |
| } |
| #ifndef CONFIG_C2H_PACKET_EN |
| if (phal->sdio_hisr & SDIO_HISR_C2HCMD) { |
| struct c2h_evt_hdr_88xx *c2h_evt; |
| |
| DBG_8192C("%s: C2H Command\n", __func__); |
| c2h_evt = (struct c2h_evt_hdr_88xx *)rtw_zmalloc(16); |
| if (c2h_evt != NULL) { |
| if (rtw_hal_c2h_evt_read(padapter, (u8 *)c2h_evt) == _SUCCESS) { |
| if (c2h_id_filter_ccx_8723b((u8 *)c2h_evt)) { |
| /* Handle CCX report here */ |
| rtw_hal_c2h_handler(padapter, (u8 *)c2h_evt); |
| kfree((u8 *)c2h_evt); |
| } else { |
| rtw_c2h_wk_cmd(padapter, (u8 *)c2h_evt); |
| } |
| } |
| } else { |
| /* Error handling for malloc fail */ |
| if (rtw_cbuf_push(padapter->evtpriv.c2h_queue, NULL) != _SUCCESS) |
| DBG_871X("%s rtw_cbuf_push fail\n", __func__); |
| _set_workitem(&padapter->evtpriv.c2h_wk); |
| } |
| } |
| #endif |
| |
| if (phal->sdio_hisr & SDIO_HISR_RXFOVW) { |
| DBG_8192C("%s: Rx Overflow\n", __func__); |
| } |
| |
| if (phal->sdio_hisr & SDIO_HISR_RXERR) { |
| DBG_8192C("%s: Rx Error\n", __func__); |
| } |
| |
| if (phal->sdio_hisr & SDIO_HISR_RX_REQUEST) { |
| struct recv_buf *precvbuf; |
| int alloc_fail_time = 0; |
| u32 hisr; |
| |
| /* DBG_8192C("%s: RX Request, size =%d\n", __func__, phal->SdioRxFIFOSize); */ |
| phal->sdio_hisr ^= SDIO_HISR_RX_REQUEST; |
| do { |
| phal->SdioRxFIFOSize = SdioLocalCmd52Read2Byte(padapter, SDIO_REG_RX0_REQ_LEN); |
| if (phal->SdioRxFIFOSize != 0) { |
| precvbuf = sd_recv_rxfifo(padapter, phal->SdioRxFIFOSize); |
| if (precvbuf) |
| sd_rxhandler(padapter, precvbuf); |
| else { |
| alloc_fail_time++; |
| DBG_871X("precvbuf is Null for %d times because alloc memory failed\n", alloc_fail_time); |
| if (alloc_fail_time >= 10) |
| break; |
| } |
| phal->SdioRxFIFOSize = 0; |
| } else |
| break; |
| |
| hisr = 0; |
| ReadInterrupt8723BSdio(padapter, &hisr); |
| hisr &= SDIO_HISR_RX_REQUEST; |
| if (!hisr) |
| break; |
| } while (1); |
| |
| if (alloc_fail_time == 10) |
| DBG_871X("exit because alloc memory failed more than 10 times\n"); |
| |
| } |
| } |
| |
| void sd_int_hdl(struct adapter *padapter) |
| { |
| struct hal_com_data *phal; |
| |
| |
| if ( |
| (padapter->bDriverStopped == true) || |
| (padapter->bSurpriseRemoved == true) |
| ) |
| return; |
| |
| phal = GET_HAL_DATA(padapter); |
| |
| phal->sdio_hisr = 0; |
| ReadInterrupt8723BSdio(padapter, &phal->sdio_hisr); |
| |
| if (phal->sdio_hisr & phal->sdio_himr) { |
| u32 v32; |
| |
| phal->sdio_hisr &= phal->sdio_himr; |
| |
| /* clear HISR */ |
| v32 = phal->sdio_hisr & MASK_SDIO_HISR_CLEAR; |
| if (v32) { |
| SdioLocalCmd52Write4Byte(padapter, SDIO_REG_HISR, v32); |
| } |
| |
| sd_int_dpc(padapter); |
| } else { |
| RT_TRACE(_module_hci_ops_c_, _drv_err_, |
| ("%s: HISR(0x%08x) and HIMR(0x%08x) not match!\n", |
| __func__, phal->sdio_hisr, phal->sdio_himr)); |
| } |
| } |
| |
| /* */ |
| /* Description: */ |
| /* Query SDIO Local register to query current the number of Free TxPacketBuffer page. */ |
| /* */ |
| /* Assumption: */ |
| /* 1. Running at PASSIVE_LEVEL */ |
| /* 2. RT_TX_SPINLOCK is NOT acquired. */ |
| /* */ |
| /* Created by Roger, 2011.01.28. */ |
| /* */ |
| u8 HalQueryTxBufferStatus8723BSdio(struct adapter *padapter) |
| { |
| struct hal_com_data *phal; |
| u32 NumOfFreePage; |
| /* _irqL irql; */ |
| |
| |
| phal = GET_HAL_DATA(padapter); |
| |
| NumOfFreePage = SdioLocalCmd53Read4Byte(padapter, SDIO_REG_FREE_TXPG); |
| |
| /* spin_lock_bh(&phal->SdioTxFIFOFreePageLock); */ |
| memcpy(phal->SdioTxFIFOFreePage, &NumOfFreePage, 4); |
| RT_TRACE(_module_hci_ops_c_, _drv_notice_, |
| ("%s: Free page for HIQ(%#x), MIDQ(%#x), LOWQ(%#x), PUBQ(%#x)\n", |
| __func__, |
| phal->SdioTxFIFOFreePage[HI_QUEUE_IDX], |
| phal->SdioTxFIFOFreePage[MID_QUEUE_IDX], |
| phal->SdioTxFIFOFreePage[LOW_QUEUE_IDX], |
| phal->SdioTxFIFOFreePage[PUBLIC_QUEUE_IDX])); |
| /* spin_unlock_bh(&phal->SdioTxFIFOFreePageLock); */ |
| |
| return true; |
| } |
| |
| /* */ |
| /* Description: */ |
| /* Query SDIO Local register to get the current number of TX OQT Free Space. */ |
| /* */ |
| u8 HalQueryTxOQTBufferStatus8723BSdio(struct adapter *padapter) |
| { |
| struct hal_com_data *pHalData = GET_HAL_DATA(padapter); |
| |
| pHalData->SdioTxOQTFreeSpace = SdioLocalCmd52Read1Byte(padapter, SDIO_REG_OQT_FREE_PG); |
| return true; |
| } |
| |
| #if defined(CONFIG_WOWLAN) || defined(CONFIG_AP_WOWLAN) |
| u8 RecvOnePkt(struct adapter *padapter, u32 size) |
| { |
| struct recv_buf *precvbuf; |
| struct dvobj_priv *psddev; |
| PSDIO_DATA psdio_data; |
| struct sdio_func *func; |
| |
| u8 res = false; |
| |
| DBG_871X("+%s: size: %d+\n", __func__, size); |
| |
| if (padapter == NULL) { |
| DBG_871X(KERN_ERR "%s: padapter is NULL!\n", __func__); |
| return false; |
| } |
| |
| psddev = adapter_to_dvobj(padapter); |
| psdio_data = &psddev->intf_data; |
| func = psdio_data->func; |
| |
| if (size) { |
| sdio_claim_host(func); |
| precvbuf = sd_recv_rxfifo(padapter, size); |
| |
| if (precvbuf) { |
| /* printk("Completed Recv One Pkt.\n"); */ |
| sd_rxhandler(padapter, precvbuf); |
| res = true; |
| } else { |
| res = false; |
| } |
| sdio_release_host(func); |
| } |
| DBG_871X("-%s-\n", __func__); |
| return res; |
| } |
| #endif /* CONFIG_WOWLAN */ |