* Re: [PATCH RFC v5 08/18] riscv_cbqri: Add capacity controller monitoring device ops
From: sashiko-bot @ 2026-05-25 0:29 UTC (permalink / raw)
To: Drew Fustini; +Cc: devicetree, robh, conor+dt
In-Reply-To: <20260524-ssqosid-cbqri-rqsc-v7-0-v5-8-78d3a7ba9dbe@kernel.org>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Using GENMASK() instead of GENMASK_ULL() for 64-bit register fields causes the upper 32 bits of the register to be unintentionally cleared on 32-bit architectures.
- [High] Monitoring support is incorrectly probed by issuing a READ_COUNTER operation for an invalid event on an unconfigured slot, which may falsely fail and disable monitoring.
- [Medium] Probing monitoring capabilities reuses cbqri_probe_feature() on MON_CTL, illegally writing to reserved (WPRI) bits to test for Access Type (AT) support.
- [Medium] Lack of bounds checking on mcid_count allows arbitrarily large loops that can block the CPU during hotplug, and causes implicit hardware ID aliasing.
--
commit 1c3fe5b330888927a5ac94480749c86c852d54d3
Author: Drew Fustini <fustini@kernel.org>
Subject: riscv_cbqri: Add capacity controller monitoring device ops
This commit introduces capacity monitoring operations for the RISC-V capacity
controller. It provides functions to pre-arm hardware slots with monitoring
events and execute monitoring register operations. It also updates the probe
sequence to detect capacity and bandwidth monitoring capabilities.
> diff --git a/drivers/resctrl/cbqri_devices.c b/drivers/resctrl/cbqri_devices.c
> index a97511825eedf..90a95fd111909 100644
> --- a/drivers/resctrl/cbqri_devices.c
> +++ b/drivers/resctrl/cbqri_devices.c
[ ... ]
> @@ -369,6 +407,28 @@ static int cbqri_probe_cc(struct cbqri_controller *ctrl)
> }
> cpus_read_unlock();
>
> + /* Probe monitoring features */
> + err = cbqri_probe_feature(ctrl, CBQRI_CC_MON_CTL_OFF,
> + CBQRI_CC_MON_CTL_OP_READ_COUNTER, &status,
> + &has_mon_at_code);
[Severity: Medium]
Is it safe to reuse cbqri_probe_feature() for the MON_CTL register here?
cbqri_probe_feature() attempts to verify Access Type (AT) support by writing
to the AT field bits. However, the MON_CTL register does not define an AT
field, which means this implicitly writes to reserved (WPRI) bits. Could
this helper be updated to conditionally skip the AT probe sequence for
monitoring registers?
[Severity: High]
Will this probe sequence falsely disable monitoring on hardware?
This invokes cbqri_probe_feature() using the READ_COUNTER operation, which
zeroes out all other fields including EVT_ID. This sends a read command for
the NONE event (EVT_ID=0) to an unconfigured slot, which the hardware may
reject with an error status. If the hardware returns an error, this check
will fail and disable monitoring. Could this probe instead use
CONFIG_EVENT or handle expected failure codes?
> + if (err)
> + return err;
> +
[ ... ]
> @@ -436,6 +496,28 @@ static int cbqri_probe_controller(struct cbqri_controller *ctrl)
> return err;
> }
>
> +/*
> + * Pre-arm every MCID with the Occupancy event so a subsequent READ_COUNTER
> + * just snapshots the live counter rather than re-configuring the slot.
> + * Called once per CC during resctrl-side cpuhp online for the L3 monitoring
> + * domain.
> + */
> +int cbqri_init_mon_counters(struct cbqri_controller *ctrl)
> +{
> + int i, err;
> +
> + for (i = 0; i < ctrl->mcid_count; i++) {
> + mutex_lock(&ctrl->lock);
> + err = cbqri_mon_op(ctrl, CBQRI_CC_MON_CTL_OFF,
> + CBQRI_CC_MON_CTL_OP_CONFIG_EVENT,
> + i, CBQRI_CC_EVT_ID_OCCUPANCY, NULL);
[Severity: Medium]
Does mcid_count need bounds checking against the physical hardware limit?
The ctrl->mcid_count value is used as the loop boundary, but if firmware
supplies an erroneously large value, this loop could block the CPU for an
extended period during hotplug.
Additionally, because the MCID field mask is 12 bits, any mcid_count larger
than 4096 will be truncated in cbqri_mon_op(), causing logical MCIDs to
incorrectly alias to the same physical hardware slot. Should an upper bound be
enforced on mcid_count?
> + mutex_unlock(&ctrl->lock);
> + if (err)
> + return err;
> + }
> + return 0;
> +}
> +
> void cbqri_controller_destroy(struct cbqri_controller *ctrl)
> {
> /*
> diff --git a/drivers/resctrl/cbqri_internal.h b/drivers/resctrl/cbqri_internal.h
> index cce13026a11ed..e776b0267837a 100644
> --- a/drivers/resctrl/cbqri_internal.h
> +++ b/drivers/resctrl/cbqri_internal.h
[ ... ]
> @@ -39,6 +41,20 @@
> #define CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT 2
> #define CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS 1
>
> +#define CBQRI_CC_MON_CTL_OP_CONFIG_EVENT 1
> +#define CBQRI_CC_MON_CTL_OP_READ_COUNTER 2
> +
> +/* mon_ctl field masks (CC and BC share an identical OP/MCID/EVT_ID/STATUS layout) */
> +#define CBQRI_MON_CTL_OP_MASK GENMASK(4, 0)
> +#define CBQRI_MON_CTL_MCID_MASK GENMASK(19, 8)
> +#define CBQRI_MON_CTL_EVT_ID_MASK GENMASK(27, 20)
[Severity: High]
Will using GENMASK() here inadvertently clear the upper 32 bits of the 64-bit
control register on 32-bit architectures?
When used with FIELD_MODIFY(), ~(_mask) will evaluate to a 32-bit unsigned
value which gets zero-extended to 64 bits. When this is bitwise ANDed with
the 64-bit register variable, it will wipe out the upper 32 bits, corrupting
the STATUS field before the register is written back. Should these 64-bit
field masks be defined with GENMASK_ULL() instead?
> +#define CBQRI_MON_CTL_STATUS_MASK GENMASK_ULL(38, 32)
> +#define CBQRI_MON_CTL_STATUS_SUCCESS 1
> +
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260524-ssqosid-cbqri-rqsc-v7-0-v5-0-78d3a7ba9dbe@kernel.org?part=8
^ permalink raw reply
* Re: [PATCH v3 2/4] iio: light: add support for veml6031x00 ALS series
From: Javier Carrasco @ 2026-05-25 0:29 UTC (permalink / raw)
To: sashiko-reviews, Javier Carrasco; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260524225336.450AF1F000E9@smtp.kernel.org>
On Mon May 25, 2026 at 12:53 AM CEST, sashiko-bot wrote:
> Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
> - [High] Mixing `devm_pm_runtime_get_noresume()` with manual `pm_runtime_put_autosuspend()` causes a PM usage counter underflow.
> - [High] `veml6031x00_write_raw()` modifies hardware registers over I2C without acquiring a runtime PM reference.
> - [High] The shutdown devres action performs I2C writes without ensuring the device and its parent are resumed.
> - [Medium] Missing locks in `veml6031x00_read_raw()` for `IIO_CHAN_INFO_SCALE` and `IIO_CHAN_INFO_INT_TIME` allow torn reads of configuration fields.
> - [Medium] Incorrect scale attribute exposure for the IR channel and failure to mark shared hardware attributes.
> - [Medium] The driver unconditionally sleeps for the integration time on every raw read, introducing unnecessary latency.
> --
...
>> +static int veml6031x00_write_raw(struct iio_dev *iio,
>> + struct iio_chan_spec const *chan,
>> + int val, int val2, long mask)
>> +{
>> + IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim);
>> + if (IIO_DEV_ACQUIRE_FAILED(claim))
>> + return -EBUSY;
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_INT_TIME:
>> + return veml6031x00_set_it(iio, val, val2);
>> + case IIO_CHAN_INFO_SCALE:
>> + return veml6031x00_set_scale(iio, val, val2);
>
> [Severity: High]
> Does this code modify hardware registers over I2C without acquiring a runtime
> PM reference?
>
> Unlike veml6031x00_single_read(), this function directly writes to the
> sensor's configuration registers without a PM reference. Because the regmap is
> not configured with .pm_runtime = true, I2C transactions may be attempted
> while the client device (or its parent controller) is runtime suspended, which
> can trigger bus transfer errors.
>
The registers are always accessible as long as the device is powered,
both in normal operation and power down. Therefore, this function cannot
be compared to veml6031x00_single_read() that acquires a PM reference to
carry out a measurement, and the write operations work fine.
>> +static int veml6031x00_probe(struct i2c_client *i2c)
>> +{
>
> [ ... ]
>
>> + ret = devm_add_action_or_reset(dev, veml6031x00_als_shutdown_action, data);
>> + if (ret)
>> + return dev_err_probe(dev, ret, "Failed to add shutdown action\n");
>
> [Severity: High]
> Can this shutdown devres action perform I2C writes while the device is
> suspended?
>
> Because this devres action is registered before
> devm_pm_runtime_set_active_enabled(), on driver unbind, PM is disabled and the
> device state is forced to suspended before the shutdown action executes due to
> devres LIFO execution order.
>
> The shutdown action then attempts to perform I2C writes to the shutdown
> register. Accessing the I2C bus while the device state is suspended can cause
> bus errors or hangs.
>
> [ ... ]
>
>> + ret = devm_pm_runtime_set_active_enabled(dev);
>> + if (ret)
>> + return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
>> +
>> + ret = devm_pm_runtime_get_noresume(dev);
>> + if (ret)
>> + return dev_err_probe(dev, ret, "Failed to get runtime PM\n");
>
> [ ... ]
>
>> + ret = veml6031x00_hw_init(iio);
>> + if (ret)
>> + return ret;
>> +
>> + pm_runtime_put_autosuspend(dev);
>
> [Severity: High]
> Does mixing devm_pm_runtime_get_noresume() with manual
> pm_runtime_put_autosuspend() cause a PM usage counter underflow?
>
> The devm_pm_runtime_get_noresume(dev) call increments the PM usage counter
> and registers a devres action to decrement it on driver unbind.
>
> However, the explicit pm_runtime_put_autosuspend(dev) decrements the usage
> counter to its pre-get state. When the driver is subsequently removed (or if
> probe fails after the put), the devres action will blindly decrement the
> counter a second time, resulting in an underflow.
I believe Sashiko is misinterpreting how this pattern works (not
introduced by me, it is already available upstream in
drivers/spi/atmel-quadspi.c). Decrementing the usage counter is fine
because the devres action uses pm_runtime_put_noidle(), which in turns
uses atomic_add_unless(usage_count, -1, 0) to avoid decrementing the
usage_count if it is already 0. Sashiko has reported the same issue in
other patches of this series.
Best,
Javier
^ permalink raw reply
* Re: [PATCH RFC v5 03/18] riscv: add support for srmcfg CSR from Ssqosid extension
From: sashiko-bot @ 2026-05-25 0:30 UTC (permalink / raw)
To: Drew Fustini; +Cc: robh, devicetree, conor+dt
In-Reply-To: <20260524-ssqosid-cbqri-rqsc-v7-0-v5-3-78d3a7ba9dbe@kernel.org>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Missing explicit restoration of the CSR_SRMCFG hardware register for the currently executing task after PM resume.
- [High] Stale `cpu_srmcfg` cache is not invalidated during CPU offline, leading to Resource Control bypass during CPU bring-up.
--
commit a2aa09c6b4fa4803db0225c807231e47944cc13e
Author: Drew Fustini <fustini@kernel.org>
riscv: add support for srmcfg CSR from Ssqosid extension
This commit adds support for the srmcfg CSR defined in the Ssqosid ISA
extension, allowing tasks to be tagged with Resource Control ID (RCID) and
Monitoring Counter ID (MCID). It handles context switching by saving and
restoring the srmcfg value per task, while caching it per-cpu to avoid
redundant writes.
Link: https://github.com/riscv/riscv-ssqosid/releases/tag/v1.0
> diff --git a/arch/riscv/kernel/qos.c b/arch/riscv/kernel/qos.c
> new file mode 100644
> index 0000000000000..2f3fbb08dcc97
> --- /dev/null
> +++ b/arch/riscv/kernel/qos.c
[ ... ]
> +static int riscv_srmcfg_pm_notify(struct notifier_block *nb,
> + unsigned long action, void *unused)
> +{
> + switch (action) {
> + case CPU_PM_EXIT:
> + case CPU_PM_ENTER_FAILED:
> + __this_cpu_write(cpu_srmcfg, U32_MAX);
> + break;
> + }
> + return NOTIFY_OK;
> +}
[Severity: High]
Does this leave the currently executing task with an incorrect hardware CSR
state?
While the software cache is invalidated by setting cpu_srmcfg to U32_MAX,
the hardware CSR_SRMCFG register is not explicitly reprogrammed during
CPU_PM_EXIT.
Since the hardware state may be lost or reset to an implementation-defined
value upon resume, wouldn't the task running at resume time continue executing
with the wrong QoS tag until it is eventually preempted?
[ ... ]
> +static int __init riscv_srmcfg_init(void)
> +{
> + int err;
> +
> + if (!riscv_has_extension_unlikely(RISCV_ISA_EXT_SSQOSID))
> + return 0;
> +
> + /*
> + * cpuhp_setup_state() invokes the startup callback locally on every
> + * already-online CPU, so no separate seed loop is needed here.
> + */
> + err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "riscv/srmcfg:online",
> + riscv_srmcfg_online, NULL);
[Severity: High]
Should a teardown callback be provided to cpuhp_setup_state() to invalidate
the cpu_srmcfg cache when a CPU goes offline?
Without a teardown callback, a CPU going offline retains its previous
cpu_srmcfg value. When brought back online, its hardware CSR is reset.
Tasks scheduling before the CPU reaches the CPUHP_AP_ONLINE_DYN state might
skip the CSR write if their thread srmcfg tag matches the stale cache. Could
this allow those early context switches to bypass resource constraints by
running with the hardware-reset CSR?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260524-ssqosid-cbqri-rqsc-v7-0-v5-0-78d3a7ba9dbe@kernel.org?part=3
^ permalink raw reply
* Re: [PATCH RFC v5 07/18] riscv_cbqri: Add capacity controller probe and allocation device ops
From: sashiko-bot @ 2026-05-25 0:30 UTC (permalink / raw)
To: Drew Fustini; +Cc: devicetree, conor+dt, robh
In-Reply-To: <20260524-ssqosid-cbqri-rqsc-v7-0-v5-7-78d3a7ba9dbe@kernel.org>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Build failure due to undefined macro `CBQRI_MON_CTL_EVT_ID_MASK`.
- [High] Generic registration API unconditionally calls ACPI-specific functions, breaking DT support.
- [High] Synchronous `cache_size` resolution during early boot permanently disables occupancy monitoring.
- [High] Bitwise inversion of `GENMASK` 32-bit masks silently zeroes the upper 32 bits of 64-bit hardware registers.
- [Medium] Invalid bounds check for `rcid_count` uses a register bitmask instead of a max count, and `mcid_count` is unchecked.
--
commit 13b45d9c450f72077987054f81435f84c681a8fe
Author: Drew Fustini <fustini@kernel.org>
Subject: riscv_cbqri: Add capacity controller probe and allocation device ops
Add support for the RISC-V CBQRI capacity controller (CC). The firmware
discovery layer (ACPI or DT) is responsible for passing the
cbqri_controller_info descriptor to riscv_cbqri_register_controller().
> diff --git a/drivers/resctrl/cbqri_devices.c b/drivers/resctrl/cbqri_devices.c
> new file mode 100644
> index 0000000000000..a97511825eedf
> --- /dev/null
> +++ b/drivers/resctrl/cbqri_devices.c
> @@ -0,0 +1,564 @@
[ ... ]
> +static int cbqri_cc_alloc_op(struct cbqri_controller *ctrl, int operation,
> + int rcid, enum cbqri_at at)
> +{
> + int reg_offset = CBQRI_CC_ALLOC_CTL_OFF;
> + int status;
> + u64 reg;
> +
> + lockdep_assert_held(&ctrl->lock);
> +
> + if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) {
> + pr_err_ratelimited("BUSY timeout before starting operation\n");
> + return -EIO;
> + }
> + FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_OP_MASK, ®, operation);
> + FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_RCID_MASK, ®, rcid);
> +
> + /*
> + * CBQRI Table 1: AT 0=Data, 1=Code. Program AT on controllers
> + * that report supports_alloc_at_code. On controllers that don't,
> + * AT is reserved-zero and the op acts on both halves.
> + */
> + reg &= ~CBQRI_CONTROL_REGISTERS_AT_MASK;
[Severity: High]
On 32-bit architectures, does bitwise inversion of a GENMASK macro (which
evaluates to unsigned long) silently zero the upper 32 bits of the 64-bit
register variable?
If the 32-bit inverted mask is zero-extended to 64 bits, this would truncate
bits 63:32, potentially clearing the STATUS and BUSY fields. Should
GENMASK_ULL be used for these masks instead?
> + if (ctrl->cc.supports_alloc_at_code)
> + reg |= FIELD_PREP(CBQRI_CONTROL_REGISTERS_AT_MASK, at);
[ ... ]
> +static int cbqri_probe_feature(struct cbqri_controller *ctrl, int reg_offset,
> + int operation, int *status, bool *access_type_supported)
> +{
> + const u64 active_mask = CBQRI_CONTROL_REGISTERS_OP_MASK |
> + CBQRI_CONTROL_REGISTERS_AT_MASK |
> + CBQRI_CONTROL_REGISTERS_RCID_MASK |
> + CBQRI_MON_CTL_EVT_ID_MASK;
[Severity: High]
Is CBQRI_MON_CTL_EVT_ID_MASK defined in this patch? It seems to be missing,
which might cause a build failure due to an undeclared identifier.
> + u64 reg, saved_reg;
> + int at;
[ ... ]
> +static int cbqri_probe_cc(struct cbqri_controller *ctrl)
> +{
> + int err, status;
> + u64 reg;
[ ... ]
> + /*
> + * Resolve cache_size via cacheinfo. cpus_read_lock satisfies
> + * lockdep_assert_cpus_held() inside get_cpu_cacheinfo_level(). If
> + * every cpu_mask member is offline, cache_size stays 0 and the
> + * controller cannot back occupancy monitoring.
> + */
> + cpus_read_lock();
> + if (!ctrl->cache.cache_size) {
> + int cpu = cpumask_first_and(&ctrl->cache.cpu_mask, cpu_online_mask);
> +
> + if (cpu < nr_cpu_ids) {
> + struct cacheinfo *ci;
> +
> + ci = get_cpu_cacheinfo_level(cpu, ctrl->cache.cache_level);
> + if (ci)
> + ctrl->cache.cache_size = ci->size;
[Severity: High]
Is this resolution actually lazy? cbqri_probe_cc() is called immediately
during registration by cbqri_probe_controller(), which happens at
acpi_arch_init time.
Since cacheinfo is unpopulated during early boot, won't
get_cpu_cacheinfo_level() return NULL and leave cache_size as 0 permanently,
disabling occupancy monitoring?
> + }
> + }
> + cpus_read_unlock();
[ ... ]
> +int riscv_cbqri_register_controller(const struct cbqri_controller_info *info)
> +{
> + struct cbqri_controller *ctrl;
> + int err;
[ ... ]
> + ctrl->addr = info->addr;
> + ctrl->size = info->size;
> + ctrl->type = info->type;
> + ctrl->rcid_count = info->rcid_count;
> + ctrl->mcid_count = info->mcid_count;
> +
> + /*
> + * SRMCFG encodes RCID in 12 bits. ACPI's acpi_parse_rqsc() already
> + * caps info->rcid_count at CBQRI_MAX_RCID (1024) so this is unreachable
> + * today, but a future DT discovery path or a malformed firmware table
> + * routed through a different validator could bypass that ceiling.
> + * Catch the violation here rather than silently truncating in every
> + * FIELD_PREP(SRMCFG_RCID_MASK, closid) on the schedule-in fast path.
> + */
> + if (WARN_ON_ONCE(ctrl->rcid_count > SRMCFG_RCID_MASK)) {
[Severity: Medium]
Does this correctly validate the count limit? If SRMCFG_RCID_MASK is a
register bitmask (like 0xFFF), won't a hardware limit of 4096 IDs be
incorrectly rejected because 4096 is greater than 4095?
Also, should there be a similar bounds check for mcid_count?
> + cbqri_controller_destroy(ctrl);
> + return -EINVAL;
> + }
> +
> + switch (info->type) {
> + case CBQRI_CONTROLLER_TYPE_CAPACITY: {
> + int level;
> +
> + ctrl->cache.cache_id = info->cache_id;
> +
> + level = find_acpi_cache_level_from_id(info->cache_id);
[Severity: High]
Does this unconditional call to ACPI-specific functions break Device Tree
support?
If CONFIG_ACPI is disabled, these stubs return -ENOENT. For DT-based
platforms, these functions might fail to resolve DT cache IDs, potentially
causing registration to fail entirely.
> + if (level < 0) {
> + pr_warn("Failed to resolve cache level for cache id 0x%x (%d), skipping\n",
> + info->cache_id, level);
> + cbqri_controller_destroy(ctrl);
> + return level;
> + }
> + ctrl->cache.cache_level = level;
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260524-ssqosid-cbqri-rqsc-v7-0-v5-0-78d3a7ba9dbe@kernel.org?part=7
^ permalink raw reply
* Re: [PATCH RFC v5 09/18] riscv_cbqri: Add bandwidth controller probe and allocation device ops
From: sashiko-bot @ 2026-05-25 0:30 UTC (permalink / raw)
To: Drew Fustini; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260524-ssqosid-cbqri-rqsc-v7-0-v5-9-78d3a7ba9dbe@kernel.org>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The standalone read functions (`cbqri_read_rbwb` and `cbqri_read_mweight`) are vulnerable to the silent READ_LIMIT no-op hardware bug described in the commit message.
- [High] The bandwidth allocation operation inadvertently preserves the Access Type (AT) field left by firmware or prior probe operations, potentially applying limits to the wrong access type.
- [Medium] Modifying the hardware staging register before verifying the controller is ready (BUSY=0) can cause silent corruption of the unmodified field.
--
commit 43b9fe0d5868fdc96ff8dc83b2e5d5819da5ef2e
Author: Drew Fustini <fustini@kernel.org>
riscv_cbqri: Add bandwidth controller probe and allocation device ops
This commit adds support for CBQRI bandwidth controller discovery and
allocation control knobs (Rbwb and Mweight). It implements software caches
and pre-write sentinels to prevent stale data leakage caused by a silent
hardware bug during READ_LIMIT operations.
> diff --git a/drivers/resctrl/cbqri_devices.c b/drivers/resctrl/cbqri_devices.c
> index 90a95fd111909..2a80197764a9c 100644
> --- a/drivers/resctrl/cbqri_devices.c
> +++ b/drivers/resctrl/cbqri_devices.c
[ ... ]
> @@ -135,6 +193,41 @@ int cbqri_mon_op(struct cbqri_controller *ctrl, int reg_offset,
> return 0;
> }
>
> +/*
> + * Perform bandwidth allocation control operation on bandwidth controller.
> + * Caller must hold ctrl->lock.
> + */
> +static int cbqri_bc_alloc_op(struct cbqri_controller *ctrl, int operation, int rcid)
> +{
> + int reg_offset = CBQRI_BC_ALLOC_CTL_OFF;
> + int status;
> + u64 reg;
> +
> + lockdep_assert_held(&ctrl->lock);
> +
> + if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) {
> + pr_err_ratelimited("BUSY timeout before starting operation\n");
> + return -EIO;
> + }
> + FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_OP_MASK, ®, operation);
> + FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_RCID_MASK, ®, rcid);
> + iowrite64(reg, ctrl->base + reg_offset);
[Severity: High]
Does this operation inadvertently preserve the Access Type (AT) field left over
by firmware or prior probe operations?
Since this reads the control register and modifies only OP and RCID via
FIELD_MODIFY, the AT bits are left untouched. If the field was left set to
a specific type (such as CBQRI_CONTROL_REGISTERS_AT_CODE during probe),
would the limits applied here silently inherit that type, throttling only
Code bandwidth while leaving Data bandwidth unconstrained?
[ ... ]
> @@ -266,6 +359,152 @@ int cbqri_read_cache_config(struct cbqri_controller *ctrl, u32 closid,
> return err;
> }
>
> +/*
> + * Apply a per-RCID update to one field (Rbwb or Mweight) of bc_bw_alloc.
> + * bc_bw_alloc packs both fields, so both halves are seeded from the
> + * authoritative software caches before CONFIG_LIMIT. This avoids the
> + * silent READ_LIMIT no-op window where stale data from a prior op's
> + * RCID could leak into the unmodified field. The verify step uses an
> + * inverted-value sentinel to confirm hardware accepted the target field.
> + *
> + * Caller must hold ctrl->lock.
> + */
> +static int cbqri_apply_bc_field(struct cbqri_controller *ctrl, u32 closid,
> + enum cbqri_bc_field field, u64 val)
> +{
> + u64 rbwb = ctrl->rbwb_cache[closid];
> + u64 mweight = ctrl->mweight_cache[closid];
> + u64 readback;
> + int ret;
> +
> + lockdep_assert_held(&ctrl->lock);
> +
> + if (field == CBQRI_BC_FIELD_RBWB)
> + rbwb = val;
> + else
> + mweight = val;
> +
> + cbqri_set_bc_bw_alloc(ctrl, rbwb, mweight);
> +
> + ret = cbqri_bc_alloc_op(ctrl, CBQRI_BC_ALLOC_CTL_OP_CONFIG_LIMIT, closid);
[Severity: Medium]
Could modifying the hardware staging register before verifying the controller
is ready cause a silent corruption of the unmodified field?
If a previous operation timed out but is still executing, the BUSY flag would
be 1, and the hardware might ignore the cbqri_set_bc_bw_alloc() write.
If that prior operation then completes while cbqri_bc_alloc_op() polls for
BUSY=0, the new CONFIG_LIMIT would be issued. Could the hardware then apply
the stale staging register payload?
If the modified field in the stale payload happens to match the target value,
the post-write sentinel verification would falsely succeed, while the
unmodified field is silently corrupted.
[ ... ]
> +/*
> + * Read the Rbwb (reserved bandwidth blocks) for closid via READ_LIMIT.
> + */
> +int cbqri_read_rbwb(struct cbqri_controller *ctrl, u32 closid, u64 *rbwb_out)
> +{
> + int err;
> +
> + mutex_lock(&ctrl->lock);
> + err = cbqri_bc_alloc_op(ctrl, CBQRI_BC_ALLOC_CTL_OP_READ_LIMIT, closid);
> + if (err == 0)
> + *rbwb_out = cbqri_get_rbwb(ctrl);
> + mutex_unlock(&ctrl->lock);
> + return err;
> +}
> +
> +/*
> + * Read the Mweight (opportunistic weight) for closid via READ_LIMIT.
> + */
> +int cbqri_read_mweight(struct cbqri_controller *ctrl, u32 closid, u64 *mweight_out)
> +{
> + int err;
> +
> + mutex_lock(&ctrl->lock);
> + err = cbqri_bc_alloc_op(ctrl, CBQRI_BC_ALLOC_CTL_OP_READ_LIMIT, closid);
> + if (err == 0)
> + *mweight_out = cbqri_get_mweight(ctrl);
> + mutex_unlock(&ctrl->lock);
> + return err;
> +}
[Severity: High]
Are these standalone read functions vulnerable to the same silent READ_LIMIT
no-op hardware bug described in the commit message?
Since they execute a bare READ_LIMIT without the pre-write sentinel validation
used in cbqri_apply_bc_field(), could they return stale data from a prior
operation if the hardware bug triggers?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260524-ssqosid-cbqri-rqsc-v7-0-v5-0-78d3a7ba9dbe@kernel.org?part=9
^ permalink raw reply
* Re: [PATCH RFC v5 10/18] riscv_cbqri: Add bandwidth controller monitoring device ops
From: sashiko-bot @ 2026-05-25 0:36 UTC (permalink / raw)
To: Drew Fustini; +Cc: devicetree, robh, conor+dt
In-Reply-To: <20260524-ssqosid-cbqri-rqsc-v7-0-v5-10-78d3a7ba9dbe@kernel.org>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] FIELD_MODIFY() with 32-bit GENMASK clobbers the upper 32 bits of u64 registers on 32-bit architectures.
--
commit 2899144e0d033d58b2dccb062c36578b86f11e62
Author: Drew Fustini <fustini@kernel.org>
riscv_cbqri: Add bandwidth controller monitoring device ops
This commit adds bandwidth controller monitoring primitives. It allocates a
per-MCID software accumulator to extend the 62-bit hardware counter into a
64-bit byte total and handles overflow when reading the counter. It also
provides a function to discover the single mon-capable bandwidth controller.
> diff --git a/drivers/resctrl/cbqri_internal.h b/drivers/resctrl/cbqri_internal.h
> index 48f8a6e1923e2..555512cf7eab9 100644
> --- a/drivers/resctrl/cbqri_internal.h
> +++ b/drivers/resctrl/cbqri_internal.h
[ ... ]
> @@ -63,12 +63,26 @@
> #define CBQRI_CC_MON_CTL_OP_CONFIG_EVENT 1
> #define CBQRI_CC_MON_CTL_OP_READ_COUNTER 2
>
> +#define CBQRI_BC_MON_CTL_OP_CONFIG_EVENT 1
> #define CBQRI_BC_MON_CTL_OP_READ_COUNTER 2
>
> -/* mon_ctl field masks (CC and BC share an identical OP/MCID/EVT_ID/STATUS layout) */
> -#define CBQRI_MON_CTL_OP_MASK GENMASK(4, 0)
> -#define CBQRI_MON_CTL_MCID_MASK GENMASK(19, 8)
> -#define CBQRI_MON_CTL_EVT_ID_MASK GENMASK(27, 20)
> +/* Bandwidth usage monitoring event IDs (CBQRI spec Table 10) */
> +#define CBQRI_BC_EVT_ID_TOTAL_READ_WRITE 1
> +
> +/* bc_mon_ctr_val layout (CBQRI spec section 4.3, Figure 7) */
> +#define CBQRI_BC_MON_CTR_VAL_CTR_MASK GENMASK_ULL(61, 0)
> +#define CBQRI_BC_MON_CTR_VAL_INVALID BIT_ULL(62)
> +#define CBQRI_BC_MON_CTR_VAL_OVF BIT_ULL(63)
> +
> +/*
> + * mon_ctl field masks (CC and BC share an identical OP/MCID/EVT_ID/STATUS
> + * layout). Keep all masks u64-wide so FIELD_MODIFY() on a u64 register
> + * never zero-extends a 32-bit ~mask and clobbers the WPRI/STATUS/BUSY
> + * fields in bits 63:32 for future support of RV32
> + */
> +#define CBQRI_MON_CTL_OP_MASK GENMASK_ULL(4, 0)
> +#define CBQRI_MON_CTL_MCID_MASK GENMASK_ULL(19, 8)
> +#define CBQRI_MON_CTL_EVT_ID_MASK GENMASK_ULL(27, 20)
[Severity: High]
This isn't a bug introduced by this patch, but does the same fix need to be
applied to other masks?
It looks like the CBQRI_CONTROL_REGISTERS_*_MASK macros (for example,
CBQRI_CONTROL_REGISTERS_OP_MASK) continue to use the 32-bit GENMASK().
When these 32-bit masks are passed to FIELD_MODIFY() on a u64 variable in
functions like cbqri_cc_alloc_op(), standard C promotion rules will zero-extend
the 32-bit ~mask value. This would clear bits 63:32 of the register, clobbering
the WPRI, STATUS, and BUSY fields when written back to the device via
iowrite64().
Should these other macros also be upgraded to GENMASK_ULL() to prevent silent
hardware state corruption on 32-bit architectures like RV32?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260524-ssqosid-cbqri-rqsc-v7-0-v5-0-78d3a7ba9dbe@kernel.org?part=10
^ permalink raw reply
* Re: [PATCH RFC v5 12/18] riscv_cbqri: resctrl: Add L3 cache occupancy monitoring
From: sashiko-bot @ 2026-05-25 0:46 UTC (permalink / raw)
To: Drew Fustini; +Cc: devicetree, robh, conor+dt
In-Reply-To: <20260524-ssqosid-cbqri-rqsc-v7-0-v5-12-78d3a7ba9dbe@kernel.org>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Freeing the monitor domain after a non-sync cancellation of delayed work causes a Use-After-Free vulnerability.
- [High] The `resctrl_arch_reset_rmid` re-arming logic is ineffective because the resctrl core never invokes it for Occupancy events.
- [Medium] The reported `num_rmid` capability is hardcoded to the first controller's `mcid_count`, leading to incorrect capabilities reporting.
- [Low] The `cbqri_resctrl_counters` array is defined and populated but never used.
--
commit e0ea68d0dff2235cb1ae515b494803e2b894c85f
Author: Drew Fustini <fustini@kernel.org>
riscv_cbqri: resctrl: Add L3 cache occupancy monitoring
This commit exposes QOS_L3_OCCUP_EVENT_ID to allow userspace to read
per-MCID llc_occupancy. It creates L3 mon_domains lazily on the first CPU of
a cache_id, linked to the paired ctrl_domain. It also implements architecture
specific resctrl hooks to read and reset RMIDs.
> resctrl_arch_reset_rmid() re-arms CONFIG_EVENT with EVT_ID=Occupancy.
> CONFIG_EVENT both resets the counter to 0 and selects the event, so
> re-arming with the same event keeps the MCID counting after reset rather
> than relying on sticky-last-event semantics that the CBQRI register
> definition does not guarantee.
[Severity: High]
Does the resctrl core actually invoke resctrl_arch_reset_rmid() for Occupancy
events?
The resctrl core only invokes resctrl_arch_reset_rmid() individually for MBM
events during mon_event_read() (when rr->first is true). For
QOS_L3_OCCUP_EVENT_ID, mon_event_read() is never called with first = true,
and the recycle path (__check_limbo()) only reads the counter without
resetting.
Will this cause the Occupancy counter to stop functioning when an RMID is
recycled since it is never re-armed?
> diff --git a/drivers/resctrl/cbqri_resctrl.c b/drivers/resctrl/cbqri_resctrl.c
> index 1e6198e25fd29..3ba895c3f8111 100644
> --- a/drivers/resctrl/cbqri_resctrl.c
> +++ b/drivers/resctrl/cbqri_resctrl.c
[ ... ]
> @@ -32,7 +33,18 @@ struct cbqri_resctrl_dom {
>
> static struct cbqri_resctrl_res cbqri_resctrl_resources[RDT_NUM_RESOURCES];
>
> +/*
> + * Per-event controller table. Only events CBQRI can back occupy a
> + * slot, so other events do not bloat the array.
> + */
> +#define CBQRI_MAX_EVENT QOS_L3_OCCUP_EVENT_ID
> +static struct cbqri_controller *cbqri_resctrl_counters[CBQRI_MAX_EVENT + 1];
[Severity: Low]
Is this array necessary?
The patch introduces this per-event controller array and populates it in
cbqri_resctrl_pick_counters(), but it appears to be unused.
resctrl_arch_rmid_read() dynamically resolves the controller via
cbqri_find_ctrl_domain() instead.
[ ... ]
> @@ -500,6 +630,14 @@ static int cbqri_resctrl_control_init(struct cbqri_resctrl_res *cbqri_res)
> res->alloc_capable = ctrl->alloc_capable;
> INIT_LIST_HEAD(&res->ctrl_domains);
> INIT_LIST_HEAD(&res->mon_domains);
> +
> + if (ctrl->mon_capable && res->rid == RDT_RESOURCE_L3) {
> + res->mon_scope = RESCTRL_L3_CACHE;
> + res->mon.num_rmid = ctrl->mcid_count;
[Severity: Medium]
Will this expose an inflated number of RMIDs to userspace if subsequent
controllers have fewer MCIDs?
cbqri_resctrl_accumulate_caps() calculates max_rmid as the system-wide
minimum mcid_count across all controllers, which bounds internal
resctrl allocations.
However, res->mon.num_rmid is initialized here using only the first
controller's mcid_count. If a subsequent controller has a smaller mcid_count,
could this cause userspace to receive unexpected "Out of RMIDs" errors when
creating groups?
[ ... ]
> +static void cbqri_detach_cpu_from_l3_mon(struct rdt_resource *res,
> + unsigned int cpu)
> +{
> + struct rdt_l3_mon_domain *mon_dom, *tmp;
> +
> + lockdep_assert_held(&cbqri_domain_list_lock);
> +
> + list_for_each_entry_safe(mon_dom, tmp, &res->mon_domains, hdr.list) {
> + if (!cpumask_test_cpu(cpu, &mon_dom->hdr.cpu_mask))
> + continue;
> + cpumask_clear_cpu(cpu, &mon_dom->hdr.cpu_mask);
> + if (cpumask_empty(&mon_dom->hdr.cpu_mask)) {
> + cancel_delayed_work(&mon_dom->cqm_limbo);
> + if (resctrl_is_mon_event_enabled(QOS_L3_MBM_TOTAL_EVENT_ID))
> + cancel_delayed_work(&mon_dom->mbm_over);
> + resctrl_offline_mon_domain(res, &mon_dom->hdr);
> + list_del(&mon_dom->hdr.list);
> + kfree(mon_dom);
[Severity: High]
Can this cause a use-after-free if the delayed work is executing?
cancel_delayed_work() is used without synchronization (_sync) to avoid a
deadlock against cpus_write_lock(). However, if the work is currently
executing but has not yet acquired cpus_read_lock() (or is preempted),
cancel_delayed_work() returns immediately.
The hotplug thread then frees the domain with kfree(mon_dom). When the
delayed work thread resumes and acquires cpus_read_lock(), won't it access
its work_struct and other fields within the freed mon_dom structure?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260524-ssqosid-cbqri-rqsc-v7-0-v5-0-78d3a7ba9dbe@kernel.org?part=12
^ permalink raw reply
* Re: [PATCH RFC v5 11/18] riscv_cbqri: resctrl: Add cache allocation via capacity block mask
From: sashiko-bot @ 2026-05-25 0:50 UTC (permalink / raw)
To: Drew Fustini; +Cc: devicetree, robh, conor+dt
In-Reply-To: <20260524-ssqosid-cbqri-rqsc-v7-0-v5-11-78d3a7ba9dbe@kernel.org>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] The CPU hotplug callbacks do not notify the generic resctrl subsystem when CPUs come online or go offline, leaving the `rdtgroup` cpumasks out of sync.
- [High] The per-CPU default CLOSID and RMID are not reset to the reserved defaults when a CPU comes online.
- [High] CONFIG_RISCV_ISA_SSQOSID unconditionally selects ARCH_HAS_CPU_RESCTRL without requiring the implementation driver, leading to build failures.
- [High] Missing rollback on partial failure in CPU hotplug online callback leaves the system in an inconsistent state.
--
commit 31a7934518aa1237167aa6b7e0cb45ba9107610b
Author: Drew Fustini <fustini@kernel.org>
riscv_cbqri: resctrl: Add cache allocation via capacity block mask
This commit wires CBQRI capacity controllers into resctrl as RDT_RESOURCE_L2
and RDT_RESOURCE_L3 schemata. It treats mismatched controller capacities at
the same cache level as a fatal error and creates domains lazily during CPU
hotplug.
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 6abbb21f3a0dc..390353a6153a2 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -595,6 +595,7 @@ config RISCV_ISA_SSQOSID
> bool "Ssqosid extension support for supervisor mode Quality of Service ID"
> depends on 64BIT
> default n
> + select ARCH_HAS_CPU_RESCTRL
[Severity: High]
Could this lead to build failures if the CBQRI driver is not enabled?
The Kconfig symbol RISCV_ISA_SSQOSID unconditionally selects
ARCH_HAS_CPU_RESCTRL, which signals to the core kernel that the architecture
provides the resctrl_arch_* interface. This permits CONFIG_RESCTRL_FS to be
enabled.
However, since the implementation resides in drivers/resctrl/cbqri_resctrl.c
which is only compiled when CONFIG_RISCV_CBQRI_DRIVER is set, enabling
RISCV_ISA_SSQOSID and RESCTRL_FS without RISCV_CBQRI_DRIVER might cause
linker errors due to undefined references to the resctrl_arch_* functions.
Would it be safer to add a dependency on the driver?
> diff --git a/drivers/resctrl/cbqri_resctrl.c b/drivers/resctrl/cbqri_resctrl.c
> new file mode 100644
> index 0000000000000..1e6198e25fd29
> --- /dev/null
> +++ b/drivers/resctrl/cbqri_resctrl.c
> @@ -0,0 +1,736 @@
[ ... ]
> +static int cbqri_resctrl_online_cpu(unsigned int cpu)
> +{
> + struct cbqri_controller *ctrl;
> + int err = 0;
> +
> + mutex_lock(&cbqri_domain_list_lock);
> +
> + list_for_each_entry(ctrl, &cbqri_controllers, list) {
> + if (ctrl->type != CBQRI_CONTROLLER_TYPE_CAPACITY)
> + continue;
> + if (!cpumask_test_cpu(cpu, &ctrl->cache.cpu_mask))
> + continue;
> + if (!ctrl->alloc_capable)
> + continue;
> +
> + err = cbqri_attach_cpu_to_cap_ctrl(ctrl, cpu);
> + if (err)
> + break;
> + }
[Severity: High]
Does this loop leave the system in an inconsistent state if it fails partway
through?
If cbqri_attach_cpu_to_cap_ctrl() succeeds for an earlier controller
but fails for a subsequent one, the loop breaks and returns an error. The CPU
hotplug framework will abort the bring-up without invoking the teardown
callback cbqri_resctrl_offline_cpu().
Will this leak the rdt_ctrl_domain structs for the controllers that
were successfully attached?
> +
> + mutex_unlock(&cbqri_domain_list_lock);
> + return err;
> +}
[Severity: High]
Should we reset the per-CPU default closid and rmid when a CPU comes online?
When a CPU is offlined while in a custom resctrl group, its cpu_srmcfg_default
might still hold that custom closid/rmid. When the CPU comes back online, it
conceptually returns to the default rdtgroup, so it might need its state
reset via resctrl_arch_set_cpu_default_closid_rmid() to prevent tasks from
running with stale assignments.
[Severity: High]
Does this missing notification leave the generic resctrl subsystem out of sync?
The generic fs/resctrl layer usually relies on the architecture to notify it
via resctrl_online_cpu() to update resource group masks. Without this, newly
onlined CPUs might not be added to the default resctrl group, potentially
breaking sysfs CPU tracking.
> +
> +static int cbqri_resctrl_offline_cpu(unsigned int cpu)
> +{
> + int rid;
> +
> + mutex_lock(&cbqri_domain_list_lock);
> +
> + for (rid = 0; rid < RDT_NUM_RESOURCES; rid++) {
> + struct cbqri_resctrl_res *hw_res = &cbqri_resctrl_resources[rid];
> +
> + if (!hw_res->ctrl)
> + continue;
> + cbqri_detach_cpu_from_ctrl_domains(&hw_res->resctrl_res, cpu);
> + }
> +
> + mutex_unlock(&cbqri_domain_list_lock);
> + return 0;
> +}
[Severity: High]
Does this also need to call resctrl_offline_cpu() to notify the core layer?
Without notifying the generic layer, offlined CPUs might remain tracked
permanently in the rdtgroup cpumasks.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260524-ssqosid-cbqri-rqsc-v7-0-v5-0-78d3a7ba9dbe@kernel.org?part=11
^ permalink raw reply
* Re: [PATCH RFC v5 14/18] riscv_cbqri: resctrl: Add MB_WGHT bandwidth allocation via Mweight
From: sashiko-bot @ 2026-05-25 0:52 UTC (permalink / raw)
To: Drew Fustini; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260524-ssqosid-cbqri-rqsc-v7-0-v5-14-78d3a7ba9dbe@kernel.org>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] If `cbqri_attach_cpu_to_bw_ctrl()` fails to attach the CPU to `RDT_RESOURCE_MB_WGHT`, the previously successful attachment to `RDT_RESOURCE_MB_MIN` is not rolled back, leading to a domain cpumask and memory leak.
Pre-existing issues:
- [High] If the controller iteration in `cbqri_resctrl_online_cpu` encounters an error midway, earlier successfully attached controllers are not detached, causing domain leaks on partial CPU bringup failure.
--
commit e6b193d2dcc8f7ab213d18abdb79919066dddd04
Author: Drew Fustini <fustini@kernel.org>
riscv_cbqri: resctrl: Add MB_WGHT bandwidth allocation via Mweight
This patch adds bandwidth allocation through Mweight, exposed as the MB_WGHT
resource. It configures the shared weight for unreserved bandwidth, handling
initialization and resource attachment.
> diff --git a/drivers/resctrl/cbqri_resctrl.c b/drivers/resctrl/cbqri_resctrl.c
> index 5d8389c58f4fe..ba764bc6ef176 100644
> --- a/drivers/resctrl/cbqri_resctrl.c
> +++ b/drivers/resctrl/cbqri_resctrl.c
[ ... ]
> @@ -1019,7 +1065,13 @@ static int cbqri_attach_cpu_to_one_bw_res(struct cbqri_controller *ctrl,
> static int cbqri_attach_cpu_to_bw_ctrl(struct cbqri_controller *ctrl,
> unsigned int cpu)
> {
> - return cbqri_attach_cpu_to_one_bw_res(ctrl, RDT_RESOURCE_MB_MIN, cpu);
> + int err;
> +
> + err = cbqri_attach_cpu_to_one_bw_res(ctrl, RDT_RESOURCE_MB_MIN, cpu);
> + if (err)
> + return err;
> +
> + return cbqri_attach_cpu_to_one_bw_res(ctrl, RDT_RESOURCE_MB_WGHT, cpu);
> }
[Severity: High]
If cbqri_attach_cpu_to_one_bw_res() fails for RDT_RESOURCE_MB_WGHT, does
this leave the CPU permanently attached to the RDT_RESOURCE_MB_MIN domain?
Since the CPU hotplug core expects a startup callback to either fully succeed
or completely roll back its progress on error, returning an error here skips
the teardown callback. It looks like this might leak the domain structure if
a new one was allocated, and leave the CPU in the domain's cpumask.
[Severity: High]
This is a pre-existing issue, but looking at the caller
cbqri_resctrl_online_cpu(), there appears to be a similar problem with the
controller iteration:
drivers/resctrl/cbqri_resctrl.c:cbqri_resctrl_online_cpu() {
...
list_for_each_entry(ctrl, &cbqri_controllers, list) {
switch (ctrl->type) {
...
case CBQRI_CONTROLLER_TYPE_BANDWIDTH:
...
err = cbqri_attach_cpu_to_bw_ctrl(ctrl, cpu);
break;
}
if (err)
break;
}
mutex_unlock(&cbqri_domain_list_lock);
return err;
}
If the loop terminates early due to an error, are the domains that were
successfully attached in earlier iterations left without being detached? Like
the issue above, the offline callback won't be called, potentially leaking
the domain allocations.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260524-ssqosid-cbqri-rqsc-v7-0-v5-0-78d3a7ba9dbe@kernel.org?part=14
^ permalink raw reply
* Re: [PATCH RFC v5 13/18] riscv_cbqri: resctrl: Add MB_MIN bandwidth allocation via Rbwb
From: sashiko-bot @ 2026-05-25 0:55 UTC (permalink / raw)
To: Drew Fustini; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260524-ssqosid-cbqri-rqsc-v7-0-v5-13-78d3a7ba9dbe@kernel.org>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Deleted resctrl groups leak MB_MIN bandwidth because rmdir does not reset the hardware limit, preventing other groups from using the freed bandwidth due to the strict sum constraint.
- [High] Using `prox_dom` as `dom_id` for bandwidth controllers aliases multiple memory controllers within the same NUMA node to a single resctrl domain, leaving some unmanaged.
Pre-existing issues:
- [High] If attaching a CPU to a controller fails inside `cbqri_resctrl_online_cpu()`, the loop breaks and returns the error without rolling back previously successful attachments, leading to a permanent domain state leak.
--
commit c8ab4b9082ddd1241fd1636c42cf108a5428c02c
Author: Drew Fustini <fustini@kernel.org>
riscv_cbqri: resctrl: Add MB_MIN bandwidth allocation via Rbwb
This patch adds bandwidth allocation through reserved bandwidth blocks (Rbwb),
exposed as the MB_MIN resource. The CBQRI sum constraint is enforced at
schemata-write time using a software cache. RCID 0 is assigned the remaining
MRBWB budget on reset.
> diff --git a/drivers/resctrl/cbqri_resctrl.c b/drivers/resctrl/cbqri_resctrl.c
> --- a/drivers/resctrl/cbqri_resctrl.c
> +++ b/drivers/resctrl/cbqri_resctrl.c
[ ... ]
> @@ -411,6 +411,9 @@ int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d,
> case RDT_RESOURCE_L2:
> case RDT_RESOURCE_L3:
> return cbqri_apply_cache_config_dom(dom, r, closid, t, cfg_val);
> + case RDT_RESOURCE_MB_MIN:
> + /* sum(Rbwb) <= MRBWB validation runs inside cbqri_apply_rbwb(). */
> + return cbqri_apply_rbwb(dom->hw_ctrl, closid, cfg_val, true);
[Severity: High]
Does this validation approach leak bandwidth when groups are deleted?
When a resctrl group is deleted via rmdir, the CLOSID is returned to the free
pool, but standard resctrl flows do not trigger an architecture-specific
callback to reset the resource's value.
Because cbqri_apply_rbwb() iterates over all RCIDs (including inactive ones)
to enforce sum(Rbwb) <= MRBWB, the bandwidth assigned to deleted groups remains
permanently accounted against the global MRBWB budget.
If a user deletes a group to free up bandwidth and tries to assign it to an
active group, could it be falsely rejected with -EINVAL, effectively leaking
that capacity until the exact CLOSID is randomly reused?
[ ... ]
> @@ -860,6 +991,37 @@ static int cbqri_attach_cpu_to_cap_ctrl(struct cbqri_controller *ctrl,
> return err;
> }
>
> +static int cbqri_attach_cpu_to_one_bw_res(struct cbqri_controller *ctrl,
> + enum resctrl_res_level rid,
> + unsigned int cpu)
> +{
> + struct cbqri_resctrl_res *hw_res = &cbqri_resctrl_resources[rid];
> + struct rdt_resource *res = &hw_res->resctrl_res;
> + struct rdt_ctrl_domain *domain;
> + int dom_id = ctrl->mem.prox_dom;
> +
> + if (!hw_res->ctrl)
> + return 0;
> +
> + domain = cbqri_find_ctrl_domain(&res->ctrl_domains, dom_id);
> + if (domain) {
> + cpumask_set_cpu(cpu, &domain->hdr.cpu_mask);
> + return 0;
> + }
[Severity: High]
Can this alias multiple memory controllers to a single domain?
A single NUMA node (proximity domain) can contain multiple memory controllers.
If multiple bandwidth controllers share the same proximity domain, the first one
processed will create a domain and assign its hw_ctrl pointer to it.
When the second controller is processed, cbqri_find_ctrl_domain() will match
the existing dom_id, and simply add its CPUs to the cpu_mask without saving
the second controller's hw_ctrl pointer.
Will subsequent schemata writes to this domain exclusively program the first
bandwidth controller, leaving the second unmanaged by resctrl?
[ ... ]
> @@ -983,14 +1149,24 @@ static int cbqri_resctrl_online_cpu(unsigned int cpu)
> mutex_lock(&cbqri_domain_list_lock);
>
> list_for_each_entry(ctrl, &cbqri_controllers, list) {
> - if (ctrl->type != CBQRI_CONTROLLER_TYPE_CAPACITY)
> - continue;
> - if (!cpumask_test_cpu(cpu, &ctrl->cache.cpu_mask))
> - continue;
> - if (!ctrl->alloc_capable)
> + switch (ctrl->type) {
> + case CBQRI_CONTROLLER_TYPE_CAPACITY:
> + if (!cpumask_test_cpu(cpu, &ctrl->cache.cpu_mask))
> + continue;
> + if (!ctrl->alloc_capable)
> + continue;
> + err = cbqri_attach_cpu_to_cap_ctrl(ctrl, cpu);
> + break;
> + case CBQRI_CONTROLLER_TYPE_BANDWIDTH:
> + if (!cpumask_test_cpu(cpu, &ctrl->mem.cpu_mask))
> + continue;
> + if (!ctrl->alloc_capable)
> + continue;
> + err = cbqri_attach_cpu_to_bw_ctrl(ctrl, cpu);
> + break;
> + default:
> continue;
> -
> - err = cbqri_attach_cpu_to_cap_ctrl(ctrl, cpu);
> + }
> if (err)
> break;
> }
[Severity: High]
This is a pre-existing issue, but does this leak domain state if an error
occurs here?
If an attachment fails, the loop breaks and returns the error immediately. The
CPU hotplug core catches the error and aborts the online state transition.
However, it does not invoke the corresponding offline teardown callback because
this state failed during its startup sequence.
Since cbqri_resctrl_online_cpu() fails to manually roll back the domain state
for the controllers that successfully processed the CPU in earlier iterations,
will the CPU remain permanently set in their cpu_mask?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260524-ssqosid-cbqri-rqsc-v7-0-v5-0-78d3a7ba9dbe@kernel.org?part=13
^ permalink raw reply
* [PATCH v12 0/2] Add Lontium LT7911EXC eDP to MIPI DSI bridge
From: syyang @ 2026-05-25 1:05 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, andrzej.hajda, neil.armstrong,
dmitry.baryshkov, maarten.lankhorst, rfoss, mripard
Cc: Laurent.pinchart, tzimmermann, jonas, jernej.skrabec, devicetree,
dri-devel, linux-kernel, xmzhu, xmzhu, rlyu, xbpeng, qdchen,
llzhang, Sunyun Yang
From: Sunyun Yang <syyang@lontium.com>
The LT7911EXC is an I2C-controlled bridge that receives eDP1.4
and output mipi dsi. This series introduces:
- A device tree binding YAML file describing the hardware
- A new DRM bridge driver implementing the basic functionality
Signed-off-by: Sunyun Yang<syyang@lontium.com>
---
Change in v12:
- dt-binding:
- drm/bridge:
1. Move the cleanup work of drm_panel_bridge_remove() forward into lt7911exc_dsi_host_detach(). [sashiko-bot]
2. Do not hold any lock during lt7911exc_reset().
- Link to v11: https://lore.kernel.org/lkml/20260522015735.2833-1-syyang@lontium.com/
Change in v11:
- dt-binding:
- drm/bridge:
1. Removed Reviewed-by, we would appreciate it if the maintainer could help review this.
2. Use devm_gpiod_get(,, GPIOD_OUT_HIGH) to fix the reset GPIO backpowering issue. [sashiko-bot]
3. Fixed Use-After-Free vulnerability due to dynamic DRM bridge removal in DSI detach callback.
4. Fixed Use-After-Free of regmap during DRM teardown because firmware upgrade worker defeats removal safeguard.
5. Fixed firmware upgrade performs a hardware reset, causing silent loss of DRM state and a blank screen.
6. Use of kmalloc for a 64KB software buffer is susceptible to memory fragmentation failures - Fix by using kvmalloc
7. Use the 'lt7911exc->ocm_lock' mutex and the 'lt7911exc->upgrade' flag to resolve the race condition between
DRM atomic modeset and firmware upgrade.
8. Use the 'lt7911exc->upgrade_lock' mutex to prevent redundant upgrades.
9. Use the 'lt7911exc->removed' flag to prevent the driver from being triggered to upgrade during the unload process.
- Link to v10: https://lore.kernel.org/lkml/20260519135816.26996-1-syyang@lontium.com/
Change in v10:
- dt-binding:
- drm/bridge:
1. Fixed the firmware upgrade error paths to always clear the upgrade
flag before returning, including firmware size validation failures
and allocation failures. [sashiko-bot]
2. Added proper locking in lt7911exc_atomic_pre_enable() and
lt7911exc_atomic_post_disable() to serialize register accesses with the
firmware upgrade flow and avoid concurrent I2C transactions.
3. Added an exclusivity check in lt7911exc_dsi_host_attach() to reject multiple
downstream attachments and prevent repeated drm_bridge_add() calls and panel bridge leaks.
4. Reworked lt7911exc_firmware_store() to use mutex_trylock() so concurrent sysfs writers
immediately return -EBUSY instead of blocking behind an active firmware upgrade.
5. Updated the remove path to prevent new firmware upgrade work from being queued after
device removal by setting the upgrade state before cancelling the worker.
- Link to v9: https://lore.kernel.org/lkml/20260519105019.22622-1-syyang@lontium.com/
Change in v9:
- dt-binding:
- drm/bridge:
1. DSI transfer callback returns success for reads without populating [sashiko-bot]
the receive buffer, leaking uninitialized memory. - fixed it by
implementing a strict whitelist mechanism.
2. DSI transfer callback polls for eDP video readiness before the
upstream encoder is enabled, guaranteeing a timeout. - removed
3. The driver attempts I2C transfers while the hardware is held in
physical reset. - fixed
4. Missing DRM_MIPI_DSI Kconfig dependency causes linker errors. - fixed
5. request_firmware is called while holding the hardware lock and
halting the MCU, risking a system pipeline stall. - fixed
6. Sleeping functions are called from atomic context in the DRM bridge callbacks. - fixed
7. lt7911exc_dsi_host_transfer bypasses the required MCU hardware halt sequence. - fixed by
internal firmware controls the panel initialization sequence and handles all MIPI
DSI command transmission.
- Link to v8: https://lore.kernel.org/lkml/20260515080934.9870-1-syyang@lontium.com/
Change in v8:
- dt-binding:
- drm/bridge:
1. Protect firmware upgrade and DRM bridge callback paths with ocm_lock. [sashiko-bot]
2. Remove the hardware reset from the remove callback, and ensure that
all hardware reset operations are protected by ocm_lock.
3. crc reconstruction explicitly casts each byte to u32 before shifting
4. The display configuration is handled by the firmware, and the MIPI
DSI host registration issue has been fixed.
5. The batch register read/write operations have already been updated
to include return value checking.
6. The dev_err_probe() used outside of probe context has been fixed.
- Link to v7: https://lore.kernel.org/lkml/20260512064013.40066-1-syyang@lontium.com/
Change in v7:
- dt-binding:
1. fix commit message typos(Receiver、signal) [sashiko-bot]
2. remove the ambiguity caused by "signal/dual".
- drm/bridge:
1. using devm_regulator_get_enable avoids power leaks. [sashiko-bot]
2. set reset gpio is low after cutting off power in lt7911exc_remove function, avoid backpowering.
3. synchronous request_firmware() call cause a permanent probe failure if the driver is built-in,
probe executes before the root filesystem is mounted, which would cause this to fail with -ENOENT,
we have removed this functionality. Use trigger to upgrade.
4. add `depends on I2C` and `select REGMAP_I2C` in Kconfig.
5. add return value of `devm_drm_bridge_add()` in `probe()`.
6. add directly header files (linux/slab.h, linux/delay.h, linux/regulator/consumer.h)
- Link to v6: https://lore.kernel.org/lkml/20260508134702.4713-1-syyang@lontium.com/
Change in v6:
- dt-binding:
- drm/bridge:
1. use #define FW_FILE "Lontium/lt7911exc_fw.bin" to match linux-firmware
- Link to v5: https://lore.kernel.org/lkml/20260506013153.2240-1-syyang@lontium.com/
Change in v5:
- dt-binding:
- drm/bridge:
1. Change "mipi" to "mipi dsi" in the commit message. [Dmitry]
2. Change "eDP/MIPI" to "eDP/MIPI DSI" in Kconfig.
- Link to v4: https://lore.kernel.org/lkml/20260430094612.3408174-1-syyang@lontium.com/
Change in v4:
- dt-binding:
1. Fix the missing spaces on the "subject". [Krzysztof]
2. Fix the error descriptions for port@0 and port@1.
- drm/bridge:
- Link to v3: https://lore.kernel.org/lkml/20260429040541.3404116-1-syyang@lontium.com/
Change in v3:
- dt-binding:
- drm/bridge:
1. already submit lt7911exc_fw.bin to linux-firmware. [Dmitry]
2. remove lt7911exc_remove function.
3. drop the "lontium, " in lt7911exc_i2c_table.
- Link to v2: https://lore.kernel.org/lkml/20260428063224.3316655-1-syyang@lontium.com/
Change in v2:
- dt-binding:
1. reset pins use active low. [Dmitry]
- drm/bridge:
1. use atomic_* callbacks. [Quentin]
2. fix the incorrect formatting and spaces.
3. add the required header files. [Dmitry]
4. remove "enabled" flag.
5. remove *fw from the lt7911exc struct.
6. .max_register and .range_max use actual range.
7. regulator use bulk interface.
8. use dev_err_probe, devm_mutex_init and devm_drm_bridge_add.
9. Replace GPL v2 with GPL.
- Link to v1: https://lore.kernel.org/lkml/20260420023354.1192642-1-syyang@lontium.com/
---
Sunyun Yang (2):
dt-bindings: bridge: Add Lontium LT7911EXC eDP to MIPI DSI bridge
drm/bridge: Add Lontium LT7911EXC eDP to MIPI DSI bridge
.../display/bridge/lontium,lt7911exc.yaml | 89 +++
drivers/gpu/drm/bridge/Kconfig | 16 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/lontium-lt7911exc.c | 700 ++++++++++++++++++
4 files changed, 806 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/bridge/lontium,lt7911exc.yaml
create mode 100644 drivers/gpu/drm/bridge/lontium-lt7911exc.c
--
2.34.1
^ permalink raw reply
* [PATCH v12 1/2] dt-bindings: bridge: Add Lontium LT7911EXC eDP to MIPI DSI bridge
From: syyang @ 2026-05-25 1:05 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, andrzej.hajda, neil.armstrong,
dmitry.baryshkov, maarten.lankhorst, rfoss, mripard
Cc: Laurent.pinchart, tzimmermann, jonas, jernej.skrabec, devicetree,
dri-devel, linux-kernel, xmzhu, xmzhu, rlyu, xbpeng, qdchen,
llzhang, Sunyun Yang, Krzysztof Kozlowski
In-Reply-To: <20260525010545.9470-1-syyang@lontium.com>
From: Sunyun Yang <syyang@lontium.com>
This commit adds the device tree binding schema for the Lontium LT7911EXC.
This device is an I2C-controlled bridge that converts eDP 1.4 input to MIPI
DSI output.
Signed-off-by: Sunyun Yang <syyang@lontium.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../display/bridge/lontium,lt7911exc.yaml | 89 +++++++++++++++++++
1 file changed, 89 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/bridge/lontium,lt7911exc.yaml
diff --git a/Documentation/devicetree/bindings/display/bridge/lontium,lt7911exc.yaml b/Documentation/devicetree/bindings/display/bridge/lontium,lt7911exc.yaml
new file mode 100644
index 000000000000..3290b10ce883
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/lontium,lt7911exc.yaml
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/lontium,lt7911exc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Lontium LT7911EXC eDP to MIPI DSI Bridge
+
+maintainers:
+ - Sunyun Yang <syyang@lontium.com>
+
+properties:
+ compatible:
+ enum:
+ - lontium,lt7911exc
+
+ reg:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+ description: GPIO connected to RST_ pin.
+
+ vdd-supply:
+ description: Regulator for 1.2V MIPI phy power.
+
+ vcc-supply:
+ description: Regulator for 3.3V IO power.
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Video port for eDP input.
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Video port for MIPI DSI output.
+
+ required:
+ - port@0
+ - port@1
+
+required:
+ - compatible
+ - reg
+ - reset-gpios
+ - vdd-supply
+ - vcc-supply
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mipi-bridge@41 {
+ compatible = "lontium,lt7911exc";
+ reg = <0x41>;
+ reset-gpios = <&gpy8 8 GPIO_ACTIVE_LOW>;
+ vdd-supply = <<7911exc_1v2>;
+ vcc-supply = <<7911exc_3v3>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ bridge_in: endpoint {
+ remote-endpoint = <&edp_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ bridge_out: endpoint {
+ remote-endpoint = <&panel_in>;
+ };
+ };
+ };
+ };
+ };
--
2.34.1
^ permalink raw reply related
* [PATCH v12 2/2] drm/bridge: Add Lontium LT7911EXC eDP to MIPI DSI bridge
From: syyang @ 2026-05-25 1:05 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, andrzej.hajda, neil.armstrong,
dmitry.baryshkov, maarten.lankhorst, rfoss, mripard
Cc: Laurent.pinchart, tzimmermann, jonas, jernej.skrabec, devicetree,
dri-devel, linux-kernel, xmzhu, xmzhu, rlyu, xbpeng, qdchen,
llzhang, Sunyun Yang
In-Reply-To: <20260525010545.9470-1-syyang@lontium.com>
From: Sunyun Yang <syyang@lontium.com>
Add support for the Lontium LT7911EXC bridge chip, which converts
eDP input to MIPI DSI output using an internal firmware-controlled
pipeline.
The driver provides:
- DRM bridge integration for eDP-to-DSI routing
- MIPI DSI host interface for downstream panel attachment
- Firmware upgrade mechanism over I2C (erase/program/verify)
- GPIO-based reset and regulator management
Display timing and MIPI DCS packet generation are handled by the chip
firmware and are not configured by the driver.
Signed-off-by: Sunyun Yang <syyang@lontium.com>
---
drivers/gpu/drm/bridge/Kconfig | 16 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/lontium-lt7911exc.c | 700 +++++++++++++++++++++
3 files changed, 717 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/lontium-lt7911exc.c
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index c3209b0f4678..013e431e8871 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -132,6 +132,22 @@ config DRM_ITE_IT6505
help
ITE IT6505 DisplayPort bridge chip driver.
+config DRM_LONTIUM_LT7911EXC
+ tristate "Lontium eDP/MIPI DSI bridge"
+ depends on OF
+ depends on I2C
+ select CRC32
+ select DRM_PANEL
+ select DRM_MIPI_DSI
+ select DRM_KMS_HELPER
+ select FW_LOADER
+ select REGMAP_I2C
+ help
+ DRM driver for the Lontium LT7911EXC bridge
+ chip.The LT7911EXC converts eDP input to MIPI
+ DSI output.
+ Please say Y if you have such hardware.
+
config DRM_LONTIUM_LT8912B
tristate "Lontium LT8912B DSI/HDMI bridge"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index beab5b695a6e..70ddca75dd3a 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
obj-$(CONFIG_DRM_INNO_HDMI) += inno-hdmi.o
obj-$(CONFIG_DRM_ITE_IT6263) += ite-it6263.o
obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
+obj-$(CONFIG_DRM_LONTIUM_LT7911EXC) += lontium-lt7911exc.o
obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o
obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o
diff --git a/drivers/gpu/drm/bridge/lontium-lt7911exc.c b/drivers/gpu/drm/bridge/lontium-lt7911exc.c
new file mode 100644
index 000000000000..de6953ed1e3b
--- /dev/null
+++ b/drivers/gpu/drm/bridge/lontium-lt7911exc.c
@@ -0,0 +1,700 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Lontium Semiconductor, Inc.
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <video/mipi_display.h>
+
+#define FW_SIZE (64 * 1024)
+#define LT_PAGE_SIZE 32
+#define FW_FILE "Lontium/lt7911exc_fw.bin"
+#define LT7911EXC_PAGE_CONTROL 0xff
+
+struct lt7911exc_dsi_output {
+ struct mipi_dsi_device *dev;
+ struct drm_panel *panel;
+ struct drm_bridge *bridge;
+};
+
+struct lt7911exc {
+ struct device *dev;
+ struct i2c_client *client;
+ struct drm_bridge bridge;
+ struct work_struct work;
+ struct mipi_dsi_host dsi_host;
+ struct lt7911exc_dsi_output output;
+ struct regmap *regmap;
+ /* Fast lock: guards short register r/w and status checks */
+ struct mutex ocm_lock;
+ /* Long lock: serializes firmware upgrade process to prevent DRM interference */
+ struct mutex upgrade_lock;
+ struct gpio_desc *reset_gpio;
+ int fw_version;
+ bool upgrade;
+ bool removed;
+};
+
+static const struct regmap_range_cfg lt7911exc_ranges[] = {
+ {
+ .name = "register_range",
+ .range_min = 0,
+ .range_max = 0xe8ff,
+ .selector_reg = LT7911EXC_PAGE_CONTROL,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 0x100,
+ },
+};
+
+static const struct regmap_config lt7911exc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xe8ff,
+ .ranges = lt7911exc_ranges,
+ .num_ranges = ARRAY_SIZE(lt7911exc_ranges),
+};
+
+static u32 cal_crc32_custom(const u8 *data, u64 length)
+{
+ u32 crc = 0xffffffff;
+ u8 buf[4];
+ u64 i;
+
+ if (!length || (length & 3))
+ return 0;
+
+ for (i = 0; i < length; i += 4) {
+ buf[0] = data[i + 3];
+ buf[1] = data[i + 2];
+ buf[2] = data[i + 1];
+ buf[3] = data[i + 0];
+ crc = crc32_be(crc, buf, 4);
+ }
+
+ return crc;
+}
+
+static inline struct lt7911exc *bridge_to_lt7911exc(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct lt7911exc, bridge);
+}
+
+static inline struct lt7911exc *dsi_host_to_lt7911exc(struct mipi_dsi_host *host)
+{
+ return container_of(host, struct lt7911exc, dsi_host);
+}
+
+static void lt7911exc_reset(struct lt7911exc *lt7911exc)
+{
+ /* Assert reset pin: logical 1 -> physical state low (Reset Active) */
+ gpiod_set_value_cansleep(lt7911exc->reset_gpio, 1);
+ msleep(20);
+
+ /* Deassert reset pin: logical 0 -> physical state high (Run state) */
+ gpiod_set_value_cansleep(lt7911exc->reset_gpio, 0);
+ msleep(400);
+
+ dev_dbg(lt7911exc->dev, "lt7911exc physical hardware reset sequence completed.\n");
+}
+
+static int lt7911exc_hw_mcu_halt(struct lt7911exc *lt7911exc)
+{
+ return regmap_write(lt7911exc->regmap, 0xe0ee, 0x01);
+}
+
+static int lt7911exc_hw_mcu_run(struct lt7911exc *lt7911exc)
+{
+ return regmap_write(lt7911exc->regmap, 0xe0ee, 0x00);
+}
+
+static int lt7911exc_regulator_enable(struct lt7911exc *lt7911exc)
+{
+ int ret;
+
+ ret = devm_regulator_get_enable(lt7911exc->dev, "vcc");
+ if (ret < 0)
+ return dev_err_probe(lt7911exc->dev, ret, "failed to enable vcc regulator\n");
+
+ usleep_range(5000, 10000);
+
+ ret = devm_regulator_get_enable(lt7911exc->dev, "vdd");
+ if (ret < 0)
+ return dev_err_probe(lt7911exc->dev, ret, "failed to enable vdd regulator\n");
+
+ return 0;
+}
+
+static int lt7911exc_read_version(struct lt7911exc *lt7911exc)
+{
+ u8 buf[3];
+ int ret;
+
+ /* no need to halt MCU for this register access */
+ ret = regmap_bulk_read(lt7911exc->regmap, 0xe081, buf, ARRAY_SIZE(buf));
+ if (ret)
+ return ret;
+
+ return (buf[0] << 16) | (buf[1] << 8) | buf[2];
+}
+
+static int lt7911exc_block_erase(struct lt7911exc *lt7911exc)
+{
+ struct device *dev = lt7911exc->dev;
+ const u32 addr = 0x00;
+ int ret;
+
+ const struct reg_sequence seq_write[] = {
+ REG_SEQ0(0xe0ee, 0x01),
+ REG_SEQ0(0xe054, 0x01),
+ REG_SEQ0(0xe055, 0x06),
+ REG_SEQ0(0xe051, 0x01),
+ REG_SEQ0(0xe051, 0x00),
+ REG_SEQ0(0xe054, 0x05),
+ REG_SEQ0(0xe055, 0xd8),
+ REG_SEQ0(0xe05a, (addr >> 16) & 0xff),
+ REG_SEQ0(0xe05b, (addr >> 8) & 0xff),
+ REG_SEQ0(0xe05c, addr & 0xff),
+ REG_SEQ0(0xe051, 0x01),
+ REG_SEQ0(0xe050, 0x00),
+ };
+
+ ret = regmap_multi_reg_write(lt7911exc->regmap, seq_write, ARRAY_SIZE(seq_write));
+ if (ret)
+ return ret;
+
+ msleep(200);
+ dev_dbg(dev, "erase flash done.\n");
+
+ return 0;
+}
+
+static int lt7911exc_prog_init(struct lt7911exc *lt7911exc, u64 addr)
+{
+ int ret;
+
+ const struct reg_sequence seq_write[] = {
+ REG_SEQ0(0xe0ee, 0x01),
+ REG_SEQ0(0xe05f, 0x01),
+ REG_SEQ0(0xe05a, (addr >> 16) & 0xff),
+ REG_SEQ0(0xe05b, (addr >> 8) & 0xff),
+ REG_SEQ0(0xe05c, addr & 0xff),
+ };
+
+ ret = regmap_multi_reg_write(lt7911exc->regmap, seq_write, ARRAY_SIZE(seq_write));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int lt7911exc_write_data(struct lt7911exc *lt7911exc, const struct firmware *fw, u64 addr)
+{
+ struct device *dev = lt7911exc->dev;
+ int ret;
+ int page = 0, num = 0, page_len = 0;
+ u64 size, offset;
+ const u8 *data;
+
+ data = fw->data;
+ size = fw->size;
+ page = (size + LT_PAGE_SIZE - 1) / LT_PAGE_SIZE;
+ if (page * LT_PAGE_SIZE > FW_SIZE) {
+ dev_err(dev, "firmware size out of range\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "%u pages, total size %llu byte\n", page, size);
+
+ for (num = 0; num < page; num++) {
+ offset = num * LT_PAGE_SIZE;
+ page_len = (offset + LT_PAGE_SIZE <= size) ? LT_PAGE_SIZE : (size - offset);
+ ret = lt7911exc_prog_init(lt7911exc, addr);
+ if (ret)
+ return ret;
+
+ ret = regmap_raw_write(lt7911exc->regmap, 0xe05d, &data[offset], page_len);
+ if (ret) {
+ dev_err(dev, "write error at page %d\n", num);
+ return ret;
+ }
+
+ if (page_len < LT_PAGE_SIZE) {
+ regmap_write(lt7911exc->regmap, 0xe05f, 0x05);
+ regmap_write(lt7911exc->regmap, 0xe05f, 0x01);
+ //hardware requires delay
+ usleep_range(1000, 2000);
+ }
+
+ regmap_write(lt7911exc->regmap, 0xe05f, 0x00);
+ addr += LT_PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+static int lt7911exc_write_crc(struct lt7911exc *lt7911exc, u32 crc32, u64 addr)
+{
+ u8 crc[4];
+ int ret;
+
+ crc[0] = crc32 & 0xff;
+ crc[1] = (crc32 >> 8) & 0xff;
+ crc[2] = (crc32 >> 16) & 0xff;
+ crc[3] = (crc32 >> 24) & 0xff;
+
+ regmap_write(lt7911exc->regmap, 0xe05f, 0x01);
+ regmap_write(lt7911exc->regmap, 0xe05a, (addr >> 16) & 0xff);
+ regmap_write(lt7911exc->regmap, 0xe05b, (addr >> 8) & 0xff);
+ regmap_write(lt7911exc->regmap, 0xe05c, addr & 0xff);
+
+ ret = regmap_raw_write(lt7911exc->regmap, 0xe05d, crc, 4);
+ if (ret)
+ return ret;
+
+ regmap_write(lt7911exc->regmap, 0xe05f, 0x05);
+ regmap_write(lt7911exc->regmap, 0xe05f, 0x01);
+ usleep_range(1000, 2000);
+ regmap_write(lt7911exc->regmap, 0xe05f, 0x00);
+
+ return 0;
+}
+
+static int lt7911exc_upgrade_result(struct lt7911exc *lt7911exc, u32 crc32)
+{
+ struct device *dev = lt7911exc->dev;
+ u32 read_hw_crc = 0;
+ u8 crc_tmp[4];
+ int ret;
+
+ regmap_write(lt7911exc->regmap, 0xe0ee, 0x01);
+ regmap_write(lt7911exc->regmap, 0xe07b, 0x60);
+ regmap_write(lt7911exc->regmap, 0xe07b, 0x40);
+ msleep(150);
+ ret = regmap_bulk_read(lt7911exc->regmap, 0x22, crc_tmp, ARRAY_SIZE(crc_tmp));
+ if (ret) {
+ dev_err(lt7911exc->dev, "Failed to read CRC: %d\n", ret);
+ return ret;
+ }
+ regmap_write(lt7911exc->regmap, 0xe0ee, 0x00);
+
+ read_hw_crc = ((u32)crc_tmp[0] << 24) | ((u32)crc_tmp[1] << 16) |
+ ((u32)crc_tmp[2] << 8) | ((u32)crc_tmp[3]);
+
+ if (read_hw_crc != crc32) {
+ dev_err(dev, "lt7911exc firmware upgrade failed, expected CRC=0x%08x, read CRC=0x%08x\n",
+ crc32, read_hw_crc);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "lt7911exc firmware upgrade success, CRC=0x%08x\n", read_hw_crc);
+ return 0;
+}
+
+static void lt7911exc_firmware_upgrade_work(struct work_struct *work)
+{
+ struct lt7911exc *lt7911exc = container_of(work, struct lt7911exc, work);
+ struct device *dev = lt7911exc->dev;
+ const struct firmware *fw;
+ u8 *buffer;
+ size_t total_size = FW_SIZE - 4;
+ u32 crc32;
+ int ret;
+
+ ret = request_firmware(&fw, FW_FILE, dev);
+ if (ret) {
+ dev_err(dev, "failed to load '%s'\n", FW_FILE);
+ goto out_clear_status;
+ }
+
+ if (fw->size > total_size) {
+ dev_err(dev, "firmware too large (%zu > %zu)\n", fw->size, total_size);
+ goto out_release_fw;
+ }
+
+ buffer = kvmalloc(total_size, GFP_KERNEL);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto out_release_fw;
+ }
+
+ memset(buffer, 0xff, total_size);
+ memcpy(buffer, fw->data, fw->size);
+ crc32 = cal_crc32_custom(buffer, total_size);
+
+ kvfree(buffer);
+
+ mutex_lock(<7911exc->upgrade_lock);
+
+ lt7911exc_reset(lt7911exc);
+ mutex_lock(<7911exc->ocm_lock);
+ lt7911exc_hw_mcu_halt(lt7911exc);
+ mutex_unlock(<7911exc->ocm_lock);
+
+ ret = lt7911exc_block_erase(lt7911exc);
+ if (ret) {
+ dev_err(dev, "failed to block erase.\n");
+ goto out_unlock;
+ }
+
+ ret = lt7911exc_write_data(lt7911exc, fw, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to write firmware data\n");
+ goto out_unlock;
+ }
+
+ ret = lt7911exc_write_crc(lt7911exc, crc32, FW_SIZE - 4);
+ if (ret < 0) {
+ dev_err(dev, "failed to write firmware crc\n");
+ goto out_unlock;
+ }
+
+ lt7911exc_reset(lt7911exc);
+
+ ret = lt7911exc_upgrade_result(lt7911exc, crc32);
+ if (ret)
+ dev_err(dev, "firmware verification failed\n");
+
+out_unlock:
+ mutex_lock(<7911exc->ocm_lock);
+ lt7911exc_hw_mcu_run(lt7911exc);
+ lt7911exc->fw_version = lt7911exc_read_version(lt7911exc);
+ mutex_unlock(<7911exc->ocm_lock);
+
+ mutex_unlock(<7911exc->upgrade_lock);
+ /* Notify DRM framework that hardware state changed/needs a modeset */
+ if (lt7911exc->bridge.dev)
+ drm_kms_helper_hotplug_event(lt7911exc->bridge.dev);
+
+out_release_fw:
+ release_firmware(fw);
+
+out_clear_status:
+ mutex_lock(<7911exc->ocm_lock);
+ /* Only clear status if the module is not in the process of removal */
+ if (!lt7911exc->removed)
+ lt7911exc->upgrade = false;
+ mutex_unlock(<7911exc->ocm_lock);
+}
+
+static void lt7911exc_atomic_pre_enable(struct drm_bridge *bridge, struct drm_atomic_state *state)
+{
+ struct lt7911exc *lt7911exc = bridge_to_lt7911exc(bridge);
+
+ guard(mutex)(<7911exc->ocm_lock);
+
+ //enable mipi stream
+ if (!lt7911exc->upgrade)
+ regmap_write(lt7911exc->regmap, 0xe0b0, 0x01);
+}
+
+static void lt7911exc_atomic_post_disable(struct drm_bridge *bridge, struct drm_atomic_state *state)
+{
+ struct lt7911exc *lt7911exc = bridge_to_lt7911exc(bridge);
+
+ guard(mutex)(<7911exc->ocm_lock);
+
+ //disable mipi stream
+ if (!lt7911exc->upgrade)
+ regmap_write(lt7911exc->regmap, 0xe0b0, 0x00);
+}
+
+static int lt7911exc_bridge_attach(struct drm_bridge *bridge,
+ struct drm_encoder *encoder,
+ enum drm_bridge_attach_flags flags)
+{
+ struct lt7911exc *lt7911exc = bridge_to_lt7911exc(bridge);
+
+ if (!lt7911exc->output.bridge) {
+ dev_warn(lt7911exc->dev, "Next bridge/panel not attached yet, deferring\n");
+ return -EPROBE_DEFER;
+ }
+
+ return drm_bridge_attach(encoder, lt7911exc->output.bridge, bridge, flags);
+}
+
+static const struct drm_bridge_funcs lt7911exc_bridge_funcs = {
+ .attach = lt7911exc_bridge_attach,
+ .atomic_pre_enable = lt7911exc_atomic_pre_enable,
+ .atomic_post_disable = lt7911exc_atomic_post_disable,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+};
+
+static int lt7911exc_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_device *dev)
+{
+ struct lt7911exc *lt7911exc = dsi_host_to_lt7911exc(host);
+ struct drm_bridge *bridge;
+ struct drm_panel *panel;
+ int ret;
+
+ if (lt7911exc->output.dev)
+ return -EBUSY;
+
+ ret = drm_of_find_panel_or_bridge(host->dev->of_node, 1, 0, &panel, &bridge);
+ if (ret)
+ return ret;
+
+ if (panel) {
+ bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
+ }
+ lt7911exc->output.dev = dev;
+ lt7911exc->output.bridge = bridge;
+ lt7911exc->output.panel = panel;
+
+ return 0;
+}
+
+static int lt7911exc_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *dev)
+{
+ struct lt7911exc *lt7911exc = dsi_host_to_lt7911exc(host);
+
+ if (!lt7911exc->output.dev)
+ return 0;
+
+ if (lt7911exc->output.panel && lt7911exc->output.bridge) {
+ drm_panel_bridge_remove(lt7911exc->output.bridge);
+ lt7911exc->output.bridge = NULL;
+ lt7911exc->output.panel = NULL;
+ }
+
+ lt7911exc->output.dev = NULL;
+
+ return 0;
+}
+
+/*
+ * The internal firmware controls the panel initialization
+ * sequence and handles all MIPI DSI command transmission.
+ */
+static ssize_t lt7911exc_dsi_host_transfer(struct mipi_dsi_host *host,
+ const struct mipi_dsi_msg *msg)
+{
+ struct lt7911exc *lt7911exc = dsi_host_to_lt7911exc(host);
+
+ if (msg->rx_len) {
+ dev_warn(lt7911exc->dev, "MIPI DSI read is not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ switch (msg->type) {
+ case MIPI_DSI_DCS_SHORT_WRITE:
+ case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+ case MIPI_DSI_DCS_LONG_WRITE:
+ case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+ case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+ case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+ case MIPI_DSI_GENERIC_LONG_WRITE:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ guard(mutex)(<7911exc->ocm_lock);
+
+ if (lt7911exc->upgrade)
+ return -EBUSY;
+
+ return msg->tx_len;
+}
+
+static const struct mipi_dsi_host_ops lt7911exc_dsi_host_ops = {
+ .attach = lt7911exc_dsi_host_attach,
+ .detach = lt7911exc_dsi_host_detach,
+ .transfer = lt7911exc_dsi_host_transfer,
+};
+
+static ssize_t lt7911exc_firmware_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lt7911exc *lt7911exc = dev_get_drvdata(dev);
+
+ if (!mutex_trylock(<7911exc->upgrade_lock))
+ return -EBUSY;
+
+ mutex_lock(<7911exc->ocm_lock);
+
+ if (lt7911exc->upgrade || lt7911exc->removed) {
+ mutex_unlock(<7911exc->ocm_lock);
+ mutex_unlock(<7911exc->upgrade_lock);
+ return -EBUSY;
+ }
+
+ lt7911exc->upgrade = true;
+ mutex_unlock(<7911exc->ocm_lock);
+ mutex_unlock(<7911exc->upgrade_lock);
+
+ schedule_work(<7911exc->work);
+
+ return len;
+}
+
+static ssize_t lt7911exc_firmware_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct lt7911exc *lt7911exc = dev_get_drvdata(dev);
+ int version;
+
+ mutex_lock(<7911exc->ocm_lock);
+ version = lt7911exc->fw_version;
+ mutex_unlock(<7911exc->ocm_lock);
+ return sysfs_emit(buf, "0x%04x\n", version);
+}
+
+static DEVICE_ATTR_RW(lt7911exc_firmware);
+
+static struct attribute *lt7911exc_attrs[] = {
+ &dev_attr_lt7911exc_firmware.attr,
+ NULL,
+};
+
+static const struct attribute_group lt7911exc_attr_group = {
+ .attrs = lt7911exc_attrs,
+};
+
+static const struct attribute_group *lt7911exc_attr_groups[] = {
+ <7911exc_attr_group,
+ NULL,
+};
+
+static int lt7911exc_probe(struct i2c_client *client)
+{
+ struct lt7911exc *lt7911exc;
+ struct device *dev = &client->dev;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return dev_err_probe(dev, -ENODEV, "device doesn't support I2C\n");
+
+ lt7911exc = devm_drm_bridge_alloc(dev, struct lt7911exc, bridge, <7911exc_bridge_funcs);
+ if (IS_ERR(lt7911exc))
+ return dev_err_probe(dev, PTR_ERR(lt7911exc), "drm bridge alloc failed.\n");
+
+ dev_set_drvdata(dev, lt7911exc);
+
+ lt7911exc->client = client;
+ lt7911exc->dev = dev;
+ lt7911exc->upgrade = false;
+ lt7911exc->removed = false;
+
+ ret = devm_mutex_init(dev, <7911exc->ocm_lock);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init mutex\n");
+
+ ret = devm_mutex_init(dev, <7911exc->upgrade_lock);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init upgrade_lock\n");
+
+ lt7911exc->regmap = devm_regmap_init_i2c(client, <7911exc_regmap_config);
+ if (IS_ERR(lt7911exc->regmap))
+ return dev_err_probe(dev, PTR_ERR(lt7911exc->regmap), "regmap i2c init failed\n");
+
+ lt7911exc->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(lt7911exc->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(lt7911exc->reset_gpio),
+ "failed to acquire reset gpio\n");
+
+ ret = lt7911exc_regulator_enable(lt7911exc);
+ if (ret)
+ return ret;
+
+ lt7911exc_reset(lt7911exc);
+
+ mutex_lock(<7911exc->ocm_lock);
+
+ lt7911exc->fw_version = lt7911exc_read_version(lt7911exc);
+
+ mutex_unlock(<7911exc->ocm_lock);
+
+ if (lt7911exc->fw_version < 0)
+ return dev_err_probe(dev, lt7911exc->fw_version, "failed read version of chip\n");
+
+ lt7911exc->dsi_host.dev = dev;
+ lt7911exc->dsi_host.ops = <7911exc_dsi_host_ops;
+ lt7911exc->bridge.of_node = np;
+
+ INIT_WORK(<7911exc->work, lt7911exc_firmware_upgrade_work);
+
+ i2c_set_clientdata(client, lt7911exc);
+
+ drm_bridge_add(<7911exc->bridge);
+
+ ret = mipi_dsi_host_register(<7911exc->dsi_host);
+ if (ret) {
+ drm_bridge_remove(<7911exc->bridge);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void lt7911exc_remove(struct i2c_client *client)
+{
+ struct lt7911exc *lt7911exc = i2c_get_clientdata(client);
+
+ mutex_lock(<7911exc->ocm_lock);
+ lt7911exc->removed = true;
+ lt7911exc->upgrade = true;
+ mutex_unlock(<7911exc->ocm_lock);
+
+ cancel_work_sync(<7911exc->work);
+
+ mipi_dsi_host_unregister(<7911exc->dsi_host);
+ drm_bridge_remove(<7911exc->bridge);
+
+ gpiod_set_value_cansleep(lt7911exc->reset_gpio, 1);
+}
+
+static const struct i2c_device_id lt7911exc_i2c_table[] = {
+ {"lt7911exc"},
+ {/* sentinel */}
+};
+
+MODULE_DEVICE_TABLE(i2c, lt7911exc_i2c_table);
+
+static const struct of_device_id lt7911exc_devices[] = {
+ {.compatible = "lontium,lt7911exc"},
+ {/* sentinel */}
+};
+MODULE_DEVICE_TABLE(of, lt7911exc_devices);
+
+static struct i2c_driver lt7911exc_driver = {
+ .id_table = lt7911exc_i2c_table,
+ .probe = lt7911exc_probe,
+ .remove = lt7911exc_remove,
+ .driver = {
+ .name = "lt7911exc",
+ .of_match_table = lt7911exc_devices,
+ .dev_groups = lt7911exc_attr_groups,
+ },
+};
+module_i2c_driver(lt7911exc_driver);
+
+MODULE_AUTHOR("SunYun Yang <syyang@lontium.com>");
+MODULE_DESCRIPTION("Lontium LT7911EXC EDP to MIPI DSI bridge driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FW_FILE);
--
2.34.1
^ permalink raw reply related
* Re: [PATCH RFC v5 15/18] riscv_cbqri: resctrl: Add mbm_total_bytes bandwidth monitoring
From: sashiko-bot @ 2026-05-25 1:27 UTC (permalink / raw)
To: Drew Fustini; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260524-ssqosid-cbqri-rqsc-v7-0-v5-15-78d3a7ba9dbe@kernel.org>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [Medium] Domain bringup failure and redundant allocations due to bypassed topology validation.
- [Medium] System RMID capacity artificially crippled by unused Bandwidth Controllers.
Pre-existing issues:
- [Critical] Use-after-free (UAF) execution context upon cpuhp read lock release.
- [High] Out-of-bounds RMID allocations lead to -ERANGE read failures and stale monitoring state.
--
commit 1063299534eebd0856c698cf6583a1b91c07b030
Author: Drew Fustini <fustini@kernel.org>
riscv_cbqri: resctrl: Add mbm_total_bytes bandwidth monitoring
Expose CBQRI bandwidth controller's combined read+write counter as
the L3 mbm_total_bytes event. A software accumulator keeps the
64-bit byte total monotonic across the 62-bit hardware counter wrap.
> diff --git a/drivers/resctrl/cbqri_resctrl.c b/drivers/resctrl/cbqri_resctrl.c
> --- a/drivers/resctrl/cbqri_resctrl.c
> +++ b/drivers/resctrl/cbqri_resctrl.c
[ ... ]
> @@ -305,6 +344,82 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_domain_hdr *hdr,
> mutex_unlock(&ctrl->lock);
> break;
>
> + case QOS_L3_MBM_TOTAL_EVENT_ID: {
> + struct cbqri_controller *bc;
> +
> + /*
> + * The L3 monitoring domain's id is the L3 cache id. The
> + * matching ctrl domain's hw_dom->paired_bc was cached at
> + * add time to avoid walking cbqri_controllers on every read.
> + */
> + d = cbqri_find_ctrl_domain(&r->ctrl_domains, hdr->id);
> + if (!d) {
> + err = -ENOENT;
> + break;
> + }
> + hw_dom = container_of(d, struct cbqri_resctrl_dom, resctrl_ctrl_dom);
> + bc = hw_dom->paired_bc;
> + if (!bc) {
> + err = -ENOENT;
> + break;
> + }
> + if (WARN_ON_ONCE(!bc->mbm_total_states)) {
> + err = -EIO;
> + break;
> + }
> + if (rmid >= bc->mcid_count) {
> + err = -ERANGE;
> + break;
> + }
[Severity: High]
This is a pre-existing issue, but will the core allocate out-of-bounds
RMIDs that trigger this -ERANGE error? Looking at
cbqri_resctrl_control_init(), num_rmid is bound to the local capacity
rather than the global minimum (max_rmid). Could this lead to stale
monitoring state and bypass the max_rmid-bounded reset loop?
> +
> + mutex_lock(&bc->lock);
[ ... ]
> @@ -824,15 +948,54 @@ static int cbqri_resctrl_pick_bw_alloc(void)
> }
>
> /*
> - * Pick one controller per monitoring event. L3 OCCUP comes from the
> - * picked L3 CC (if mon_capable).
> + * Pick one controller per monitoring event. L3 OCCUP comes from the
> + * picked L3 CC if mon_capable. MBM_TOTAL from the only mon-capable BC,
> + * but only when the system exposes exactly one L3 cache. Pairing a
> + * single BC with multiple L3 domains would overcount system bandwidth
> + * by a factor equal to the L3 domain count.
> */
> static void cbqri_resctrl_pick_counters(void)
> {
> struct cbqri_resctrl_res *l3 = &cbqri_resctrl_resources[RDT_RESOURCE_L3];
> + struct cbqri_controller *ctrl, *prev;
> + unsigned int l3_count = 0;
>
> if (l3->ctrl && l3->ctrl->mon_capable)
> cbqri_resctrl_counters[QOS_L3_OCCUP_EVENT_ID] = l3->ctrl;
> +
> + /* Count distinct L3 cache_ids */
> + list_for_each_entry(ctrl, &cbqri_controllers, list) {
[ ... ]
> + if (!seen)
> + l3_count++;
> + }
> +
> + if (l3_count > 1) {
> + pr_warn_once("multiple L3 domains (%u) detected. mbm_total_bytes disabled\n",
> + l3_count);
> + return;
> + }
[Severity: Medium]
When mbm_total_bytes is disabled here, will the unused Bandwidth
Controller's smaller mcid_count still artificially clamp the global
max_rmid in cbqri_resctrl_accumulate_caps()? The unused BC remains
mon_capable, which might restrict valid occupancy monitoring system-wide.
> +
> + cbqri_resctrl_counters[QOS_L3_MBM_TOTAL_EVENT_ID] =
> + cbqri_find_only_mon_bc();
> }
[ ... ]
> @@ -948,6 +1111,26 @@ static int cbqri_attach_cpu_to_l3_mon(struct cbqri_controller *ctrl,
> else
> list_add_tail(&mon_dom->hdr.list, &res->mon_domains);
>
> + /*
> + * Pair this L3 domain with the system's mon-capable BC and
> + * initialise the BC's per-MCID software accumulators before
> + * resctrl_online_mon_domain() exposes the domain to userspace.
> + * A concurrent sysfs read of mbm_total_bytes between online and
> + * BC init would otherwise pass the !bc->mbm_total_states check
> + * with a half-initialised pointer.
> + */
> + hw_dom = container_of(ctrl_dom, struct cbqri_resctrl_dom, resctrl_ctrl_dom);
> +
> + hw_dom->paired_bc = cbqri_find_only_mon_bc();
> + if (hw_dom->paired_bc) {
> + err = cbqri_init_bc_mon_counters(hw_dom->paired_bc);
[Severity: Medium]
If mbm_total_bytes was disabled in cbqri_resctrl_pick_counters() due to
multiple L3 domains, does this unconditionally allocate memory and issue
MMIO resets for an unused BC? Could an initialization failure in this unused
BC cause the entire L3 bringup to abort here?
> + if (err) {
> + pr_err("BC @%pa: mon init failed (%d)\n", &hw_dom->paired_bc->addr, err);
> + hw_dom->paired_bc = NULL;
> + goto err_listdel;
> + }
> + }
> +
> err = resctrl_online_mon_domain(res, &mon_dom->hdr);
> if (err)
> goto err_listdel;
>
> err = cbqri_init_mon_counters(ctrl);
> if (err)
> goto err_offline;
>
> return 0;
>
> err_offline:
> /*
> * cancel_delayed_work (not _sync) avoids deadlocking against the
> * cqm_limbo worker which takes cpus_read_lock while this hotplug
> * callback already holds cpus_write_lock. mbm_over is only
> * INIT_DELAYED_WORK'd when MBM_TOTAL was enabled, so gate the
> * cancel on the same condition to avoid touching a zeroed work
> * struct.
> */
> cancel_delayed_work(&mon_dom->cqm_limbo);
> + if (resctrl_is_mon_event_enabled(QOS_L3_MBM_TOTAL_EVENT_ID))
> + cancel_delayed_work(&mon_dom->mbm_over);
> resctrl_offline_mon_domain(res, &mon_dom->hdr);
> err_listdel:
> list_del(&mon_dom->hdr.list);
> err_free:
> kfree(mon_dom);
> return err;
> }
[Severity: Critical]
This is a pre-existing issue, but since this patch introduces a new trigger
instance for mbm_over, does this create a use-after-free window?
Since cancel_delayed_work() is used without _sync, if a worker is currently
stalled waiting on the cpus_read_lock, won't kfree(mon_dom) execute
synchronously here? Once the write lock is dropped, couldn't the pending
worker resume and dereference the freed memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260524-ssqosid-cbqri-rqsc-v7-0-v5-0-78d3a7ba9dbe@kernel.org?part=15
^ permalink raw reply
* Re: [PATCH v2] ARM: dts: aspeed: Enable networking for Asus Kommando IPMI Card
From: Andrew Jeffery @ 2026-05-25 1:25 UTC (permalink / raw)
To: Andrew Lunn, Anirudh Srinivasan
Cc: devicetree, linux-arm-kernel, linux-aspeed, linux-kernel,
Rob Herring, Conor Dooley, Krzysztof Kozlowski, Joel Stanley
In-Reply-To: <d8c7d7c9-6f2f-4d3f-95d4-877e8504a1b6@lunn.ch>
On Fri, 2026-05-22 at 14:05 +0200, Andrew Lunn wrote:
> On Thu, May 21, 2026 at 10:49:24PM -0500, Anirudh Srinivasan wrote:
> > Hi Andrew,
> >
> > On Tue, Mar 31, 2026 at 9:18 AM Anirudh Srinivasan
> > <anirudhsriniv@gmail.com> wrote:
> > >
> > > Adds the DT nodes needed for ethernet support for Asus Kommando, with
> > > phy mode set to rgmii-id.
> > >
> > > When this DT was originally added, the phy mode was set to rgmii (which
> > > was incorrect). It was suggested to remove networking support from the
> > > DT till the Aspeed networking driver was patched so that the correct phy
> > > mode could be used.
> > >
> > > The discussion in [1] mentions that u-boot was inserting clk delays that
> > > weren't needed, which resulted in needing to set the phy mode in linux
> > > to rgmii incorrectly. The solution suggested there was to patch u-boot to
> > > no longer insert these clk delays and use rgmii-id as the phy mode for
> > > any future DTs added to linux.
> > >
> > > This DT was tested (on the OpenBMC u-boot fork [2]) with a u-boot DT
> > > modified to insert clk delays of 0 (instead of patching u-boot itself).
> > > [3] adds a u-boot DT for this device (without networking) and describes
> > > how to patch it to add networking support. If this patched DT is used,
> > > then networking works with rgmii-id phy mode in both u-boot and linux.
> > >
> > > [1] https://lore.kernel.org/linux-aspeed/ef88bb50-9f2c-458d-a7e5-dc5ecb9c777a@lunn.ch/
> > > [2] https://github.com/openbmc/u-boot/tree/v2019.04-aspeed-openbmc
> > > [3] https://lore.kernel.org/openbmc/20260328-asus-kommando-v2-1-2a656f8cd314@gmail.com/
> > >
> > > Signed-off-by: Anirudh Srinivasan <anirudhsriniv@gmail.com>
> > > ---
> > > This patch is based off aspeed/arm/dt from bmc tree
> > > ---
> > > Changes in v2:
> > > - Commit message now mentions that the u-boot tested against is the
> > > openbmc u-boot fork
> > > - Link to v1: https://lore.kernel.org/r/20260328-asus-kommando-networking-v1-1-66d308b88536@gmail.com
> > > ---
> > > .../dts/aspeed/aspeed-bmc-asus-kommando-ipmi-card.dts | 18 ++++++++++++++++++
> > > 1 file changed, 18 insertions(+)
> > >
> > > diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-asus-kommando-ipmi-card.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-asus-kommando-ipmi-card.dts
> > > index ab7ad320067c1ddc0fea9ac386fd488c8ef28184..e0f7d92efa18ccbad2c336236c3b9d01b7de1bba 100644
> > > --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-asus-kommando-ipmi-card.dts
> > > +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-asus-kommando-ipmi-card.dts
> > > @@ -107,6 +107,24 @@ &gpio1 {
> > > /*18E0 32*/ "","","","","","","","";
> > > };
> > >
> > > +&mac2 {
> > > + status = "okay";
> > > +
> > > + phy-mode = "rgmii-id";
> > > + phy-handle = <ðphy2>;
> > > + pinctrl-names = "default";
> > > + pinctrl-0 = <&pinctrl_rgmii3_default>;
> > > +};
> > > +
> > > +&mdio2 {
> > > + status = "okay";
> > > +
> > > + ethphy2: ethernet-phy@0 {
> > > + compatible = "ethernet-phy-ieee802.3-c22";
> > > + reg = <0>;
> > > + };
> > > +};
> > > +
> > > &vhub {
> > > status = "okay";
> > > };
> > >
> > > ---
> > > base-commit: 76b4ec8efdc3887cdbf730da2e55881fc1a18770
> > > change-id: 20260328-asus-kommando-networking-5c0612aa6b8c
> > >
> > > Best regards,
> > > --
> > > Anirudh Srinivasan <anirudhsriniv@gmail.com>
> > >
> >
> > While we're figuring out what to do with u-boot, what are your
> > thoughts on getting this patch in so that the kernel DTS changes
> > needed for networking land in this cycle?
> >
> > The current commit message might become somewhat outdated if the
> > u-boot patch changes though, so not sure if that's okay.
>
> The commit message explains "Why?", which is what is important. So it
> should not matter if it becomes outdated. And the DT is correct, no
> matter how the issue is solved.
Yeah, this was my thought too, so I intend to apply it.
>
> So i'm O.K. with this.
Thanks
Andrew
^ permalink raw reply
* Re: [PATCH v12 2/2] drm/bridge: Add Lontium LT7911EXC eDP to MIPI DSI bridge
From: sashiko-bot @ 2026-05-25 1:52 UTC (permalink / raw)
To: syyang; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260525010545.9470-3-syyang@lontium.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Dangling pointer created during MIPI DSI host detach if the downstream device is a bridge rather than a panel.
- [High] Firmware upgrade CRC mismatch due to unpadded partial page writes.
- [High] Firmware upgrade workqueue accesses `bridge.dev` without ensuring the DRM device is alive, leading to a Use-After-Free.
- [High] Regmap page selector cache desynchronization after device reset causes firmware upgrade commands to be routed to the wrong register page.
--
commit 43236ff612b99bc861cbbc490c9d4569107b4d46
Author: Sunyun Yang <syyang@lontium.com>
Subject: drm/bridge: Add Lontium LT7911EXC eDP to MIPI DSI bridge
This commit adds support for the Lontium LT7911EXC bridge chip, which converts
eDP input to MIPI DSI output. It provides DRM bridge integration, MIPI DSI
host interfaces, I2C firmware upgrade mechanisms, and GPIO resets.
> diff --git a/drivers/gpu/drm/bridge/lontium-lt7911exc.c b/drivers/gpu/drm/bridge/lontium-lt7911exc.c
> new file mode 100644
> index 0000000000000..de6953ed1e3b0
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/lontium-lt7911exc.c
[ ... ]
> +static int lt7911exc_write_data(struct lt7911exc *lt7911exc, const struct firmware *fw, u64 addr)
> +{
> + struct device *dev = lt7911exc->dev;
> + int ret;
> + int page = 0, num = 0, page_len = 0;
> + u64 size, offset;
> + const u8 *data;
> +
> + data = fw->data;
> + size = fw->size;
> + page = (size + LT_PAGE_SIZE - 1) / LT_PAGE_SIZE;
[ ... ]
> + for (num = 0; num < page; num++) {
> + offset = num * LT_PAGE_SIZE;
> + page_len = (offset + LT_PAGE_SIZE <= size) ? LT_PAGE_SIZE : (size - offset);
> + ret = lt7911exc_prog_init(lt7911exc, addr);
> + if (ret)
> + return ret;
> +
> + ret = regmap_raw_write(lt7911exc->regmap, 0xe05d, &data[offset], page_len);
[Severity: High]
The software CRC in lt7911exc_firmware_upgrade_work() is calculated over a
buffer padded with 0xff up to FW_SIZE - 4. However, this function writes the
original unpadded fw->data.
If fw->size is not a multiple of the 32-byte page size, does the short write
leave residual data in the hardware's internal page buffer? This could cause
a CRC mismatch and firmware upgrade failure since the padded bytes are
actually flashed. Should this function write the padded buffer instead?
[ ... ]
> +static void lt7911exc_firmware_upgrade_work(struct work_struct *work)
> +{
[ ... ]
> + lt7911exc_reset(lt7911exc);
> + mutex_lock(<7911exc->ocm_lock);
> + lt7911exc_hw_mcu_halt(lt7911exc);
[Severity: High]
When lt7911exc_reset() asserts the reset GPIO, the physical hardware's page
control register is reset to 0x00.
Does the regmap cache know about this external reset? If not, regmap might
assume the hardware is already on page 0xe0 and omit the page control write
during lt7911exc_hw_mcu_halt().
Can this route commands to the wrong page and corrupt hardware state? Should
the regmap cache be synchronized or marked dirty after a reset?
[ ... ]
> +out_unlock:
> + mutex_lock(<7911exc->ocm_lock);
> + lt7911exc_hw_mcu_run(lt7911exc);
> + lt7911exc->fw_version = lt7911exc_read_version(lt7911exc);
> + mutex_unlock(<7911exc->ocm_lock);
> +
> + mutex_unlock(<7911exc->upgrade_lock);
> + /* Notify DRM framework that hardware state changed/needs a modeset */
> + if (lt7911exc->bridge.dev)
> + drm_kms_helper_hotplug_event(lt7911exc->bridge.dev);
[Severity: High]
Is it possible for lt7911exc->bridge.dev to be a dangling pointer here?
The DRM core does not clear bridge->dev on bridge detachment. Since the sysfs
attribute is tied to the I2C device (which remains bound), could a user
trigger a firmware upgrade after the KMS driver unbinds? This would lead to a
use-after-free when drm_kms_helper_hotplug_event() executes.
[ ... ]
> +static int lt7911exc_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *dev)
> +{
> + struct lt7911exc *lt7911exc = dsi_host_to_lt7911exc(host);
> +
> + if (!lt7911exc->output.dev)
> + return 0;
> +
> + if (lt7911exc->output.panel && lt7911exc->output.bridge) {
> + drm_panel_bridge_remove(lt7911exc->output.bridge);
> + lt7911exc->output.bridge = NULL;
> + lt7911exc->output.panel = NULL;
> + }
> +
> + lt7911exc->output.dev = NULL;
> +
> + return 0;
> +}
[Severity: High]
If the downstream device is a raw bridge instead of a panel, output.panel
will be NULL, causing this block to be skipped.
Does this leave output.bridge as a dangling pointer? If the bridge is freed
and the display pipeline is re-attached, lt7911exc_bridge_attach() might
dereference this stale pointer and cause a use-after-free.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260525010545.9470-1-syyang@lontium.com?part=2
^ permalink raw reply
* Re: [PATCH RFC v2 1/7] dt-bindings: iio: add Open Sensor Fusion UART device
From: Kim Jinseob @ 2026-05-25 2:06 UTC (permalink / raw)
To: Conor Dooley
Cc: Jonathan Cameron, linux-iio, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
devicetree, linux-kernel
In-Reply-To: <20260524-tidbit-shorty-a4754de1db64@spud>
> Most of my v1 review was ignored it seems. Please go back to v1 and
> respond to the points I made.
I missed the v1 DT binding feedback when preparing v2. I will go back to
the v1 thread and reply there before the next revision.
Jinseob
> Thanks,
> Conor.
^ permalink raw reply
* Re: [PATCH 3/5] arm64: dts: freescale: imx95-toradex-smarc: move CM7 node to SoC DTSI
From: Peng Fan @ 2026-05-25 2:15 UTC (permalink / raw)
To: Laurentiu Mihalcea
Cc: Bjorn Andersson, Mathieu Poirier, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
Peng Fan, Fabio Estevam, Pengutronix Kernel Team,
linux-remoteproc, devicetree, imx, linux-arm-kernel, linux-kernel
In-Reply-To: <20260522111849.783-4-laurentiumihalcea111@gmail.com>
On Fri, May 22, 2026 at 04:18:47AM -0700, Laurentiu Mihalcea wrote:
>From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>
>The CM7 remoteproc configuration is common to multiple MX95-based
>platforms (e.g. MX95-19x19-EVK, MX95-15x15-FRDM, SMARC-IMX95, etc.).
>Therefore, move the node to the MX95 SoC DTSI. While at it, split the mbox
>channels using <>.
>
>Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>---
> .../boot/dts/freescale/imx95-toradex-smarc.dtsi | 14 ++++++--------
> arch/arm64/boot/dts/freescale/imx95.dtsi | 7 +++++++
> 2 files changed, 13 insertions(+), 8 deletions(-)
>
>diff --git a/arch/arm64/boot/dts/freescale/imx95-toradex-smarc.dtsi b/arch/arm64/boot/dts/freescale/imx95-toradex-smarc.dtsi
>index 7d760470201f..c94a63a3bf8f 100644
>--- a/arch/arm64/boot/dts/freescale/imx95-toradex-smarc.dtsi
>+++ b/arch/arm64/boot/dts/freescale/imx95-toradex-smarc.dtsi
>@@ -145,14 +145,6 @@ reg_wifi_en: regulator-wifi-en {
> startup-delay-us = <2000>;
> };
>
>- remoteproc-cm7 {
>- compatible = "fsl,imx95-cm7";
>- mboxes = <&mu7 0 1 &mu7 1 1 &mu7 3 1>;
>- mbox-names = "tx", "rx", "rxdb";
>- memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>,
>- <&vdev1vring0>, <&vdev1vring1>, <&rsc_table>, <&m7_reserved>;
>- };
>-
> reserved-memory {
> #address-cells = <2>;
> #size-cells = <2>;
>@@ -204,6 +196,12 @@ vdevbuffer: vdevbuffer@88020000 {
> };
> };
>
>+&cm7 {
>+ memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>,
>+ <&vdev1vring0>, <&vdev1vring1>, <&rsc_table>, <&m7_reserved>;
>+ status = "okay";
>+};
>+
> /* SMARC GBE0 */
> &enetc_port0 {
> pinctrl-names = "default";
>diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi
>index 3e35c956a4d7..f8760ac067fa 100644
>--- a/arch/arm64/boot/dts/freescale/imx95.dtsi
>+++ b/arch/arm64/boot/dts/freescale/imx95.dtsi
>@@ -272,6 +272,13 @@ opp-1000000000 {
> };
> };
>
>+ cm7: remoteproc-cm7 {
>+ compatible = "fsl,imx95-cm7";
>+ mboxes = <&mu7 0 1>, <&mu7 1 1>, <&mu7 3 1>;
>+ mbox-names = "tx", "rx", "rxdb";
Please not put mboxes and mbox-names in dtsi. Some demos may not
require them and boards may use different MUs.
Regards
Peng
>+ status = "disabled";
>+ };
>+
> clk_ext1: clock-ext1 {
> compatible = "fixed-clock";
> #clock-cells = <0>;
>--
>2.43.0
>
^ permalink raw reply
* Re: [PATCH 2/5] remoteproc: imx_rpoc: fix carveout name parsing
From: Peng Fan @ 2026-05-25 2:13 UTC (permalink / raw)
To: Laurentiu Mihalcea
Cc: Bjorn Andersson, Mathieu Poirier, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
Peng Fan, Fabio Estevam, Pengutronix Kernel Team,
linux-remoteproc, devicetree, imx, linux-arm-kernel, linux-kernel
In-Reply-To: <20260522111849.783-3-laurentiumihalcea111@gmail.com>
On Fri, May 22, 2026 at 04:18:46AM -0700, Laurentiu Mihalcea wrote:
>From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>
>The imx remoteproc driver assumes that the names of the reserved memory
>regions reflect their usage (e.g. "vdevbuffer", "vdev0vring0", etc.). This
>conflicts with the devicetree specification's recommendation, which states
>that the names of the devicetree nodes should be generic.
>
>Therefore, instead of relying on the node names, use the names passed via
>the "memory-region-names" property if present. Otherwise, keep the old
>behavior.
>
>The definition of imx_rproc_rmem_to_resource() is added to a common place
>as imx_dsp_rproc.c can also use it given that it suffers from the same
>aforementioned problem.
>
>Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>---
> drivers/remoteproc/imx_rproc.c | 7 +++++--
> drivers/remoteproc/imx_rproc.h | 19 +++++++++++++++++++
> 2 files changed, 24 insertions(+), 2 deletions(-)
>
>diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
>index 7f54322244ac..1ee1c658dcc1 100644
>--- a/drivers/remoteproc/imx_rproc.c
>+++ b/drivers/remoteproc/imx_rproc.c
>@@ -672,7 +672,7 @@ static int imx_rproc_prepare(struct rproc *rproc)
> int err;
> struct resource res;
>
>- err = of_reserved_mem_region_to_resource(np, i++, &res);
>+ err = imx_rproc_rmem_to_resource(np, i++, &res);
> if (err)
> break;
>
>@@ -850,11 +850,14 @@ static int imx_rproc_addr_init(struct imx_rproc *priv,
> if (nph <= 0)
> return 0;
>
>+ if (!of_property_present(np, "memory-region-names"))
>+ dev_warn(dev, "using node names for carveouts should be avoided\n");
Please check 'memory-regions && !memory-region-names', some demos may not
need to use memory regions.
Regards
Peng
>+
> /* remap optional addresses */
> for (a = 0; a < nph; a++) {
> struct resource res;
>
>- err = of_reserved_mem_region_to_resource(np, a, &res);
>+ err = imx_rproc_rmem_to_resource(np, a, &res);
> if (err) {
> dev_err(dev, "unable to resolve memory region\n");
> return err;
>diff --git a/drivers/remoteproc/imx_rproc.h b/drivers/remoteproc/imx_rproc.h
>index 0d7d48352a10..58e9daa41afe 100644
>--- a/drivers/remoteproc/imx_rproc.h
>+++ b/drivers/remoteproc/imx_rproc.h
>@@ -45,4 +45,23 @@ struct imx_rproc_dcfg {
> u32 reset_vector_mask;
> };
>
>+static inline int imx_rproc_rmem_to_resource(struct device_node *np,
>+ int index,
>+ struct resource *res)
>+{
>+ int ret;
>+
>+ ret = of_reserved_mem_region_to_resource(np, index, res);
>+ if (ret)
>+ return ret;
>+
>+ /* "memory-region-names" is optional */
>+ ret = of_property_read_string_index(np, "memory-region-names",
>+ index, &res->name);
>+ if (ret == -EINVAL)
>+ return 0;
>+
>+ return ret;
>+}
>+
> #endif /* _IMX_RPROC_H */
>--
>2.43.0
>
^ permalink raw reply
* [PATCH] dt-bindings: leds: bcm6358: Convert to DT schema
From: Ninad Naik @ 2026-05-25 3:32 UTC (permalink / raw)
To: lee, pavel, robh, krzk+dt, conor+dt, jonas.gorski
Cc: linux-leds, devicetree, linux-kernel, me, linux-kernel-mentees,
skhan, Ninad Naik
Convert the brcm,bcm6358 to DT schema.
Signed-off-by: Ninad Naik <ninadnaik07@gmail.com>
---
Referred to this thread:
https://lore.kernel.org/all/20250930-brcm6358-to-dt-v1-1-ba833ceb1575@thegoodpenguin.co.uk/
and added the fixes suggested.
.../bindings/leds/brcm,bcm6358-leds.yaml | 98 ++++++++++++
.../devicetree/bindings/leds/leds-bcm6358.txt | 143 ------------------
2 files changed, 98 insertions(+), 143 deletions(-)
create mode 100644 Documentation/devicetree/bindings/leds/brcm,bcm6358-leds.yaml
delete mode 100644 Documentation/devicetree/bindings/leds/leds-bcm6358.txt
diff --git a/Documentation/devicetree/bindings/leds/brcm,bcm6358-leds.yaml b/Documentation/devicetree/bindings/leds/brcm,bcm6358-leds.yaml
new file mode 100644
index 000000000000..a61417e5690b
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/brcm,bcm6358-leds.yaml
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/brcm,bcm6358-leds.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: LEDs connected to Broadcom BCM6358 controller
+
+description: |
+ This controller is present on BCM6358 and BCM6368.
+ In these SoCs there are Serial LEDs (LEDs connected to a 74x164 controller),
+ which can either be controlled by software (exporting the 74x164 as spi-gpio.
+ See Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml), or
+ by hardware using this driver.
+
+maintainers:
+ - Jonas Gorski <jonas.gorski@gmail.com>
+
+properties:
+ compatible:
+ const: brcm,bcm6358-leds
+
+ reg:
+ maxItems: 1
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ brcm,clk-div:
+ description: SCK signal divider.
+ default: 1
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [1, 2, 4, 8]
+
+ brcm,clk-dat-low:
+ description: Makes clock and data signals active low.
+ type: boolean
+
+patternProperties:
+ "^led@(0|1?[0-9a-f])$":
+ type: object
+ $ref: common.yaml#
+ description: Each LED is represented as a sub-node of
+ this device.
+
+ properties:
+ reg:
+ description: LED pin number.
+ minimum: 0
+ maximum: 31
+
+ required:
+ - reg
+
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - "#address-cells"
+ - "#size-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/leds/common.h>
+ led-controller@fffe00d0 {
+ compatible = "brcm,bcm6358-leds";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0xfffe00d0 0x8>;
+
+ led@0 {
+ reg = <0>;
+ active-low;
+ label = "white:alarm";
+ };
+ led@2 {
+ reg = <2>;
+ active-low;
+ label = "white:tv";
+ };
+ led@3 {
+ reg = <3>;
+ active-low;
+ label = "white:tel";
+ };
+ led@4 {
+ reg = <4>;
+ active-low;
+ label = "white:adsl";
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/leds/leds-bcm6358.txt b/Documentation/devicetree/bindings/leds/leds-bcm6358.txt
deleted file mode 100644
index 211ffc3c4a20..000000000000
--- a/Documentation/devicetree/bindings/leds/leds-bcm6358.txt
+++ /dev/null
@@ -1,143 +0,0 @@
-LEDs connected to Broadcom BCM6358 controller
-
-This controller is present on BCM6358 and BCM6368.
-In these SoCs there are Serial LEDs (LEDs connected to a 74x164 controller),
-which can either be controlled by software (exporting the 74x164 as spi-gpio.
-See Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml), or
-by hardware using this driver.
-
-Required properties:
- - compatible : should be "brcm,bcm6358-leds".
- - #address-cells : must be 1.
- - #size-cells : must be 0.
- - reg : BCM6358 LED controller address and size.
-
-Optional properties:
- - brcm,clk-div : SCK signal divider. Possible values are 1, 2, 4 and 8.
- Default : 1
- - brcm,clk-dat-low : Boolean, makes clock and data signals active low.
- Default : false
-
-Each LED is represented as a sub-node of the brcm,bcm6358-leds device.
-
-LED sub-node required properties:
- - reg : LED pin number (only LEDs 0 to 31 are valid).
-
-LED sub-node optional properties:
- - label : see Documentation/devicetree/bindings/leds/common.txt
- - default-state : see
- Documentation/devicetree/bindings/leds/common.txt
- - linux,default-trigger : see
- Documentation/devicetree/bindings/leds/common.txt
-
-Examples:
-Scenario 1 : BCM6358
- leds0: led-controller@fffe00d0 {
- compatible = "brcm,bcm6358-leds";
- #address-cells = <1>;
- #size-cells = <0>;
- reg = <0xfffe00d0 0x8>;
-
- alarm_white {
- reg = <0>;
- active-low;
- label = "white:alarm";
- };
- tv_white {
- reg = <2>;
- active-low;
- label = "white:tv";
- };
- tel_white {
- reg = <3>;
- active-low;
- label = "white:tel";
- };
- adsl_white {
- reg = <4>;
- active-low;
- label = "white:adsl";
- };
- };
-
-Scenario 2 : BCM6368
- leds0: led-controller@100000d0 {
- compatible = "brcm,bcm6358-leds";
- #address-cells = <1>;
- #size-cells = <0>;
- reg = <0x100000d0 0x8>;
- brcm,pol-low;
- brcm,clk-div = <4>;
-
- power_red {
- reg = <0>;
- active-low;
- label = "red:power";
- };
- power_green {
- reg = <1>;
- active-low;
- label = "green:power";
- default-state = "on";
- };
- power_blue {
- reg = <2>;
- label = "blue:power";
- };
- broadband_red {
- reg = <3>;
- active-low;
- label = "red:broadband";
- };
- broadband_green {
- reg = <4>;
- label = "green:broadband";
- };
- broadband_blue {
- reg = <5>;
- active-low;
- label = "blue:broadband";
- };
- wireless_red {
- reg = <6>;
- active-low;
- label = "red:wireless";
- };
- wireless_green {
- reg = <7>;
- active-low;
- label = "green:wireless";
- };
- wireless_blue {
- reg = <8>;
- label = "blue:wireless";
- };
- phone_red {
- reg = <9>;
- active-low;
- label = "red:phone";
- };
- phone_green {
- reg = <10>;
- active-low;
- label = "green:phone";
- };
- phone_blue {
- reg = <11>;
- label = "blue:phone";
- };
- upgrading_red {
- reg = <12>;
- active-low;
- label = "red:upgrading";
- };
- upgrading_green {
- reg = <13>;
- active-low;
- label = "green:upgrading";
- };
- upgrading_blue {
- reg = <14>;
- label = "blue:upgrading";
- };
- };
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v6 5/6] remoteproc: qcom: pas: Add late attach support for subsystems
From: Shawn Guo @ 2026-05-25 4:30 UTC (permalink / raw)
To: Stephan Gerhold
Cc: Jingyi Wang, Bjorn Andersson, Mathieu Poirier, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Manivannan Sadhasivam,
Luca Weiss, Bartosz Golaszewski, Konrad Dybcio, aiqun.yu,
tingwei.zhang, trilok.soni, yijie.yang, linux-arm-msm,
linux-remoteproc, devicetree, linux-kernel,
Gokul Krishna Krishnakumar
In-Reply-To: <ahBG6jKYdSAboWjs@linaro.org>
Hi Stephan,
Thank you for your great input!
On Fri, May 22, 2026 at 02:07:06PM +0200, Stephan Gerhold wrote:
> On Tue, May 19, 2026 at 12:24:23AM -0700, Jingyi Wang wrote:
> > Subsystems can be brought out of reset by entities such as bootloaders.
> > As the irq enablement could be later than subsystem bring up, the state
> > of subsystem should be checked by reading SMP2P bits.
> >
> > A new qcom_pas_attach() function is introduced. if a crash state is
> > detected for the subsystem, rproc_report_crash() is called. If the ready
> > state is detected, it will be marked as "attached", otherwise it could
> > be the early boot feature is not supported by other entities. In this
> > case, the state will be marked as RPROC_OFFLINE so that the PAS driver
> > can load the firmware and start the remoteproc.
> >
> > Co-developed-by: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
> > Signed-off-by: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
> > Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
>
> Unfortunately, removing the ping-pong functionality that was present in
> previous patch versions makes the whole mechanism a lot more fragile.
We are not discarding ping-pong functionality but would like to support
it as a second step, because it's not supported by all remote processors
these days, e.g. Nord ADSPs are brought out of reset by XBL but it doesn't
support pong.
> I'm not entirely sure if this has changed in SMP2P v2 or more recent
> firmware versions, but in my experience the SMP2P "ready" bit does not
> tell you if the remoteproc is actually running. The problem is that the
> "ready" bit is asserted by the remoteproc when the firmware is ready,
> but it is not cleared when you shutdown or forcibly stop the remoteproc.
>
> If this is still the case, you can easily reproduce that with the
> following test:
>
> 1. Start the system as usual and let it attach the remoteproc
> 2. Manually stop the remoteproc in sysfs (echo stop > state)
> 3. modprobe -r qcom_q6v5_pas
> 4. modprobe qcom_q6v5_pas
> 5. If the "ready" bit is still set, the driver will try attaching the
> remoteproc, but it's actually not running. No recovery will happen.
Indeed! I can reproduce the buggy state with Nord ADSP.
> In this situation, it is very difficult to detect the correct remoteproc
> state without relying on an additional query mechanism like the
> ping-pong feature.
>
> You can make it a bit more reliable if you also check the status of the
> "stop-ack" bit. This would tell you if the remoteproc was cleanly
> stopped with the SMP2P "stop" mechanism. However, that will typically
> still not fix the case above since nowadays remoteprocs are typically
> stopped via the QMI qcom_sysmon and the "stop-ack" is not set in that
> case. I believe this might set the separate "shutdown-ack" bit though
> that is described for some SoCs, I never finished testing that.
You are right! Per my testing on Nord ADSP, stop-ack is not set in any
way, but shutdown-ack is set via sysmon with ssctl_request_shutdown()
call. So a check on shutdown-ack during probe would be helpful for remote
processors like Nord ADSP.
> And even if you check both "stop-ack" and "shutdown-ack", that doesn't
> tell you if the remoteproc was forcibly killed using
> qcom_scm_pas_shutdown() without gracefully stopping it first. The ideal
> solution would be querying the PAS API to tell us if the remoteproc is
> actively running, but the last time I checked I was unfortunately not
> able to find a documented call that would tell us that.
I agree with you!
Thanks,
Shawn
^ permalink raw reply
* [PATCH net v1] dt-bindings: ethernet: eswin: fix hsp-sp-csr backward compatibility
From: lizhi2 @ 2026-05-25 5:24 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, robh, krzk+dt,
conor+dt, netdev, devicetree, linux-kernel
Cc: ningyu, linmin, pinkesh.vaghela, pritesh.patel, weishangjuan,
sashiko-bot, Zhi Li
From: Zhi Li <lizhi2@eswincomputing.com>
The previous change added two optional cells to
eswin,hsp-sp-csr, but omitted minItems: 4.
As a result, dt-schema implicitly required all 6 cells,
breaking backward compatibility with existing 4-cell
device trees.
Add minItems: 4 to preserve backward compatibility.
Fixes: c36069c6f46c ("dt-bindings: ethernet: eswin: add optional TXD and RXD delay register offsets")
Reported-by: Sashiko AI <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260519022334.35742C2BCB7@smtp.kernel.org/
Signed-off-by: Zhi Li <lizhi2@eswincomputing.com>
---
Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml b/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml
index b66ae6300faf..65882ff79d8d 100644
--- a/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml
+++ b/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml
@@ -84,7 +84,8 @@ properties:
This reference is provided for background information only.
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- - items:
+ - minItems: 4
+ items:
- description: Phandle to HSP(High-Speed Peripheral) device
- description: Offset of phy control register for internal
or external clock selection
--
2.25.1
^ permalink raw reply related
* Re: [PATCH 0/3] Add support for qcrypto on shikra
From: Kuldeep Singh @ 2026-05-25 5:40 UTC (permalink / raw)
To: Eric Biggers
Cc: Thara Gopinath, Herbert Xu, David S. Miller, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
Vinod Koul, Frank Li, Andy Gross, linux-arm-msm, linux-crypto,
devicetree, linux-kernel, dmaengine, Bartosz Golaszewski,
Bartosz Golaszewski, Gaurav Kashyap, Neeraj Soni
In-Reply-To: <20260522024912.GC5937@quark>
> It sounds like you don't actually have an answer to my questions, then.
>
> Performance tests (e.g.
> https://lore.kernel.org/r/20250615031807.GA81869@sol/) have clearly
> shown that this driver is an order of magnitude slower than the CPU.
>
> This driver has historically been quite harmful. People were using it
> accidentally and encountering very bad performance, as well as bugs such
> as crashes and filesystem hangs. We fixed that by lowering its
> cra_priority. But for the same reason, even when enabled on a platform,
> it's not actually used. Linux would be better without this driver.
>
+Bartosz, Gaurav, Neeraj
Hi Eric,
GPCE is relevant in terms of providing hardware security.
There are multiple usecases coming up for example to handle DRM/secure
buffer usecases to improve overall throughput for secure content.
Regarding performance, it's currently slower compared to arm CE but
provides an edge by giving hardware security which is considered more
secure.
Btw, there's been performance improvement with new targets and we are
expecting to achieve far more better performance with new SoCs family.
Pakala: GPCE - 550MBps, ARMv8 - 8GBps
Kaanapali: GPCE - 3GBps, ARMv8 - 10GBps
Please note, there's almost 5x improvement in kaanapali compared to
pakala. Though overall is still slower compared to arm but as mentioned,
expecting better performance with hardware improvements as we progress.
Also, currently qce driver exhibit stability issues and that's what we
are putting effort in stabilizing the software on immediate basis.
There's parallel effort ongoing by Bartosz to introduce baseline for
secure buffer usecases.
https://lore.kernel.org/lkml/20260522-qcom-qce-cmd-descr-v18-0-99103926bafc@oss.qualcomm.com/
There's active development ongoing and i believe lowering cra_priority
for qce is fine as of now and can scale values once qce becomes
performance efficient.
Please share your thoughts. Thanks!
--
Regards
Kuldeep
^ permalink raw reply
* Re: [PATCH V2 1/3] dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Add Hawi UFS PHY compatible
From: Manivannan Sadhasivam @ 2026-05-25 5:46 UTC (permalink / raw)
To: palash.kambar
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, alim.akhtar,
bvanassche, andersson, dmitry.baryshkov, abel.vesa, luca.weiss,
linux-arm-msm, linux-phy, devicetree, linux-kernel, linux-scsi
In-Reply-To: <20260522172716.820490-2-palash.kambar@oss.qualcomm.com>
On Fri, May 22, 2026 at 10:57:14PM +0530, palash.kambar@oss.qualcomm.com wrote:
> From: Palash Kambar <palash.kambar@oss.qualcomm.com>
>
> Document QMP UFS PHY compatible for Hawi SoC.
>
> Signed-off-by: Palash Kambar <palash.kambar@oss.qualcomm.com>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
- Mani
> ---
> .../devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
> index 9616c736b6d4..b75015f3ea70 100644
> --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
> +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
> @@ -37,6 +37,7 @@ properties:
> - qcom,kaanapali-qmp-ufs-phy
> - const: qcom,sm8750-qmp-ufs-phy
> - enum:
> + - qcom,hawi-qmp-ufs-phy
> - qcom,milos-qmp-ufs-phy
> - qcom,msm8996-qmp-ufs-phy
> - qcom,msm8998-qmp-ufs-phy
> @@ -107,6 +108,7 @@ allOf:
> compatible:
> contains:
> enum:
> + - qcom,hawi-qmp-ufs-phy
> - qcom,milos-qmp-ufs-phy
> - qcom,msm8998-qmp-ufs-phy
> - qcom,sa8775p-qmp-ufs-phy
> --
> 2.34.1
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH V2 2/3] scsi: ufs: qcom :dt-bindings: Document the Hawi UFS controller
From: Manivannan Sadhasivam @ 2026-05-25 5:46 UTC (permalink / raw)
To: palash.kambar
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, alim.akhtar,
bvanassche, andersson, dmitry.baryshkov, abel.vesa, luca.weiss,
linux-arm-msm, linux-phy, devicetree, linux-kernel, linux-scsi
In-Reply-To: <20260522172716.820490-3-palash.kambar@oss.qualcomm.com>
On Fri, May 22, 2026 at 10:57:15PM +0530, palash.kambar@oss.qualcomm.com wrote:
> From: Palash Kambar <palash.kambar@oss.qualcomm.com>
>
> Document the UFS Controller on the Hawi Platform.
>
> Signed-off-by: Palash Kambar <palash.kambar@oss.qualcomm.com>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
- Mani
> ---
> Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
> index f28641c6e68f..3de00affa4c6 100644
> --- a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
> +++ b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
> @@ -16,6 +16,7 @@ select:
> contains:
> enum:
> - qcom,eliza-ufshc
> + - qcom,hawi-ufshc
> - qcom,kaanapali-ufshc
> - qcom,sm8650-ufshc
> - qcom,sm8750-ufshc
> @@ -27,6 +28,7 @@ properties:
> items:
> - enum:
> - qcom,eliza-ufshc
> + - qcom,hawi-ufshc
> - qcom,kaanapali-ufshc
> - qcom,sm8650-ufshc
> - qcom,sm8750-ufshc
> --
> 2.34.1
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox