1407 lines
42 KiB
C
1407 lines
42 KiB
C
/*
|
|
* Copyright (c) 202X Amlogic, Inc. All rights reserved.
|
|
*
|
|
* This source code is subject to the terms and conditions defined in the
|
|
* file 'LICENSE' which is part of this source code package.
|
|
*
|
|
* Description:
|
|
*/
|
|
|
|
#include "w1_sdio.h"
|
|
#include <linux/mutex.h>
|
|
|
|
#include "chip_pmu_reg.h"
|
|
#include "chip_intf_reg.h"
|
|
#include "wifi_intf_addr.h"
|
|
#include "wifi_drv_reg_ops.h"
|
|
#include "rf_d_top_reg.h"
|
|
#include "wifi_coex_addr.h"
|
|
|
|
struct amlw1_hwif_sdio g_w1_hwif_sdio;
|
|
struct amlw1_hif_ops g_w1_hif_ops;
|
|
|
|
unsigned char w1_sdio_wifi_bt_alive;
|
|
unsigned char w1_sdio_driver_insmoded;
|
|
unsigned char w1_sdio_after_porbe;
|
|
unsigned char wifi_in_insmod;
|
|
unsigned char wifi_in_rmmod;
|
|
unsigned char wifi_sdio_access = 1;
|
|
unsigned int shutdown_i = 0;
|
|
#define I2C_CLK_QTR 0x4
|
|
|
|
static DEFINE_MUTEX(wifi_bt_sdio_mutex);
|
|
|
|
#define AML_W1_BT_WIFI_MUTEX_ON() do {\
|
|
mutex_lock(&wifi_bt_sdio_mutex);\
|
|
} while (0)
|
|
|
|
#define AML_W1_BT_WIFI_MUTEX_OFF() do {\
|
|
mutex_unlock(&wifi_bt_sdio_mutex);\
|
|
} while (0)
|
|
|
|
/* protect cmd53 and host sleep request */
|
|
static DEFINE_MUTEX(wifi_sdio_power_mutex);
|
|
|
|
void aml_wifi_sdio_power_lock(void)
|
|
{
|
|
mutex_lock(&wifi_sdio_power_mutex);
|
|
}
|
|
|
|
void aml_wifi_sdio_power_unlock(void)
|
|
{
|
|
mutex_unlock(&wifi_sdio_power_mutex);
|
|
}
|
|
|
|
unsigned char (*host_wake_w1_req)(void);
|
|
int (*host_suspend_req)(struct device *device);
|
|
int (*host_resume_req)(struct device *device);
|
|
|
|
|
|
struct sdio_func *aml_priv_to_func(int func_n)
|
|
{
|
|
ASSERT(func_n >= 0 && func_n < SDIO_FUNCNUM_MAX);
|
|
return g_w1_hwif_sdio.sdio_func_if[func_n];
|
|
}
|
|
|
|
unsigned int aml_w1_bt_hi_read_word(unsigned int addr)
|
|
{
|
|
unsigned int regdata = 0;
|
|
unsigned int reg_tmp;
|
|
/*
|
|
* make sure function 5 section address-mapping feature is disabled,
|
|
* when this feature is disabled,
|
|
* all 128k space in one sdio-function use only
|
|
* one address-mapping: 32-bit AHB Address = BaseAddr + cmdRegAddr
|
|
*/
|
|
|
|
reg_tmp = g_w1_hif_ops.hi_read_word( RG_SDIO_IF_MISC_CTRL);
|
|
|
|
if (!(reg_tmp & BIT(23))) {
|
|
reg_tmp |= BIT(23);
|
|
g_w1_hif_ops.hi_write_word( RG_SDIO_IF_MISC_CTRL, reg_tmp);
|
|
}
|
|
|
|
/*config msb 15 bit address in BaseAddr Register*/
|
|
g_w1_hif_ops.hi_write_reg32(RG_SCFG_FUNC5_BADDR_A,addr & 0xfffe0000);
|
|
g_w1_hif_ops.bt_hi_read_sram((unsigned char*)(SYS_TYPE)®data,
|
|
/*sdio cmd 52/53 can only take 17 bit address*/
|
|
(unsigned char*)(SYS_TYPE)(addr & 0x1ffff), sizeof(unsigned int));
|
|
|
|
return regdata;
|
|
}
|
|
|
|
void aml_w1_bt_hi_write_word(unsigned int addr,unsigned int data)
|
|
{
|
|
unsigned int reg_tmp;
|
|
/*
|
|
* make sure function 5 section address-mapping feature is disabled,
|
|
* when this feature is disabled,
|
|
* all 128k space in one sdio-function use only
|
|
* one address-mapping: 32-bit AHB Address = BaseAddr + cmdRegAddr
|
|
*/
|
|
reg_tmp = g_w1_hif_ops.hi_read_word( RG_SDIO_IF_MISC_CTRL);
|
|
|
|
if (!(reg_tmp & BIT(23))) {
|
|
reg_tmp |= BIT(23);
|
|
g_w1_hif_ops.hi_write_word( RG_SDIO_IF_MISC_CTRL, reg_tmp);
|
|
}
|
|
/*config msb 15 bit address in BaseAddr Register*/
|
|
g_w1_hif_ops.hi_write_reg32(RG_SCFG_FUNC5_BADDR_A,addr & 0xfffe0000);
|
|
|
|
g_w1_hif_ops.bt_hi_write_sram((unsigned char *)&data,
|
|
/*sdio cmd 52/53 can only take 17 bit address*/
|
|
(unsigned char*)(SYS_TYPE)(addr & 0x1ffff), sizeof(unsigned int));
|
|
}
|
|
|
|
void aml_w1_bt_sdio_read_sram (unsigned char *buf, unsigned char *addr, SYS_TYPE len)
|
|
{
|
|
g_w1_hif_ops.hi_bottom_read(SDIO_FUNC5, ((SYS_TYPE)addr & SDIO_ADDR_MASK),
|
|
buf, len, (len > 8 ? SDIO_OPMODE_INCREMENT : SDIO_OPMODE_FIXED));
|
|
}
|
|
|
|
/*For BT use only start */
|
|
void aml_w1_bt_sdio_write_sram (unsigned char *buf, unsigned char *addr, SYS_TYPE len)
|
|
{
|
|
g_w1_hif_ops.hi_bottom_write(SDIO_FUNC5, ((SYS_TYPE)addr & SDIO_ADDR_MASK),
|
|
buf, len, (len > 8 ? SDIO_OPMODE_INCREMENT : SDIO_OPMODE_FIXED));
|
|
}
|
|
|
|
int aml_w1_sdio_write_reg32(unsigned long sram_addr, unsigned long sramdata)
|
|
{
|
|
return g_w1_hif_ops.hi_bottom_write(SDIO_FUNC1, sram_addr&SDIO_ADDR_MASK,
|
|
(unsigned char *)&sramdata, sizeof(unsigned long), SDIO_OPMODE_INCREMENT);
|
|
}
|
|
|
|
unsigned int aml_w1_aon_read_reg(unsigned int addr)
|
|
{
|
|
unsigned int regdata = 0;
|
|
|
|
regdata = g_w1_hif_ops.bt_hi_read_word((addr));
|
|
return regdata;
|
|
}
|
|
|
|
/* aon module address from 0x00f00000, we need read/write by sdio func5 */
|
|
void aml_w1_aon_write_reg(unsigned int addr,unsigned int data)
|
|
{
|
|
g_w1_hif_ops.bt_hi_write_word((addr), data);
|
|
}
|
|
|
|
static int _aml_w1_sdio_request_byte(unsigned char func_num,
|
|
unsigned char write, unsigned int reg_addr, unsigned char *byte)
|
|
{
|
|
int err_ret;
|
|
struct sdio_func * func = aml_priv_to_func(func_num);
|
|
unsigned char *kmalloc_buf = NULL;
|
|
unsigned char len = sizeof(unsigned char);
|
|
|
|
#if defined(DBG_PRINT_COST_TIME)
|
|
struct timespec now, before;
|
|
getnstimeofday(&before);
|
|
#endif /* End of DBG_PRINT_COST_TIME */
|
|
|
|
ASSERT(func != NULL);
|
|
ASSERT(byte != NULL);
|
|
ASSERT(func->num == func_num);
|
|
|
|
AML_W1_BT_WIFI_MUTEX_ON();
|
|
kmalloc_buf = (unsigned char *)kzalloc(len, GFP_DMA);//virt_to_phys(fwICCM);
|
|
if (kmalloc_buf == NULL)
|
|
{
|
|
ERROR_DEBUG_OUT("kmalloc buf fail\n");
|
|
AML_W1_BT_WIFI_MUTEX_OFF();
|
|
return SDIOH_API_RC_FAIL;
|
|
}
|
|
memcpy(kmalloc_buf, byte, len);
|
|
|
|
/* Claim host controller */
|
|
sdio_claim_host(func);
|
|
|
|
if (write) {
|
|
/* CMD52 Write */
|
|
sdio_writeb(func, *kmalloc_buf, reg_addr, &err_ret);
|
|
}
|
|
else {
|
|
/* CMD52 Read */
|
|
*byte = sdio_readb(func, reg_addr, &err_ret);
|
|
}
|
|
|
|
/* Release host controller */
|
|
sdio_release_host(func);
|
|
|
|
#if defined(DBG_PRINT_COST_TIME)
|
|
getnstimeofday(&now);
|
|
|
|
printk("[sdio byte]: len=1 cost=%lds %luus\n",
|
|
now.tv_sec-before.tv_sec, now.tv_nsec/1000 - before.tv_nsec/1000);
|
|
#endif /* End of DBG_PRINT_COST_TIME */
|
|
|
|
kfree(kmalloc_buf);
|
|
AML_W1_BT_WIFI_MUTEX_OFF();
|
|
return (err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL;
|
|
}
|
|
|
|
static int _aml_w1_sdio_request_buffer(unsigned char func_num,
|
|
unsigned int fix_incr, unsigned char write, unsigned int addr, void *buf, unsigned int nbytes)
|
|
{
|
|
int err_ret;
|
|
int align_nbytes = nbytes;
|
|
struct sdio_func * func = aml_priv_to_func(func_num);
|
|
bool fifo = (fix_incr == SDIO_OPMODE_FIXED);
|
|
|
|
ASSERT(buf != NULL);
|
|
ASSERT(func != NULL);
|
|
ASSERT(fix_incr == SDIO_OPMODE_FIXED|| fix_incr == SDIO_OPMODE_INCREMENT);
|
|
ASSERT(func->num == func_num);
|
|
|
|
/* Claim host controller */
|
|
sdio_claim_host(func);
|
|
|
|
if (write && !fifo)
|
|
{
|
|
/* write, increment */
|
|
align_nbytes = sdio_align_size(func, nbytes);
|
|
err_ret = sdio_memcpy_toio(func, addr, buf, align_nbytes);
|
|
}
|
|
else if (write)
|
|
{
|
|
/* write, fifo */
|
|
err_ret = sdio_writesb(func, addr, buf, align_nbytes);
|
|
}
|
|
else if (fifo)
|
|
{
|
|
/* read */
|
|
err_ret = sdio_readsb(func, buf, addr, align_nbytes);
|
|
}
|
|
else
|
|
{
|
|
/* read */
|
|
align_nbytes = sdio_align_size(func, nbytes);
|
|
err_ret = sdio_memcpy_fromio(func, buf, addr, align_nbytes);
|
|
}
|
|
|
|
/* Release host controller */
|
|
sdio_release_host(func);
|
|
|
|
return (err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL;
|
|
}
|
|
|
|
unsigned char aml_w1_sdio_bottom_read8(unsigned char func_num, int addr);
|
|
|
|
//cmd53
|
|
int aml_w1_sdio_bottom_read(unsigned char func_num, int addr, void *buf, size_t len, int incr_addr)
|
|
{
|
|
void *kmalloc_buf = NULL;
|
|
int result;
|
|
int align_len = 0;
|
|
|
|
ASSERT(func_num != SDIO_FUNC0);
|
|
|
|
if (!wifi_sdio_access) {
|
|
if (func_num == SDIO_FUNC5) {
|
|
/*SDIO_FUNC5 ignore*/
|
|
ERROR_DEBUG_OUT("SDIO_FUNC5, func num %d, addr 0x%08x\n", func_num, addr);
|
|
|
|
} else if (func_num == SDIO_FUNC1) {
|
|
/*SDIO_FUNC1 ignore*/
|
|
ERROR_DEBUG_OUT("SDIO_FUNC1, func num %d, addr 0x%08x\n", func_num, addr);
|
|
|
|
} else if ((func_num == SDIO_FUNC2) && (addr == 0x00005080)) {
|
|
/*SDIO_FUNC2 && 0x00005080 ignore*/
|
|
ERROR_DEBUG_OUT("SDIO_FUNC1, func num %d, addr 0x%08x\n", func_num, addr);
|
|
|
|
} else {
|
|
ERROR_DEBUG_OUT("fw recovery downloading, func num %d, addr 0x%08x\n", func_num, addr);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
aml_wifi_sdio_power_lock();
|
|
|
|
if (host_wake_w1_req != NULL)
|
|
{
|
|
if (host_wake_w1_req() == 0)
|
|
{
|
|
aml_wifi_sdio_power_unlock();
|
|
ERROR_DEBUG_OUT("aml_w1_sdio_bottom_read, host wake w1 fail\n");
|
|
return -1;
|
|
}
|
|
}
|
|
{
|
|
unsigned char fw_st = aml_w1_sdio_bottom_read8(SDIO_FUNC1, 0x23c) & 0xF;
|
|
if (fw_st != 6)
|
|
printk("%s:%d, BUG! fw_st %x, func_num %x, addr %x \n", __func__, __LINE__, fw_st, func_num, addr);
|
|
}
|
|
AML_W1_BT_WIFI_MUTEX_ON();
|
|
/* read block mode */
|
|
if (func_num != SDIO_FUNC6)
|
|
{
|
|
if (incr_addr == SDIO_OPMODE_INCREMENT)
|
|
{
|
|
struct sdio_func * func = aml_priv_to_func(func_num);
|
|
align_len = sdio_align_size(func, len);
|
|
}
|
|
else
|
|
align_len = len;
|
|
|
|
kmalloc_buf = (unsigned char *)kzalloc(align_len, GFP_DMA|GFP_ATOMIC);
|
|
}
|
|
else
|
|
{
|
|
kmalloc_buf = buf;
|
|
}
|
|
|
|
if (kmalloc_buf == NULL)
|
|
{
|
|
ERROR_DEBUG_OUT("kmalloc buf fail\n");
|
|
AML_W1_BT_WIFI_MUTEX_OFF();
|
|
aml_wifi_sdio_power_unlock();
|
|
return SDIOH_API_RC_FAIL;
|
|
}
|
|
|
|
result = _aml_w1_sdio_request_buffer(func_num, incr_addr, SDIO_READ, addr, kmalloc_buf, len);
|
|
|
|
if (func_num != SDIO_FUNC6)
|
|
{
|
|
memcpy(buf, kmalloc_buf, len);
|
|
kfree(kmalloc_buf);
|
|
}
|
|
|
|
AML_W1_BT_WIFI_MUTEX_OFF();
|
|
aml_wifi_sdio_power_unlock();
|
|
return result;
|
|
}
|
|
|
|
|
|
//cmd53
|
|
int aml_w1_sdio_bottom_write(unsigned char func_num, int addr, void *buf, size_t len, int incr_addr)
|
|
{
|
|
void *kmalloc_buf;
|
|
int result;
|
|
|
|
if (!wifi_sdio_access) {
|
|
if (func_num == SDIO_FUNC5) {
|
|
/*SDIO_FUNC5 ignore*/
|
|
ERROR_DEBUG_OUT("SDIO_FUNC5, func num %d, addr 0x%08x\n", func_num, addr);
|
|
|
|
} else if (func_num == SDIO_FUNC1) {
|
|
/*SDIO_FUNC1 ignore*/
|
|
ERROR_DEBUG_OUT("SDIO_FUNC1, func num %d, addr 0x%08x\n", func_num, addr);
|
|
|
|
} else if ((func_num == SDIO_FUNC2) && (addr == 0x00005080)) {
|
|
/*SDIO_FUNC2 && 0x00005080 ignore*/
|
|
ERROR_DEBUG_OUT("SDIO_FUNC2, func num %d, addr 0x%08x\n", func_num, addr);
|
|
|
|
} else {
|
|
ERROR_DEBUG_OUT("fw recovery downloading, func num %d, addr 0x%08x\n", func_num, addr);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
aml_wifi_sdio_power_lock();
|
|
ASSERT(func_num != SDIO_FUNC0);
|
|
|
|
if (host_wake_w1_req != NULL)
|
|
{
|
|
if (host_wake_w1_req() == 0)
|
|
{
|
|
aml_wifi_sdio_power_unlock();
|
|
ERROR_DEBUG_OUT("aml_w1_sdio_bottom_write, host wake w1 fail\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
{
|
|
unsigned char fw_st = aml_w1_sdio_bottom_read8(SDIO_FUNC1, 0x23c) & 0xF;
|
|
if (fw_st != 6)
|
|
printk("%s:%d, BUG! fw_st %x, func_num %x, addr %x \n", __func__, __LINE__, fw_st, func_num, addr);
|
|
}
|
|
|
|
AML_W1_BT_WIFI_MUTEX_ON();
|
|
kmalloc_buf = (unsigned char *)kzalloc(len, GFP_DMA);//virt_to_phys(fwICCM);
|
|
if (kmalloc_buf == NULL)
|
|
{
|
|
ERROR_DEBUG_OUT("kmalloc buf fail\n");
|
|
AML_W1_BT_WIFI_MUTEX_OFF();
|
|
aml_wifi_sdio_power_unlock();
|
|
return SDIOH_API_RC_FAIL;
|
|
}
|
|
memcpy(kmalloc_buf, buf, len);
|
|
|
|
result = _aml_w1_sdio_request_buffer(func_num, incr_addr, SDIO_WRITE, addr, kmalloc_buf, len);
|
|
|
|
kfree(kmalloc_buf);
|
|
AML_W1_BT_WIFI_MUTEX_OFF();
|
|
aml_wifi_sdio_power_unlock();
|
|
return result;
|
|
}
|
|
|
|
void aml_w1_sdio_read_sram (unsigned char *buf, unsigned char *addr, SYS_TYPE len)
|
|
{
|
|
g_w1_hif_ops.hi_bottom_read(SDIO_FUNC2, (SYS_TYPE)addr&SDIO_ADDR_MASK,
|
|
buf, len, (len > 8 ? SDIO_OPMODE_INCREMENT : SDIO_OPMODE_FIXED));
|
|
}
|
|
|
|
void aml_w1_sdio_write_sram (unsigned char *buf, unsigned char *addr, SYS_TYPE len)
|
|
{
|
|
g_w1_hif_ops.hi_bottom_write(SDIO_FUNC2, (SYS_TYPE)addr&SDIO_ADDR_MASK,
|
|
buf, len, (len > 8 ? SDIO_OPMODE_INCREMENT : SDIO_OPMODE_FIXED));
|
|
}
|
|
|
|
unsigned int aml_w1_sdio_read_word(unsigned int addr)
|
|
{
|
|
unsigned int regdata = 0;
|
|
|
|
// for bt access always on reg
|
|
if ((addr & 0x00f00000) == 0x00f00000)
|
|
{
|
|
regdata = aml_w1_aon_read_reg(addr);
|
|
}
|
|
else if(((addr & 0x00f00000) == 0x00b00000)||
|
|
((addr & 0x00f00000) == 0x00d00000)||
|
|
((addr & 0x00f00000) == 0x00900000))
|
|
{
|
|
regdata = aml_w1_aon_read_reg(addr);
|
|
}
|
|
else if(((addr & 0x00f00000) == 0x00200000)||
|
|
((addr & 0x00f00000) == 0x00300000)||
|
|
((addr & 0x00f00000) == 0x00400000))
|
|
{
|
|
regdata = aml_w1_aon_read_reg(addr);
|
|
}
|
|
else
|
|
{
|
|
aml_w1_sdio_read_sram((unsigned char*)(SYS_TYPE)®data,
|
|
(unsigned char*)(SYS_TYPE)(addr), sizeof(unsigned int));
|
|
}
|
|
return regdata;
|
|
}
|
|
|
|
void aml_w1_sdio_write_word(unsigned int addr, unsigned int data)
|
|
{
|
|
// for bt access always on reg
|
|
if ((addr & 0x00f00000) == 0x00f00000)
|
|
{
|
|
aml_w1_aon_write_reg(addr, data);
|
|
}
|
|
else if(((addr & 0x00f00000) == 0x00b00000)||
|
|
((addr & 0x00f00000) == 0x00d00000)||
|
|
((addr & 0x00f00000) == 0x00900000))
|
|
{
|
|
aml_w1_aon_write_reg(addr, data);
|
|
}
|
|
else if(((addr & 0x00f00000) == 0x00200000)||
|
|
((addr & 0x00f00000) == 0x00300000)||
|
|
((addr & 0x00f00000) == 0x00400000))
|
|
{
|
|
aml_w1_aon_write_reg((addr), data);
|
|
}
|
|
else
|
|
{
|
|
aml_w1_sdio_write_sram((unsigned char *)&data,
|
|
(unsigned char*)(SYS_TYPE)(addr), sizeof(unsigned int));
|
|
}
|
|
}
|
|
|
|
//cmd52
|
|
int aml_w1_sdio_bottom_write8(unsigned char func_num, int addr, unsigned char data)
|
|
{
|
|
int ret = 0;
|
|
|
|
ASSERT(func_num != SDIO_FUNC0);
|
|
ret = _aml_w1_sdio_request_byte(func_num, SDIO_WRITE, addr, &data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
//cmd52
|
|
unsigned char aml_w1_sdio_bottom_read8(unsigned char func_num, int addr)
|
|
{
|
|
unsigned char sramdata;
|
|
|
|
_aml_w1_sdio_request_byte(func_num, SDIO_READ, addr, &sramdata);
|
|
return sramdata;
|
|
}
|
|
|
|
/* cmd52
|
|
buff[7:0], // funcIoNum
|
|
buff[25:8], // regAddr
|
|
buff[39:32]); // regValue
|
|
*/
|
|
void aml_w1_sdio_bottom_write8_func0(unsigned long sram_addr, unsigned char sramdata)
|
|
{
|
|
_aml_w1_sdio_request_byte(SDIO_FUNC0, SDIO_WRITE, sram_addr, &sramdata);
|
|
}
|
|
|
|
//cmd52
|
|
unsigned char aml_w1_sdio_bottom_read8_func0(unsigned long sram_addr)
|
|
{
|
|
unsigned char sramdata;
|
|
|
|
_aml_w1_sdio_request_byte(SDIO_FUNC0, SDIO_READ, sram_addr, &sramdata);
|
|
return sramdata;
|
|
}
|
|
|
|
void aml_w1_sdio_write_cmd32(unsigned long sram_addr, unsigned long sramdata)
|
|
{
|
|
#if defined (HAL_SIM_VER)
|
|
aml_sdio_read_write(sram_addr&SDIO_ADDR_MASK, (unsigned char *)&sramdata, 4,
|
|
SDIO_FUNC3,SDIO_RW_FLAG_WRITE,SDIO_F_SYNCHRONOUS);
|
|
#elif defined (HAL_FPGA_VER)
|
|
g_w1_hif_ops.hi_bottom_write(SDIO_FUNC3, sram_addr&SDIO_ADDR_MASK,
|
|
(unsigned char *)&sramdata, sizeof(unsigned long), SDIO_OPMODE_INCREMENT);
|
|
#endif /* End of HAL_SIM_VER */
|
|
}
|
|
|
|
int aml_w1_sdio_suspend(unsigned int suspend_enable)
|
|
{
|
|
mmc_pm_flag_t flags;
|
|
struct sdio_func *func = NULL;
|
|
int ret = 0, i;
|
|
|
|
if (suspend_enable == 0)
|
|
{
|
|
/* when enable == 0 that's resume operation
|
|
and we do nothing for resume now. */
|
|
return ret;
|
|
}
|
|
|
|
/* just clear sdio clock value for emmc init when resume */
|
|
//amlwifi_set_sdio_host_clk(0);
|
|
|
|
AML_W1_BT_WIFI_MUTEX_ON();
|
|
/* we shall suspend all card for sdio. */
|
|
for (i = SDIO_FUNC1; i <= FUNCNUM_SDIO_LAST; i++)
|
|
{
|
|
func = aml_priv_to_func(i);
|
|
if (func == NULL)
|
|
continue;
|
|
flags = sdio_get_host_pm_caps(func);
|
|
|
|
if ((flags & MMC_PM_KEEP_POWER) != 0)
|
|
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
|
|
|
|
if (ret != 0) {
|
|
AML_W1_BT_WIFI_MUTEX_OFF();
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* if we don't use sdio irq, we can't get functions' capability with
|
|
* MMC_PM_WAKE_SDIO_IRQ, so we don't need set irq for wake up
|
|
* sdio for upcoming suspend.
|
|
*/
|
|
if ((flags & MMC_PM_WAKE_SDIO_IRQ) != 0)
|
|
ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
|
|
|
|
if (ret != 0) {
|
|
AML_W1_BT_WIFI_MUTEX_OFF();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
AML_W1_BT_WIFI_MUTEX_OFF();
|
|
return ret;
|
|
}
|
|
|
|
unsigned long aml_w1_sdio_read_reg8(unsigned long sram_addr )
|
|
{
|
|
unsigned char regdata[8] = {0};
|
|
|
|
g_w1_hif_ops.hi_bottom_read(SDIO_FUNC1, sram_addr&SDIO_ADDR_MASK, regdata, 1, SDIO_OPMODE_INCREMENT);
|
|
return regdata[0];
|
|
}
|
|
|
|
void aml_w1_sdio_write_reg8(unsigned long sram_addr, unsigned long sramdata)
|
|
{
|
|
g_w1_hif_ops.hi_bottom_write(SDIO_FUNC1, sram_addr&SDIO_ADDR_MASK,
|
|
(unsigned char *)&sramdata, sizeof(unsigned long), SDIO_OPMODE_INCREMENT);
|
|
}
|
|
|
|
unsigned long aml_w1_sdio_read_reg32(unsigned long sram_addr)
|
|
{
|
|
unsigned long sramdata;
|
|
|
|
g_w1_hif_ops.hi_bottom_read(SDIO_FUNC1, sram_addr&SDIO_ADDR_MASK, &sramdata, 4, SDIO_OPMODE_INCREMENT);
|
|
return sramdata;
|
|
}
|
|
|
|
struct amlw_hif_scatter_req *aml_w1_sdio_scatter_req_get(void)
|
|
{
|
|
struct amlw1_hwif_sdio *hif_sdio = &g_w1_hwif_sdio;
|
|
|
|
struct amlw_hif_scatter_req *scat_req = NULL;
|
|
|
|
ASSERT(hif_sdio != NULL);
|
|
|
|
scat_req = hif_sdio->scat_req;
|
|
|
|
if (scat_req->free)
|
|
{
|
|
scat_req->free = false;
|
|
}
|
|
else if (scat_req->scat_count != 0) // get scat_req, but not build scatter list
|
|
{
|
|
scat_req = NULL;
|
|
}
|
|
|
|
return scat_req;
|
|
}
|
|
|
|
static int amlw_w1_sdio_alloc_prep_scat_req(struct amlw1_hwif_sdio *hif_sdio)
|
|
{
|
|
struct amlw_hif_scatter_req * scat_req = NULL;
|
|
|
|
ASSERT(hif_sdio != NULL);
|
|
|
|
/* allocate the scatter request */
|
|
scat_req = kzalloc(sizeof(struct amlw_hif_scatter_req), GFP_KERNEL);
|
|
if (scat_req == NULL)
|
|
{
|
|
ERROR_DEBUG_OUT("[sdio sg alloc_scat_req]: no mem\n");
|
|
return 1;
|
|
}
|
|
|
|
scat_req->free = true;
|
|
hif_sdio->scat_req = scat_req;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int aml_w1_sdio_enable_scatter(void)
|
|
{
|
|
struct amlw1_hwif_sdio *hif_sdio = &g_w1_hwif_sdio;
|
|
int ret;
|
|
|
|
ASSERT(hif_sdio != NULL);
|
|
|
|
if (hif_sdio->scatter_enabled)
|
|
return 0;
|
|
|
|
// TODO : getting hw_config to configure scatter number;
|
|
|
|
hif_sdio->scatter_enabled = true;
|
|
|
|
ret = amlw_w1_sdio_alloc_prep_scat_req(&g_w1_hwif_sdio);
|
|
return ret;
|
|
}
|
|
|
|
int aml_w1_sdio_scat_rw(struct scatterlist *sg_list, unsigned int sg_num, unsigned int blkcnt,
|
|
unsigned char func_num, unsigned int addr, unsigned char write)
|
|
{
|
|
struct mmc_request mmc_req;
|
|
struct mmc_command mmc_cmd;
|
|
struct mmc_data mmc_dat;
|
|
struct sdio_func *func = aml_priv_to_func(func_num);
|
|
int ret = 0;
|
|
|
|
AML_W1_BT_WIFI_MUTEX_ON();
|
|
memset(&mmc_req, 0, sizeof(struct mmc_request));
|
|
memset(&mmc_cmd, 0, sizeof(struct mmc_command));
|
|
memset(&mmc_dat, 0, sizeof(struct mmc_data));
|
|
|
|
mmc_dat.sg = sg_list;
|
|
mmc_dat.sg_len = sg_num;
|
|
mmc_dat.blksz = FUNC4_BLKSIZE;
|
|
mmc_dat.blocks = blkcnt;
|
|
mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
|
|
|
|
mmc_cmd.opcode = SD_IO_RW_EXTENDED;
|
|
mmc_cmd.arg = write ? 1 << 31 : 0;
|
|
mmc_cmd.arg |= (func_num & 0x7) << 28;
|
|
mmc_cmd.arg |= 1 << 27; /* block basic */
|
|
mmc_cmd.arg |= 0 << 26; /* 1 << 26;*/ /*0 fix address */
|
|
mmc_cmd.arg |= (addr & 0x1ffff)<< 9;
|
|
mmc_cmd.arg |= blkcnt & 0x1ff;
|
|
mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
|
|
|
|
mmc_req.cmd = &mmc_cmd;
|
|
mmc_req.data = &mmc_dat;
|
|
|
|
sdio_claim_host(func);
|
|
mmc_set_data_timeout(&mmc_dat, func->card);
|
|
mmc_wait_for_req(func->card->host, &mmc_req);
|
|
sdio_release_host(func);
|
|
|
|
if (mmc_cmd.error || mmc_dat.error) {
|
|
printk("ERROR CMD53 %s cmd_error = %d data_error=%d\n",
|
|
write ? "write" : "read", mmc_cmd.error, mmc_dat.error);
|
|
ret = mmc_cmd.error;
|
|
}
|
|
|
|
AML_W1_BT_WIFI_MUTEX_OFF();
|
|
return ret;
|
|
}
|
|
|
|
void aml_w1_sdio_scat_complete (struct amlw_hif_scatter_req * scat_req)
|
|
{
|
|
int i;
|
|
struct amlw1_hwif_sdio *hif_sdio = &g_w1_hwif_sdio;
|
|
|
|
ASSERT(scat_req != NULL);
|
|
ASSERT(hif_sdio != NULL);
|
|
|
|
if (scat_req->complete)
|
|
{
|
|
for (i = 0; i < scat_req->scat_count; i++)
|
|
{
|
|
(scat_req->complete)(scat_req->scat_list[i].skbbuf);
|
|
scat_req->scat_list[i].skbbuf = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_DEBUG_OUT("error: no complete function\n");
|
|
}
|
|
|
|
scat_req->free = true;
|
|
scat_req->scat_count = 0;
|
|
scat_req->len = 0;
|
|
scat_req->addr = 0;
|
|
memset(scat_req->sgentries, 0, SDIO_MAX_SG_ENTRIES * sizeof(struct scatterlist));
|
|
}
|
|
|
|
void aml_w1_sdio_cleanup_scatter(void)
|
|
{
|
|
struct amlw1_hwif_sdio *hif_sdio = &g_w1_hwif_sdio;
|
|
printk("[sdio sg cleanup]: enter\n");
|
|
|
|
ASSERT(hif_sdio != NULL);
|
|
|
|
if (!hif_sdio->scatter_enabled)
|
|
return;
|
|
|
|
hif_sdio->scatter_enabled = false;
|
|
|
|
/* empty the free list */
|
|
kfree(hif_sdio->scat_req);
|
|
printk("[sdio sg cleanup]: exit\n");
|
|
|
|
return;
|
|
}
|
|
|
|
void aml_w1_sdio_recv_frame (unsigned char *buf, unsigned char *addr, SYS_TYPE len)
|
|
{
|
|
g_w1_hif_ops.hi_bottom_read(SDIO_FUNC6, ((SYS_TYPE)addr & SDIO_ADDR_MASK),
|
|
buf, len, SDIO_OPMODE_INCREMENT);
|
|
}
|
|
|
|
void aml_w1_sdio_init_ops(void)
|
|
{
|
|
struct amlw1_hif_ops* ops = &g_w1_hif_ops;
|
|
|
|
ops->hi_bottom_write8 = aml_w1_sdio_bottom_write8;
|
|
ops->hi_bottom_read8 = aml_w1_sdio_bottom_read8;
|
|
ops->hi_bottom_read = aml_w1_sdio_bottom_read;
|
|
ops->hi_bottom_write = aml_w1_sdio_bottom_write;
|
|
ops->hi_write8_func0 = aml_w1_sdio_bottom_write8_func0;
|
|
ops->hi_read8_func0 = aml_w1_sdio_bottom_read8_func0;
|
|
|
|
ops->hi_enable_scat = aml_w1_sdio_enable_scatter;
|
|
ops->hi_cleanup_scat = aml_w1_sdio_cleanup_scatter;
|
|
ops->hi_get_scatreq = aml_w1_sdio_scatter_req_get;
|
|
ops->hi_rcv_frame = aml_w1_sdio_recv_frame;
|
|
|
|
ops->hi_read_reg8 = aml_w1_sdio_read_reg8;
|
|
ops->hi_write_reg8 = aml_w1_sdio_write_reg8;
|
|
ops->hi_read_reg32 = aml_w1_sdio_read_reg32;
|
|
ops->hi_write_reg32 = aml_w1_sdio_write_reg32;
|
|
ops->hi_write_cmd = aml_w1_sdio_write_cmd32;
|
|
ops->hi_write_sram = aml_w1_sdio_write_sram;
|
|
ops->hi_read_sram = aml_w1_sdio_read_sram;
|
|
|
|
ops->hi_write_word = aml_w1_sdio_write_word;
|
|
ops->hi_read_word = aml_w1_sdio_read_word;
|
|
|
|
ops->bt_hi_write_sram = aml_w1_bt_sdio_write_sram;
|
|
ops->bt_hi_read_sram = aml_w1_bt_sdio_read_sram;
|
|
ops->bt_hi_write_word = aml_w1_bt_hi_write_word;
|
|
ops->bt_hi_read_word = aml_w1_bt_hi_read_word;
|
|
ops->hif_suspend = aml_w1_sdio_suspend;
|
|
|
|
//ops->hif_get_sts = hif_get_sts;
|
|
//ops->hif_pt_rx_start = hif_pt_rx_start;
|
|
//ops->hif_pt_rx_stop = hif_pt_rx_stop;
|
|
w1_sdio_after_porbe = 1;
|
|
|
|
// check and wake w1 firstly.
|
|
host_wake_w1_req = NULL;
|
|
host_suspend_req = NULL;
|
|
}
|
|
|
|
int aml_w1_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
|
|
{
|
|
int ret = 0;
|
|
static struct sdio_func sdio_func_0;
|
|
|
|
sdio_claim_host(func);
|
|
ret = sdio_enable_func(func);
|
|
if (ret)
|
|
goto sdio_enable_error;
|
|
|
|
sdio_set_block_size(func, 512);
|
|
|
|
printk("%s(%d): func->num %d sdio block size=%d, \n", __func__, __LINE__,
|
|
func->num, func->cur_blksize);
|
|
|
|
if (func->num == 1)
|
|
{
|
|
sdio_func_0.num = 0;
|
|
sdio_func_0.card = func->card;
|
|
g_w1_hwif_sdio.sdio_func_if[0] = &sdio_func_0;
|
|
}
|
|
g_w1_hwif_sdio.sdio_func_if[func->num] = func;
|
|
printk("%s(%d): func->num %d sdio_func=%p, \n", __func__, __LINE__,
|
|
func->num, func);
|
|
|
|
sdio_release_host(func);
|
|
sdio_set_drvdata(func, (void *)(&g_w1_hwif_sdio));
|
|
if (func->num != FUNCNUM_SDIO_LAST)
|
|
{
|
|
printk("%s(%d):func_num=%d, last func num=%d\n", __func__, __LINE__,
|
|
func->num, FUNCNUM_SDIO_LAST);
|
|
return 0;
|
|
}
|
|
|
|
aml_w1_sdio_init_ops();
|
|
|
|
return ret;
|
|
|
|
sdio_enable_error:
|
|
printk("sdio_enable_error: line %d\n",__LINE__);
|
|
sdio_release_host(func);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void aml_w1_sdio_remove(struct sdio_func *func)
|
|
{
|
|
if (func== NULL)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
printk("\n==========================================\n");
|
|
printk("aml_sdio_remove++ func->num =%d \n",func->num);
|
|
printk("==========================================\n");
|
|
|
|
sdio_claim_host(func);
|
|
sdio_disable_func(func);
|
|
sdio_release_host(func);
|
|
|
|
host_wake_w1_req = NULL;
|
|
host_suspend_req = NULL;
|
|
host_resume_req = NULL;
|
|
}
|
|
|
|
__attribute__((unused)) static int aml_sdio_pm_suspend(struct device *device)
|
|
{
|
|
if (host_suspend_req != NULL)
|
|
return host_suspend_req(device);
|
|
else
|
|
return aml_w1_sdio_suspend(1);
|
|
}
|
|
|
|
__attribute__((unused)) static int aml_sdio_pm_resume(struct device *device)
|
|
{
|
|
if (host_resume_req != NULL)
|
|
return host_resume_req(device);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
void write_byte_8ba(unsigned char Bus, unsigned char SlaveAddr,
|
|
unsigned char RegAddr, unsigned char Data)
|
|
{
|
|
unsigned int tmp,cnt = 0;
|
|
|
|
// Set the I2C bus to 100khz
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
tmp = (tmp & (~(0x3FF << I2C_CLOCK_OFFSET))) | (I2C_CLK_QTR << I2C_CLOCK_OFFSET);
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
|
|
// Set the I2C Address
|
|
aml_w1_sdio_write_word(I2C_SLAVE_ADDR, SlaveAddr);
|
|
// Fill the token registers
|
|
tmp = aml_w1_sdio_read_word(I2C_TOKEN_LIST_REG0);
|
|
tmp = (I2C_END << 16) |
|
|
(I2C_DATA << 12) | // Write Data
|
|
(I2C_DATA << 8) | // Write RegAddr
|
|
(I2C_SLAVE_ADDR_WRITE << 4) |
|
|
(I2C_START << 0);
|
|
aml_w1_sdio_write_word(I2C_TOKEN_LIST_REG0, tmp);
|
|
|
|
// Fill the write data registers
|
|
aml_w1_sdio_write_word(I2C_TOKEN_WDATA_REG0,(Data << 8) | (RegAddr << 0));
|
|
// Start and Wait
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
tmp &= (~(1 << 0));
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
tmp |= ( (1 << 0));
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
|
|
do {
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
|
|
cnt++;
|
|
if (cnt == 1000) {
|
|
printk("-------[ERR]-----> i2c[W] err\n");
|
|
break;
|
|
}
|
|
} while (tmp & (1 << 2));
|
|
|
|
}
|
|
|
|
unsigned char read_byte_8ba(unsigned char Bus, unsigned char SlaveAddr, unsigned char RegAddr)
|
|
{
|
|
//struct hw_interface* hif = hif_get_hw_interface();
|
|
unsigned int tmp,cnt = 0;
|
|
|
|
// Set the I2C bus to 100khz
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
tmp = (tmp & (~(0x3FF << I2C_CLOCK_OFFSET))) | (I2C_CLK_QTR << I2C_CLOCK_OFFSET);
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
|
|
// Set the I2C Address
|
|
aml_w1_sdio_write_word(I2C_SLAVE_ADDR, SlaveAddr);
|
|
// Fill the token registers
|
|
tmp = aml_w1_sdio_read_word(I2C_TOKEN_LIST_REG0);
|
|
tmp = (I2C_END << 24) |
|
|
(I2C_DATA_LAST << 20) | // this is a data read
|
|
(I2C_SLAVE_ADDR_READ << 16) |
|
|
(I2C_START << 12) |
|
|
(I2C_DATA << 8) | // This is a data write
|
|
(I2C_SLAVE_ADDR_WRITE << 4) |
|
|
(I2C_START << 0);
|
|
aml_w1_sdio_write_word(I2C_TOKEN_LIST_REG0, tmp);
|
|
|
|
// Fill the write data registers
|
|
aml_w1_sdio_write_word(I2C_TOKEN_WDATA_REG0,(RegAddr << 0));
|
|
// Start and Wait
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
tmp &= (~(1 << 0));
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
tmp |= ( (1 << 0));
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
|
|
do {
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
|
|
cnt++;
|
|
if (cnt == 1000) {
|
|
printk("-------[ERR]-----> i2c[W] err\n");
|
|
break;
|
|
}
|
|
} while (tmp & (1 << 2));
|
|
|
|
tmp = aml_w1_sdio_read_word(I2C_TOKEN_RDATA_REG0) & 0xff;
|
|
return (unsigned char)tmp;
|
|
}
|
|
|
|
|
|
//void write_rdaddr_32ba(U8 Bus, U8 SlaveAddr, U32 TkData0, U32 TkData1)
|
|
void write_word_32ba(unsigned char Bus, unsigned char SlaveAddr,
|
|
unsigned int StartToken, unsigned int Data)
|
|
{
|
|
//struct hw_interface* hif = hif_get_hw_interface();
|
|
unsigned int tmp,cnt = 0;
|
|
|
|
//printk("%s(%d) token 0x%x data 0x%x\n", __func__, __LINE__, StartToken,Data);
|
|
|
|
// Set the I2C bus to 100khz
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
tmp = (tmp & (~(0x3FF << I2C_CLOCK_OFFSET))) | (I2C_CLK_QTR << I2C_CLOCK_OFFSET);
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
|
|
// Set the I2C Address
|
|
aml_w1_sdio_write_word(I2C_SLAVE_ADDR, SlaveAddr);
|
|
// Fill the token registers
|
|
tmp = aml_w1_sdio_read_word(I2C_TOKEN_LIST_REG0);
|
|
tmp = (I2C_END << 28) | //
|
|
(I2C_DATA << 24) | //
|
|
(I2C_DATA << 20) | //
|
|
(I2C_DATA << 16) | //
|
|
(I2C_DATA << 12) | //
|
|
(I2C_DATA << 8) | // This shall be related to start token (token = 0x04 for receiving paddr, token = 0x00 for receiving pwdata)
|
|
(I2C_SLAVE_ADDR_WRITE << 4) |
|
|
(I2C_START << 0);
|
|
aml_w1_sdio_write_word(I2C_TOKEN_LIST_REG0, tmp);
|
|
|
|
//Fill the write data registers
|
|
aml_w1_sdio_write_word(I2C_TOKEN_WDATA_REG0,StartToken | (Data<<8));
|
|
aml_w1_sdio_write_word(I2C_TOKEN_WDATA_REG1,(Data >> 24));
|
|
//Start and Wait
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
tmp &= (~(1 << 0));
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
tmp |= ( (1 << 0));
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
|
|
tmp = 0;
|
|
do {
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
|
|
cnt++;
|
|
if (cnt == 100000) {
|
|
ERROR_DEBUG_OUT("-------[ERR]-----> i2c[W] err\n");
|
|
break;
|
|
}
|
|
} while (tmp & (1 << 2));
|
|
}
|
|
|
|
unsigned int read_word_32ba(unsigned int SlaveAddr, unsigned int RegAddr)
|
|
{
|
|
//struct hw_interface* hif = hif_get_hw_interface();
|
|
unsigned int tmp,cnt = 0;
|
|
|
|
// Set the I2C bus to 100khz
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
tmp = (tmp & (~(0x3FF << I2C_CLOCK_OFFSET))) | (I2C_CLK_QTR << I2C_CLOCK_OFFSET);
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
|
|
// Set the I2C Address
|
|
aml_w1_sdio_write_word(I2C_SLAVE_ADDR, SlaveAddr);
|
|
// Fill the token registers
|
|
tmp = aml_w1_sdio_read_word(I2C_TOKEN_LIST_REG0);
|
|
tmp = (I2C_DATA << 28) |
|
|
(I2C_DATA << 24) |
|
|
(I2C_DATA << 20) |
|
|
(I2C_SLAVE_ADDR_READ << 16) | // the second burst is for READ
|
|
(I2C_START << 12) | // after addr pointer has been reset to 0x00, we start a new burst for reading data out
|
|
(I2C_DATA << 8) | // This shall be related a 0x00 written into rf i2c in order to reset addr pointer to 0x00
|
|
(I2C_SLAVE_ADDR_WRITE << 4) | // the first burst is for WRITE token 0x00 to reset addr pointer
|
|
(I2C_START << 0); // start the first burst
|
|
aml_w1_sdio_write_word(I2C_TOKEN_LIST_REG0, tmp);
|
|
|
|
tmp = aml_w1_sdio_read_word(I2C_TOKEN_LIST_REG1);
|
|
tmp = (I2C_END << 4) | (I2C_DATA_LAST << 0);
|
|
aml_w1_sdio_write_word(I2C_TOKEN_LIST_REG1, tmp);
|
|
|
|
// Fill the write data registers
|
|
aml_w1_sdio_write_word(I2C_TOKEN_WDATA_REG0,RegAddr << 0);
|
|
aml_w1_sdio_write_word(I2C_TOKEN_WDATA_REG1,0);
|
|
// Start and Wait
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
tmp &= (~(1 << 0));
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
tmp |= ( (1 << 0));
|
|
aml_w1_sdio_write_word(I2C_CONTROL_REG, tmp);
|
|
|
|
tmp = 0;
|
|
do {
|
|
tmp = aml_w1_sdio_read_word(I2C_CONTROL_REG);
|
|
|
|
cnt++;
|
|
if (cnt == 100000) {
|
|
ERROR_DEBUG_OUT("-------[ERR]-----> i2c[R] err\n");
|
|
break;
|
|
}
|
|
} while( tmp & (1 << 2));
|
|
|
|
tmp = aml_w1_sdio_read_word(I2C_TOKEN_RDATA_REG0);
|
|
return tmp;
|
|
}
|
|
|
|
unsigned int rf_i2c_read(unsigned int reg_addr)
|
|
{
|
|
unsigned char bus = 0;
|
|
unsigned int slave_addr = 0x7a;
|
|
unsigned int read_data = 0;
|
|
unsigned int start_token;
|
|
|
|
start_token = 0x04;
|
|
|
|
/* 0x4-0x7 save address of read register in slave device*/
|
|
write_word_32ba(bus, slave_addr, start_token, reg_addr);
|
|
|
|
/* 0x0-0x3 save address of read data in slave device*/
|
|
read_data = read_word_32ba(slave_addr, 0x0);
|
|
return read_data;
|
|
}
|
|
|
|
void rf_i2c_write(unsigned int reg_addr, unsigned int data)
|
|
{
|
|
unsigned char bus = 0;
|
|
unsigned int slave_addr = 0x7a;
|
|
unsigned int start_token;
|
|
|
|
/* 0x0-0x3 save address of write data in slave device*/
|
|
start_token = 0x00; //for data to be written in
|
|
write_word_32ba(bus, slave_addr, 0x00, data);
|
|
|
|
//system_wait(1);
|
|
|
|
/* 0x4-0x7 save address of write register in slave device*/
|
|
start_token = 0x04; //for address to be written to
|
|
write_word_32ba(bus, slave_addr, start_token, reg_addr);
|
|
|
|
write_byte_8ba(bus, slave_addr, 0x8, bus);
|
|
}
|
|
|
|
void config_pmu_reg_off(void)
|
|
{
|
|
int value_pmu_A12 = 0;
|
|
int value_pmu_A15 = 0;
|
|
int value_pmu_A17 = 0;
|
|
int value_pmu_A18 = 0;
|
|
int value_pmu_A20 = 0;
|
|
int value_pmu_A22 = 0;
|
|
int value_pmu_A24 = 0;
|
|
int value_aon30 = 0;
|
|
|
|
RG_AON_A30_FIELD_T reg_aon30_data;
|
|
RG_AON_A29_FIELD_T reg_aon29_data;
|
|
|
|
unsigned char host_req_status= 0;
|
|
unsigned int reg_val = 0;
|
|
|
|
//reg_val = rf_read_register(RG_TOP_A2);
|
|
reg_val = rf_i2c_read(RG_TOP_A2);
|
|
reg_val = reg_val &(~0x1f);
|
|
/*RF enter sx mode*/
|
|
// reg_val = reg_val |(0x15 );
|
|
|
|
/*RF enter sleep mode*/
|
|
//reg_val = reg_val |(0x10 );
|
|
|
|
/*2.4G sx mode*/
|
|
reg_val |= 0x15;
|
|
rf_i2c_write(RG_TOP_A2, reg_val);
|
|
|
|
/*infor BT ,wifi not work*/
|
|
reg_val = aml_w1_sdio_read_word(RG_PMU_A16);
|
|
reg_val &= (~BIT(31));
|
|
aml_w1_sdio_write_word(RG_PMU_A16, reg_val);
|
|
|
|
/*close all WIFI coexist thread*/
|
|
reg_val = aml_w1_sdio_read_word(RG_COEX_WF_OWNER_CTRL);
|
|
reg_val &= (~BIT(28) );
|
|
aml_w1_sdio_write_word(RG_COEX_WF_OWNER_CTRL, reg_val);
|
|
|
|
{
|
|
value_pmu_A12 = aml_w1_sdio_read_word(RG_PMU_A12);
|
|
value_pmu_A15 = aml_w1_sdio_read_word(RG_PMU_A15);
|
|
value_pmu_A17 = aml_w1_sdio_read_word(RG_PMU_A17);
|
|
value_pmu_A18 = aml_w1_sdio_read_word(RG_PMU_A18);
|
|
value_pmu_A20 = aml_w1_sdio_read_word(RG_PMU_A20);
|
|
value_pmu_A22 = aml_w1_sdio_read_word(RG_PMU_A22);
|
|
value_pmu_A24 = aml_w1_sdio_read_word(RG_PMU_A24);
|
|
value_aon30 = aml_w1_sdio_read_word(RG_AON_A30);
|
|
printk("%s power off: before write A12=0x%x, A15=0x%x, A17=0x%x, A18=0x%x, A20=0x%x, A22=0x%x, A24=0x%x, AON30=0x%x\n",
|
|
__func__, value_pmu_A12,value_pmu_A15,value_pmu_A17,value_pmu_A18,value_pmu_A20,value_pmu_A22,value_pmu_A24, value_aon30);
|
|
|
|
aml_w1_sdio_write_word(RG_INTF_CPU_CLK, 0x4f070001);
|
|
|
|
reg_aon29_data.data = aml_w1_sdio_read_word(RG_AON_A29);
|
|
reg_aon29_data.b.rg_ana_bpll_cfg |= BIT(1) | BIT(0);
|
|
aml_w1_sdio_write_word(RG_AON_A29, reg_aon29_data.data);
|
|
|
|
aml_w1_sdio_write_word(RG_PMU_A12, 0x9ea2e); //set dpll_val(bit16) for sdio resp_timeout
|
|
aml_w1_sdio_write_word(RG_PMU_A14, 0x1);
|
|
aml_w1_sdio_write_word(RG_PMU_A16, 0x0);
|
|
aml_w1_sdio_write_word(RG_PMU_A22, 0x707);
|
|
aml_w1_sdio_write_word(RG_PMU_A18, 0x8700);
|
|
aml_w1_sdio_write_word(RG_PMU_A20, 0x3ff01ff);
|
|
aml_w1_sdio_write_word(RG_PMU_A17, 0x703);
|
|
//aml_w1_sdio_write_word(RG_PMU_A24, 0x3f20000);
|
|
|
|
reg_aon30_data.data = aml_w1_sdio_read_word(RG_AON_A30);
|
|
/* change rf to idle mode */
|
|
reg_aon30_data.b.rg_always_on_cfg4 |= BIT(12);
|
|
aml_w1_sdio_write_word(RG_AON_A30, reg_aon30_data.data);
|
|
|
|
value_pmu_A12 = aml_w1_sdio_read_word(RG_PMU_A12);
|
|
value_pmu_A15 = aml_w1_sdio_read_word(RG_PMU_A15);
|
|
value_pmu_A17 = aml_w1_sdio_read_word(RG_PMU_A17);
|
|
value_pmu_A18 = aml_w1_sdio_read_word(RG_PMU_A18);
|
|
value_pmu_A20 = aml_w1_sdio_read_word(RG_PMU_A20);
|
|
value_pmu_A22 = aml_w1_sdio_read_word(RG_PMU_A22);
|
|
value_pmu_A24 = aml_w1_sdio_read_word(RG_PMU_A24);
|
|
value_aon30 = aml_w1_sdio_read_word(RG_AON_A30);
|
|
printk("%s power off: after write A12=0x%x, A15=0x%x, A17=0x%x, A18=0x%x, A20=0x%x, A22=0x%x, A24=0x%x, AON30=0x%x\n",
|
|
__func__, value_pmu_A12,value_pmu_A15,value_pmu_A17,value_pmu_A18,value_pmu_A20,value_pmu_A22,value_pmu_A24, value_aon30);
|
|
|
|
//force wifi pmu fsm to sleep mode
|
|
host_req_status = (0x8 << 1)| BIT(0);
|
|
aml_w1_sdio_bottom_write8(SDIO_FUNC1, 0x221, host_req_status);
|
|
}
|
|
}
|
|
|
|
static void aml_sdio_shutdown(struct device *device)
|
|
{
|
|
printk("===>>> enter %s <<<===\n", __func__);
|
|
shutdown_i += 1;
|
|
if (shutdown_i == 1) {
|
|
config_pmu_reg_off();
|
|
} else if (shutdown_i == 7) {
|
|
shutdown_i = 0;
|
|
printk("===>>> end <<<===\n");
|
|
} else {
|
|
;
|
|
}
|
|
printk("=== shutdown_i:%d ===\n", shutdown_i);
|
|
}
|
|
|
|
|
|
static SIMPLE_DEV_PM_OPS(aml_sdio_pm_ops, aml_sdio_pm_suspend,
|
|
aml_sdio_pm_resume);
|
|
|
|
|
|
static const struct sdio_device_id aml_w1_sdio[] =
|
|
{
|
|
{SDIO_DEVICE(W1_VENDOR_AMLOGIC,W1_PRODUCT_AMLOGIC) },
|
|
{SDIO_DEVICE(W1_VENDOR_AMLOGIC_EFUSE,W1_PRODUCT_AMLOGIC_EFUSE)},
|
|
{}
|
|
};
|
|
|
|
static struct sdio_driver aml_w1_sdio_driver =
|
|
{
|
|
.name = "aml_w1_sdio",
|
|
.id_table = aml_w1_sdio,
|
|
.probe = aml_w1_sdio_probe,
|
|
.remove = aml_w1_sdio_remove,
|
|
.drv.pm = &aml_sdio_pm_ops,
|
|
.drv.shutdown = aml_sdio_shutdown,
|
|
};
|
|
|
|
#ifdef NOT_AMLOGIC_PLATFORM
|
|
|
|
#define AML_STATIC_VERSION_STR "101.10.361.10 (wlan=r892223-20210623-1)"
|
|
|
|
#define DHD_SKB_1PAGE_BUFSIZE (PAGE_SIZE * 1)
|
|
#define DHD_SKB_2PAGE_BUFSIZE (PAGE_SIZE * 2)
|
|
#define DHD_SKB_4PAGE_BUFSIZE (PAGE_SIZE * 4)
|
|
|
|
#define DHD_SKB_1PAGE_BUF_NUM 8
|
|
#define DHD_SKB_2PAGE_BUF_NUM 8
|
|
#define DHD_SKB_4PAGE_BUF_NUM 1
|
|
|
|
#define FW_VERBOSE_RING_SIZE (256 * 1024)
|
|
#define DHD_PREALLOC_MEMDUMP_RAM_SIZE (1290 * 1024)
|
|
#define NAN_EVENT_RING_SIZE (64 * 1024)
|
|
#define WLAN_SKB_1_2PAGE_BUF_NUM ((DHD_SKB_1PAGE_BUF_NUM) + \
|
|
(DHD_SKB_2PAGE_BUF_NUM))
|
|
#define WLAN_SKB_BUF_NUM ((WLAN_SKB_1_2PAGE_BUF_NUM) + (DHD_SKB_4PAGE_BUF_NUM))
|
|
|
|
void *wlan_static_dhd_memdump_ram_buf;
|
|
void *wlan_static_nan_event_ring_buf;
|
|
void *wlan_static_fw_verbose_ring_buf;
|
|
|
|
enum dhd_prealloc_index {
|
|
DHD_PREALLOC_SKB_BUF = 4,
|
|
DHD_PREALLOC_MEMDUMP_RAM = 11,
|
|
DHD_PREALLOC_FW_VERBOSE_RING = 20,
|
|
DHD_PREALLOC_NAN_EVENT_RING = 23,
|
|
DHD_PREALLOC_MAX
|
|
};
|
|
|
|
void *aml_mem_prealloc(int section, unsigned long size)
|
|
{
|
|
PRINT("sectoin %d, size %ld\n", section, size);
|
|
|
|
if (section == DHD_PREALLOC_MEMDUMP_RAM) {
|
|
if (size > DHD_PREALLOC_MEMDUMP_RAM_SIZE) {
|
|
PRINT("request MEMDUMP_RAM(%lu) > %d\n",
|
|
size, DHD_PREALLOC_MEMDUMP_RAM_SIZE);
|
|
return NULL;
|
|
}
|
|
return wlan_static_dhd_memdump_ram_buf;
|
|
}
|
|
|
|
if (section == DHD_PREALLOC_FW_VERBOSE_RING) {
|
|
if (size > FW_VERBOSE_RING_SIZE) {
|
|
PRINT("request FW_VERBOSE_RING(%lu) > %d\n",
|
|
size, FW_VERBOSE_RING_SIZE);
|
|
return NULL;
|
|
}
|
|
return wlan_static_fw_verbose_ring_buf;
|
|
}
|
|
|
|
if (section < 0 || section > DHD_PREALLOC_MAX)
|
|
PRINT("request section id(%d) is out of max %d\n",
|
|
section, DHD_PREALLOC_MAX);
|
|
|
|
PRINT("failed to alloc section %d, size=%ld\n",
|
|
section, size);
|
|
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(aml_mem_prealloc);
|
|
|
|
int aml_init_wlan_mem(void)
|
|
{
|
|
unsigned long size = 0;
|
|
PRINT("%s\n", AML_STATIC_VERSION_STR);
|
|
|
|
wlan_static_dhd_memdump_ram_buf = kmalloc(DHD_PREALLOC_MEMDUMP_RAM_SIZE, GFP_KERNEL);
|
|
if (!wlan_static_dhd_memdump_ram_buf)
|
|
goto err_mem_alloc;
|
|
size += DHD_PREALLOC_MEMDUMP_RAM_SIZE;
|
|
PRINT("sectoin %d, size=%d\n",
|
|
DHD_PREALLOC_MEMDUMP_RAM, DHD_PREALLOC_MEMDUMP_RAM_SIZE);
|
|
|
|
wlan_static_fw_verbose_ring_buf = kmalloc(FW_VERBOSE_RING_SIZE, GFP_KERNEL);
|
|
if (!wlan_static_fw_verbose_ring_buf)
|
|
goto err_mem_alloc;
|
|
size += FW_VERBOSE_RING_SIZE;
|
|
PRINT("sectoin %d, size=%d\n",
|
|
DHD_PREALLOC_FW_VERBOSE_RING, FW_VERBOSE_RING_SIZE);
|
|
|
|
PRINT("prealloc ok: %ld(%ldK)\n", size, size / 1024);
|
|
return 0;
|
|
|
|
err_mem_alloc:
|
|
kfree(wlan_static_dhd_memdump_ram_buf);
|
|
kfree(wlan_static_fw_verbose_ring_buf);
|
|
PRINT("Failed to mem_alloc for WLAN\n");
|
|
|
|
return -ENOMEM;
|
|
}
|
|
#endif
|
|
|
|
int aml_w1_sdio_init(void)
|
|
{
|
|
int err = 0;
|
|
|
|
//amlwifi_set_sdio_host_clk(200000000);//200MHZ
|
|
|
|
err = sdio_register_driver(&aml_w1_sdio_driver);
|
|
w1_sdio_driver_insmoded = 1;
|
|
wifi_in_insmod = 0;
|
|
wifi_in_rmmod = 0;
|
|
PRINT("*****************aml sdio common driver is insmoded********************\n");
|
|
if (err)
|
|
PRINT("failed to register sdio driver: %d \n", err);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(aml_w1_sdio_init);
|
|
|
|
void aml_w1_sdio_exit(void)
|
|
{
|
|
PRINT("aml_w1_sdio_exit++ \n");
|
|
sdio_unregister_driver(&aml_w1_sdio_driver);
|
|
w1_sdio_driver_insmoded = 0;
|
|
w1_sdio_after_porbe = 0;
|
|
PRINT("*****************aml sdio common driver is rmmoded********************\n");
|
|
}
|
|
//EXPORT_SYMBOL(aml_w1_sdio_exit);
|
|
|
|
EXPORT_SYMBOL(w1_sdio_driver_insmoded);
|
|
EXPORT_SYMBOL(wifi_in_insmod);
|
|
EXPORT_SYMBOL(wifi_in_rmmod);
|
|
EXPORT_SYMBOL(w1_sdio_after_porbe);
|
|
EXPORT_SYMBOL(host_wake_w1_req);
|
|
EXPORT_SYMBOL(host_suspend_req);
|
|
EXPORT_SYMBOL(host_resume_req);
|
|
EXPORT_SYMBOL(wifi_sdio_access);
|
|
|
|
EXPORT_SYMBOL(aml_wifi_sdio_power_lock);
|
|
EXPORT_SYMBOL(aml_wifi_sdio_power_unlock);
|
|
/*set_wifi_bt_sdio_driver_bit() is used to determine whether to unregister sdio power driver.
|
|
*Only when w1_sdio_wifi_bt_alive is 0, then call aml_w1_sdio_exit().
|
|
*/
|
|
void set_wifi_bt_sdio_driver_bit(bool is_register, int shift)
|
|
{
|
|
AML_W1_BT_WIFI_MUTEX_ON();
|
|
if (is_register) {
|
|
w1_sdio_wifi_bt_alive |= (1 << shift);
|
|
PRINT("Insmod %s sdio driver!\n", (shift ? "WiFi":"BT"));
|
|
} else {
|
|
PRINT("Rmmod %s sdio driver!\n", (shift ? "WiFi":"BT"));
|
|
w1_sdio_wifi_bt_alive &= ~(1 << shift);
|
|
if (!w1_sdio_wifi_bt_alive) {
|
|
aml_w1_sdio_exit();
|
|
}
|
|
}
|
|
AML_W1_BT_WIFI_MUTEX_OFF();
|
|
}
|
|
EXPORT_SYMBOL(set_wifi_bt_sdio_driver_bit);
|
|
EXPORT_SYMBOL(g_w1_hwif_sdio);
|
|
EXPORT_SYMBOL(g_w1_hif_ops);
|
|
|
|
static int aml_w1_sdio_insmod(void)
|
|
{
|
|
#ifdef NOT_AMLOGIC_PLATFORM
|
|
int ret;
|
|
ret = aml_init_wlan_mem();
|
|
if (ret) {
|
|
PRINT("aml_init_wlan_mem err: %d \n", ret);
|
|
return -ENOMEM;
|
|
}
|
|
#endif
|
|
aml_w1_sdio_init();
|
|
printk("%s(%d) start...\n",__func__, __LINE__);
|
|
return 0;
|
|
}
|
|
|
|
static void aml_w1_sdio_rmmod(void)
|
|
{
|
|
aml_w1_sdio_exit();
|
|
}
|
|
|
|
module_init(aml_w1_sdio_insmod);
|
|
module_exit(aml_w1_sdio_rmmod);
|
|
MODULE_LICENSE("GPL");
|