use sdio_host::{SdioHost, error::SdioError};
use super::{
super::{
chip::*,
protocol::{
IpcTransport, ipc_mem_block_write, ipc_mem_mask_write, ipc_mem_read, ipc_mem_write,
ipc_start_app,
},
},
data,
};
const CHIP_ID_ADDR: u32 = 0x4050_0000;
const CHIP_SUB_ID_ADDR: u32 = 0x0000_0020;
const CFG_BASE: u32 = 0x0001_0164;
const CHIP_ID_H_MASK: u8 = 0xC0;
const PATCH_TBL_DESC_BYTES: usize = 124 + 4;
const CFG_CHUNK: usize = 512;
const SYSCFG_TBL_DC: &[(u32, u32)] = &[(0x4050_0010, 0x0000_0004), (0x4050_0010, 0x0000_0006)];
const SYSCFG_TBL_DC_SDIO_U02: &[(u32, u32)] = &[
(0x4003_0000, 0x0003_6DA4), (0x0011_E800, 0xE7FE_4070),
(0x4003_0084, 0x0011_E800),
(0x4003_0080, 0x0000_0001),
(0x4010_001C, 0x0000_0000),
];
const SYSCFG_TBL_MASKED_DC: &[(u32, u32, u32)] = &[
(0x7000_216C, 0x3 << 2, 0x1 << 2),
(0x7000_21BC, 0x3 << 2, 0x1 << 2),
(
0x7000_2118,
(0x7 << 4) | (0x1 << 7),
(0x2 << 4) | (0x1 << 7),
),
(0x7000_2104, 0x3F | (0x1 << 6), 0x2 | (0x1 << 6)),
(0x7000_210C, 0x3F | (0x1 << 6), 0x2 | (0x1 << 6)),
(0x7000_2170, 0xF, 0x1),
(0x7000_2190, 0x3F, 24),
(0x7000_21CC, (0x7 << 4) | (0x1 << 7), 0x0),
(0x7000_10A0, 0x1 << 11, 0x1 << 11),
(0x7000_1034, (0x1 << 20) | (0x7 << 26), 0x2 << 26),
(0x7000_1038, 0x1 << 8, 0x1 << 8),
(0x7000_1094, 0x3 << 2, 0x0),
(
0x7000_21D0,
(0x1 << 5) | (0x1 << 6),
(0x1 << 5) | (0x1 << 6),
),
(
0x7000_1000,
(0x1 << 0) | (0x1 << 20) | (0x1 << 22),
(0x1 << 0) | (0x1 << 20),
),
(0x7000_1028, 0xF << 2, 0x1 << 2),
];
const SYSCFG_TBL_MASKED_DC_U01: &[(u32, u32, u32)] = &[
(0x7000_1000, 0x1 << 16, 0x1 << 16),
(0x7000_1028, 0x1 << 6, 0x1 << 6),
(0x7000_1000, 0x1 << 16, 0x0),
];
const SYSCFG_TBL_MASKED_DC_H: &[(u32, u32, u32)] = &[
(
0x7000_216C,
(0x3 << 2) | (0x3 << 4),
(0x2 << 2) | (0x2 << 4),
),
(0x7000_2138, 0xFF, 0xFF),
(0x7000_213C, 0xFF, 0xFF),
(0x7000_2144, 0xFF, 0xFF),
(0x7000_21BC, 0x3 << 2, 0x1 << 2),
(
0x7000_2118,
(0x7 << 4) | (0x1 << 7),
(0x2 << 4) | (0x1 << 7),
),
(0x7000_2104, 0x3F | (0x1 << 6), 0x2 | (0x1 << 6)),
(0x7000_210C, 0x3F | (0x1 << 6), 0x2 | (0x1 << 6)),
(0x7000_2170, 0xF, 0x1),
(0x7000_2190, 0x3F, 24),
(0x7000_21CC, (0x7 << 4) | (0x1 << 7), 0x0),
(0x7000_10A0, 0x1 << 11, 0x1 << 11),
(0x7000_1038, 0x1 << 8, 0x1 << 8),
(0x7000_1094, 0x3 << 2, 0x0),
(
0x7000_21D0,
(0x1 << 5) | (0x1 << 6),
(0x1 << 5) | (0x1 << 6),
),
(
0x7000_1000,
(0x1 << 0) | (0x1 << 20) | (0x1 << 22),
(0x1 << 0) | (0x1 << 20),
),
(0x7000_1028, 0xF << 2, 0x1 << 2),
];
struct DcChipId {
chip_id: u8,
sub_id: u8,
mcu_id: u8,
}
impl DcChipId {
fn is_h(&self) -> bool {
(self.chip_id & CHIP_ID_H_MASK) == CHIP_ID_H_MASK
}
}
fn set_bbpll_config<H: SdioHost>(transport: &mut IpcTransport<H>) -> Result<(), SdioError> {
let v = ipc_mem_read(transport, 0x4050_0148)?;
if v & 0x01 == 0 {
log::debug!("[aic8800] DC crystal not provided by CPU");
return Ok(());
}
let bb = ipc_mem_read(transport, 0x4050_5010)?;
if (bb >> 29) == 3 {
return Ok(()); }
let new = (bb | (0x1 << 29) | (0x1 << 30)) & !(0x1 << 31);
ipc_mem_write(transport, 0x4050_5010, new)?;
Ok(())
}
fn system_config<H: SdioHost>(transport: &mut IpcTransport<H>) -> Result<DcChipId, SdioError> {
let md = ipc_mem_read(transport, CHIP_ID_ADDR)?;
let chip_id = (md >> 16) as u8;
let mcu_id: u8 = if (md >> 25) & 0x1 == 0 { 1 } else { 0 };
let sub_id = (ipc_mem_read(transport, CHIP_SUB_ID_ADDR)? & 0xFF) as u8;
let id = DcChipId {
chip_id,
sub_id,
mcu_id,
};
log::info!(
"[aic8800] DC chip_id=0x{:02x} sub_id=0x{:02x} mcu_id={} is_h={}",
chip_id,
sub_id,
mcu_id,
id.is_h()
);
set_bbpll_config(transport)?;
let _ = ipc_mem_read(transport, 0x4050_0010)?;
for &(a, d) in SYSCFG_TBL_DC {
ipc_mem_write(transport, a, d)?;
}
if mcu_id == 0 && (sub_id == 1 || sub_id == 2) {
for &(a, d) in SYSCFG_TBL_DC_SDIO_U02 {
ipc_mem_write(transport, a, d)?;
}
}
let masked_tbl = if id.is_h() {
SYSCFG_TBL_MASKED_DC_H
} else {
SYSCFG_TBL_MASKED_DC
};
for &(a, mask, data) in masked_tbl {
let (m, dd) = if a == 0x7000_1000 && mcu_id == 0 {
let extra = (0x1 << 8) | (0x1 << 15);
(mask | extra, data | extra)
} else {
(mask, data)
};
ipc_mem_mask_write(transport, a, m, dd)?;
}
if sub_id == 0 {
for &(a, mask, data) in SYSCFG_TBL_MASKED_DC_U01 {
ipc_mem_mask_write(transport, a, mask, data)?;
}
}
Ok(id)
}
fn misc_ram_init<H: SdioHost>(transport: &mut IpcTransport<H>) -> Result<(), SdioError> {
let misc_ram_addr = ipc_mem_read(transport, CFG_BASE + 0x14)?;
log::info!("[aic8800] DC misc_ram_addr=0x{:08x}", misc_ram_addr);
for i in 0..3 {
ipc_mem_write(transport, misc_ram_addr + i * 4, 0)?;
}
Ok(())
}
const RF_MISC_RAM_BITMASK_OFF: u32 = 0;
fn misc_ram_valid<H: SdioHost>(transport: &mut IpcTransport<H>) -> Result<bool, SdioError> {
let misc_ram_addr = ipc_mem_read(transport, CFG_BASE + 0x14)?;
let base = misc_ram_addr + RF_MISC_RAM_BITMASK_OFF;
let mut bm = [0u32; 4];
for (i, slot) in bm.iter_mut().enumerate() {
*slot = ipc_mem_read(transport, base + (i as u32) * 4)?;
}
let valid = bm[0] == 0
&& (bm[1] & 0xFFF0_0000) == 0x8000_0000
&& bm[2] == 0
&& (bm[3] & 0xFFFF_FF00) == 0;
log::info!(
"[aic8800] DC misc_ram bit_mask={:08x},{:08x},{:08x},{:08x} valid={}",
bm[0],
bm[1],
bm[2],
bm[3],
valid
);
Ok(valid)
}
fn dpd_calib<H: SdioHost>(
transport: &mut IpcTransport<H>,
calib_fw: &[u8],
) -> Result<(), SdioError> {
if misc_ram_valid(transport)? {
log::info!("[aic8800] DC misc ram valid, skip dpd calib");
return Ok(());
}
super::upload::upload_firmware(transport, calib_fw, ROM_FMAC_CALIB_ADDR)?;
log::info!("[aic8800] DC calib fw uploaded ({} bytes)", calib_fw.len());
let status = ipc_start_app(transport, ROM_FMAC_CALIB_ADDR + 9, HOST_START_APP_FNCALL)?;
log::info!("[aic8800] DC dpd calib done status=0x{:08x}", status);
Ok(())
}
fn cfg_block_upload<H: SdioHost>(
transport: &mut IpcTransport<H>,
addr: u32,
blob: &[u8],
) -> Result<(), SdioError> {
let mut off = 0;
while off < blob.len() {
let end = core::cmp::min(off + CFG_CHUNK, blob.len());
ipc_mem_block_write(transport, addr + off as u32, &blob[off..end])?;
off = end;
}
Ok(())
}
fn patch_table_load<H: SdioHost>(
transport: &mut IpcTransport<H>,
blob: &[u8],
) -> Result<(), SdioError> {
if blob.len() < 128 {
log::error!("[aic8800] DC patch_tbl too small: {}", blob.len());
return Err(SdioError::Unsupported);
}
let describe_base = u32::from_le_bytes([blob[0], blob[1], blob[2], blob[3]]);
log::debug!(
"[aic8800] DC patch_tbl describe_base=0x{:08x}",
describe_base
);
ipc_mem_block_write(transport, describe_base, &blob[..PATCH_TBL_DESC_BYTES])?;
let mut off = 128;
while off + 8 <= blob.len() {
let a = u32::from_le_bytes([blob[off], blob[off + 1], blob[off + 2], blob[off + 3]]);
let d = u32::from_le_bytes([blob[off + 4], blob[off + 5], blob[off + 6], blob[off + 7]]);
ipc_mem_write(transport, a, d)?;
off += 8;
}
Ok(())
}
fn patch_config<H: SdioHost>(
transport: &mut IpcTransport<H>,
id: &DcChipId,
) -> Result<(), SdioError> {
if id.sub_id == 0 {
log::error!("[aic8800] DC sub_id==0 (u01) not supported by this port");
return Err(SdioError::Unsupported);
}
let wifisetting_cfg_addr = ipc_mem_read(transport, CFG_BASE)?;
let ldpc_cfg_addr = ipc_mem_read(transport, CFG_BASE + 0x8)?;
let agc_cfg_addr = ipc_mem_read(transport, CFG_BASE + 0xC)?;
let txgain_cfg_addr = ipc_mem_read(transport, CFG_BASE + 0x10)?;
log::info!(
"[aic8800] DC cfg: wifi=0x{:08x} ldpc=0x{:08x} agc=0x{:08x} txgain=0x{:08x}",
wifisetting_cfg_addr,
ldpc_cfg_addr,
agc_cfg_addr,
txgain_cfg_addr
);
let ws_addr = wifisetting_cfg_addr + 0x0124;
let ws_cur = ipc_mem_read(transport, ws_addr)?;
let ws_val = (0x0100_1E01 & 0x00FF_FFFF) | (ws_cur & 0xFF00_0000);
log::info!(
"[aic8800] DC wifisetting [0x{:08x}] cur=0x{:08x} -> 0x{:08x}",
ws_addr,
ws_cur,
ws_val
);
ipc_mem_write(transport, ws_addr, ws_val)?;
cfg_block_upload(transport, ldpc_cfg_addr, data::FW_DC_LDPC_CFG)?;
cfg_block_upload(transport, agc_cfg_addr, data::FW_DC_AGC_CFG)?;
let txgain = if id.is_h() {
data::FW_DC_TXGAIN_MAP_H
} else {
data::FW_DC_TXGAIN_MAP
};
cfg_block_upload(transport, txgain_cfg_addr, txgain)?;
let patch_tbl = if id.is_h() {
data::FW_DC_H_FMAC_PATCH_TBL
} else {
data::FW_DC_FMAC_PATCH_TBL
};
patch_table_load(transport, patch_tbl)?;
Ok(())
}
pub fn init<H: SdioHost>(
transport: &mut IpcTransport<H>,
fw_set: &super::data::FirmwareSet,
) -> Result<(), SdioError> {
let id = system_config(transport)?;
log::info!("[aic8800] DC system_config done");
let patch_fw = if id.is_h() {
data::FW_DC_H_PATCH
} else {
fw_set.wl_fw
};
super::upload::upload_firmware(transport, patch_fw, ROM_FMAC_PATCH_ADDR)?;
if id.is_h() {
dpd_calib(transport, data::FW_DC_H_CALIB)?;
} else {
misc_ram_init(transport)?;
}
log::info!("[aic8800] DC patch_load done");
patch_config(transport, &id)?;
log::info!("[aic8800] DC patch_config done");
let rd = ipc_mem_read(transport, RAM_FMAC_FW_ADDR)?;
log::debug!(
"[aic8800] DC fw mem [0x{:08x}]=0x{:08x}",
RAM_FMAC_FW_ADDR,
rd
);
let status = ipc_start_app(transport, RAM_FMAC_FW_ADDR, HOST_START_APP_DUMMY)?;
log::info!("[aic8800] DC start_app status=0x{:08x}", status);
Ok(())
}