use log::{error, info};
use rd_net::WifiLinkPolicy;
use rdrive::{probe::OnProbeError, register::ProbeFdt};
use sdhci_cv1800::{
CviSdhci,
hw_init::{Sdio1HwConfig, sdio1_hw_init},
};
use sdio_host::SdioHost;
use crate::net::PlatformDeviceNet;
const SYSCON_PADDR: usize = 0x0300_0000;
const CRG_PADDR: usize = 0x0300_2000;
const RTCSYS_CTRL_PADDR: usize = 0x0502_5000;
const RTCSYS_IO_PADDR: usize = 0x0502_7000;
const MMIO_PAGE: usize = 0x1000;
const SYSCON_SIZE: usize = 0x2000;
const AP_SSID: &[u8] = b"PicoClaw-Car";
const AP_CHANNEL: u8 = 6;
const AP_SERVER_IP: [u8; 4] = [192, 168, 50, 1];
const AP_CLIENT_IP: [u8; 4] = [192, 168, 50, 2];
const AP_PREFIX_LEN: u8 = 24;
crate::model_register!(
name: "AIC8800 WiFi (CV181x SDIO)",
level: ProbeLevel::PostKernel,
priority: ProbePriority::DEFAULT,
probe_kinds: &[ProbeKind::Fdt {
compatibles: &["cvitek,cv181x-sdio"],
on_probe: probe
}],
);
fn map_mmio(paddr: usize, size: usize) -> Result<usize, OnProbeError> {
crate::mmio::iomap(paddr, size).map(|p| p.as_ptr() as usize)
}
fn resolve_fdt_irq(
info: &rdrive::register::FdtInfo<'_>,
) -> Result<axklib::irq::IrqId, OnProbeError> {
let interrupt =
info.interrupts().into_iter().next().ok_or_else(|| {
OnProbeError::other(alloc::format!("[{}] has no irq", info.node.name()))
})?;
let parent = info
.phandle_to_device_id(interrupt.interrupt_parent)
.ok_or_else(|| {
OnProbeError::other(alloc::format!(
"[{}] interrupt-parent {} is not registered",
info.node.name(),
interrupt.interrupt_parent
))
})?;
let mut intc = rdrive::get::<rdif_intc::Intc>(parent)
.map_err(|err| OnProbeError::other(alloc::format!("failed to get IRQ parent: {err:?}")))?
.lock()
.map_err(|err| OnProbeError::other(alloc::format!("failed to lock IRQ parent: {err:?}")))?;
let translation = intc
.translate_fdt(&interrupt.specifier)
.map_err(|err| OnProbeError::other(alloc::format!("failed to translate IRQ: {err:?}")))?;
intc.configure(&translation)
.map_err(|err| OnProbeError::other(alloc::format!("failed to configure IRQ: {err:?}")))?;
Ok(translation.id)
}
fn sdio1_irq_handler(_ctx: axklib::irq::IrqContext) -> axklib::irq::IrqReturn {
sdhci_cv1800::irq::sdhci_irq_handler(0);
axklib::irq::IrqReturn::Handled
}
fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> {
let (info, plat_dev) = probe.into_parts();
let sdio1_reg =
info.node.regs().into_iter().next().ok_or_else(|| {
OnProbeError::other(alloc::format!("[{}] has no reg", info.node.name()))
})?;
let sdio1_paddr = sdio1_reg.address as usize;
let cfg = Sdio1HwConfig::new(
map_mmio(CRG_PADDR, MMIO_PAGE)?,
map_mmio(SYSCON_PADDR, SYSCON_SIZE)?,
map_mmio(RTCSYS_CTRL_PADDR, MMIO_PAGE)?,
map_mmio(RTCSYS_IO_PADDR, MMIO_PAGE)?,
map_mmio(sdio1_paddr, MMIO_PAGE)?,
0,
);
info!(
"[wifi] SDIO1 HW init (node={}, sdio1={:#x})",
info.node.name(),
sdio1_paddr
);
sdio1_hw_init(&cfg);
let irq = resolve_fdt_irq(&info)?;
info!("[wifi] SDIO1 IRQ resolved to {irq:?}");
let irq_handle = axklib::irq::request_shared_disabled(irq, sdio1_irq_handler).map_err(|e| {
OnProbeError::other(alloc::format!(
"[wifi] failed to register SDIO1 IRQ {irq:?}: {e:?}"
))
})?;
let mut sdio = CviSdhci::new(cfg.sdio1_base_va);
if let Err(e) = sdio.init() {
let _ = axklib::irq::free(irq_handle);
return Err(OnProbeError::other(alloc::format!(
"[wifi] SDIO1 init failed: {e:?}"
)));
}
let (vid, did) = sdio.vendor_device_id();
info!("[wifi] SDIO device: vendor={vid:#06x} device={did:#06x}");
sdio.prepare_first_data_xfer();
let mut wifi = match aic8800::probe(sdio) {
Ok(wifi) => wifi,
Err(e) => {
let _ = axklib::irq::free(irq_handle);
return Err(OnProbeError::other(alloc::format!(
"[wifi] chip probe failed: {e}"
)));
}
};
info!("[wifi] chip probe complete");
if let Err(e) = rd_net::WifiControl::start_ap_open(&mut wifi, AP_SSID, AP_CHANNEL) {
error!("[wifi] AP start failed: {e:?}");
let _ = axklib::irq::free(irq_handle);
return Err(OnProbeError::other(alloc::format!(
"[wifi] AP start failed: {e:?}"
)));
}
info!("[wifi] SoftAP started, channel {AP_CHANNEL}");
if let Err(e) = axklib::irq::enable(irq_handle) {
let _ = axklib::irq::free(irq_handle);
return Err(OnProbeError::other(alloc::format!(
"[wifi] failed to enable SDIO1 IRQ {irq:?}: {e:?}"
)));
}
info!("[wifi] SDIO1 IRQ {irq:?} registered and enabled");
let wifi = wifi.with_link_policy(WifiLinkPolicy {
ip: AP_SERVER_IP,
prefix_len: AP_PREFIX_LEN,
dhcp_server_client_ip: Some(AP_CLIENT_IP),
});
plat_dev.register_net("wlan0", wifi);
info!("[wifi] wlan0 device registered (probe stage done)");
Ok(())
}