fn clock_specs_for_node(node: NodeType<'_>) -> Vec<ClockSpec> {
let assigned_clocks = node
.as_node()
.get_property("assigned-clocks")
.map(|prop| {
let vals = prop.get_u32_iter().collect::<Vec<_>>();
let mut ids = Vec::new();
for cells in vals.chunks(2) {
if let [_, id] = cells {
ids.push(*id);
}
}
ids
})
.unwrap_or_default();
let assigned_rates = node
.as_node()
.get_property("assigned-clock-rates")
.map(|prop| prop.get_u32_iter().collect::<Vec<_>>())
.unwrap_or_default();
node.clocks()
.into_iter()
.filter_map(|clock| {
let assigned_rate = clock.specifier.first().and_then(|id| {
assigned_clocks
.iter()
.position(|assigned| assigned == id)
.and_then(|index| assigned_rates.get(index).copied())
.filter(|rate| *rate != 0)
});
let id = *clock.specifier.first()?;
Some(ClockSpec {
name: clock.name,
id,
assigned_rate,
})
})
.collect()
}
fn clock_specs(clocks: Vec<ClockRef>) -> Vec<ClockSpec> {
clocks
.into_iter()
.filter_map(|clock| {
let id = *clock.specifier.first()?;
Some(ClockSpec {
name: clock.name,
id,
assigned_rate: None,
})
})
.collect()
}
fn enable_clocks(clocks: &[ClockSpec]) -> Result<(), OnProbeError> {
for clock in clocks {
let id = clock.id;
if id == 0 {
continue;
}
if let Some(rate) = clock.assigned_rate {
rk3588_set_clock_rate(id, u64::from(rate)).map_err(|err| {
OnProbeError::other(format!(
"failed to set RK3588 PCIe clock {:?} ({id:#x}) rate to {rate}: {err}",
clock.name
))
})?;
}
rk3588_enable_clock(id).map_err(|err| {
OnProbeError::other(format!(
"failed to enable RK3588 PCIe clock {:?} ({id:#x}): {err}",
clock.name
))
})?;
}
Ok(())
}
fn parse_resets(node: NodeType<'_>) -> Result<Vec<ResetSpec>, OnProbeError> {
let Some(prop) = node.as_node().get_property("resets") else {
return Ok(Vec::new());
};
let cells = prop.get_u32_iter().collect::<Vec<_>>();
if cells.len() % 2 != 0 {
return Err(OnProbeError::other(format!(
"[{}] has malformed resets",
node.name()
)));
}
let reset_names = prop_str_list(node.as_node(), "reset-names");
Ok(cells
.chunks(2)
.enumerate()
.map(|(idx, chunk)| ResetSpec {
name: reset_names.get(idx).cloned(),
id: u64::from(chunk[1]),
})
.collect())
}
fn assert_resets(resets: &[ResetSpec]) -> Result<(), OnProbeError> {
for reset in resets {
rk3588_reset_assert(reset.id).map_err(|err| {
OnProbeError::other(format!(
"failed to assert RK3588 PCIe reset {:?} ({:#x}): {err}",
reset.name, reset.id
))
})?;
}
Ok(())
}
fn deassert_resets(resets: &[ResetSpec]) -> Result<(), OnProbeError> {
for reset in resets {
rk3588_reset_deassert(reset.id).map_err(|err| {
OnProbeError::other(format!(
"failed to deassert RK3588 PCIe reset {:?} ({:#x}): {err}",
reset.name, reset.id
))
})?;
}
Ok(())
}
fn parse_reset_gpio(info: &FdtInfo<'_>, apb_base: u64) -> Result<Option<GpioSpec>, OnProbeError> {
if let Some(gpio) = parse_gpio_spec(info.node, "reset-gpios")? {
return Ok(Some(gpio));
}
if let Some(default) = rk3588_pcie_reset_pin(apb_base) {
warn!(
"Rockchip RK3588 PCIe host {:#x}: reset-gpios missing; using diagnostic fallback \
GPIO{} pin {}",
apb_base, default.bank, default.pin
);
return Ok(Some(GpioSpec {
bank: default.bank,
pin: default.pin,
active_high: default.active_high,
}));
}
Ok(None)
}
fn parse_gpio_spec(
node_type: NodeType<'_>,
prop_name: &str,
) -> Result<Option<GpioSpec>, OnProbeError> {
let node = node_type.as_node();
let Some(prop) = node.get_property(prop_name) else {
return Ok(None);
};
let mut cells = prop.get_u32_iter();
let phandle_raw = cells.next().ok_or_else(|| {
OnProbeError::other(format!("[{}] has malformed {prop_name}", node.name()))
})?;
let pin = cells.next().ok_or_else(|| {
OnProbeError::other(format!("[{}] has malformed {prop_name}", node.name()))
})?;
let flags = cells.next().unwrap_or(0);
let bank = gpio_bank_from_phandle(Phandle::from(phandle_raw))?;
Ok(Some(GpioSpec {
bank,
pin: pin.try_into().map_err(|_| {
OnProbeError::other(format!(
"[{}] {prop_name} pin {pin} does not fit RK3588 GPIO",
node.name()
))
})?,
active_high: flags & 1 == 0,
}))
}
fn gpio_bank_from_phandle(phandle: Phandle) -> Result<u8, OnProbeError> {
let fdt = live_fdt()?;
let gpio = fdt
.get_by_phandle(phandle)
.ok_or_else(|| OnProbeError::other(format!("GPIO phandle {phandle:?} not found")))?;
gpio_bank_index(gpio.as_node()).ok_or_else(|| {
OnProbeError::other(format!(
"failed to resolve RK3588 GPIO bank for phandle {phandle:?}"
))
})
}
fn gpio_bank_index(node: &Node) -> Option<u8> {
let name = node.name();
if let Some(name) = name
.strip_prefix("gpio")
.filter(|name| !name.starts_with('@'))
&& let Some(bank) = name
.chars()
.next()
.and_then(|ch| ch.to_digit(10))
.and_then(|bank| u8::try_from(bank).ok())
.filter(|bank| usize::from(*bank) < RK3588_GPIO_BASES.len())
{
return Some(bank);
}
let address = gpio_bank_address(node)?;
RK3588_GPIO_BASES
.iter()
.position(|base| *base == address)
.and_then(|bank| u8::try_from(bank).ok())
}
fn gpio_bank_address(node: &Node) -> Option<u64> {
if let Some(address) = node
.name()
.split_once('@')
.and_then(|(_, unit)| u64::from_str_radix(unit, 16).ok())
{
return Some(address);
}
let reg = node.get_property("reg")?.get_u32_iter().collect::<Vec<_>>();
match reg.as_slice() {
[addr] => Some(u64::from(*addr)),
cells if cells.len() >= 2 => Some((u64::from(cells[0]) << 32) | u64::from(cells[1])),
_ => None,
}
}