From: Zhirun Liu Add the build configuration and SDIO board-support layer for the initial AIC8800D80 U02 upstream target. The BSP handles platform power sequencing, SDIO transport setup, firmware loading and chip initialization. Limit the selectable configuration to the D80 U02 SDIO station baseline and use the kernel crypto library for firmware MD5 diagnostics. Signed-off-by: Zhirun Liu Signed-off-by: Yanli Yang --- drivers/net/wireless/aic/Kconfig | 40 + drivers/net/wireless/aic/Makefile | 7 + drivers/net/wireless/aic/aic8800_bsp/Kconfig | 3 + drivers/net/wireless/aic/aic8800_bsp/Makefile | 16 + .../aic/aic8800_bsp/aic8800d80_compat.c | 290 +++ .../aic/aic8800_bsp/aic8800d80_compat.h | 11 + .../aic/aic8800_bsp/aic8800dc_compat.c | 1342 +++++++++++ .../aic/aic8800_bsp/aic8800dc_compat.h | 34 + .../wireless/aic/aic8800_bsp/aic_bsp_driver.c | 1670 ++++++++++++++ .../wireless/aic/aic8800_bsp/aic_bsp_driver.h | 598 +++++ .../wireless/aic/aic8800_bsp/aic_bsp_export.h | 70 + .../wireless/aic/aic8800_bsp/aic_bsp_main.c | 310 +++ .../aic/aic8800_bsp/aic_chip_8800d80.c | 98 + .../aic/aic8800_bsp/aic_chip_8800dc.c | 133 ++ .../wireless/aic/aic8800_bsp/aic_chip_8801.c | 117 + .../wireless/aic/aic8800_bsp/aic_chip_ops.c | 64 + .../wireless/aic/aic8800_bsp/aic_chip_ops.h | 69 + .../net/wireless/aic/aic8800_bsp/aicsdio.c | 2027 +++++++++++++++++ .../net/wireless/aic/aic8800_bsp/aicsdio.h | 176 ++ .../wireless/aic/aic8800_bsp/aicsdio_txrxif.c | 464 ++++ .../wireless/aic/aic8800_bsp/aicsdio_txrxif.h | 180 ++ .../aic/aic8800_bsp/aicwf_txq_prealloc.c | 72 + .../aic/aic8800_bsp/aicwf_txq_prealloc.h | 8 + drivers/net/wireless/aic/aic8800_fdrv/Kconfig | 37 + .../net/wireless/aic/aic8800_fdrv/Makefile | 46 + 25 files changed, 7882 insertions(+) create mode 100644 drivers/net/wireless/aic/Kconfig create mode 100644 drivers/net/wireless/aic/Makefile create mode 100644 drivers/net/wireless/aic/aic8800_bsp/Kconfig create mode 100644 drivers/net/wireless/aic/aic8800_bsp/Makefile create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic8800d80_compat.c create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic8800d80_compat.h create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic8800dc_compat.c create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic8800dc_compat.h create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic_bsp_driver.c create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic_bsp_driver.h create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic_bsp_export.h create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic_bsp_main.c create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic_chip_8800d80.c create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic_chip_8800dc.c create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic_chip_8801.c create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic_chip_ops.c create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aic_chip_ops.h create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aicsdio.c create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aicsdio.h create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aicsdio_txrxif.c create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aicsdio_txrxif.h create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aicwf_txq_prealloc.c create mode 100644 drivers/net/wireless/aic/aic8800_bsp/aicwf_txq_prealloc.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/Kconfig create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/Makefile diff --git a/drivers/net/wireless/aic/Kconfig b/drivers/net/wireless/aic/Kconfig new file mode 100644 index 0000000000000..5218351da5e38 --- /dev/null +++ b/drivers/net/wireless/aic/Kconfig @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config WLAN_VENDOR_AIC + bool "AIC devices" + default y + help + Say Y to expose the AIC8800D80 U02 SDIO Wi-Fi driver. + The first upstreaming target is a narrow D80 U02 SDIO station + configuration; legacy transport, Bluetooth, Android, test and + manufacturing build switches are intentionally not exposed. + +if WLAN_VENDOR_AIC + +menuconfig AIC8800_BSP_SUPPORT + tristate "AIC8800D80 U02 SDIO bus support" + depends on MMC + select CRYPTO_LIB_MD5 + help + Enable the AIC8800D80 U02 SDIO bus support package. It provides + the SDIO transport, chip operations and firmware download path used + by the AIC8800 FullMAC WLAN driver. + + Firmware files are loaded from the aic8800/ subdirectory of the + kernel firmware search path, normally /lib/firmware/aic8800/. + + If unsure, say N. + +if AIC8800_BSP_SUPPORT + +config AIC8800_SDIO_SUPPORT + def_bool y + +config AIC8800_SDIO_PWRCTRL + def_bool y + +source "drivers/net/wireless/aic/aic8800_bsp/Kconfig" +source "drivers/net/wireless/aic/aic8800_fdrv/Kconfig" + +endif # AIC8800_BSP_SUPPORT +endif # WLAN_VENDOR_AIC diff --git a/drivers/net/wireless/aic/Makefile b/drivers/net/wireless/aic/Makefile new file mode 100644 index 0000000000000..1ce5651aa4a75 --- /dev/null +++ b/drivers/net/wireless/aic/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_AIC8800_BSP_SUPPORT) += aic8800_bsp/ +obj-$(CONFIG_AIC8800_WLAN_SUPPORT) += aic8800_fdrv/ + +ccflags-y += -DAICWF_SDIO_SUPPORT +ccflags-y += -DCONFIG_SDIO_PWRCTRL diff --git a/drivers/net/wireless/aic/aic8800_bsp/Kconfig b/drivers/net/wireless/aic/aic8800_bsp/Kconfig new file mode 100644 index 0000000000000..e82acc8875bc2 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/Kconfig @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# The D80 U02 SDIO station baseline has no selectable BSP suboptions. diff --git a/drivers/net/wireless/aic/aic8800_bsp/Makefile b/drivers/net/wireless/aic/aic8800_bsp/Makefile new file mode 100644 index 0000000000000..ad9763e704b89 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_AIC8800_BSP_SUPPORT) += aic8800_bsp.o + +aic8800_bsp-y := \ + aic_chip_8800d80.o \ + aic_chip_ops.o \ + aic8800d80_compat.o \ + aic_bsp_main.o \ + aic_bsp_driver.o \ + aicsdio.o \ + aicsdio_txrxif.o + +ccflags-y += -I$(src)/../aic8800_fdrv +ccflags-y += -DAICWF_SDIO_SUPPORT +ccflags-y += -DCONFIG_SDIO_PWRCTRL diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic8800d80_compat.c b/drivers/net/wireless/aic/aic8800_bsp/aic8800d80_compat.c new file mode 100644 index 0000000000000..0c3ed50dc7862 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic8800d80_compat.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief 8800d80 chip config + * + ****************************************************************************** + */ + +#include "aic8800d80_compat.h" +#include "aic_bsp_driver.h" + +extern struct aicbsp_info_t aicbsp_info; + +typedef u32 (*array2_tbl_t)[2]; + +#define AIC_PATCH_MAGIG_NUM 0x48435450 // "PTCH" +#define AIC_PATCH_MAGIG_NUM_2 0x50544348 // "HCTP" +#define AIC_PATCH_BLOCK_MAX 4 + +struct aic_patch_t { + u32 magic_num; + u32 pair_start; + u32 magic_num_2; + u32 pair_count; + u32 block_dst[AIC_PATCH_BLOCK_MAX]; + u32 block_src[AIC_PATCH_BLOCK_MAX]; + u32 block_size[AIC_PATCH_BLOCK_MAX]; // word count +}; + +//#define AIC_PATCH_OFST(mem) ((size_t)&((struct aic_patch_t *)0)->(mem)) +#define AIC_PATCH_OFST(mem) offsetof(struct aic_patch_t, mem) +#define AIC_PATCH_ADDR(mem) ((u32)(aic_patch_str_base + AIC_PATCH_OFST(mem))) + +static u32 aicbsp_syscfg_tbl_8800d80[][2] = {}; + +int aicbsp_system_config_8800d80(struct aic_sdio_dev *sdiodev) +{ + int syscfg_num = sizeof(aicbsp_syscfg_tbl_8800d80) / sizeof(u32) / 2; + int ret, cnt; + + for (cnt = 0; cnt < syscfg_num; cnt++) { + ret = rwnx_send_dbg_mem_write_req(sdiodev, + aicbsp_syscfg_tbl_8800d80[cnt][0], + aicbsp_syscfg_tbl_8800d80[cnt][1]); + if (ret) { + pr_err("%x write fail: %d\n", aicbsp_syscfg_tbl_8800d80[cnt][0], + ret); + return ret; + } + } + return 0; +} + +static u32 adaptivity_patch_tbl_8800d80[][2] = { + {0x000C, 0x0000320A}, // linkloss_thd + {0x009C, 0x00000000}, // ac_param_conf + {0x0168, 0x00010000}, // tx_adaptivity_en +}; + +#define USER_CHAN_MAX_TXPWR_EN_FLAG (0x01U << 1) +#define USER_TX_USE_ANA_F_FLAG (0x01U << 2) + +#define CFG_USER_CHAN_MAX_TXPWR_EN 1 +#define CFG_USER_TX_USE_ANA_F 0 + +#define CFG_USER_EXT_FLAGS_EN \ + (CFG_USER_CHAN_MAX_TXPWR_EN || CFG_USER_TX_USE_ANA_F) + +static u32 patch_tbl_8800d80[][2] = { +#ifdef AIC8800_USE_5G + {0x00b4, 0xf3010001}, +#else + {0x00b4, 0xf3010000}, +#endif +#if defined(CONFIG_AMSDU_RX) + {0x170, 0x0100000a}, +#endif +#ifdef CONFIG_IRQ_FALL + {0x00000170, 0x0000010a}, // irqf +#endif + +#if CFG_USER_EXT_FLAGS_EN + {0x0188, 0x00000001 +#if CFG_USER_CHAN_MAX_TXPWR_EN + | USER_CHAN_MAX_TXPWR_EN_FLAG +#endif +#if CFG_USER_TX_USE_ANA_F + | USER_TX_USE_ANA_F_FLAG +#endif + }, // user_ext_flags +#endif +}; + +#ifdef CONFIG_OOB +// for 8800d40/d80 map data1 isr to gpiob1 +static u32 gpio_cfg_tbl_8800d40d80[][2] = { + {0x40504084, 0x00000006}, {0x40500040, 0x00000000}, + {0x40100030, 0x00000001}, {0x40241020, 0x00000001}, + {0x40240030, 0x00000004}, {0x40240020, 0x03020700}, +}; +#endif + +int aicwifi_sys_config_8800d80(struct aic_sdio_dev *sdiodev) +{ +#ifdef CONFIG_OOB + int ret, cnt; + int gpiocfg_num = sizeof(gpio_cfg_tbl_8800d40d80) / sizeof(u32) / 2; + + for (cnt = 0; cnt < gpiocfg_num; cnt++) { + ret = rwnx_send_dbg_mem_write_req(sdiodev, + gpio_cfg_tbl_8800d40d80[cnt][0], + gpio_cfg_tbl_8800d40d80[cnt][1]); + if (ret) { + pr_err("%x write fail: %d\n", gpio_cfg_tbl_8800d40d80[cnt][0], ret); + return ret; + } + } +#endif + + return 0; +} + +#define NEW_PATCH_BUFFER_MAP 1 + +int aicwifi_patch_config_8800d80(struct aic_sdio_dev *sdiodev) +{ + const u32 rd_patch_addr = RAM_FMAC_FW_ADDR + 0x0198; + u32 aic_patch_addr; + u32 config_base, aic_patch_str_base; +#if (NEW_PATCH_BUFFER_MAP) + u32 patch_buff_addr, patch_buff_base, rd_version_addr, rd_version_val; +#endif + u32 start_addr = 0x0016F800; + u32 patch_addr = start_addr; + u32 patch_cnt = sizeof(patch_tbl_8800d80) / sizeof(u32) / 2; + struct dbg_mem_read_cfm rd_patch_addr_cfm; + int ret = 0; + int cnt = 0; + // adap test + int adap_patch_cnt = 0; + + if (aicbsp_info.adap_test) { //(adap_test) { + pr_info("%s for adaptivity test \r\n", __func__); + adap_patch_cnt = sizeof(adaptivity_patch_tbl_8800d80) / sizeof(u32) / 2; + } + + aic_patch_addr = rd_patch_addr + 8; + + ret = + rwnx_send_dbg_mem_read_req(sdiodev, rd_patch_addr, &rd_patch_addr_cfm); + if (ret) { + pr_info("patch rd fail\n"); + return ret; + } + + config_base = rd_patch_addr_cfm.memdata; + + ret = + rwnx_send_dbg_mem_read_req(sdiodev, aic_patch_addr, &rd_patch_addr_cfm); + if (ret) { + pr_info("patch str rd fail\n"); + return ret; + } + aic_patch_str_base = rd_patch_addr_cfm.memdata; + +#if (NEW_PATCH_BUFFER_MAP) + rd_version_addr = RAM_FMAC_FW_ADDR + 0x01C; + ret = rwnx_send_dbg_mem_read_req(sdiodev, rd_version_addr, + &rd_patch_addr_cfm); + if (ret) { + pr_err("version val[0x%x] rd fail: %d\n", rd_version_addr, ret); + return ret; + } + rd_version_val = rd_patch_addr_cfm.memdata; + pr_info("AICWF rd_version_val=%08X\n", rd_version_val); + sdiodev->fw_version_uint = rd_version_val; + if (rd_version_val > 0x06090100) { + patch_buff_addr = rd_patch_addr + 12; + ret = rwnx_send_dbg_mem_read_req(sdiodev, patch_buff_addr, + &rd_patch_addr_cfm); + if (ret) { + pr_err("patch buf rd fail\n"); + return ret; + } + patch_buff_base = rd_patch_addr_cfm.memdata; + start_addr = patch_buff_base; + patch_addr = start_addr; + } +#endif + + ret = rwnx_send_dbg_mem_write_req(sdiodev, AIC_PATCH_ADDR(magic_num), + AIC_PATCH_MAGIG_NUM); + if (ret) { + pr_err("0x%x write fail\n", AIC_PATCH_ADDR(magic_num)); + return ret; + } + + ret = rwnx_send_dbg_mem_write_req(sdiodev, AIC_PATCH_ADDR(magic_num_2), + AIC_PATCH_MAGIG_NUM_2); + if (ret) { + pr_err("0x%x write fail\n", AIC_PATCH_ADDR(magic_num_2)); + return ret; + } + + ret = rwnx_send_dbg_mem_write_req(sdiodev, AIC_PATCH_ADDR(pair_start), + patch_addr); + if (ret) { + pr_err("0x%x write fail\n", AIC_PATCH_ADDR(pair_start)); + return ret; + } + + ret = rwnx_send_dbg_mem_write_req(sdiodev, AIC_PATCH_ADDR(pair_count), + patch_cnt + adap_patch_cnt); + if (ret) { + pr_err("0x%x write fail\n", AIC_PATCH_ADDR(pair_count)); + return ret; + } + + for (cnt = 0; cnt < patch_cnt; cnt++) { + ret = rwnx_send_dbg_mem_write_req(sdiodev, start_addr + 8 * cnt, + patch_tbl_8800d80[cnt][0] + + config_base); + if (ret) { + pr_err("%x write fail\n", start_addr + 8 * cnt); + return ret; + } + ret = rwnx_send_dbg_mem_write_req(sdiodev, start_addr + 8 * cnt + 4, + patch_tbl_8800d80[cnt][1]); + if (ret) { + pr_err("%x write fail\n", start_addr + 8 * cnt + 4); + return ret; + } + } + + if (aicbsp_info.adap_test) { + int tmp_cnt = patch_cnt + adap_patch_cnt; + + for (cnt = patch_cnt; cnt < tmp_cnt; cnt++) { + int tbl_idx = cnt - patch_cnt; + + ret = rwnx_send_dbg_mem_write_req(sdiodev, start_addr + 8 * cnt, + adaptivity_patch_tbl_8800d80[tbl_idx][0] + + config_base); + if (ret) { + pr_err("%x write fail\n", start_addr + 8 * cnt); + return ret; + } + ret = rwnx_send_dbg_mem_write_req(sdiodev, start_addr + 8 * cnt + 4, + adaptivity_patch_tbl_8800d80[tbl_idx][1]); + if (ret) { + pr_err("%x write fail\n", start_addr + 8 * cnt + 4); + return ret; + } + } + } + + ret = + rwnx_send_dbg_mem_write_req(sdiodev, AIC_PATCH_ADDR(block_size[0]), 0); + if (ret) { + pr_err("block_size[0x%x] write fail: %d\n", + AIC_PATCH_ADDR(block_size[0]), ret); + return ret; + } + ret = + rwnx_send_dbg_mem_write_req(sdiodev, AIC_PATCH_ADDR(block_size[1]), 0); + if (ret) { + pr_err("block_size[0x%x] write fail: %d\n", + AIC_PATCH_ADDR(block_size[1]), ret); + return ret; + } + ret = + rwnx_send_dbg_mem_write_req(sdiodev, AIC_PATCH_ADDR(block_size[2]), 0); + if (ret) { + pr_err("block_size[0x%x] write fail: %d\n", + AIC_PATCH_ADDR(block_size[2]), ret); + return ret; + } + ret = + rwnx_send_dbg_mem_write_req(sdiodev, AIC_PATCH_ADDR(block_size[3]), 0); + if (ret) { + pr_err("block_size[0x%x] write fail: %d\n", + AIC_PATCH_ADDR(block_size[3]), ret); + return ret; + } + + return 0; +} diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic8800d80_compat.h b/drivers/net/wireless/aic/aic8800_bsp/aic8800d80_compat.h new file mode 100644 index 0000000000000..a22e440b09a66 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic8800d80_compat.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _AIC8800D80_COMPAT_H_ +#define _AIC8800D80_COMPAT_H_ + +#include "aicsdio.h" + +int aicbsp_system_config_8800d80(struct aic_sdio_dev *sdiodev); +int aicwifi_sys_config_8800d80(struct aic_sdio_dev *sdiodev); +int aicwifi_patch_config_8800d80(struct aic_sdio_dev *sdiodev); + +#endif diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic8800dc_compat.c b/drivers/net/wireless/aic/aic8800_bsp/aic8800dc_compat.c new file mode 100644 index 0000000000000..e3f05fc41376d --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic8800dc_compat.c @@ -0,0 +1,1342 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief 8800dc chip config + * + ****************************************************************************** + */ + +#include "aic8800dc_compat.h" +#include "aic_bsp_driver.h" +#include + +u8 chip_sub_id; +u8 chip_mcu_id; + +static u32 syscfg_tbl_8800dc[][2] = { + {0x40500010, 0x00000004}, {0x40500010, 0x00000006}, // 160m clk +}; + +static u32 syscfg_tbl_8800dc_sdio_u01[][2] = { + {0x40030000, 0x00036724}, // loop forever after assert_err + {0x0011E800, 0xE7FE4070}, {0x40030084, 0x0011E800}, + {0x40030080, 0x00000001}, {0x4010001C, 0x00000000}, +}; + +static u32 syscfg_tbl_8800dc_sdio_u02[][2] = { + {0x40030000, 0x00036DA4}, // loop forever after assert_err + {0x0011E800, 0xE7FE4070}, {0x40030084, 0x0011E800}, + {0x40030080, 0x00000001}, {0x4010001C, 0x00000000}, +}; + +#ifdef CONFIG_OOB +static u32 oobcfg_tbl_8800dc_sdio_u02[][2] = { + {0x40504044, 0x2}, // oob_enable + {0x40500060, 0x03020700}, {0x40500040, 0}, {0x40100030, 1}, + {0x40241020, 1}, {0x402400f0, 0x340022}, +}; +#endif // CONFIG_OOB + +static u32 syscfg_tbl_masked_8800dc[][3] = { +// #ifdef CONFIG_PMIC_SETTING +#if defined(CONFIG_VRF_DCDC_MODE) + {0x7000216C, (0x3 << 2), (0x1 << 2)}, // pmic_pmu_init + {0x700021BC, (0x3 << 2), (0x1 << 2)}, + {0x70002118, ((0x7 << 4) | (0x1 << 7)), ((0x2 << 4) | (0x1 << 7))}, + {0x70002104, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))}, + {0x7000210C, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))}, + {0x70002170, (0xF << 0), (0x1 << 0)}, + {0x70002190, (0x3F << 0), (24 << 0)}, + {0x700021CC, ((0x7 << 4) | (0x1 << 7)), ((0x0 << 4) | (0x0 << 7))}, + {0x700010A0, (0x1 << 11), (0x1 << 11)}, + {0x70001034, ((0x1 << 20) | (0x7 << 26)), ((0x0 << 20) | (0x2 << 26))}, + {0x70001038, (0x1 << 8), (0x1 << 8)}, + {0x70001094, (0x3 << 2), (0x0 << 2)}, + {0x700021D0, ((0x1 << 5) | (0x1 << 6)), ((0x1 << 5) | (0x1 << 6))}, + {0x70001000, ((0x1 << 0) | (0x1 << 20) | (0x1 << 22)), + ((0x1 << 0) | (0x1 << 20) | (0x0 << 22))}, + {0x70001028, (0xf << 2), (0x1 << 2)}, +#else + {0x7000216C, (0x3 << 2), (0x1 << 2)}, // pmic_pmu_init + {0x700021BC, (0x3 << 2), (0x1 << 2)}, + {0x70002118, ((0x7 << 4) | (0x1 << 7)), ((0x2 << 4) | (0x1 << 7))}, + {0x70002104, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))}, + {0x7000210C, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))}, + {0x70002170, (0xF << 0), (0x1 << 0)}, + {0x70002190, (0x3F << 0), (24 << 0)}, + {0x700021CC, ((0x7 << 4) | (0x1 << 7)), ((0x0 << 4) | (0x0 << 7))}, + {0x700010A0, (0x1 << 11), (0x1 << 11)}, + {0x70001034, ((0x1 << 20) | (0x7 << 26)), ((0x0 << 20) | (0x2 << 26))}, + {0x70001038, (0x1 << 8), (0x1 << 8)}, + {0x70001094, (0x3 << 2), (0x0 << 2)}, + {0x700021D0, ((0x1 << 5) | (0x1 << 6)), ((0x1 << 5) | (0x1 << 6))}, + {0x70001000, ((0x1 << 0) | (0x1 << 20) | (0x1 << 22)), + ((0x0 << 0) | (0x1 << 20) | (0x0 << 22))}, + {0x70001028, (0xf << 2), (0x1 << 2)}, +#endif + // #endif /* CONFIG_PMIC_SETTING */ + {0x00000000, 0x00000000, 0x00000000}, // last one +}; + +static u32 syscfg_tbl_masked_8800dc_h[][3] = { + {0x7000216C, ((0x3 << 2) | (0x3 << 4)), + ((0x2 << 2) | (0x2 << 4))}, // pmic_pmu_init + {0x70002138, (0xFF << 0), (0xFF << 0)}, + {0x7000213C, (0xFF << 0), (0xFF << 0)}, + {0x70002144, (0xFF << 0), (0xFF << 0)}, + {0x700021BC, (0x3 << 2), (0x1 << 2)}, + {0x70002118, ((0x7 << 4) | (0x1 << 7)), ((0x2 << 4) | (0x1 << 7))}, + {0x70002104, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))}, + {0x7000210C, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))}, + {0x70002170, (0xF << 0), (0x1 << 0)}, + {0x70002190, (0x3F << 0), (24 << 0)}, + {0x700021CC, ((0x7 << 4) | (0x1 << 7)), ((0x0 << 4) | (0x0 << 7))}, + {0x700010A0, (0x1 << 11), (0x1 << 11)}, + //{0x70001034, ((0x1 << 20) | (0x7 << 26)), ((0x0 << 20) | (0x2 << 26))}, + {0x70001038, (0x1 << 8), (0x1 << 8)}, + {0x70001094, (0x3 << 2), (0x0 << 2)}, + {0x700021D0, ((0x1 << 5) | (0x1 << 6)), ((0x1 << 5) | (0x1 << 6))}, +#if defined(CONFIG_VRF_DCDC_MODE) + {0x70001000, ((0x1 << 0) | (0x1 << 20) | (0x1 << 22)), + ((0x1 << 0) | (0x1 << 20) | (0x0 << 22))}, +#else + {0x70001000, ((0x1 << 0) | (0x1 << 20) | (0x1 << 22)), + ((0x0 << 0) | (0x1 << 20) | (0x0 << 22))}, +#endif + {0x70001028, (0xf << 2), (0x1 << 2)}, + + {0x00000000, 0x00000000, 0x00000000}, // last one +}; + +static u32 syscfg_tbl_masked_8800dc_u01[][3] = { + // #ifdef CONFIG_PMIC_SETTING + {0x70001000, (0x1 << 16), (0x1 << 16)}, // for low temperature + {0x70001028, (0x1 << 6), (0x1 << 6)}, + {0x70001000, (0x1 << 16), (0x0 << 16)}, + // #endif /* CONFIG_PMIC_SETTING */ +}; + +static u32 patch_tbl_wifisetting_8800dc_u01[][2] = {{0x010c, 0x01001E01}}; + +static u32 patch_tbl_wifisetting_8800dc_u02[][2] = { +#if defined(CONFIG_SDIO_PWRCTRL) + {0x0124, 0x01011E01} +#else + {0x0124, 0x01001E01} +#endif +}; + +static u32 ldpc_cfg_ram[] = { + 0x00767679, 0x1DF8F870, 0x1DF8F870, 0x1DF8F870, 0x1DF8F870, 0x006E6E72, + 0x1DF8F869, 0x1DF8F869, 0x1DF8F869, 0x1DF8F869, 0x0076767B, 0x1DF8F870, + 0x1DF8F870, 0x1DF8F870, 0x1DF8F870, 0x007E7E85, 0x1DF4F876, 0x1DF4F876, + 0x1DF4F876, 0x1DF8F876, 0x0081818A, 0x1DF8F87B, 0x1DF8F87B, 0x1DF8F87B, + 0x1DF8F87B, 0x0081818D, 0x1DF8F87B, 0x1DF8F87B, 0x1DF8F87B, 0x1DF8F87B, + 0x0081818A, 0x1DF8F87B, 0x1DF8F87C, 0x1DF8F87B, 0x1DF8F87B, 0x007E7E40, + 0x1DF8F87B, 0x1DF8F87B, 0x1DF8F87B, 0x1DF8F87B, 0x008B8B92, 0x1DF8F887, + 0x1DF8F889, 0x1DF8F887, 0x1DF8F887, 0x00515155, 0x1DF8F84C, 0x1DF8F84C, + 0x1DF8F889, 0x1DF8F889, 0x00515154, 0x1DF8F84C, 0x1DF8F84C, 0x1DF8F888, + 0x1DF8F888, 0x004F4F53, 0x1DF8F84A, 0x1DF8F84A, 0x1DF8F84A, 0x1DF8F84A, + 0x004F4F53, 0x1DF8F84A, 0x1DF8F84A, 0x1DF8F84A, 0x1DF8F84A, 0x004F4F53, + 0x1DF8F84A, 0x1DF8F84A, 0x1DF8F84A, 0x1DF8F84A, 0x004E4E53, 0x1DF8F849, + 0x1DF8F848, 0x1DF8F848, 0x1DF8F848, 0x004D4D52, 0x1DF8F847, 0x1DF8F847, + 0x1DF8F847, 0x1DF8F847, 0x004F4F55, 0x1DF8F84B, 0x1DF8F84A, 0x1DF8F84A, + 0x1DF8F84A, 0x004E4E53, 0x1DF8F849, 0x1DF8F848, 0x1DF8F848, 0x1DF8F848, + 0x0049494D, 0x1DF8F844, 0x1DF8F844, 0x1DF8F844, 0x1DF8F844, 0x0051518F, + 0x1DF8F849, 0x1DF8F848, 0x1DF8F848, 0x1DF8F848, 0x00424277, 0x1DF8F83F, + 0x1DF8F83C, 0x1DF8F83C, 0x1DF8F83C, 0x00424275, 0x1DF8F89E, 0x1DF8F83C, + 0x1DF8F83C, 0x1DF8F83C, 0x0055555C, 0x1DF8F84C, 0x1DF8F84C, 0x1DF8F84C, + 0x1DF8F84C, 0x0053535C, 0x1DF8F84C, 0x1DF8F84B, 0x1DF8F84B, 0x1DF8F84B, + 0x00F8F89E, 0x1DF8F88C, 0x1DF8F84A, 0x1DF8F84A, 0x1DF8F84A, 0x00898940, + 0x18F8F846, 0x18CFF845, 0x18CFF844, 0x18CFF844, 0x0056565F, 0x1DF8F84F, + 0x1DF8F84F, 0x1DF8F84F, 0x1DF8F84F, 0x0055555E, 0x1DF8F84E, 0x1DF8F84E, + 0x1DF8F84E, 0x1DF8F84E, 0x0056565F, 0x1DF8F84F, 0x1DF8F84F, 0x1DF8F84F, + 0x1DF8F84F, 0x00555561, 0x1DF8F850, 0x1DF8F84E, 0x1DF8F84E, 0x1DF8F84E, + 0x0053535F, 0x1DF8F84D, 0x1DF8F84C, 0x1DF8F84C, 0x1DF8F84C, 0x0055555F, + 0x1DF8F84F, 0x1DF8F84E, 0x1DF8F84E, 0x1DF8F84E, 0x005555AA, 0x1DF8F854, + 0x1DF8F84E, 0x1DF8F84E, 0x1DF8F84E, 0x005959A6, 0x1DF8F84D, 0x1DF8F84C, + 0x1DF8F84C, 0x1DF8F84C, 0x004F4F9B, 0x1DF8F84E, 0x1DF8F846, 0x1DF8F846, + 0x1DF8F846, 0x00F8F8A5, 0x1DF8F894, 0x1DF8F84C, 0x1DF8F84C, 0x1DF8F84C, + 0x009898A4, 0x1DF8F84D, 0x1DF8F84C, 0x1DF8F84C, 0x1DF8F84C, 0x00464686, + 0x1DF8F8B3, 0x1DF8F83D, 0x1DF8F83D, 0x1DF8F83D, 0x008E8E40, 0x1AF8F848, + 0x1ADFF848, 0x1ADFF846, 0x1ADFF846, 0x007F7F40, 0x18D2D275, 0x18D2D23A, + 0x18D2D23A, 0x18D2D239, 0x00454540, 0x0F868664, 0x0F86863E, 0x0F86863D, + 0x0F86863D, 0x005C5C64, 0x1DF8F856, 0x1DF8F855, 0x1DF8F855, 0x1DF8F855, + 0x005B5B68, 0x1DF8F858, 0x1DF8F855, 0x1DF8F855, 0x1DF8F855, 0x005A5A64, + 0x1DF8F855, 0x1DF8F854, 0x1DF8F854, 0x1DF8F854, 0x005A5AB5, 0x1DF8F85B, + 0x1DF8F855, 0x1DF8F854, 0x1DF8F854, 0x00F8F8B0, 0x1DF8F8A3, 0x1DF8F852, + 0x1DF8F852, 0x1DF8F852, 0x00A4A4AE, 0x1DF8F854, 0x1DF8F852, 0x1DF8F852, + 0x1DF8F852, 0x009A9A40, 0x1DF8F84E, 0x1DF8F84D, 0x1DF8F84C, 0x1DF8F84C, + 0x009C9C40, 0x1DF8F895, 0x1DF8F849, 0x1DF8F84A, 0x1DF8F84A, 0x00494940, + 0x1197976F, 0x11979742, 0x11979741, 0x11979741, 0x006E6E74, 0x1DF8F869, + 0x1DF8F869, 0x1DF8F869, 0x1DF8F869, 0x006E6E40, 0x1ADEF869, 0x1ADEF869, + 0x1ADEF869, 0x1ADEF869, 0x00757540, 0x0D78F86E, 0x0D78F86E, 0x0D78F86E, + 0x0D79F86E, 0x00787885, 0x1DF8F873, 0x1DF8F873, 0x1DF8F873, 0x1DF8F873, + 0x00787840, 0x1DF8F873, 0x1DF8F873, 0x1DF8F873, 0x1DF8F873, 0x00787840, + 0x0E81F873, 0x0E81F873, 0x0E81F873, 0x0E82F873, 0x00404040, 0x0E82F873, + 0x0E82F873, 0x0E82F873, 0x0E82F873, 0x00818140, 0x1092F87E, 0x1092F87E, + 0x1092F87E, 0x1092F87E, 0x00404040, 0x1092F87E, 0x1092F87E, 0x1092F87E, + 0x1092F87E, 0x00737340, 0x14B2B26B, 0x14B2B235, 0x14B2B235, 0x14B2B235, + 0x00404040, 0x0E828260, 0x0E82823D, 0x0E82823C, 0x0E82823C, 0x00404040, + 0x0F8B8B66, 0x0F8B8B3F, 0x0F8B8B3D, 0x0F8B8B3D, 0x00404040, 0x0B68683D, + 0x0B68681E, 0x0B68681E, 0x0B68681E, 0x00222240, 0x06434318, 0x06434329, + 0x06434318, 0x06434318, 0x00404040, 0x129D9D72, 0x129D9D43, 0x129D9D41, + 0x129D9D41, 0x00404040, 0x0D757542, 0x0D757520, 0x0D757520, 0x0D757520, + 0x00232340, 0x084C4C19, 0x084C4C2C, 0x084C4C19, 0x084C4C19 +}; + +static u32 agc_cfg_ram[] = { + 0x20000000, 0x0400000E, 0x3000200E, 0x5B000000, 0x0400004B, 0x3000008E, + 0x32000000, 0x0400007B, 0x40000000, 0xF8000026, 0x04000011, 0x4819008E, + 0x9C000020, 0x08000191, 0x38008000, 0x0A000000, 0x08104411, 0x38018000, + 0x0C004641, 0x08D00014, 0x30000000, 0x01000000, 0x04000017, 0x30000000, + 0x3C000000, 0x0400001A, 0x38020000, 0x40000001, 0x0800001D, 0x3808008E, + 0x14000050, 0x08000020, 0x4000008E, 0xA400007B, 0x00000101, 0x3000339F, + 0x41000700, 0x04104420, 0x90000000, 0x49000000, 0xF00E842F, 0xEC0E842C, + 0xEC0E842C, 0x04000032, 0x30000000, 0x48000101, 0x04000032, 0x30000000, + 0x48000202, 0x04000032, 0x30000000, 0x46000000, 0x04000011, 0x58010006, + 0x3D040472, 0xDC204439, 0x081DD4D2, 0x480A0006, 0xDC2044DC, 0x081DD43C, + 0x38050004, 0x0EF1F1C3, 0x342044DC, 0x30000000, 0x01000000, 0x04000042, + 0x30000000, 0x33000000, 0x04104445, 0x38008000, 0x2200109C, 0x08104448, + 0x38008000, 0x23D4509C, 0x08104417, 0x9000A000, 0x32000000, 0x18000063, + 0x14000060, 0x1C000051, 0x10000057, 0x38028000, 0x0C000001, 0x08D04466, + 0x3000200F, 0x00000000, 0x00000000, 0x38030000, 0x0C002601, 0x08D0445A, + 0x30000000, 0x3D020230, 0x0400005D, 0x30000000, 0x3E000100, 0x04000066, + 0x38028000, 0x0C001601, 0x34204466, 0x38028000, 0x0C000A01, 0x34204466, + 0x38008004, 0xFF000000, 0x0800007B, 0x3800802F, 0x26000000, 0x0800006C, + 0x380404AF, 0x1F191010, 0x0800006F, 0x20000CAF, 0x04000071, 0x60000CAF, + 0x18700079, 0x14000077, 0x10000075, 0x28140CAF, 0x09B00084, 0x280A0CAF, + 0x09B00084, 0x28060CAF, 0x09B00084, 0x28048086, 0x0800007D, 0x38000086, + 0x22800000, 0x04000080, 0x30000000, 0x0EF1F101, 0x36004883, 0x28020000, + 0x08000085, 0x3802008E, 0x3D040431, 0x08000088, 0x3805008E, 0x1F241821, + 0x0800008B, 0x3000008E, 0xA0163021, 0x0400008E, 0x3000008E, 0x0EF10012, + 0x34000091, 0x300000CC, 0x50000000, 0x04000094, 0x380095FE, 0x32010000, + 0x04000097, 0x50001FFE, 0x5A010000, 0x6DC9989B, 0xFC19D4B9, 0x30000186, + 0x3D840373, 0x0400009E, 0x3000008E, 0x0A000000, 0x040000A1, 0x3000008E, + 0x22C00000, 0x040000A4, 0x9000028E, 0x32010001, 0x8E4000AA, 0xC80000B0, + 0x00000000, 0x00000000, 0x3000008E, 0x32010001, 0x040000CB, 0x3000008E, + 0x29000000, 0x94045011, 0x300019B6, 0x32010000, 0x040000B3, 0x300019B6, + 0x3D040431, 0x040000B6, 0x300019B6, 0x22800000, 0x04000097, 0x30000186, + 0x3D840473, 0x040000BC, 0x3000008E, 0x29030000, 0x040000BF, 0x9AEE028E, + 0x32010100, 0x7C0000C5, 0xCC0000B0, 0x080000B0, 0x00000000, 0x3000008E, + 0x32010100, 0x040000C8, 0x3000028E, 0x29000000, 0x94045011, 0x5000038E, + 0x29000000, 0x94045011, 0xC0000035, 0x38010006, 0x3D040472, 0x080000D2, + 0x30000004, 0x0EF1F141, 0x340000D5, 0x28040004, 0x080000D7, 0x2808000E, + 0x080000D9, 0x3000018E, 0x0EF10052, 0x340000DC, 0x3000038E, 0x29000000, + 0x94045011, 0x38020000, 0x32000000, 0x080000E2, 0x60000000, 0xD80000E6, + 0xD40000E9, 0x040000EC, 0x30000000, 0x0EF1F121, 0x360048EF, 0x30000000, + 0x0C002421, 0x360048EF, 0x30000000, 0x0C000021, 0x360048EF, 0x28020000, + 0x0800007B, 0x50001EFE, 0x5A010000, 0x6DC998F5, 0xFC19D4F8, 0x3000028E, + 0x32000040, 0x040000FB, 0x3AEE028E, 0x32000080, 0x040000FB, 0x30000000, + 0x0EF1F101, 0x360048FE, 0x28020000, 0x08000100, 0x3802008E, 0x3D040431, + 0x08000103, 0x3805008E, 0x1F241821, 0x08000106, 0x3000008E, 0xA0163021, + 0x04000109, 0x3000008E, 0x0EF10012, 0x3400010C, 0x300014F6, 0x32010000, + 0x04000114, 0x20000000, 0x04000111, 0x300000EC, 0x50000000, 0x040000F1, + 0x300014F6, 0x32030000, 0x04000117, 0x30001086, 0x3D840473, 0x0400011A, + 0x5000108E, 0x22C00000, 0x8E47C0CB, 0xCB30011E, 0x300019B6, 0x32040000, + 0x04000121, 0x300019B6, 0x3D040431, 0x04000124, 0x300019B6, 0x22800000, + 0x04000111, 0x00000000, 0x00000000, 0x00000000, 0x30000186, 0x3D840473, + 0x0400012D, 0x5000038E, 0x29000000, 0x94045011, 0xC0000131, 0x380C800E, + 0xFF000000, 0x08000134, 0x30000004, 0x0FF1F103, 0x34000137, 0x28020000, + 0x08000139, 0x3000038E, 0x29000000, 0x94045011, 0x00000000, 0x00000000, + 0x00000000, 0x58010006, 0x3D040472, 0xDC204543, 0x081DD4D2, 0x480A0006, + 0xDC2044DC, 0x081DD546, 0x38050004, 0x0EF1F141, 0x342044DC, 0x2802800E, + 0x080000DC, 0x48000035, 0x0400014A, 0x7896638F, 0x4100000F, 0x8C00014F, + 0x080450C4, 0x90104574, 0x88C8620F, 0xC000015A, 0x90104574, 0x08104554, + 0x94104557, 0x3000628F, 0x29000000, 0x9404517A, 0x3000638F, 0x29000000, + 0x0410457A, 0x3800E005, 0x3D010131, 0x0810455D, 0xA832600F, 0x90104574, + 0x08000154, 0x94104557, 0xC6104567, 0xC4185563, 0x5802E00F, 0x0FEEEA07, + 0x80000174, 0x3420456B, 0x5802E00F, 0x0EEEEA07, 0x80000174, 0x3420456B, + 0x30004000, 0x33000001, 0x0400016E, 0x38034005, 0x3D030373, 0x08000171, + 0x30006007, 0x33000000, 0x04000174, 0x3000608F, 0x29000000, 0x94045177, + 0x4000608F, 0xA010457D, 0x0410457A, 0x3000608F, 0x64000101, 0x04104411, + 0x3000608F, 0x64000101, 0x04104580, 0x3000618F, 0x42000001, 0x04000183, + 0x38028000, 0x32000000, 0x08104586, 0x280A618F, 0x08000188, 0x480A618F, + 0xBC00018B, 0x0800018E, 0x3000618F, 0x34000001, 0x04000005, 0x3000618F, + 0x34000000, 0x04000008, 0x3000008F, 0x0EEAED0F, 0x36000194, 0x38038000, + 0x34000000, 0x08000197, 0x38028005, 0x29010002, 0x0800019A, 0x3000028F, + 0x2200209C, 0x0400019D, 0x3000028F, 0x23D4509C, 0x040001A0, 0x2814028F, + 0x080001A2, 0x3000028F, 0x43010201, 0x040001A5, 0x3000128F, 0x32000100, + 0x040001A8, 0x5AEE138F, 0x4100000F, 0x7C0001AC, 0x080000F9, 0x592C138F, + 0x29000000, 0x8C0001B0, 0x080000F9, 0x2000138F, 0x94045011, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000}; + +static u32 txgain_map[96] = { +#ifdef CONFIG_FPGA_VERIFICATION + 0x20c0c971, 0x20c0c980, 0x20c0c992, 0x20c0c9a6, 0x20c0c9bf, 0x20c0caa5, + 0x20c0cabd, 0x20c0cba0, 0x20c0cbb6, 0x20c0cbea, 0x20c0ccc5, 0x20c0cdac, + 0x20c0cdd0, 0x20c0ceb2, 0x20c0ceff, 0x20c0cfff, 0x20c0c922, 0x20c0c922, + 0x20c0c922, 0x20c0c922, 0x20c0c922, 0x20c0c922, 0x20c0c922, 0x20c0c927, + 0x20c0c92c, 0x20c0c931, 0x20c0c937, 0x20c0c93f, 0x20c0c946, 0x20c0c94f, + 0x20c0c959, 0x20c0c964, 0x20c0cbee, 0x20c0cce0, 0x20c0ccff, 0x20c0cde2, + 0x20c0cdfe, 0x20c0cede, 0x20c0cefc, 0x20c0cfd9, 0x20c0cff8, 0x20c0cfff, + 0x20c0cfff, 0x20c0cfff, 0x20c0cfff, 0x20c0cfff, 0x20c0cfff, 0x20c0cfff, + 0x20c0c97c, 0x20c0c97c, 0x20c0c97c, 0x20c0c97c, 0x20c0c97c, 0x20c0c97c, + 0x20c0c97c, 0x20c0c98c, 0x20c0ca79, 0x20c0ca89, 0x20c0cb74, 0x20c0cb84, + 0x20c0cb94, 0x20c0cba8, 0x20c0cbbb, 0x20c0cbd2, 0x20c0cbee, 0x20c0cce0, + 0x20c0ccff, 0x20c0cde2, 0x20c0cdfe, 0x20c0cede, 0x20c0cefc, 0x20c0cfd9, + 0x20c0cff8, 0x20c0cfff, 0x20c0cfff, 0x20c0cfff, 0x20c0cfff, 0x20c0cfff, + 0x20c0cfff, 0x20c0cfff, 0x20c0c97c, 0x20c0c97c, 0x20c0c97c, 0x20c0c97c, + 0x20c0c97c, 0x20c0c97c, 0x20c0c97c, 0x20c0c98c, 0x20c0ca79, 0x20c0ca89, + 0x20c0cb74, 0x20c0cb84, 0x20c0cb94, 0x20c0cba8, 0x20c0cbbb, 0x20c0cbd2, +#else + // 11b + 0x00ffd780, + 0x00ffd872, + 0x00ffd880, + 0x00ffd972, + 0x00ffd980, + 0x00ffda75, + 0x00ffda86, + 0x00ffdb77, + 0x00ffdb86, + 0x00ffdc78, + 0x00ffdc89, + 0x00ffdd79, + 0x00ffdd89, + 0x00ffde83, + 0x00ffdf79, + 0x00ffdf8b, + 0x00ffd072, + 0x00ffd072, + 0x00ffd080, + 0x00ffd172, + 0x00ffd180, + 0x00ffd272, + 0x00ffd280, + 0x00ffd36d, + 0x00ffd379, + 0x00ffd46d, + 0x00ffd479, + 0x00ffd572, + 0x00ffd580, + 0x00ffd672, + 0x00ffd680, + 0x00ffd772, + // high + 0x00ffc87d, + 0x00ffc88b, + 0x00ffc979, + 0x00ffc989, + 0x00ffcc4b, + 0x00ffcc54, + 0x00ffcc5e, + 0x00ffcc69, + 0x00ffcc78, + 0x00ffcc85, + 0x00ffcd70, + 0x00ffcd80, + 0x00ffce70, + 0x00ffce80, + 0x00ffcf7d, + 0x00ffcf90, + 0x00ffc080, + 0x00ffc090, + 0x00ffc180, + 0x00ffc190, + 0x00ffc27b, + 0x00ffc28b, + 0x00ffc37b, + 0x00ffc390, + 0x00ffc485, + 0x00ffc495, + 0x00ffc579, + 0x00ffc589, + 0x00ffc679, + 0x00ffc689, + 0x00ffc780, + 0x00ffc790, + // low + 0x00ffc87d, + 0x00ffc88b, + 0x00ffc979, + 0x00ffc989, + 0x00ffcc4b, + 0x00ffcc54, + 0x00ffcc5e, + 0x00ffcc69, + 0x00ffcc78, + 0x00ffcc85, + 0x00ffcd70, + 0x00ffcd80, + 0x00ffcd90, + 0x00ffce80, + 0x00ffce93, + 0x00ffcf90, + 0x00ffc080, + 0x00ffc090, + 0x00ffc180, + 0x00ffc190, + 0x00ffc27b, + 0x00ffc28b, + 0x00ffc37b, + 0x00ffc390, + 0x00ffc485, + 0x00ffc495, + 0x00ffc579, + 0x00ffc589, + 0x00ffc679, + 0x00ffc689, + 0x00ffc780, + 0x00ffc790, +#endif +}; + +static const u32 txgain_map_h[96] = { + // 11b + 0xffd888, // 11 + 0xffd979, // 12 + 0xffd988, // 13 + 0xffda79, // 14 + 0xffda88, // 15 + 0xffdb79, // 16 + 0xffdb88, // 17 + 0xffdc72, // 18 + 0xffdc80, // 19 + 0xffdd80, // 20 + 0xffde66, // 21 + 0xffde72, // 22 + 0xffde80, // 23 + 0xffdf79, // 24 + 0xffdf88, // 25 + 0xffdf98, // 26 + 0xffd079, //-5 + 0xffd088, //-4 + 0xffd179, //-3 + 0xffd188, //-2 + 0xffd288, //-1 + 0xffd36c, // 0 + 0xffd379, // 1 + 0xffd388, // 2 + 0xffd479, // 3 + 0xffd488, // 4 + 0xffd579, // 5 + 0xffd588, // 6 + 0xffd679, // 7 + 0xffd688, // 8 + 0xffd779, // 9 + 0xffd879, // 10 + // high + 0xffc879, // 8 + 0xffc96b, // 9 + 0xffc979, // 10 + 0xffcc45, // 11 + 0xffcc4d, // 12 + 0xffcc56, // 13 + 0xffcc60, // 14 + 0xffcc6b, // 15 + 0xffcc79, // 16 + 0xffcd72, // 17 + 0xffce60, // 18 + 0xffce72, // 19 + 0xffcf72, // 20 + 0xffcf80, // 21 + 0xffcf90, // 22 + 0xffcf90, // 23 + 0xffc079, //-8 + 0xffc16b, //-7 + 0xffc179, //-6 + 0xffc26b, //-5 + 0xffc279, //-4 + 0xffc36b, //-3 + 0xffc379, //-2 + 0xffc46b, //-1 + 0xffc479, // 0 + 0xffc56b, // 1 + 0xffc579, // 2 + 0xffc66b, // 3 + 0xffc679, // 4 + 0xffc76b, // 5 + 0xffc779, // 6 + 0xffc86b, // 7 + // low + 0xffc879, // 8 + 0xffc96b, // 9 + 0xffc979, // 10 + 0xffcc45, // 11 + 0xffcc4d, // 12 + 0xffcc56, // 13 + 0xffcc60, // 14 + 0xffcc6b, // 15 + 0xffcc79, // 16 + 0xffcd72, // 17 + 0xffce60, // 18 + 0xffce72, // 19 + 0xffcf72, // 20 + 0xffcf80, // 21 + 0xffcf90, // 22 + 0xffcf90, // 23 + 0xffc079, //-8 + 0xffc16b, //-7 + 0xffc179, //-6 + 0xffc26b, //-5 + 0xffc279, //-4 + 0xffc36b, //-3 + 0xffc379, //-2 + 0xffc46b, //-1 + 0xffc479, // 0 + 0xffc56b, // 1 + 0xffc579, // 2 + 0xffc66b, // 3 + 0xffc679, // 4 + 0xffc76b, // 5 + 0xffc779, // 6 + 0xffc86b, // 7 +}; + +static u32 jump_tbl[][2] = { +#ifndef CONFIG_FOR_IPCOM + {296, 0x180001}, {137, 0x180011}, {303, 0x1810f9}, + {168, 0x18186d}, {308, 0x181bbd}, {288, 0x1820c1}, +#else + {308, 0x181001}, {288, 0x181031}, {296, 0x18120d}, {137, 0x18121d}, + {303, 0x182305}, {168, 0x182a79}, {258, 0x182ae1}, +#endif +}; + +static u32 patch_tbl_func[][2] = { +#ifndef CONFIG_FOR_IPCOM + {0x00110054, 0x0018186D}, // same as jump_tbl idx 168 + {0x0011005C, 0x0018186D}, // same as jump_tbl idx 168 +#else + {0x00110054, 0x00182A79}, // same as jump_tbl idx 168 + {0x0011005C, 0x00182A79}, // same as jump_tbl idx 168 + {0x001118D4, 0x00000011}, +#endif +}; + +static u32 patch_tbl_rf_func[][2] = { + {0x00110bf0, 0x00180001}, +}; + +// adap test +static u32 adaptivity_patch_tbl_8800dc[][2] = { + {0x000C, 0x0000320A}, // linkloss_thd + {0x009C, 0x00000000}, // ac_param_conf + {0x0128, 0xF6140001}, // tx_adaptivity_en +}; + +static u8 chip_id; +#define CHIP_ID_H_MASK 0xC0 +#define IS_CHIP_ID_H() ((chip_id & CHIP_ID_H_MASK) == CHIP_ID_H_MASK) + +// Crystal provided by CPU (start) +int set_bbpll_config(struct aic_sdio_dev *rwnx_hw) +{ + int ret = 0; + struct dbg_mem_read_cfm rd_mem_addr_cfm; + + // Read crystal provided by CPU or not. + ret = rwnx_send_dbg_mem_read_req(rwnx_hw, 0x40500148, &rd_mem_addr_cfm); + if (ret) { + AICWFDBG(LOGERROR, "%x rd fail: %d\n", 0x40500148, ret); + return -1; + } + + AICWFDBG(LOGDEBUG, "%s rd_mem_addr_cfm.memdata:%x \r\n", __func__, + rd_mem_addr_cfm.memdata); + + if (!(rd_mem_addr_cfm.memdata & 0x01)) { + AICWFDBG(LOGINFO, "%s Crystal not provided by CPU \r\n", __func__); + return 0; + } + AICWFDBG(LOGINFO, "%s Crystal provided by CPU \r\n", __func__); + // Read 0x40505010 value to check bbpll set or not. + ret = rwnx_send_dbg_mem_read_req(rwnx_hw, 0x40505010, &rd_mem_addr_cfm); + if (ret < 0) { + AICWFDBG(LOGERROR, "%s error ret_val:%d\r\n", __func__, ret); + return -1; + } + + if ((rd_mem_addr_cfm.memdata >> 29) == 3) { + AICWFDBG(LOGERROR, "%s Not need to set \r\n", __func__); + return 0; + } + rd_mem_addr_cfm.memdata |= ((0x1 << 29) | (0x1 << 30)); + rd_mem_addr_cfm.memdata &= (~(0x1 << 31)); + rwnx_send_dbg_mem_write_req(rwnx_hw, 0x40505010, + rd_mem_addr_cfm.memdata); + return 0; +} + +void system_config_8800dc(struct aic_sdio_dev *rwnx_hw) +{ + int syscfg_num; + array3_tbl_t p_syscfg_msk_tbl; + int ret, cnt; + const u32 mem_addr = 0x40500000; + struct dbg_mem_read_cfm rd_mem_addr_cfm; +#ifdef CONFIG_OOB + int oobcfg_num; +#endif + + ret = rwnx_send_dbg_mem_read_req(rwnx_hw, mem_addr, &rd_mem_addr_cfm); + if (ret) { + AICWFDBG(LOGERROR, "%x rd fail: %d\n", mem_addr, ret); + return; + } + chip_id = (u8)(rd_mem_addr_cfm.memdata >> 16); + // printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata); + if (((rd_mem_addr_cfm.memdata >> 25) & 0x01UL) == 0x00UL) + chip_mcu_id = 1; + + ret = rwnx_send_dbg_mem_read_req(rwnx_hw, 0x00000020, &rd_mem_addr_cfm); + if (ret) { + AICWFDBG(LOGERROR, "[0x00000020] rd fail: %d\n", ret); + return; + } + chip_sub_id = (u8)(rd_mem_addr_cfm.memdata); + AICWFDBG(LOGINFO, "chip_id=%x, chip_sub_id=%x!!\n", chip_id, chip_sub_id); + + // Crystal provided by CPU (start) + ret = set_bbpll_config(rwnx_hw); + if (ret) { + AICWFDBG(LOGERROR, "set_bbpll_config fail: %d\n", ret); + return; + } + // Crystal provided by CPU (end) + + ret = rwnx_send_dbg_mem_read_req(rwnx_hw, 0x40500010, &rd_mem_addr_cfm); + AICWFDBG(LOGDEBUG, "[0x40500010]=%x\n", rd_mem_addr_cfm.memdata); + if (ret) { + AICWFDBG(LOGERROR, "[0x40500010] rd fail: %d\n", ret); + return; + } + + syscfg_num = sizeof(syscfg_tbl_8800dc) / sizeof(uint32_t) / 2; + + for (cnt = 0; cnt < syscfg_num; cnt++) { + ret = rwnx_send_dbg_mem_write_req(rwnx_hw, syscfg_tbl_8800dc[cnt][0], + syscfg_tbl_8800dc[cnt][1]); + if (ret) { + AICWFDBG(LOGERROR, "%x write fail: %d\n", syscfg_tbl_8800dc[cnt][0], + ret); + return; + } + } + + u32 tmp_mem_addr = 0; + u32 mem_mask = 0; + u32 mem_data = 0; + + if (chip_mcu_id == 0) { + if (chip_sub_id == 0) { + syscfg_num = sizeof(syscfg_tbl_8800dc_sdio_u01) / sizeof(u32) / 2; + for (cnt = 0; cnt < syscfg_num; cnt++) { + tmp_mem_addr = syscfg_tbl_8800dc_sdio_u01[cnt][0]; + mem_data = syscfg_tbl_8800dc_sdio_u01[cnt][1]; + ret = rwnx_send_dbg_mem_write_req(rwnx_hw, + tmp_mem_addr, + mem_data); + if (ret) { + AICWFDBG(LOGERROR, "%x write fail: %d\n", + syscfg_tbl_8800dc_sdio_u01[cnt][0], ret); + return; + } + } + } else if ((chip_sub_id == 1) || (chip_sub_id == 2)) { + syscfg_num = sizeof(syscfg_tbl_8800dc_sdio_u02) / sizeof(u32) / 2; + for (cnt = 0; cnt < syscfg_num; cnt++) { + tmp_mem_addr = syscfg_tbl_8800dc_sdio_u02[cnt][0]; + mem_data = syscfg_tbl_8800dc_sdio_u02[cnt][1]; + ret = rwnx_send_dbg_mem_write_req(rwnx_hw, + tmp_mem_addr, + mem_data); + if (ret) { + AICWFDBG(LOGERROR, "%x write fail: %d\n", + syscfg_tbl_8800dc_sdio_u02[cnt][0], ret); + return; + } + } + } + } +#ifdef CONFIG_OOB + if (chip_sub_id == 1 || chip_sub_id == 2) { + oobcfg_num = sizeof(oobcfg_tbl_8800dc_sdio_u02) / sizeof(u32) / 2; + for (cnt = 0; cnt < oobcfg_num; cnt++) { + tmp_mem_addr = oobcfg_tbl_8800dc_sdio_u02[cnt][0]; + mem_data = oobcfg_tbl_8800dc_sdio_u02[cnt][1]; + ret = rwnx_send_dbg_mem_write_req(rwnx_hw, + tmp_mem_addr, + mem_data); + if (ret) { + AICWFDBG(LOGERROR, "%x write fail: %d\n", + oobcfg_tbl_8800dc_sdio_u02[cnt][0], ret); + return; + } + } + } +#endif + if (IS_CHIP_ID_H()) { + syscfg_num = sizeof(syscfg_tbl_masked_8800dc_h) / sizeof(u32) / 3; + p_syscfg_msk_tbl = syscfg_tbl_masked_8800dc_h; + } else { + syscfg_num = sizeof(syscfg_tbl_masked_8800dc) / sizeof(u32) / 3; + p_syscfg_msk_tbl = syscfg_tbl_masked_8800dc; + } + + for (cnt = 0; cnt < syscfg_num; cnt++) { + if (p_syscfg_msk_tbl[cnt][0] == 0x00000000) { + break; + } else if (p_syscfg_msk_tbl[cnt][0] == 0x70001000) { + if (chip_mcu_id == 0) { + p_syscfg_msk_tbl[cnt][1] |= ((0x1 << 8) | (0x1 << 15)); // mask + p_syscfg_msk_tbl[cnt][2] |= ((0x1 << 8) | (0x1 << 15)); + } + } + + ret = rwnx_send_dbg_mem_mask_write_req(rwnx_hw, p_syscfg_msk_tbl[cnt][0], + p_syscfg_msk_tbl[cnt][1], + p_syscfg_msk_tbl[cnt][2]); + if (ret) { + AICWFDBG(LOGERROR, "%x mask write fail: %d\n", + p_syscfg_msk_tbl[cnt][0], ret); + return; + } + } + + if (chip_sub_id == 0) { + syscfg_num = sizeof(syscfg_tbl_masked_8800dc_u01) / sizeof(u32) / 3; + for (cnt = 0; cnt < syscfg_num; cnt++) { + tmp_mem_addr = syscfg_tbl_masked_8800dc_u01[cnt][0]; + mem_mask = syscfg_tbl_masked_8800dc_u01[cnt][1]; + mem_data = syscfg_tbl_masked_8800dc_u01[cnt][2]; + ret = rwnx_send_dbg_mem_mask_write_req(rwnx_hw, + tmp_mem_addr, + mem_mask, + mem_data); + if (ret) { + AICWFDBG(LOGERROR, "%x mask write fail: %d\n", + syscfg_tbl_masked_8800dc_u01[cnt][0], ret); + return; + } + } + } +} + +extern struct aicbsp_info_t aicbsp_info; + +void aicwf_patch_config_8800dc(struct aic_sdio_dev *rwnx_hw) +{ + int ret = 0; + int cnt = 0; + u32 mem_addr = 0; + u32 mem_data = 0; + + if (testmode == 0) { + const u32 cfg_base = 0x10164; + struct dbg_mem_read_cfm cfm; + int i; + u32 wifisetting_cfg_addr; + u32 ldpc_cfg_addr; + u32 agc_cfg_addr; + u32 txgain_cfg_addr; + u32 jump_tbl_addr = 0; + + u32 patch_tbl_wifisetting_num; // = + // sizeof(patch_tbl_wifisetting_8800dc_u02)/sizeof(u32)/2; + u32 ldpc_cfg_size = sizeof(ldpc_cfg_ram); + u32 agc_cfg_size = sizeof(agc_cfg_ram); + u32 txgain_cfg_size, *txgain_cfg_array; + u32 jump_tbl_size = 0; + u32 patch_tbl_func_num = 0; + + // adap test + int adap_patch_num = 0; + // adap test + + array2_tbl_t jump_tbl_base = NULL; + array2_tbl_t patch_tbl_func_base = NULL; + array2_tbl_t patch_tbl_wifisetting_8800dc_base = NULL; + + if (chip_sub_id == 0) { + jump_tbl_base = jump_tbl; + jump_tbl_size = sizeof(jump_tbl) / 2; + patch_tbl_func_base = patch_tbl_func; + patch_tbl_func_num = sizeof(patch_tbl_func) / sizeof(u32) / 2; + patch_tbl_wifisetting_num = + sizeof(patch_tbl_wifisetting_8800dc_u01) / sizeof(u32) / 2; + patch_tbl_wifisetting_8800dc_base = + patch_tbl_wifisetting_8800dc_u01; + } else if ((chip_sub_id == 1) || (chip_sub_id == 2)) { + patch_tbl_wifisetting_num = + sizeof(patch_tbl_wifisetting_8800dc_u02) / sizeof(u32) / 2; + patch_tbl_wifisetting_8800dc_base = + patch_tbl_wifisetting_8800dc_u02; + } else { + pr_info("unsupported id: %d", chip_sub_id); + return; + } + + ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "setting base[0x%x] rd fail: %d\n", cfg_base, + ret); + } + wifisetting_cfg_addr = cfm.memdata; + + if (chip_sub_id == 0) { + ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 4, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "setting base[0x%x] rd fail: %d\n", + cfg_base + 4, ret); + } + jump_tbl_addr = cfm.memdata; + } + + ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 8, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "setting base[0x%x] rd fail: %d\n", cfg_base + 8, + ret); + } + ldpc_cfg_addr = cfm.memdata; + + ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0xc, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "setting base[0x%x] rd fail: %d\n", + cfg_base + 0xc, ret); + } + agc_cfg_addr = cfm.memdata; + + ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x10, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "setting base[0x%x] rd fail: %d\n", + cfg_base + 0x10, ret); + } + txgain_cfg_addr = cfm.memdata; + + AICWFDBG(LOGINFO, + "wifisetting_cfg_addr=%x, ldpc_cfg_addr=%x, agc_cfg_addr=%x, txgain_cfg_addr=%x\n", + wifisetting_cfg_addr, ldpc_cfg_addr, agc_cfg_addr, + txgain_cfg_addr); + + for (cnt = 0; cnt < patch_tbl_wifisetting_num; cnt++) { + mem_addr = wifisetting_cfg_addr + + patch_tbl_wifisetting_8800dc_base[cnt][0]; + mem_data = patch_tbl_wifisetting_8800dc_base[cnt][1]; + ret = rwnx_send_dbg_mem_write_req(rwnx_hw, mem_addr, mem_data); + if (ret) { + AICWFDBG(LOGERROR, "wifisetting %x write fail\n", + patch_tbl_wifisetting_8800dc_base[cnt][0]); + } + } + + // adap test + if (aicbsp_info.adap_test) { + AICWFDBG(LOGINFO, "%s for adaptivity test \r\n", __func__); + adap_patch_num = + sizeof(adaptivity_patch_tbl_8800dc) / sizeof(u32) / 2; + for (cnt = 0; cnt < adap_patch_num; cnt++) { + mem_addr = wifisetting_cfg_addr + + adaptivity_patch_tbl_8800dc[cnt][0]; + mem_data = adaptivity_patch_tbl_8800dc[cnt][1]; + ret = rwnx_send_dbg_mem_write_req(rwnx_hw, mem_addr, mem_data); + if (ret) { + AICWFDBG(LOGERROR, "%x write fail\n", + wifisetting_cfg_addr + + adaptivity_patch_tbl_8800dc[cnt][0]); + } + } + } + // adap test + + if (ldpc_cfg_size > 512) { // > 0.5KB data + for (i = 0; i < (ldpc_cfg_size - 512); + i += 512) { // each time write 0.5KB + ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ldpc_cfg_addr + i, + 512, ldpc_cfg_ram + i / 4); + if (ret) { + AICWFDBG(LOGERROR, "ldpc upload fail: %x, err:%d\r\n", + ldpc_cfg_addr + i, ret); + break; + } + } + } + + if (!ret && i < ldpc_cfg_size) { // < 0.5KB data + ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ldpc_cfg_addr + i, + ldpc_cfg_size - i, + ldpc_cfg_ram + i / 4); + if (ret) { + AICWFDBG(LOGERROR, "ldpc upload fail: %x, err:%d\r\n", + ldpc_cfg_addr + i, ret); + } + } + + if (agc_cfg_size > 512) { // > 0.5KB data + for (i = 0; i < (agc_cfg_size - 512); + i += 512) { // each time write 0.5KB + ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, agc_cfg_addr + i, + 512, agc_cfg_ram + i / 4); + if (ret) { + AICWFDBG(LOGERROR, "agc upload fail: %x, err:%d\r\n", + agc_cfg_addr + i, ret); + break; + } + } + } + + if (!ret && i < agc_cfg_size) { // < 0.5KB data + ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, agc_cfg_addr + i, + agc_cfg_size - i, + agc_cfg_ram + i / 4); + if (ret) { + AICWFDBG(LOGERROR, "agc upload fail: %x, err:%d\r\n", + agc_cfg_addr + i, ret); + } + } + +#if !defined(CONFIG_FPGA_VERIFICATION) + if ((IS_CHIP_ID_H())) { + txgain_cfg_size = sizeof(txgain_map_h); + txgain_cfg_array = (u32 *)txgain_map_h; + } else { + txgain_cfg_size = sizeof(txgain_map); + txgain_cfg_array = (u32 *)txgain_map; + } + ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, txgain_cfg_addr, + txgain_cfg_size, txgain_cfg_array); + if (ret) { + AICWFDBG(LOGERROR, "txgain upload fail: %x, err:%d\r\n", + txgain_cfg_addr, ret); + } + + if (chip_sub_id == 0) { + for (cnt = 0; cnt < jump_tbl_size / 4; cnt += 1) { + AICWFDBG(LOGDEBUG, "%x = %x\n", + jump_tbl_base[cnt][0] * 4 + jump_tbl_addr, + jump_tbl_base[cnt][1]); + ret = rwnx_send_dbg_mem_write_req(rwnx_hw, + jump_tbl_base[cnt][0] * + 4 + jump_tbl_addr, + jump_tbl_base[cnt][1]); + if (ret) { + AICWFDBG(LOGERROR, "%x write fail\n", + jump_tbl_addr + 8 * cnt); + } + } + for (cnt = 0; cnt < patch_tbl_func_num; cnt++) { + mem_addr = patch_tbl_func_base[cnt][0]; + mem_data = patch_tbl_func_base[cnt][1]; + ret = rwnx_send_dbg_mem_write_req(rwnx_hw, mem_addr, mem_data); + if (ret) { + AICWFDBG(LOGERROR, "patch_tbl_func %x write fail\n", + patch_tbl_func_base[cnt][0]); + } + } + } else if (chip_sub_id == 1) { + ret = aicwf_patch_table_load(rwnx_hw, + RWNX_MAC_PATCH_TABLE_8800DC_U02); + if (ret) + pr_warn("patch_tbl upload fail: err:%d\r\n", ret); + } else if (chip_sub_id == 2) { + ret = aicwf_patch_table_load(rwnx_hw, + RWNX_MAC_PATCH_TABLE_8800DC_H_U02); + if (ret) + pr_warn("patch_tbl upload fail: err:%d\r\n", ret); + } else { + pr_info("unsupported id: %d\n", chip_sub_id); + } + +#endif + } else { + if (chip_sub_id == 0) { + u32 patch_tbl_rf_func_num = + sizeof(patch_tbl_rf_func) / sizeof(u32) / 2; + for (cnt = 0; cnt < patch_tbl_rf_func_num; cnt++) { + mem_addr = patch_tbl_rf_func[cnt][0]; + mem_data = patch_tbl_rf_func[cnt][1]; + ret = rwnx_send_dbg_mem_write_req(rwnx_hw, mem_addr, mem_data); + if (ret) { + AICWFDBG(LOGERROR, "patch_tbl_rf_func %x write fail\n", + patch_tbl_rf_func[cnt][0]); + } + } + } + } +} + +int rwnx_plat_patch_load_8800dc(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + AICWFDBG(LOGINFO, "testmode=%d\n", testmode); + if (chip_sub_id == 0) { + if (testmode == FW_NORMAL_MODE) { + AICWFDBG(LOGINFO, "rwnx_plat_patch_loading\n"); + ret = aicwf_plat_patch_load_8800dc(sdiodev); + if (ret) { + AICWFDBG(LOGINFO, "load patch bin fail: %d\n", ret); + return ret; + } + } else if (testmode == FW_RFTEST_MODE) { + ret = aicwf_plat_rftest_load_8800dc(sdiodev); + if (ret) { + AICWFDBG(LOGINFO, "load rftest bin fail: %d\n", ret); + return ret; + } + } + } else if (chip_sub_id >= 1) { + if (testmode == FW_NORMAL_MODE) { + AICWFDBG(LOGINFO, "rwnx_plat_patch_loading\n"); + ret = aicwf_plat_patch_load_8800dc(sdiodev); + if (ret) { + AICWFDBG(LOGINFO, "load patch bin fail: %d\n", ret); + return ret; + } +#ifdef CONFIG_DPD +#ifdef CONFIG_FORCE_DPD_CALIB + if (1) { + AICWFDBG(LOGINFO, "dpd calib & write\n"); + ret = aicwf_dpd_calib_8800dc(sdiodev, &dpd_res); + if (ret) { + AICWFDBG(LOGINFO, "dpd calib fail: %d\n", ret); + return ret; + } + } +#else + if (is_file_exist(FW_DPDRESULT_NAME_8800DC) == 1) { + AICWFDBG(LOGINFO, "dpd bin load\n"); + ret = aicwf_dpd_result_load_8800dc(sdiodev, &dpd_res); + if (ret) { + AICWFDBG(LOGINFO, "load dpd bin fail: %d\n", ret); + return ret; + } + ret = aicwf_dpd_result_apply_8800dc(sdiodev, &dpd_res); + if (ret) { + AICWFDBG(LOGINFO, "apply dpd bin fail: %d\n", ret); + return ret; + } + } +#endif + else +#endif + { + ret = aicwf_misc_ram_init_8800dc(sdiodev); + if (ret) { + AICWFDBG(LOGINFO, "misc ram init fail: %d\n", ret); + return ret; + } + } + } else if (testmode == FW_RFTEST_MODE) { +#ifdef CONFIG_DPD +#ifdef CONFIG_FORCE_DPD_CALIB + if (1) { + AICWFDBG(LOGINFO, "patch load\n"); + ret = aicwf_plat_patch_load_8800dc(sdiodev); + if (ret) { + AICWFDBG(LOGINFO, "load patch bin fail: %d\n", ret); + return ret; + } + AICWFDBG(LOGINFO, "dpd calib & write\n"); + ret = aicwf_dpd_calib_8800dc(sdiodev, &dpd_res); + if (ret) { + AICWFDBG(LOGINFO, "dpd calib fail: %d\n", ret); + return ret; + } + } +#endif +#endif + ret = aicwf_plat_rftest_load_8800dc(sdiodev); + if (ret) { + AICWFDBG(LOGINFO, "load rftest bin fail: %d\n", ret); + return ret; + } + } else if (testmode == FW_DPDCALIB_MODE) { +#if (defined(CONFIG_DPD) && !defined(CONFIG_FORCE_DPD_CALIB)) + if (is_file_exist(FW_DPDRESULT_NAME_8800DC) == 0) { + AICWFDBG(LOGINFO, "patch load\n"); + ret = aicwf_plat_patch_load_8800dc(sdiodev); + if (ret) { + AICWFDBG(LOGINFO, "load patch bin fail: %d\n", ret); + return ret; + } + AICWFDBG(LOGINFO, "dpd calib & write\n"); + ret = aicwf_dpd_calib_8800dc(sdiodev, &dpd_res); + if (ret) { + AICWFDBG(LOGINFO, "dpd calib fail: %d\n", ret); + return ret; + } + ret = aicwf_dpd_result_write_8800dc((void *)&dpd_res, + DPD_RESULT_SIZE_8800DC); + if (ret) { + AICWFDBG(LOGINFO, "file write fail: %d\n", ret); + return ret; + } + } +#endif + return 1; // exit calib mode + } + } + + return ret; +} + +int aicwf_misc_ram_init_8800dc(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; + u32 cfg_base = 0x10164; + struct dbg_mem_read_cfm cfm; + u32 misc_ram_addr; + u32 misc_ram_size = 12; + int i; + + if (testmode == FW_RFTEST_MODE) + cfg_base = RAM_LMAC_FW_ADDR + 0x0164; + // init misc ram + ret = rwnx_send_dbg_mem_read_req(sdiodev, cfg_base + 0x14, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "rf misc ram[0x%x] rd fail: %d\n", cfg_base + 0x14, + ret); + return ret; + } + misc_ram_addr = cfm.memdata; + AICWFDBG(LOGERROR, "misc_ram_addr=%x\n", misc_ram_addr); + for (i = 0; i < (misc_ram_size / 4); i++) { + ret = rwnx_send_dbg_mem_write_req(sdiodev, misc_ram_addr + i * 4, 0); + if (ret) { + AICWFDBG(LOGERROR, "rf misc ram[0x%x] wr fail: %d\n", + misc_ram_addr + i * 4, ret); + return ret; + } + } + return ret; +} + +int start_from_bootrom_8800DC(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; + u32 rd_addr; + u32 fw_addr; + u32 boot_type; + struct dbg_mem_read_cfm rd_cfm; + + /* memory access */ + if (testmode == 1) { + rd_addr = RAM_LMAC_FW_ADDR; + fw_addr = RAM_LMAC_FW_ADDR; + } else { + rd_addr = RAM_FMAC_FW_ADDR; + fw_addr = RAM_FMAC_FW_ADDR; + } + + AICWFDBG(LOGINFO, "Read FW mem: %08x\n", rd_addr); + ret = rwnx_send_dbg_mem_read_req(sdiodev, rd_addr, &rd_cfm); + if (ret) + return -1; + AICWFDBG(LOGINFO, "cfm: [%08x] = %08x\n", rd_cfm.memaddr, rd_cfm.memdata); + + if (testmode == 0) + boot_type = HOST_START_APP_DUMMY; + else + boot_type = HOST_START_APP_AUTO; + /* fw start */ + AICWFDBG(LOGINFO, "Start app: %08x, %d\n", fw_addr, boot_type); + ret = rwnx_send_dbg_start_app_req(sdiodev, fw_addr, boot_type, NULL); + if (ret) + return -1; + return 0; +} + +#ifdef CONFIG_DPD +int aicwf_dpd_calib_8800dc(struct aic_sdio_dev *sdiodev, + struct rf_misc_ram_lite_t *dpd_res) +{ + int ret = 0; + u32 fw_addr, boot_type; + int valid_flag; + + ret = aicwf_misc_ram_valid_check_8800dc(sdiodev, &valid_flag); + if (ret) { + AICWFDBG(LOGINFO, "misc ram check fail: %d\n", ret); + return ret; + } + if (valid_flag) { + AICWFDBG(LOGINFO, "misc ram valid, skip calib process\n"); + return ret; + } + ret = aicwf_plat_calib_load_8800dc(sdiodev); + if (ret) { + AICWFDBG(LOGINFO, "load calib bin fail: %d\n", ret); + return ret; + } + /* fw start */ + fw_addr = 0x00130009; + boot_type = HOST_START_APP_FNCALL; + AICWFDBG(LOGINFO, "Start app: %08x, %d\n", fw_addr, boot_type); + ret = rwnx_send_dbg_start_app_req(sdiodev, fw_addr, boot_type, NULL); + if (ret) { + AICWFDBG(LOGINFO, "start app fail: %d\n", ret); + return ret; + } + { // read dpd res + const u32 cfg_base = 0x10164; + struct dbg_mem_read_cfm cfm; + u32 misc_ram_addr; + u32 ram_base_addr, ram_word_cnt; + int i; + + ret = rwnx_send_dbg_mem_read_req(sdiodev, cfg_base + 0x14, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "rf misc ram[0x%x] rd fail: %d\n", + cfg_base + 0x14, ret); + return ret; + } + misc_ram_addr = cfm.memdata; + // bit_mask + ram_base_addr = misc_ram_addr + offsetof(struct rf_misc_ram_t, bit_mask); + ram_word_cnt = (MEMBER_SIZE(struct rf_misc_ram_t, bit_mask) + + MEMBER_SIZE(struct rf_misc_ram_t, reserved)) / + 4; + for (i = 0; i < ram_word_cnt; i++) { + ret = rwnx_send_dbg_mem_read_req(sdiodev, ram_base_addr + i * 4, + &cfm); + if (ret) { + AICWFDBG(LOGERROR, "bit_mask[0x%x] rd fail: %d\n", + ram_base_addr + i * 4, ret); + return ret; + } + dpd_res->bit_mask[i] = cfm.memdata; + } + // dpd_high + ram_base_addr = misc_ram_addr + offsetof(struct rf_misc_ram_t, dpd_high); + ram_word_cnt = MEMBER_SIZE(struct rf_misc_ram_t, dpd_high) / 4; + for (i = 0; i < ram_word_cnt; i++) { + ret = rwnx_send_dbg_mem_read_req(sdiodev, ram_base_addr + i * 4, + &cfm); + if (ret) { + AICWFDBG(LOGERROR, "bit_mask[0x%x] rd fail: %d\n", + ram_base_addr + i * 4, ret); + return ret; + } + dpd_res->dpd_high[i] = cfm.memdata; + } + // loft_res + ram_base_addr = misc_ram_addr + offsetof(struct rf_misc_ram_t, loft_res); + ram_word_cnt = MEMBER_SIZE(struct rf_misc_ram_t, loft_res) / 4; + for (i = 0; i < ram_word_cnt; i++) { + ret = rwnx_send_dbg_mem_read_req(sdiodev, ram_base_addr + i * 4, + &cfm); + if (ret) { + AICWFDBG(LOGERROR, "bit_mask[0x%x] rd fail: %d\n", + ram_base_addr + i * 4, ret); + return ret; + } + dpd_res->loft_res[i] = cfm.memdata; + } + } + return ret; +} + +int aicwf_dpd_result_apply_8800dc(struct aic_sdio_dev *sdiodev, + struct rf_misc_ram_lite_t *dpd_res) +{ + int ret = 0; + u32 cfg_base = 0x10164; + struct dbg_mem_read_cfm cfm; + u32 misc_ram_addr; + u32 ram_base_addr, ram_byte_cnt; + + AICWFDBG(LOGINFO, "bit_mask[1]=%x\n", dpd_res->bit_mask[1]); + if (dpd_res->bit_mask[1] == 0) { + AICWFDBG(LOGERROR, "void dpd_res, bypass it.\n"); + return 0; + } + if (testmode == FW_RFTEST_MODE) + cfg_base = RAM_LMAC_FW_ADDR + 0x0164; + ret = rwnx_send_dbg_mem_read_req(sdiodev, cfg_base + 0x14, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "rf misc ram[0x%x] rd fail: %d\n", cfg_base + 0x14, + ret); + return ret; + } + misc_ram_addr = cfm.memdata; + AICWFDBG(LOGINFO, "misc_ram_addr: %x\n", misc_ram_addr); + /* Copy dpd_res on the Embedded side */ + // bit_mask + AICWFDBG(LOGINFO, "bit_mask[0]=%x\n", dpd_res->bit_mask[0]); + ram_base_addr = misc_ram_addr + offsetof(struct rf_misc_ram_t, bit_mask); + ram_byte_cnt = MEMBER_SIZE(struct rf_misc_ram_t, bit_mask) + + MEMBER_SIZE(struct rf_misc_ram_t, reserved); + ret = rwnx_send_dbg_mem_block_write_req(sdiodev, ram_base_addr, ram_byte_cnt, + (u32 *)&dpd_res->bit_mask[0]); + if (ret) { + AICWFDBG(LOGERROR, "bit_mask wr fail: %x, ret:%d\r\n", ram_base_addr, + ret); + return ret; + } + // dpd_high + AICWFDBG(LOGINFO, "dpd_high[0]=%x\n", dpd_res->dpd_high[0]); + ram_base_addr = misc_ram_addr + offsetof(struct rf_misc_ram_t, dpd_high); + ram_byte_cnt = MEMBER_SIZE(struct rf_misc_ram_t, dpd_high); + ret = rwnx_send_dbg_mem_block_write_req(sdiodev, ram_base_addr, ram_byte_cnt, + (u32 *)&dpd_res->dpd_high[0]); + if (ret) { + AICWFDBG(LOGERROR, "dpd_high wr fail: %x, ret:%d\r\n", ram_base_addr, + ret); + return ret; + } + // loft_res + AICWFDBG(LOGINFO, "loft_res[0]=%x\n", dpd_res->loft_res[0]); + ram_base_addr = misc_ram_addr + offsetof(struct rf_misc_ram_t, loft_res); + ram_byte_cnt = MEMBER_SIZE(struct rf_misc_ram_t, loft_res); + ret = rwnx_send_dbg_mem_block_write_req(sdiodev, ram_base_addr, ram_byte_cnt, + (u32 *)&dpd_res->loft_res[0]); + if (ret) { + AICWFDBG(LOGERROR, "loft_res wr fail: %x, ret:%d\r\n", ram_base_addr, + ret); + return ret; + } + return ret; +} + +#ifndef CONFIG_FORCE_DPD_CALIB +int aicwf_dpd_result_load_8800dc(struct aic_sdio_dev *sdiodev, + struct rf_misc_ram_lite_t *dpd_res) +{ + int ret = 0; + int size; + u32 *dst = NULL; + char *filename = FW_DPDRESULT_NAME_8800DC; + struct device *dev = sdiodev->dev; + + AICWFDBG(LOGINFO, "%s: dpd_res file path:%s \r\n", __func__, filename); + /* load file */ + size = rwnx_load_firmware(&dst, filename, dev); + if (size <= 0) { + AICWFDBG(LOGERROR, "wrong size of dpd_res file\n"); + if (dst) { + vfree(dst); + dst = NULL; + } + return -1; + } + AICWFDBG(LOGINFO, "### Load file done: %s, size=%d, dst[0]=%x\n", filename, + size, dst[0]); + memcpy((u8 *)dpd_res, (u8 *)dst, sizeof(struct rf_misc_ram_lite_t)); + if (dst) { + vfree(dst); + dst = NULL; + } + return ret; +} + +#endif /* !CONFIG_FORCE_DPD_CALIB */ +#endif diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic8800dc_compat.h b/drivers/net/wireless/aic/aic8800_bsp/aic8800dc_compat.h new file mode 100644 index 0000000000000..0edee0f8d6cc3 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic8800dc_compat.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _AIC8800DC_COMPAT_H_ +#define _AIC8800DC_COMPAT_H_ + +#include + +#include "aicsdio.h" +typedef u32 (*array2_tbl_t)[2]; +typedef u32 (*array3_tbl_t)[3]; + +extern u8 chip_sub_id; +extern u8 chip_mcu_id; +#define FW_PATH_MAX_LEN 200 + +void aicwf_patch_config_8800dc(struct aic_sdio_dev *rwnx_hw); +void system_config_8800dc(struct aic_sdio_dev *rwnx_hw); +int rwnx_plat_patch_load_8800dc(struct aic_sdio_dev *sdiodev); +int aicwf_misc_ram_init_8800dc(struct aic_sdio_dev *sdiodev); +int start_from_bootrom_8800DC(struct aic_sdio_dev *sdiodev); + +#ifdef CONFIG_DPD +int aicwf_dpd_calib_8800dc(struct aic_sdio_dev *sdiodev, + struct rf_misc_ram_lite_t *dpd_res); +int aicwf_dpd_result_apply_8800dc(struct aic_sdio_dev *sdiodev, + struct rf_misc_ram_lite_t *dpd_res); +#ifndef CONFIG_FORCE_DPD_CALIB +int aicwf_dpd_result_load_8800dc(struct aic_sdio_dev *sdiodev, + struct rf_misc_ram_lite_t *dpd_res); +#endif /* !CONFIG_FORCE_DPD_CALIB */ +#endif + +int set_bbpll_config(struct aic_sdio_dev *rwnx_hw); + +#endif diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_driver.c b/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_driver.c new file mode 100644 index 0000000000000..167c161b097d3 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_driver.c @@ -0,0 +1,1670 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief bsp sdio declarations + * + ****************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aic8800d80_compat.h" +#include "aic8800dc_compat.h" +#include "aic_bsp_driver.h" +#include "aicsdio.h" +#include "aicsdio_txrxif.h" +#include "aic_chip_ops.h" +#include + +int cur_mode; + +static void cmd_dump(const struct rwnx_cmd *cmd) +{ + if (!cmd) { + pr_err("aicbsp %s cmd is null\n", __func__); + return; + } + pr_crit("aicbsp tkn[%d] flags:%04x result:%3d cmd:%4d - reqcfm(%4d)\n", + cmd->tkn, cmd->flags, cmd->result, cmd->id, cmd->reqid); +} + +static void cmd_complete(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd) +{ + lockdep_assert_held(&cmd_mgr->lock); + + list_del(&cmd->list); + cmd_mgr->queue_sz--; + + cmd->flags |= RWNX_CMD_FLAG_DONE; + if (cmd->flags & RWNX_CMD_FLAG_NONBLOCK) { + kfree(cmd); + } else { + if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) { + cmd->result = 0; + complete(&cmd->complete); + } + } +} + +static int cmd_mgr_queue(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd) +{ + bool defer_push = false; + int err = 0; + + spin_lock_bh(&cmd_mgr->lock); + + if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) { + pr_crit("aicbsp cmd queue crashed\n"); + cmd->result = -EPIPE; + spin_unlock_bh(&cmd_mgr->lock); + return -EPIPE; + } + + if (!list_empty(&cmd_mgr->cmds)) { + struct rwnx_cmd *last; + + if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) { + pr_crit("aicbsp Too many cmds (%d) already queued\n", + cmd_mgr->max_queue_sz); + cmd->result = -ENOMEM; + spin_unlock_bh(&cmd_mgr->lock); + return -ENOMEM; + } + last = list_entry(cmd_mgr->cmds.prev, struct rwnx_cmd, list); + if (last->flags & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_PUSH)) { + cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH; + defer_push = true; + } + } + + if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM) + cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM; + + cmd->tkn = cmd_mgr->next_tkn++; + cmd->result = -EINTR; + + if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) + init_completion(&cmd->complete); + + list_add_tail(&cmd->list, &cmd_mgr->cmds); + cmd_mgr->queue_sz++; + spin_unlock_bh(&cmd_mgr->lock); + + if (!defer_push) { + rwnx_set_cmd_tx((void *)(cmd_mgr->sdiodev), cmd->a2e_msg, + sizeof(struct lmac_msg) + cmd->a2e_msg->param_len); + kfree(cmd->a2e_msg); + } else { + pr_err("aicbsp ERR: never defer push!!!!"); + return 0; + } + + if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) { + unsigned long tout = + msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz); + if (!wait_for_completion_timeout(&cmd->complete, tout)) { + pr_crit("aicbsp cmd timed-out\n"); + cmd_dump(cmd); + spin_lock_bh(&cmd_mgr->lock); + cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED; + if (!(cmd->flags & RWNX_CMD_FLAG_DONE)) { + cmd->result = -ETIMEDOUT; + cmd_complete(cmd_mgr, cmd); + } + spin_unlock_bh(&cmd_mgr->lock); + err = -ETIMEDOUT; + } else { + kfree(cmd); + } + } else { + cmd->result = 0; + } + + return err; +} + +static int cmd_mgr_run_callback(struct rwnx_cmd_mgr *cmd_mgr, + struct rwnx_cmd *cmd, + struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb) +{ + int res; + + if (!cb) + return 0; + spin_lock(&cmd_mgr->cb_lock); + res = cb(cmd, msg); + spin_unlock(&cmd_mgr->cb_lock); + + return res; +} + +static int cmd_mgr_msgind(struct rwnx_cmd_mgr *cmd_mgr, + struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb) +{ + struct rwnx_cmd *cmd; + bool found = false; + + // printk("aicbsp cmd->id=%x\n", msg->id); + spin_lock(&cmd_mgr->lock); + list_for_each_entry(cmd, &cmd_mgr->cmds, list) { + if (cmd->reqid == msg->id && (cmd->flags & RWNX_CMD_FLAG_WAIT_CFM)) { + if (!cmd_mgr_run_callback(cmd_mgr, cmd, msg, cb)) { + found = true; + cmd->flags &= ~RWNX_CMD_FLAG_WAIT_CFM; + + if (WARN(msg->param_len > RWNX_CMD_E2AMSG_LEN_MAX, + "Unexpect E2A msg len %d > %d\n", msg->param_len, + RWNX_CMD_E2AMSG_LEN_MAX)) { + msg->param_len = RWNX_CMD_E2AMSG_LEN_MAX; + } + + if (cmd->e2a_msg && msg->param_len) + memcpy(cmd->e2a_msg, &msg->param, msg->param_len); + + if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) + cmd_complete(cmd_mgr, cmd); + + break; + } + } + } + spin_unlock(&cmd_mgr->lock); + + if (!found) + cmd_mgr_run_callback(cmd_mgr, NULL, msg, cb); + + return 0; +} + +static void cmd_mgr_print(struct rwnx_cmd_mgr *cmd_mgr) +{ + struct rwnx_cmd *cur; + + if (!cmd_mgr) { + pr_err("aicbsp %s cmd_mgr is null\n", __func__); + return; + } + + spin_lock_bh(&cmd_mgr->lock); + list_for_each_entry(cur, &cmd_mgr->cmds, list) { + cmd_dump(cur); + } + spin_unlock_bh(&cmd_mgr->lock); +} + +static void cmd_mgr_drain(struct rwnx_cmd_mgr *cmd_mgr) +{ + struct rwnx_cmd *cur, *nxt; + + if (!cmd_mgr) { + pr_err("aicbsp %s cmd_mgr is null\n", __func__); + return; + } + + spin_lock_bh(&cmd_mgr->lock); + list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) { + list_del(&cur->list); + cmd_mgr->queue_sz--; + if (!(cur->flags & RWNX_CMD_FLAG_NONBLOCK)) + complete(&cur->complete); + } + spin_unlock_bh(&cmd_mgr->lock); +} + +void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr) +{ + cmd_mgr->max_queue_sz = RWNX_CMD_MAX_QUEUED; + INIT_LIST_HEAD(&cmd_mgr->cmds); + cmd_mgr->state = RWNX_CMD_MGR_STATE_INITED; + spin_lock_init(&cmd_mgr->lock); + spin_lock_init(&cmd_mgr->cb_lock); + cmd_mgr->queue = &cmd_mgr_queue; + cmd_mgr->print = &cmd_mgr_print; + cmd_mgr->drain = &cmd_mgr_drain; + cmd_mgr->llind = NULL; + cmd_mgr->msgind = &cmd_mgr_msgind; +} + +void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr) +{ + if (!cmd_mgr) { + pr_err("aicbsp %s cmd_mgr is null\n", __func__); + return; + } + cmd_mgr->print(cmd_mgr); + cmd_mgr->drain(cmd_mgr); + cmd_mgr->print(cmd_mgr); + memset(cmd_mgr, 0, sizeof(*cmd_mgr)); +} + +void rwnx_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len) +{ + struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *)dev; + struct aicwf_bus *bus = sdiodev->bus_if; + u8 *buffer = bus->cmd_buf; + u16 index = 0; + + memset(buffer, 0, CMD_BUF_MAX); + buffer[0] = (len + 4) & 0x00ff; + buffer[1] = ((len + 4) >> 8) & 0x0f; + buffer[2] = 0x11; + if (sdiodev->chip_ops->use_hdr_checksum) + buffer[3] = crc8_ponl_107(&buffer[0], 3); // crc8 + else + buffer[3] = 0x0; + + index += 4; + // there is a dummy word + index += 4; + + // make sure little endian + put_u16(&buffer[index], msg->id); + index += 2; + put_u16(&buffer[index], msg->dest_id); + index += 2; + put_u16(&buffer[index], msg->src_id); + index += 2; + put_u16(&buffer[index], msg->param_len); + index += 2; + memcpy(&buffer[index], (u8 *)msg->param, msg->param_len); + + aicwf_bus_txmsg(bus, buffer, len + 8); +} + +static inline void *rwnx_msg_zalloc(lmac_msg_id_t const id, + lmac_task_id_t const dest_id, + lmac_task_id_t const src_id, + uint16_t const param_len) +{ + struct lmac_msg *msg; + gfp_t flags; + + if (in_softirq()) + flags = GFP_ATOMIC; + else + flags = GFP_KERNEL; + + msg = + kzalloc(sizeof(struct lmac_msg) + param_len, flags); + if (!msg) { + pr_crit("aicbsp %s: msg allocation failed\n", __func__); + return NULL; + } + msg->id = id; + msg->dest_id = dest_id; + msg->src_id = src_id; + msg->param_len = param_len; + + return msg->param; +} + +static void rwnx_msg_free(struct lmac_msg *msg, const void *msg_params) +{ + kfree(msg); +} + +static int rwnx_send_msg(struct aic_sdio_dev *sdiodev, const void *msg_params, + int reqcfm, lmac_msg_id_t reqid, void *cfm) +{ + struct lmac_msg *msg; + struct rwnx_cmd *cmd; + bool nonblock; + int ret = 0; + + msg = container_of((void *)msg_params, struct lmac_msg, param); + if (sdiodev->bus_if->state == BUS_DOWN_ST) { + rwnx_msg_free(msg, msg_params); + pr_info("aicbsp bus is down\n"); + return 0; + } + + nonblock = 0; + cmd = kzalloc_obj(*cmd, nonblock ? GFP_ATOMIC : GFP_KERNEL); + cmd->result = -EINTR; + cmd->id = msg->id; + cmd->reqid = reqid; + cmd->a2e_msg = msg; + cmd->e2a_msg = cfm; + if (nonblock) + cmd->flags = RWNX_CMD_FLAG_NONBLOCK; + if (reqcfm) + cmd->flags |= RWNX_CMD_FLAG_REQ_CFM; + + if (reqcfm) { + cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; // we don't need ack any more + ret = sdiodev->cmd_mgr.queue(&sdiodev->cmd_mgr, cmd); + } else { + rwnx_set_cmd_tx((void *)(sdiodev), cmd->a2e_msg, + sizeof(struct lmac_msg) + cmd->a2e_msg->param_len); + } + + if (!reqcfm) + kfree(cmd); + + return ret; +} + +int rwnx_send_dbg_mem_block_write_req(struct aic_sdio_dev *sdiodev, + u32 mem_addr, u32 mem_size, u32 *mem_data) +{ + struct dbg_mem_block_write_req *mem_blk_write_req; + + /* Build the DBG_MEM_BLOCK_WRITE_REQ message */ + mem_blk_write_req = + rwnx_msg_zalloc(DBG_MEM_BLOCK_WRITE_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_mem_block_write_req)); + if (!mem_blk_write_req) + return -ENOMEM; + + /* Set parameters for the DBG_MEM_BLOCK_WRITE_REQ message */ + mem_blk_write_req->memaddr = mem_addr; + mem_blk_write_req->memsize = mem_size; + memcpy(mem_blk_write_req->memdata, mem_data, mem_size); + + /* Send the DBG_MEM_BLOCK_WRITE_REQ message to LMAC FW */ + return rwnx_send_msg(sdiodev, mem_blk_write_req, 1, DBG_MEM_BLOCK_WRITE_CFM, + NULL); +} + +int rwnx_send_dbg_mem_read_req(struct aic_sdio_dev *sdiodev, u32 mem_addr, + struct dbg_mem_read_cfm *cfm) +{ + struct dbg_mem_read_req *mem_read_req; + + /* Build the DBG_MEM_READ_REQ message */ + mem_read_req = rwnx_msg_zalloc(DBG_MEM_READ_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_mem_read_req)); + if (!mem_read_req) + return -ENOMEM; + + /* Set parameters for the DBG_MEM_READ_REQ message */ + mem_read_req->memaddr = mem_addr; + + /* Send the DBG_MEM_READ_REQ message to LMAC FW */ + return rwnx_send_msg(sdiodev, mem_read_req, 1, DBG_MEM_READ_CFM, cfm); +} + +int rwnx_send_dbg_mem_write_req(struct aic_sdio_dev *sdiodev, u32 mem_addr, + u32 mem_data) +{ + struct dbg_mem_write_req *mem_write_req; + + /* Build the DBG_MEM_WRITE_REQ message */ + mem_write_req = rwnx_msg_zalloc(DBG_MEM_WRITE_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_mem_write_req)); + if (!mem_write_req) + return -ENOMEM; + + /* Set parameters for the DBG_MEM_WRITE_REQ message */ + mem_write_req->memaddr = mem_addr; + mem_write_req->memdata = mem_data; + + /* Send the DBG_MEM_WRITE_REQ message to LMAC FW */ + return rwnx_send_msg(sdiodev, mem_write_req, 1, DBG_MEM_WRITE_CFM, NULL); +} + +int rwnx_send_dbg_mem_mask_write_req(struct aic_sdio_dev *sdiodev, u32 mem_addr, + u32 mem_mask, u32 mem_data) +{ + struct dbg_mem_mask_write_req *mem_mask_write_req; + + /* Build the DBG_MEM_MASK_WRITE_REQ message */ + mem_mask_write_req = + rwnx_msg_zalloc(DBG_MEM_MASK_WRITE_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_mem_mask_write_req)); + if (!mem_mask_write_req) + return -ENOMEM; + + /* Set parameters for the DBG_MEM_MASK_WRITE_REQ message */ + mem_mask_write_req->memaddr = mem_addr; + mem_mask_write_req->memmask = mem_mask; + mem_mask_write_req->memdata = mem_data; + + /* Send the DBG_MEM_MASK_WRITE_REQ message to LMAC FW */ + return rwnx_send_msg(sdiodev, mem_mask_write_req, 1, DBG_MEM_MASK_WRITE_CFM, + NULL); +} + +int rwnx_send_dbg_start_app_req(struct aic_sdio_dev *sdiodev, u32 boot_addr, + u32 boot_type, + struct dbg_start_app_cfm *start_app_cfm) +{ + struct dbg_start_app_req *start_app_req; + + /* Build the DBG_START_APP_REQ message */ + start_app_req = rwnx_msg_zalloc(DBG_START_APP_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_start_app_req)); + if (!start_app_req) { + pr_err("aicbsp start app nomen\n"); + return -ENOMEM; + } + + /* Set parameters for the DBG_START_APP_REQ message */ + start_app_req->bootaddr = boot_addr; + start_app_req->boottype = boot_type; + + /* Send the DBG_START_APP_REQ message to LMAC FW */ + return rwnx_send_msg(sdiodev, start_app_req, 1, DBG_START_APP_CFM, + start_app_cfm); +} + +static msg_cb_fct dbg_hdlrs[MSG_I(DBG_MAX)] = {}; + +static msg_cb_fct *msg_hdlrs[] = { + [TASK_DBG] = dbg_hdlrs, +}; + +void rwnx_rx_handle_msg(struct aic_sdio_dev *sdiodev, struct ipc_e2a_msg *msg) +{ + sdiodev->cmd_mgr.msgind(&sdiodev->cmd_mgr, msg, + msg_hdlrs[MSG_T(msg->id)][MSG_I(msg->id)]); +} + +static inline void print_md5(const u8 *x) +{ + pr_info("aicbsp file md5:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\r\n", + x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], + x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]); +} + +static int aicbsp_request_firmware(const struct firmware **fw, + const char *name, struct device *dev) +{ + char *fw_name; + int ret; + + if (!name || !name[0]) + return -EINVAL; + + fw_name = kasprintf(GFP_KERNEL, AIC8800_FW_DIR "%s", name); + if (!fw_name) + return -ENOMEM; + + ret = request_firmware(fw, fw_name, dev); + kfree(fw_name); + + return ret; +} + +int rwnx_load_firmware(u32 **fw_buf, const char *name, struct device *device) +{ + const struct firmware *fw = NULL; + u32 *dst = NULL; + void *buffer = NULL; +#ifdef CONFIG_TEDIOUS_PRT + u8 digest[MD5_DIGEST_SIZE]; +#endif + int size = 0; + int ret = 0; + + pr_info("aicbsp %s: request firmware = %s\n", __func__, name); + + ret = aicbsp_request_firmware(&fw, name, device); + + if (ret < 0) { + pr_err("aicbsp Load %s fail\n", name); + return ret; + } + + size = fw->size; + dst = (u32 *)fw->data; + + if (size <= 0) { + pr_err("aicbsp wrong size of firmware file\n"); + release_firmware(fw); + return -1; + } + + buffer = vmalloc(size); + if (!buffer) { + release_firmware(fw); + return -ENOMEM; + } + memset(buffer, 0, size); + memcpy(buffer, dst, size); + + *fw_buf = buffer; + +#ifdef CONFIG_TEDIOUS_PRT + md5(buffer, size, digest); + print_md5(digest); +#endif + + release_firmware(fw); + + return size; +} + +extern int testmode; + +#ifdef CONFIG_M2D_OTA_AUTO_SUPPORT +int rwnx_plat_m2d_flash_ota(struct aic_sdio_dev *sdiodev, + char *filename) +{ + struct device *dev = sdiodev->dev; + unsigned int i = 0; + int size; + u32 *dst = NULL; + int err = 0; + int ret; + u8 bond_id; + const u32 mem_addr = 0x40500000; + struct dbg_mem_read_cfm rd_mem_addr_cfm; + + ret = rwnx_send_dbg_mem_read_req(sdiodev, mem_addr, &rd_mem_addr_cfm); + if (ret) { + pr_err("aicbsp m2d %x rd fail: %d\n", mem_addr, ret); + return ret; + } + bond_id = (u8)(rd_mem_addr_cfm.memdata >> 24); + pr_info("aicbsp %x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata); + if (bond_id & (1 << 1)) { + // flash is invalid + pr_err("aicbsp m2d flash is invalid\n"); + return -1; + } + + /* load aic firmware */ + size = rwnx_load_firmware(&dst, filename, dev); + if (size <= 0) { + pr_err("aicbsp wrong size of m2d file\n"); + vfree(dst); + dst = NULL; + return -1; + } + + /* Copy the file on the Embedded side */ + pr_info("aicbsp ### Upload m2d %s flash, size=%d\n", filename, size); + + /*send info first */ + err = rwnx_send_dbg_mem_block_write_req(sdiodev, AIC_M2D_OTA_INFO_ADDR, 4, + (u32 *)&size); + + /*send data first */ + if (size > 1024) { // > 1KB data + for (i = 0; i < (size - 1024); i += 1024) { // each time write 1KB + err = rwnx_send_dbg_mem_block_write_req(sdiodev, + AIC_M2D_OTA_DATA_ADDR, + 1024, dst + i / 4); + if (err) { + pr_err("aicbsp m2d upload fail: %x, err:%d\r\n", + AIC_M2D_OTA_DATA_ADDR, err); + break; + } + } + } + + if (!err && i < size) { // <1KB data + err = rwnx_send_dbg_mem_block_write_req(sdiodev, AIC_M2D_OTA_DATA_ADDR, + size - i, dst + i / 4); + if (err) { + pr_err("aicbsp m2d upload fail: %x, err:%d\r\n", AIC_M2D_OTA_DATA_ADDR, + err); + } + } + + if (dst) { + vfree(dst); + dst = NULL; + } + testmode = FW_NORMAL_MODE; + aicbsp_info.cpmode = testmode; + + pr_info("aicbsp m2d flash update complete\n\n"); + + return err; +} + +int rwnx_plat_m2d_flash_ota_check(struct aic_sdio_dev *sdiodev, char *filename) +{ + struct device *dev = sdiodev->dev; + unsigned int i = 0, j = 0; + int size; + u32 *dst = NULL; + int err = 0; + int ret = 0; + u8 bond_id; + const u32 mem_addr = 0x40500000; + const u32 mem_addr_code_start = AIC_M2D_OTA_CODE_START_ADDR; + const u32 mem_addr_sdk_ver = AIC_M2D_OTA_VER_ADDR; + const u32 driver_code_start_idx = + (AIC_M2D_OTA_CODE_START_ADDR - AIC_M2D_OTA_FLASH_ADDR) / 4; + const u32 driver_sdk_ver_idx = + (AIC_M2D_OTA_VER_ADDR - AIC_M2D_OTA_FLASH_ADDR) / 4; + u32 driver_sdk_ver_addr_idx = 0; + u32 code_start_addr = 0xffffffff; + u32 sdk_ver_addr = 0xffffffff; + u32 drv_code_start_addr = 0xffffffff; + u32 drv_sdk_ver_addr = 0xffffffff; + struct dbg_mem_read_cfm rd_mem_addr_cfm; + char m2d_sdk_ver[64]; + char flash_sdk_ver[64]; + u32 flash_ver[16]; + u32 ota_ver[16]; + + ret = rwnx_send_dbg_mem_read_req(sdiodev, mem_addr, &rd_mem_addr_cfm); + if (ret) { + pr_err("aicbsp m2d %x rd fail: %d\n", mem_addr, ret); + return ret; + } + bond_id = (u8)(rd_mem_addr_cfm.memdata >> 24); + pr_info("aicbsp %x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata); + if (bond_id & (1 << 1)) { + // flash is invalid + pr_err("aicbsp m2d flash is invalid\n"); + return -1; + } + ret = rwnx_send_dbg_mem_read_req(sdiodev, mem_addr_code_start, + &rd_mem_addr_cfm); + if (ret) { + pr_err("mem_addr_code_start %x rd fail: %d\n", mem_addr_code_start, + ret); + return ret; + } + code_start_addr = rd_mem_addr_cfm.memdata; + +#if !defined(CONFIG_M2D_OTA_LZMA_SUPPORT) + ret = + rwnx_send_dbg_mem_read_req(sdiodev, mem_addr_sdk_ver, &rd_mem_addr_cfm); + if (ret) { + pr_err("aicbsp mem_addr_sdk_ver %x rd fail: %d\n", mem_addr_code_start, ret); + return ret; + } + sdk_ver_addr = rd_mem_addr_cfm.memdata; +#else + sdk_ver_addr = mem_addr_sdk_ver; +#endif + pr_info("aicbsp code_start_addr: 0x%x, sdk_ver_addr: 0x%x\n", code_start_addr, + sdk_ver_addr); + + /* load aic firmware */ + size = rwnx_load_firmware(&dst, filename, dev); + if (size <= 0) { + pr_err("aicbsp wrong size of m2d file\n"); + vfree(dst); + dst = NULL; + return -1; + } + if (code_start_addr == 0xffffffff && sdk_ver_addr == 0xffffffff) { + pr_warn("aicbsp ########m2d flash old version , must be upgrade\n"); + drv_code_start_addr = dst[driver_code_start_idx]; + drv_sdk_ver_addr = dst[driver_sdk_ver_idx]; + + pr_info("aicbsp drv_code_start_addr: 0x%x, drv_sdk_ver_addr: 0x%x\n", + drv_code_start_addr, drv_sdk_ver_addr); + + if (drv_sdk_ver_addr == 0xffffffff) { + pr_err("aicbsp ########driver m2d_ota.bin is old ,not need upgrade\n"); + return -1; + } + + } else { + for (i = 0; i < 16; i++) { + ret = rwnx_send_dbg_mem_read_req(sdiodev, (sdk_ver_addr + i * 4), + &rd_mem_addr_cfm); + if (ret) { + pr_err("aicbsp mem_addr_sdk_ver %x rd fail: %d\n", + mem_addr_code_start, ret); + return ret; + } + flash_ver[i] = rd_mem_addr_cfm.memdata; + } + memcpy((u8 *)flash_sdk_ver, (u8 *)flash_ver, 64); + memcpy((u8 *)saved_sdk_ver, (u8 *)flash_sdk_ver, 64); + pr_info("aicbsp flash SDK Version: %s\r\n\r\n", flash_sdk_ver); + + drv_code_start_addr = dst[driver_code_start_idx]; + drv_sdk_ver_addr = dst[driver_sdk_ver_idx]; + + pr_info("aicbsp drv_code_start_addr: 0x%x, drv_sdk_ver_addr: 0x%x\n", + drv_code_start_addr, drv_sdk_ver_addr); + + if (drv_sdk_ver_addr == 0xffffffff) { + pr_err("aicbsp ########driver m2d_ota.bin is old ,not need upgrade\n"); + return -1; + } +#if !defined(CONFIG_M2D_OTA_LZMA_SUPPORT) + driver_sdk_ver_addr_idx = (drv_sdk_ver_addr - drv_code_start_addr) / 4; +#else + driver_sdk_ver_addr_idx = driver_sdk_ver_idx; +#endif + pr_info("aicbsp driver_sdk_ver_addr_idx %d\n", driver_sdk_ver_addr_idx); + + if (driver_sdk_ver_addr_idx) { + for (j = 0; j < 16; j++) + ota_ver[j] = dst[driver_sdk_ver_addr_idx + j]; + + memcpy((u8 *)m2d_sdk_ver, (u8 *)ota_ver, 64); + pr_info("aicbsp m2d_ota SDK Version: %s\r\n\r\n", m2d_sdk_ver); + } else { + return -1; + } + + if (!strcmp(m2d_sdk_ver, flash_sdk_ver)) { + pr_err("aicbsp ######## m2d %s flash is not need upgrade\r\n", filename); + return -1; + } + } + + /* Copy the file on the Embedded side */ + pr_info("aicbsp ### Upload m2d %s flash, size=%d\n", filename, size); + + /*send info first */ + err = rwnx_send_dbg_mem_block_write_req(sdiodev, AIC_M2D_OTA_INFO_ADDR, 4, + (u32 *)&size); + + /*send data first */ + if (size > 1024) { // > 1KB data + for (i = 0; i < (size - 1024); i += 1024) { // each time write 1KB + err = rwnx_send_dbg_mem_block_write_req(sdiodev, AIC_M2D_OTA_DATA_ADDR, + 1024, dst + i / 4); + if (err) { + pr_err("aicbsp m2d upload fail: %x, err:%d\r\n", + AIC_M2D_OTA_DATA_ADDR, err); + break; + } + } + } + + if (!err && i < size) { + err = rwnx_send_dbg_mem_block_write_req(sdiodev, AIC_M2D_OTA_DATA_ADDR, + size - i, dst + i / 4); + if (err) { + pr_err("aicbsp m2d upload fail: %x, err:%d\r\n", AIC_M2D_OTA_DATA_ADDR, + err); + } + } + + if (dst) { + vfree(dst); + dst = NULL; + } + testmode = FW_NORMAL_MODE; + + pr_info("aicbsp m2d flash update complete\n\n"); + + return err; +} +#endif // CONFIG_M2D_OTA_AUTO_SUPPORT + +int aicwf_patch_table_load(struct aic_sdio_dev *rwnx_hw, char *filename) +{ + struct device *dev = rwnx_hw->dev; + int err = 0; + unsigned int i = 0, size; + u32 *dst = NULL; + u8 *describle; + u32 fmacfw_patch_tbl_8800dc_u02_describe_size = 124; + u32 fmacfw_patch_tbl_8800dc_u02_describe_base; // read from patch_tbl + + /* Copy the file on the Embedded side */ + pr_info("aicbsp ### Upload %s\n", filename); + + size = rwnx_load_firmware(&dst, filename, dev); + if (!dst) { + pr_err("aicbsp No such file or directory\n"); + return -1; + } + if (size <= 0) { + pr_err("aicbsp wrong size of firmware file\n"); + dst = NULL; + err = -1; + } + + pr_info("aicbsp tbl size = %d\n", size); + + fmacfw_patch_tbl_8800dc_u02_describe_base = dst[0]; + AICWFDBG(LOGINFO, "FMACFW_PATCH_TBL_8800DC_U02_DESCRIBE_BASE = %x\n", + fmacfw_patch_tbl_8800dc_u02_describe_base); + + if (!err && i < size) { + err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, + fmacfw_patch_tbl_8800dc_u02_describe_base, + fmacfw_patch_tbl_8800dc_u02_describe_size + 4, dst); + if (err) + pr_err("aicbsp write describe information fail\n"); + + describle = + kzalloc(fmacfw_patch_tbl_8800dc_u02_describe_size, GFP_KERNEL); + memcpy(describle, &dst[1], fmacfw_patch_tbl_8800dc_u02_describe_size); + pr_info("aicbsp %s", describle); + kfree(describle); + describle = NULL; + } + + if (!err && i < size) { + for (i = (128 / 4); i < (size / 4); i += 2) { + pr_info("aicbsp patch_tbl: %x %x\n", dst[i], dst[i + 1]); + err = rwnx_send_dbg_mem_write_req(rwnx_hw, dst[i], dst[i + 1]); + } + if (err) + pr_err("aicbsp bin upload fail: %x, err:%d\r\n", dst[i], err); + } + + if (dst) { + vfree(dst); + dst = NULL; + } + + return err; +} + +int aicwf_plat_patch_load_8800dc(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; +#if !defined(CONFIG_FPGA_VERIFICATION) + if (chip_sub_id == 0) { + pr_info("aicbsp u01 is loaing ###############\n"); + ret = rwnx_plat_bin_fw_upload(sdiodev, ROM_FMAC_PATCH_ADDR, + RWNX_MAC_PATCH_NAME2_8800DC); + } else if (chip_sub_id == 1) { + pr_info("aicbsp u02 is loaing ###############\n"); + ret = rwnx_plat_bin_fw_upload(sdiodev, ROM_FMAC_PATCH_ADDR, + RWNX_MAC_PATCH_NAME2_8800DC_U02); + } else if (chip_sub_id == 2) { + pr_info("aicbsp h_u02 is loaing ###############\n"); + ret = rwnx_plat_bin_fw_upload(sdiodev, ROM_FMAC_PATCH_ADDR, + RWNX_MAC_PATCH_NAME2_8800DC_H_U02); + } else { + pr_info("aicbsp unsupported id: %d\n", chip_sub_id); + } +#endif + return ret; +} + +int aicwf_plat_rftest_load_8800dc(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; + + ret = rwnx_plat_bin_fw_upload(sdiodev, RAM_LMAC_FW_ADDR, + RWNX_MAC_FW_RF_BASE_NAME_8800DC); + if (ret) { + AICWFDBG(LOGINFO, "load rftest bin fail: %d\n", ret); + return ret; + } + return ret; +} + +#ifdef CONFIG_DPD +int aicwf_misc_ram_valid_check_8800dc(struct aic_sdio_dev *sdiodev, + int *valid_out) +{ + int ret = 0; + u32 cfg_base = 0x10164; + struct dbg_mem_read_cfm cfm; + u32 misc_ram_addr; + u32 ram_base_addr, ram_word_cnt; + u32 bit_mask[4]; + int i; + + if (valid_out) + *valid_out = 0; + + if (testmode == FW_RFTEST_MODE) { + u32 vect1 = 0; + u32 vect2 = 0; + + cfg_base = RAM_LMAC_FW_ADDR + 0x0004; + ret = rwnx_send_dbg_mem_read_req(sdiodev, cfg_base, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "cfg_base:%x vcet1 rd fail: %d\n", cfg_base, + ret); + return ret; + } + vect1 = cfm.memdata; + if ((vect1 & 0xFFFF0000) != (RAM_LMAC_FW_ADDR & 0xFFFF0000)) { + AICWFDBG(LOGERROR, "vect1 invalid: %x\n", vect1); + return ret; + } + cfg_base = RAM_LMAC_FW_ADDR + 0x0008; + ret = rwnx_send_dbg_mem_read_req(sdiodev, cfg_base, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "cfg_base:%x vcet2 rd fail: %d\n", cfg_base, + ret); + return ret; + } + vect2 = cfm.memdata; + if ((vect2 & 0xFFFF0000) != (RAM_LMAC_FW_ADDR & 0xFFFF0000)) { + AICWFDBG(LOGERROR, "vect2 invalid: %x\n", vect2); + return ret; + } + + cfg_base = RAM_LMAC_FW_ADDR + 0x0164; + } + // init misc ram + ret = rwnx_send_dbg_mem_read_req(sdiodev, cfg_base + 0x14, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "rf misc ram[0x%x] rd fail: %d\n", cfg_base + 0x14, + ret); + return ret; + } + misc_ram_addr = cfm.memdata; + AICWFDBG(LOGERROR, "misc_ram_addr=%x\n", misc_ram_addr); + // bit_mask + ram_base_addr = misc_ram_addr + offsetof(struct rf_misc_ram_t, bit_mask); + ram_word_cnt = (MEMBER_SIZE(struct rf_misc_ram_t, bit_mask) + + MEMBER_SIZE(struct rf_misc_ram_t, reserved)) / + 4; + for (i = 0; i < ram_word_cnt; i++) { + ret = rwnx_send_dbg_mem_read_req(sdiodev, ram_base_addr + i * 4, &cfm); + if (ret) { + AICWFDBG(LOGERROR, "bit_mask[0x%x] rd fail: %d\n", + ram_base_addr + i * 4, ret); + return ret; + } + bit_mask[i] = cfm.memdata; + } + AICWFDBG(LOGTRACE, "bit_mask:%x,%x,%x,%x\n", bit_mask[0], bit_mask[1], + bit_mask[2], bit_mask[3]); + if (bit_mask[0] == 0 && ((bit_mask[1] & 0xFFF00000) == 0x80000000) && + bit_mask[2] == 0 && ((bit_mask[3] & 0xFFFFFF00) == 0x00000000)) { + if (valid_out) + *valid_out = 1; + } + return ret; +} + +int aicwf_plat_calib_load_8800dc(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; + + if (chip_sub_id == 1) { + ret = rwnx_plat_bin_fw_upload(sdiodev, ROM_FMAC_CALIB_ADDR, + RWNX_MAC_CALIB_NAME_8800DC_U02); + if (ret) { + AICWFDBG(LOGINFO, "load rftest bin fail: %d\n", ret); + return ret; + } + } else if (chip_sub_id == 2) { + ret = rwnx_plat_bin_fw_upload(sdiodev, ROM_FMAC_CALIB_ADDR, + RWNX_MAC_CALIB_NAME_8800DC_H_U02); + if (ret) { + AICWFDBG(LOGINFO, "load rftest bin fail: %d\n", ret); + return ret; + } + } + return ret; +} + +#endif + +#ifdef CONFIG_DPD +/** + * var dpd_res - DPD calibration result shared with the WLAN driver + */ +struct rf_misc_ram_lite_t dpd_res = { + {0}, +}; +EXPORT_SYMBOL_GPL(dpd_res); +#endif + +int rwnx_plat_bin_fw_upload(struct aic_sdio_dev *sdiodev, u32 fw_addr, + const char *filename) +{ + struct device *dev = sdiodev->dev; + unsigned int i = 0; + int size; + u32 *dst = NULL; + int err = 0; + + pr_info("aicbsp %s\n", __func__); + + /* load aic firmware */ + size = rwnx_load_firmware(&dst, filename, dev); + if (size <= 0) { + pr_err("aicbsp wrong size of firmware file\n"); + vfree(dst); + dst = NULL; + return -1; + } + + /* Copy the file on the Embedded side */ + if (size > 1024) { // > 1KB data + for (i = 0; i < (size - 1024); i += 1024) { // each time write 1KB + err = rwnx_send_dbg_mem_block_write_req(sdiodev, fw_addr + i, 1024, + dst + i / 4); + if (err) { + pr_err("aicbsp bin upload fail: %x, err:%d\r\n", fw_addr + i, err); + break; + } + } + } + + if (!err && i < size) { // <1KB data + err = rwnx_send_dbg_mem_block_write_req(sdiodev, fw_addr + i, size - i, + dst + i / 4); + if (err) + pr_err("aicbsp bin upload fail: %x, err:%d\r\n", fw_addr + i, err); + } + + if (dst) { + vfree(dst); + dst = NULL; + } + + return err; +} + +int aicbt_patch_table_free(struct aicbt_patch_table **head) +{ + struct aicbt_patch_table *p = *head, *n = NULL; + + while (p) { + n = p->next; + vfree(p->name); + vfree(p->data); + vfree(p); + p = n; + } + *head = NULL; + return 0; +} + +struct aicbt_patch_table *aicbt_patch_table_alloc(const char *filename) +{ + u8 *rawdata = NULL, *p; + int size; + struct aicbt_patch_table *head = NULL, *new = NULL, *cur = NULL; + + /* load aic firmware */ + size = rwnx_load_firmware((u32 **)&rawdata, filename, NULL); + if (size <= 0) { + pr_err("aicbsp wrong size of firmware file\n"); + goto err; + } + + p = rawdata; + if (memcmp(p, AICBT_PT_TAG, + sizeof(AICBT_PT_TAG) < 16 ? sizeof(AICBT_PT_TAG) : 16)) { + pr_err("aicbsp TAG err\n"); + goto err; + } + p += 16; + + while (p - rawdata < size) { + new = vmalloc(sizeof(*new)); + memset(new, 0, sizeof(struct aicbt_patch_table)); + if (!head) { + head = new; + cur = new; + } else { + cur->next = new; + cur = cur->next; + } + + cur->name = vmalloc(sizeof(char) * 16); + memset(cur->name, 0, sizeof(char) * 16); + memcpy(cur->name, p, 16); + p += 16; + + cur->type = *(uint32_t *)p; + p += 4; + + cur->len = *(uint32_t *)p; + p += 4; + + if (cur->type >= 1000) { // Temp Workaround + cur->len = 0; + } else { + if (cur->len > 0) { + cur->data = vmalloc(sizeof(uint8_t) * cur->len * 8); + memset(cur->data, 0, sizeof(uint8_t) * cur->len * 8); + memcpy(cur->data, p, cur->len * 8); + p += cur->len * 8; + } + } + } + vfree(rawdata); + return head; + +err: + aicbt_patch_table_free(&head); + if (rawdata) + vfree(rawdata); + return NULL; +} + +int aicbt_patch_info_unpack(struct aicbt_patch_info_t *patch_info, + struct aicbt_patch_table *head_t) +{ + u8 *patch_info_array = (u8 *)patch_info; + + if (head_t->type == AICBT_PT_INF) { + patch_info->info_len = head_t->len; + if (patch_info->info_len == 0) + return 0; + + memcpy(patch_info_array + sizeof(patch_info->info_len), + head_t->data, + patch_info->info_len * sizeof(uint32_t) * 2); + AICWFDBG(LOGDEBUG, "%s adid_addrinf:%x addr_adid:%x \r\n", __func__, + ((struct aicbt_patch_info_t *)patch_info_array)->adid_addrinf, + ((struct aicbt_patch_info_t *)patch_info_array)->addr_adid); + } + return 0; +} + +int aicbt_patch_trap_data_load(struct aic_sdio_dev *sdiodev, + struct aicbt_patch_table *head) +{ + struct aicbt_patch_info_t patch_info = { + .info_len = 0, + .adid_addrinf = 0, + .addr_adid = 0, + .patch_addrinf = 0, + .addr_patch = 0, + .reset_addr = 0, + .reset_val = 0, + .adid_flag_addr = 0, + .adid_flag = 0, + }; + + int ret; + + if (!head) + return -1; + + ret = aic_chip_set_patch_info(sdiodev, &patch_info, &aicbsp_info, head); + if (ret) { + pr_err("aicbsp chip set patch info fail\n"); + return ret; + } + + if (rwnx_plat_bin_fw_upload(sdiodev, patch_info.addr_adid, + aicbsp_firmware_list[aicbsp_info.cpmode].bt_adid)) + return -1; + if (rwnx_plat_bin_fw_upload(sdiodev, patch_info.addr_patch, + aicbsp_firmware_list[aicbsp_info.cpmode].bt_patch)) + return -1; + return 0; +} + +static struct aicbt_info_t aicbt_info[] = { + { + .btmode = AICBT_BTMODE_DEFAULT, + .btport = AICBT_BTPORT_DEFAULT, + .uart_baud = AICBT_UART_BAUD_DEFAULT, + .uart_flowctrl = AICBT_UART_FC_DEFAULT, + .lpm_enable = AICBT_LPM_ENABLE_DEFAULT, + .txpwr_lvl = AICBT_TXPWR_LVL_DEFAULT, + }, // PRODUCT_ID_AIC8801 + { + .btmode = AICBT_BTMODE_BT_WIFI_COMBO, + .btport = AICBT_BTPORT_DEFAULT, + .uart_baud = AICBT_UART_BAUD_DEFAULT, + .uart_flowctrl = AICBT_UART_FC_DEFAULT, + .lpm_enable = AICBT_LPM_ENABLE_DEFAULT, + .txpwr_lvl = AICBT_TXPWR_LVL_DEFAULT_8800dc, + }, // PRODUCT_ID_AIC8800DC + { + .btmode = AICBT_BTMODE_BT_WIFI_COMBO, + .btport = AICBT_BTPORT_DEFAULT, + .uart_baud = AICBT_UART_BAUD_DEFAULT, + .uart_flowctrl = AICBT_UART_FC_DEFAULT, + .lpm_enable = AICBT_LPM_ENABLE_DEFAULT, + .txpwr_lvl = AICBT_TXPWR_LVL_DEFAULT_8800dc, + }, // PRODUCT_ID_AIC8800DW + { + .btmode = AICBT_BTMODE_DEFAULT_8800d80, + .btport = AICBT_BTPORT_DEFAULT, + .uart_baud = AICBT_UART_BAUD_DEFAULT, + .uart_flowctrl = AICBT_UART_FC_DEFAULT, + .lpm_enable = AICBT_LPM_ENABLE_DEFAULT, + .txpwr_lvl = AICBT_TXPWR_LVL_DEFAULT_8800d80, + } // PRODUCT_ID_AIC8800D80 +}; + +int aicbt_patch_table_load(struct aic_sdio_dev *sdiodev, + struct aicbt_patch_table *head) +{ + struct aicbt_patch_table *p; + int ret = 0, i; + u32 *data = NULL; + + if (!head) + return -1; + + for (p = head; p; p = p->next) { + data = p->data; + if (p->type == AICBT_PT_BTMODE) { + *(data + 1) = aicbsp_info.hwinfo < 0; + *(data + 3) = aicbsp_info.hwinfo; + *(data + 5) = (sdiodev->chipid == PRODUCT_ID_AIC8800DC + ? aicbsp_info.cpmode + : 0); // 0;//aicbsp_info.cpmode; + + *(data + 7) = aicbt_info[sdiodev->chipid].btmode; + *(data + 9) = aicbt_info[sdiodev->chipid].btport; + *(data + 11) = aicbt_info[sdiodev->chipid].uart_baud; + *(data + 13) = aicbt_info[sdiodev->chipid].uart_flowctrl; + *(data + 15) = aicbt_info[sdiodev->chipid].lpm_enable; + *(data + 17) = aicbt_info[sdiodev->chipid].txpwr_lvl; + + pr_info("aicbsp %s bt btmode[%d]:%d \r\n", __func__, sdiodev->chipid, + aicbt_info[sdiodev->chipid].btmode); + pr_info("aicbsp %s bt uart_baud[%d]:%d \r\n", __func__, sdiodev->chipid, + aicbt_info[sdiodev->chipid].uart_baud); + pr_info("aicbsp %s bt uart_flowctrl[%d]:%d \r\n", __func__, sdiodev->chipid, + aicbt_info[sdiodev->chipid].uart_flowctrl); + pr_info("aicbsp %s bt lpm_enable[%d]:%d \r\n", __func__, sdiodev->chipid, + aicbt_info[sdiodev->chipid].lpm_enable); + pr_info("aicbsp %s bt tx_pwr[%d]:%d \r\n", __func__, sdiodev->chipid, + aicbt_info[sdiodev->chipid].txpwr_lvl); + } + + if (p->type == AICBT_PT_VER) { + pr_info("aicbsp bt patch version: %s\n", (char *)p->data); + continue; + } + + for (i = 0; i < p->len; i++) { + ret = rwnx_send_dbg_mem_write_req(sdiodev, *data, *(data + 1)); + if (ret != 0) + return ret; + data += 2; + } + if (p->type == AICBT_PT_PWRON) + usleep_range(50, 100); + } + + return 0; +} + +int aicbt_init(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; + struct aicbt_patch_table *head = + aicbt_patch_table_alloc(aicbsp_firmware_list[aicbsp_info.cpmode].bt_table); + if (!head) { + pr_err("aicbsp aicbt_patch_table_alloc fail\n"); + return -1; + } + + if (aicbt_patch_trap_data_load(sdiodev, head)) { + pr_err("aicbsp aicbt_patch_trap_data_load fail\n"); + ret = -1; + goto err; + } + + if (aicbt_patch_table_load(sdiodev, head)) { + pr_err("aicbsp aicbt_patch_table_load fail\n"); + ret = -1; + goto err; + } + +err: + aicbt_patch_table_free(&head); + return ret; +} + +int aicwifi_start_from_bootrom(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; + + /* memory access */ + const u32 fw_addr = RAM_FMAC_FW_ADDR; + struct dbg_start_app_cfm start_app_cfm; + + /* fw start */ + ret = rwnx_send_dbg_start_app_req(sdiodev, fw_addr, HOST_START_APP_AUTO, + &start_app_cfm); + if (ret) + return -1; + aicbsp_info.hwinfo_r = start_app_cfm.bootstatus & 0xFF; + + return 0; +} + +static u32 adaptivity_patch_tbl[][2] = { + {0x0004, 0x0000320A}, // linkloss_thd + {0x0094, 0x00000000}, // ac_param_conf + {0x00F8, 0x00010138}, // tx_adaptivity_en +}; + +static u32 patch_tbl[][2] = { +#if !defined(CONFIG_LINK_DET_5G) + {0x0104, 0x00000000}, // link_det_5g +#endif +}; + +static u32 syscfg_tbl_masked[][3] = { + {0x40506024, 0x000000FF, 0x000000DF}, // for clk gate lp_level +}; + +static u32 rf_tbl_masked[][3] = { + {0x40344058, 0x00800000, 0x00000000}, // pll trx +}; + +int aicwifi_sys_config(struct aic_sdio_dev *sdiodev) +{ + int ret, cnt; + int syscfg_num = sizeof(syscfg_tbl_masked) / sizeof(u32) / 3; + + for (cnt = 0; cnt < syscfg_num; cnt++) { + ret = rwnx_send_dbg_mem_mask_write_req(sdiodev, syscfg_tbl_masked[cnt][0], + syscfg_tbl_masked[cnt][1], + syscfg_tbl_masked[cnt][2]); + if (ret) { + pr_err("aicbsp %x mask write fail: %d\n", syscfg_tbl_masked[cnt][0], ret); + return ret; + } + } + + ret = rwnx_send_dbg_mem_mask_write_req(sdiodev, rf_tbl_masked[0][0], rf_tbl_masked[0][1], + rf_tbl_masked[0][2]); + if (ret) { + pr_err("aicbsp rf config %x write fail: %d\n", rf_tbl_masked[0][0], ret); + return ret; + } + + return 0; +} + +int aicwifi_patch_config(struct aic_sdio_dev *sdiodev) +{ + const u32 rd_patch_addr = RAM_FMAC_FW_ADDR + 0x0180; + u32 config_base; + u32 start_addr = 0x1e6000; + u32 patch_addr = start_addr; + u32 patch_num = sizeof(patch_tbl) / 4; + struct dbg_mem_read_cfm rd_patch_addr_cfm; + u32 patch_addr_reg = 0x1e5318; + u32 patch_num_reg = 0x1e531c; + int ret = 0; + u16 cnt = 0; + int tmp_cnt = 0; + int adap_patch_num = 0; + + if (aicbsp_info.cpmode == AICBSP_CPMODE_TEST) { + patch_addr_reg = 0x1e5304; + patch_num_reg = 0x1e5308; + } + + ret = + rwnx_send_dbg_mem_read_req(sdiodev, rd_patch_addr, &rd_patch_addr_cfm); + if (ret) { + pr_err("aicbsp patch rd fail\n"); + return ret; + } + + config_base = rd_patch_addr_cfm.memdata; + + ret = rwnx_send_dbg_mem_write_req(sdiodev, patch_addr_reg, patch_addr); + if (ret) { + pr_err("aicbsp 0x%x write fail\n", patch_addr_reg); + return ret; + } + + if (aicbsp_info.adap_test) { + pr_err("aicbsp %s for adaptivity test \r\n", __func__); + adap_patch_num = sizeof(adaptivity_patch_tbl) / 4; + ret = rwnx_send_dbg_mem_write_req(sdiodev, patch_num_reg, + patch_num + adap_patch_num); + } else { + ret = rwnx_send_dbg_mem_write_req(sdiodev, patch_num_reg, patch_num); + } + if (ret) { + pr_err("aicbsp 0x%x write fail\n", patch_num_reg); + return ret; + } + + for (cnt = 0; cnt < patch_num / 2; cnt += 1) { + ret = rwnx_send_dbg_mem_write_req(sdiodev, start_addr + 8 * cnt, + patch_tbl[cnt][0] + config_base); + if (ret) { + pr_err("aicbsp %x write fail\n", start_addr + 8 * cnt); + return ret; + } + + ret = rwnx_send_dbg_mem_write_req(sdiodev, start_addr + 8 * cnt + 4, + patch_tbl[cnt][1]); + if (ret) { + pr_err("aicbsp %x write fail\n", start_addr + 8 * cnt + 4); + return ret; + } + } + + tmp_cnt = cnt; + + if (aicbsp_info.adap_test) { //(adap_test){ + for (cnt = 0; cnt < adap_patch_num / 2; cnt += 1) { + ret = rwnx_send_dbg_mem_write_req(sdiodev, + start_addr + 8 * (cnt + tmp_cnt), + adaptivity_patch_tbl[cnt][0] + + config_base); + if (ret) + pr_err("aicbsp %x write fail\n", start_addr + 8 * cnt); + + ret = rwnx_send_dbg_mem_write_req(sdiodev, start_addr + + 8 * (cnt + tmp_cnt) + 4, + adaptivity_patch_tbl[cnt][1]); + if (ret) + pr_err("aicbsp %x write fail\n", start_addr + 8 * cnt + 4); + } + } + + return 0; +} + +int aicwifi_init(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; + + ret = aic_chip_wifi_init(sdiodev, testmode); + if (ret) { + pr_err("aicbsp wifi init fail\n"); + return ret; + } + +#ifdef CONFIG_GPIO_WAKEUP + if (aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, 4)) { + sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.wakeup_reg); + return -1; + } +#endif + return 0; +} + +static u32 aicbsp_syscfg_tbl[][2] = { + {0x40500014, 0x00000101}, // 1) + {0x40500018, 0x00000109}, // 2) + {0x40500004, 0x00000010}, // 3) the order should not be changed + + // def CONFIG_PMIC_SETTING + // U02 bootrom only + {0x40040000, 0x00001AC8}, // 1) fix panic + {0x40040084, 0x00011580}, + {0x40040080, 0x00000001}, + {0x40100058, 0x00000000}, + + {0x50000000, 0x03220204}, // 2) pmic interface init + {0x50019150, 0x00000002}, // 3) for 26m xtal, set div1 + {0x50017008, 0x00000000}, // 4) stop wdg +}; + +int aicbsp_system_config(struct aic_sdio_dev *sdiodev) +{ + int syscfg_num = sizeof(aicbsp_syscfg_tbl) / sizeof(u32) / 2; + int ret, cnt; + + for (cnt = 0; cnt < syscfg_num; cnt++) { + ret = rwnx_send_dbg_mem_write_req(sdiodev, aicbsp_syscfg_tbl[cnt][0], + aicbsp_syscfg_tbl[cnt][1]); + if (ret) { + sdio_err("%x write fail: %d\n", aicbsp_syscfg_tbl[cnt][0], ret); + return ret; + } + } + return 0; +} + +int aicbsp_platform_init(struct aic_sdio_dev *sdiodev) +{ + rwnx_cmd_mgr_init(&sdiodev->cmd_mgr); + sdiodev->cmd_mgr.sdiodev = (void *)sdiodev; + + return 0; +} + +void aicbsp_platform_deinit(struct aic_sdio_dev *sdiodev) +{ + (void)sdiodev; +} + +int aicbsp_driver_fw_init(struct aic_sdio_dev *sdiodev) +{ + u32 btenable = 0; + int ret = 0; + + aicbsp_info.cpmode = AICBSP_CPMODE_WORK; + testmode = AICBSP_CPMODE_WORK; + cur_mode = AICBSP_CPMODE_WORK; + + ret = aic_chip_driver_fw_init(sdiodev, &btenable, &aicbsp_info, &aicbsp_firmware_list); + if (ret) { + pr_err("aicbsp chip driver fw init fail\n"); + return -1; + } + + AICWFDBG(LOGINFO, "aicbsp: %s, chip rev: %d\n", __func__, + aicbsp_info.chip_rev); + + ret = aicwifi_init(sdiodev); + if (ret) + return ret; + + return 0; +} + +/** + * aicbsp_get_feature - Read BSP settings required by the WLAN driver + * @feature: Location in which to return the current BSP settings + * + * Return: 0 on success, or a negative error code if the SDIO device is absent. + */ +int aicbsp_get_feature(struct aicbsp_feature_t *feature) +{ + if (!aicbsp_sdiodev) { + sdio_err("%s, aicbsp_sdiodev is null\n", __func__); + return -1; + } + + feature->sdio_clock = aicbsp_sdiodev->chip_ops->sdio_clock; + + feature->sdio_phase = FEATURE_SDIO_PHASE; + feature->cpmode = aicbsp_info.cpmode; + feature->adapt = aicbsp_info.adap_test; + feature->hwinfo = aicbsp_info.hwinfo; + feature->fwlog_en = aicbsp_info.fwlog_en; + feature->irqf = aicbsp_info.irqf; + sdio_dbg("%s, set FEATURE_SDIO_CLOCK %d MHz\n", __func__, + feature->sdio_clock / 1000000); + return 0; +} +EXPORT_SYMBOL_GPL(aicbsp_get_feature); + +#ifdef CONFIG_RESV_MEM_SUPPORT +static struct skb_buff_pool resv_skb[] = { + {AIC_RESV_MEM_TXDATA, 1536 * 64, "resv_mem_txdata", 0, NULL}, +}; + +int aicbsp_resv_mem_init(void) +{ + int i = 0; + + pr_info("aicbsp %s\n", __func__); + for (i = 0; i < ARRAY_SIZE(resv_skb); i++) + resv_skb[i].skb = dev_alloc_skb(resv_skb[i].size); + return 0; +} + +int aicbsp_resv_mem_deinit(void) +{ + int i = 0; + + pr_info("aicbsp %s\n", __func__); + for (i = 0; i < ARRAY_SIZE(resv_skb); i++) { + if (resv_skb[i].used == 0 && resv_skb[i].skb) + dev_kfree_skb(resv_skb[i].skb); + } + return 0; +} + +/** + * aicbsp_resv_mem_alloc_skb - Acquire a reserved socket buffer + * @length: Minimum required buffer size + * @id: Reserved-buffer pool identifier + * + * Return: The reserved buffer, or %NULL if it is too small, already in use, + * or cannot be allocated. + */ +struct sk_buff *aicbsp_resv_mem_alloc_skb(unsigned int length, uint32_t id) +{ + if (resv_skb[id].size < length) { + pr_err("aicbsp: %s, no enough mem\n", __func__); + goto fail; + } + + if (resv_skb[id].used) { + pr_err("aicbsp: %s, mem in use\n", __func__); + goto fail; + } + + if (!resv_skb[id].skb) { + //pr_err("aicbsp: %s, skb not initialazed\n", __func__); + resv_skb[id].skb = + dev_alloc_skb(resv_skb[id].size); + if (!resv_skb[id].skb) { + pr_err("aicbsp: %s, mem reinitial still fail\n", __func__); + goto fail; + } + } + + pr_info("aicbsp: %s, alloc %s succuss, id: %d, size: %d\n", __func__, + resv_skb[id].name, resv_skb[id].id, resv_skb[id].size); + + resv_skb[id].used = 1; + return resv_skb[id].skb; + +fail: + return NULL; +} +EXPORT_SYMBOL_GPL(aicbsp_resv_mem_alloc_skb); + +/** + * aicbsp_resv_mem_kfree_skb - Release a reserved socket buffer for reuse + * @skb: Reserved buffer returned by aicbsp_resv_mem_alloc_skb() + * @id: Reserved-buffer pool identifier + */ +void aicbsp_resv_mem_kfree_skb(struct sk_buff *skb, uint32_t id) +{ + resv_skb[id].used = 0; + pr_info("aicbsp: %s, free %s succuss, id: %d, size: %d\n", __func__, + resv_skb[id].name, resv_skb[id].id, resv_skb[id].size); +} +EXPORT_SYMBOL_GPL(aicbsp_resv_mem_kfree_skb); + +#else + +int aicbsp_resv_mem_init(void) +{ + return 0; +} + +int aicbsp_resv_mem_deinit(void) +{ + return 0; +} + +#endif diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_driver.h b/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_driver.h new file mode 100644 index 0000000000000..16da7a2557220 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_driver.h @@ -0,0 +1,598 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _AIC_BSP_DRIVER_H +#define _AIC_BSP_DRIVER_H + +#include "aic_bsp_export.h" +#include +#include +#include +#include + +#define AIC8800_FW_DIR "aic8800/" + +#define RWNX_80211_CMD_TIMEOUT_MS 6000 + +#define RWNX_CMD_FLAG_NONBLOCK BIT(0) +#define RWNX_CMD_FLAG_REQ_CFM BIT(1) +#define RWNX_CMD_FLAG_WAIT_PUSH BIT(2) +#define RWNX_CMD_FLAG_WAIT_ACK BIT(3) +#define RWNX_CMD_FLAG_WAIT_CFM BIT(4) +#define RWNX_CMD_FLAG_DONE BIT(5) +/* ATM IPC design makes it possible to get the CFM before the ACK, + * otherwise this could have simply been a state enum + */ +#define RWNX_CMD_WAIT_COMPLETE(flags) \ + (!((flags) & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_CFM))) + +#define RWNX_CMD_MAX_QUEUED 8 + +#define IPC_E2A_MSG_PARAM_SIZE 256 + +#define RWNX_FN_ENTRY_STR ">>> %s()\n", __func__ + +/* message levels */ +#define LOGERROR 0x0001 +#define LOGINFO 0x0002 +#define LOGTRACE 0x0004 +#define LOGDEBUG 0x0008 +#define LOGDATA 0x0010 + +extern int testmode; +extern int cur_mode; +extern int adap_test; +extern struct aic_sdio_dev *aicbsp_sdiodev; +extern int aicwf_dbg_level_bsp; +#ifdef CONFIG_M2D_OTA_AUTO_SUPPORT +extern char saved_sdk_ver[64]; +#endif + +#define AICWF_LOG "AICBSP(" + +#define AICWFDBG(level, args, arg...) \ + do { \ + if (aicwf_dbg_level_bsp & (level)) { \ + pr_debug(AICWF_LOG #level ")\t" args, ##arg); \ + } \ + } while (0) + +#define RWNX_DBG(fmt, ...) \ + do { \ + if (aicwf_dbg_level_bsp & LOGTRACE) { \ + pr_debug(AICWF_LOG "LOGTRACE)\t" fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +/// Message structure for MSGs from Emb to App +struct ipc_e2a_msg { + u16 id; ///< Message id. + u16 dummy_dest_id; + u16 dummy_src_id; + u16 param_len; ///< Parameter embedded struct length. + u32 pattern; ///< Used to stamp a valid MSG buffer + u32 param[IPC_E2A_MSG_PARAM_SIZE]; ///< Parameter embedded struct. Must be + ///< word-aligned. +}; + +typedef u16 lmac_msg_id_t; +typedef u16 lmac_task_id_t; + +struct lmac_msg { + lmac_msg_id_t id; ///< Message id. + lmac_task_id_t dest_id; ///< Destination kernel identifier. + lmac_task_id_t src_id; ///< Source kernel identifier. + u16 param_len; ///< Parameter embedded struct length. + u32 param[]; ///< Parameter embedded struct. Must be word-aligned. +}; + +#define rwnx_cmd_e2amsg ipc_e2a_msg +#define rwnx_cmd_a2emsg lmac_msg +#define RWNX_CMD_A2EMSG_LEN(m) (sizeof(struct lmac_msg) + (m)->param_len) +#define RWNX_CMD_E2AMSG_LEN_MAX (IPC_E2A_MSG_PARAM_SIZE * 4) + +static inline void put_u16(u8 *buf, u16 data) +{ + buf[0] = (u8)(data & 0x00ff); + buf[1] = (u8)((data >> 8) & 0x00ff); +} + +enum rwnx_cmd_mgr_state { + RWNX_CMD_MGR_STATE_DEINIT, + RWNX_CMD_MGR_STATE_INITED, + RWNX_CMD_MGR_STATE_CRASHED, +}; + +struct rwnx_cmd { + struct list_head list; + lmac_msg_id_t id; + lmac_msg_id_t reqid; + struct rwnx_cmd_a2emsg *a2e_msg; + char *e2a_msg; + u32 tkn; + u16 flags; + struct completion complete; + u32 result; +}; + +struct aic_sdio_dev; +struct rwnx_cmd; +typedef int (*msg_cb_fct)(struct rwnx_cmd *cmd, struct rwnx_cmd_e2amsg *msg); + +struct rwnx_cmd_mgr { + enum rwnx_cmd_mgr_state state; + /* lock for cmd mgr */ + spinlock_t lock; + u32 next_tkn; + u32 queue_sz; + u32 max_queue_sz; + /* lock for cb */ + spinlock_t cb_lock; + void *sdiodev; + + struct list_head cmds; + + int (*queue)(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd); + int (*llind)(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd); + int (*msgind)(struct rwnx_cmd_mgr *cmd_mgr, + struct rwnx_cmd_e2amsg *cmd_e2amsg, + msg_cb_fct cb_fct); + void (*print)(struct rwnx_cmd_mgr *cmd_mgr); + void (*drain)(struct rwnx_cmd_mgr *cmd_mgr); + + struct work_struct cmd_work; + struct workqueue_struct *cmd_wq; +}; + +void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr); +void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr); +int cmd_mgr_queue_force_defer(struct rwnx_cmd_mgr *cmd_mgr, + struct rwnx_cmd *cmd); +void rwnx_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len); + +enum { + TASK_NONE = (u8)-1, + + // MAC Management task. + TASK_MM = 0, + // DEBUG task + TASK_DBG, + /// SCAN task + TASK_SCAN, + /// TDLS task + TASK_TDLS, + /// SCANU task + TASK_SCANU, + /// ME task + TASK_ME, + /// SM task + TASK_SM, + /// APM task + TASK_APM, + /// BAM task + TASK_BAM, + /// MESH task + TASK_MESH, + /// RXU task + TASK_RXU, + // This is used to define the last task that is running on the EMB processor + TASK_LAST_EMB = TASK_RXU, + + // nX API task + TASK_API, + TASK_MAX, +}; + +#define LMAC_FIRST_MSG(task) ((lmac_msg_id_t)((task) << 10)) +#define DRV_TASK_ID 100 +#define MSG_I(msg) ((msg) & ((1 << 10) - 1)) +#define MSG_T(msg) ((lmac_task_id_t)((msg) >> 10)) + +enum dbg_msg_tag { + /// Memory read request + DBG_MEM_READ_REQ = LMAC_FIRST_MSG(TASK_DBG), + /// Memory read confirm + DBG_MEM_READ_CFM, + /// Memory write request + DBG_MEM_WRITE_REQ, + /// Memory write confirm + DBG_MEM_WRITE_CFM, + /// Module filter request + DBG_SET_MOD_FILTER_REQ, + /// Module filter confirm + DBG_SET_MOD_FILTER_CFM, + /// Severity filter request + DBG_SET_SEV_FILTER_REQ, + /// Severity filter confirm + DBG_SET_SEV_FILTER_CFM, + /// LMAC/MAC HW fatal error indication + DBG_ERROR_IND, + /// Request to get system statistics + DBG_GET_SYS_STAT_REQ, + /// COnfirmation of system statistics + DBG_GET_SYS_STAT_CFM, + /// Memory block write request + DBG_MEM_BLOCK_WRITE_REQ, + /// Memory block write confirm + DBG_MEM_BLOCK_WRITE_CFM, + /// Start app request + DBG_START_APP_REQ, + /// Start app confirm + DBG_START_APP_CFM, + /// Start npc request + DBG_START_NPC_REQ, + /// Start npc confirm + DBG_START_NPC_CFM, + /// Memory mask write request + DBG_MEM_MASK_WRITE_REQ, + /// Memory mask write confirm + DBG_MEM_MASK_WRITE_CFM, + + DBG_RFTEST_CMD_REQ, + DBG_RFTEST_CMD_CFM, + DBG_BINDING_REQ, + DBG_BINDING_CFM, + DBG_BINDING_IND, + + DBG_CUSTOM_MSG_REQ, + DBG_CUSTOM_MSG_CFM, + DBG_CUSTOM_MSG_IND, + + DBG_GPIO_WRITE_REQ, + DBG_GPIO_WRITE_CFM, + + /// Max number of Debug messages + DBG_MAX, +}; + +#if !defined(CONFIG_M2D_OTA_LZMA_SUPPORT) +#define FW_M2D_OTA_NAME "m2d_ota.bin" +#else +#define FW_M2D_OTA_NAME "m2d_ota_lzma.bin" +#endif + +enum { + HOST_START_APP_AUTO = 1, + HOST_START_APP_CUSTOM, + HOST_START_APP_FNCALL = 4, + HOST_START_APP_DUMMY = 5, +}; + +struct dbg_mem_block_write_req { + u32 memaddr; + u32 memsize; + u32 memdata[1024 / sizeof(u32)]; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_BLOCK_WRITE_CFM +/// message. +struct dbg_mem_block_write_cfm { + u32 wstatus; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_WRITE_REQ message. +struct dbg_mem_write_req { + u32 memaddr; + u32 memdata; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_WRITE_CFM message. +struct dbg_mem_write_cfm { + u32 memaddr; + u32 memdata; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_READ_REQ message. +struct dbg_mem_read_req { + u32 memaddr; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_READ_CFM message. +struct dbg_mem_read_cfm { + u32 memaddr; + u32 memdata; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_MASK_WRITE_REQ +/// message. +struct dbg_mem_mask_write_req { + u32 memaddr; + u32 memmask; + u32 memdata; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_MASK_WRITE_CFM +/// message. +struct dbg_mem_mask_write_cfm { + u32 memaddr; + u32 memdata; +}; + +/// Structure containing the parameters of the @ref DBG_START_APP_REQ message. +struct dbg_start_app_req { + u32 bootaddr; + u32 boottype; +}; + +/// Structure containing the parameters of the @ref DBG_START_APP_CFM message. +struct dbg_start_app_cfm { + u32 bootstatus; +}; + +int aicwf_plat_patch_load_8800dc(struct aic_sdio_dev *sdiodev); +int aicwf_plat_rftest_load_8800dc(struct aic_sdio_dev *sdiodev); +#ifdef CONFIG_DPD +int aicwf_misc_ram_valid_check_8800dc(struct aic_sdio_dev *sdiodev, + int *valid_out); +int aicwf_plat_calib_load_8800dc(struct aic_sdio_dev *sdiodev); +#endif + +int rwnx_load_firmware(u32 **fw_buf, const char *name, struct device *device); +int aicwf_patch_table_load(struct aic_sdio_dev *rwnx_hw, char *filename); + +int rwnx_send_dbg_mem_read_req(struct aic_sdio_dev *sdiodev, u32 mem_addr, + struct dbg_mem_read_cfm *cfm); +int rwnx_send_dbg_mem_block_write_req(struct aic_sdio_dev *sdiodev, + u32 mem_addr, u32 mem_size, + u32 *mem_data); +int rwnx_send_dbg_mem_write_req(struct aic_sdio_dev *sdiodev, u32 mem_addr, + u32 mem_data); +int rwnx_send_dbg_mem_mask_write_req(struct aic_sdio_dev *sdiodev, u32 mem_addr, + u32 mem_mask, u32 mem_data); +int rwnx_send_dbg_start_app_req(struct aic_sdio_dev *sdiodev, u32 boot_addr, + u32 boot_type, + struct dbg_start_app_cfm *start_app_cfm); + +int rwnx_plat_bin_fw_upload(struct aic_sdio_dev *sdiodev, u32 fw_addr, + const char *filename); +#ifdef CONFIG_M2D_OTA_AUTO_SUPPORT +int rwnx_plat_m2d_flash_ota(struct aic_sdio_dev *sdiodev, char *filename); +int rwnx_plat_m2d_flash_ota_check(struct aic_sdio_dev *sdiodev, + char *filename); +#endif + +void rwnx_rx_handle_msg(struct aic_sdio_dev *sdiodev, struct ipc_e2a_msg *msg); +int aicbsp_platform_init(struct aic_sdio_dev *sdiodev); +void aicbsp_platform_deinit(struct aic_sdio_dev *sdiodev); +int aicbsp_driver_fw_init(struct aic_sdio_dev *sdiodev); +#if (defined(CONFIG_DPD) && !defined(CONFIG_FORCE_DPD_CALIB)) +#endif +int aicbsp_resv_mem_init(void); +int aicbsp_resv_mem_deinit(void); + +#define RAM_FMAC_FW_ADDR 0x00120000 +#define RAM_FMAC_FW_PATCH_ADDR 0x00190000 +#define FW_RAM_ADID_BASE_ADDR 0x00161928 +#define FW_RAM_ADID_BASE_ADDR_U03 0x00161928 +#define FW_RAM_PATCH_BASE_ADDR 0x00100000 +#define RAM_8800DC_U01_ADID_ADDR 0x00101788 +#define RAM_8800DC_U02_ADID_ADDR 0x001017d8 +#define RAM_8800DC_FW_PATCH_ADDR 0x00184000 +#define FW_RESET_START_ADDR 0x40500128 +#define FW_RESET_START_VAL 0x40 +#define FW_ADID_FLAG_ADDR 0x40500150 +#define FW_ADID_FLAG_VAL 0x01 +#define FW_RAM_ADID_BASE_ADDR_8800D80 0x002017E0 +#define FW_RAM_PATCH_BASE_ADDR_8800D80 0x0020B2B0 +#define FW_RAM_ADID_BASE_ADDR_8800D80_U02 0x00201940 +#define FW_RAM_PATCH_BASE_ADDR_8800D80_U02 0x0020b43c + +#define AICBT_PT_TAG "AICBT_PT_TAG" + +/***************************************************************************** + * Addresses within RWNX_ADDR_CPU + *****************************************************************************/ +#define RAM_LMAC_FW_ADDR 0x00150000 + +#define ROM_FMAC_FW_ADDR 0x00010000 +#define ROM_FMAC_PATCH_ADDR 0x00180000 + +#define RAM_FMAC_FW_PATCH_NAME "fmacfw_patch.bin" +#define RWNX_MAC_CALIB_BASE_NAME_8800DC "fmacfw_calib_8800dc" +#define RWNX_MAC_CALIB_NAME_8800DC_U02 RWNX_MAC_CALIB_BASE_NAME_8800DC "_u02.bin" +#define RWNX_MAC_CALIB_NAME_8800DC_H_U02 \ + RWNX_MAC_CALIB_BASE_NAME_8800DC "_h_u02.bin" + +#ifdef CONFIG_DPD +#define ROM_FMAC_CALIB_ADDR 0x00130000 +#ifndef CONFIG_FORCE_DPD_CALIB +#define FW_DPDRESULT_NAME_8800DC "aic_dpdresult_lite_8800dc.bin" +#endif +#endif + +#define RWNX_MAC_FW_RF_BASE_NAME_8800DC "lmacfw_rf_8800dc.bin" + +#ifdef CONFIG_FOR_IPCOM +#define RWNX_MAC_PATCH_BASE_NAME_8800DC "fmacfw_patch_8800dc_ipc" +#define RWNX_MAC_PATCH_NAME2_8800DC RWNX_MAC_PATCH_BASE_NAME_8800DC ".bin" +#else +#define RWNX_MAC_PATCH_BASE_NAME_8800DC "fmacfw_patch_8800dc" +#define RWNX_MAC_PATCH_NAME2_8800DC RWNX_MAC_PATCH_BASE_NAME_8800DC ".bin" +#define RWNX_MAC_PATCH_NAME2_8800DC_U02 \ + RWNX_MAC_PATCH_BASE_NAME_8800DC "_u02.bin" +#define RWNX_MAC_PATCH_NAME2_8800DC_H_U02 \ + RWNX_MAC_PATCH_BASE_NAME_8800DC "_h_u02.bin" +#endif + +#define RWNX_MAC_PATCH_TABLE_NAME_8800DC "fmacfw_patch_tbl_8800dc" +#define RWNX_MAC_PATCH_TABLE_8800DC RWNX_MAC_PATCH_TABLE_NAME_8800DC ".bin" +#define RWNX_MAC_PATCH_TABLE_8800DC_U02 \ + RWNX_MAC_PATCH_TABLE_NAME_8800DC "_u02.bin" +#define RWNX_MAC_PATCH_TABLE_8800DC_H_U02 \ + RWNX_MAC_PATCH_TABLE_NAME_8800DC "_h_u02.bin" + +#define RWNX_MAC_RF_PATCH_BASE_NAME_8800DC "fmacfw_rf_patch_8800dc" +#define RWNX_MAC_RF_PATCH_NAME_8800DC RWNX_MAC_RF_PATCH_BASE_NAME_8800DC ".bin" +#define FW_USERCONFIG_NAME_8800DC "aic_userconfig_8800dc.txt" + +enum { + FW_NORMAL_MODE = 0, + FW_RFTEST_MODE = 1, + FW_BLE_SCAN_WAKEUP_MODE = 2, + FW_M2D_OTA_MODE = 3, + FW_DPDCALIB_MODE = 4, + FW_BLE_SCAN_AD_FILTER_MODE = 5, +}; + +enum aicbt_patch_table_type { + AICBT_PT_INF = 0x00, + AICBT_PT_TRAP = 0x1, + AICBT_PT_B4, + AICBT_PT_BTMODE, + AICBT_PT_PWRON, + AICBT_PT_AF, + AICBT_PT_VER, +}; + +enum aicbt_btport_type { + AICBT_BTPORT_NULL, + AICBT_BTPORT_MB, + AICBT_BTPORT_UART, +}; + +/* btmode + * used for force bt mode,if not AICBSP_MODE_NULL + * efuse valid and vendor_info will be invalid, even has beed set valid + */ +enum aicbt_btmode_type { + AICBT_BTMODE_BT_ONLY_SW = 0x0, // bt only mode with switch + AICBT_BTMODE_BT_WIFI_COMBO, // wifi/bt combo mode + AICBT_BTMODE_BT_ONLY, // bt only mode without switch + AICBT_BTMODE_BT_ONLY_TEST, // bt only test mode + AICBT_BTMODE_BT_WIFI_COMBO_TEST, // wifi/bt combo test mode + AICBT_BTMODE_BT_ONLY_COANT, // bt only mode with no external switch + AICBT_MODE_NULL = 0xFF, // invalid value +}; + +/* uart_baud + * used for config uart baud when btport set to uart, + * otherwise meaningless + */ +enum aicbt_uart_baud_type { + AICBT_UART_BAUD_115200 = 115200, + AICBT_UART_BAUD_921600 = 921600, + AICBT_UART_BAUD_1_5M = 1500000, + AICBT_UART_BAUD_3_25M = 3250000, +}; + +enum aicbt_uart_flowctrl_type { + AICBT_UART_FLOWCTRL_DISABLE = 0x0, // uart without flow ctrl + AICBT_UART_FLOWCTRL_ENABLE, // uart with flow ctrl +}; + +enum aicbsp_cpmode_type { + AICBSP_CPMODE_WORK, + AICBSP_CPMODE_TEST, + AICBSP_CPMODE_MAX, +}; + +enum chip_rev { + CHIP_REV_U01 = 1, + CHIP_REV_U02 = 3, + CHIP_REV_U03 = 7, + CHIP_REV_U04 = 7, +}; + +#define AIC_M2D_OTA_INFO_ADDR 0x88000020 +#define AIC_M2D_OTA_DATA_ADDR 0x88000040 +#if !defined(CONFIG_M2D_OTA_LZMA_SUPPORT) +#define AIC_M2D_OTA_FLASH_ADDR 0x08004000 +#define AIC_M2D_OTA_CODE_START_ADDR (AIC_M2D_OTA_FLASH_ADDR + 0x0188) +#define AIC_M2D_OTA_VER_ADDR (AIC_M2D_OTA_FLASH_ADDR + 0x018C) +#else +#define AIC_M2D_OTA_FLASH_ADDR 0x08005000 +#define AIC_M2D_OTA_CODE_START_ADDR (AIC_M2D_OTA_FLASH_ADDR + 0x1188) +#define AIC_M2D_OTA_VER_ADDR (AIC_M2D_OTA_FLASH_ADDR + 0x0010) +#endif +/// aic bt tx pwr lvl :lsb->msb: first byte, min pwr lvl; second byte, max pwr +/// lvl; pwr lvl:20(min), 30 , 40 , 50 , 60(max) +#define AICBT_TXPWR_LVL 0x00006020 +#define AICBT_TXPWR_LVL_8800dc 0x00006f2f +#define AICBT_TXPWR_LVL_8800d80 0x00006f2f + +#define AICBSP_HWINFO_DEFAULT (-1) +#define AICBSP_CPMODE_DEFAULT AICBSP_CPMODE_WORK +#define AICBSP_FWLOG_EN_DEFAULT 0 + +#define AICBT_BTMODE_DEFAULT_8800d80 AICBT_BTMODE_BT_ONLY_COANT +#define AICBT_BTMODE_DEFAULT AICBT_BTMODE_BT_ONLY_SW +#define AICBT_BTPORT_DEFAULT AICBT_BTPORT_UART +#define AICBT_UART_BAUD_DEFAULT AICBT_UART_BAUD_1_5M +#define AICBT_UART_FC_DEFAULT AICBT_UART_FLOWCTRL_ENABLE +#define AICBT_LPM_ENABLE_DEFAULT 1 +#define AICBT_TXPWR_LVL_DEFAULT AICBT_TXPWR_LVL +#define AICBT_TXPWR_LVL_DEFAULT_8800dc AICBT_TXPWR_LVL_8800dc +#define AICBT_TXPWR_LVL_DEFAULT_8800d80 AICBT_TXPWR_LVL_8800d80 + +#define FEATURE_SDIO_CLOCK 50000000 // 0: default, other: target clock rate +#define FEATURE_SDIO_CLOCK_V3 150000000 // 0: default, other: target clock rate +#define FEATURE_SDIO_PHASE 2 // 0: default, 2: 180° + +struct aicbt_patch_table { + char *name; + u32 type; + u32 *data; + u32 len; + struct aicbt_patch_table *next; +}; + +struct aicbt_info_t { + u32 btmode; + u32 btport; + u32 uart_baud; + u32 uart_flowctrl; + u32 lpm_enable; + u32 txpwr_lvl; +}; + +struct aicbt_patch_info_t { + u32 info_len; + u32 adid_addrinf; + u32 addr_adid; + u32 patch_addrinf; + u32 addr_patch; + u32 reset_addr; + u32 reset_val; + u32 adid_flag_addr; + u32 adid_flag; +}; + +struct aicbsp_firmware { + const char *desc; + const char *bt_adid; + const char *bt_patch; + const char *bt_table; + const char *wl_fw; +}; + +struct aicbsp_info_t { + int hwinfo; + int hwinfo_r; + u32 cpmode; + u32 adap_test; + u32 chip_rev; + bool fwlog_en; + u8 irqf; +}; + +extern int adap_test; + +extern struct aicbsp_info_t aicbsp_info; +extern struct mutex aicbsp_power_lock; +extern const struct aicbsp_firmware *aicbsp_firmware_list; +extern const struct aicbsp_firmware fw_8800d80_u02[]; + +int aicbsp_system_config(struct aic_sdio_dev *sdiodev); +int aicwifi_patch_config(struct aic_sdio_dev *sdiodev); +int aicwifi_sys_config(struct aic_sdio_dev *sdiodev); +int aicbt_patch_table_free(struct aicbt_patch_table **head); +struct aicbt_patch_table *aicbt_patch_table_alloc(const char *filename); +int aicbt_patch_info_unpack(struct aicbt_patch_info_t *patch_info, + struct aicbt_patch_table *head_t); +int aicbt_patch_trap_data_load(struct aic_sdio_dev *sdiodev, + struct aicbt_patch_table *head); +int aicbt_patch_table_load(struct aic_sdio_dev *sdiodev, + struct aicbt_patch_table *head); +int aicbt_init(struct aic_sdio_dev *sdiodev); +int aicwifi_init(struct aic_sdio_dev *sdiodev); +int aicwifi_start_from_bootrom(struct aic_sdio_dev *sdiodev); + +#endif diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_export.h b/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_export.h new file mode 100644 index 0000000000000..5d2a5c38c95e6 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_export.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __AIC_BSP_EXPORT_H +#define __AIC_BSP_EXPORT_H + +#include + +enum aicbsp_subsys { + AIC_BLUETOOTH, + AIC_WIFI, +}; + +enum aicbsp_pwr_state { + AIC_PWR_OFF, + AIC_PWR_ON, +}; + +enum skb_buff_id { + AIC_RESV_MEM_TXDATA, +}; + +struct skb_buff_pool { + u32 id; + u32 size; + const char *name; + u8 used; + struct sk_buff *skb; +}; + +struct aicbsp_feature_t { + int hwinfo; + u32 sdio_clock; + u8 sdio_phase; + bool fwlog_en; + u8 irqf; + u8 cpmode; + int adapt; +}; + +#ifdef CONFIG_DPD +struct rf_misc_ram_t { + u32 bit_mask[3]; + u32 reserved; + u32 dpd_high[96]; + u32 dpd_11b[96]; + u32 dpd_low[96]; + u32 idac_11b[48]; + u32 idac_high[48]; + u32 idac_low[48]; + u32 loft_res[18]; + u32 rx_iqim_res[16]; +}; + +struct rf_misc_ram_lite_t { + u32 bit_mask[4]; + u32 dpd_high[96]; + u32 loft_res[18]; +}; + +#define MEMBER_SIZE(type, member) sizeof_field(type, member) +#define DPD_RESULT_SIZE_8800DC sizeof(struct rf_misc_ram_lite_t) + +extern struct rf_misc_ram_lite_t dpd_res; +#endif + +int aicbsp_set_subsys(int, int); +int aicbsp_get_feature(struct aicbsp_feature_t *feature); +struct sk_buff *aicbsp_resv_mem_alloc_skb(unsigned int length, uint32_t id); +void aicbsp_resv_mem_kfree_skb(struct sk_buff *skb, uint32_t id); + +#endif diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_main.c b/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_main.c new file mode 100644 index 0000000000000..f753f124d1df8 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic_bsp_main.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief bsp sdio main function + * + ****************************************************************************** + */ + +#include "aic_bsp_driver.h" +#include "aicwf_txq_prealloc.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DRV_DESCRIPTION "AIC BSP" +#define DRV_COPYRIGHT "Copyright(c) 2015-2020 AICSemi" +#define DRV_AUTHOR "AICSemi" + +int aicwf_dbg_level_bsp = LOGERROR; // | LOGINFO | LOGDEBUG | LOGTRACE; +module_param(aicwf_dbg_level_bsp, int, 0660); + +static struct platform_device *aicbsp_pdev; + +const struct aicbsp_firmware fw_8800d80_u02[AICBSP_CPMODE_MAX] = { + [AICBSP_CPMODE_WORK] = { + .desc = "normal work mode(8800d80 sdio u02)", + .wl_fw = "fmacfw_8800d80_u02.bin", + }, +}; + +const struct aicbsp_firmware *aicbsp_firmware_list = fw_8800d80_u02; + +struct aicbsp_info_t aicbsp_info = { + .hwinfo_r = AICBSP_HWINFO_DEFAULT, + .hwinfo = AICBSP_HWINFO_DEFAULT, + .cpmode = AICBSP_CPMODE_DEFAULT, + .fwlog_en = AICBSP_FWLOG_EN_DEFAULT, + .adap_test = 0, +#ifdef CONFIG_IRQ_FALL + .irqf = 1, +#else + .irqf = 0, +#endif +}; + +/* mutex for aicbsp power */ +struct mutex aicbsp_power_lock; + +static struct platform_driver aicbsp_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "aic_bsp", + }, +}; + +static ssize_t hwinfo_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t count = 0; + + count += sprintf(&buf[count], "chip hw rev: "); + if (aicbsp_info.hwinfo_r < 0) + count += sprintf(&buf[count], "-1(not avalible)\n"); + else + count += sprintf(&buf[count], "0x%02X\n", aicbsp_info.chip_rev); + + count += sprintf(&buf[count], "hwinfo read: "); + if (aicbsp_info.hwinfo_r < 0) + count += + sprintf(&buf[count], "%d(not avalible), ", aicbsp_info.hwinfo_r); + else + count += sprintf(&buf[count], "0x%02X, ", aicbsp_info.hwinfo_r); + + if (aicbsp_info.hwinfo < 0) + count += + sprintf(&buf[count], "set: %d(not avalible)\n", aicbsp_info.hwinfo); + else + count += sprintf(&buf[count], "set: 0x%02X\n", aicbsp_info.hwinfo); + + return count; +} + +static ssize_t hwinfo_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + long val; + int err = kstrtol(buf, 0, &val); + + if (err) { + pr_err("invalid input\n"); + return err; + } + + if ((val == -1) || (val >= 0 && val <= 0xFF)) { + aicbsp_info.hwinfo = val; + } else { + pr_err("invalid values\n"); + return -EINVAL; + } + return count; +} + +static ssize_t fwdebug_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t count = 0; + + count += sprintf(&buf[count], "fw log status: %s\n", + aicbsp_info.fwlog_en ? "on" : "off"); + + return count; +} + +static ssize_t fwdebug_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + long val; + int err = kstrtol(buf, 0, &val); + + if (err) { + pr_err("invalid input\n"); + return err; + } + + if (val > 1 || val < 0) { + pr_err("must be 0 or 1\n"); + return -EINVAL; + } + + aicbsp_info.fwlog_en = val; + return count; +} + +static ssize_t adapt_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t count = 0; + + count += sprintf(&buf[count], "adapt status: %s\n", + aicbsp_info.adap_test ? "on" : "off"); + + return count; +} + +static ssize_t adapt_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + long val; + int err = kstrtol(buf, 0, &val); + + if (err) { + pr_err("invalid input\n"); + return err; + } + + if (val > 1 || val < 0) { + pr_err("must be 0 or 1\n"); + return err; + } + + aicbsp_info.adap_test = val; + return count; +} + +static DEVICE_ATTR_RW(hwinfo); + +static DEVICE_ATTR_RW(fwdebug); + +static DEVICE_ATTR_RW(adapt); + +static struct attribute *aicbsp_attributes[] = { + &dev_attr_hwinfo.attr, + &dev_attr_fwdebug.attr, + &dev_attr_adapt.attr, + NULL, +}; + +static struct attribute_group aicbsp_attribute_group = { + .name = "aicbsp_info", + .attrs = aicbsp_attributes, +}; + +int testmode = AICBSP_CPMODE_DEFAULT; +int adap_test; +module_param(adap_test, int, 0660); + +#if defined CONFIG_PLATFORM_EXTERNAL && defined CONFIG_CUSTOM_PWF +int gpio_pwr; +bool gpio_pwr_assert; + +void nxp_pwr_ctrl(int value) +{ + pr_info("aic_bsp: %s: %d\n", __func__, value); + gpio_set_value(gpio_pwr, value); +} + +static int aicbsp_pwr_node(void) +{ + struct device_node *np; + int err; + + np = of_find_compatible_node(NULL, NULL, "aic,wifi-power"); + if (!np) { + pr_err("aic_bsp: %s np not found\n", __func__); + return -ENODEV; + } + + gpio_pwr = of_get_named_gpio(np, "wifi-power", 0); + if (!gpio_is_valid(gpio_pwr)) { + pr_err("aic_bsp: %s is invalid\n", __func__); + return -ENODEV; + } + + err = gpio_request(gpio_pwr, "wifi-power"); + if (err < 0) { + pr_err("aic_bsp: %s can't request gpio_pwr gpio %d\n", __func__, + gpio_pwr); + return -ENODEV; + } + + err = gpio_direction_output(gpio_pwr, 1); + if (err < 0) { + pr_err("aic_bsp: %s can't request output direction gpio_pwr gpio %d\n", + __func__, gpio_pwr); + return -ENODEV; + } + + return 0; +} +#endif + +static int __init aicbsp_init(void) +{ + int ret; + + testmode = AICBSP_CPMODE_WORK; + aicbsp_info.cpmode = AICBSP_CPMODE_WORK; + + pr_info("AIC_BSP driver, built against Linux %s\n", UTS_RELEASE); + + aicbsp_resv_mem_init(); + ret = platform_driver_register(&aicbsp_driver); + if (ret) { + pr_err("register platform driver failed: %d\n", ret); + return ret; + } + + aicbsp_pdev = platform_device_alloc("aic-bsp", -1); + ret = platform_device_add(aicbsp_pdev); + if (ret) { + pr_err("register platform device failed: %d\n", ret); + return ret; + } + + ret = sysfs_create_group(&aicbsp_pdev->dev.kobj, &aicbsp_attribute_group); + if (ret) { + pr_err("register sysfs create group failed!\n"); + return ret; + } + + mutex_init(&aicbsp_power_lock); + +#if defined CONFIG_PLATFORM_EXTERNAL && defined CONFIG_CUSTOM_PWF + ret = aicbsp_pwr_node(); + if (ret) { + pr_err("aic_bsp: aicbsp_pwr_node fail\n"); + return ret; + } +#endif + return 0; +} + +void aicbsp_sdio_exit(void); + +static void __exit aicbsp_exit(void) +{ +#if defined CONFIG_PLATFORM_EXTERNAL && defined CONFIG_CUSTOM_PWF + gpio_free(gpio_pwr); +#endif + + sysfs_remove_group(&aicbsp_pdev->dev.kobj, &aicbsp_attribute_group); + platform_device_del(aicbsp_pdev); + platform_driver_unregister(&aicbsp_driver); + mutex_destroy(&aicbsp_power_lock); + aicbsp_resv_mem_deinit(); +#ifdef CONFIG_PREALLOC_TXQ + aicwf_prealloc_txq_free(); +#endif +} + +module_init(aicbsp_init); +module_exit(aicbsp_exit); + +MODULE_FIRMWARE(AIC8800_FW_DIR "fmacfw_8800d80_u02.bin"); + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic_chip_8800d80.c b/drivers/net/wireless/aic/aic8800_bsp/aic_chip_8800d80.c new file mode 100644 index 0000000000000..1648b01d588a4 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic_chip_8800d80.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 AIC semiconductor. + * + * @file aic_chip_8800d80.c + * @brief Chip operations for AIC8800D80 + */ + +#include +#include +#include +#include "aic_chip_ops.h" + +#include "aic8800d80_compat.h" +#include "aic_bsp_driver.h" + +static int aic8800d80_wifi_init(struct aic_sdio_dev *sdiodev, int testmode) +{ + const char *fw_path = fw_8800d80_u02[AICBSP_CPMODE_WORK].wl_fw; + + (void)testmode; + aicbsp_firmware_list = fw_8800d80_u02; + aicbsp_info.cpmode = AICBSP_CPMODE_WORK; + if (!fw_path) + return -ENOENT; + + pr_info("aicbsp 8800d80 u02 wifi firmware: %s\n", fw_path); + + if (rwnx_plat_bin_fw_upload(sdiodev, RAM_FMAC_FW_ADDR, + fw_path)) { + pr_err("aicbsp 8800d80 download wifi fw fail\n"); + return -1; + } + + if (aicwifi_patch_config_8800d80(sdiodev)) { + pr_err("aicbsp aicwifi_patch_config_8800d80 fail\n"); + return -1; + } + + if (aicwifi_sys_config_8800d80(sdiodev)) { + pr_err("aicbsp aicwifi_patch_config_8800d80 fail\n"); + return -1; + } + + if (aicwifi_start_from_bootrom(sdiodev)) { + pr_err("aicbsp 8800d80 wifi start fail\n"); + return -1; + } + return 0; +} + +static int aic8800d80_driver_fw_init(struct aic_sdio_dev *sdiodev, u32 *btenable, + struct aicbsp_info_t *aicbsp_info, + const struct aicbsp_firmware **aicbsp_firmware_list) +{ + u32 mem_addr; + struct dbg_mem_read_cfm rd_mem_addr_cfm; + u8 is_chip_id_h = 0; + + mem_addr = 0x40500000; + + if (rwnx_send_dbg_mem_read_req(sdiodev, mem_addr, &rd_mem_addr_cfm)) + return -1; + + aicbsp_info->chip_rev = (u8)((rd_mem_addr_cfm.memdata >> 16) & 0x3F); + is_chip_id_h = (u8)(((rd_mem_addr_cfm.memdata >> 16) & 0xC0) == 0xC0); + if (is_chip_id_h || aicbsp_info->chip_rev != CHIP_REV_U02) { + pr_err("aicbsp: AIC8800D80 U02 is required (revision %u)\n", + aicbsp_info->chip_rev); + return -ENODEV; + } + + *btenable = 0; + *aicbsp_firmware_list = fw_8800d80_u02; + if (aicbsp_system_config_8800d80(sdiodev)) + return -1; + + return 0; +} + +/* ========== Ops instances ========== */ + +const struct aic_chip_ops aic_chip_aic8800d80_ops = { + .name = "AIC8800D80", + .use_func_msg = false, + .use_sdiov3_func = true, + .need_flowctrl_mask = false, + .wakeup_reg_val = 0x11, + .use_flowctrl_msg = true, + .use_hdr_checksum = true, + .need_fix_hdr_len = false, + .need_func0_intr = true, + .use_func2 = false, + .sdio_clock = FEATURE_SDIO_CLOCK_V3, + .wifi_init = aic8800d80_wifi_init, + .driver_fw_init = aic8800d80_driver_fw_init, + +}; diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic_chip_8800dc.c b/drivers/net/wireless/aic/aic8800_bsp/aic_chip_8800dc.c new file mode 100644 index 0000000000000..b3841261312f9 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic_chip_8800dc.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 AIC semiconductor. + * + * @file aic_chip_8800dc.c + * @brief Chip operations for AIC8801 / AIC8800DC / AIC8800DW + */ + +#include +#include +#include +#include "aic_chip_ops.h" + +#include "aic8800dc_compat.h" +#include "aic_bsp_driver.h" + +static int aic8800dc_wifi_init(struct aic_sdio_dev *sdiodev, int testmode) +{ + int ret; + + pr_info("aicbsp ############ %s begin\n", __func__); + + system_config_8800dc(sdiodev); + pr_info("aicbsp ############ system_config_8800dc done\n"); + + ret = rwnx_plat_patch_load_8800dc(sdiodev); + if (ret) { + pr_err("aicbsp patch load return %d\n", ret); + return ret; + } + pr_info("aicbsp ############ rwnx_plat_patch_load done\n"); + + // rwnx_plat_userconfig_load(sdiodev); + + aicwf_patch_config_8800dc(sdiodev); + pr_info("aicbsp ############ aicwf_patch_config_8800dc done\n"); + + start_from_bootrom_8800DC(sdiodev); + + return 0; +} + +static int aic8800dc_set_patch_info(struct aic_sdio_dev *sdiodev, + struct aicbt_patch_info_t *patch_info, + struct aicbsp_info_t *aicbsp_info, + struct aicbt_patch_table *head) +{ + if (aicbsp_info->chip_rev == CHIP_REV_U01) + patch_info->addr_adid = RAM_8800DC_U01_ADID_ADDR; + else if (aicbsp_info->chip_rev == CHIP_REV_U02) + patch_info->addr_adid = RAM_8800DC_U02_ADID_ADDR; + patch_info->addr_patch = RAM_8800DC_FW_PATCH_ADDR; + aicbt_patch_info_unpack(patch_info, head); + if (patch_info->reset_addr == 0) { + patch_info->reset_addr = FW_RESET_START_ADDR; + patch_info->reset_val = FW_RESET_START_VAL; + patch_info->adid_flag_addr = FW_ADID_FLAG_ADDR; + patch_info->adid_flag = FW_ADID_FLAG_VAL; + if (rwnx_send_dbg_mem_write_req(sdiodev, patch_info->reset_addr, + patch_info->reset_val)) + return -1; + if (rwnx_send_dbg_mem_write_req(sdiodev, patch_info->adid_flag_addr, + patch_info->adid_flag)) + return -1; + } + + return 0; +} + +static int aic8800dc_driver_fw_init(struct aic_sdio_dev *sdiodev, u32 *btenable, + struct aicbsp_info_t *aicbsp_info, + const struct aicbsp_firmware **aicbsp_firmware_list) +{ + u32 mem_addr; + struct dbg_mem_read_cfm rd_mem_addr_cfm; + u8 is_chip_id_h = 0; + + mem_addr = 0x40500000; + + if (rwnx_send_dbg_mem_read_req(sdiodev, mem_addr, &rd_mem_addr_cfm)) + return -1; + + aicbsp_info->chip_rev = (u8)((rd_mem_addr_cfm.memdata >> 16) & 0x3F); + is_chip_id_h = (u8)(((rd_mem_addr_cfm.memdata >> 16) & 0xC0) == 0xC0); + + *btenable = ((rd_mem_addr_cfm.memdata >> 26) & 0x1); + AICWFDBG(LOGINFO, "btenable = %d\n", *btenable); + + if (*btenable == 0) { + sdiodev->chipid = PRODUCT_ID_AIC8800DW; + AICWFDBG(LOGINFO, "AIC8800DC change to AIC8800DW\n"); + } + + if (aicbsp_info->chip_rev != CHIP_REV_U01 && + aicbsp_info->chip_rev != CHIP_REV_U02 && + aicbsp_info->chip_rev != CHIP_REV_U03 && + aicbsp_info->chip_rev != CHIP_REV_U04) { + pr_err("aicbsp: %s, unsupport chip rev: %d\n", __func__, + aicbsp_info->chip_rev); + return -1; + } + if (is_chip_id_h) { + AICWFDBG(LOGINFO, "IS_CHIP_ID_H\n"); + *aicbsp_firmware_list = fw_8800dc_h_u02; + } else { + if (aicbsp_info->chip_rev == CHIP_REV_U01) + *aicbsp_firmware_list = fw_8800dc_u01; + else + *aicbsp_firmware_list = fw_8800dc_u02; + } + + return 0; +} + +/* ========== Ops instances ========== */ + +const struct aic_chip_ops aic_chip_aic8800dc_ops = { + .name = "AIC8800DC", + .use_func_msg = true, + .use_sdiov3_func = false, + .need_flowctrl_mask = true, + .wakeup_reg_val = 1, + .use_flowctrl_msg = false, + .use_hdr_checksum = false, + .need_fix_hdr_len = true, + .need_func0_intr = false, + .use_func2 = true, + .sdio_clock = FEATURE_SDIO_CLOCK, + .wifi_init = aic8800dc_wifi_init, + .set_patch_info = aic8800dc_set_patch_info, + .driver_fw_init = aic8800dc_driver_fw_init, + +}; diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic_chip_8801.c b/drivers/net/wireless/aic/aic8800_bsp/aic_chip_8801.c new file mode 100644 index 0000000000000..f28979e25a28c --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic_chip_8801.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 AIC semiconductor. + * + * @file aic_chip_8801.c + * @brief Chip operations for AIC8801 / AIC8800DC / AIC8800DW + */ + +#include +#include +#include +#include "aic_chip_ops.h" +#include "aic_bsp_driver.h" + +#include "aic8800dc_compat.h" +#include "aic8800d80_compat.h" + +static int aic8801_wifi_init(struct aic_sdio_dev *sdiodev, int testmode) +{ +#ifdef CONFIG_M2D_OTA_AUTO_SUPPORT + if (testmode == FW_M2D_OTA_MODE) + rwnx_plat_m2d_flash_ota(sdiodev, FW_M2D_OTA_NAME); + else if (testmode == FW_NORMAL_MODE) + rwnx_plat_m2d_flash_ota_check(sdiodev, FW_M2D_OTA_NAME); +#endif + const char *fw_path = aicbsp_firmware_list[aicbsp_info.cpmode].wl_fw; + + if (rwnx_plat_bin_fw_upload(sdiodev, RAM_FMAC_FW_ADDR, + fw_path)) { + pr_err("aicbsp download wifi fw fail\n"); + return -1; + } + if (testmode == FW_NORMAL_MODE) { + if (rwnx_plat_bin_fw_upload(sdiodev, RAM_FMAC_FW_PATCH_ADDR, + RAM_FMAC_FW_PATCH_NAME)) { + pr_err("aicbsp download wifi fw patch fail\n"); + return -1; + } + } + + if (aicwifi_patch_config(sdiodev)) { + pr_err("aicbsp aicwifi_patch_config fail\n"); + return -1; + } + + if (aicwifi_sys_config(sdiodev)) { + pr_err("aicbsp aicwifi_sys_config fail\n"); + return -1; + } + + if (aicwifi_start_from_bootrom(sdiodev)) { + pr_err("aicbsp wifi start fail\n"); + return -1; + } + return 0; +} + +static int aic8801_set_patch_info(struct aic_sdio_dev *sdiodev, + struct aicbt_patch_info_t *patch_info, + struct aicbsp_info_t *aicbsp_info, + struct aicbt_patch_table *head) +{ + patch_info->addr_adid = FW_RAM_ADID_BASE_ADDR; + patch_info->addr_patch = FW_RAM_PATCH_BASE_ADDR; + return 0; +} + +static int aic8801_driver_fw_init(struct aic_sdio_dev *sdiodev, u32 *btenable, + struct aicbsp_info_t *aicbsp_info, + const struct aicbsp_firmware **aicbsp_firmware_list) +{ + u32 mem_addr; + struct dbg_mem_read_cfm rd_mem_addr_cfm; + + mem_addr = 0x40500000; + + if (rwnx_send_dbg_mem_read_req(sdiodev, mem_addr, &rd_mem_addr_cfm)) + return -1; + + aicbsp_info->chip_rev = (u8)(rd_mem_addr_cfm.memdata >> 16); + *btenable = 1; + + if (aicbsp_info->chip_rev != CHIP_REV_U02 && + aicbsp_info->chip_rev != CHIP_REV_U03 && + aicbsp_info->chip_rev != CHIP_REV_U04) { + pr_err("aicbsp: %s, unsupport chip rev: %d\n", __func__, + aicbsp_info->chip_rev); + return -1; + } + + if (aicbsp_info->chip_rev != CHIP_REV_U02) + *aicbsp_firmware_list = fw_u03; + + if (aicbsp_system_config(sdiodev)) + return -1; + + return 0; +} + +/* ========== Ops instances ========== */ + +const struct aic_chip_ops aic_chip_aic8801_ops = { + .name = "AIC8801", + .use_func_msg = false, + .use_sdiov3_func = false, + .need_flowctrl_mask = true, + .wakeup_reg_val = 1, + .use_flowctrl_msg = true, + .use_hdr_checksum = false, + .need_fix_hdr_len = true, + .need_func0_intr = false, + .use_func2 = true, + .sdio_clock = FEATURE_SDIO_CLOCK, + .wifi_init = aic8801_wifi_init, + .set_patch_info = aic8801_set_patch_info, + .driver_fw_init = aic8801_driver_fw_init, +}; diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic_chip_ops.c b/drivers/net/wireless/aic/aic8800_bsp/aic_chip_ops.c new file mode 100644 index 0000000000000..6a874bbe23df0 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic_chip_ops.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 AIC semiconductor. + * + * @file aic_chip_ops.c + * @brief Wrapper function implementations for chip-specific operations. + */ + +#include +#include +#include "aic_chip_ops.h" +#include "aicsdio.h" + +int aicwf_sdio_chipmatch(u16 vid, u16 did, u16 *chipid) +{ + if (vid != SDIO_VENDOR_ID_AIC8800D80) + return -ENODEV; + + if (did != SDIO_DEVICE_ID_AIC8800D80 && + did != SDIO_DEVICE_ID_AIC8800D80_FUNC2) + return -ENODEV; + + *chipid = PRODUCT_ID_AIC8800D80; + AICWFDBG(LOGINFO, "%s USE AIC8800D80\r\n", __func__); + return 0; +} + +/*========== Chip ops wrappers ========== */ +int aic_chip_wifi_init(struct aic_sdio_dev *sdiodev, int testmode) +{ + if (sdiodev->chip_ops && sdiodev->chip_ops->wifi_init) + return sdiodev->chip_ops->wifi_init(sdiodev, testmode); + return 0; +} + +int aic_chip_set_patch_info(struct aic_sdio_dev *sdiodev, + struct aicbt_patch_info_t *patch_info, + struct aicbsp_info_t *aicbsp_info, + struct aicbt_patch_table *head) +{ + if (sdiodev->chip_ops && sdiodev->chip_ops->set_patch_info) + return sdiodev->chip_ops->set_patch_info(sdiodev, patch_info, + aicbsp_info, head); + return 0; +} + +int aic_chip_driver_fw_init(struct aic_sdio_dev *sdiodev, u32 *btenable, + struct aicbsp_info_t *aicbsp_info, + const struct aicbsp_firmware **aicbsp_firmware_list) +{ + if (sdiodev->chip_ops && sdiodev->chip_ops->driver_fw_init) + return sdiodev->chip_ops->driver_fw_init(sdiodev, btenable, + aicbsp_info, + aicbsp_firmware_list); + return 0; +} + +const struct aic_chip_ops *aic_chip_ops_select(u16 chipid) +{ + if (chipid == PRODUCT_ID_AIC8800D80) + return &aic_chip_aic8800d80_ops; + + return NULL; +} diff --git a/drivers/net/wireless/aic/aic8800_bsp/aic_chip_ops.h b/drivers/net/wireless/aic/aic8800_bsp/aic_chip_ops.h new file mode 100644 index 0000000000000..702b0a4a73baa --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aic_chip_ops.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2024 AIC semiconductor. + * + * @file aic_chip_ops.h + * @brief Chip-specific operation interfaces for AIC8800 series. + */ +#ifndef _AIC_BSP_CHIP_OPS_H_ +#define _AIC_BSP_CHIP_OPS_H_ + +#include + +struct aic_sdio_dev; +struct aicbsp_info_t; +struct aicbt_patch_info_t; +struct aicbt_patch_table; +struct aicbsp_firmware; + +/* Match SDIO vendor/device IDs to a chip product ID */ +int aicwf_sdio_chipmatch(u16 vendor, u16 device, u16 *chipid); + +/* + * struct aic_chip_ops - Chip-specific operations + * + * Each chip variant provides an instance of this struct. + * Selected at probe time based on chipid, then used to + * eliminate chipid-based if-else branching throughout the driver. + */ +struct aic_chip_ops { + const char *name; + const bool use_func_msg; + const bool use_sdiov3_func; + const bool need_flowctrl_mask; + const u8 wakeup_reg_val; + const bool use_flowctrl_msg; + const bool use_hdr_checksum; + const bool need_fix_hdr_len; + const bool need_func0_intr; + const bool use_func2; + const u32 sdio_clock; + + int (*wifi_init)(struct aic_sdio_dev *sdiodev, int testmode); + + int (*set_patch_info)(struct aic_sdio_dev *sdiodev, + struct aicbt_patch_info_t *patch_info, + struct aicbsp_info_t *aicbsp_info, + struct aicbt_patch_table *head); + + int (*driver_fw_init)(struct aic_sdio_dev *sdiodev, u32 *btenable, + struct aicbsp_info_t *aicbsp_info, + const struct aicbsp_firmware **aicbsp_firmware_list); +}; + +extern const struct aic_chip_ops aic_chip_aic8800d80_ops; + +/* Select the appropriate chip ops based on chip product ID */ +const struct aic_chip_ops *aic_chip_ops_select(u16 chipid); + +/* Wrapper function declarations (implemented in aic_chip_ops.c) */ +int aic_chip_wifi_init(struct aic_sdio_dev *sdiodev, int testmode); +int aic_chip_set_patch_info(struct aic_sdio_dev *sdiodev, + struct aicbt_patch_info_t *patch_info, + struct aicbsp_info_t *aicbsp_info, + struct aicbt_patch_table *head); +int aic_chip_driver_fw_init(struct aic_sdio_dev *sdiodev, u32 *btenable, + struct aicbsp_info_t *aicbsp_info, + const struct aicbsp_firmware **aicbsp_firmware_list); + +#endif /* _AIC_BSP_CHIP_OPS_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_bsp/aicsdio.c b/drivers/net/wireless/aic/aic8800_bsp/aicsdio.c new file mode 100644 index 0000000000000..e17a6b2e6c838 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aicsdio.c @@ -0,0 +1,2027 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief bsp sdio config + * + ****************************************************************************** + */ +#include "aicsdio.h" +#include "aic_bsp_driver.h" +#include "aicsdio_txrxif.h" +#include "aic_bsp_export.h" +#include "aic_chip_ops.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int aicbsp_platform_power_on(void); +static void aicbsp_platform_power_off(void); + +struct aic_sdio_dev *aicbsp_sdiodev; +static struct completion *aicbsp_notify_completion; +static struct completion *aicbsp_probe_completion; + +static const struct sdio_device_id aicbsp_sdmmc_ids[]; +static int bsp_init; +static int bsp_func_init; + +#ifdef CONFIG_M2D_OTA_AUTO_SUPPORT +char saved_sdk_ver[64]; +module_param_string(saved_sdk_ver, saved_sdk_ver, 64, 0660); +#endif + +extern int testmode; + +static int aicbsp_dummy_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + if (func && func->num != 2) + return 0; + + if (aicbsp_notify_completion) + complete(aicbsp_notify_completion); + return 0; +} + +static void aicbsp_dummy_remove(struct sdio_func *func) +{ +} + +static struct sdio_driver aicbsp_dummy_sdmmc_driver = { + .probe = aicbsp_dummy_probe, + .remove = aicbsp_dummy_remove, + .name = "aicbsp_dummy_sdmmc", + .id_table = aicbsp_sdmmc_ids, +}; + +static int aicbsp_reg_sdio_notify(void *semaphore) +{ + aicbsp_notify_completion = semaphore; + return sdio_register_driver(&aicbsp_dummy_sdmmc_driver); +} + +static void aicbsp_unreg_sdio_notify(void) +{ + mdelay(15); + sdio_unregister_driver(&aicbsp_dummy_sdmmc_driver); +} + +static const char *aicbsp_subsys_name(int subsys) +{ + switch (subsys) { + case AIC_BLUETOOTH: + return "AIC_BLUETOOTH"; + case AIC_WIFI: + return "AIC_WIFI"; + default: + return "unknown subsys"; + } +} + +/** + * aicbsp_set_subsys - Change the power request of an AIC subsystem + * @subsys: Subsystem identifier from &enum aicbsp_subsys + * @state: Requested power state from &enum aicbsp_pwr_state + * + * Return: The resulting aggregate power state, or a negative error code. + */ +int aicbsp_set_subsys(int subsys, int state) +{ + static int pre_power_map; + int cur_power_map; + int pre_power_state; + int cur_power_state; + + mutex_lock(&aicbsp_power_lock); + cur_power_map = pre_power_map; + if (state) + cur_power_map |= (1 << subsys); + else + cur_power_map &= ~(1 << subsys); + + pre_power_state = pre_power_map > 0; + cur_power_state = cur_power_map > 0; + + sdio_dbg("%s, subsys: %s, state to: %d\n", __func__, + aicbsp_subsys_name(subsys), state); + + if (cur_power_state != pre_power_state) { + sdio_dbg("%s, power state change to %d dure to %s\n", __func__, + cur_power_state, aicbsp_subsys_name(subsys)); + if (cur_power_state) { + if (aicbsp_platform_power_on() < 0) + goto err0; + if (aicbsp_sdio_init()) + goto err1; + if (aicbsp_driver_fw_init(aicbsp_sdiodev)) + goto err2; +#ifndef CONFIG_FDRV_NO_REG_SDIO + aicbsp_sdio_release(aicbsp_sdiodev); +#endif + } else { + aicbsp_sdio_exit(); + aicbsp_platform_power_off(); + } + } else { + sdio_dbg("%s, power state no need to change, current: %d\n", __func__, + cur_power_state); + } + pre_power_map = cur_power_map; + mutex_unlock(&aicbsp_power_lock); + + return cur_power_state; + +err2: + aicbsp_sdio_release(aicbsp_sdiodev); + aicbsp_sdio_exit(); + +err1: + aicbsp_platform_power_off(); + +err0: + sdio_dbg("%s, fail to set %s power state to %d\n", __func__, + aicbsp_subsys_name(subsys), state); + mutex_unlock(&aicbsp_power_lock); + return -1; +} +EXPORT_SYMBOL_GPL(aicbsp_set_subsys); + +void *aicbsp_get_drvdata(void *args) +{ + (void)args; + if (aicbsp_sdiodev) + return aicbsp_sdiodev->bus_if; + return dev_get_drvdata((const struct device *)args); +} + +#if defined CONFIG_PLATFORM_EXTERNAL && defined CONFIG_CUSTOM_PWF +extern void nxp_pwr_ctrl(int value); +struct mmc_host *g_host; +#endif + +static int aicbsp_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct mmc_host *host; + struct aic_sdio_dev *sdiodev; + struct aicwf_bus *bus_if; + int err = -ENODEV; + u16 chipid; + + bsp_init = 1; + bsp_func_init = 1; + + if (!func) { + sdio_err("%s func is null\n", __func__); + return err; + } + + if (!aicbsp_probe_completion) { + sdio_err("%s bsp_probe_semaphore is null\n", __func__); + return err; + } + + sdio_dbg("%s:%d vid:0x%04X did:0x%04X\n", __func__, func->num, + func->vendor, func->device); + + if (func->num != 2) { + sdio_dbg("%s func num != 2\n", __func__); + return err; + } + + host = func->card->host; + host->caps |= MMC_CAP_NONREMOVABLE; + + func = func->card->sdio_func[1 - 1]; // replace 2 with 1 + + sdio_dbg("%s after replace:%d\n", __func__, func->num); + + bus_if = + kzalloc_obj(*bus_if, GFP_KERNEL); + if (!bus_if) { + sdio_err("alloc bus fail\n"); + return -ENOMEM; + } + + sdiodev = + kzalloc_obj(*sdiodev, GFP_KERNEL); + if (!sdiodev) { + sdio_err("alloc sdiodev fail\n"); + kfree(bus_if); + return -ENOMEM; + } + aicbsp_sdiodev = sdiodev; + + err = aicwf_sdio_chipmatch(func->vendor, func->device, &chipid); + if (err) { + sdio_err("unsupported SDIO device %04x:%04x\n", + func->vendor, func->device); + goto fail; + } + sdiodev->chipid = chipid; + sdiodev->chip_ops = aic_chip_ops_select(sdiodev->chipid); + if (!sdiodev->chip_ops) { + sdio_err("unsupported AIC chip id %u\n", sdiodev->chipid); + err = -ENODEV; + goto fail; + } + + sdiodev->func = func; + if (sdiodev->chip_ops->use_func_msg) + sdiodev->func_msg = func->card->sdio_func[1]; + sdiodev->bus_if = bus_if; + bus_if->bus_priv.sdio = sdiodev; + + if (sdiodev->chip_ops->use_func_msg) { + dev_set_drvdata(&sdiodev->func_msg->dev, bus_if); + pr_info("aicbsp the device is PRODUCT_ID_AIC8800DC\n"); + } + dev_set_drvdata(&func->dev, bus_if); + sdiodev->dev = &func->dev; + + if (!sdiodev->chip_ops->use_sdiov3_func) + err = aicwf_sdio_func_init(sdiodev); + else + err = aicwf_sdiov3_func_init(sdiodev); + + if (err < 0) { + sdio_err("sdio func init fail\n"); + goto fail; + } + + if (!aicwf_sdio_bus_init(sdiodev)) { + sdio_err("sdio bus init err\r\n"); + goto fail; + } + +#if defined CONFIG_PLATFORM_EXTERNAL && defined CONFIG_CUSTOM_PWF + g_host = host; +#endif + + aicbsp_platform_init(sdiodev); + + complete(aicbsp_probe_completion); + + return 0; +fail: + aicwf_sdio_func_deinit(sdiodev); + dev_set_drvdata(&func->dev, NULL); + if (aicbsp_sdiodev == sdiodev) + aicbsp_sdiodev = NULL; + kfree(sdiodev); + kfree(bus_if); + return err; +} + +static void aicbsp_sdio_remove(struct sdio_func *func) +{ + struct mmc_host *host; + struct aicwf_bus *bus_if = NULL; + struct aic_sdio_dev *sdiodev = NULL; + + pr_info("aicbsp %s\n", __func__); + + if (!aicbsp_sdiodev) { + AICWFDBG(LOGERROR, "%s: allready unregister\n", __func__); + goto done; + } + if (!func) { + AICWFDBG(LOGERROR, "%s, sdio func is null\n", __func__); + goto done; + } + + host = func->card->host; + host->caps &= ~MMC_CAP_NONREMOVABLE; + + bus_if = aicbsp_get_drvdata(&func->dev); + + if (!bus_if) { + AICWFDBG(LOGERROR, "%s bus_if is NULL \r\n", __func__); + goto done; + } + + sdiodev = bus_if->bus_priv.sdio; + if (!sdiodev) { + AICWFDBG(LOGERROR, "%s sdiodev is NULL \r\n", __func__); + goto done; + } + + aicwf_sdio_release(sdiodev); + aicwf_sdio_func_deinit(sdiodev); + + if (bsp_init != 0 && bsp_func_init != 0) { + dev_set_drvdata(&sdiodev->func->dev, NULL); + kfree(sdiodev); + } + +done: + if (bsp_init != 0 && bsp_func_init != 0 && bus_if) + kfree(bus_if); + aicbsp_sdiodev = NULL; + aicbsp_probe_completion = NULL; +} + +static int aicbsp_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + int err; + mmc_pm_flag_t sdio_flags; + + //sdio_dbg("%s, func->num = %d\n", __func__, func->num); + if (func->num != 2) + return 0; + + sdio_flags = sdio_get_host_pm_caps(func); + if (!(sdio_flags & MMC_PM_KEEP_POWER)) { + sdio_dbg("%s: can't keep power while host is suspended\n", __func__); + return -EINVAL; + } + + /* keep power while host suspended */ + err = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (err) { + sdio_dbg("%s: error while trying to keep power\n", __func__); + return err; + } + + return 0; +} + +static int aicbsp_sdio_resume(struct device *dev) +{ + return 0; +} + +static const struct sdio_device_id aicbsp_sdmmc_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_AIC8800D80, + SDIO_DEVICE_ID_AIC8800D80_FUNC2) }, + { } +}; + +MODULE_DEVICE_TABLE(sdio, aicbsp_sdmmc_ids); + +static const struct dev_pm_ops aicbsp_sdio_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(aicbsp_sdio_suspend, aicbsp_sdio_resume)}; + +static struct sdio_driver aicbsp_sdio_driver = { + .probe = aicbsp_sdio_probe, + .remove = aicbsp_sdio_remove, + .name = AICBSP_SDIO_NAME, + .id_table = aicbsp_sdmmc_ids, + .drv = { + .pm = &aicbsp_sdio_pm_ops, + }, +}; + +static int aicbsp_platform_power_on(void) +{ + int ret = 0; + struct completion aic_chipup_comp; + +#if defined CONFIG_PLATFORM_EXTERNAL && defined CONFIG_CUSTOM_PWF + if (g_host) { + nxp_pwr_ctrl(0); + mdelay(10); + nxp_pwr_ctrl(1); + mdelay(50); + sdio_dbg("mmc detect change\n"); + mmc_detect_change(g_host, 0); + } +#endif + + init_completion(&aic_chipup_comp); + ret = aicbsp_reg_sdio_notify(&aic_chipup_comp); + if (ret) { + sdio_dbg("%s aicbsp_reg_sdio_notify fail(%d)\n", __func__, ret); + return ret; + } + + if (wait_for_completion_timeout(&aic_chipup_comp, msecs_to_jiffies(2000))) { + aicbsp_unreg_sdio_notify(); + return 0; + } + + aicbsp_unreg_sdio_notify(); + + return -1; +} + +static void aicbsp_platform_power_off(void) +{ +// TODO wifi disable and sdio card detection + +#if defined CONFIG_PLATFORM_EXTERNAL && defined CONFIG_CUSTOM_PWF + nxp_pwr_ctrl(0); +#endif +} + +int aicbsp_sdio_init(void) +{ + struct completion aic_chipup_comp; + + init_completion(&aic_chipup_comp); + aicbsp_probe_completion = &aic_chipup_comp; + + if (sdio_register_driver(&aicbsp_sdio_driver)) + return -1; + + if (wait_for_completion_timeout(aicbsp_probe_completion, msecs_to_jiffies(2000)) == 0) { + pr_err("aicbsp %s aicbsp_sdio_probe fail\r\n", __func__); + return -1; + } + + return 0; +} + +void aicbsp_sdio_exit(void) +{ + sdio_unregister_driver(&aicbsp_sdio_driver); +} + +void aicbsp_sdio_release(struct aic_sdio_dev *sdiodev) +{ + if (!sdiodev->func) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return; + } + + sdiodev->bus_if->state = BUS_DOWN_ST; + sdio_claim_host(sdiodev->func); + sdio_release_irq(sdiodev->func); + sdio_release_host(sdiodev->func); + if (sdiodev->chip_ops->use_func_msg) { + sdio_claim_host(sdiodev->func_msg); + sdio_release_irq(sdiodev->func_msg); + sdio_release_host(sdiodev->func_msg); + } +} + +int aicwf_sdio_readb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 *val) +{ + int ret; + + if (!sdiodev->func) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return 0; + } + + sdio_claim_host(sdiodev->func); + *val = sdio_readb(sdiodev->func, regaddr, &ret); + sdio_release_host(sdiodev->func); + return ret; +} + +int aicwf_sdio_readb_func2(struct aic_sdio_dev *sdiodev, uint regaddr, u8 *val) +{ + int ret; + + if (!sdiodev->func_msg) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return 0; + } + + sdio_claim_host(sdiodev->func_msg); + *val = sdio_readb(sdiodev->func_msg, regaddr, &ret); + sdio_release_host(sdiodev->func_msg); + return ret; +} + +int aicwf_sdio_writeb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val) +{ + int ret; + + if (!sdiodev->func) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return 0; + } + + sdio_claim_host(sdiodev->func); + sdio_writeb(sdiodev->func, val, regaddr, &ret); + sdio_release_host(sdiodev->func); + return ret; +} + +int aicwf_sdio_writeb_func2(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val) +{ + int ret; + + if (!sdiodev->func_msg) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return 0; + } + sdio_claim_host(sdiodev->func_msg); + sdio_writeb(sdiodev->func_msg, val, regaddr, &ret); + sdio_release_host(sdiodev->func_msg); + return ret; +} + +int aicwf_sdio_flow_ctrl(struct aic_sdio_dev *sdiodev) +{ + int ret = -1; + u8 fc_reg = 0; + u32 count = 0; + + while (true) { + ret = + aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.flow_ctrl_reg, &fc_reg); + if (ret) + return -1; + + if (sdiodev->chip_ops->need_flowctrl_mask) + fc_reg &= SDIOWIFI_FLOWCTRL_MASK_REG; + + if (fc_reg != 0) { + ret = fc_reg; + return ret; + } + if (count >= FLOW_CTRL_RETRY_COUNT) { + ret = -fc_reg; + break; + } + count++; + if (count < 30) + usleep_range(30, 50); + else if (count < 40) + mdelay(1); + else + mdelay(10); + } + + return ret; +} + +int aicwf_sdio_send_msg(struct aic_sdio_dev *sdiodev, u8 *buf, uint count) +{ + int ret = 0; + + if (!sdiodev->func_msg) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return 0; + } + sdio_claim_host(sdiodev->func_msg); + ret = sdio_writesb(sdiodev->func_msg, 7, buf, count); + sdio_release_host(sdiodev->func_msg); + + return ret; +} + +int aicwf_sdio_send_pkt(struct aic_sdio_dev *sdiodev, u8 *buf, uint count) +{ + int ret = 0; + + if (!sdiodev->func) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return 0; + } + + sdio_claim_host(sdiodev->func); + ret = + sdio_writesb(sdiodev->func, sdiodev->sdio_reg.wr_fifo_addr, buf, count); + sdio_release_host(sdiodev->func); + + return ret; +} + +int aicwf_sdio_recv_pkt(struct aic_sdio_dev *sdiodev, struct sk_buff *skbbuf, + u32 size, u8 msg) +{ + int ret; + + if (!skbbuf || !size) + return -EINVAL; + + if (!msg) { + if (!sdiodev->func) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return 0; + } + sdio_claim_host(sdiodev->func); + ret = sdio_readsb(sdiodev->func, skbbuf->data, + sdiodev->sdio_reg.rd_fifo_addr, size); + sdio_release_host(sdiodev->func); + } else { + if (!sdiodev->func_msg) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return 0; + } + sdio_claim_host(sdiodev->func_msg); + ret = sdio_readsb(sdiodev->func_msg, skbbuf->data, + sdiodev->sdio_reg.rd_fifo_addr, size); + sdio_release_host(sdiodev->func_msg); + } + + if (ret < 0) + return ret; + + skbbuf->len = size; + + return ret; +} + +#if defined(CONFIG_SDIO_PWRCTRL) +int aicwf_sdio_wakeup(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; + int read_retry; + int write_retry = 20; + int wakeup_reg_val = 0; + + wakeup_reg_val = sdiodev->chip_ops->wakeup_reg_val; + + if (sdiodev->state == SDIO_SLEEP_ST) { + down(&sdiodev->pwrctl_wakeup_sema); + while (write_retry) { + ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, + wakeup_reg_val); + if (ret) { + txrx_err("sdio wakeup fail\n"); + ret = -1; + } else { + read_retry = 10; + while (read_retry) { + u8 val; + + ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.sleep_reg, + &val); + if (ret == 0 && val & 0x10) + break; + + read_retry--; + usleep_range(30, 50); + } + if (read_retry != 0) + break; + } + sdio_dbg("write retry: %d\n", write_retry); + write_retry--; + usleep_range(30, 50); + } + up(&sdiodev->pwrctl_wakeup_sema); + } + + sdiodev->state = SDIO_ACTIVE_ST; + aicwf_sdio_pwrctl_timer(sdiodev, sdiodev->active_duration); + + return ret; +} + +int aicwf_sdio_sleep_allow(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; + struct aicwf_bus *bus_if = sdiodev->bus_if; + + if (bus_if->state == BUS_DOWN_ST) { + ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.sleep_reg, 0x10); + if (ret) + sdio_err("Write sleep fail!\n"); + + aicwf_sdio_pwrctl_timer(sdiodev, 0); + return ret; + } + + if (sdiodev->state == SDIO_ACTIVE_ST) { + { + sdio_dbg("s\n"); + ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.sleep_reg, 0x10); + if (ret) + sdio_err("Write sleep fail!\n"); + } + sdiodev->state = SDIO_SLEEP_ST; + aicwf_sdio_pwrctl_timer(sdiodev, 0); + } else { + aicwf_sdio_pwrctl_timer(sdiodev, sdiodev->active_duration); + } + + return ret; +} + +int aicwf_sdio_pwr_stctl(struct aic_sdio_dev *sdiodev, uint target) +{ + int ret = 0; + + if (sdiodev->bus_if->state == BUS_DOWN_ST) + return -1; + + if (sdiodev->state == target) { + if (target == SDIO_ACTIVE_ST) + aicwf_sdio_pwrctl_timer(sdiodev, sdiodev->active_duration); + + return ret; + } + + switch (target) { + case SDIO_ACTIVE_ST: + aicwf_sdio_wakeup(sdiodev); + break; + case SDIO_SLEEP_ST: + aicwf_sdio_sleep_allow(sdiodev); + break; + } + + return ret; +} +#endif + +int aicwf_sdio_txpkt(struct aic_sdio_dev *sdiodev, struct sk_buff *pkt) +{ + int ret = 0; + u32 len = 0; + struct aicwf_bus *bus_if = dev_get_drvdata(sdiodev->dev); + + if (bus_if->state == BUS_DOWN_ST) { + sdio_dbg("tx bus is down!\n"); + return -EINVAL; + } + + len = pkt->len; + len = (len + SDIOWIFI_FUNC_BLOCKSIZE - 1) / SDIOWIFI_FUNC_BLOCKSIZE * + SDIOWIFI_FUNC_BLOCKSIZE; + ret = aicwf_sdio_send_pkt(sdiodev, pkt->data, len); + if (ret) + sdio_err("aicwf_sdio_send_pkt fail%d\n", ret); + + return ret; +} + +static int aicwf_sdio_intr_get_len_bytemode(struct aic_sdio_dev *sdiodev, + u8 *byte_len) +{ + int ret = 0; + + if (!byte_len) + return -EBADE; + + if (sdiodev->bus_if->state == BUS_DOWN_ST) { + *byte_len = 0; + } else { + ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.bytemode_len_reg, + byte_len); + sdiodev->rx_priv->data_len = (*byte_len) * 4; + } + + return ret; +} + +static void aicwf_sdio_bus_stop(struct device *dev) +{ + struct aicwf_bus *bus_if = aicbsp_get_drvdata(dev); + struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + int ret; + +#if defined(CONFIG_SDIO_PWRCTRL) + aicwf_sdio_pwrctl_timer(sdiodev, 0); + if (sdiodev->pwrctl_tsk) { + complete_all(&sdiodev->pwrctrl_trgg); + kthread_stop(sdiodev->pwrctl_tsk); + sdiodev->pwrctl_tsk = NULL; + } +#endif + bus_if->state = BUS_DOWN_ST; + ret = down_interruptible(&sdiodev->tx_priv->txctl_sema); + if (ret) + sdio_err("down txctl_sema fail\n"); + +#if defined(CONFIG_SDIO_PWRCTRL) + aicwf_sdio_pwr_stctl(sdiodev, SDIO_SLEEP_ST); +#endif + if (!ret) + up(&sdiodev->tx_priv->txctl_sema); + aicwf_frame_queue_flush(&sdiodev->tx_priv->txq); +} + +struct sk_buff *aicwf_sdio_readframes(struct aic_sdio_dev *sdiodev, u8 msg) +{ + int ret = 0; + u32 size = 0; + struct sk_buff *skb = NULL; + struct aicwf_bus *bus_if = dev_get_drvdata(sdiodev->dev); + + if (bus_if->state == BUS_DOWN_ST) { + sdio_dbg("bus down\n"); + return NULL; + } + + size = sdiodev->rx_priv->data_len; + skb = __dev_alloc_skb(size, GFP_KERNEL); + if (!skb) + return NULL; + + ret = aicwf_sdio_recv_pkt(sdiodev, skb, size, msg); + if (ret) { + dev_kfree_skb(skb); + skb = NULL; + } + + return skb; +} + +static int aicwf_sdio_tx_msg(struct aic_sdio_dev *sdiodev) +{ + int err = 0; + u16 len; + u8 *payload = sdiodev->tx_priv->cmd_buf; + u16 payload_len = sdiodev->tx_priv->cmd_len; + u8 adjust_str[4] = {0, 0, 0, 0}; + int adjust_len = 0; + int buffer_cnt = 0; + u8 retry = 0; + + len = payload_len; + if ((len % TX_ALIGNMENT) != 0) { + adjust_len = roundup(len, TX_ALIGNMENT); + memcpy(payload + payload_len, adjust_str, (adjust_len - len)); + payload_len += (adjust_len - len); + } + len = payload_len; + + // link tail is necessary + if ((len % SDIOWIFI_FUNC_BLOCKSIZE) != 0) { + memset(payload + payload_len, 0, TAIL_LEN); + payload_len += TAIL_LEN; + len = (payload_len / SDIOWIFI_FUNC_BLOCKSIZE + 1) * + SDIOWIFI_FUNC_BLOCKSIZE; + } else { + len = payload_len; + } + + if (sdiodev->chip_ops->use_flowctrl_msg) { + buffer_cnt = aicwf_sdio_flow_ctrl(sdiodev); + while ((buffer_cnt <= 0 || + (buffer_cnt > 0 && len > (buffer_cnt * BUFFER_SIZE))) && + retry < 10) { + retry++; + buffer_cnt = aicwf_sdio_flow_ctrl(sdiodev); + pr_info("aicbsp buffer_cnt = %d\n", buffer_cnt); + } + } + down(&sdiodev->tx_priv->cmd_txsema); + + if (sdiodev->chip_ops->use_flowctrl_msg) { + if (buffer_cnt > 0 && len < (buffer_cnt * BUFFER_SIZE)) { + err = aicwf_sdio_send_pkt(sdiodev, payload, len); + if (err) + sdio_err("aicwf_sdio_send_pkt fail%d\n", err); + + } else { + sdio_err("tx msg fc retry fail:%d, %d\n", buffer_cnt, len); + up(&sdiodev->tx_priv->cmd_txsema); + return -1; + } + } else { + err = aicwf_sdio_send_msg(sdiodev, payload, len); + if (err) + sdio_err("aicwf_sdio_send_pkt fail%d\n", err); + } + + sdiodev->tx_priv->cmd_txstate = false; + if (!err) + sdiodev->tx_priv->cmd_tx_succ = true; + else + sdiodev->tx_priv->cmd_tx_succ = false; + + up(&sdiodev->tx_priv->cmd_txsema); + + return err; +} + +static void aicwf_sdio_tx_process(struct aic_sdio_dev *sdiodev) +{ + if (sdiodev->bus_if->state == BUS_DOWN_ST) { + sdio_err("Bus is down\n"); + return; + } + +#if defined(CONFIG_SDIO_PWRCTRL) + aicwf_sdio_pwr_stctl(sdiodev, SDIO_ACTIVE_ST); +#endif + + // config + sdio_info("send cmd\n"); + if (sdiodev->tx_priv->cmd_txstate) { + if (down_interruptible(&sdiodev->tx_priv->txctl_sema)) { + txrx_err("txctl down bus->txctl_sema fail\n"); + return; + } + if (sdiodev->state != SDIO_ACTIVE_ST) { + txrx_err("state err\n"); + up(&sdiodev->tx_priv->txctl_sema); + txrx_err("txctl up bus->txctl_sema fail\n"); + return; + } + + if (aicwf_sdio_tx_msg(sdiodev)) + sdio_err("failed to send command\n"); + up(&sdiodev->tx_priv->txctl_sema); + /* to prevent race conditions with wake-up operations */ + if (waitqueue_active(&sdiodev->tx_priv->cmd_txdone_wait)) + wake_up(&sdiodev->tx_priv->cmd_txdone_wait); + } + + // data + sdio_info("send data\n"); + if (down_interruptible(&sdiodev->tx_priv->txctl_sema)) { + txrx_err("txdata down bus->txctl_sema\n"); + return; + } + + if (sdiodev->state != SDIO_ACTIVE_ST) { + txrx_err("sdio state err\n"); + up(&sdiodev->tx_priv->txctl_sema); + return; + } + + if (!aicwf_is_framequeue_empty(&sdiodev->tx_priv->txq)) + sdiodev->tx_priv->fw_avail_bufcnt = aicwf_sdio_flow_ctrl(sdiodev); + while (!aicwf_is_framequeue_empty(&sdiodev->tx_priv->txq)) { + aicwf_sdio_send(sdiodev->tx_priv); + if (sdiodev->tx_priv->cmd_txstate) + break; + } + + up(&sdiodev->tx_priv->txctl_sema); +} + +static int aicwf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) +{ + uint prio; + int ret = -EBADE; + struct aicwf_bus *bus_if = dev_get_drvdata(dev); + struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + + prio = (pkt->priority & 0x7); + spin_lock_bh(&sdiodev->tx_priv->txqlock); + if (!aicwf_frame_enq(sdiodev->dev, &sdiodev->tx_priv->txq, pkt, prio)) { + spin_unlock_bh(&sdiodev->tx_priv->txqlock); + return -ENOSR; + } + ret = 0; + + if (bus_if->state != BUS_UP_ST) { + sdio_err("bus_if stopped\n"); + spin_unlock_bh(&sdiodev->tx_priv->txqlock); + return -1; + } + + atomic_inc(&sdiodev->tx_priv->tx_pktcnt); + spin_unlock_bh(&sdiodev->tx_priv->txqlock); + complete(&bus_if->bustx_trgg); + + return ret; +} + +static int aicwf_sdio_bus_txmsg(struct device *dev, u8 *msg, uint msglen) +{ + int ret = -1; + struct aicwf_bus *bus_if = dev_get_drvdata(dev); + struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + + down(&sdiodev->tx_priv->cmd_txsema); + sdiodev->tx_priv->cmd_txstate = true; + sdiodev->tx_priv->cmd_tx_succ = false; + sdiodev->tx_priv->cmd_buf = msg; + sdiodev->tx_priv->cmd_len = msglen; + up(&sdiodev->tx_priv->cmd_txsema); + + if (bus_if->state != BUS_UP_ST) { + sdio_err("bus has stop\n"); + return -1; + } + + complete(&bus_if->bustx_trgg); + + if (sdiodev->tx_priv->cmd_txstate) { + int timeout = msecs_to_jiffies(CMD_TX_TIMEOUT); + + ret = wait_event_timeout(sdiodev->tx_priv->cmd_txdone_wait, + !(sdiodev->tx_priv->cmd_txstate), timeout); + } + + if (!sdiodev->tx_priv->cmd_txstate && sdiodev->tx_priv->cmd_tx_succ) { + ret = 0; + } else { + sdio_err("send failed:%d, %d,%x\n", sdiodev->tx_priv->cmd_txstate, + sdiodev->tx_priv->cmd_tx_succ, ret); + ret = -EIO; + } + + return ret; +} + +int aicwf_sdio_send(struct aicwf_tx_priv *tx_priv) +{ + struct sk_buff *pkt; + struct aic_sdio_dev *sdiodev = tx_priv->sdiodev; + u16 aggr_len = 0; + int retry_times = 0; + int max_retry_times = 5; + + aggr_len = (tx_priv->tail - tx_priv->head); + if (((atomic_read(&tx_priv->aggr_count) == 0) && aggr_len != 0) || + ((atomic_read(&tx_priv->aggr_count) != 0) && aggr_len == 0)) { + if (aggr_len > 0) + aicwf_sdio_aggrbuf_reset(tx_priv); + goto done; + } + + if (tx_priv->fw_avail_bufcnt <= 0) { // flow control failed + tx_priv->fw_avail_bufcnt = aicwf_sdio_flow_ctrl(sdiodev); + while (tx_priv->fw_avail_bufcnt <= 0 && retry_times < max_retry_times) { + retry_times++; + tx_priv->fw_avail_bufcnt = aicwf_sdio_flow_ctrl(sdiodev); + } + if (tx_priv->fw_avail_bufcnt <= 0) { + sdio_err("fc retry %d fail\n", tx_priv->fw_avail_bufcnt); + goto done; + } + } + + if (atomic_read(&tx_priv->aggr_count) == tx_priv->fw_avail_bufcnt) { + if (atomic_read(&tx_priv->aggr_count) > 0) { + tx_priv->fw_avail_bufcnt -= atomic_read(&tx_priv->aggr_count); + aicwf_sdio_aggr_send(tx_priv); // send and check the next pkt; + } + } else { + spin_lock_bh(&sdiodev->tx_priv->txqlock); + pkt = aicwf_frame_dequeue(&sdiodev->tx_priv->txq); + if (!pkt) { + sdio_err("txq no pkt\n"); + spin_unlock_bh(&sdiodev->tx_priv->txqlock); + goto done; + } + atomic_dec(&sdiodev->tx_priv->tx_pktcnt); + spin_unlock_bh(&sdiodev->tx_priv->txqlock); + + if (!tx_priv || !tx_priv->tail || !pkt) + txrx_err("null error\n"); + if (aicwf_sdio_aggr(tx_priv, pkt)) { + aicwf_sdio_aggrbuf_reset(tx_priv); + sdio_err("add aggr pkts failed!\n"); + goto done; + } + + // when aggr finish or there is cmd to send, just send this aggr pkt to + // fw + if ((int)atomic_read(&sdiodev->tx_priv->tx_pktcnt) == 0 || + sdiodev->tx_priv->cmd_txstate) { // no more pkt send it! + tx_priv->fw_avail_bufcnt -= atomic_read(&tx_priv->aggr_count); + aicwf_sdio_aggr_send(tx_priv); + } else { + goto done; + } + } + +done: + return 0; +} + +int aicwf_sdio_aggr(struct aicwf_tx_priv *tx_priv, struct sk_buff *pkt) +{ + // struct rwnx_txhdr *txhdr = (struct rwnx_txhdr *)pkt->data; + u8 *start_ptr = tx_priv->tail; + u8 sdio_header[4]; + u8 adjust_str[4] = {0, 0, 0, 0}; + u16 curr_len = 0; + int allign_len = 0; + + sdio_header[2] = 0x01; // data + if (aicbsp_sdiodev->chip_ops->use_hdr_checksum) + sdio_header[3] = crc8_ponl_107(&sdio_header[0], 3); // crc8 + else + sdio_header[3] = 0; // reserved + + memcpy(tx_priv->tail, (u8 *)&sdio_header, sizeof(sdio_header)); + tx_priv->tail += sizeof(sdio_header); + + // word alignment + curr_len = tx_priv->tail - tx_priv->head; + if (curr_len & (TX_ALIGNMENT - 1)) { + allign_len = roundup(curr_len, TX_ALIGNMENT) - curr_len; + memcpy(tx_priv->tail, adjust_str, allign_len); + tx_priv->tail += allign_len; + } + + if (aicbsp_sdiodev->chip_ops->need_fix_hdr_len) { + start_ptr[0] = ((tx_priv->tail - start_ptr - 4) & 0xff); + start_ptr[1] = (((tx_priv->tail - start_ptr - 4) >> 8) & 0x0f); + } + + tx_priv->aggr_buf->dev = pkt->dev; + + consume_skb(pkt); + atomic_inc(&tx_priv->aggr_count); + return 0; +} + +void aicwf_sdio_aggr_send(struct aicwf_tx_priv *tx_priv) +{ + struct sk_buff *tx_buf = tx_priv->aggr_buf; + int ret = 0; + int curr_len = 0; + + // link tail is necessary + curr_len = tx_priv->tail - tx_priv->head; + if ((curr_len % TXPKT_BLOCKSIZE) != 0) { + memset(tx_priv->tail, 0, TAIL_LEN); + tx_priv->tail += TAIL_LEN; + } + + tx_buf->len = tx_priv->tail - tx_priv->head; + ret = aicwf_sdio_txpkt(tx_priv->sdiodev, tx_buf); + if (ret < 0) + sdio_err("fail to send aggr pkt!\n"); + + aicwf_sdio_aggrbuf_reset(tx_priv); +} + +void aicwf_sdio_aggrbuf_reset(struct aicwf_tx_priv *tx_priv) +{ + struct sk_buff *aggr_buf = tx_priv->aggr_buf; + + tx_priv->tail = tx_priv->head; + aggr_buf->len = 0; + atomic_set(&tx_priv->aggr_count, 0); +} + +static int aicwf_sdio_bus_start(struct device *dev) +{ + struct aicwf_bus *bus_if = dev_get_drvdata(dev); + struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + int ret = 0; + + if (!sdiodev->func) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return 0; + } + + if (sdiodev->chip_ops->use_func_msg) { + sdio_claim_host(sdiodev->func); + + // since we have func2 we don't register irq handler + sdio_claim_irq(sdiodev->func, NULL); + sdio_claim_irq(sdiodev->func_msg, NULL); + + sdiodev->func->irq_handler = + (sdio_irq_handler_t *)aicwf_sdio_hal_irqhandler; + sdiodev->func_msg->irq_handler = + (sdio_irq_handler_t *)aicwf_sdio_hal_irqhandler_func2; + sdio_release_host(sdiodev->func); + + // enable sdio interrupt + ret = + aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x07); + + if (ret != 0) + sdio_err("intr register failed:%d\n", ret); + + // enable sdio interrupt + ret = aicwf_sdio_writeb_func2(sdiodev, + sdiodev->sdio_reg.intr_config_reg, 0x07); + + if (ret != 0) + sdio_err("func2 intr register failed:%d\n", ret); + } else { + sdio_claim_host(sdiodev->func); + sdio_claim_irq(sdiodev->func, aicwf_sdio_hal_irqhandler); + + if (sdiodev->chip_ops->need_func0_intr) { + sdio_f0_writeb(sdiodev->func, 0x07, 0x04, &ret); + if (ret) + sdio_err("set func0 int en fail %d\n", ret); + } + sdio_release_host(sdiodev->func); + // enable sdio interrupt + ret = + aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x07); + if (ret != 0) + sdio_err("intr register failed:%d\n", ret); + } + + bus_if->state = BUS_UP_ST; + return ret; +} + +int aicwf_sdio_bustx_thread(void *data) +{ + struct aicwf_bus *bus = (struct aicwf_bus *)data; + struct aic_sdio_dev *sdiodev = bus->bus_priv.sdio; + + while (1) { + if (kthread_should_stop()) { + sdio_dbg("sdio bustx thread stop\n"); + break; + } + if (!wait_for_completion_interruptible(&bus->bustx_trgg)) { + if ((int)(atomic_read(&sdiodev->tx_priv->tx_pktcnt) > 0) || + sdiodev->tx_priv->cmd_txstate) + aicwf_sdio_tx_process(sdiodev); + } + } + + return 0; +} + +int aicwf_sdio_busrx_thread(void *data) +{ + struct aicwf_rx_priv *rx_priv = (struct aicwf_rx_priv *)data; + struct aicwf_bus *bus_if = rx_priv->sdiodev->bus_if; + + while (1) { + if (kthread_should_stop()) { + sdio_dbg("sdio busrx thread stop\n"); + break; + } + if (!wait_for_completion_interruptible(&bus_if->busrx_trgg)) + aicwf_process_rxframes(rx_priv); + } + + return 0; +} + +#if defined(CONFIG_SDIO_PWRCTRL) +static void aicwf_sdio_bus_pwrctl(struct timer_list *t) +{ + //struct aic_sdio_dev *sdiodev = from_timer(sdiodev, t, timer); + struct aic_sdio_dev *sdiodev = timer_container_of(sdiodev, t, timer); + + if (sdiodev->bus_if->state == BUS_DOWN_ST) { + sdio_info("bus down\n"); + return; + } + + if (sdiodev->pwrctl_tsk) + complete(&sdiodev->pwrctrl_trgg); +} +#endif + +static void aicwf_sdio_enq_rxpkt(struct aic_sdio_dev *sdiodev, + struct sk_buff *pkt) +{ + struct aicwf_rx_priv *rx_priv = sdiodev->rx_priv; + unsigned long flags = 0; + + spin_lock_irqsave(&rx_priv->rxqlock, flags); + if (!aicwf_rxframe_enqueue(sdiodev->dev, &rx_priv->rxq, pkt)) { + spin_unlock_irqrestore(&rx_priv->rxqlock, flags); + aicwf_dev_skb_free(pkt); + return; + } + spin_unlock_irqrestore(&rx_priv->rxqlock, flags); + + atomic_inc(&rx_priv->rx_cnt); +} + +#define SDIO_OTHER_INTERRUPT (0x1ul << 7) + +void aicwf_sdio_hal_irqhandler(struct sdio_func *func) +{ + struct aicwf_bus *bus_if = NULL; + struct aic_sdio_dev *sdiodev = NULL; + u8 intstatus = 0; + u8 byte_len = 0; + struct sk_buff *pkt = NULL; + int ret; + + if (!func) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return; + } + bus_if = dev_get_drvdata(&func->dev); + sdiodev = bus_if->bus_priv.sdio; + + if (aicbsp_sdiodev->sdio_hal_irqhandler) { + aicbsp_sdiodev->sdio_hal_irqhandler(func); + return; + } + + if (!bus_if || bus_if->state == BUS_DOWN_ST) { + sdio_err("bus err\n"); + return; + } + + if (aicbsp_sdiodev->chip_ops->use_func2) { + ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.block_cnt_reg, + &intstatus); + + while (intstatus) { + sdiodev->rx_priv->data_len = intstatus * SDIOWIFI_FUNC_BLOCKSIZE; + if (intstatus > 0) { + if (intstatus < 64) { + pkt = aicwf_sdio_readframes(sdiodev, 0); + } else { + // byte_len must<= 128 + aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len); + sdio_info("byte mode len=%d\r\n", byte_len); + pkt = aicwf_sdio_readframes(sdiodev, 0); + } + } else { + sdio_err("Interrupt but no data\n"); + } + + if (pkt) + aicwf_sdio_enq_rxpkt(sdiodev, pkt); + + ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.block_cnt_reg, + &intstatus); + } + } else { + do { + ret = aicwf_sdio_readb(sdiodev, + sdiodev->sdio_reg.misc_int_status_reg, + &intstatus); + if (!ret) + break; + sdio_err("ret=%d, intstatus=%x\r\n", ret, intstatus); + } while (1); + if (intstatus & SDIO_OTHER_INTERRUPT) { + u8 int_pending; + + ret = aicwf_sdio_readb(sdiodev, sdiodev->sdio_reg.sleep_reg, + &int_pending); + if (ret < 0) + sdio_err("reg:%d read failed!\n", sdiodev->sdio_reg.sleep_reg); + int_pending &= ~0x01; // dev to host soft irq + ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.sleep_reg, + int_pending); + if (ret < 0) + sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.sleep_reg); + } + + if (intstatus > 0) { + u8 intmaskf2 = intstatus | (0x1UL << 3); + + if (intmaskf2 > 120U) { // func2 + if (intmaskf2 == 127U) { // byte mode + // byte_len must<= 128 + aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len); + sdio_info("byte mode len=%d\r\n", byte_len); + pkt = aicwf_sdio_readframes(sdiodev, 1); + } else { // block mode + sdiodev->rx_priv->data_len = + (intstatus & 0x7U) * SDIOWIFI_FUNC_BLOCKSIZE; + pkt = aicwf_sdio_readframes(sdiodev, 1); + } + } else { // func1 + if (intstatus == 120U) { // byte mode + // byte_len must<= 128 + aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len); + sdio_info("byte mode len=%d\r\n", byte_len); + pkt = aicwf_sdio_readframes(sdiodev, 0); + } else { // block mode + sdiodev->rx_priv->data_len = + (intstatus & 0x7FU) * SDIOWIFI_FUNC_BLOCKSIZE; + pkt = aicwf_sdio_readframes(sdiodev, 0); + } + } + } else { + sdio_err("Interrupt but no data\n"); + } + + if (pkt) + aicwf_sdio_enq_rxpkt(sdiodev, pkt); + } + + complete(&bus_if->busrx_trgg); +} + +void aicwf_sdio_hal_irqhandler_func2(struct sdio_func *func) +{ + struct aicwf_bus *bus_if = dev_get_drvdata(&func->dev); + struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + u8 intstatus = 0; + u8 byte_len = 0; +#ifdef CONFIG_PREALLOC_RX_SKB + struct rx_buff *pkt = NULL; +#else + struct sk_buff *pkt = NULL; +#endif + int ret; + + if (!bus_if || bus_if->state == BUS_DOWN_ST) { + sdio_err("bus err\n"); + return; + } + +#ifdef CONFIG_PREALLOC_RX_SKB + if (list_empty(&aic_rx_buff_list.rxbuff_list)) { + pr_warn("aicbsp %s %d, rxbuff list is empty\n", __func__, __LINE__); + return; + } +#endif + + ret = aicwf_sdio_readb_func2(sdiodev, sdiodev->sdio_reg.block_cnt_reg, + &intstatus); + if (ret) { + sdio_err("failed to read interrupt status: %d\n", ret); + return; + } + + while (intstatus) { + sdiodev->rx_priv->data_len = intstatus * SDIOWIFI_FUNC_BLOCKSIZE; + if (intstatus > 0) { + if (intstatus < 64) { + pkt = aicwf_sdio_readframes(sdiodev, 1); + } else { + sdio_info("byte mode len=%d\r\n", byte_len); + // byte_len must<= 128 + aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len); + pkt = aicwf_sdio_readframes(sdiodev, 1); + } + } else { + sdio_err("Interrupt but no data\n"); + } + + if (pkt) + aicwf_sdio_enq_rxpkt(sdiodev, pkt); + + ret = aicwf_sdio_readb_func2(sdiodev, sdiodev->sdio_reg.block_cnt_reg, + &intstatus); + if (ret) { + sdio_err("failed to read interrupt status: %d\n", ret); + break; + } + } + + complete(&bus_if->busrx_trgg); +} + +#if defined(CONFIG_SDIO_PWRCTRL) +void aicwf_sdio_pwrctl_timer(struct aic_sdio_dev *sdiodev, uint duration) +{ + uint timeout; + + if (sdiodev->bus_if->state == BUS_DOWN_ST && duration) + return; + + spin_lock_bh(&sdiodev->pwrctl_lock); + if (!duration) { + if (timer_pending(&sdiodev->timer)) + //del_timer_sync(&sdiodev->timer); + timer_delete_sync(&sdiodev->timer); + } else { + sdiodev->active_duration = duration; + timeout = msecs_to_jiffies(sdiodev->active_duration); + mod_timer(&sdiodev->timer, jiffies + timeout); + } + spin_unlock_bh(&sdiodev->pwrctl_lock); +} +#endif + +/* Forward declarations for new bus_ops callbacks */ +static int aicwf_bsp_bus_read_reg(struct device *dev, u32 regaddr, u8 *val); +static int aicwf_bsp_bus_write_reg(struct device *dev, u32 regaddr, u8 val); +static int aicwf_bsp_bus_send_pkt(struct device *dev, u8 *buf, uint count); +static int aicwf_bsp_bus_recv_pkt(struct device *dev, u8 *buf, u32 size); +static int aicwf_bsp_bus_enable_irq(struct device *dev); +static void aicwf_bsp_bus_disable_irq(struct device *dev); +static bool aicwf_bsp_bus_flow_ctrl(struct device *dev); +static int aicwf_bsp_bus_sleep_allow(struct device *dev); +static int aicwf_bsp_bus_wakeup(struct device *dev); +static const void *aicwf_bsp_bus_get_hw_props(struct device *dev); + +static struct aicwf_bus_ops aicwf_sdio_bus_ops = { + .start = aicwf_sdio_bus_start, + .stop = aicwf_sdio_bus_stop, + .txdata = aicwf_sdio_bus_txdata, + .txmsg = aicwf_sdio_bus_txmsg, + .read_reg = aicwf_bsp_bus_read_reg, + .write_reg = aicwf_bsp_bus_write_reg, + .send_pkt = aicwf_bsp_bus_send_pkt, + .recv_pkt = aicwf_bsp_bus_recv_pkt, + .enable_irq = aicwf_bsp_bus_enable_irq, + .disable_irq = aicwf_bsp_bus_disable_irq, + .flow_ctrl = aicwf_bsp_bus_flow_ctrl, + .sleep_allow = aicwf_bsp_bus_sleep_allow, + .wakeup = aicwf_bsp_bus_wakeup, + .get_hw_props = aicwf_bsp_bus_get_hw_props, +}; + +void aicwf_sdio_release_func2(struct aic_sdio_dev *sdiodev) +{ + int ret = 0; + + if (!sdiodev->func_msg) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return; + } + sdio_claim_host(sdiodev->func_msg); + // disable sdio interrupt + ret = aicwf_sdio_writeb_func2(sdiodev, sdiodev->sdio_reg.intr_config_reg, + 0x0); + if (ret < 0) + sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.intr_config_reg); + + sdio_release_irq(sdiodev->func_msg); + sdio_release_host(sdiodev->func_msg); +} + +void aicwf_sdio_release(struct aic_sdio_dev *sdiodev) +{ + struct aicwf_bus *bus_if = NULL; + + if (bsp_init == 0) { + pr_err("aicbsp already sdio_release\n"); + return; + } + + bsp_init = 0; + + if (!sdiodev->func) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return; + } + + bus_if = aicbsp_get_drvdata(sdiodev->dev); + if (bus_if) + bus_if->state = BUS_DOWN_ST; + + if (aicbsp_sdiodev->chip_ops->use_func_msg) + aicwf_sdio_release_func2(sdiodev); + + if (sdiodev->dev) + aicwf_bus_deinit(sdiodev->dev); + + if (sdiodev->tx_priv) + aicwf_tx_deinit(sdiodev->tx_priv); + + if (sdiodev->rx_priv) + aicwf_rx_deinit(sdiodev->rx_priv); + + if (sdiodev->cmd_mgr.state == RWNX_CMD_MGR_STATE_INITED) + rwnx_cmd_mgr_deinit(&sdiodev->cmd_mgr); +} + +void aicwf_sdio_reg_init(struct aic_sdio_dev *sdiodev) +{ + if (aicbsp_sdiodev->chip_ops->use_func2) { + sdiodev->sdio_reg.bytemode_len_reg = SDIOWIFI_BYTEMODE_LEN_REG; + sdiodev->sdio_reg.intr_config_reg = SDIOWIFI_INTR_CONFIG_REG; + sdiodev->sdio_reg.sleep_reg = SDIOWIFI_SLEEP_REG; + sdiodev->sdio_reg.wakeup_reg = SDIOWIFI_WAKEUP_REG; + sdiodev->sdio_reg.flow_ctrl_reg = SDIOWIFI_FLOW_CTRL_REG; + sdiodev->sdio_reg.register_block = SDIOWIFI_REGISTER_BLOCK; + sdiodev->sdio_reg.bytemode_enable_reg = SDIOWIFI_BYTEMODE_ENABLE_REG; + sdiodev->sdio_reg.block_cnt_reg = SDIOWIFI_BLOCK_CNT_REG; + sdiodev->sdio_reg.rd_fifo_addr = SDIOWIFI_RD_FIFO_ADDR; + sdiodev->sdio_reg.wr_fifo_addr = SDIOWIFI_WR_FIFO_ADDR; + } else { + sdiodev->sdio_reg.bytemode_len_reg = SDIOWIFI_BYTEMODE_LEN_REG_V3; + sdiodev->sdio_reg.intr_config_reg = SDIOWIFI_INTR_ENABLE_REG_V3; + sdiodev->sdio_reg.sleep_reg = SDIOWIFI_INTR_PENDING_REG_V3; + sdiodev->sdio_reg.wakeup_reg = SDIOWIFI_INTR_TO_DEVICE_REG_V3; + sdiodev->sdio_reg.flow_ctrl_reg = SDIOWIFI_FLOW_CTRL_Q1_REG_V3; + sdiodev->sdio_reg.bytemode_enable_reg = SDIOWIFI_BYTEMODE_ENABLE_REG_V3; + sdiodev->sdio_reg.misc_int_status_reg = SDIOWIFI_MISC_INT_STATUS_REG_V3; + sdiodev->sdio_reg.rd_fifo_addr = SDIOWIFI_RD_FIFO_ADDR_V3; + sdiodev->sdio_reg.wr_fifo_addr = SDIOWIFI_WR_FIFO_ADDR_V3; + } +} + +int aicwf_sdio_func_init(struct aic_sdio_dev *sdiodev) +{ + struct mmc_host *host; + u8 block_bit0 = 0x1; + u8 byte_mode_disable = 0x1; // 1: no byte mode + int ret = 0; + struct aicbsp_feature_t feature; + + aicbsp_get_feature(&feature); + aicwf_sdio_reg_init(sdiodev); + + if (!sdiodev->func) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return 0; + } + + host = sdiodev->func->card->host; + + sdio_claim_host(sdiodev->func); + + ret = sdio_set_block_size(sdiodev->func, SDIOWIFI_FUNC_BLOCKSIZE); + if (ret < 0) { + sdio_err("set blocksize fail %d\n", ret); + sdio_release_host(sdiodev->func); + return ret; + } + ret = sdio_enable_func(sdiodev->func); + if (ret < 0) { + sdio_err("enable func fail %d.\n", ret); + sdio_release_host(sdiodev->func); + return ret; + } + usleep_range(20, 40); + // SDIO CLOCK SETTING + if (feature.sdio_clock > 0) { + host->ios.clock = feature.sdio_clock; + host->ops->set_ios(host, &host->ios); + sdio_dbg("Set SDIO Clock %d MHz\n", host->ios.clock / 1000000); + } + sdio_release_host(sdiodev->func); + + if (aicbsp_sdiodev->chip_ops->use_func_msg) { + sdio_claim_host(sdiodev->func_msg); + + // set sdio blocksize + ret = sdio_set_block_size(sdiodev->func_msg, SDIOWIFI_FUNC_BLOCKSIZE); + if (ret < 0) { + AICWFDBG(LOGERROR, "set func2 blocksize fail %d\n", ret); + sdio_release_host(sdiodev->func_msg); + return ret; + } + + // set sdio enable func + ret = sdio_enable_func(sdiodev->func_msg); + if (ret < 0) + AICWFDBG(LOGERROR, "enable func2 fail %d.\n", ret); + + sdio_release_host(sdiodev->func_msg); + + ret = aicwf_sdio_writeb_func2(sdiodev, sdiodev->sdio_reg.register_block, + block_bit0); + if (ret < 0) { + AICWFDBG(LOGERROR, "reg:%d write failed!\n", + sdiodev->sdio_reg.register_block); + return ret; + } + + // 1: no byte mode + ret = aicwf_sdio_writeb_func2(sdiodev, + sdiodev->sdio_reg.bytemode_enable_reg, + byte_mode_disable); + if (ret < 0) { + AICWFDBG(LOGERROR, "reg:%d write failed!\n", + sdiodev->sdio_reg.bytemode_enable_reg); + return ret; + } + } + + ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.register_block, + block_bit0); + if (ret < 0) { + sdio_err("reg:%d write failed!\n", sdiodev->sdio_reg.register_block); + return ret; + } + + // 1: no byte mode + ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.bytemode_enable_reg, + byte_mode_disable); + if (ret < 0) { + sdio_err("reg:%d write failed!\n", + sdiodev->sdio_reg.bytemode_enable_reg); + return ret; + } + + return ret; +} + +int aicwf_sdiov3_func_init(struct aic_sdio_dev *sdiodev) +{ + struct mmc_host *host; + u8 byte_mode_disable = 0x1; // 1: no byte mode + int ret = 0; + struct aicbsp_feature_t feature; + int val; + + aicbsp_get_feature(&feature); + aicwf_sdio_reg_init(sdiodev); + + if (!sdiodev->func) { + pr_warn("aicbsp %s, NULL sdio func\n", __func__); + return 0; + } + + host = sdiodev->func->card->host; + + sdio_claim_host(sdiodev->func); + sdiodev->func->card->quirks |= MMC_QUIRK_LENIENT_FN0; + + ret = sdio_set_block_size(sdiodev->func, SDIOWIFI_FUNC_BLOCKSIZE); + if (ret < 0) { + sdio_err("set blocksize fail %d\n", ret); + sdio_release_host(sdiodev->func); + return ret; + } + ret = sdio_enable_func(sdiodev->func); + if (ret < 0) { + sdio_err("enable func fail %d.\n", ret); + sdio_release_host(sdiodev->func); + return ret; + } + + sdio_f0_writeb(sdiodev->func, 0x7F, 0xF2, &ret); + if (ret) { + sdio_err("set fn0 0xF2 fail %d\n", ret); + sdio_release_host(sdiodev->func); + return ret; + } + // mod sdio phase + if (host->ios.timing == MMC_TIMING_UHS_DDR50) + val = 0x20; // 0x21; //0x1D; //0x5; + else + val = 0x00; // 0x01; //0x19; //0x1; + val |= SDIOCLK_FREE_RUNNING_BIT; + sdio_f0_writeb(sdiodev->func, val, 0xF0, &ret); + if (ret) { + sdio_err("set iopad ctrl fail %d\n", ret); + sdio_release_host(sdiodev->func); + return ret; + } + sdio_f0_writeb(sdiodev->func, 0x00, 0xF8, &ret); + if (ret) { + sdio_err("set iopad delay2 fail %d\n", ret); + sdio_release_host(sdiodev->func); + return ret; + } + sdio_f0_writeb(sdiodev->func, 0x00, 0xF1, &ret); + if (ret) { + sdio_err("set iopad delay1 fail %d\n", ret); + sdio_release_host(sdiodev->func); + return ret; + } + usleep_range(1000, 2000); + sdio_dbg("Set SDIO Clock %d MHz\n", host->ios.clock / 1000000); + sdio_release_host(sdiodev->func); + + // 1: no byte mode + ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.bytemode_enable_reg, + byte_mode_disable); + if (ret < 0) { + sdio_err("reg:%d write failed!\n", + sdiodev->sdio_reg.bytemode_enable_reg); + return ret; + } + + return ret; +} + +void aicwf_sdio_func_deinit(struct aic_sdio_dev *sdiodev) +{ + struct aicwf_bus *bus_if = NULL; + + if (bsp_func_init == 0) { + pr_err("aicbsp already func_deinit\n"); + return; + } + + bsp_func_init = 0; + + if (!sdiodev->func) { + sdio_info("%s, NULL sdio func\n", __func__); + return; + } + + bus_if = dev_get_drvdata(sdiodev->dev); + if (!bus_if) { + sdio_info("%s, bus_if is null\n", __func__); + return; + } + + if (sdiodev->bus_if == bus_if) { + sdio_dbg("%s bsp disable\n", __func__); + sdio_claim_host(sdiodev->func); + sdio_disable_func(sdiodev->func); + sdio_release_host(sdiodev->func); + } + + if (aicbsp_sdiodev->chip_ops->use_func_msg) { + sdio_claim_host(sdiodev->func_msg); + sdio_disable_func(sdiodev->func_msg); + sdio_release_host(sdiodev->func_msg); + } +} + +void *aicwf_sdio_bus_init(struct aic_sdio_dev *sdiodev) +{ + int ret; + struct aicwf_bus *bus_if; + struct aicwf_rx_priv *rx_priv; + struct aicwf_tx_priv *tx_priv; + +#if defined(CONFIG_SDIO_PWRCTRL) + spin_lock_init(&sdiodev->pwrctl_lock); + sema_init(&sdiodev->pwrctl_wakeup_sema, 1); +#endif + + bus_if = sdiodev->bus_if; + bus_if->dev = sdiodev->dev; + bus_if->ops = &aicwf_sdio_bus_ops; + bus_if->state = BUS_DOWN_ST; +#if defined(CONFIG_SDIO_PWRCTRL) + sdiodev->state = SDIO_SLEEP_ST; + sdiodev->active_duration = SDIOWIFI_PWR_CTRL_INTERVAL; +#else + sdiodev->state = SDIO_ACTIVE_ST; +#endif + + rx_priv = aicwf_rx_init(sdiodev); + if (!rx_priv) { + sdio_err("rx init fail\n"); + goto fail; + } + sdiodev->rx_priv = rx_priv; + + tx_priv = aicwf_tx_init(sdiodev); + if (!tx_priv) { + sdio_err("tx init fail\n"); + goto fail; + } + sdiodev->tx_priv = tx_priv; + aicwf_frame_queue_init(&tx_priv->txq, 8, TXQLEN); + spin_lock_init(&tx_priv->txqlock); + sema_init(&tx_priv->txctl_sema, 1); + sema_init(&tx_priv->cmd_txsema, 1); + init_waitqueue_head(&tx_priv->cmd_txdone_wait); + atomic_set(&tx_priv->tx_pktcnt, 0); + +#if defined(CONFIG_SDIO_PWRCTRL) + timer_setup(&sdiodev->timer, aicwf_sdio_bus_pwrctl, 0); + init_completion(&sdiodev->pwrctrl_trgg); +#endif + ret = aicwf_bus_init(0, sdiodev->dev); + if (ret < 0) { + sdio_err("bus init fail\n"); + goto fail; + } + + ret = aicwf_bus_start(bus_if); + if (ret != 0) { + sdio_err("bus start fail\n"); + goto fail; + } + + return sdiodev; + +fail: + aicwf_sdio_release(sdiodev); + return NULL; +} + +/** + * get_testmode - Get the firmware test mode selected by the BSP + * + * Return: Current firmware test mode. + */ +int get_testmode(void) +{ + return testmode; +} +EXPORT_SYMBOL_GPL(get_testmode); + +/** + * get_sdio_func - Get the active AIC SDIO function + * + * Return: Pointer to the active SDIO function. + */ +struct sdio_func *get_sdio_func(void) +{ + return aicbsp_sdiodev->func; +} +EXPORT_SYMBOL_GPL(get_sdio_func); + +/** + * set_irq_handler - Set the WLAN driver's SDIO interrupt callback + * @fn: Callback invoked for an SDIO interrupt + */ +void set_irq_handler(void *fn) +{ + aicbsp_sdiodev->sdio_hal_irqhandler = (sdio_irq_handler_t *)fn; +} +EXPORT_SYMBOL_GPL(set_irq_handler); + +u8 crc8_ponl_107(u8 *p_buffer, u16 cal_size) +{ + u8 i; + u8 crc = 0; + + if (cal_size == 0) + return crc; + while (cal_size--) { + for (i = 0x80; i > 0; i /= 2) { + if (crc & 0x80) { + crc *= 2; + crc ^= 0x07; // polynomial X8 + X2 + X + 1,(0x107) + } else { + crc *= 2; + } + if ((*p_buffer) & i) + crc ^= 0x07; + } + p_buffer++; + } + return crc; +} + +/* ================================================================ + * Bus abstraction layer – BSP SDIO bus_ops callbacks + * ================================================================ + */ + +static inline struct aic_sdio_dev *aicwf_bsp_bus_to_sdiodev(struct device *dev) +{ + struct aicwf_bus *bus_if = dev_get_drvdata(dev); + + return bus_if->bus_priv.sdio; +} + +static int aicwf_bsp_bus_read_reg(struct device *dev, u32 regaddr, u8 *val) +{ + return aicwf_sdio_readb(aicwf_bsp_bus_to_sdiodev(dev), regaddr, val); +} + +static int aicwf_bsp_bus_write_reg(struct device *dev, u32 regaddr, u8 val) +{ + return aicwf_sdio_writeb(aicwf_bsp_bus_to_sdiodev(dev), regaddr, val); +} + +static int aicwf_bsp_bus_send_pkt(struct device *dev, u8 *buf, uint count) +{ + return aicwf_sdio_send_pkt(aicwf_bsp_bus_to_sdiodev(dev), buf, count); +} + +static int aicwf_bsp_bus_recv_pkt(struct device *dev, u8 *buf, u32 size) +{ + struct aic_sdio_dev *sdiodev = aicwf_bsp_bus_to_sdiodev(dev); + int ret; + + sdio_claim_host(sdiodev->func); + ret = sdio_readsb(sdiodev->func, buf, + sdiodev->sdio_reg.rd_fifo_addr, size); + sdio_release_host(sdiodev->func); + + return ret; +} + +static int aicwf_bsp_bus_enable_irq(struct device *dev) +{ + struct aic_sdio_dev *sdiodev = aicwf_bsp_bus_to_sdiodev(dev); + int ret; + + sdio_claim_host(sdiodev->func); + sdio_claim_irq(sdiodev->func, sdiodev->sdio_hal_irqhandler); + sdio_release_host(sdiodev->func); + + ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x07); + if (ret) + sdio_err("intr register failed:%d\n", ret); + + return ret; +} + +static void aicwf_bsp_bus_disable_irq(struct device *dev) +{ + struct aic_sdio_dev *sdiodev = aicwf_bsp_bus_to_sdiodev(dev); + + sdio_claim_host(sdiodev->func); + aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.intr_config_reg, 0x0); + sdio_release_irq(sdiodev->func); + sdio_release_host(sdiodev->func); +} + +static bool aicwf_bsp_bus_flow_ctrl(struct device *dev) +{ + int ret; + + ret = aicwf_sdio_flow_ctrl(aicwf_bsp_bus_to_sdiodev(dev)); + return (ret > 0); +} + +#if defined(CONFIG_SDIO_PWRCTRL) +static int aicwf_bsp_bus_sleep_allow(struct device *dev) +{ + return aicwf_sdio_sleep_allow(aicwf_bsp_bus_to_sdiodev(dev)); +} + +static int aicwf_bsp_bus_wakeup(struct device *dev) +{ + return aicwf_sdio_wakeup(aicwf_bsp_bus_to_sdiodev(dev)); +} +#else +static int aicwf_bsp_bus_sleep_allow(struct device *dev) +{ + return 0; +} + +static int aicwf_bsp_bus_wakeup(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_SDIO_PWRCTRL */ + +static const void *aicwf_bsp_bus_get_hw_props(struct device *dev) +{ + return NULL; /* BSP layer does not expose chip HW props via bus_ops */ +} diff --git a/drivers/net/wireless/aic/aic8800_bsp/aicsdio.h b/drivers/net/wireless/aic/aic8800_bsp/aicsdio.h new file mode 100644 index 0000000000000..14915b1447b2a --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aicsdio.h @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * aicwf_sdio.h + * + * SDIO function declarations + * + * Copyright (C) AICSemi 2018-2020 + */ + +#ifndef _AICWF_SDMMC_H_ +#define _AICWF_SDMMC_H_ + +#ifdef AICWF_SDIO_SUPPORT +#include "aic_bsp_driver.h" +#include +#include +#include +#include + +#define AICBSP_SDIO_NAME "aicbsp_sdio" +#define SDIOWIFI_FUNC_BLOCKSIZE 512 + +#define SDIO_VENDOR_ID_AIC 0x8800 +#define SDIO_DEVICE_ID_AIC 0x0001 +#define SDIOWIFI_BYTEMODE_LEN_REG 0x02 +#define SDIOWIFI_INTR_CONFIG_REG 0x04 +#define SDIOWIFI_SLEEP_REG 0x05 +#define SDIOWIFI_WAKEUP_REG 0x09 +#define SDIOWIFI_FLOW_CTRL_REG 0x0A +#define SDIOWIFI_REGISTER_BLOCK 0x0B +#define SDIOWIFI_BYTEMODE_ENABLE_REG 0x11 +#define SDIOWIFI_BLOCK_CNT_REG 0x12 +#define SDIOWIFI_FLOWCTRL_MASK_REG 0x7F +#define SDIOWIFI_WR_FIFO_ADDR 0x07 +#define SDIOWIFI_RD_FIFO_ADDR 0x08 + +#define SDIOWIFI_INTR_ENABLE_REG_V3 0x00 +#define SDIOWIFI_INTR_PENDING_REG_V3 0x01 +#define SDIOWIFI_INTR_TO_DEVICE_REG_V3 0x02 +#define SDIOWIFI_FLOW_CTRL_Q1_REG_V3 0x03 +#define SDIOWIFI_MISC_INT_STATUS_REG_V3 0x04 +#define SDIOWIFI_BYTEMODE_LEN_REG_V3 0x05 +#define SDIOWIFI_BYTEMODE_LEN_MSB_REG_V3 0x06 +#define SDIOWIFI_BYTEMODE_ENABLE_REG_V3 0x07 +#define SDIOWIFI_MISC_CTRL_REG_V3 0x08 +#define SDIOWIFI_FLOW_CTRL_Q2_REG_V3 0x09 +#define SDIOWIFI_CLK_TEST_RESULT_REG_V3 0x0A +#define SDIOWIFI_RD_FIFO_ADDR_V3 0x0F +#define SDIOWIFI_WR_FIFO_ADDR_V3 0x10 + +#define SDIOCLK_FREE_RUNNING_BIT BIT(6) + +#define SDIOWIFI_PWR_CTRL_INTERVAL 30 +#define FLOW_CTRL_RETRY_COUNT 50 +#define BUFFER_SIZE 1536 +#define TAIL_LEN 4 +#define TXQLEN (2048 * 4) + +#define SDIO_SLEEP_ST 0 +#define SDIO_ACTIVE_ST 1 + +/* SDIO Device ID */ +#define SDIO_VENDOR_ID_AIC8801 0x5449 +#define SDIO_VENDOR_ID_AIC8800DC 0xc8a1 +#define SDIO_VENDOR_ID_AIC8800D80 0xc8a1 + +#define SDIO_DEVICE_ID_AIC8801 0x0145 +#define SDIO_DEVICE_ID_AIC8800DC 0xc08d +#define SDIO_DEVICE_ID_AIC8800D80 0x0082 +#define SDIO_DEVICE_ID_AIC8801_FUNC2 0x0146 +#define SDIO_DEVICE_ID_AIC8800D80_FUNC2 0x0182 + +enum sdio_type { + SDIO_TYPE_DATA = 0X00, + SDIO_TYPE_CFG = 0X10, + SDIO_TYPE_CFG_CMD_RSP = 0X11, + SDIO_TYPE_CFG_DATA_CFM = 0X12 +}; + +enum AICWF_IC { + PRODUCT_ID_AIC8801 = 0, + PRODUCT_ID_AIC8800DC, + PRODUCT_ID_AIC8800DW, + PRODUCT_ID_AIC8800D80 +}; + +struct aic_sdio_reg { + u8 bytemode_len_reg; + u8 intr_config_reg; + u8 sleep_reg; + u8 wakeup_reg; + u8 flow_ctrl_reg; + u8 flowctrl_mask_reg; + u8 register_block; + u8 bytemode_enable_reg; + u8 block_cnt_reg; + u8 misc_int_status_reg; + u8 rd_fifo_addr; + u8 wr_fifo_addr; +}; + +struct aic_sdio_dev { + struct rwnx_cmd_mgr cmd_mgr; + struct sdio_func *func; + struct sdio_func *func_msg; + struct device *dev; + struct aicwf_bus *bus_if; + + struct aicwf_rx_priv *rx_priv; + struct aicwf_tx_priv *tx_priv; + u32 state; + +#if defined(CONFIG_SDIO_PWRCTRL) + // for sdio pwr ctrl + struct timer_list timer; + uint active_duration; + struct completion pwrctrl_trgg; + struct task_struct *pwrctl_tsk; + /* lock for pwrctl */ + spinlock_t pwrctl_lock; + struct semaphore pwrctl_wakeup_sema; +#endif + u16 chipid; + const struct aic_chip_ops *chip_ops; + u32 fw_version_uint; + struct aic_sdio_reg sdio_reg; + void (*sdio_hal_irqhandler)(struct sdio_func *func); +}; + +int aicwf_sdio_readb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 *val); +int aicwf_sdio_readb_func2(struct aic_sdio_dev *sdiodev, uint regaddr, u8 *val); +int aicwf_sdio_writeb_func2(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val); +int aicwf_sdio_send_msg(struct aic_sdio_dev *sdiodev, u8 *buf, uint count); +struct sk_buff *aicwf_sdio_readframes(struct aic_sdio_dev *sdiodev, u8 msg); +void aicwf_sdio_release_func2(struct aic_sdio_dev *sdiodev); +int get_testmode(void); +struct sdio_func *get_sdio_func(void); +void set_irq_handler(void *fn); + +void *aicbsp_get_drvdata(void *args); +int aicwf_sdio_writeb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val); +void aicwf_sdio_hal_irqhandler(struct sdio_func *func); +void aicwf_sdio_hal_irqhandler_func2(struct sdio_func *func); +#if defined(CONFIG_SDIO_PWRCTRL) +int aicwf_sdio_wakeup(struct aic_sdio_dev *sdiodev); +int aicwf_sdio_sleep_allow(struct aic_sdio_dev *sdiodev); + +void aicwf_sdio_pwrctl_timer(struct aic_sdio_dev *sdiodev, uint duration); +int aicwf_sdio_pwr_stctl(struct aic_sdio_dev *sdiodev, uint target); +#endif +void aicwf_sdio_reg_init(struct aic_sdio_dev *sdiodev); +int aicwf_sdio_func_init(struct aic_sdio_dev *sdiodev); +int aicwf_sdiov3_func_init(struct aic_sdio_dev *sdiodev); +void aicwf_sdio_func_deinit(struct aic_sdio_dev *sdiodev); +int aicwf_sdio_flow_ctrl(struct aic_sdio_dev *sdiodev); +int aicwf_sdio_recv_pkt(struct aic_sdio_dev *sdiodev, struct sk_buff *skbbuf, + u32 size, u8 msg); +int aicwf_sdio_send_pkt(struct aic_sdio_dev *sdiodev, u8 *buf, uint count); +void *aicwf_sdio_bus_init(struct aic_sdio_dev *sdiodev); +void aicwf_sdio_release(struct aic_sdio_dev *sdiodev); +void aicbsp_sdio_exit(void); +int aicbsp_sdio_init(void); +void aicbsp_sdio_release(struct aic_sdio_dev *sdiodev); +int aicwf_sdio_txpkt(struct aic_sdio_dev *sdiodev, struct sk_buff *pkt); +int aicwf_sdio_bustx_thread(void *data); +int aicwf_sdio_busrx_thread(void *data); +int aicwf_sdio_aggr(struct aicwf_tx_priv *tx_priv, struct sk_buff *pkt); +int aicwf_sdio_send(struct aicwf_tx_priv *tx_priv); +void aicwf_sdio_aggr_send(struct aicwf_tx_priv *tx_priv); +void aicwf_sdio_aggrbuf_reset(struct aicwf_tx_priv *tx_priv); +void aicwf_hostif_ready(void); +int aicwf_process_rxframes(struct aicwf_rx_priv *rx_priv); +u8 crc8_ponl_107(u8 *p_buffer, u16 cal_size); +#endif /* AICWF_SDIO_SUPPORT */ + +#endif /*_AICWF_SDMMC_H_*/ diff --git a/drivers/net/wireless/aic/aic8800_bsp/aicsdio_txrxif.c b/drivers/net/wireless/aic/aic8800_bsp/aicsdio_txrxif.c new file mode 100644 index 0000000000000..120f0ec267fc0 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aicsdio_txrxif.c @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief bsp tx/rx interface + * + ****************************************************************************** + */ + +#include "aicsdio_txrxif.h" +#include "aic_bsp_driver.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int aicwf_bus_init(uint bus_hdrlen, struct device *dev) +{ + int ret = 0; + struct aicwf_bus *bus_if; + + if (!dev) { + txrx_err("device not found\n"); + return -1; + } + bus_if = dev_get_drvdata(dev); + bus_if->cmd_buf = kzalloc(CMD_BUF_MAX, GFP_KERNEL); + if (!bus_if->cmd_buf) { + ret = -ENOMEM; + txrx_err("proto_attach failed\n"); + goto out; + } + memset(bus_if->cmd_buf, '\0', CMD_BUF_MAX); + + init_completion(&bus_if->bustx_trgg); + init_completion(&bus_if->busrx_trgg); +#ifdef AICWF_SDIO_SUPPORT + bus_if->bustx_thread = kthread_run(aicwf_sdio_bustx_thread, (void *)bus_if, + "aicwf_bustx_thread"); + bus_if->busrx_thread = kthread_run(aicwf_sdio_busrx_thread, + (void *)bus_if->bus_priv.sdio->rx_priv, + "aicwf_busrx_thread"); +#endif + + if (IS_ERR(bus_if->bustx_thread)) { + bus_if->bustx_thread = NULL; + txrx_err("aicwf_bustx_thread run fail\n"); + ret = -1; + } + + if (IS_ERR(bus_if->busrx_thread)) { + bus_if->busrx_thread = NULL; + txrx_err("aicwf_bustx_thread run fail\n"); + ret = -1; + } + + if (ret < 0) + goto out; + + return ret; + +out: + return ret; +} + +void aicwf_bus_deinit(struct device *dev) +{ + struct aicwf_bus *bus_if; + + if (!dev) { + txrx_err("device not found\n"); + return; + } + bus_if = aicbsp_get_drvdata(dev); + aicwf_bus_stop(bus_if); + + kfree(bus_if->cmd_buf); + if (bus_if->cmd_buf) + bus_if->cmd_buf = NULL; + + if (bus_if->bustx_thread) { + complete_all(&bus_if->bustx_trgg); + kthread_stop(bus_if->bustx_thread); + bus_if->bustx_thread = NULL; + } +} + +void aicwf_frame_tx(void *dev, struct sk_buff *skb) +{ + struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *)dev; + + aicwf_bus_txdata(sdiodev->bus_if, skb); +} + +struct aicwf_tx_priv *aicwf_tx_init(void *arg) +{ + struct aicwf_tx_priv *tx_priv; + + tx_priv = kzalloc_obj(*tx_priv, GFP_KERNEL); + if (!tx_priv) + return NULL; + + tx_priv->sdiodev = (struct aic_sdio_dev *)arg; + + atomic_set(&tx_priv->aggr_count, 0); + tx_priv->aggr_buf = dev_alloc_skb(MAX_AGGR_TXPKT_LEN); + if (!tx_priv->aggr_buf) { + txrx_err("Alloc bus->txdata_buf failed!\n"); + kfree(tx_priv); + return NULL; + } + tx_priv->head = tx_priv->aggr_buf->data; + tx_priv->tail = tx_priv->aggr_buf->data; + + return tx_priv; +} + +void aicwf_tx_deinit(struct aicwf_tx_priv *tx_priv) +{ + if (tx_priv && tx_priv->aggr_buf) + dev_kfree_skb(tx_priv->aggr_buf); + + kfree(tx_priv); +} + +static bool aicwf_another_ptk(struct sk_buff *skb) +{ + u16 aggr_len = 0; + + if (!skb->data || skb->len == 0) + return false; + aggr_len = (*skb->data | (*(skb->data + 1) << 8)); + if (aggr_len == 0) + return false; + + return true; +} + +int aicwf_process_rxframes(struct aicwf_rx_priv *rx_priv) +{ + int ret = 0; + unsigned long flags = 0; + struct sk_buff *skb = NULL; + u16 pkt_len = 0; + struct sk_buff *skb_inblock = NULL; + u16 aggr_len = 0, adjust_len = 0; + u8 *data = NULL; + + while (1) { + spin_lock_irqsave(&rx_priv->rxqlock, flags); + if (aicwf_is_framequeue_empty(&rx_priv->rxq)) { + spin_unlock_irqrestore(&rx_priv->rxqlock, flags); + break; + } + skb = aicwf_frame_dequeue(&rx_priv->rxq); + spin_unlock_irqrestore(&rx_priv->rxqlock, flags); + if (!skb) { + txrx_err("skb_error\r\n"); + break; + } + while (aicwf_another_ptk(skb)) { + data = skb->data; + pkt_len = (*skb->data | (*(skb->data + 1) << 8)); + + if ((skb->data[2] & SDIO_TYPE_CFG) != + SDIO_TYPE_CFG) { // type : data + aggr_len = pkt_len + RX_HWHRD_LEN; + + if (aggr_len & (RX_ALIGNMENT - 1)) + adjust_len = roundup(aggr_len, RX_ALIGNMENT); + else + adjust_len = aggr_len; + + skb_inblock = + __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, + GFP_KERNEL); // 8 is for ccmp mic or wep icv + if (!skb_inblock) { + txrx_err("no more space!\n"); + aicwf_dev_skb_free(skb); + return -EBADE; + } + + skb_put(skb_inblock, aggr_len); + memcpy(skb_inblock->data, data, aggr_len); + skb_pull(skb, adjust_len); + } else { // type : config + aggr_len = pkt_len; + + if (aggr_len & (RX_ALIGNMENT - 1)) + adjust_len = roundup(aggr_len, RX_ALIGNMENT); + else + adjust_len = aggr_len; + + skb_inblock = + __dev_alloc_skb(aggr_len + 4, GFP_KERNEL); + if (!skb_inblock) { + txrx_err("no more space!\n"); + aicwf_dev_skb_free(skb); + return -EBADE; + } + + skb_put(skb_inblock, aggr_len + 4); + memcpy(skb_inblock->data, data, aggr_len + 4); + if ((*(skb_inblock->data + 2) & 0x7f) == SDIO_TYPE_CFG_CMD_RSP) + rwnx_rx_handle_msg(rx_priv->sdiodev, + (struct ipc_e2a_msg *)(skb_inblock->data + + 4)); + skb_pull(skb, adjust_len + 4); + } + } + + /* skb_inblock no used currently, just free it! */ + dev_kfree_skb(skb_inblock); + dev_kfree_skb(skb); + atomic_dec(&rx_priv->rx_cnt); + } + +#if defined(CONFIG_SDIO_PWRCTRL) + aicwf_sdio_pwr_stctl(rx_priv->sdiodev, SDIO_ACTIVE_ST); +#endif + + return ret; +} + +static struct recv_msdu *aicwf_rxframe_queue_init(struct list_head *q, + int qsize) +{ + int i; + struct recv_msdu *req, *reqs; + + reqs = vmalloc(qsize * sizeof(struct recv_msdu)); + if (!reqs) + return NULL; + + req = reqs; + for (i = 0; i < qsize; i++) { + INIT_LIST_HEAD(&req->rxframe_list); + list_add(&req->rxframe_list, q); + req->len = 0; + req++; + } + + return reqs; +} + +struct aicwf_rx_priv *aicwf_rx_init(void *arg) +{ + struct aicwf_rx_priv *rx_priv; + + rx_priv = kzalloc_obj(*rx_priv, GFP_KERNEL); + if (!rx_priv) + return NULL; + + rx_priv->sdiodev = (struct aic_sdio_dev *)arg; + aicwf_frame_queue_init(&rx_priv->rxq, 1, MAX_RXQLEN); + spin_lock_init(&rx_priv->rxqlock); + atomic_set(&rx_priv->rx_cnt, 0); + + INIT_LIST_HEAD(&rx_priv->rxframes_freequeue); + spin_lock_init(&rx_priv->freeq_lock); + rx_priv->recv_frames = aicwf_rxframe_queue_init(&rx_priv->rxframes_freequeue, + MAX_REORD_RXFRAME); + if (!rx_priv->recv_frames) { + txrx_err("no enough buffer for free recv frame queue!\n"); + kfree(rx_priv); + return NULL; + } + spin_lock_init(&rx_priv->stas_reord_lock); + INIT_LIST_HEAD(&rx_priv->stas_reord_list); + + return rx_priv; +} + +static void aicwf_recvframe_queue_deinit(struct list_head *q) +{ + struct recv_msdu *req, *next; + + list_for_each_entry_safe(req, next, q, rxframe_list) { + list_del_init(&req->rxframe_list); + } +} + +void aicwf_rx_deinit(struct aicwf_rx_priv *rx_priv) +{ + if (rx_priv->sdiodev->bus_if->busrx_thread) { + complete_all(&rx_priv->sdiodev->bus_if->busrx_trgg); + kthread_stop(rx_priv->sdiodev->bus_if->busrx_thread); + rx_priv->sdiodev->bus_if->busrx_thread = NULL; + } + + aicwf_frame_queue_flush(&rx_priv->rxq); + aicwf_recvframe_queue_deinit(&rx_priv->rxframes_freequeue); + if (rx_priv->recv_frames) + vfree(rx_priv->recv_frames); + + kfree(rx_priv); +} + +bool aicwf_rxframe_enqueue(struct device *dev, struct frame_queue *q, + struct sk_buff *pkt) +{ + return aicwf_frame_enq(dev, q, pkt, 0); +} + +void aicwf_dev_skb_free(struct sk_buff *skb) +{ + if (!skb) + return; + + dev_kfree_skb_any(skb); +} + +static struct sk_buff *aicwf_frame_queue_penq(struct frame_queue *pq, int prio, + struct sk_buff *p) +{ + struct sk_buff_head *q; + + if (pq->queuelist[prio].qlen >= pq->qmax) + return NULL; + + q = &pq->queuelist[prio]; + __skb_queue_tail(q, p); + pq->qcnt++; + if (pq->hi_prio < prio) + pq->hi_prio = (u16)prio; + + return p; +} + +void aicwf_frame_queue_flush(struct frame_queue *pq) +{ + int prio; + struct sk_buff_head *q; + struct sk_buff *p, *next; + + for (prio = 0; prio < pq->num_prio; prio++) { + q = &pq->queuelist[prio]; + skb_queue_walk_safe(q, p, next) + { + skb_unlink(p, q); + aicwf_dev_skb_free(p); + pq->qcnt--; + } + } +} + +void aicwf_frame_queue_init(struct frame_queue *pq, int num_prio, int max_len) +{ + int prio; + + memset(pq, 0, + offsetof(struct frame_queue, queuelist) + + (sizeof(struct sk_buff_head) * num_prio)); + pq->num_prio = (u16)num_prio; + pq->qmax = (u16)max_len; + + for (prio = 0; prio < num_prio; prio++) + skb_queue_head_init(&pq->queuelist[prio]); +} + +struct sk_buff *aicwf_frame_queue_peek_tail(struct frame_queue *pq, + int *prio_out) +{ + int prio; + + if (pq->qcnt == 0) + return NULL; + + for (prio = 0; prio < pq->hi_prio; prio++) + if (!skb_queue_empty(&pq->queuelist[prio])) + break; + + if (prio_out) + *prio_out = prio; + + return skb_peek_tail(&pq->queuelist[prio]); +} + +bool aicwf_is_framequeue_empty(struct frame_queue *pq) +{ + int prio, len = 0; + + for (prio = 0; prio <= pq->hi_prio; prio++) + len += pq->queuelist[prio].qlen; + + if (len > 0) + return false; + else + return true; +} + +struct sk_buff *aicwf_frame_dequeue(struct frame_queue *pq) +{ + struct sk_buff_head *q; + struct sk_buff *p; + int prio; + + if (pq->qcnt == 0) + return NULL; + + while ((prio = pq->hi_prio) > 0 && skb_queue_empty(&pq->queuelist[prio])) + pq->hi_prio--; + + q = &pq->queuelist[prio]; + p = __skb_dequeue(q); + if (!p) + return NULL; + + pq->qcnt--; + + return p; +} + +static struct sk_buff *aicwf_skb_dequeue_tail(struct frame_queue *pq, int prio) +{ + struct sk_buff_head *q = &pq->queuelist[prio]; + struct sk_buff *p = skb_dequeue_tail(q); + + if (!p) + return NULL; + + pq->qcnt--; + return p; +} + +bool aicwf_frame_enq(struct device *dev, struct frame_queue *q, + struct sk_buff *pkt, int prio) +{ + struct sk_buff *p = NULL; + int prio_modified = -1; + + if (q->queuelist[prio].qlen < q->qmax && q->qcnt < q->qmax) { + aicwf_frame_queue_penq(q, prio, pkt); + return true; + } + if (q->queuelist[prio].qlen >= q->qmax) { + prio_modified = prio; + } else if (q->qcnt >= q->qmax) { + p = aicwf_frame_queue_peek_tail(q, &prio_modified); + if (prio_modified > prio) + return false; + } + + if (prio_modified >= 0) { + if (prio_modified == prio) + return false; + + p = aicwf_skb_dequeue_tail(q, prio_modified); + aicwf_dev_skb_free(p); + + p = aicwf_frame_queue_penq(q, prio_modified, pkt); + if (!p) + txrx_err("failed\n"); + } + + return p; +} diff --git a/drivers/net/wireless/aic/aic8800_bsp/aicsdio_txrxif.h b/drivers/net/wireless/aic/aic8800_bsp/aicsdio_txrxif.h new file mode 100644 index 0000000000000..2ed64b3ae9949 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aicsdio_txrxif.h @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * aicwf_txrxif.h + * + * bus function declarations + * + * Copyright (C) AICSemi 2018-2020 + */ + +#ifndef _AICWF_TXRXIF_H_ +#define _AICWF_TXRXIF_H_ + +#include "aicsdio.h" +#include +#include +#include "aic_bus.h" + +#define CMD_BUF_MAX 1536 +#define TXPKT_BLOCKSIZE 512 +#define MAX_AGGR_TXPKT_LEN (1536 * 4) +#define CMD_TX_TIMEOUT 5000 +#define TX_ALIGNMENT 4 + +#define RX_HWHRD_LEN 60 // 58->60 word allined +#define CCMP_OR_WEP_INFO 8 +#define MAX_RXQLEN 2000 +#define RX_ALIGNMENT 4 + +#define DEBUG_ERROR_LEVEL 0 +#define DEBUG_DEBUG_LEVEL 1 +#define DEBUG_INFO_LEVEL 2 + +#define DBG_LEVEL DEBUG_DEBUG_LEVEL + +#define txrx_err(fmt, ...) \ + pr_err("aicbsp: txrx_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) +#define sdio_err(fmt, ...) \ + pr_err("aicbsp: sdio_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) +#define usb_err(fmt, ...) \ + pr_err("aicbsp: usb_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__) +#if DBG_LEVEL >= DEBUG_DEBUG_LEVEL +#define sdio_dbg(fmt, ...) pr_debug("aicbsp: " fmt, ##__VA_ARGS__) +#define usb_dbg(fmt, ...) pr_debug("aicbsp: " fmt, ##__VA_ARGS__) +#else +#define sdio_dbg(fmt, ...) +#define usb_dbg(fmt, ...) +#endif +#if DBG_LEVEL >= DEBUG_INFO_LEVEL +#define sdio_info(fmt, ...) pr_info("aicbsp: " fmt, ##__VA_ARGS__) +#define usb_info(fmt, ...) pr_info("aicbsp: " fmt, ##__VA_ARGS__) +#else +#define sdio_info(fmt, ...) +#define usb_info(fmt, ...) +#endif + +/* aicwf_bus_state and aicwf_bus_ops are now defined in aic_bus.h */ + +struct frame_queue { + u16 num_prio; + u16 hi_prio; + u16 qmax; /* max number of queued frames */ + u16 qcnt; + struct sk_buff_head queuelist[8]; +}; + +struct aicwf_tx_priv { +#ifdef AICWF_SDIO_SUPPORT + struct aic_sdio_dev *sdiodev; + int fw_avail_bufcnt; + // for cmd tx + u8 *cmd_buf; + uint cmd_len; + bool cmd_txstate; + bool cmd_tx_succ; + struct semaphore cmd_txsema; + wait_queue_head_t cmd_txdone_wait; + // for data tx + atomic_t tx_pktcnt; + + struct frame_queue txq; + /* lock for tx_priv */ + spinlock_t txqlock; + struct semaphore txctl_sema; +#endif + struct sk_buff *aggr_buf; + atomic_t aggr_count; + u8 *head; + u8 *tail; +}; + +#define MAX_REORD_RXFRAME 250 +#define REORDER_UPDATE_TIME 50 +#define AICWF_REORDER_WINSIZE 64 +#define SN_LESS(a, b) ((((a) - (b)) & 0x800) != 0) +#define SN_EQUAL(a, b) ((a) == (b)) + +struct reord_ctrl { + struct aicwf_rx_priv *rx_priv; + u8 enable; + u16 ind_sn; + u8 wsize_b; + /* lock for reord list */ + spinlock_t reord_list_lock; + struct list_head reord_list; + struct timer_list reord_timer; + struct work_struct reord_timer_work; +}; + +struct reord_ctrl_info { + u8 mac_addr[6]; + struct reord_ctrl preorder_ctrl[8]; + struct list_head list; +}; + +struct recv_msdu { + struct sk_buff *pkt; + u8 tid; + u16 seq_num; + uint len; + u8 *rx_data; + // for pending rx reorder list + struct list_head reord_pending_list; + // for total frame list, when rxframe from busif, dequeue, when submit frame + // to net, enqueue + struct list_head rxframe_list; + struct reord_ctrl *preorder_ctrl; +}; + +struct aicwf_rx_priv { + struct aic_sdio_dev *sdiodev; + void *rwnx_vif; + atomic_t rx_cnt; + u32 data_len; + /* lock for rx priv */ + spinlock_t rxqlock; + struct frame_queue rxq; + + /* lock for freeq */ + spinlock_t freeq_lock; + struct list_head rxframes_freequeue; + struct list_head stas_reord_list; + /* lock for reord */ + spinlock_t stas_reord_lock; + struct recv_msdu *recv_frames; +}; + +/* Inline bus wrappers and aicwf_bus_init/deinit are now in aic_bus.h */ + +static inline void aicwf_sched_timeout(u32 millisec) +{ + ulong timeout = 0, expires = 0; + + expires = jiffies + msecs_to_jiffies(millisec); + timeout = millisec; + + while (timeout) { + timeout = schedule_timeout(timeout); + if (time_after(jiffies, expires)) + break; + } +} + +void aicwf_tx_deinit(struct aicwf_tx_priv *tx_priv); +void aicwf_rx_deinit(struct aicwf_rx_priv *rx_priv); +struct aicwf_tx_priv *aicwf_tx_init(void *arg); +struct aicwf_rx_priv *aicwf_rx_init(void *arg); +void aicwf_frame_queue_init(struct frame_queue *pq, int num_prio, int max_len); +void aicwf_frame_queue_flush(struct frame_queue *pq); +bool aicwf_frame_enq(struct device *dev, struct frame_queue *q, + struct sk_buff *pkt, int prio); +bool aicwf_rxframe_enqueue(struct device *dev, struct frame_queue *q, + struct sk_buff *pkt); +bool aicwf_is_framequeue_empty(struct frame_queue *pq); +void aicwf_frame_tx(void *dev, struct sk_buff *skb); +void aicwf_dev_skb_free(struct sk_buff *skb); +struct sk_buff *aicwf_frame_dequeue(struct frame_queue *pq); +struct sk_buff *aicwf_frame_queue_peek_tail(struct frame_queue *pq, + int *prio_out); + +#endif /* _AICWF_TXRXIF_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_bsp/aicwf_txq_prealloc.c b/drivers/net/wireless/aic/aic8800_bsp/aicwf_txq_prealloc.c new file mode 100644 index 0000000000000..0e0387f502e12 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aicwf_txq_prealloc.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief bsp txq prealloc mem + * + ****************************************************************************** + */ +#include "aic_bsp_driver.h" +#include "aicsdio_txrxif.h" +#include "aicwf_txq_prealloc.h" +#include + +struct prealloc_txq { + int prealloced; + void *txq; + size_t size; +}; + +static struct prealloc_txq prealloc_txq; +#define MAX_TXQ_SIZE (100 * 1024) + +/** + * aicwf_prealloc_txq_alloc - Allocate or reuse the preallocated TX queue + * @size: Required queue size in bytes + * + * Return: Pointer to a zeroed queue buffer, or %NULL if allocation failed. + */ +void *aicwf_prealloc_txq_alloc(size_t size) +{ + WARN_ON_ONCE(size > MAX_TXQ_SIZE); + + // check prealloc_txq.size + if ((int)prealloc_txq.size != (int)size) { + AICWFDBG(LOGINFO, "%s size is diff will to be kzalloc \r\n", __func__); + + if (prealloc_txq.txq) { + AICWFDBG(LOGINFO, "%s txq to kfree \r\n", __func__); + kfree(prealloc_txq.txq); + prealloc_txq.txq = NULL; + } + + prealloc_txq.size = size; + prealloc_txq.prealloced = 0; + } + // check prealloc or not + if (!prealloc_txq.prealloced) { + prealloc_txq.txq = kzalloc(size, GFP_KERNEL); + if (!prealloc_txq.txq) { + AICWFDBG(LOGERROR, "%s txq kzalloc fail \r\n", __func__); + } else { + AICWFDBG(LOGINFO, "%s txq kzalloc successful \r\n", __func__); + prealloc_txq.prealloced = 1; + } + } else { + AICWFDBG(LOGINFO, "%s txq not need to kzalloc \r\n", __func__); + } + + return prealloc_txq.txq; +} + +void aicwf_prealloc_txq_free(void) +{ + if (prealloc_txq.txq) { + AICWFDBG(LOGINFO, "%s txq to kfree \r\n", __func__); + kfree(prealloc_txq.txq); + prealloc_txq.txq = NULL; + } +} +EXPORT_SYMBOL_GPL(aicwf_prealloc_txq_alloc); diff --git a/drivers/net/wireless/aic/aic8800_bsp/aicwf_txq_prealloc.h b/drivers/net/wireless/aic/aic8800_bsp/aicwf_txq_prealloc.h new file mode 100644 index 0000000000000..680d37ffca931 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_bsp/aicwf_txq_prealloc.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _AICWF_TXQ_PREALLOC_H_ +#define _AICWF_TXQ_PREALLOC_H_ + +void *aicwf_prealloc_txq_alloc(size_t size); +void aicwf_prealloc_txq_free(void); + +#endif diff --git a/drivers/net/wireless/aic/aic8800_fdrv/Kconfig b/drivers/net/wireless/aic/aic8800_fdrv/Kconfig new file mode 100644 index 0000000000000..59cf740aa6202 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/Kconfig @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig AIC8800_WLAN_SUPPORT + tristate "AIC8800D80 U02 SDIO station support" + depends on AIC8800_BSP_SUPPORT + depends on CFG80211 + help + Enable the AIC8800D80 U02 FullMAC Wi-Fi driver for the first + upstreaming baseline. This build is restricted to the SDIO station + path and uses cfg80211/nl80211 for normal Wi-Fi operation. + + If unsure, say N. + +if AIC8800_WLAN_SUPPORT + +config RWNX_FULLMAC + def_bool y + +config AIC8800_STA_ONLY + def_bool y + help + Limit the initial upstream driver to station mode. The AP, P2P, + monitor, mesh and TDLS implementations remain in the source tree + so they can be enabled when those modes are ready for upstream. + +config AIC8800_NONSTA_SUPPORT + def_bool y if !AIC8800_STA_ONLY + +config AIC8800_GENL + bool "AIC8800 private generic netlink interface" + help + Enable the private generic netlink command interface used by + vendor tools. + + If unsure, say N. + +endif # AIC8800_WLAN_SUPPORT diff --git a/drivers/net/wireless/aic/aic8800_fdrv/Makefile b/drivers/net/wireless/aic/aic8800_fdrv/Makefile new file mode 100644 index 0000000000000..025a7906612df --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/Makefile @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_AIC8800_WLAN_SUPPORT) += aic8800_fdrv.o + +aic8800_fdrv-y := \ + rwnx_msg_tx.o \ + rwnx_msg_rx.o \ + rwnx_utils.o \ + rwnx_cmds.o \ + rwnx_irqs.o \ + rwnx_cfgfile.o \ + rwnx_strs.o \ + rwnx_rx.o \ + rwnx_tx.o \ + rwnx_txq.o \ + rwnx_main.o \ + rwnx_mod_params.o \ + rwnx_platform.o \ + aicwf_chip_8800d80.o \ + aicwf_chip_ops.o \ + rwnx_v7.o \ + ipc_host.o \ + aicwf_compat_8800d80.o \ + regdb.o \ + aic_priv_cmd.o \ + sdio_host.o \ + aicwf_txrxif.o \ + aicwf_sdio.o \ + aicwf_rx_prealloc.o + +aic8800_fdrv-$(CONFIG_AIC8800_NONSTA_SUPPORT) += rwnx_mesh.o rwnx_tdls.o + +aic8800_fdrv-$(CONFIG_AIC8800_GENL) += aicwf_genl.o + +ccflags-y += -I$(srctree) +ccflags-y += -I$(src) +ccflags-y += -I$(src)/../aic8800_bsp +ccflags-y += -DAIC_TRACE_INCLUDE_PATH=$(src) +ccflags-y += -DAICWF_SDIO_SUPPORT +ccflags-y += -DCONFIG_SDIO_PWRCTRL +ccflags-y += -DCONFIG_RWNX_FULLMAC +ccflags-y += -DUSE_5G +ccflags-y += -DAICWF_RX_REORDER +ccflags-y += -DAICWF_ARP_OFFLOAD +ccflags-y += -DCONFIG_RX_NETIF_RECV_SKB +ccflags-y += -DCONFIG_AIC8800_RX_NETIF_RECV_SKB -- 2.34.1