From: Oleg Zadorozhnyi The priority table is ordered: the slot an input occupies is its priority, and the device selects the lowest occupied slot whose signal is qualified. Getting the priority is therefore a search of the table for the input, and setting it moves the input to the requested slot. An input absent from the table has no priority to report, and the core is told so rather than handed a made-up number. Signed-off-by: Oleg Zadorozhnyi Assisted-by: Claude:claude-4-opus [chat] Signed-off-by: Ali Rouhi --- drivers/dpll/sit9531x/core.c | 76 +++++++++++++++++++++++--- drivers/dpll/sit9531x/core.h | 8 +++ drivers/dpll/sit9531x/dpll.c | 102 +++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 8 deletions(-) diff --git a/drivers/dpll/sit9531x/core.c b/drivers/dpll/sit9531x/core.c index 25418485e975..484c94def578 100644 --- a/drivers/dpll/sit9531x/core.c +++ b/drivers/dpll/sit9531x/core.c @@ -521,21 +521,81 @@ bool sit9531x_input_prio_present(struct sit9531x_dev *sitdev, u8 pll_idx, } /* - * Rebuild a PLL's membership mask from the source codes of its priority - * table. The mask is what the pin state getters test, so it is refreshed - * from exactly the values the table holds -- here after a write, and once - * per poll from the read-back in sit9531x_chan_state_fetch(). + * sit9531x_input_prio_get - read an input's priority slot for a PLL + * @input_idx: input source in hardware encoding (see + * sit9531x_input_hw_src()) + * @prio: output slot position (0 = highest) + * + * Reports the last slot this source occupied on this PLL. The value is + * cached from the hardware table read at startup and refreshed after every + * table write and poll read-back, so pin-get reflects hardware state without + * issuing synchronous register reads per pin. A source with no known slot + * falls back to the lowest-priority valid slot. + * + * Caller must hold sitdev->multiop_lock. + */ +int sit9531x_input_prio_get(struct sit9531x_dev *sitdev, u8 pll_idx, + u8 input_idx, u8 *prio) +{ + const struct sit9531x_chan *chan; + u8 slot; + + lockdep_assert_held(&sitdev->multiop_lock); + + if (pll_idx >= SIT9531X_NUM_PLLS) + return -EINVAL; + input_idx = sit9531x_prio_src_canon(sitdev, input_idx); + if (input_idx >= SIT9531X_PRIO_NUM_SRC) + return -EINVAL; + + chan = &sitdev->chan[pll_idx]; + slot = chan->prio_last[input_idx]; + if (!slot) + slot = SIT9531X_PRIO_MAX_SLOTS; + + *prio = slot - 1; + + return 0; +} + +/* + * Refresh a PLL's cached view of its priority table from the source codes + * the table holds -- here after a write, and once per poll from the + * read-back in sit9531x_chan_state_fetch(). + * + * The membership mask is what the pin state getters test; the per-slot + * copy and the last-slot-seen array are what priority get answers from, + * so neither costs a register read per pin. */ static void sit9531x_prio_mask_build(struct sit9531x_dev *sitdev, u8 pll_idx, const u8 *srcs) { + struct sit9531x_chan *chan = &sitdev->chan[pll_idx]; + u8 first[SIT9531X_PRIO_NUM_SRC] = { 0 }; u16 mask = 0; - u8 slot; + u8 slot, src, src_canon; - for (slot = 0; slot < SIT9531X_PRIO_MAX_SLOTS; slot++) - mask |= BIT(srcs[slot] & SIT9531X_PRIO_NIBBLE_MASK); + for (slot = 0; slot < SIT9531X_PRIO_MAX_SLOTS; slot++) { + src = srcs[slot] & SIT9531X_PRIO_NIBBLE_MASK; + chan->prio_srcs[slot] = src; + src_canon = sit9531x_prio_src_canon(sitdev, src); + if (src_canon >= SIT9531X_PRIO_NUM_SRC) + continue; + + mask |= BIT(src_canon); + if (!first[src_canon]) + first[src_canon] = slot + 1; + } + + /* + * Assign unconditionally: a source that has left the table has no + * slot, and leaving its old one behind would keep reporting it as + * listed for as long as the device runs. + */ + for (src = 0; src < SIT9531X_PRIO_NUM_SRC; src++) + chan->prio_last[src] = first[src]; - sitdev->chan[pll_idx].prio_mask = mask; + chan->prio_mask = mask; } /* Attempts to release a forced holdover before reporting it stuck. */ diff --git a/drivers/dpll/sit9531x/core.h b/drivers/dpll/sit9531x/core.h index f628bd5b7a97..1ab8ebfd78da 100644 --- a/drivers/dpll/sit9531x/core.h +++ b/drivers/dpll/sit9531x/core.h @@ -129,6 +129,12 @@ struct sit9531x_out { * @ho_freeze: holdover freeze active * @ho_valid: holdover memory acquired, i.e. the holdover window * holds a valid estimate to fall back on + * @prio_srcs: cached copy of the priority table, one source code + * per slot; refreshed together with @prio_mask, so + * priority reads generate no register traffic + * @prio_last: slot each source occupies, plus one (0 = the source + * is not in the table); refreshed from the same scan + * as @prio_mask, so the two never disagree * @prio_mask: bit per hardware source code present in this PLL's * priority table, i.e. the sources it may select. Read * back from the table by the periodic worker and @@ -143,6 +149,8 @@ struct sit9531x_chan { bool inner_lol; bool ho_freeze; bool ho_valid; + u8 prio_srcs[SIT9531X_PRIO_MAX_SLOTS]; + u8 prio_last[SIT9531X_PRIO_NUM_SRC]; u16 prio_mask; }; diff --git a/drivers/dpll/sit9531x/dpll.c b/drivers/dpll/sit9531x/dpll.c index ab9aa7db1839..e2ff21adfdb2 100644 --- a/drivers/dpll/sit9531x/dpll.c +++ b/drivers/dpll/sit9531x/dpll.c @@ -469,10 +469,112 @@ sit9531x_dpll_input_pin_state_on_dpll_set(const struct dpll_pin *pin, return rc; } +/* + * sit9531x_dpll_input_pin_prio_get - read input pin priority + * + * Reports the cached slot from sit9531x_input_prio_get(). The cache is + * refreshed from hardware at startup and by periodic read-back, so pin-get + * reports hardware priority without synchronous per-pin I2C reads. + */ +static int +sit9531x_dpll_input_pin_prio_get(const struct dpll_pin *pin, void *pin_priv, + const struct dpll_device *dpll, void *dpll_priv, + u32 *prio, struct netlink_ext_ack *extack) +{ + struct sit9531x_dpll_pin *dpin = pin_priv; + struct sit9531x_dpll *sitdpll = dpll_priv; + struct sit9531x_dev *sitdev = sitdpll->dev; + u8 slot; + int rc; + + mutex_lock(&sitdev->multiop_lock); + rc = sit9531x_input_prio_get(sitdev, sitdpll->id, + sit9531x_input_hw_src(dpin->id), &slot); + mutex_unlock(&sitdev->multiop_lock); + if (rc) + return rc; + + dpin->prio = slot; + *prio = slot; + return 0; +} + +/* + * sit9531x_dpll_input_pin_prio_set - set input pin priority + * + * writes input priority table on Page 1 via + * core.c sit9531x_input_prio_set(). Forces holdover during update. + */ +static int +sit9531x_dpll_input_pin_prio_set(const struct dpll_pin *pin, void *pin_priv, + const struct dpll_device *dpll, void *dpll_priv, + u32 prio, struct netlink_ext_ack *extack) +{ + struct dpll_pin *changed[SIT9531X_MAX_INPUTS + 1]; + struct sit9531x_dpll_pin *sibling; + struct sit9531x_dpll_pin *dpin = pin_priv; + struct sit9531x_dpll *sitdpll = dpll_priv; + struct sit9531x_dev *sitdev = sitdpll->dev; + u8 changed_cnt = 0, hw_src, slot; + int get_rc, rc; + + if (dpin->dir != DPLL_PIN_DIRECTION_INPUT) { + NL_SET_ERR_MSG(extack, "Priority applies only to input pins"); + return -EINVAL; + } + + if (prio >= SIT9531X_PRIO_MAX_SLOTS) { + NL_SET_ERR_MSG(extack, "Priority out of range (0-10)"); + return -EINVAL; + } + + mutex_lock(&sitdev->multiop_lock); + rc = sit9531x_input_prio_set(sitdev, sitdpll->id, + sit9531x_input_hw_src(dpin->id), + (u8)prio); + if (!rc) { + list_for_each_entry(sibling, &sitdpll->pins, list) { + if (!sit9531x_dpll_is_input_pin(sibling) || + sit9531x_dpll_is_xo_pin(sibling)) + continue; + + hw_src = sit9531x_input_hw_src(sibling->id); + get_rc = sit9531x_input_prio_get(sitdev, sitdpll->id, hw_src, &slot); + if (get_rc) + continue; + + if (sibling->prio == slot) + continue; + + sibling->prio = slot; + if (changed_cnt < ARRAY_SIZE(changed)) + changed[changed_cnt++] = sibling->dpll_pin; + } + } + mutex_unlock(&sitdev->multiop_lock); + + if (rc == -EINVAL) { + NL_SET_ERR_MSG(extack, + "Pin is not a reference of this DPLL; connect it first"); + return rc; + } + if (rc) { + NL_SET_ERR_MSG(extack, "Failed to set input priority"); + return rc; + } + + while (changed_cnt--) + dpll_pin_change_ntf(changed[changed_cnt]); + + return 0; +} + static const struct dpll_pin_ops sit9531x_dpll_input_pin_ops = { .direction_get = sit9531x_dpll_input_pin_direction_get, .state_on_dpll_get = sit9531x_dpll_input_pin_state_on_dpll_get, .state_on_dpll_set = sit9531x_dpll_input_pin_state_on_dpll_set, + .prio_get = sit9531x_dpll_input_pin_prio_get, + .prio_set = sit9531x_dpll_input_pin_prio_set, }; /* -- 2.39.2 (Apple Git-143)