Files
刘可亮 803cac77d5 V1.0.6
2024-09-03 11:16:08 +08:00

1198 lines
35 KiB
C

#include <stdio.h>
#include "uwifi_sdio.h"
#include "asr_rtos_api.h"
#include "asr_dbg.h"
#include "uwifi_hif.h"
#include "uwifi_msg.h"
#if !defined ALIOS_SUPPORT && !defined JL_SDK && !defined THREADX_STM32 && !defined AWORKS && !defined STM32H7_SDK && !defined(HX_SDK) && !defined(JXC_SDK)
#include "gpio.h"
#include "sdio.h"
#else
#include "asr_gpio.h"
#include "asr_sdio.h"
#endif
#ifdef THREADX
#include "timer.h"
#include "cgpio.h"
#include "sdio_api.h"
#endif
//extern int tx_conserve;
asr_hisr_t sdio_card_gpio_hisr = NULL;
bool fw_download_done = 0;
extern uint8_t is_fw_downloaded;
extern bool txlogen;
int count_sdio_card_gpio_evt = 0;
int tx_aggr = 8;
void sdio_card_gpio_hisr_func(void)
{
//dbg(D_ERR, D_UWIFI_CTRL, "%s ..\r\n", __func__);
uwifi_sdio_event_set(UWIFI_SDIO_EVENT_RX);
if (txlogen)
dbg(D_ERR, D_UWIFI_CTRL, "tri rx ..\r\n");
count_sdio_card_gpio_evt ++;
}
/*
void sdio_card_gpio_isr(void)
{
if (0 == fw_download_done || NULL == sdio_card_gpio_hisr|| 0 == is_fw_downloaded)//ignore it until firmware done
return;
// dbg(D_ERR, D_UWIFI_CTRL, "%s ...\r\n", __func__);
//count_sdio_card_gpio_isr ++;
asr_rtos_active_hisr(&sdio_card_gpio_hisr);
}
GPIOConfiguration sdio_card_gpio = {
GPIO_IN_PIN,
0,
GPIO_PULLUP_ENABLE,
GPIO_TWO_EDGE,
sdio_card_gpio_isr,
NULL,
};
*/
/*
get_ioport: get ioport value
*/
int sdio_get_ioport(void)
{
bool ret;
uint8_t reg;
int ioport;
dbg(D_DBG, D_UWIFI_CTRL, "%s start...\r\n", __func__);
/*get out ioport value*/
asr_sdio_claim_host();
ret = sdio_readb_cmd52(IO_PORT_0, &reg);
asr_sdio_release_host();
if (!ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s can't read 0x%x\n", __func__, IO_PORT_0);
goto exit;
}
ioport = reg & 0xff;
asr_sdio_claim_host();
ret = sdio_readb_cmd52(IO_PORT_1, &reg);
asr_sdio_release_host();
if (!ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s can't read 0x%x\n", __func__, IO_PORT_1);
goto exit;
}
ioport |= ((reg & 0xff) << 8);
asr_sdio_claim_host();
ret = sdio_readb_cmd52(IO_PORT_2, &reg);
asr_sdio_release_host();
if (!ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s can't read 0x%x\n", __func__, IO_PORT_2);
goto exit;
}
ioport |= ((reg & 0xff) << 16);
dbg(D_DBG, D_UWIFI_CTRL, "%s end ioport:0x%X\r\n", __func__, ioport);
return ioport;
exit:
return -1;
}
int asr_sdio_config_rsr(uint8_t mask)
{
uint8_t reg_val;
bool ret = 0;
asr_sdio_claim_host();
ret = sdio_readb_cmd52(HOST_INT_RSR, &reg_val);
asr_sdio_release_host();
if (!ret)
return ret;
reg_val |= mask;
asr_sdio_claim_host();
ret = sdio_writeb_cmd52(HOST_INT_RSR, reg_val);
asr_sdio_release_host();
return ret;
}
//extern int scan_done_flag;
unsigned char asr_sdio_read_cmd53(unsigned int dataport, unsigned char *data, int size)
{
//int retry_cnt = 0;
int ret = -1;
//while(retry_cnt++<300 && SCAN_DEINIT != scan_done_flag)
//{
ret = sdio_read_cmd53(dataport, data, size);
#if 0
if(ret)
{
dbg(D_ERR, D_UWIFI_CTRL,"%s err ret:%d cnt:%d, addr:0x%x,len:%d\n",__func__,ret,retry_cnt,dataport,size);
#if 1
// if read cmd53 error need funnum0 0ffset06 set bit0-bit2==1
int _ret = SDIO_CMD52(1,0,0x06,1,0); // write func0 0x6(I/O Abort reg) , 0x1 means to stop func1 transfer.
dbg(D_ERR, D_UWIFI_CTRL,"%s fix ret=%d\n",__func__,_ret);
asr_rtos_delay_milliseconds(25);// sleep more time
_ret = SDIO_CMD52(1,1,0x03,0,0); // write func1 0x3(host interrupt status) , clear host interrupt status
dbg(D_ERR, D_UWIFI_CTRL,"%s host_intstatus ret=%d\n",__func__,_ret);
asr_rtos_delay_milliseconds(20);// sleep more time
#endif
continue;
}
else
break;
#endif
//}
return (unsigned char)ret;
}
int asr_sdio_config_auto_reenable(void)
{
uint8_t reg_val;
bool ret = 0;
asr_sdio_claim_host();
ret = sdio_readb_cmd52(SDIO_CONFIG2, &reg_val);
asr_sdio_release_host();
if (!ret)
return ret;
reg_val |= AUTO_RE_ENABLE_INT;
asr_sdio_claim_host();
ret = sdio_writeb_cmd52(SDIO_CONFIG2, reg_val);
asr_sdio_release_host();
return ret;
}
int asr_sdio_init_config(struct asr_hw *asr_hw)
{
int ioport;
int ret = 0;
/*step 0: get ioport*/
ioport = sdio_get_ioport();
if (ioport < 0) {
dbg(D_ERR, D_UWIFI_CTRL, "%s get ioport failed\n", __func__);
goto exit;
}
asr_hw->ioport = ioport & 0xffff0000;
/*step 1: config host_int_status(0x03) clear when read*/
ret = asr_sdio_config_rsr(HOST_INT_RSR_MASK);
if (!ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s config int rsr failed\n", __func__);
goto exit;
}
/*step 2: config auto re-enable(0x6c) bit4*/
ret = asr_sdio_config_auto_reenable();
if (!ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s config auto re-enable failed\n", __func__);
goto exit;
}
return 0;
exit:
return -1;
}
uint8_t asr_get_tx_aggr_bitmap_addr(uint8_t start_port, uint8_t end_port)
{
int i;
uint8_t bitmap = 0;
if(start_port <= end_port)
{
for(i=0; i<end_port-start_port+1; i++)
{
bitmap |= (1<<i);
}
}
else
{
for(i=0; i<end_port+17-start_port; i++)
{
if(start_port+i != 16)
bitmap |= (1<<i);
}
}
return bitmap;
}
uint16_t asr_get_tx_bitmap_from_ports(uint8_t start_port, uint8_t end_port)
{
int i;
uint16_t bitmap = 0;
if(start_port <= end_port)
{
for(i=start_port; i<=end_port; i++)
{
bitmap |= (1<<i);
}
}
else
{
for(i=start_port; i<=15; i++)
{
bitmap |= (1<<i);
}
for(i=1; i<=end_port; i++)
{
bitmap |= (1<<i);
}
}
return bitmap;
}
int asr_sdio_tx_common_port_dispatch(struct asr_hw *asr_hw, u8 * src, u32 len, unsigned int io_addr, u16 bitmap_record)
{
int ret = 0;
//struct sdio_func *func = asr_hw->plat->func;
asr_sdio_claim_host();
ret = sdio_write_cmd53(io_addr, src, len);
asr_rtos_lock_mutex(&asr_hw->tx_msg_lock);
asr_hw->tx_use_bitmap &= ~(bitmap_record);
asr_rtos_unlock_mutex(&asr_hw->tx_msg_lock);
#ifdef TXBM_DELAY_UPDATE
asr_hw->tx_last_trans_bitmap = bitmap_record;
#endif
asr_sdio_release_host();
//seconds = ktime_to_us(ktime_get());
//dbg("tda %d\n%s",((int)ktime_to_us(ktime_get())-seconds), "\0");
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s write data failed,ret=%d\n", __func__, ret);
}
return ret;
}
int asr_sdio_enable_int(uint8_t mask)
{
uint8_t reg_val, reg_val1;
int ret = -1;
if (!(mask & HOST_INT_MASK)) {
dbg(D_ERR, D_UWIFI_CTRL, "%s 0x%x is invalid int mask\n", __func__, mask);
return ret;
}
/*enable specific interrupt*/
asr_sdio_claim_host();
ret = sdio_readb_cmd52(HOST_INTMASK, &reg_val);
asr_sdio_release_host();
if (!ret)
return ret;
reg_val |= (mask & HOST_INT_MASK);
asr_sdio_claim_host();
ret = sdio_writeb_cmd52(HOST_INTMASK, reg_val);
ret = sdio_readb_cmd52(HOST_INTMASK, &reg_val1);
asr_sdio_release_host();
if (!ret)
return ret;
return ret;
}
int asr_sdio_disable_int(uint8_t mask)
{
uint8_t reg_val;
int ret = -1;
/*enable specific interrupt*/
asr_sdio_claim_host();
ret = sdio_readb_cmd52(HOST_INTMASK, &reg_val);
asr_sdio_release_host();
if (!ret)
{
dbg(D_ERR, D_UWIFI_CTRL,"%s read host int mask failed\n", __func__);
return ret;
}
reg_val &= ~mask;
asr_sdio_claim_host();
ret = sdio_writeb_cmd52(HOST_INTMASK, reg_val);
asr_sdio_release_host();
if (!ret)
{
dbg(D_ERR, D_UWIFI_CTRL,"%s disable host int mask failed\n", __func__);
return ret;
}
asr_sdio_claim_host();
ret = sdio_readb_cmd52(HOST_INTMASK, &reg_val);
asr_sdio_release_host();
if (!ret)
{
dbg(D_ERR, D_UWIFI_CTRL,"%s read host int mask failed\n", __func__);
return ret;
}
return ret;
}
int asr_sdio_detect_change()
{
return sdio_bus_probe();
}
uint32_t crc32_recursive(const uint8_t *buf, uint32_t len, uint32_t crc_in)
{
uint32_t i;
uint32_t crc = ~crc_in;
while (len--) {
crc ^= *buf++;
for (i = 0; i < 8; ++i) {
if (crc & 1)
crc = (crc >> 1) ^ 0xEDB88320;
else
crc = (crc >> 1);
}
}
return ~crc;
}
#if 0
static void sdio_delay(uint32_t count)
{
uint32_t i = 0;
uint32_t j = 0;
for(i=0; i<count; i++)
{
for(j=0; j<30000; j++)
{
;
}
}
}
#endif
/*
poll_card_status: check if card in ready state
*/
int poll_card_status(uint8_t mask)
{
uint32_t tries;
uint8_t reg_val;
int ret;
for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
asr_sdio_claim_host();
ret = sdio_readb_cmd52(C2H_INTEVENT, &reg_val);
asr_sdio_release_host();
if (!ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s can't read 0x%x out\n", __func__, C2H_INTEVENT);
break;
}
//dbg(D_ERR, D_UWIFI_CTRL, "%s reg_val:0x%x mask:0x%x\n", __func__, reg_val, mask);
if ((reg_val & mask) == mask)
return 0;
else {
dbg(D_ERR, D_UWIFI_CTRL, "%d regval 0x%x\n", tries,reg_val);
}
//sdio_delay(10);
asr_rtos_delay_milliseconds(10);
}
dbg(D_ERR, D_UWIFI_CTRL, "[poll_card_status]fail:%d regval 0x%x\n", tries,reg_val);
return -1;
}
/*
check_scratch_status: check scratch register value
*/
int check_scratch_status(uint16_t status)
{
uint32_t tries;
//uint16_t scratch_val;
uint8_t scratch_val;
int ret;
for (tries = 0; tries < MAX_POLL_TRIES * 10; tries++) {
asr_sdio_claim_host();
ret = sdio_readb_cmd52(SCRATCH_1, &scratch_val);
asr_sdio_release_host();
if (!ret) {
dbg(D_ERR, D_UWIFI_CTRL, "can't read 0x%x out\n", SCRATCH_1);
break;
}
if ((scratch_val << 8) & status) {
dbg(D_ERR, D_UWIFI_CTRL, "scratch_val1:0x%x status:0x%x\n", scratch_val, status);
return 0; //success
} else {
dbg(D_WRN, D_UWIFI_CTRL, "%d regval 0x%x\n",tries,scratch_val);
}
//sdio_delay(1);
asr_rtos_delay_milliseconds(20);
}
dbg(D_ERR, D_UWIFI_CTRL, "check scratch fail:%d regval 0x%x\n",tries,scratch_val);
asr_sdio_claim_host();
ret = sdio_readb_cmd52(SCRATCH_0, &scratch_val);
asr_sdio_release_host();
if (!ret) {
dbg(D_ERR, D_UWIFI_CTRL, "can't read 0x%x out\n", SCRATCH_0);
} else {
dbg(D_ERR, D_UWIFI_CTRL, "check scratch0 :regval 0x%x\n",scratch_val);
}
return -1;//fail
}
//extern int tx_aggr;
#define MAX_AGG_THRESHOLD 1
uint8_t asr_sdio_tx_get_available_data_port(struct asr_hw *asr_hw,
uint16_t ava_pkt_num, uint8_t * port_num, unsigned int *io_addr, uint16_t * bitmap_record)
{
u16 bitmap;
u8 start_port;
u8 end_port;
uint8_t port_idx;
//u16 bitmap_record = 0x0;
*bitmap_record = 0;
#if 1
if (ava_pkt_num > tx_aggr)
ava_pkt_num = tx_aggr;
*io_addr = 0x0;
port_idx = 0x0;
bitmap = asr_hw->tx_use_bitmap;
if (bitmap & (1 << asr_hw->tx_data_cur_idx)) {
start_port = asr_hw->tx_data_cur_idx;
end_port = asr_hw->tx_data_cur_idx;
*io_addr |= (1 << port_idx++);
*port_num = 1;
ava_pkt_num--;
*bitmap_record |= (1 << asr_hw->tx_data_cur_idx);
if ((++asr_hw->tx_data_cur_idx) == 16) {
asr_hw->tx_data_cur_idx = 1;
if (ava_pkt_num) {
//ava_pkt_num--;
*io_addr &= ~(1 << port_idx++);
}
}
} else {
return 0;
}
while (ava_pkt_num--) {
if (bitmap & (1 << asr_hw->tx_data_cur_idx)) {
*bitmap_record |= (1 << asr_hw->tx_data_cur_idx);
end_port = asr_hw->tx_data_cur_idx;
(*port_num)++;
*io_addr |= (1 << port_idx++);
if ((++asr_hw->tx_data_cur_idx) == 16) {
asr_hw->tx_data_cur_idx = 1;
if (ava_pkt_num) {
//ava_pkt_num--;
*io_addr &= ~(1 << port_idx++);
}
}
} else
break;
if (port_idx >= tx_aggr)
break;
}
if (*port_num == 1)
*io_addr = asr_hw->ioport | start_port;
else
*io_addr = asr_hw->ioport | 0x1000 | start_port | ((*io_addr) << 4);
return *port_num;
#else
bitmap = asr_hw->tx_use_bitmap;
if (bitmap & (1 << asr_hw->tx_data_cur_idx)) {
*start_port = asr_hw->tx_data_cur_idx;
*end_port = asr_hw->tx_data_cur_idx;
if (++asr_hw->tx_data_cur_idx == 16)
asr_hw->tx_data_cur_idx = 1;
if (ava_pkt_num > tx_aggr)
*port_num = tx_aggr;
else
*port_num = ava_pkt_num;
} else {
*port_num = 0;
}
return *port_num;
#endif
}
bool asr_sdio_tx_msg_port_available(struct asr_hw * asr_hw)
{
bool ret;
asr_rtos_lock_mutex(&asr_hw->tx_msg_lock);
ret = (asr_hw->tx_use_bitmap & (1 << SDIO_PORT_IDX(0))) ? true : false;
asr_hw->tx_use_bitmap &= ~(1 << SDIO_PORT_IDX(0));
asr_rtos_unlock_mutex(&asr_hw->tx_msg_lock);
return ret;
}
int asr_sdio_send_data(struct asr_hw *asr_hw,uint8_t type,uint8_t* src,uint16_t len,uint32_t io_addr,uint16_t bitmap_record)
{
int ret = 0;
static int times = 0;
if(type == HIF_TX_MSG)//msg
{
int count = 20;
if(times==0)//first time,poll state
{
times++;
ret = poll_card_status(C2H_DNLD_CARD_RDY | C2H_IO_RDY);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s card is not in rdy\n", __func__);
return -EBUSY;
}
}
else
{
while ((!(asr_sdio_tx_msg_port_available(asr_hw))) && (--count)) //no available msg port 0
{
asr_rtos_delay_milliseconds(1);
}
if (count == 0) {
dbg(D_ERR, D_UWIFI_CTRL, "ERROR: msg port 0 busy! wbm=0x%x\n",asr_hw->tx_use_bitmap);
return -EBUSY;
}
}
//adjust len, must be block size blocks
len = len + 4; // 4 is for end token
len = ASR_ALIGN_BLKSZ_HI(len);
#if 0
{
uint32_t *temp = (uint32_t*)src;
if (src)
dbg(D_ERR, D_UWIFI_CTRL, "tx msg %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n",temp[0],temp[1],temp[2],temp[3],temp[4],temp[5],temp[6],temp[7],temp[8],temp[9],temp[10],temp[11],temp[12],temp[13],temp[14],temp[15]);
else
dbg(D_ERR, D_UWIFI_CTRL, "src is null\n");
}
#endif
ret = asr_sdio_tx_common_port_dispatch(asr_hw, src, len, io_addr, 0x1);
}
else//data
{
if (len % SDIO_BLOCK_SIZE)
dbg(D_ERR, D_UWIFI_CTRL, "data size:%d is not %d aligned\r\n", len, SDIO_BLOCK_SIZE);
//dbg(D_ERR, D_UWIFI_CTRL, "[%s] transfer start:%d end:%d aggr_num:%d src:0x%x len:%d", __func__, start_port, end_port, (start_port>end_port) ?(start_port - end_port + 1):(end_port-start_port +1),src, len);
ret = asr_sdio_tx_common_port_dispatch(asr_hw, src, len, io_addr, bitmap_record);
}
return ret;
}
/*
download_firmware: download firmware into card
total data format will be: | header data | fw_data | padding | CRC |(CRC check fw_data + padding)
header data will be: | fw_len | transfer unit | transfer times |
*/
extern unsigned int sdio_get_block_size(void);
#if 0
struct asr_hw* uwifi_get_asr_hw(void)
{
return sp_asr_hw;
}
#endif
#ifdef SDIO_CLOSE_PS
static int _first_download = 0;
#endif
#if defined(CONFIG_ASR5505) || defined(CONFIG_ASR595X)
int asr_sdio_download_firmware(struct asr_hw *asr_hw, uint8_t *fw_img_data, uint32_t fw_img_size)
{
#ifdef SDIO_CLOSE_PS
if(_first_download ==0)
{
#endif
int ret;
uint32_t header_data[3];
uint32_t total_len, pad_len;
//int sdio2host_gpio_no;
uint8_t *fw_buf, *temp_fw_buf;
uint8_t padding_data[4] = {0};
uint8_t *tempbuf = NULL;
uint8_t crc_len;
uint32_t fw_crc = 0;
int retry_times = 3;
int blk_size;
blk_size = sdio_get_block_size() * 2;// /2 to change to byte mode; *2 change to block mode
// unused:
//sdio2host_gpio_no = get_sdio2host_gpio_no();
/*step 0 prepare header data and other initializations*/
pad_len = (fw_img_size % 4) ? (4 - (fw_img_size % 4)) : 0;
crc_len = 4;
total_len = fw_img_size + pad_len + crc_len;
//total_len = ASR_ALIGN_DLDBLKSZ_HI(total_len);
total_len = ((total_len + blk_size - 1) & (~(blk_size - 1)));
//blk_size = total_len;
header_data[0] = fw_img_size + pad_len;
header_data[1] = blk_size;
header_data[2] = (total_len)/blk_size;
//buffer alloc and not align_addr
temp_fw_buf = fw_buf = (uint8_t *)asr_rtos_malloc(blk_size + sdio_get_block_size());
if (fw_buf == NULL) {
dbg(D_ERR, D_UWIFI_CTRL, "alloc buffer failed\r\n");
return -1;
}
fw_buf = (uint8_t *)(ALIGN_ADDR(fw_buf, DMA_ALIGNMENT));
retry:
fw_download_done = 0;
if (!retry_times)
goto exit;
dbg(D_DBG, D_UWIFI_CTRL, "%s retry %d times", __func__, 3 - retry_times);
retry_times--;
{
memcpy(fw_buf, header_data, sizeof(header_data));
//memcpy(fw_img_data + fw_img_size, padding_data, pad_len);
/*calculate CRC, data parts include the padding data*/
fw_crc = 0;//crc32(fw_img_data, fw_img_size + pad_len);
//memcpy(fw_img_data + fw_img_size + pad_len, &fw_crc, crc_len);
dbg(D_ERR, D_UWIFI_CTRL, "fw_len:%d pad_len:%d crc_len:%d total_len:%d headers: 0x%x-0x%x-0x%x crc:0x%x blk_size:%d\r\n",
fw_img_size, pad_len, crc_len, total_len, header_data[0], header_data[1], header_data[2], fw_crc, blk_size);
}
/*step 1 polling if card is in rdy*/
ret = poll_card_status(C2H_DNLD_CARD_RDY | C2H_IO_RDY);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s card is not in rdy\n", __func__);
goto exit;
}
/*step 2 send out header data*/
ret = asr_sdio_tx_common_port_dispatch(asr_hw, fw_buf, sizeof(header_data), (asr_hw->ioport | 0x0), 0x1);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s write header data failed %d\n", __func__, ret);
goto exit;
}
/*step 3 send out fw data*/
//memcpy(fw_buf, fw_img_data, total_len);
int i = 0;
do {
#ifdef STM32H7_SDK
extern void asr_usleep(uint32_t time_us);
asr_usleep(10);
#endif
//use msg port 0, still polling
ret = poll_card_status(C2H_DNLD_CARD_RDY | C2H_IO_RDY);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s card is not in rdy\n", __func__);
goto exit;
}
memcpy(fw_buf, fw_img_data+i*blk_size, blk_size);
fw_crc = crc32_recursive(fw_buf, blk_size, fw_crc);
ret = asr_sdio_tx_common_port_dispatch(asr_hw, fw_buf, blk_size, (asr_hw->ioport | 0x0), 0x1);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s can't write %d data into card %d\n", __func__, total_len, ret);
goto exit;
}
/*
if (blk_size >= 2 * sdio_get_block_size())
dbg(D_ERR, D_UWIFI_CTRL, "send %d bytes in block mode [%d]\r\n", blk_size, i);
else
dbg(D_ERR, D_UWIFI_CTRL, "send %d bytes in byte mode [%d]\r\n", blk_size, i);
*/
i++;
} while (i < ((total_len / blk_size) - 1));
//use msg port 0, still polling
ret = poll_card_status(C2H_DNLD_CARD_RDY | C2H_IO_RDY);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s card is not in rdy\n", __func__);
goto exit;
}
memcpy(fw_buf, fw_img_data+i*blk_size, fw_img_size - i*blk_size);
fw_crc = crc32_recursive(fw_buf, fw_img_size - i*blk_size, fw_crc);
memcpy(fw_buf + fw_img_size - i*blk_size, padding_data, pad_len);
dbg(D_DBG, D_UWIFI_CTRL, "crc:0x%x", fw_crc);
memcpy(fw_buf + fw_img_size - i*blk_size + pad_len, &fw_crc, crc_len);
ret = asr_sdio_tx_common_port_dispatch(asr_hw, fw_buf, blk_size, (asr_hw->ioport | 0x0), 0x1);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s can't write %d data into card %d\n", __func__, total_len, ret);
goto exit;
}
#ifdef SDIO_CLOSE_PS
}
#endif
/*
sdio card side will reset sdio for some next transfer, otherwise sdio fw boot up can't receive message
if sdio card do reset operation, it need re-do initialization after transfer image done for cmd53
*/
#if 1
fw_download_done = 1;
#if 0 // no need to reinit sdio again.
sdio_host_enable_isr(1);
asr_sdio_set_block_size(SDIO_BLOCK_SIZE_DLD);//is a question, it should set the hardware of the block size
sdio_enable_func(1);
asr_sdio_init_config(asr_hw);
#endif
#ifdef SDIO_CLOSE_PS
if(_first_download ==0)
{
_first_download = 1;
#endif
/*step 4 check fw_crc status*/
if (check_scratch_status(CRC_SUCCESS) < 0)
goto retry;
/*step 5 check fw runing status*/
if (check_scratch_status(BOOT_SUCCESS) < 0)
goto exit;
dbg(D_ERR, D_UWIFI_CTRL, "BOOT_SUCCESS:0x%x\n", BOOT_SUCCESS);
#ifndef SDIO_CLOSE_PS
asr_rtos_free(temp_fw_buf);
#endif
#ifdef SDIO_CLOSE_PS
}
#endif
return 0;
#endif
exit:
return -1;
}
#elif defined(CONFIG_ASR5825)
#define ASR_SND_BOOT_LOADER_SIZE 1024
/*
Download fw README
1, lega/duet chip integrate romcode v1.0, which support download directly into one continuous "ram" region
2, canon chip intergrate romcode v2.0, which support download different sections of firmware into non-continuous "ram" region
Now duet with wifi + ble function's firmware have different sections, and its "ram" region is non-continuous,
but it integrated the romcode v1.0. so for duet support non-continuous "ram" region,
it needs using 1st_firmware download the snd_bootloader, then using 2nd_firmware download work with snd_bootloader
to download the total firmware with different section into different "ram" region.
*/
/*
download_firmware: download firmware into card
total data format will be: | header data | fw_data | padding | CRC |(CRC check fw_data + padding)
header data will be: | fw_len | transfer unit | transfer times |
*/
static int asr_sdio_send_fw(struct asr_hw *asr_hw, uint8_t *fw_img_data, uint32_t fw_img_size,
u8 *fw_buf, int blk_size, u32 total_len, u32 pad_len, bool cal_crc)
{
int ret = 0;
int i = 0;
u32 fw_crc = 0;
u8 padding_data[4] = { 0 };
while (i < total_len / blk_size ){
#ifdef STM32H7_SDK
extern void asr_usleep(uint32_t time_us);
asr_usleep(5);
#endif
//use msg port 0, still polling
ret = poll_card_status(C2H_DNLD_CARD_RDY | C2H_IO_RDY);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s card is not in rdy\n", __func__);
return ret;
}
memcpy(fw_buf, fw_img_data+i*blk_size, blk_size);
if (cal_crc) {
fw_crc = crc32_recursive(fw_buf, blk_size, fw_crc);
}
ret = asr_sdio_tx_common_port_dispatch(asr_hw, fw_buf, blk_size, (asr_hw->ioport | 0x0), 0x1);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s can't write %d data into card %d\n", __func__, total_len, ret);
return ret;
}
#if 0
if (blk_size >= 2 * sdio_get_block_size())
dbg(D_ERR, D_UWIFI_CTRL, "send %d bytes in block mode [%d]", blk_size, i);
else
dbg(D_ERR, D_UWIFI_CTRL, "send %d bytes in byte mode [%d]", blk_size, i);
#endif
i++;
}
if (!(total_len - i*blk_size || pad_len || cal_crc)) {
return ret;
}
memset(fw_buf, 0, blk_size);
dbg(D_ERR, D_UWIFI_CTRL, "%s:fw_img_size=%d,len=%d,fw_crc=0x%X\n",
__func__, fw_img_size, total_len - i*blk_size, fw_crc);
//use msg port 0, still polling
ret = poll_card_status(C2H_DNLD_CARD_RDY | C2H_IO_RDY);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s card is not in rdy\n", __func__);
return ret;
}
if (fw_img_size - i*blk_size != 0) {
memcpy(fw_buf, fw_img_data+i*blk_size, fw_img_size - i*blk_size);
}
if (pad_len) {
memcpy(fw_buf + fw_img_size - i*blk_size, padding_data, pad_len);
}
if (cal_crc) {
fw_crc = crc32_recursive(fw_buf, fw_img_size - i*blk_size, fw_crc);
dbg(D_DBG, D_UWIFI_CTRL, "CRC:0x%x\n", fw_crc);
memcpy(fw_buf + fw_img_size - i*blk_size + pad_len, &fw_crc, sizeof(fw_crc));
}
ret = asr_sdio_tx_common_port_dispatch(asr_hw, fw_buf, blk_size, (asr_hw->ioport | 0x0), 0x1);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s can't write %d data into card %d\n", __func__, total_len, ret);
return ret;
}
return ret;
}
static int asr_sdio_download_1st_firmware(struct asr_hw *asr_hw, uint8_t *fw_img_data, uint32_t fw_img_size)
{
int ret = 0;
u32 header_data[3];
u32 fw_len, total_len, pad_len = 0, crc_len = 4;
int blk_size;
u8 *temp_fw_buf = NULL;
u8 *fw_buf = NULL;
blk_size = sdio_get_block_size() * 2;// /2 to change to byte mode; *2 change to block mode
/*step 0 prepare header data and other initializations */
fw_len = ASR_SND_BOOT_LOADER_SIZE;
pad_len = (fw_len % 4) ? (4 - (fw_len % 4)) : 0;
total_len = fw_len + pad_len + crc_len;
total_len = ASR_ALIGN_DLDBLKSZ_HI(total_len);
header_data[0] = ASR_SND_BOOT_LOADER_SIZE + pad_len;
header_data[1] = blk_size;
header_data[2] = total_len / blk_size;
if (total_len % blk_size) {
header_data[2]++;
}
//buffer alloc and not align_addr
temp_fw_buf = fw_buf = (uint8_t *)asr_rtos_malloc(blk_size + sdio_get_block_size());
if (fw_buf == NULL) {
dbg(D_ERR, D_UWIFI_CTRL, "%s:alloc buffer failed\r\n", __func__);
return -ENOMEM;
}
fw_buf = (uint8_t *)(ALIGN_ADDR(fw_buf, DMA_ALIGNMENT));
memcpy(fw_buf, (u8 *) header_data, sizeof(header_data));
dbg(D_INF, D_UWIFI_CTRL,
"%s fw_len:%d total_len:%d blk_size:%d headers: 0x%x-0x%x-0x%x\n", __func__,
fw_len, total_len, blk_size, header_data[0], header_data[1], header_data[2]);
/*step 1 polling if card is in rdy */
ret = poll_card_status(C2H_DNLD_CARD_RDY | C2H_IO_RDY);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s card is not in rdy\n", __func__);
goto exit_1st_fw;
}
/*step 2 send out header data */
ret = asr_sdio_tx_common_port_dispatch(asr_hw, fw_buf, sizeof(header_data), (asr_hw->ioport | 0x0), 0x1);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s write header data failed %d\n", __func__, ret);
goto exit_1st_fw;
}
/*step 3 send out fw data */
ret = asr_sdio_send_fw(asr_hw, fw_img_data, fw_len, fw_buf, blk_size, total_len, pad_len, true);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s can't write %d data into card %d\n", __func__, total_len, ret);
goto exit_1st_fw;
}
/*step 4 check fw_crc status */
//if (check_scratch_status(func, CRC_SUCCESS) < 0)
// ret = -1;
// goto exit_1st_fw;
//}
//dev_info(g_asr_para.dev, "%s CRC_SUCCESS:0x%x\n", __func__, CRC_SUCCESS);
/*step 5 check fw runing status */
if (check_scratch_status(BOOT_SUCCESS) < 0) {
ret = -1;
goto exit_1st_fw;
}
dbg(D_INF, D_UWIFI_CTRL, "%s BOOT_SUCCESS:0x%x\n", __func__, BOOT_SUCCESS);
ret = 0;
exit_1st_fw:
if (temp_fw_buf) {
asr_rtos_free(temp_fw_buf);
temp_fw_buf = NULL;
}
return ret;
}
static int asr_sdio_clear_status(uint16_t status)
{
int ret = 0;
uint8_t scratch_val;
asr_sdio_claim_host();
ret = sdio_readb_cmd52(SCRATCH_1, &scratch_val);
asr_sdio_release_host();
if (!ret) {
dbg(D_ERR, D_UWIFI_CTRL, "can't read 0x%x out\n", SCRATCH_1);
return -1;
}
asr_sdio_claim_host();
ret = sdio_writeb_cmd52(SCRATCH_1, scratch_val | ~(status >> 8));
asr_sdio_release_host();
if (!ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s write fw_sec_num fail!!! (%d)\n", __func__, ret);
return -1;
}
dbg(D_INF, D_UWIFI_CTRL, "%s clear 0x%X\n", __func__, status);
return 0;
}
static int asr_sdio_send_section_firmware(struct asr_hw *asr_hw, struct sdio_host_sec_fw_header
*p_sec_fw_hdr, u8 * fw_buf, u8 *fw_img_data, int blk_size)
{
int ret = 0;
/*step 1 polling if card is in rdy */
ret = poll_card_status(C2H_DNLD_CARD_RDY | C2H_IO_RDY);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s card is not in rdy\n", __func__);
return ret;
}
ret = asr_sdio_clear_status(CRC_SUCCESS);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s clear crc fail\n", __func__);
return ret;
}
/*step 2 send out header data */
memmove(fw_buf, p_sec_fw_hdr, sizeof(struct sdio_host_sec_fw_header));
ret = asr_sdio_tx_common_port_dispatch(asr_hw, fw_buf, sizeof(struct sdio_host_sec_fw_header), (asr_hw->ioport | 0x0), 0x1);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s write header data failed %d\n", __func__, ret);
return ret;
}
/*step 3 send out fw data */
ret = asr_sdio_send_fw(asr_hw, fw_img_data, p_sec_fw_hdr->sec_fw_len, fw_buf, blk_size,
p_sec_fw_hdr->sec_fw_len, 0, false);
if (ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s can't write %d data into card %d\n", __func__, p_sec_fw_hdr->sec_fw_len, ret);
return ret;
}
/*step 4 check fw_crc status */
if (check_scratch_status(CRC_SUCCESS) == 0) {
dbg(D_ERR, D_UWIFI_CTRL, "%s CRC_SUCCESS:0x%x\n", __func__, CRC_SUCCESS);
} else {
return -1;
}
return ret;
}
#define HYBRID_BIN_TAG 0xAABBCCDD
static int asr_sdio_download_2nd_firmware(struct asr_hw *asr_hw, uint8_t *fw_img_data, uint32_t fw_img_size)
{
u8 *temp_fw_buf = NULL;
u8 *fw_buf = NULL;
u8 *fw_data;
size_t fw_size;
int ret = 0;
struct sdio_host_sec_fw_header sdio_host_sec_fw_header = { 0 };
u32 total_sec_num, fw_tag, sec_idx, fw_hybrid_header_len, fw_sec_total_len, fw_dled_len;
u8 fw_sec_num;
int blk_size;
blk_size = sdio_get_block_size() * 2;// /2 to change to byte mode; *2 change to block mode
/*parse fw header and get sec num */
fw_data = fw_img_data + ASR_SND_BOOT_LOADER_SIZE;
fw_size = fw_img_size - ASR_SND_BOOT_LOADER_SIZE;
memcpy(&fw_tag, fw_data, sizeof(u32));
if (HYBRID_BIN_TAG != fw_tag) {
dbg(D_ERR, D_UWIFI_CTRL, "%s fw tag mismatch(0x%x 0x%x)\n", __func__, HYBRID_BIN_TAG, fw_tag);
ret = -EINVAL;
goto exit_2nd_fw;
}
memcpy(&total_sec_num, fw_data + 4, sizeof(u32));
fw_sec_num = (u8) total_sec_num;
// write sec num to scra register for fw read.
asr_sdio_claim_host();
ret = sdio_writeb_cmd52(SCRATCH0_3, fw_sec_num);
ret = sdio_writeb_cmd52(SCRATCH1_0, asr_hw->mac_addr[0]);
ret = sdio_writeb_cmd52(SCRATCH1_1, asr_hw->mac_addr[1]);
ret = sdio_writeb_cmd52(SCRATCH1_2, asr_hw->mac_addr[2]);
ret = sdio_writeb_cmd52(SCRATCH1_3, asr_hw->mac_addr[5]);
asr_sdio_release_host();
if (!ret) {
dbg(D_ERR, D_UWIFI_CTRL, "%s write fw_sec_num fail!!! (%d)\n", __func__, ret);
goto exit_2nd_fw;
}
// tag + sec num + [len/crc/addr]...+
fw_hybrid_header_len = 8 + total_sec_num * sizeof(struct sdio_fw_sec_fw_header);
fw_sec_total_len = fw_size - fw_hybrid_header_len;
if (fw_sec_total_len % 4) {
dbg(D_ERR, D_UWIFI_CTRL, "%s error: fw bin length not 4byte allignment\n", __func__);
ret = -EINVAL;
goto exit_2nd_fw;
}
dbg(D_INF, D_UWIFI_CTRL, "%s hybrid hdr (0x%x %d %d %d)!!\n", __func__, fw_tag,
total_sec_num, fw_hybrid_header_len, fw_sec_total_len);
//buffer alloc and not align_addr
temp_fw_buf = fw_buf = (uint8_t *)asr_rtos_malloc(blk_size + sdio_get_block_size());
if (fw_buf == NULL) {
dbg(D_ERR, D_UWIFI_CTRL, "%s:alloc buffer failed\r\n", __func__);
ret = -ENOMEM;
goto exit_2nd_fw;
}
fw_buf = (uint8_t *)(ALIGN_ADDR(fw_buf, DMA_ALIGNMENT));
sec_idx = 0;
fw_dled_len = 0;
do {
// prepare sdio_host_sec_fw_header
memcpy(&sdio_host_sec_fw_header, fw_data + 8 +
sec_idx * sizeof(struct sdio_fw_sec_fw_header), sizeof(struct sdio_fw_sec_fw_header));
sdio_host_sec_fw_header.transfer_unit = blk_size;
sdio_host_sec_fw_header.transfer_times = sdio_host_sec_fw_header.sec_fw_len / blk_size;
if (sdio_host_sec_fw_header.sec_fw_len % blk_size) {
sdio_host_sec_fw_header.transfer_times++;
}
dbg(D_INF, D_UWIFI_CTRL, "%s idx(%d),dled_len=%d,sec headers: 0x%x-0x%x-0x%x-0x%x-0x%x\n",
__func__, sec_idx, fw_dled_len,
sdio_host_sec_fw_header.sec_fw_len,
sdio_host_sec_fw_header.sec_crc,
sdio_host_sec_fw_header.chip_ram_addr,
sdio_host_sec_fw_header.transfer_unit, sdio_host_sec_fw_header.transfer_times);
if (asr_sdio_send_section_firmware(asr_hw, &sdio_host_sec_fw_header, fw_buf,
fw_data + fw_hybrid_header_len + fw_dled_len, blk_size)) {
// any section fail,exit
dbg(D_ERR, D_UWIFI_CTRL, "%s sec(%d) download fail!\n", __func__, sec_idx);
ret = -1;
goto exit_2nd_fw;
}
// caculate downloaded bin length.
fw_dled_len += sdio_host_sec_fw_header.sec_fw_len;
sec_idx++;
} while (sec_idx < total_sec_num);
if (fw_dled_len != fw_sec_total_len) {
dbg(D_ERR, D_UWIFI_CTRL, "%s fw len mismatch(%d %d)\n", __func__, fw_dled_len, fw_sec_total_len);
ret = -1;
goto exit_2nd_fw;
}
/*step 5 check fw runing status */
if (check_scratch_status(BOOT_SUCCESS) < 0) {
ret = -1;
goto exit_2nd_fw;
}
asr_msleep(20);
dbg(D_INF, D_UWIFI_CTRL, "%s BOOT_SUCCESS:0x%x\n", __func__, BOOT_SUCCESS);
if (poll_card_status(C2H_IO_RDY)) {
dbg(D_ERR, D_UWIFI_CTRL, "%s card is not in rdy\n", __func__);
ret = -1;
goto exit_2nd_fw;
}
ret = 0;
exit_2nd_fw:
if (temp_fw_buf) {
asr_rtos_free(temp_fw_buf);
temp_fw_buf = NULL;
}
return ret;
}
int asr_sdio_download_firmware(struct asr_hw *asr_hw, uint8_t *fw_img_data, uint32_t fw_img_size)
{
int ret = 0;
//download 2nd bootloader work with romcode v1.0
ret = asr_sdio_download_1st_firmware(asr_hw, fw_img_data, fw_img_size);
if (ret != 0) {
return ret;
}
//download the hybrid firmware work with the 2nd bootloader
ret = asr_sdio_download_2nd_firmware(asr_hw, fw_img_data, fw_img_size);
return ret;
}
#endif