#![allow(dead_code)]
pub const GPIO_BASE: usize = 0x3FF4_4000;
pub const GPIO_OUT_W1TS_OFFSET: usize = 0x08;
pub const GPIO_OUT_W1TC_OFFSET: usize = 0x0C;
pub const GPIO_ENABLE_W1TS_OFFSET: usize = 0x24;
pub const GPIO_ENABLE_W1TC_OFFSET: usize = 0x28;
pub const GPIO_ENABLE1_W1TS_OFFSET: usize = 0x30;
pub const GPIO_ENABLE1_W1TC_OFFSET: usize = 0x34;
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_SEL_DISCONNECT: u32 = 256;
pub const GPIO_FUNC_IN_SEL_MASK: u32 = 0x3F;
pub const GPIO_SIG_IN_SEL: u32 = 1 << 7;
pub const IO_MUX_BASE: usize = 0x3FF4_9000;
pub const IO_MUX_MCU_SEL_SHIFT: u32 = 12;
pub const IO_MUX_MCU_SEL_MASK: u32 = 0x07 << 12;
pub const IO_MUX_FUNC_GPIO: u32 = 2;
pub const IO_MUX_FUNC_EMAC: u32 = 5;
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 = 0x03 << 10;
pub fn configure_smi_pins(mdc_gpio: u8, mdio_gpio: u8) {
if !is_valid_smi_pin(mdc_gpio) || !is_valid_smi_pin(mdio_gpio) {
return;
}
configure_mdc(mdc_gpio);
configure_mdio(mdio_gpio);
}
#[must_use]
pub const fn is_valid_smi_pin(gpio_num: u8) -> bool {
matches!(gpio_num, 0..=23 | 25..=27 | 32..=33)
}
pub fn configure_rmii_pins() {
configure_iomux_output(19, IO_MUX_FUNC_EMAC);
configure_iomux_output(22, IO_MUX_FUNC_EMAC);
configure_iomux_output(21, IO_MUX_FUNC_EMAC);
configure_iomux_input(25, IO_MUX_FUNC_EMAC);
configure_iomux_input(26, IO_MUX_FUNC_EMAC);
configure_iomux_input(27, IO_MUX_FUNC_EMAC);
}
fn configure_mdc(gpio_num: u8) {
unsafe {
if let Some(iomux) = iomux_addr_for_gpio(gpio_num) {
let cur = read_reg(iomux);
let new_val = (cur & !IO_MUX_MCU_SEL_MASK) | (IO_MUX_FUNC_GPIO << IO_MUX_MCU_SEL_SHIFT);
write_reg(iomux, new_val);
}
gpio_output_enable_set(gpio_num);
let out_sel = GPIO_BASE + GPIO_FUNC_OUT_SEL_CFG_BASE + (gpio_num as usize * 4);
write_reg(
out_sel,
(EMAC_MDC_O_IDX & GPIO_FUNC_OUT_SEL_MASK) | GPIO_OEN_SEL,
);
}
}
fn configure_mdio(gpio_num: u8) {
unsafe {
if let Some(iomux) = iomux_addr_for_gpio(gpio_num) {
let cur = read_reg(iomux);
let new_val = (cur & !IO_MUX_MCU_SEL_MASK)
| (IO_MUX_FUNC_GPIO << IO_MUX_MCU_SEL_SHIFT)
| IO_MUX_FUN_IE;
write_reg(iomux, new_val);
}
gpio_output_enable_set(gpio_num);
let out_sel = GPIO_BASE + GPIO_FUNC_OUT_SEL_CFG_BASE + (gpio_num as usize * 4);
write_reg(
out_sel,
(EMAC_MDO_O_IDX & GPIO_FUNC_OUT_SEL_MASK) | GPIO_OEN_SEL,
);
let in_sel = GPIO_BASE + GPIO_FUNC_IN_SEL_CFG_BASE + (EMAC_MDI_I_IDX as usize * 4);
write_reg(
in_sel,
(gpio_num as u32 & GPIO_FUNC_IN_SEL_MASK) | GPIO_SIG_IN_SEL,
);
}
}
fn configure_iomux_output(gpio_num: u8, func: u32) {
let Some(iomux) = iomux_addr_for_gpio(gpio_num) else {
return;
};
unsafe {
let cur = read_reg(iomux);
let new_val = (cur
& !IO_MUX_MCU_SEL_MASK
& !(1 << 7)
& !(1 << 8)
& !IO_MUX_FUN_IE
& !IO_MUX_FUN_DRV_MASK)
| (func << IO_MUX_MCU_SEL_SHIFT)
| (3 << IO_MUX_FUN_DRV_SHIFT);
write_reg(iomux, new_val);
let out_sel = GPIO_BASE + GPIO_FUNC_OUT_SEL_CFG_BASE + (gpio_num as usize * 4);
write_reg(out_sel, GPIO_OUT_SEL_DISCONNECT);
}
}
fn configure_iomux_input(gpio_num: u8, func: u32) {
let Some(iomux) = iomux_addr_for_gpio(gpio_num) else {
return;
};
unsafe {
let cur = read_reg(iomux);
let new_val = (cur & !IO_MUX_MCU_SEL_MASK & !(1 << 7) & !(1 << 8))
| (func << IO_MUX_MCU_SEL_SHIFT)
| IO_MUX_FUN_IE;
write_reg(iomux, new_val);
let out_sel = GPIO_BASE + GPIO_FUNC_OUT_SEL_CFG_BASE + (gpio_num as usize * 4);
write_reg(out_sel, GPIO_OUT_SEL_DISCONNECT);
}
}
#[inline(always)]
unsafe fn read_reg(addr: usize) -> u32 {
unsafe { core::ptr::read_volatile(addr as *const u32) }
}
#[inline(always)]
unsafe fn write_reg(addr: usize, val: u32) {
unsafe { core::ptr::write_volatile(addr as *mut u32, val) }
}
#[inline]
unsafe fn gpio_output_enable_set(gpio_num: u8) {
unsafe {
if gpio_num < 32 {
write_reg(GPIO_BASE + GPIO_ENABLE_W1TS_OFFSET, 1u32 << gpio_num);
} else if gpio_num < 40 {
write_reg(
GPIO_BASE + GPIO_ENABLE1_W1TS_OFFSET,
1u32 << (gpio_num - 32),
);
}
}
}
fn iomux_addr_for_gpio(gpio_num: u8) -> Option<usize> {
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 None,
};
Some(IO_MUX_BASE + offset)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn 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 out_sel_address_gpio23() {
let addr = GPIO_BASE + GPIO_FUNC_OUT_SEL_CFG_BASE + (23 * 4);
assert_eq!(addr, 0x3FF4_458C);
}
#[test]
fn in_sel_address_emac_mdi() {
let addr = GPIO_BASE + GPIO_FUNC_IN_SEL_CFG_BASE + (EMAC_MDI_I_IDX as usize * 4);
assert_eq!(addr, 0x3FF4_4454);
}
#[test]
fn iomux_addresses_for_smi() {
assert_eq!(iomux_addr_for_gpio(18), Some(0x3FF4_9070));
assert_eq!(iomux_addr_for_gpio(23), Some(0x3FF4_908C));
}
#[test]
fn iomux_addr_for_gpio20_is_known() {
assert_eq!(iomux_addr_for_gpio(20), Some(0x3FF4_9078));
}
#[test]
fn iomux_addr_for_gpio_out_of_range_is_none() {
assert_eq!(iomux_addr_for_gpio(24), None);
assert_eq!(iomux_addr_for_gpio(40), None);
}
#[test]
fn smi_pin_validation() {
assert!(is_valid_smi_pin(23));
assert!(is_valid_smi_pin(18));
assert!(is_valid_smi_pin(0));
assert!(is_valid_smi_pin(23));
assert!(is_valid_smi_pin(25));
assert!(is_valid_smi_pin(27));
assert!(!is_valid_smi_pin(24));
assert!(!is_valid_smi_pin(28));
assert!(!is_valid_smi_pin(31));
assert!(is_valid_smi_pin(32));
assert!(is_valid_smi_pin(33));
assert!(!is_valid_smi_pin(34));
assert!(!is_valid_smi_pin(39));
assert!(!is_valid_smi_pin(40));
assert!(!is_valid_smi_pin(255));
}
#[test]
fn enable_register_offsets() {
assert_eq!(GPIO_BASE + GPIO_ENABLE_W1TS_OFFSET, 0x3FF4_4024);
assert_eq!(GPIO_BASE + GPIO_ENABLE_W1TC_OFFSET, 0x3FF4_4028);
assert_eq!(GPIO_BASE + GPIO_ENABLE1_W1TS_OFFSET, 0x3FF4_4030);
assert_eq!(GPIO_BASE + GPIO_ENABLE1_W1TC_OFFSET, 0x3FF4_4034);
}
}