"opt.ok_or(err)?" is a pretty neat way of returning early with the error "err" if opt is empty. This is pretty idiomatic Rust you should get familiar with before comparing the readability with a language you know better.
I see a few places where Rust is definitely more readable, for example
for offset in 0..PL061_GPIO_NR {
if inner.csave_regs.gpio_dir & bit(offset) != 0 {
vs
for (offset = 0; offset < PL061_GPIO_NR; offset++) {
if (pl061->csave_regs.gpio_dir & (BIT(offset)))
OTOH, the next line is a real mouthful in Rust compared to C:
if let Ok(v) = <Self as gpio::Chip>::get(data, offset.into()) {
inner.csave_regs.gpio_data |= (v as u8) << offset;
}
vs
pl061->csave_regs.gpio_data |=
pl061_get_value(&pl061->gc, offset) << offset;
* "<Self as gpio::Chip>::" is the price Rust pays for having multiple possible "get()" for the same data, but it could be hidden away in a wrapper if needed.
* ".into()" might be due to a young not-yet-ergonomic API
* The last difference comes from the API difference where Rust's get() actually tells you if it could get that integer instead of (presumably) returning the integer 0. The C API is arguably a footgun, justifying Rust's slightly wordier syntax.
For a more subjective example, I find "pl061.base.readb(GPIODIR)" more readable than "readb(pl061->base + GPIODIR)". Bonus: the offset argument can be enum-typed, avoiding the footgun of reading from an invalid offset.
Going back to the probe functions, I find them hard to compare as I do not know how the hardware works. If that "device::Data::new()" is equivalent to the many lines of init in the C version, it looks less footguny. In the same vein, "Ref::try_new_and_init()" and "data.registrations()" look like they are giving the developer more guarantees.
There's a trend there : some Rust APIs may be wordier but still easier to read/review, because they uphold more invariants, which reduces the reviewer's cognitive load.
Concerning the ack() functions I'm not sure. It seems that Rust is checking that it has proper access to the data ressource while C doesn't. I would turn the question around : How are you sure that "gpiochip_get_data(irq_data_get_irq_chip_data(...))" never fails and can be safely dereferrenced ?