IR: the device-tree clock fix does not take effect #43

Open
opened 2026-08-28 09:27:58 +00:00 by tiagoagueda · 1 comment
Owner

Split out of #16, which is otherwise resolved.

IR decodes correctly only while probe/irclk.ko is loaded, which writes the clock register by
hand. The proper device-tree fix does not work.

On the IR consumer node (ir@8002000):

assigned-clocks = <&r_ir_clk>;
assigned-clock-parents = <&osc24M>;
assigned-clock-rates = <8000000>;

These are correct and reach the DTB — assigned-clock-parents resolves to clk-24M
(phandle 0x08), assigned-clock-rates is 8000000, and the device probes (rc0 exists). But
after a reboot the mux still reads 0 and the clock is still 2048 Hz. Tried on the provider node
too, same result.

Why, probably

r_ir_clk is registered with CLK_OF_DECLARE_DRIVER, so it exists both as an early clock
(registered at of_clk_init() time) and as a platform driver. of_clk_set_defaults() runs
at probe (drivers/base/platform.c:1497) and the composite does use clk_mux_ops, which has
.set_parent — so the machinery should work. The suspicion is that it acts on a different
instance than the one the IR device resolves. Not yet confirmed.

The better fix

Rather than forcing the parent per board, give the sunxi factors clock a determine_rate that
can choose a parent. Then sunxi-cir's own clk_set_rate(8000000) would do the right thing
by itself, on every sunxi SoC using a mod0 clock.

This is arguably a latent bug beyond this board: the A31 has the identical clock description
(sun4i-a10-mod0-clk with <&rtc CLK_OSC32K>, <&osc24M>) and nothing forces its mux either. It
only works there because of what its bootloader happens to leave behind.

  • Tree: clk (drivers/clk/sunxi/clk-factors.c) — lists: linux-clk, linux-sunxi
  • Notes: 39-ir-and-leds.md
  • Patch as it stands: patches/linux-dts-sun9i-ir-clk/ (committed with the caveat in its
    message that it does not take effect)

⚠️ Nothing has been submitted upstream. See 42-upstreaming.md.

Split out of #16, which is otherwise resolved. IR decodes correctly **only while `probe/irclk.ko` is loaded**, which writes the clock register by hand. The proper device-tree fix does not work. On the IR consumer node (`ir@8002000`): ```dts assigned-clocks = <&r_ir_clk>; assigned-clock-parents = <&osc24M>; assigned-clock-rates = <8000000>; ``` These are **correct and reach the DTB** — `assigned-clock-parents` resolves to `clk-24M` (phandle `0x08`), `assigned-clock-rates` is 8000000, and the device probes (`rc0` exists). But after a reboot the mux still reads 0 and the clock is still 2048 Hz. Tried on the provider node too, same result. ### Why, probably `r_ir_clk` is registered with `CLK_OF_DECLARE_DRIVER`, so it exists **both** as an early clock (registered at `of_clk_init()` time) **and** as a platform driver. `of_clk_set_defaults()` runs at probe (`drivers/base/platform.c:1497`) and the composite does use `clk_mux_ops`, which has `.set_parent` — so the machinery should work. The suspicion is that it acts on a different instance than the one the IR device resolves. Not yet confirmed. ### The better fix Rather than forcing the parent per board, give the sunxi factors clock a `determine_rate` that can **choose a parent**. Then `sunxi-cir`'s own `clk_set_rate(8000000)` would do the right thing by itself, on every sunxi SoC using a mod0 clock. This is arguably a latent bug beyond this board: the **A31 has the identical clock description** (`sun4i-a10-mod0-clk` with `<&rtc CLK_OSC32K>, <&osc24M>`) and nothing forces its mux either. It only works there because of what its bootloader happens to leave behind. - Tree: clk (`drivers/clk/sunxi/clk-factors.c`) — lists: linux-clk, linux-sunxi - Notes: [39-ir-and-leds.md](39-ir-and-leds.md) - Patch as it stands: `patches/linux-dts-sun9i-ir-clk/` (committed with the caveat in its message that it does not take effect) --- ⚠️ **Nothing has been submitted upstream.** See [42-upstreaming.md](42-upstreaming.md).
Author
Owner

Re-scoped rather than fixed, because the diagnosis in this issue is wrong - and so is the fix it
proposes. Measured on the running board with a throwaway module (probe/irclkdbg.ko) that
replays what __set_clk_parents() does, one call at a time, printing every return value.

The device tree was never the problem

num_parents=2 flags=00000800                    (CLK_IS_CRITICAL only)
  parent[0] = osc32k rate=32768
  parent[1] = osc24M rate=24000000
of_clk_get_from_provider(parent)   = 0 (osc24M)
of_clk_get_from_provider(assigned) = 0 (r_ir)
clk_set_parent()        = 0   reg 80000000 -> 81000000, parent=osc24M, 24 MHz
clk_round_rate(8000000) = 8000000
clk_set_rate(8000000)   = 0   reg 81000000 -> 80000000, parent=osc32k, 32768

So:

  • assigned-clock-parents works. of_clk_set_defaults() reaches the right instance, resolves
    both clocks, and clk_set_parent() returns 0 and moves the mux. The CLK_OF_DECLARE_DRIVER
    double-instance theory in the original post is not what is happening.
  • clk_factors_determine_rate() already picks the right parent. clk_round_rate(8 MHz)
    returns exactly 8000000 from osc24M. The "better fix" proposed above - give the factors clock a
    determine_rate that can choose a parent - would change nothing, because it already has one and
    it already chooses correctly.
  • What undoes it is sunxi-cir's own clk_set_rate(SUNXI_IR_BASE_CLK) in probe, which runs
    after of_clk_set_defaults() and reparents the clock back to osc32k while returning success.

clk_factors_set_rate() only read-modify-writes the n/k/m/p fields, so it is not clobbering the
mux. This is a genuine CCF reparent.

It lands on parent[0] no matter what is asked

ordering result
set_parent(osc24M) then set_rate(8M) reverts to osc32k / 32768
set_rate(8M) then set_parent(osc24M) stays osc24M, but M=0 so 24 MHz
...then set_rate(8M) again reverts to osc32k
set_rate(12 MHz) - unreachable from osc32k still osc32k / 32768, returns 0
with clk_set_rate_range(1 MHz, 24 MHz) still osc32k / 32768, returns 0

The last two are the damning ones. clk_set_rate() selects a parent that physically cannot
produce the requested rate, ignores an explicit rate floor, and reports success - while
clk_round_rate(), on the same clock in the same state, answers correctly.

What this means

No ordering of the public clk API produces 8 MHz on osc24M, so this cannot be fixed from the
device tree at all.
The DT patch in patches/linux-dts-sun9i-ir-clk/ describes the hardware
correctly and should stay, but it will never be sufficient on its own.

probe/irclk.ko is still the only thing that makes IR work, and it works precisely because it
bypasses the CCF and writes the register.

Next step

Instrument clk_calc_new_rates() / clk_change_rate() to find where the parent chosen by
determine_rate is lost between the round and the set. The candidates worth printing are
core->new_parent, core->new_parent_index, and what clk_composite_set_rate_and_parent()
receives - a composite with both rate_ops->set_rate and mux_ops->set_parent takes that path
rather than plain set_rate.

Worth restating the wider claim in the original post, which still holds and is now better
supported: the A31 has the identical clock description and nothing forces its mux either, so if
this is a core bug rather than something specific to how sunxi builds the composite, it is latent
on more than this board.

The title should probably change too - it is not that "the device-tree clock fix does not take
effect", it is that clk_set_rate() reparents away from it.

Re-scoped rather than fixed, because the diagnosis in this issue is wrong - and so is the fix it proposes. Measured on the running board with a throwaway module (`probe/irclkdbg.ko`) that replays what `__set_clk_parents()` does, one call at a time, printing every return value. ## The device tree was never the problem ``` num_parents=2 flags=00000800 (CLK_IS_CRITICAL only) parent[0] = osc32k rate=32768 parent[1] = osc24M rate=24000000 of_clk_get_from_provider(parent) = 0 (osc24M) of_clk_get_from_provider(assigned) = 0 (r_ir) clk_set_parent() = 0 reg 80000000 -> 81000000, parent=osc24M, 24 MHz clk_round_rate(8000000) = 8000000 clk_set_rate(8000000) = 0 reg 81000000 -> 80000000, parent=osc32k, 32768 ``` So: - **`assigned-clock-parents` works.** `of_clk_set_defaults()` reaches the right instance, resolves both clocks, and `clk_set_parent()` returns 0 and moves the mux. The `CLK_OF_DECLARE_DRIVER` double-instance theory in the original post is not what is happening. - **`clk_factors_determine_rate()` already picks the right parent.** `clk_round_rate(8 MHz)` returns exactly 8000000 from osc24M. The "better fix" proposed above - give the factors clock a `determine_rate` that can choose a parent - would change nothing, because it already has one and it already chooses correctly. - **What undoes it is `sunxi-cir`'s own `clk_set_rate(SUNXI_IR_BASE_CLK)` in probe**, which runs *after* `of_clk_set_defaults()` and reparents the clock back to osc32k while returning success. `clk_factors_set_rate()` only read-modify-writes the n/k/m/p fields, so it is not clobbering the mux. This is a genuine CCF reparent. ## It lands on parent[0] no matter what is asked | ordering | result | |---|---| | `set_parent(osc24M)` then `set_rate(8M)` | reverts to osc32k / 32768 | | `set_rate(8M)` then `set_parent(osc24M)` | stays osc24M, but M=0 so 24 MHz | | ...then `set_rate(8M)` again | reverts to osc32k | | `set_rate(12 MHz)` - **unreachable** from osc32k | **still osc32k / 32768**, returns 0 | | with `clk_set_rate_range(1 MHz, 24 MHz)` | **still osc32k / 32768**, returns 0 | The last two are the damning ones. `clk_set_rate()` selects a parent that physically cannot produce the requested rate, ignores an explicit rate floor, and reports success - while `clk_round_rate()`, on the same clock in the same state, answers correctly. ## What this means **No ordering of the public clk API produces 8 MHz on osc24M, so this cannot be fixed from the device tree at all.** The DT patch in `patches/linux-dts-sun9i-ir-clk/` describes the hardware correctly and should stay, but it will never be sufficient on its own. `probe/irclk.ko` is still the only thing that makes IR work, and it works precisely because it bypasses the CCF and writes the register. ## Next step Instrument `clk_calc_new_rates()` / `clk_change_rate()` to find where the parent chosen by `determine_rate` is lost between the round and the set. The candidates worth printing are `core->new_parent`, `core->new_parent_index`, and what `clk_composite_set_rate_and_parent()` receives - a composite with both `rate_ops->set_rate` and `mux_ops->set_parent` takes that path rather than plain `set_rate`. Worth restating the wider claim in the original post, which still holds and is now better supported: the A31 has the identical clock description and nothing forces its mux either, so if this is a core bug rather than something specific to how sunxi builds the composite, it is latent on more than this board. The title should probably change too - it is not that "the device-tree clock fix does not take effect", it is that `clk_set_rate()` reparents away from it.
Sign in to join this conversation.
No description provided.