use super::{read_reg, write_reg};
#[cfg(feature = "esp32")]
pub const GPIO_BASE: usize = 0x3FF4_4000;
pub const GPIO_ENABLE_W1TS_OFFSET: usize = 0x24;
pub const GPIO_ENABLE_W1TC_OFFSET: usize = 0x28;
pub const GPIO_OUT_W1TS_OFFSET: usize = 0x08;
pub const GPIO_OUT_W1TC_OFFSET: usize = 0x0C;
pub const GPIO_FUNC_IN_SEL_CFG_BASE: usize = 0x130;
pub const GPIO_FUNC_OUT_SEL_CFG_BASE: usize = 0x530;
pub const EMAC_MDC_O_IDX: u32 = 200;
pub const EMAC_MDI_I_IDX: u32 = 201;
pub const EMAC_MDO_O_IDX: u32 = 201;
pub const GPIO_FUNC_OUT_SEL_MASK: u32 = 0x1FF;
pub const GPIO_OEN_SEL: u32 = 1 << 10;
pub const GPIO_OUT_INV_SEL: u32 = 1 << 9;
pub const GPIO_OEN_INV_SEL: u32 = 1 << 11;
pub const GPIO_FUNC_IN_SEL_MASK: u32 = 0x3F;
pub const GPIO_IN_INV_SEL: u32 = 1 << 6;
pub const GPIO_SIG_IN_SEL: u32 = 1 << 7;
#[cfg(feature = "esp32")]
pub const IO_MUX_BASE: usize = 0x3FF4_9000;
pub const IO_MUX_GPIO18_OFFSET: usize = 0x70; pub const IO_MUX_GPIO23_OFFSET: usize = 0x8C;
pub const IO_MUX_MCU_SEL_SHIFT: u32 = 12;
pub const IO_MUX_MCU_SEL_MASK: u32 = 0x7 << 12;
pub const IO_MUX_FUNC_GPIO: u32 = 2;
pub const IO_MUX_FUN_IE: u32 = 1 << 9;
pub const IO_MUX_FUN_DRV_SHIFT: u32 = 10;
pub const IO_MUX_FUN_DRV_MASK: u32 = 0x3 << 10;
pub struct GpioMatrix;
impl GpioMatrix {
pub fn configure_mdc(gpio_num: u8) {
unsafe {
let iomux_addr = Self::iomux_addr_for_gpio(gpio_num);
if iomux_addr != 0 {
let iomux_val = read_reg(iomux_addr);
let new_iomux =
(iomux_val & !IO_MUX_MCU_SEL_MASK) | (IO_MUX_FUNC_GPIO << IO_MUX_MCU_SEL_SHIFT);
write_reg(iomux_addr, new_iomux);
}
write_reg(GPIO_BASE + GPIO_ENABLE_W1TS_OFFSET, 1 << gpio_num);
let out_sel_addr = GPIO_BASE + GPIO_FUNC_OUT_SEL_CFG_BASE + (gpio_num as usize * 4);
let out_sel_val = (EMAC_MDC_O_IDX & GPIO_FUNC_OUT_SEL_MASK) | GPIO_OEN_SEL;
write_reg(out_sel_addr, out_sel_val);
#[cfg(feature = "defmt")]
defmt::debug!(
"GPIO{} configured as MDC: IOMUX={:#010x} OUT_SEL={:#010x}",
gpio_num,
if iomux_addr != 0 {
read_reg(iomux_addr)
} else {
0
},
out_sel_val
);
}
}
pub fn configure_mdio(gpio_num: u8) {
unsafe {
let iomux_addr = Self::iomux_addr_for_gpio(gpio_num);
if iomux_addr != 0 {
let iomux_val = read_reg(iomux_addr);
let new_iomux = (iomux_val & !IO_MUX_MCU_SEL_MASK)
| (IO_MUX_FUNC_GPIO << IO_MUX_MCU_SEL_SHIFT)
| IO_MUX_FUN_IE; write_reg(iomux_addr, new_iomux);
}
write_reg(GPIO_BASE + GPIO_ENABLE_W1TS_OFFSET, 1 << gpio_num);
let out_sel_addr = GPIO_BASE + GPIO_FUNC_OUT_SEL_CFG_BASE + (gpio_num as usize * 4);
let out_sel_val = (EMAC_MDO_O_IDX & GPIO_FUNC_OUT_SEL_MASK) | GPIO_OEN_SEL;
write_reg(out_sel_addr, out_sel_val);
let in_sel_addr = GPIO_BASE + GPIO_FUNC_IN_SEL_CFG_BASE + (EMAC_MDI_I_IDX as usize * 4);
let in_sel_val = (gpio_num as u32 & GPIO_FUNC_IN_SEL_MASK) | GPIO_SIG_IN_SEL;
write_reg(in_sel_addr, in_sel_val);
#[cfg(feature = "defmt")]
defmt::debug!(
"GPIO{} configured as MDIO: IOMUX={:#010x} OUT_SEL={:#010x} IN_SEL={:#010x}",
gpio_num,
if iomux_addr != 0 {
read_reg(iomux_addr)
} else {
0
},
out_sel_val,
in_sel_val
);
}
}
pub fn configure_smi_pins() {
Self::configure_mdc(23);
Self::configure_mdio(18);
}
pub fn configure_smi_pins_custom(mdc_gpio: u8, mdio_gpio: u8) {
Self::configure_mdc(mdc_gpio);
Self::configure_mdio(mdio_gpio);
}
pub fn configure_rmii_pins() {
const EMAC_FUNC: u32 = 5;
Self::configure_iomux_output(19, EMAC_FUNC); Self::configure_iomux_output(22, EMAC_FUNC); Self::configure_iomux_output(21, EMAC_FUNC);
Self::configure_iomux_input(25, EMAC_FUNC); Self::configure_iomux_input(26, EMAC_FUNC); Self::configure_iomux_input(27, EMAC_FUNC);
#[cfg(feature = "defmt")]
defmt::info!("RMII data pins configured via IO_MUX (function 5)");
}
fn configure_iomux_output(gpio_num: u8, func: u32) {
let iomux_addr = Self::iomux_addr_for_gpio(gpio_num);
if iomux_addr == 0 {
return;
}
unsafe {
let current = read_reg(iomux_addr);
let new_val = (current
& !IO_MUX_MCU_SEL_MASK
& !(1 << 7)
& !(1 << 8)
& !IO_MUX_FUN_IE
& !(3 << 10))
| (func << IO_MUX_MCU_SEL_SHIFT)
| (3 << 10); write_reg(iomux_addr, new_val);
let out_sel_addr = GPIO_BASE + GPIO_FUNC_OUT_SEL_CFG_BASE + (gpio_num as usize * 4);
write_reg(out_sel_addr, 256); }
}
fn configure_iomux_input(gpio_num: u8, func: u32) {
let iomux_addr = Self::iomux_addr_for_gpio(gpio_num);
if iomux_addr == 0 {
return;
}
unsafe {
let current = read_reg(iomux_addr);
let new_val = (current & !IO_MUX_MCU_SEL_MASK & !(1 << 7) & !(1 << 8))
| (func << IO_MUX_MCU_SEL_SHIFT)
| IO_MUX_FUN_IE; write_reg(iomux_addr, new_val);
let out_sel_addr = GPIO_BASE + GPIO_FUNC_OUT_SEL_CFG_BASE + (gpio_num as usize * 4);
write_reg(out_sel_addr, 256);
}
}
fn iomux_addr_for_gpio(gpio_num: u8) -> usize {
#[cfg(feature = "esp32")]
let offset = match gpio_num {
0 => 0x44,
1 => 0x88,
2 => 0x40,
3 => 0x84,
4 => 0x48,
5 => 0x6C,
6 => 0x60,
7 => 0x64,
8 => 0x68,
9 => 0x54,
10 => 0x58,
11 => 0x5C,
12 => 0x34,
13 => 0x38,
14 => 0x30,
15 => 0x3C,
16 => 0x4C,
17 => 0x50,
18 => 0x70,
19 => 0x74,
20 => 0x78,
21 => 0x7C,
22 => 0x80,
23 => 0x8C,
25 => 0x24,
26 => 0x28,
27 => 0x2C,
32 => 0x1C,
33 => 0x20,
34 => 0x14,
35 => 0x18,
36 => 0x04,
37 => 0x08,
38 => 0x0C,
39 => 0x10,
_ => return 0,
};
#[cfg(feature = "esp32p4")]
let offset = 0;
if offset == 0 {
return 0;
}
IO_MUX_BASE + offset
}
}
#[cfg(all(test, feature = "esp32"))]
mod tests {
use super::*;
#[test]
fn test_signal_indices() {
assert_eq!(EMAC_MDC_O_IDX, 200);
assert_eq!(EMAC_MDI_I_IDX, 201);
assert_eq!(EMAC_MDO_O_IDX, 201);
}
#[test]
fn test_gpio_out_sel_address() {
let addr = GPIO_BASE + GPIO_FUNC_OUT_SEL_CFG_BASE + (23 * 4);
assert_eq!(addr, 0x3FF4_458C);
}
#[test]
fn test_gpio_in_sel_address() {
let addr = GPIO_BASE + GPIO_FUNC_IN_SEL_CFG_BASE + (EMAC_MDI_I_IDX as usize * 4);
assert_eq!(addr, 0x3FF4_4454);
}
#[test]
fn test_iomux_addresses() {
assert_eq!(GpioMatrix::iomux_addr_for_gpio(18), 0x3FF4_9070);
assert_eq!(GpioMatrix::iomux_addr_for_gpio(23), 0x3FF4_908C);
}
}