* Re: [PATCH v1] arm64: dts: qcom: sm8750-mtp: Set sufficient voltage for panel nt37801
From: Bjorn Andersson @ 2026-06-17 17:41 UTC (permalink / raw)
To: Ayushi Makhija
Cc: konrad.dybcio, robh+dt, krzysztof.kozlowski+dt, conor+dt,
dmitry.baryshkov, linux-arm-msm, devicetree, linux-kernel,
linux-arm-kernel, quic_rajeevny, quic_vproddut, Dmitry Baryshkov,
Konrad Dybcio
In-Reply-To: <20260409122110.214680-1-ayushi.makhija@oss.qualcomm.com>
On Thu, Apr 09, 2026 at 05:51:09PM +0530, Ayushi Makhija wrote:
Here's a link to "v1":
https://lore.kernel.org/all/20260323102229.1546504-1-quic_amakhija@quicinc.com/
Which makes this v2.
You also ignored my feedback.
Thank you,
Bjorn
> The NT37801 Sepc V1.0 chapter "5.7.1 Power On Sequence" states
> VDDI=1.65V~1.95V, so set sufficient voltage for panel nt37801.
>
> Fixes: 4fca6849864d ("drm/panel: Add Novatek NT37801 panel driver")
> Signed-off-by: Ayushi Makhija <ayushi.makhija@oss.qualcomm.com>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---
> arch/arm64/boot/dts/qcom/sm8750-mtp.dts | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm64/boot/dts/qcom/sm8750-mtp.dts b/arch/arm64/boot/dts/qcom/sm8750-mtp.dts
> index 3837f6785320..6ba4e69bf377 100644
> --- a/arch/arm64/boot/dts/qcom/sm8750-mtp.dts
> +++ b/arch/arm64/boot/dts/qcom/sm8750-mtp.dts
> @@ -462,7 +462,7 @@ vreg_l11b_1p0: ldo11 {
>
> vreg_l12b_1p8: ldo12 {
> regulator-name = "vreg_l12b_1p8";
> - regulator-min-microvolt = <1200000>;
> + regulator-min-microvolt = <1650000>;
> regulator-max-microvolt = <1800000>;
> regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
> regulator-allow-set-load;
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH RFC v4 01/12] dt-bindings: clk: zte: Add zx297520v3 top clock and reset bindings
From: Stefan Dösinger @ 2026-06-17 17:47 UTC (permalink / raw)
To: Conor Dooley
Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Brian Masney, linux-clk, devicetree,
linux-kernel, linux-arm-kernel
In-Reply-To: <20260617-tabasco-pulsate-454ed3f8f0b6@spud>
[-- Attachment #1: Type: text/plain, Size: 1942 bytes --]
Am Mittwoch, 17. Juni 2026, 19:08:02 Ostafrikanische Zeit schrieb Conor
Dooley:
> On Tue, Jun 16, 2026 at 11:26:21PM +0300, Stefan Dösinger wrote:
> > +F: Documentation/devicetree/zte,zx297520v3-*
>
> Sashiko complaint here looks valid.
Yes, it is valid.
It also brought up another concern in patch 12 that has an impact on this
binding:
Am Dienstag, 16. Juni 2026, 23:42:19 Ostafrikanische Zeit schrieb sashiko-
bot@kernel.org:
> issue(s) to consider: - [Low] The `syscon-reboot` node is incorrectly
> defined at the root level using the explicitly deprecated `regmap`
> property. --
And indeed "regmap" is deprecated and I missed it somehow. As far as I
understand, to put syscon-reboot as a child of the clock, I need to add the
simple-mfd .compatible:
Either
topclk: clock-controller@13b000 {
compatible = "zte,zx297520v3-topclk", "syscon", "simple-mfd";
reg = <0x0013b000 0x400>;
...
syscon-reboot {
compatible = "syscon-reboot";
regmap = <&topclk>;
offset = <0x0>;
mask = <0x1>;
};
};
--- or ---
something@13b000 {
compatible = "zte,zx297520v3-topcrm", "syscon", "simple-mfd";
reg = <0x0013b000 0x400>;
ranges;
topclk: clock-controller@0 {
compatible = "zte,zx297520v3-topclk";
...
}
syscon-reboot {
compatible = "syscon-reboot";
offset = <0x0>;
mask = <0x1>;
};
};
I see both ways in existing DTS files (e.g. imx6sl.dtsi for the fomer,
uniphier-pro4.dtsi for the latter). Is there a preferred way? I have a mild
preference for the first, as it would keep all 3 clocks in the same way. If I
go for the second, the clock driver would have to query its own node and the
parent node for the regmap.
AFAIU unrelated to syscon-reboot option 2 would give me the opportunity to
have separate clock and reset nodes and bindings and skip the aux bus, but
this would not be a correct representation of how the hardware works.
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply
* [PATCH v9 0/5] iio: adc: add Versal SysMon driver
From: Salih Erim @ 2026-06-17 18:01 UTC (permalink / raw)
To: jic23, andy
Cc: dlechner, nuno.sa, robh, krzk+dt, conor+dt, conall.ogriofa,
michal.simek, linux, erimsalih, linux-iio, devicetree,
linux-kernel, Salih Erim
This series adds a new IIO driver for the AMD/Xilinx Versal System
Monitor (SysMon), providing on-chip voltage and temperature monitoring.
The Versal SysMon measures up to 160 supply voltages and reads up to
64 temperature satellites distributed across the SoC. The hardware
also provides aggregated device temperature registers: the current
max and min across all active satellites, and peak/trough values
recorded since last hardware reset. The device can be accessed via
memory-mapped I/O or via an I2C interface.
The driver is split into a bus-agnostic core module using the regmap
API, an MMIO platform driver, and an I2C driver. This allows the
same IIO logic to be shared across different bus transports.
Previous submissions:
v8: https://lore.kernel.org/all/20260616131559.3029543-1-salih.erim@amd.com/
v7: https://lore.kernel.org/all/20260614233722.2603459-1-salih.erim@amd.com/
v6: https://lore.kernel.org/all/20260611222738.2035062-1-salih.erim@amd.com/
v5: https://lore.kernel.org/all/20260608183801.1257051-1-salih.erim@amd.com/
v4: https://lore.kernel.org/all/20260606051707.535281-1-salih.erim@amd.com/
v3: https://lore.kernel.org/all/20260527114211.174288-1-salih.erim@amd.com/
v2: https://lore.kernel.org/all/20260502111951.538488-1-salih.erim@amd.com/
v1: https://lore.kernel.org/all/cover.1757061697.git.michal.simek@amd.com/
Changes in v9:
- Address Andy v8 review items: symbol namespace, function
signature joins, minmax.h, variable naming consistency,
num_static move to P2 (Andy)
Changes in v8:
- Address Andy v7 review items: MILLIDEGREE_PER_DEGREE, overflow.h,
devm_versal_sysmon_core_probe rename, volatile register comment,
regmap_test_bits, unsigned int for osr_write, ~0 instead of
~0U (Andy)
- Fix devm teardown race in interrupt cleanup (Sashiko)
Changes in v7:
- Move TEMP event onto channel 0; drop OT as separate IIO
channel; use single temp_channels array with runtime event
attachment (Jonathan)
- Return administrative state from temp_mask in read_event_config
instead of transient hardware IMR (Jonathan, Sashiko)
- Add devm cleanup to mask HW interrupts on driver unbind (Sashiko)
- Add documentation comment for static aggregate temp channels
(Jonathan)
- Split sysmon_osr_write into per-type helpers (Jonathan)
Changes in v6:
- Address all Andy v5 review items: switch(chan->type) in event
functions, reversed xmas tree ordering, macro brace placement,
scoped_guard in unmask worker, combined regmap error checks,
join single-line constructs, IWYU header fixes (Andy)
- Fix fwnode_irq_get() to propagate only -EPROBE_DEFER; treating
all negatives as fatal broke probe on I2C nodes without
interrupts property
Changes in v5:
- Core: add err.h include (IWYU) (Andy)
- Core: drop (int) cast on MILLI in scale assignment (Andy)
- Core: sign_extend32() instead of (s16) cast (Andy)
- Core: remove unneeded parentheses in voltage address
calculation (Andy)
- Core: drop NULL checks before fwnode_get_child_node_count
(NULL-aware) (Andy)
- Core: nested size_add() for overflow-safe allocation (Andy)
- Core: if (ret) instead of if (ret < 0) for fwnode property
reads (Andy)
- Core: remove outer parentheses in satellite address
calculation (Andy)
- Core: loop index declared in for() scope (Andy)
- MMIO: add err.h, types.h includes (IWYU) (Andy)
- Header: remove unused types.h include and struct iio_dev
forward declaration at P2 stage (Andy)
- I2C: add err.h, mod_devicetable.h includes (IWYU) (Andy)
- Events: clamp() instead of clamp_t() (Andy)
- Events: regmap_assign_bits() instead of separate set/clear (Andy)
- Events: remove unneeded parentheses (2 places) (Andy)
- Events: for_each_set_bit on single line (Andy)
- Events: regmap_clear_bits() instead of regmap_update_bits() (Andy)
- Events: simplify unmask XOR to ~status & masked_temp (Andy)
- Events: add comment explaining unmask &= ~temp_mask logic (Andy)
- Events: split container_of across two lines (Andy)
- Events: move ISR write after !isr check (Andy)
- Events: unsigned int for init_hysteresis address param (Andy)
- Events: add comment explaining error check policy in
worker/IRQ (Andy)
- Events: nested size_add() for overflow-safe allocation (Andy)
- Events: propagate negative from fwnode_irq_get() for
EPROBE_DEFER (Andy)
- Events: pass irq instead of has_irq to sysmon_parse_fw (Andy)
- Oversampling: remove unneeded parentheses (Andy)
- Oversampling: use struct regmap *map local variable (Andy)
- Oversampling: switch instead of redundant if/if on
channel_type (Andy)
- Oversampling: add CONFIG register readback fence after
oversampling update to prevent NoC bus hang from posted
writes (found during hardware stress testing)
Changes in v4:
- Core: temperature channels use RAW + SCALE (IIO_VAL_FRACTIONAL,
1000/128) instead of PROCESSED; voltage channels use PROCESSED
only, drop RAW; drop scan_type from all channel macros (Jonathan)
- Core: move __free(fwnode_handle) declarations down to just
above use; devm_regmap_init() on one line; lock comment
describes RMW sequences and cached state (Jonathan)
- Events: merge event channels into static temp array -- two
arrays (with/without events) selected by has_irq; event-only
channels have no info_mask (Jonathan)
- Events: blank lines, fit under 80 chars, default returns error,
return early in each case, guard(spinlock) in IRQ handler
(Jonathan)
- Events: take irq_lock in write_event_config for temp_mask
updates (Sashiko)
- I2C: replace enum with defines, use unaligned accessors for
data and register offset packing, named initializer in
i2c_device_id (Jonathan)
- I2C: drop bitfield.h, add unaligned.h
- Oversampling: return directly, remove else after early returns,
rename mask defines, blank lines (Jonathan)
- Oversampling: move oversampling read inside guard(mutex) scope
- Fix v2 lore link in cover letter
Changes in v3:
- DT binding: single compatible, voltage-channels rename, single
quotes, drop label/bipolar/xlnx,aie-temp (Krzysztof)
- Core: IWYU throughout, __free(fwnode_handle), sign_extend32(),
size_add(), dev_err_probe(), s16 param, remove (int) casts,
drop SYSMON_MILLI in favor of (int)MILLI, rename _ext to _name
in SYSMON_CHAN_TEMP macro (Andy, Jonathan)
- Core: fwnode_irq_get() moved to core_probe, remove sysmon->dev/
indio_dev/irq from struct, describe protected data in lock
comment, add RAW+PROCESSED comment (Jonathan)
- I2C: IWYU, remove wrapper struct, explicit enum values, sizeof()
for buffers, = { } initializers, adapt to core_probe interface
change (Andy, Krzysztof)
- Events: IWYU, FIELD_GET/FIELD_PREP, regmap_set/clear_bits,
clamp_t, !!, IRQ_RETVAL(), devm_delayed_work_autocancel,
loop var scope, error checks, remove redundant else, logical
param splits, spinlock safety comment (Andy)
- Events: hysteresis rework -- store as millicelsius, hardcode
ALARM_CONFIG to hysteresis mode, compute lower threshold from
(upper - hysteresis), remove falling threshold for temperature,
single event spec per channel with IIO_EV_DIR_RISING, push
IIO_EV_DIR_RISING for temp and IIO_EV_DIR_EITHER for voltage
(Jonathan)
Tested on VCK190 (single SLR, MMIO path, 7 supplies, 10 temperature
satellites). I2C compile-tested.
A follow-up series will add thermal zone integration, secure firmware
access, and I2C remote monitoring.
Salih Erim (5):
dt-bindings: iio: adc: add xlnx,versal-sysmon binding
iio: adc: add Versal SysMon driver
iio: adc: versal-sysmon: add I2C driver
iio: adc: versal-sysmon: add threshold event support
iio: adc: versal-sysmon: add oversampling support
.../bindings/iio/adc/xlnx,versal-sysmon.yaml | 154 +++
MAINTAINERS | 7 +
drivers/iio/adc/Kconfig | 33 +
drivers/iio/adc/Makefile | 3 +
drivers/iio/adc/versal-sysmon-core.c | 1034 +++++++++++++++++
drivers/iio/adc/versal-sysmon-i2c.c | 134 +++
drivers/iio/adc/versal-sysmon.c | 92 ++
drivers/iio/adc/versal-sysmon.h | 120 ++
8 files changed, 1577 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml
create mode 100644 drivers/iio/adc/versal-sysmon-core.c
create mode 100644 drivers/iio/adc/versal-sysmon-i2c.c
create mode 100644 drivers/iio/adc/versal-sysmon.c
create mode 100644 drivers/iio/adc/versal-sysmon.h
--
2.48.1
^ permalink raw reply
* [PATCH v9 2/5] iio: adc: add Versal SysMon driver
From: Salih Erim @ 2026-06-17 18:01 UTC (permalink / raw)
To: jic23, andy
Cc: dlechner, nuno.sa, robh, krzk+dt, conor+dt, conall.ogriofa,
michal.simek, linux, erimsalih, linux-iio, devicetree,
linux-kernel, Salih Erim, Andy Shevchenko
In-Reply-To: <20260617180147.3370346-1-salih.erim@amd.com>
Add the core driver and MMIO platform driver for the AMD/Xilinx Versal
System Monitor (SysMon) block.
The SysMon block resides in the platform management controller (PMC) and
provides on-chip voltage and temperature monitoring through a 10-bit,
200 kSPS ADC. It can monitor up to 160 voltage channels and 64
temperature satellites distributed across the SoC, with a consistent
sample rate of 8 kSPS per channel regardless of how many channels are
enabled.
The hardware also provides four aggregate temperature registers that
are always present regardless of the device tree configuration: the
current max and min across all active satellites, and the peak and
trough values recorded since the last hardware reset.
The driver is split into two compilation units:
- versal-sysmon-core: Channel parsing, IIO registration, read_raw
- versal-sysmon: MMIO platform driver with custom regmap accessors
Voltage results are stored in a 19-bit modified floating-point format
and converted to millivolts. Temperature results are stored in Q8.7
signed fixed-point Celsius format and converted to millicelsius.
The MMIO regmap backend uses a custom reg_write accessor that
automatically unlocks the NPI (NoC programming interface) lock
register before each write, as required by the hardware. The regmap
is configured with fast_io since the underlying MMIO accessors are
safe to call from atomic context.
Co-developed-by: Michal Simek <michal.simek@amd.com>
Signed-off-by: Michal Simek <michal.simek@amd.com>
Signed-off-by: Salih Erim <salih.erim@amd.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
---
Changes in v9:
- Add Reviewed-by tag from Andy Shevchenko
- Use EXPORT_SYMBOL_NS_GPL with "VERSAL_SYSMON" namespace,
add MODULE_IMPORT_NS to MMIO driver (Andy)
- Join sysmon_mmio_reg_read and sysmon_mmio_reg_write
signatures to single lines (Andy)
- Introduce num_static variable in sysmon_parse_fw to reduce
churn in P4 (Andy)
Changes in v8:
- Use MILLIDEGREE_PER_DEGREE instead of MILLI for temperature
scale (Andy)
- Add overflow.h include for size_add() (Andy)
- Rename sysmon_core_probe to devm_versal_sysmon_core_probe (Andy)
Changes in v7:
- Add documentation comment explaining the four static aggregate
temperature registers (Jonathan)
Changes in v6:
- Macro brace on separate line for SYSMON_CHAN_TEMP (Andy)
- Remove unneeded = 0 init on num_supply, num_temp (Andy)
- Use temporary variable for channel count allocation (Andy)
- Initialize idx directly after memcpy (Andy)
- Join info_mask_separate on one line (Andy)
- Join info_mask_shared_by_type on one line (Andy)
- Join devm_kcalloc on one line (Andy)
- Reversed xmas tree ordering in sysmon_parse_fw
Changes in v5:
- Add err.h include to core (IWYU) (Andy)
- Drop (int) cast on MILLI in scale assignment (Andy)
- sign_extend32() instead of (s16) cast for temperature raw (Andy)
- Remove unneeded parentheses in voltage address calculation (Andy)
- Drop NULL checks before fwnode_get_child_node_count (Andy)
- Nested size_add() for overflow-safe allocation (Andy)
- if (ret) instead of if (ret < 0) for fwnode property reads (Andy)
- Remove outer parentheses in satellite address calculation (Andy)
- Loop index declared in for() scope (Andy)
- MMIO: add err.h, types.h includes (IWYU) (Andy)
- Header: remove unused types.h include and struct iio_dev
forward declaration (Andy)
Changes in v4:
- Temperature: RAW + SCALE (IIO_VAL_FRACTIONAL, 1000/128) instead
of PROCESSED (Jonathan)
- Voltage: PROCESSED only, drop RAW (Jonathan)
- Drop scan_type from all channel macros (Jonathan)
- Move __free(fwnode_handle) declarations down to just above use
(Jonathan)
- devm_regmap_init() on one line (Jonathan)
- Lock comment: describe RMW sequences and cached state (Jonathan)
- Remove sysmon_q8p7_to_millicelsius() from this patch; the function
is now introduced in P4 where it is first used
Changes in v3:
- IWYU: add array_size.h, string.h, types.h to core; audit and
fix header and MMIO driver includes (Andy)
- Rename _ext to _name in SYSMON_CHAN_TEMP macro parameter (Andy,
Jonathan)
- Use .info_mask_separate = BIT() style in SYSMON_CHAN_TEMP (Andy)
- Use s16 parameter in sysmon_q8p7_to_millicelsius (Andy)
- Use sign_extend32() in sysmon_supply_rawtoprocessed (Andy)
- Split sysmon_read_raw parameters logically across lines (Andy)
- Remove redundant (int) casts on regval (Andy)
- Split num_supply/num_temp initialization (Andy)
- Use __free(fwnode_handle) cleanup, remove goto err_put (Andy)
- Use size_add() for overflow-safe allocation (Andy)
- Use dev_err_probe() in sysmon_parse_fw error paths (Jonathan)
- Move fwnode_irq_get() to core_probe, remove irq parameter
from bus driver interfaces (Jonathan)
- Use (int)MILLI at call sites, drop SYSMON_MILLI define (Andy,
Jonathan)
- Remove sysmon->dev, sysmon->indio_dev, sysmon->irq from struct;
pass as local variables or use regmap_get_device() (Jonathan)
- Use struct device *dev local in sysmon_platform_probe (Andy)
- Describe protected data in lock comment (Jonathan)
- Add comment explaining RAW+PROCESSED co-exposure (Jonathan)
Changes in v2:
- Split into core (versal-sysmon-core.c) + MMIO platform driver
(versal-sysmon.c) + shared header (versal-sysmon.h)
- Uses regmap API instead of direct readl/writel
- MMIO regmap uses custom callbacks with NPI unlock in write path
- Reverse Christmas Tree variable ordering throughout
- Header include order fixed
- MAINTAINERS entry folded in with wildcard F: pattern
- Kconfig: hidden VERSAL_SYSMON_CORE + VERSAL_SYSMON selects it
- Kconfig/Makefile: alphabetical ordering (VERSAL before VF610)
- Bounds validation on DT reg values
- Named constants replace magic numbers (SYSMON_REG_STRIDE,
SYSMON_SUPPLY_MANTISSA_BITS, SYSMON_MILLI)
- kernel-doc for exported sysmon_core_probe() and sysmon_parse_fw()
- Supply voltage conversion uses proper two's complement sign
extension (s16 cast) matching the hardware specification
- Register offsets sorted by address in header
- Each patch introduces only the defines, fields, and includes
it uses (no dead code in any commit)
- Removed unused linux/limits.h and linux/units.h includes
- Renamed iio_dev_info to sysmon_iio_info
- regmap_write return values checked in probe init path
MAINTAINERS | 7 +
drivers/iio/adc/Kconfig | 20 ++
drivers/iio/adc/Makefile | 2 +
drivers/iio/adc/versal-sysmon-core.c | 292 +++++++++++++++++++++++++++
drivers/iio/adc/versal-sysmon.c | 92 +++++++++
drivers/iio/adc/versal-sysmon.h | 67 ++++++
6 files changed, 480 insertions(+)
create mode 100644 drivers/iio/adc/versal-sysmon-core.c
create mode 100644 drivers/iio/adc/versal-sysmon.c
create mode 100644 drivers/iio/adc/versal-sysmon.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 2fb1c75afd1..46762c8496d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -29216,6 +29216,13 @@ F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-net-ddrmc5.y
F: drivers/edac/versalnet_edac.c
F: include/linux/cdx/edac_cdx_pcol.h
+XILINX VERSAL SYSMON DRIVER
+M: Salih Erim <salih.erim@amd.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml
+F: drivers/iio/adc/versal-sysmon*
+
XILINX WATCHDOG DRIVER
M: Srinivas Neeli <srinivas.neeli@amd.com>
R: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index a9dedbb8eb4..c7f19057484 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1943,6 +1943,26 @@ config TWL6030_GPADC
This driver can also be built as a module. If so, the module will be
called twl6030-gpadc.
+config VERSAL_SYSMON_CORE
+ tristate
+ select REGMAP
+
+config VERSAL_SYSMON
+ tristate "AMD Versal SysMon driver"
+ depends on ARCH_ZYNQMP || COMPILE_TEST
+ depends on HAS_IOMEM
+ select VERSAL_SYSMON_CORE
+ help
+ Say yes here to have support for the AMD/Xilinx Versal System
+ Monitor (SysMon). This driver provides voltage and temperature
+ monitoring through the IIO subsystem.
+
+ The SysMon measures up to 160 supply voltages and reads up to
+ 64 temperature satellites distributed across the SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called versal-sysmon.
+
config VF610_ADC
tristate "Freescale vf610 ADC driver"
depends on HAS_IOMEM
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 097357d146b..d7696b1b157 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -167,6 +167,8 @@ obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
+obj-$(CONFIG_VERSAL_SYSMON_CORE) += versal-sysmon-core.o
+obj-$(CONFIG_VERSAL_SYSMON) += versal-sysmon.o
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o
diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c
new file mode 100644
index 00000000000..e0b9b3d2b6d
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon-core.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Versal SysMon core driver
+ *
+ * Copyright (C) 2019 - 2022, Xilinx, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+
+#include "versal-sysmon.h"
+
+#define SYSMON_CHAN_TEMP(_chan, _address, _name) \
+{ \
+ .type = IIO_TEMP, \
+ .indexed = 1, \
+ .address = _address, \
+ .channel = _chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = _name, \
+}
+
+/*
+ * Static temperature channels (always present).
+ *
+ * These are hardware-computed aggregate registers across all active
+ * temperature satellites:
+ * temp: current max temperature across all active satellites
+ * min: current min temperature across all active satellites
+ * max_max: highest peak recorded since last hardware reset
+ * min_min: lowest trough recorded since last hardware reset
+ */
+static const struct iio_chan_spec temp_channels[] = {
+ SYSMON_CHAN_TEMP(0, SYSMON_TEMP_MAX, "temp"),
+ SYSMON_CHAN_TEMP(1, SYSMON_TEMP_MIN, "min"),
+ SYSMON_CHAN_TEMP(2, SYSMON_TEMP_MAX_MAX, "max_max"),
+ SYSMON_CHAN_TEMP(3, SYSMON_TEMP_MIN_MIN, "min_min"),
+};
+
+static void sysmon_supply_rawtoprocessed(int raw_data, int *val)
+{
+ int mantissa, format, exponent;
+
+ mantissa = FIELD_GET(SYSMON_MANTISSA_MASK, raw_data);
+ exponent = SYSMON_SUPPLY_MANTISSA_BITS - FIELD_GET(SYSMON_MODE_MASK, raw_data);
+ format = FIELD_GET(SYSMON_FMT_MASK, raw_data);
+ /*
+ * When format bit is set the mantissa is two's complement
+ * (per hardware spec); sign-extend to int for correct arithmetic.
+ */
+ if (format)
+ mantissa = sign_extend32(mantissa, 15);
+
+ *val = (mantissa * (int)MILLI) >> exponent;
+}
+
+static int sysmon_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int regval;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ if (mask == IIO_CHAN_INFO_SCALE) {
+ /* Q8.7 to millicelsius: raw * 1000 / 128 */
+ *val = MILLIDEGREE_PER_DEGREE;
+ *val2 = BIT(SYSMON_FRACTIONAL_SHIFT);
+ return IIO_VAL_FRACTIONAL;
+ }
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ ret = regmap_read(sysmon->regmap, chan->address, ®val);
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(regval, 15);
+ return IIO_VAL_INT;
+
+ case IIO_VOLTAGE:
+ if (mask != IIO_CHAN_INFO_PROCESSED)
+ return -EINVAL;
+
+ ret = regmap_read(sysmon->regmap,
+ chan->address * SYSMON_REG_STRIDE +
+ SYSMON_SUPPLY_BASE, ®val);
+ if (ret)
+ return ret;
+
+ sysmon_supply_rawtoprocessed(regval, val);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sysmon_read_label(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ char *label)
+{
+ if (chan->datasheet_name)
+ return sysfs_emit(label, "%s\n", chan->datasheet_name);
+
+ return -EINVAL;
+}
+
+static const struct iio_info sysmon_iio_info = {
+ .read_raw = sysmon_read_raw,
+ .read_label = sysmon_read_label,
+};
+
+/**
+ * sysmon_parse_fw() - Parse firmware nodes and configure IIO channels.
+ * @indio_dev: IIO device instance
+ * @dev: Parent device
+ *
+ * Reads voltage-channels and temperature-channels container nodes from
+ * firmware and builds the IIO channel array. Static temperature channels
+ * are prepended, followed by supply and satellite channels from DT.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev)
+{
+ unsigned int num_chan, num_static, idx, temp_chan_idx, volt_chan_idx;
+ unsigned int num_supply, num_temp;
+ struct iio_chan_spec *sysmon_channels;
+ const char *label;
+ u32 reg;
+ int ret;
+
+ struct fwnode_handle *supply_node __free(fwnode_handle) =
+ device_get_named_child_node(dev, "voltage-channels");
+ num_supply = fwnode_get_child_node_count(supply_node);
+
+ struct fwnode_handle *temp_node __free(fwnode_handle) =
+ device_get_named_child_node(dev, "temperature-channels");
+ num_temp = fwnode_get_child_node_count(temp_node);
+
+ num_static = ARRAY_SIZE(temp_channels);
+ num_chan = size_add(num_temp, size_add(num_static, num_supply));
+ sysmon_channels = devm_kcalloc(dev, num_chan, sizeof(*sysmon_channels), GFP_KERNEL);
+ if (!sysmon_channels)
+ return -ENOMEM;
+
+ /* Static temperature channels first */
+ memcpy(sysmon_channels, temp_channels, sizeof(temp_channels));
+ idx = num_static;
+
+ /* Supply channels from DT */
+ fwnode_for_each_child_node_scoped(supply_node, child) {
+ ret = fwnode_property_read_u32(child, "reg", ®);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing reg for supply channel\n");
+
+ if (reg > SYSMON_SUPPLY_IDX_MAX)
+ return dev_err_probe(dev, -EINVAL,
+ "supply reg %u exceeds max %u\n",
+ reg, SYSMON_SUPPLY_IDX_MAX);
+
+ ret = fwnode_property_read_string(child, "label", &label);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing label for supply channel\n");
+
+ sysmon_channels[idx++] = (struct iio_chan_spec) {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .address = reg,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .datasheet_name = label,
+ };
+ }
+
+ /* Temperature satellite channels from DT */
+ fwnode_for_each_child_node_scoped(temp_node, child) {
+ ret = fwnode_property_read_u32(child, "reg", ®);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing reg for temp channel\n");
+
+ if (reg < 1 || reg > SYSMON_TEMP_SAT_MAX)
+ return dev_err_probe(dev, -EINVAL,
+ "temp reg %u out of range [1..%u]\n",
+ reg, SYSMON_TEMP_SAT_MAX);
+
+ ret = fwnode_property_read_string(child, "label", &label);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing label for temp channel\n");
+
+ sysmon_channels[idx++] = (struct iio_chan_spec) {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .address = SYSMON_TEMP_SAT_BASE +
+ (reg - 1) * SYSMON_REG_STRIDE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .datasheet_name = label,
+ };
+ }
+
+ indio_dev->num_channels = idx;
+ indio_dev->info = &sysmon_iio_info;
+
+ /*
+ * Assign per-type sequential channel numbers.
+ * IIO sysfs uses type prefix (in_tempN, in_voltageN)
+ * so numbers only need to be unique within each type.
+ */
+ temp_chan_idx = 0;
+ volt_chan_idx = 0;
+ for (unsigned int idx = 0; idx < indio_dev->num_channels; idx++) {
+ if (sysmon_channels[idx].type == IIO_TEMP)
+ sysmon_channels[idx].channel = temp_chan_idx++;
+ else
+ sysmon_channels[idx].channel = volt_chan_idx++;
+ }
+
+ indio_dev->channels = sysmon_channels;
+
+ return 0;
+}
+
+/**
+ * devm_versal_sysmon_core_probe() - Initialize Versal SysMon core
+ * @dev: Parent device
+ * @regmap: Register map for hardware access
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap)
+{
+ struct iio_dev *indio_dev;
+ struct sysmon *sysmon;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*sysmon));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ sysmon = iio_priv(indio_dev);
+ sysmon->regmap = regmap;
+
+ ret = devm_mutex_init(dev, &sysmon->lock);
+ if (ret)
+ return ret;
+
+ /* Disable all interrupts and clear pending status */
+ ret = regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK);
+ if (ret)
+ return ret;
+ ret = regmap_write(sysmon->regmap, SYSMON_ISR, SYSMON_INTR_ALL_MASK);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "versal-sysmon";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = sysmon_parse_fw(indio_dev, dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_NS_GPL(devm_versal_sysmon_core_probe, "VERSAL_SYSMON");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Versal SysMon Core Driver");
+MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>");
diff --git a/drivers/iio/adc/versal-sysmon.c b/drivers/iio/adc/versal-sysmon.c
new file mode 100644
index 00000000000..529d0486c9f
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Versal SysMon MMIO platform driver
+ *
+ * Copyright (C) 2019 - 2022, Xilinx, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "versal-sysmon.h"
+
+struct sysmon_mmio {
+ void __iomem *base;
+};
+
+static int sysmon_mmio_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct sysmon_mmio *mmio = context;
+
+ *val = readl(mmio->base + reg);
+ return 0;
+}
+
+static int sysmon_mmio_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct sysmon_mmio *mmio = context;
+
+ /* NPI must be unlocked before any register write except to NPI_LOCK */
+ if (reg != SYSMON_NPI_LOCK)
+ writel(SYSMON_NPI_UNLOCK_CODE, mmio->base + SYSMON_NPI_LOCK);
+ writel(val, mmio->base + reg);
+
+ return 0;
+}
+
+static const struct regmap_config sysmon_mmio_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = SYSMON_REG_STRIDE,
+ .max_register = SYSMON_MAX_REG,
+ .reg_read = sysmon_mmio_reg_read,
+ .reg_write = sysmon_mmio_reg_write,
+ .fast_io = true,
+};
+
+static int sysmon_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sysmon_mmio *mmio;
+ struct regmap *regmap;
+
+ mmio = devm_kzalloc(dev, sizeof(*mmio), GFP_KERNEL);
+ if (!mmio)
+ return -ENOMEM;
+
+ mmio->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mmio->base))
+ return PTR_ERR(mmio->base);
+
+ regmap = devm_regmap_init(dev, NULL, mmio, &sysmon_mmio_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return devm_versal_sysmon_core_probe(dev, regmap);
+}
+
+static const struct of_device_id sysmon_of_match_table[] = {
+ { .compatible = "xlnx,versal-sysmon" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sysmon_of_match_table);
+
+static struct platform_driver sysmon_platform_driver = {
+ .probe = sysmon_platform_probe,
+ .driver = {
+ .name = "versal-sysmon",
+ .of_match_table = sysmon_of_match_table,
+ },
+};
+module_platform_driver(sysmon_platform_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Versal SysMon Platform Driver");
+MODULE_IMPORT_NS("VERSAL_SYSMON");
+MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>");
diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h
new file mode 100644
index 00000000000..e27a5357575
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * AMD Versal SysMon driver
+ *
+ * Copyright (C) 2019 - 2022, Xilinx, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _VERSAL_SYSMON_H_
+#define _VERSAL_SYSMON_H_
+
+#include <linux/bits.h>
+#include <linux/mutex.h>
+
+struct device;
+struct regmap;
+
+/* Register offsets (sorted by address) */
+#define SYSMON_NPI_LOCK 0x000C
+#define SYSMON_ISR 0x0044
+#define SYSMON_IDR 0x0050
+#define SYSMON_TEMP_MAX 0x1030
+#define SYSMON_TEMP_MIN 0x1034
+#define SYSMON_SUPPLY_BASE 0x1040
+#define SYSMON_TEMP_MIN_MIN 0x1F8C
+#define SYSMON_TEMP_MAX_MAX 0x1F90
+#define SYSMON_TEMP_SAT_BASE 0x1FAC
+#define SYSMON_MAX_REG 0x24C0
+
+/* NPI unlock value written to SYSMON_NPI_LOCK */
+#define SYSMON_NPI_UNLOCK_CODE 0xF9E8D7C6
+
+/* Register stride: 4 bytes per 32-bit register */
+#define SYSMON_REG_STRIDE 4
+
+#define SYSMON_SUPPLY_IDX_MAX 159
+#define SYSMON_TEMP_SAT_MAX 64
+#define SYSMON_INTR_ALL_MASK GENMASK(31, 0)
+
+/* Supply voltage conversion register fields */
+#define SYSMON_MANTISSA_MASK GENMASK(15, 0)
+#define SYSMON_FMT_MASK BIT(16)
+#define SYSMON_MODE_MASK GENMASK(18, 17)
+
+/* Q8.7 fractional shift */
+#define SYSMON_FRACTIONAL_SHIFT 7U
+#define SYSMON_SUPPLY_MANTISSA_BITS 16
+
+/**
+ * struct sysmon - Driver data for Versal SysMon
+ * @regmap: register map for hardware access
+ * @lock: protects read-modify-write sequences on threshold registers
+ * and cached state that spans multiple regmap calls
+ */
+struct sysmon {
+ struct regmap *regmap;
+ /*
+ * Protects read-modify-write sequences on threshold registers
+ * and cached state (oversampling ratios, hysteresis values)
+ * that spans multiple regmap calls.
+ */
+ struct mutex lock;
+};
+
+int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap);
+
+#endif /* _VERSAL_SYSMON_H_ */
--
2.48.1
^ permalink raw reply related
* [PATCH v9 1/5] dt-bindings: iio: adc: add xlnx,versal-sysmon binding
From: Salih Erim @ 2026-06-17 18:01 UTC (permalink / raw)
To: jic23, andy
Cc: dlechner, nuno.sa, robh, krzk+dt, conor+dt, conall.ogriofa,
michal.simek, linux, erimsalih, linux-iio, devicetree,
linux-kernel, Salih Erim, Krzysztof Kozlowski
In-Reply-To: <20260617180147.3370346-1-salih.erim@amd.com>
Add devicetree binding for the AMD/Xilinx Versal System Monitor (SysMon).
The Versal SysMon is the successor to the Zynq UltraScale+ AMS block,
providing on-chip voltage and temperature monitoring. The hardware
supports up to 160 supply voltage measurement points and up to 64
temperature satellites distributed across the SoC, with configurable
threshold alarms and oversampling. The device can be accessed via
memory-mapped I/O or via an I2C interface.
Supply and temperature channels are described as child nodes under
container nodes, referencing the standard adc.yaml binding for
channel properties.
Co-developed-by: Michal Simek <michal.simek@amd.com>
Signed-off-by: Michal Simek <michal.simek@amd.com>
Signed-off-by: Salih Erim <salih.erim@amd.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
Changes in v9:
- No code changes
Changes in v8:
- No code changes
Changes in v7:
- No code changes
Changes in v6:
- No code changes
Changes in v5:
- No code changes
Changes in v4:
- Add Reviewed-by tag from Krzysztof Kozlowski
Changes in v3:
- Use single compatible (xlnx,versal-sysmon only), remove
xlnx,versal-sysmon-i2c (Krzysztof)
- Rename supply-channels container to voltage-channels (Krzysztof)
- Use single quotes in patternProperties regex (Krzysztof)
- Drop label description from channel properties (Krzysztof)
- Drop bipolar from channel properties (Krzysztof)
- Remove xlnx,aie-temp property from binding and example (Krzysztof)
Changes in v2:
- Restructured to container nodes (supply-channels, temperature-channels)
with channel@N children referencing adc.yaml
- Added xlnx,versal-sysmon-i2c compatible
- Descriptions rewritten to describe hardware only
- Example simplified to #address-cells = <1>
- Interrupt example uses GIC_SPI/IRQ_TYPE_LEVEL_HIGH constants
- Commit description explains hardware context instead of schema layout
- reg required for both MMIO and I2C, interrupts optional
- Hex unit-addresses (channel@a not channel@10) per DTSpec
- patternProperties regex updated to accept hex digits [0-9a-f]
- Example trimmed to minimal variants (one basic + one bipolar supply,
one AIE temperature channel)
.../bindings/iio/adc/xlnx,versal-sysmon.yaml | 154 ++++++++++++++++++
1 file changed, 154 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml
diff --git a/Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml b/Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml
new file mode 100644
index 00000000000..1ad58e3d616
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/xlnx,versal-sysmon.yaml
@@ -0,0 +1,154 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/xlnx,versal-sysmon.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: AMD/Xilinx Versal System Monitor
+
+maintainers:
+ - Salih Erim <salih.erim@amd.com>
+
+description:
+ The AMD/Xilinx Versal System Monitor (SysMon) is the successor to the
+ Zynq UltraScale+ AMS block. It provides on-chip voltage and temperature
+ monitoring with up to 160 voltage measurement points and up to
+ 64 temperature satellites distributed across the SoC. The hardware
+ supports configurable threshold alarms and oversampling. The device
+ can be accessed via memory-mapped I/O or via an I2C interface.
+
+properties:
+ compatible:
+ const: xlnx,versal-sysmon
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ '#io-channel-cells':
+ const: 1
+
+ voltage-channels:
+ type: object
+ description:
+ Container for voltage measurement channels.
+
+ properties:
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ patternProperties:
+ '^channel@([0-9a-f]|[1-9][0-9a-f])$':
+ $ref: adc.yaml
+
+ description:
+ Measures a voltage rail. The register index and rail
+ name are assigned by the hardware design tool (Vivado).
+
+ properties:
+ reg:
+ minimum: 0
+ maximum: 159
+ description:
+ Voltage measurement register index assigned by the hardware
+ design tool.
+
+ required:
+ - reg
+ - label
+
+ unevaluatedProperties: false
+
+ required:
+ - '#address-cells'
+ - '#size-cells'
+
+ additionalProperties: false
+
+ temperature-channels:
+ type: object
+ description:
+ Container for temperature satellite measurement channels.
+
+ properties:
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ patternProperties:
+ '^channel@([1-9a-f]|[1-3][0-9a-f]|40)$':
+ $ref: adc.yaml
+
+ description:
+ Reads a temperature satellite sensor. Each satellite monitors
+ a specific region of the SoC die.
+
+ properties:
+ reg:
+ minimum: 1
+ maximum: 64
+ description:
+ Temperature satellite number (1-based hardware index).
+
+ required:
+ - reg
+ - label
+
+ unevaluatedProperties: false
+
+ required:
+ - '#address-cells'
+ - '#size-cells'
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ sysmon@f1270000 {
+ compatible = "xlnx,versal-sysmon";
+ reg = <0xf1270000 0x4000>;
+ interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
+ #io-channel-cells = <1>;
+
+ voltage-channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ channel@0 {
+ reg = <0>;
+ label = "vccaux";
+ };
+
+ channel@3 {
+ reg = <3>;
+ label = "vcc_ram";
+ bipolar;
+ };
+ };
+
+ temperature-channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ channel@a {
+ reg = <10>;
+ label = "aie-temp-ch1";
+ };
+ };
+ };
--
2.48.1
^ permalink raw reply related
* [PATCH v9 3/5] iio: adc: versal-sysmon: add I2C driver
From: Salih Erim @ 2026-06-17 18:01 UTC (permalink / raw)
To: jic23, andy
Cc: dlechner, nuno.sa, robh, krzk+dt, conor+dt, conall.ogriofa,
michal.simek, linux, erimsalih, linux-iio, devicetree,
linux-kernel, Salih Erim, Andy Shevchenko
In-Reply-To: <20260617180147.3370346-1-salih.erim@amd.com>
Add an I2C transport driver for the Versal SysMon block. The SysMon
provides an I2C slave interface that allows an external master to
read voltage and temperature measurements through the same register
map used by the MMIO path.
The I2C command frame is an 8-byte structure containing a 4-byte data
payload, a 2-byte register offset, and a 1-byte instruction field.
Read operations send the frame with a read instruction, then receive
a 4-byte response containing the register value.
Events are not supported on the I2C path because there is no
interrupt line and the I2C regmap backend cannot be called from
atomic context.
Co-developed-by: Conall O'Griofa <conall.ogriofa@amd.com>
Signed-off-by: Conall O'Griofa <conall.ogriofa@amd.com>
Signed-off-by: Salih Erim <salih.erim@amd.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
---
Changes in v9:
- Add Reviewed-by tag from Andy Shevchenko
- Add MODULE_IMPORT_NS("VERSAL_SYSMON") (Andy, from P2 namespace)
Changes in v8:
- Add volatile register comment for regmap cache (Andy)
- Update devm_versal_sysmon_core_probe call site (Andy, from P2 rename)
Changes in v7:
- No code changes
Changes in v6:
- Add types.h include (IWYU) (Andy)
- Add local struct device *dev, join devm_regmap_init on
one line (Andy)
Changes in v5:
- Add err.h, mod_devicetable.h includes (IWYU) (Andy)
Changes in v4:
- Replace enum with defines for I2C frame offsets (Jonathan)
- Use get_unaligned_le32() for read data reassembly (Jonathan)
- Use put_unaligned_le32/le16() for write data and register offset
packing (Jonathan)
- Named initializer in i2c_device_id (Jonathan)
- Drop bitfield.h, add unaligned.h (FIELD_GET/FIELD_PREP replaced
by unaligned accessors)
Changes in v3:
- IWYU: fix includes (Andy)
- Enum: assign all values explicitly for HW-mapped fields (Andy)
- Remove sysmon_i2c wrapper struct, pass i2c_client directly
(Andy)
- Use sizeof() for I2C buffer lengths instead of defines (Andy)
- Use = { } instead of = { 0 } for initializers (Andy)
- Use single compatible xlnx,versal-sysmon (Krzysztof)
- Adapt to core_probe interface change: irq moved to core,
remove irq parameter from bus driver (Jonathan)
Changes in v2:
- New patch (I2C was deferred to Series B in v1)
- Uses regmap API with custom I2C read/write callbacks
- Shares core module with MMIO driver via sysmon_core_probe()
- No event support (I2C has no interrupt line)
- Separate VERSAL_SYSMON_I2C Kconfig symbol
- Reverse Christmas Tree variable ordering in read/write functions
drivers/iio/adc/Kconfig | 13 +++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/versal-sysmon-i2c.c | 134 ++++++++++++++++++++++++++++
3 files changed, 148 insertions(+)
create mode 100644 drivers/iio/adc/versal-sysmon-i2c.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index c7f19057484..8f9fc9de74a 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1963,6 +1963,19 @@ config VERSAL_SYSMON
To compile this driver as a module, choose M here: the module
will be called versal-sysmon.
+config VERSAL_SYSMON_I2C
+ tristate "AMD Versal SysMon I2C driver"
+ depends on I2C
+ select VERSAL_SYSMON_CORE
+ help
+ Say yes here to have support for the AMD/Xilinx Versal System
+ Monitor (SysMon) via I2C interface. This driver enables voltage
+ and temperature monitoring when the Versal chip has SysMon
+ configured with I2C access.
+
+ To compile this driver as a module, choose M here: the module
+ will be called versal-sysmon-i2c.
+
config VF610_ADC
tristate "Freescale vf610 ADC driver"
depends on HAS_IOMEM
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index d7696b1b157..5abb611fe46 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -169,6 +169,7 @@ obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
obj-$(CONFIG_VERSAL_SYSMON_CORE) += versal-sysmon-core.o
obj-$(CONFIG_VERSAL_SYSMON) += versal-sysmon.o
+obj-$(CONFIG_VERSAL_SYSMON_I2C) += versal-sysmon-i2c.o
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o
diff --git a/drivers/iio/adc/versal-sysmon-i2c.c b/drivers/iio/adc/versal-sysmon-i2c.c
new file mode 100644
index 00000000000..e9a7629159a
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon-i2c.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Versal SysMon I2C driver
+ *
+ * Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include "versal-sysmon.h"
+
+#define SYSMON_I2C_INSTR_READ BIT(2)
+#define SYSMON_I2C_INSTR_WRITE BIT(3)
+
+/*
+ * I2C command frame layout (8 bytes):
+ * [0..3] data payload (little-endian u32)
+ * [4..5] register offset >> 2 (little-endian u16)
+ * [6] instruction (read/write)
+ * [7] reserved
+ */
+#define SYSMON_I2C_DATA_OFS 0
+#define SYSMON_I2C_REG_OFS 4
+#define SYSMON_I2C_INSTR_OFS 6
+
+static int sysmon_i2c_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct i2c_client *client = context;
+ u8 write_buf[8] = { };
+ u8 read_buf[4];
+ int ret;
+
+ put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]);
+ write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_READ;
+
+ ret = i2c_master_send(client, write_buf, sizeof(write_buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(write_buf))
+ return -EIO;
+
+ ret = i2c_master_recv(client, read_buf, sizeof(read_buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(read_buf))
+ return -EIO;
+
+ *val = get_unaligned_le32(read_buf);
+
+ return 0;
+}
+
+static int sysmon_i2c_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct i2c_client *client = context;
+ u8 write_buf[8] = { };
+ int ret;
+
+ put_unaligned_le32(val, &write_buf[SYSMON_I2C_DATA_OFS]);
+ put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]);
+ write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_WRITE;
+
+ ret = i2c_master_send(client, write_buf, sizeof(write_buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(write_buf))
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Almost all registers are volatile (live ADC readings, interrupt
+ * status). The rest are not accessed often enough to benefit from
+ * caching.
+ */
+static const struct regmap_config sysmon_i2c_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = SYSMON_REG_STRIDE,
+ .max_register = SYSMON_MAX_REG,
+ .reg_read = sysmon_i2c_reg_read,
+ .reg_write = sysmon_i2c_reg_write,
+};
+
+static int sysmon_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init(dev, NULL, client, &sysmon_i2c_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* I2C has no IRQ connection; events are not supported */
+ return devm_versal_sysmon_core_probe(dev, regmap);
+}
+
+static const struct of_device_id sysmon_i2c_of_match_table[] = {
+ { .compatible = "xlnx,versal-sysmon" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sysmon_i2c_of_match_table);
+
+static const struct i2c_device_id sysmon_i2c_id_table[] = {
+ { .name = "versal-sysmon" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sysmon_i2c_id_table);
+
+static struct i2c_driver sysmon_i2c_driver = {
+ .probe = sysmon_i2c_probe,
+ .driver = {
+ .name = "versal-sysmon-i2c",
+ .of_match_table = sysmon_i2c_of_match_table,
+ },
+ .id_table = sysmon_i2c_id_table,
+};
+module_i2c_driver(sysmon_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Versal SysMon I2C Driver");
+MODULE_IMPORT_NS("VERSAL_SYSMON");
+MODULE_AUTHOR("Conall O'Griofa <conall.ogriofa@amd.com>");
+MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>");
--
2.48.1
^ permalink raw reply related
* [PATCH v9 4/5] iio: adc: versal-sysmon: add threshold event support
From: Salih Erim @ 2026-06-17 18:01 UTC (permalink / raw)
To: jic23, andy
Cc: dlechner, nuno.sa, robh, krzk+dt, conor+dt, conall.ogriofa,
michal.simek, linux, erimsalih, linux-iio, devicetree,
linux-kernel, Salih Erim
In-Reply-To: <20260617180147.3370346-1-salih.erim@amd.com>
Add threshold event support for temperature and supply voltage
channels.
Temperature events:
- Rising threshold with configurable value on the device
temperature channel (current max across all satellites)
- Per-channel hysteresis as a millicelsius value
- Event direction is IIO_EV_DIR_RISING (hysteresis mode)
Supply voltage events:
- Rising/falling threshold per supply channel
- Per-channel alarm enable via alarm configuration registers
The hardware supports both window and hysteresis alarm modes for
temperature. This driver uses hysteresis mode, where the upper
threshold triggers the alarm and the lower threshold clears it
(re-arm point). The hardware has a single ISR bit per temperature
channel with no indication of which threshold was crossed, so
hysteresis mode is the natural fit. The lower threshold register
is computed internally as (upper - hysteresis).
Hysteresis is stored in the driver as a millicelsius value,
initialized from the hardware registers at probe. Writing the
rising threshold or hysteresis recomputes the lower register.
ALARM_CONFIG is hard-coded to hysteresis mode during init.
The hardware also provides a separate over-temperature (OT)
threshold, but it is not exposed through IIO as it serves as a
hardware safety mechanism for platform shutdown. OT will be
exposed through the thermal framework in a follow-up series.
The interrupt handler masks active threshold interrupts (which are
level-sensitive) and schedules a delayed worker to poll for condition
clear before unmasking. When no hardware IRQ is available, event
specs are not attached and interrupt init is skipped, since the
I2C regmap backend cannot be called from atomic context.
When disabling a supply channel alarm, the group interrupt remains
active if any other channel in the same alarm group still has an
alarm enabled.
A devm cleanup action masks all interrupts on driver unbind to
prevent unhandled interrupt storms after the IRQ handler is freed.
Signed-off-by: Salih Erim <salih.erim@amd.com>
---
Changes in v9:
- Add minmax.h include for clamp() (Andy)
- Join sysmon_supply_thresh_offset to one line, change address
parameter to unsigned long for consistency (Andy)
- Combine mask declaration with initialization in
sysmon_read_event_config (Andy)
- Rename ier to mask in sysmon_write_event_config for
consistency with sysmon_read_event_config (Andy)
- Remove blank line in sysmon_update_temp_lower between
semantically coupled lines (Andy)
- Rename unmask to ier (u32) in sysmon_unmask_temp (Andy)
- Variable name and type consistency audit across all
event functions (Andy)
Changes in v8:
- Use MILLIDEGREE_PER_DEGREE in q8p7 conversion functions (Andy)
- Use regmap_test_bits() in sysmon_read_alarm_config (Andy)
- Join sysmon_parse_fw signature onto one line (Andy)
- Fix devm teardown race: replace devm_delayed_work_autocancel
with INIT_DELAYED_WORK; fold cancel_delayed_work_sync into
sysmon_disable_interrupts to prevent the worker from
re-enabling interrupts after the IRQ handler is freed (Sashiko)
- Drop devm-helpers.h include (no longer needed)
Changes in v7:
- Move TEMP threshold event onto channel 0; drop OT as
separate IIO channel -- OT is a hardware safety mechanism
better suited for the thermal framework follow-up (Jonathan)
- Use single temp_channels array; attach event spec to
channel 0 at runtime when IRQ is available, matching the
pattern used for supply channels (Jonathan)
- Remove sysmon_temp_thresh_offset; use SYSMON_TEMP_TH_UP
and SYSMON_TEMP_TH_LOW defines directly at call sites
- Return administrative state from temp_mask in
read_event_config instead of transient hardware IMR
(Jonathan, Sashiko)
- Add devm_add_action_or_reset to mask all HW interrupts
on driver unbind (Sashiko)
- Remove SYSMON_CHAN_TEMP_EVENT macro, SYSMON_ADDR_TEMP_EVENT,
SYSMON_ADDR_OT_EVENT, SYSMON_BIT_OT, SYSMON_OT_HYST_MASK,
OT_TH_LOW/UP registers, ot_hysteresis from struct
- Simplify sysmon_get_event_mask, sysmon_update_temp_lower,
sysmon_init_hysteresis -- all now operate on single TEMP
channel only
Changes in v6:
- Remove types.h from header (not needed at any stage) (Andy)
- Macro brace on separate line for SYSMON_CHAN_TEMP_EVENT (Andy)
- switch(chan->type) in all event functions instead of cascading
if statements (Andy)
- switch(info) in read/write_event_value for nested
dispatch (Andy)
- Reversed xmas tree in sysmon_update_temp_lower and
sysmon_init_hysteresis (Andy)
- scoped_guard(spinlock_irq) with error check in
sysmon_unmask_worker (Andy)
- Combined regmap_read error check with || in
sysmon_iio_irq (Andy)
- Join devm_request_irq on one line (Andy)
- Fix fwnode_irq_get() to propagate only -EPROBE_DEFER;
treating all negatives as fatal broke probe on I2C nodes
without interrupts property
Changes in v5:
- clamp() instead of clamp_t() (Andy)
- regmap_assign_bits() instead of separate set/clear (Andy)
- Remove unneeded parentheses (2 places) (Andy)
- for_each_set_bit on single line (Andy)
- regmap_clear_bits() instead of regmap_update_bits() (Andy)
- Simplify unmask XOR to ~status & masked_temp (Andy)
- Add comment explaining unmask &= ~temp_mask logic (Andy)
- Split container_of across two lines (Andy)
- Move ISR write after !isr check to avoid writing 0 (Andy)
- unsigned int for init_hysteresis address param (Andy)
- Add comment explaining error check policy in worker/IRQ (Andy)
- Nested size_add() for overflow-safe allocation (Andy)
- Propagate negative from fwnode_irq_get() for
EPROBE_DEFER (Andy)
- Pass irq instead of has_irq to sysmon_parse_fw (Andy)
Changes in v4:
- Merge event channels into static temp array; two arrays
(with/without events) selected by has_irq (Jonathan)
- Event-only channels have no info_mask; their addresses are
logical identifiers, not readable registers
- Drop RAW for voltage events, keep PROCESSED only (Jonathan)
- Drop scan_type from event channel macro (Jonathan)
- Blank lines between call+error-check blocks (Jonathan)
- Fit under 80 chars on one line where possible (Jonathan)
- default case returns -EINVAL instead of break (Jonathan)
- sysmon_handle_event: return early in each case (Jonathan)
- guard(spinlock) in sysmon_iio_irq, return IRQ_NONE/IRQ_HANDLED
directly (Jonathan)
- Take irq_lock in write_event_config for temp_mask updates to
synchronize with unmask worker (Sashiko)
Changes in v3:
- IWYU: add new includes, group iio headers with blank line (Andy)
- Reduce casts in millicelsius_to_q8p7, consistent style with
q8p7_to_millicelsius (Andy)
- Use clamp_t with typed constants, remove tmp & U16_MAX (Andy)
- Use !! to return 0/1 from read_alarm_config (Andy)
- Use regmap_set_bits/clear_bits in write_alarm_config (Andy)
- Add comment explaining spinlock is safe (I2C never reaches
event code path) (Andy)
- Add comment explaining IMR negation logic (Andy)
- Split read_event_value/write_event_value parameters logically
across lines (Andy)
- Move mask/shift after regmap_read error check (Andy)
- Remove redundant else in read_event_value and
write_event_value (Andy)
- Use named constant for hysteresis bit, if-else not ternary
(Andy)
- Loop variable declared in for() scope (Andy)
- Add error checks in sysmon_handle_event (Andy)
- Use IRQ_RETVAL() macro (Andy)
- Use devm_delayed_work_autocancel instead of manual INIT +
devm_add_action (Andy)
- Use FIELD_GET/FIELD_PREP for hysteresis register bits
(Jonathan)
- Split OT vs TEMP handling with FIELD_GET (Jonathan)
- Rework hysteresis: store as millicelsius value, hardcode
ALARM_CONFIG to hysteresis mode, compute lower threshold
from (upper - hysteresis), initialize from HW at probe
(Jonathan)
- Remove falling threshold for temperature; single event
spec per channel with IIO_EV_DIR_RISING (Jonathan)
- Push IIO_EV_DIR_RISING events for temperature,
IIO_EV_DIR_EITHER for voltage (Jonathan)
Changes in v2:
- Reverse Christmas Tree variable ordering in all functions
- Named constants for hysteresis bits: SYSMON_OT_HYST_BIT,
SYSMON_TEMP_HYST_BIT instead of magic 0x1/0x2
- SYSMON_ALARM_BITS_PER_REG replaces magic number 32
- SYSMON_ALARM_OFFSET() helper macro deduplicates alarm register
offset computation
- BIT() macro for shift expressions in conversion functions
- Hysteresis input validated to single-bit range (0 or 1)
- Event channels only created when irq > 0 (I2C safety)
- Group alarm interrupt stays active while any channel in the
group has an alarm enabled
- write_event_value returns -EINVAL for unhandled types
- IRQ_NONE returned for spurious interrupts
- Q8.7 write path uses multiplication instead of left-shift
to avoid undefined behavior with negative temperatures
- (u16) mask prevents garbage in reserved register bits
- regmap_write return values checked for IER/IDR writes
- devm cleanup ordering: cancel_work before request_irq
drivers/iio/adc/versal-sysmon-core.c | 601 ++++++++++++++++++++++++++-
drivers/iio/adc/versal-sysmon.h | 36 ++
2 files changed, 632 insertions(+), 5 deletions(-)
diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c
index e0b9b3d2b6d..19a8edd8919 100644
--- a/drivers/iio/adc/versal-sysmon-core.c
+++ b/drivers/iio/adc/versal-sysmon-core.c
@@ -12,6 +12,8 @@
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/overflow.h>
#include <linux/property.h>
@@ -20,10 +22,18 @@
#include <linux/sysfs.h>
#include <linux/units.h>
+#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include "versal-sysmon.h"
+/* TEMP hysteresis mode bit in SYSMON_TEMP_EV_CFG */
+#define SYSMON_TEMP_HYST_MASK BIT(1)
+
+/* Compute alarm register offset from a channel address */
+#define SYSMON_ALARM_OFFSET(addr) \
+ (SYSMON_ALARM_REG + ((addr) / SYSMON_ALARM_BITS_PER_REG) * SYSMON_REG_STRIDE)
+
#define SYSMON_CHAN_TEMP(_chan, _address, _name) \
{ \
.type = IIO_TEMP, \
@@ -35,6 +45,45 @@
.datasheet_name = _name, \
}
+enum sysmon_alarm_bit {
+ SYSMON_BIT_ALARM0 = 0,
+ SYSMON_BIT_ALARM1 = 1,
+ SYSMON_BIT_ALARM2 = 2,
+ SYSMON_BIT_ALARM3 = 3,
+ SYSMON_BIT_ALARM4 = 4,
+ SYSMON_BIT_TEMP = 9,
+};
+
+/* Temperature event specification: rising threshold + hysteresis only */
+static const struct iio_event_spec sysmon_temp_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_HYSTERESIS),
+ },
+};
+
+/* Supply event specifications */
+static const struct iio_event_spec sysmon_supply_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
/*
* Static temperature channels (always present).
*
@@ -52,6 +101,16 @@ static const struct iio_chan_spec temp_channels[] = {
SYSMON_CHAN_TEMP(3, SYSMON_TEMP_MIN_MIN, "min_min"),
};
+static void sysmon_q8p7_to_millicelsius(s16 raw_data, int *val)
+{
+ *val = (raw_data * MILLIDEGREE_PER_DEGREE) >> SYSMON_FRACTIONAL_SHIFT;
+}
+
+static void sysmon_millicelsius_to_q8p7(u32 *raw_data, int val)
+{
+ *raw_data = (val << SYSMON_FRACTIONAL_SHIFT) / MILLIDEGREE_PER_DEGREE;
+}
+
static void sysmon_supply_rawtoprocessed(int raw_data, int *val)
{
int mantissa, format, exponent;
@@ -69,6 +128,33 @@ static void sysmon_supply_rawtoprocessed(int raw_data, int *val)
*val = (mantissa * (int)MILLI) >> exponent;
}
+static void sysmon_supply_processedtoraw(int val, u32 reg_val, u32 *raw_data)
+{
+ int exponent = FIELD_GET(SYSMON_MODE_MASK, reg_val);
+ int format = FIELD_GET(SYSMON_FMT_MASK, reg_val);
+ int scale, tmp;
+
+ scale = BIT(SYSMON_SUPPLY_MANTISSA_BITS - exponent);
+ tmp = (val * scale) / (int)MILLI;
+
+ if (format)
+ tmp = clamp(tmp, S16_MIN, S16_MAX);
+ else
+ tmp = clamp(tmp, 0, U16_MAX);
+
+ *raw_data = (u16)tmp;
+}
+
+static int sysmon_supply_thresh_offset(unsigned long address, enum iio_event_direction dir)
+{
+ if (dir == IIO_EV_DIR_RISING)
+ return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_UP;
+ if (dir == IIO_EV_DIR_FALLING)
+ return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_LOW;
+
+ return -EINVAL;
+}
+
static int sysmon_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -115,6 +201,256 @@ static int sysmon_read_raw(struct iio_dev *indio_dev,
}
}
+static u32 sysmon_get_event_mask(const struct iio_chan_spec *chan)
+{
+ if (chan->type == IIO_TEMP)
+ return BIT(SYSMON_BIT_TEMP);
+
+ return BIT(chan->address / SYSMON_ALARM_BITS_PER_REG);
+}
+
+static int sysmon_read_alarm_config(struct sysmon *sysmon,
+ unsigned long address)
+{
+ u32 shift = address % SYSMON_ALARM_BITS_PER_REG;
+ u32 offset = SYSMON_ALARM_OFFSET(address);
+
+ return regmap_test_bits(sysmon->regmap, offset, BIT(shift));
+}
+
+static int sysmon_write_alarm_config(struct sysmon *sysmon,
+ unsigned long address, bool enable)
+{
+ u32 shift = address % SYSMON_ALARM_BITS_PER_REG;
+ u32 offset = SYSMON_ALARM_OFFSET(address);
+
+ return regmap_assign_bits(sysmon->regmap, offset, BIT(shift), enable);
+}
+
+static int sysmon_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ u32 mask = sysmon_get_event_mask(chan);
+ unsigned int imr;
+ int config_value;
+ int ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr);
+ if (ret)
+ return ret;
+
+ /* IMR bits are 1=masked, invert to get 1=enabled */
+ imr = ~imr;
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ config_value = sysmon_read_alarm_config(sysmon, chan->address);
+ if (config_value < 0)
+ return config_value;
+ return config_value && (imr & mask);
+
+ case IIO_TEMP:
+ /*
+ * Return the administrative state, not the hardware IMR.
+ * The IRQ handler temporarily masks the interrupt during
+ * the polling window; reading IMR would show it as disabled.
+ * temp_mask bit is set when administratively disabled.
+ */
+ return !(sysmon->temp_mask & mask);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sysmon_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ u32 offset = SYSMON_ALARM_OFFSET(chan->address);
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ u32 mask = sysmon_get_event_mask(chan);
+ unsigned int alarm_config;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ ret = sysmon_write_alarm_config(sysmon, chan->address, state);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(sysmon->regmap, offset, &alarm_config);
+ if (ret)
+ return ret;
+
+ if (alarm_config)
+ return regmap_write(sysmon->regmap, SYSMON_IER, mask);
+
+ return regmap_write(sysmon->regmap, SYSMON_IDR, mask);
+
+ case IIO_TEMP:
+ if (state) {
+ ret = regmap_write(sysmon->regmap, SYSMON_IER, mask);
+ if (ret)
+ return ret;
+
+ scoped_guard(spinlock_irq, &sysmon->irq_lock)
+ sysmon->temp_mask &= ~mask;
+ } else {
+ ret = regmap_write(sysmon->regmap, SYSMON_IDR, mask);
+ if (ret)
+ return ret;
+
+ scoped_guard(spinlock_irq, &sysmon->irq_lock)
+ sysmon->temp_mask |= mask;
+ }
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Recompute the lower threshold register from upper threshold and
+ * cached hysteresis. Called when either upper threshold or hysteresis
+ * is written.
+ */
+static int sysmon_update_temp_lower(struct sysmon *sysmon)
+{
+ unsigned int upper_reg;
+ int upper_mc, lower_mc;
+ u32 raw_val;
+ int ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg);
+ if (ret)
+ return ret;
+
+ sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc);
+ lower_mc = upper_mc - sysmon->temp_hysteresis;
+ sysmon_millicelsius_to_q8p7(&raw_val, lower_mc);
+
+ return regmap_write(sysmon->regmap, SYSMON_TEMP_TH_LOW, raw_val);
+}
+
+static int sysmon_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int reg_val;
+ int offset;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, ®_val);
+ if (ret)
+ return ret;
+
+ sysmon_q8p7_to_millicelsius(reg_val, val);
+
+ return IIO_VAL_INT;
+
+ case IIO_EV_INFO_HYSTERESIS:
+ *val = sysmon->temp_hysteresis;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_VOLTAGE:
+ offset = sysmon_supply_thresh_offset(chan->address, dir);
+ if (offset < 0)
+ return offset;
+
+ ret = regmap_read(sysmon->regmap, offset, ®_val);
+ if (ret)
+ return ret;
+
+ sysmon_supply_rawtoprocessed(reg_val, val);
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sysmon_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int reg_val;
+ u32 raw_val;
+ int offset;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ sysmon_millicelsius_to_q8p7(&raw_val, val);
+
+ ret = regmap_write(sysmon->regmap, SYSMON_TEMP_TH_UP, raw_val);
+ if (ret)
+ return ret;
+
+ /* Recompute lower = upper - hysteresis */
+ return sysmon_update_temp_lower(sysmon);
+
+ case IIO_EV_INFO_HYSTERESIS:
+ if (val < 0)
+ return -EINVAL;
+
+ sysmon->temp_hysteresis = val;
+
+ return sysmon_update_temp_lower(sysmon);
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_VOLTAGE:
+ offset = sysmon_supply_thresh_offset(chan->address, dir);
+ if (offset < 0)
+ return offset;
+
+ ret = regmap_read(sysmon->regmap, offset, ®_val);
+ if (ret)
+ return ret;
+
+ sysmon_supply_processedtoraw(val, reg_val, &raw_val);
+
+ return regmap_write(sysmon->regmap, offset, raw_val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int sysmon_read_label(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
char *label)
@@ -128,24 +464,246 @@ static int sysmon_read_label(struct iio_dev *indio_dev,
static const struct iio_info sysmon_iio_info = {
.read_raw = sysmon_read_raw,
.read_label = sysmon_read_label,
+ .read_event_config = sysmon_read_event_config,
+ .write_event_config = sysmon_write_event_config,
+ .read_event_value = sysmon_read_event_value,
+ .write_event_value = sysmon_write_event_value,
};
+static void sysmon_push_event(struct iio_dev *indio_dev, u32 address)
+{
+ const struct iio_chan_spec *chan;
+ enum iio_event_direction dir;
+
+ for (unsigned int i = 0; i < indio_dev->num_channels; i++) {
+ if (indio_dev->channels[i].address != address)
+ continue;
+
+ chan = &indio_dev->channels[i];
+ /* Temp uses hysteresis mode (rising only), voltage uses window */
+ dir = (chan->type == IIO_TEMP) ? IIO_EV_DIR_RISING :
+ IIO_EV_DIR_EITHER;
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(chan->type,
+ chan->channel,
+ IIO_EV_TYPE_THRESH,
+ dir),
+ iio_get_time_ns(indio_dev));
+ }
+}
+
+static int sysmon_handle_event(struct iio_dev *indio_dev, u32 event)
+{
+ u32 alarm_flag_offset = SYSMON_ALARM_FLAG + event * SYSMON_REG_STRIDE;
+ u32 alarm_reg_offset = SYSMON_ALARM_REG + event * SYSMON_REG_STRIDE;
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned long alarm_flag_reg;
+ unsigned int reg_val;
+ u32 address, bit;
+ int ret;
+
+ switch (event) {
+ case SYSMON_BIT_TEMP:
+ sysmon_push_event(indio_dev, SYSMON_TEMP_MAX);
+
+ ret = regmap_write(sysmon->regmap, SYSMON_IDR, BIT(SYSMON_BIT_TEMP));
+ if (ret)
+ return ret;
+
+ sysmon->masked_temp |= BIT(SYSMON_BIT_TEMP);
+ return 0;
+
+ case SYSMON_BIT_ALARM0:
+ case SYSMON_BIT_ALARM1:
+ case SYSMON_BIT_ALARM2:
+ case SYSMON_BIT_ALARM3:
+ case SYSMON_BIT_ALARM4:
+ ret = regmap_read(sysmon->regmap, alarm_flag_offset, ®_val);
+ if (ret)
+ return ret;
+
+ alarm_flag_reg = reg_val;
+
+ for_each_set_bit(bit, &alarm_flag_reg, SYSMON_ALARM_BITS_PER_REG) {
+ address = bit + SYSMON_ALARM_BITS_PER_REG * event;
+ sysmon_push_event(indio_dev, address);
+ ret = regmap_clear_bits(sysmon->regmap, alarm_reg_offset, BIT(bit));
+ if (ret)
+ return ret;
+ }
+
+ return regmap_write(sysmon->regmap, alarm_flag_offset, alarm_flag_reg);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void sysmon_handle_events(struct iio_dev *indio_dev,
+ unsigned long events)
+{
+ unsigned int bit;
+
+ for_each_set_bit(bit, &events, SYSMON_NO_OF_EVENTS)
+ sysmon_handle_event(indio_dev, bit);
+}
+
+static void sysmon_unmask_temp(struct sysmon *sysmon, unsigned int isr)
+{
+ unsigned int status;
+ u32 ier;
+
+ status = isr & SYSMON_TEMP_INTR_MASK;
+
+ ier = ~status & sysmon->masked_temp;
+ sysmon->masked_temp &= status;
+
+ /* Only unmask if not administratively disabled by userspace */
+ ier &= ~sysmon->temp_mask;
+
+ regmap_write(sysmon->regmap, SYSMON_IER, ier);
+}
+
+/*
+ * Versal threshold interrupts are level-sensitive. Active threshold
+ * interrupts are masked in the handler and polled via delayed work
+ * until the condition clears, then unmasked.
+ */
+static void sysmon_unmask_worker(struct work_struct *work)
+{
+ struct sysmon *sysmon =
+ container_of(work, struct sysmon, sysmon_unmask_work.work);
+ unsigned int isr;
+
+ /*
+ * If the ISR read fails, skip processing to avoid acting
+ * on undefined data.
+ */
+ scoped_guard(spinlock_irq, &sysmon->irq_lock) {
+ if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr))
+ break;
+ regmap_write(sysmon->regmap, SYSMON_ISR, isr);
+ sysmon_unmask_temp(sysmon, isr);
+ }
+
+ if (sysmon->masked_temp)
+ schedule_delayed_work(&sysmon->sysmon_unmask_work,
+ msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS));
+ else
+ regmap_write(sysmon->regmap, SYSMON_STATUS_RESET, 1);
+}
+
+static irqreturn_t sysmon_iio_irq(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int isr, imr;
+
+ guard(spinlock)(&sysmon->irq_lock);
+
+ if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr) ||
+ regmap_read(sysmon->regmap, SYSMON_IMR, &imr))
+ return IRQ_NONE;
+
+ isr &= ~imr;
+ if (!isr)
+ return IRQ_NONE;
+
+ regmap_write(sysmon->regmap, SYSMON_ISR, isr);
+
+ sysmon_handle_events(indio_dev, isr);
+ schedule_delayed_work(&sysmon->sysmon_unmask_work,
+ msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS));
+
+ return IRQ_HANDLED;
+}
+
+static void sysmon_disable_interrupts(void *data)
+{
+ struct sysmon *sysmon = data;
+
+ regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK);
+
+ scoped_guard(spinlock_irq, &sysmon->irq_lock)
+ sysmon->masked_temp = 0;
+
+ cancel_delayed_work_sync(&sysmon->sysmon_unmask_work);
+}
+
+static int sysmon_init_interrupt(struct sysmon *sysmon,
+ struct device *dev,
+ struct iio_dev *indio_dev,
+ int irq)
+{
+ unsigned int imr;
+ int ret;
+
+ /* Events not supported without IRQ (e.g. I2C path) */
+ if (!irq)
+ return 0;
+
+ INIT_DELAYED_WORK(&sysmon->sysmon_unmask_work, sysmon_unmask_worker);
+
+ ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr);
+ if (ret)
+ return ret;
+ sysmon->temp_mask = imr & SYSMON_TEMP_INTR_MASK;
+
+ ret = devm_request_irq(dev, irq, sysmon_iio_irq, 0, "sysmon-irq", indio_dev);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, sysmon_disable_interrupts, sysmon);
+}
+
+/*
+ * Initialize the cached hysteresis for a temperature channel from the
+ * current hardware threshold registers: hysteresis = upper - lower.
+ */
+static int sysmon_init_hysteresis(struct sysmon *sysmon, int *hysteresis)
+{
+ unsigned int upper_reg, lower_reg;
+ int upper_mc, lower_mc;
+ int ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_LOW, &lower_reg);
+ if (ret)
+ return ret;
+
+ sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc);
+ sysmon_q8p7_to_millicelsius(lower_reg, &lower_mc);
+ *hysteresis = upper_mc - lower_mc;
+
+ return 0;
+}
+
/**
* sysmon_parse_fw() - Parse firmware nodes and configure IIO channels.
* @indio_dev: IIO device instance
* @dev: Parent device
+ * @irq: IRQ number (positive enables event channels, 0 disables)
*
* Reads voltage-channels and temperature-channels container nodes from
* firmware and builds the IIO channel array. Static temperature channels
- * are prepended, followed by supply and satellite channels from DT.
+ * and event channels are prepended, followed by supply and satellite
+ * channels from DT.
+ *
+ * Event channels and per-channel event specs are only added when the
+ * device has an IRQ. I2C devices have no interrupt line, and the I2C
+ * regmap cannot be called from atomic context, so events are not
+ * supported on that path.
*
* Return: 0 on success, negative errno on failure.
*/
-static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev)
+static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int irq)
{
unsigned int num_chan, num_static, idx, temp_chan_idx, volt_chan_idx;
- unsigned int num_supply, num_temp;
struct iio_chan_spec *sysmon_channels;
+ unsigned int num_supply, num_temp;
const char *label;
u32 reg;
int ret;
@@ -164,8 +722,14 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev)
if (!sysmon_channels)
return -ENOMEM;
- /* Static temperature channels first */
memcpy(sysmon_channels, temp_channels, sizeof(temp_channels));
+
+ /* Attach event spec to channel 0 when IRQ is available */
+ if (irq > 0) {
+ sysmon_channels[0].event_spec = sysmon_temp_events;
+ sysmon_channels[0].num_event_specs = ARRAY_SIZE(sysmon_temp_events);
+ }
+
idx = num_static;
/* Supply channels from DT */
@@ -190,6 +754,10 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev)
.indexed = 1,
.address = reg,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .event_spec = irq > 0 ?
+ sysmon_supply_events : NULL,
+ .num_event_specs = irq > 0 ?
+ ARRAY_SIZE(sysmon_supply_events) : 0,
.datasheet_name = label,
};
}
@@ -255,6 +823,7 @@ int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap)
{
struct iio_dev *indio_dev;
struct sysmon *sysmon;
+ int irq;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*sysmon));
@@ -267,6 +836,7 @@ int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap)
ret = devm_mutex_init(dev, &sysmon->lock);
if (ret)
return ret;
+ spin_lock_init(&sysmon->irq_lock);
/* Disable all interrupts and clear pending status */
ret = regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK);
@@ -276,13 +846,34 @@ int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap)
if (ret)
return ret;
+ irq = fwnode_irq_get(dev_fwnode(dev), 0);
+ if (irq == -EPROBE_DEFER)
+ return dev_err_probe(dev, irq, "failed to get IRQ\n");
+
indio_dev->name = "versal-sysmon";
indio_dev->modes = INDIO_DIRECT_MODE;
- ret = sysmon_parse_fw(indio_dev, dev);
+ ret = sysmon_parse_fw(indio_dev, dev, irq);
if (ret)
return ret;
+ if (irq > 0) {
+ /* Set hysteresis mode for temperature threshold */
+ ret = regmap_set_bits(sysmon->regmap, SYSMON_TEMP_EV_CFG,
+ SYSMON_TEMP_HYST_MASK);
+ if (ret)
+ return ret;
+
+ /* Initialize cached hysteresis from hardware registers */
+ ret = sysmon_init_hysteresis(sysmon, &sysmon->temp_hysteresis);
+ if (ret)
+ return ret;
+
+ ret = sysmon_init_interrupt(sysmon, dev, indio_dev, irq);
+ if (ret)
+ return ret;
+ }
+
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(devm_versal_sysmon_core_probe, "VERSAL_SYSMON");
diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h
index e27a5357575..9fe2793757a 100644
--- a/drivers/iio/adc/versal-sysmon.h
+++ b/drivers/iio/adc/versal-sysmon.h
@@ -11,6 +11,8 @@
#include <linux/bits.h>
#include <linux/mutex.h>
+#include <linux/spinlock_types.h>
+#include <linux/workqueue.h>
struct device;
struct regmap;
@@ -18,12 +20,22 @@ struct regmap;
/* Register offsets (sorted by address) */
#define SYSMON_NPI_LOCK 0x000C
#define SYSMON_ISR 0x0044
+#define SYSMON_IMR 0x0048
+#define SYSMON_IER 0x004C
#define SYSMON_IDR 0x0050
#define SYSMON_TEMP_MAX 0x1030
#define SYSMON_TEMP_MIN 0x1034
#define SYSMON_SUPPLY_BASE 0x1040
+#define SYSMON_ALARM_FLAG 0x1018
+#define SYSMON_ALARM_REG 0x1940
+#define SYSMON_TEMP_TH_LOW 0x1970
+#define SYSMON_TEMP_TH_UP 0x1974
+#define SYSMON_SUPPLY_TH_LOW 0x1980
+#define SYSMON_SUPPLY_TH_UP 0x1C80
+#define SYSMON_TEMP_EV_CFG 0x1F84
#define SYSMON_TEMP_MIN_MIN 0x1F8C
#define SYSMON_TEMP_MAX_MAX 0x1F90
+#define SYSMON_STATUS_RESET 0x1F94
#define SYSMON_TEMP_SAT_BASE 0x1FAC
#define SYSMON_MAX_REG 0x24C0
@@ -35,8 +47,12 @@ struct regmap;
#define SYSMON_SUPPLY_IDX_MAX 159
#define SYSMON_TEMP_SAT_MAX 64
+#define SYSMON_NO_OF_EVENTS 32
#define SYSMON_INTR_ALL_MASK GENMASK(31, 0)
+/* ISR/IMR temperature alarm mask (bit 9) */
+#define SYSMON_TEMP_INTR_MASK BIT(9)
+
/* Supply voltage conversion register fields */
#define SYSMON_MANTISSA_MASK GENMASK(15, 0)
#define SYSMON_FMT_MASK BIT(16)
@@ -46,11 +62,21 @@ struct regmap;
#define SYSMON_FRACTIONAL_SHIFT 7U
#define SYSMON_SUPPLY_MANTISSA_BITS 16
+/* Bits per alarm register */
+#define SYSMON_ALARM_BITS_PER_REG 32
+
+#define SYSMON_UNMASK_WORK_DELAY_MS 500
+
/**
* struct sysmon - Driver data for Versal SysMon
* @regmap: register map for hardware access
* @lock: protects read-modify-write sequences on threshold registers
* and cached state that spans multiple regmap calls
+ * @irq_lock: protects interrupt mask register updates (MMIO path only)
+ * @masked_temp: currently masked temperature alarm bits
+ * @temp_mask: temperature interrupt configuration mask
+ * @temp_hysteresis: cached DEVICE_TEMP hysteresis in millicelsius
+ * @sysmon_unmask_work: re-enables events after alarm condition clears
*/
struct sysmon {
struct regmap *regmap;
@@ -60,6 +86,16 @@ struct sysmon {
* that spans multiple regmap calls.
*/
struct mutex lock;
+ /*
+ * Protects interrupt mask register updates. Only used on the
+ * MMIO path (fast_io regmap); I2C has no IRQ and never reaches
+ * the event code that takes this lock.
+ */
+ spinlock_t irq_lock;
+ unsigned int masked_temp;
+ unsigned int temp_mask;
+ int temp_hysteresis;
+ struct delayed_work sysmon_unmask_work;
};
int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap);
--
2.48.1
^ permalink raw reply related
* [PATCH v9 5/5] iio: adc: versal-sysmon: add oversampling support
From: Salih Erim @ 2026-06-17 18:01 UTC (permalink / raw)
To: jic23, andy
Cc: dlechner, nuno.sa, robh, krzk+dt, conor+dt, conall.ogriofa,
michal.simek, linux, erimsalih, linux-iio, devicetree,
linux-kernel, Salih Erim, Andy Shevchenko
In-Reply-To: <20260617180147.3370346-1-salih.erim@amd.com>
Add support for reading and writing the oversampling ratio through
the IIO oversampling_ratio attribute. The hardware supports averaging
2, 4, 8, or 16 samples, plus a ratio of 1 (no averaging).
Temperature and supply channels share oversampling configuration at
the type level (all temperature channels share one ratio, all supply
channels share another), exposed through info_mask_shared_by_type.
The hardware encoding uses sample_count / 2 in a 4-bit field within
the CONFIG register. Per-channel averaging enable registers must also
be updated to activate or deactivate averaging.
Signed-off-by: Salih Erim <salih.erim@amd.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
---
Changes in v9:
- Add Reviewed-by tag from Andy Shevchenko
- No code changes
Changes in v8:
- Use unsigned int for val parameter and hw_val in both
osr_write helpers (Andy)
- Use ~0 instead of ~0U for avg enable bitmask (Andy)
Changes in v7:
- Split sysmon_osr_write into sysmon_osr_write_temp and
sysmon_osr_write_supply; caller dispatches with if/else
on chan->type (Jonathan)
- Restore HW encoding comment in both helpers; fix
cross-reference in sysmon_osr_write_supply
Changes in v6:
- Fix FIELD_PREP indentation in sysmon_osr_write (Andy)
- unsigned int for loop index in sysmon_write_raw (Andy)
Changes in v5:
- Remove unneeded parentheses in i * SYSMON_REG_STRIDE (Andy)
- Use struct regmap *map local variable in
sysmon_set_avg_enable (Andy)
- switch instead of redundant if/if on channel_type (Andy)
- Add CONFIG register readback fence after oversampling update
to prevent NoC bus hang from posted writes (found during
hardware stress testing)
Changes in v4:
- Return directly from sysmon_set_avg_enable calls, remove
else after early returns, drop unreachable return 0 (Jonathan)
- Rename mask defines to SYSMON_CONFIG_SUPPLY_OSR and
SYSMON_CONFIG_TEMP_SAT_OSR (Jonathan)
- Drop "bits X:Y" from GENMASK comments (Jonathan)
- Blank lines after if (ret) return ret blocks (Jonathan)
- Move oversampling read inside guard(mutex) scope
Changes in v3:
- No changes
Changes in v2:
- EN_AVG per-channel bitmask registers written with all-ones
instead of boolean 1 when oversampling is enabled
- EN_AVG write errors propagated to userspace
- Oversampling limited to satellite temp and supply channels;
static temp channels do not participate
- Oversampling exposes actual sample counts (1,2,4,8,16) to
userspace with internal HW register translation
- write_raw_get_fmt returns IIO_VAL_INT for oversampling ratio
- HW encoding documented (sample_count/2, not log2)
- oversampling_avail is const int[] (type match fix)
drivers/iio/adc/versal-sysmon-core.c | 153 ++++++++++++++++++++++++++-
drivers/iio/adc/versal-sysmon.h | 17 +++
2 files changed, 169 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c
index 19a8edd8919..16f474c1bff 100644
--- a/drivers/iio/adc/versal-sysmon-core.c
+++ b/drivers/iio/adc/versal-sysmon-core.c
@@ -27,6 +27,12 @@
#include "versal-sysmon.h"
+/*
+ * Oversampling ratio values exposed to userspace via IIO.
+ * Actual number of samples averaged: 1=none, 2=2x, 4=4x, 8=8x, 16=16x.
+ */
+static const int sysmon_oversampling_avail[] = { 1, 2, 4, 8, 16 };
+
/* TEMP hysteresis mode bit in SYSMON_TEMP_EV_CFG */
#define SYSMON_TEMP_HYST_MASK BIT(1)
@@ -165,6 +171,12 @@ static int sysmon_read_raw(struct iio_dev *indio_dev,
guard(mutex)(&sysmon->lock);
+ if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
+ *val = (chan->type == IIO_TEMP) ? sysmon->temp_oversampling :
+ sysmon->supply_oversampling;
+ return IIO_VAL_INT;
+ }
+
switch (chan->type) {
case IIO_TEMP:
if (mask == IIO_CHAN_INFO_SCALE) {
@@ -451,6 +463,132 @@ static int sysmon_write_event_value(struct iio_dev *indio_dev,
}
}
+static int sysmon_set_avg_enable(struct sysmon *sysmon,
+ u32 base, u32 count, u32 val)
+{
+ struct regmap *map = sysmon->regmap;
+ int ret;
+
+ for (unsigned int i = 0; i < count; i++) {
+ ret = regmap_write(map, base + i * SYSMON_REG_STRIDE, val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sysmon_osr_write_temp(struct sysmon *sysmon, unsigned int val)
+{
+ /*
+ * HW register encoding is sample_count / 2:
+ * 0=none, 1=2x, 2=4x, 4=8x, 8=16x (not log2-based).
+ */
+ unsigned int hw_val = val >> 1;
+ unsigned int readback;
+ int ret;
+
+ ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG,
+ SYSMON_CONFIG_TEMP_SAT_OSR,
+ FIELD_PREP(SYSMON_CONFIG_TEMP_SAT_OSR, hw_val));
+ if (ret)
+ return ret;
+
+ /*
+ * Readback fence: the SysMon CONFIG register resides in the
+ * PMC domain behind the NoC. A posted write may not reach the
+ * hardware before the next MMIO access. Reading the register
+ * back forces the interconnect to complete the write, preventing
+ * a bus hang on the subsequent access.
+ */
+ regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback);
+
+ return sysmon_set_avg_enable(sysmon, SYSMON_TEMP_EN_AVG_BASE,
+ SYSMON_TEMP_EN_AVG_COUNT,
+ hw_val ? ~0 : 0);
+}
+
+static int sysmon_osr_write_supply(struct sysmon *sysmon, unsigned int val)
+{
+ /* HW encoding: sample_count / 2 (see sysmon_osr_write_temp) */
+ unsigned int hw_val = val >> 1;
+ unsigned int readback;
+ int ret;
+
+ ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG,
+ SYSMON_CONFIG_SUPPLY_OSR,
+ FIELD_PREP(SYSMON_CONFIG_SUPPLY_OSR, hw_val));
+ if (ret)
+ return ret;
+
+ /* Readback fence -- see sysmon_osr_write_temp for details */
+ regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback);
+
+ return sysmon_set_avg_enable(sysmon, SYSMON_SUPPLY_EN_AVG_BASE,
+ SYSMON_SUPPLY_EN_AVG_COUNT,
+ hw_val ? ~0 : 0);
+}
+
+static int sysmon_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int i;
+ int ret;
+
+ if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(sysmon_oversampling_avail); i++) {
+ if (val == sysmon_oversampling_avail[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(sysmon_oversampling_avail))
+ return -EINVAL;
+
+ guard(mutex)(&sysmon->lock);
+
+ if (chan->type == IIO_TEMP) {
+ ret = sysmon_osr_write_temp(sysmon, val);
+ if (ret)
+ return ret;
+ sysmon->temp_oversampling = val;
+ } else {
+ ret = sysmon_osr_write_supply(sysmon, val);
+ if (ret)
+ return ret;
+ sysmon->supply_oversampling = val;
+ }
+
+ return 0;
+}
+
+static int sysmon_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO)
+ return IIO_VAL_INT;
+
+ return -EINVAL;
+}
+
+static int sysmon_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type,
+ int *length, long mask)
+{
+ if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
+ return -EINVAL;
+
+ *vals = sysmon_oversampling_avail;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(sysmon_oversampling_avail);
+
+ return IIO_AVAIL_LIST;
+}
+
static int sysmon_read_label(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
char *label)
@@ -463,6 +601,9 @@ static int sysmon_read_label(struct iio_dev *indio_dev,
static const struct iio_info sysmon_iio_info = {
.read_raw = sysmon_read_raw,
+ .write_raw = sysmon_write_raw,
+ .write_raw_get_fmt = sysmon_write_raw_get_fmt,
+ .read_avail = sysmon_read_avail,
.read_label = sysmon_read_label,
.read_event_config = sysmon_read_event_config,
.write_event_config = sysmon_write_event_config,
@@ -754,6 +895,10 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int ir
.indexed = 1,
.address = reg,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.event_spec = irq > 0 ?
sysmon_supply_events : NULL,
.num_event_specs = irq > 0 ?
@@ -785,7 +930,11 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int ir
.address = SYSMON_TEMP_SAT_BASE +
(reg - 1) * SYSMON_REG_STRIDE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.datasheet_name = label,
};
}
@@ -832,6 +981,8 @@ int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap)
sysmon = iio_priv(indio_dev);
sysmon->regmap = regmap;
+ sysmon->temp_oversampling = 1;
+ sysmon->supply_oversampling = 1;
ret = devm_mutex_init(dev, &sysmon->lock);
if (ret)
diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h
index 9fe2793757a..bb9a75bf71c 100644
--- a/drivers/iio/adc/versal-sysmon.h
+++ b/drivers/iio/adc/versal-sysmon.h
@@ -23,11 +23,13 @@ struct regmap;
#define SYSMON_IMR 0x0048
#define SYSMON_IER 0x004C
#define SYSMON_IDR 0x0050
+#define SYSMON_CONFIG 0x0100
#define SYSMON_TEMP_MAX 0x1030
#define SYSMON_TEMP_MIN 0x1034
#define SYSMON_SUPPLY_BASE 0x1040
#define SYSMON_ALARM_FLAG 0x1018
#define SYSMON_ALARM_REG 0x1940
+#define SYSMON_SUPPLY_EN_AVG_BASE 0x1958
#define SYSMON_TEMP_TH_LOW 0x1970
#define SYSMON_TEMP_TH_UP 0x1974
#define SYSMON_SUPPLY_TH_LOW 0x1980
@@ -37,6 +39,7 @@ struct regmap;
#define SYSMON_TEMP_MAX_MAX 0x1F90
#define SYSMON_STATUS_RESET 0x1F94
#define SYSMON_TEMP_SAT_BASE 0x1FAC
+#define SYSMON_TEMP_EN_AVG_BASE 0x24B4
#define SYSMON_MAX_REG 0x24C0
/* NPI unlock value written to SYSMON_NPI_LOCK */
@@ -53,6 +56,16 @@ struct regmap;
/* ISR/IMR temperature alarm mask (bit 9) */
#define SYSMON_TEMP_INTR_MASK BIT(9)
+/* SYSMON_CONFIG: supply oversampling ratio */
+#define SYSMON_CONFIG_SUPPLY_OSR GENMASK(17, 14)
+
+/* SYSMON_CONFIG: temperature satellite oversampling ratio */
+#define SYSMON_CONFIG_TEMP_SAT_OSR GENMASK(27, 24)
+
+/* Per-channel averaging enable register counts */
+#define SYSMON_SUPPLY_EN_AVG_COUNT 5
+#define SYSMON_TEMP_EN_AVG_COUNT 2
+
/* Supply voltage conversion register fields */
#define SYSMON_MANTISSA_MASK GENMASK(15, 0)
#define SYSMON_FMT_MASK BIT(16)
@@ -77,6 +90,8 @@ struct regmap;
* @temp_mask: temperature interrupt configuration mask
* @temp_hysteresis: cached DEVICE_TEMP hysteresis in millicelsius
* @sysmon_unmask_work: re-enables events after alarm condition clears
+ * @temp_oversampling: current temp oversampling ratio
+ * @supply_oversampling: current supply oversampling ratio
*/
struct sysmon {
struct regmap *regmap;
@@ -96,6 +111,8 @@ struct sysmon {
unsigned int temp_mask;
int temp_hysteresis;
struct delayed_work sysmon_unmask_work;
+ unsigned int temp_oversampling;
+ unsigned int supply_oversampling;
};
int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap);
--
2.48.1
^ permalink raw reply related
* Re: [PATCH v16 00/10] arm64/riscv: Add support for crashkernel CMA reservation
From: Mike Rapoport @ 2026-06-17 18:48 UTC (permalink / raw)
To: Jinjie Ruan
Cc: corbet, skhan, catalin.marinas, will, chenhuacai, kernel, maddy,
mpe, npiggin, chleroy, pjw, palmer, aou, alex, tglx, mingo, bp,
dave.hansen, hpa, robh, saravanak, akpm, bhe, pasha.tatashin,
pratyush, ruirui.yang, rdunlap, peterz, feng.tang, dapeng1.mi,
kees, elver, kuba, lirongqing, ebiggers, paulmck, leitao, coxu,
Liam.Howlett, ryan.roberts, osandov, jbohac, cfsworks,
tangyouling, sourabhjain, ritesh.list, adityag, liaoyuanhong,
seanjc, fuqiang.wang, ardb, chenjiahao16, guoren, x86, linux-doc,
linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, devicetree, kexec
In-Reply-To: <20260608073459.3119290-1-ruanjinjie@huawei.com>
Hi Jinjie,
On Mon, Jun 08, 2026 at 03:34:49PM +0800, Jinjie Ruan wrote:
> The crash memory allocation, and the exclude of crashk_res, crashk_low_res
> and crashk_cma memory are almost identical across different architectures,
> This patch set handle them in crash core in a general way, which eliminate
> a lot of duplication code.
>
> And add support for crashkernel CMA reservation for arm64 and riscv.
>
> This patch set is rebased on v7.1-rc1.
Please rebase this set on v7.2-rc1 once that's out.
I'm going to queue it in the liveupdate tree then to expose to the wider
testing.
Meanwhile it would be great to chase riscv and x86 maintainers for acks :)
--
Sincerely yours,
Mike.
^ permalink raw reply
* [PATCH 0/9] Support 10-bit YUV422 and 8/10-bit YUV420 color format on DW HDMI QP
From: Cristian Ciocaltea @ 2026-06-17 18:51 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli
Cc: kernel, Andy Yan, dri-devel, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
This series extends the output color format support of the Rockchip DW
HDMI QP controller to cover 10-bit YUV 4:2:2 and 8/10-bit YUV 4:2:0.
Please note this has a runtime dependency on the Rockchip Samsung HDPTX
PHY driver bug fixes posted separately as [1]. While there is no build
dependency, those fixes are required to address clock rate calculation
and synchronization issues that arise when changing the color depth
(bpc) while keeping the modeline constant.
Patches 1, 2 & 9 improve VOP2 robustness on RK3588, helping recover from
exceptions and preventing random display output glitches observed when
switching modes that also change the color format, e.g. from RGB to YUV
4:2:0 and vice versa.
Patch 3 avoids an incorrect DCLK source switch for 10-bit YUV 4:2:2 by
forcing 8 bpc in the bandwidth check.
Patches 4-6 are independent cleanups/improvements and can be applied on
their own.
Patch 7 adds MEDIA_BUS_FMT_UYVY10_1X20 for 10-bit YUV 4:2:2 output,
configuring the PHY with 8 bpc. YUV 4:2:2 always transmits two 12-bit
components per pixel regardless of color depth, so from a clock-rate
perspective it is equivalent to three 8-bit RGB components.
Patch 8 advertises YUV 4:2:0 output, now that the bus-format and VOP2
support are in place.
Tested on Radxa ROCK 5B (RK3588) and Radxa ROCK 4D (RK3576), up to
4K@60Hz YUV 4:2:0 and 4K@30Hz RGB.
[1] https://lore.kernel.org/all/20260612-hdptx-clk-fixes-v4-0-ce5e1d456cda@collabora.com/
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
Cristian Ciocaltea (9):
dt-bindings: display: vop2: Add missing reset properties
drm/rockchip: vop2: Reset AXI and DCLK to improve robustness
drm/rockchip: vop2: Avoid DCLK source switch for 10-bit YUV422 output
drm/rockchip: vop2: Consolidate HDMI PHY PLL clock parent switch
drm/rockchip: vop2: Switch to enum vop_csc_format
drm/bridge: dw-hdmi-qp: Log resolution and refresh rate in atomic_enable()
drm/rockchip: dw_hdmi_qp: Support 10-bit YUV422 output format
drm/rockchip: dw_hdmi_qp: Enable YUV420 output format
arm64: dts: rockchip: Add RK3588 VOP2 resets
.../bindings/display/rockchip/rockchip-vop2.yaml | 42 ++++++++++
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 12 +++
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 10 ++-
drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c | 13 +++-
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 90 +++++++++++++++-------
drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 4 +
6 files changed, 141 insertions(+), 30 deletions(-)
---
base-commit: 2bfa2e7f19a106966ca97e0aaaad49e17f614cbb
change-id: 20260617-dw-hdmi-qp-yuv-2b0f7bb5ba81
^ permalink raw reply
* [PATCH 1/9] dt-bindings: display: vop2: Add missing reset properties
From: Cristian Ciocaltea @ 2026-06-17 18:51 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli
Cc: kernel, Andy Yan, dri-devel, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com>
Document the VOP2 resets corresponding to the AXI, AHB and DCLK_VP0..2
clocks, which are common to all supported SoCs, plus DCLK_VP3 which is
provided only on RK3588.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
.../bindings/display/rockchip/rockchip-vop2.yaml | 42 ++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
index 93da1fb9adc4..d3bc5380f910 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
@@ -82,6 +82,20 @@ properties:
- {}
- {}
+ resets:
+ minItems: 5
+ maxItems: 6
+
+ reset-names:
+ minItems: 5
+ items:
+ - const: axi
+ - const: ahb
+ - const: dclk_vp0
+ - const: dclk_vp1
+ - const: dclk_vp2
+ - const: dclk_vp3
+
rockchip,grf:
$ref: /schemas/types.yaml#/definitions/phandle
description:
@@ -148,6 +162,12 @@ allOf:
clock-names:
maxItems: 5
+ resets:
+ maxItems: 5
+
+ reset-names:
+ maxItems: 5
+
interrupts:
maxItems: 1
@@ -194,6 +214,12 @@ allOf:
- {}
- const: pll_hdmiphy0
+ resets:
+ maxItems: 5
+
+ reset-names:
+ maxItems: 5
+
interrupts:
minItems: 4
@@ -246,6 +272,12 @@ allOf:
- const: pll_hdmiphy0
- const: pll_hdmiphy1
+ resets:
+ minItems: 6
+
+ reset-names:
+ minItems: 6
+
interrupts:
maxItems: 1
@@ -289,6 +321,16 @@ examples:
"dclk_vp0",
"dclk_vp1",
"dclk_vp2";
+ resets = <&cru SRST_A_VOP>,
+ <&cru SRST_H_VOP>,
+ <&cru SRST_VOP0>,
+ <&cru SRST_VOP1>,
+ <&cru SRST_VOP2>;
+ reset-names = "axi",
+ "ahb",
+ "dclk_vp0",
+ "dclk_vp1",
+ "dclk_vp2";
power-domains = <&power RK3568_PD_VO>;
rockchip,grf = <&grf>;
iommus = <&vop_mmu>;
--
2.54.0
^ permalink raw reply related
* [PATCH 2/9] drm/rockchip: vop2: Reset AXI and DCLK to improve robustness
From: Cristian Ciocaltea @ 2026-06-17 18:51 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli
Cc: kernel, Andy Yan, dri-devel, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com>
Assert the AXI reset in the CRTC disable path, and the VP DCLK reset in
the enable path.
These resets are intended to leave the hardware in a clean state for the
next use, helping recover from exceptions such as IOMMU page faults, as
well as to prevent random display output glitches, such as a blank
image, observed when switching modes that also change the color format,
e.g. from RGB to YUV420 and vice versa.
For now this seems to affect only the RK3588, hence the resets are
optional and will be provided in the device tree for this SoC only.
Co-developed-by: Andy Yan <andy.yan@rock-chips.com>
Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 35 ++++++++++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 4 ++++
2 files changed, 39 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index 4cce3e336f5b..2833fb49ad81 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#include <linux/reset.h>
#include <linux/swab.h>
#include <drm/drm.h>
@@ -860,6 +861,26 @@ static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
return ret;
}
+static void vop2_clk_reset(struct vop2 *vop2, struct reset_control *rstc)
+{
+ int ret;
+
+ if (!rstc)
+ return;
+
+ ret = reset_control_assert(rstc);
+ if (ret < 0) {
+ drm_warn(vop2->drm, "failed to assert reset: %d\n", ret);
+ return;
+ }
+
+ udelay(10);
+
+ ret = reset_control_deassert(rstc);
+ if (ret < 0)
+ drm_err(vop2->drm, "failed to deassert reset: %d\n", ret);
+}
+
static void rk3588_vop2_power_domain_enable_all(struct vop2 *vop2)
{
u32 pd;
@@ -938,6 +959,8 @@ static void vop2_disable(struct vop2 *vop2)
{
rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
+ vop2_clk_reset(vop2, vop2->axi_rst);
+
pm_runtime_put_sync(vop2->dev);
regcache_drop_region(vop2->map, 0, vop2_regmap_config.max_register);
@@ -1948,6 +1971,8 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state);
+ vop2_clk_reset(vop2, vp->dclk_rst);
+
drm_crtc_vblank_on(crtc);
vop2_unlock(vop2);
@@ -2531,6 +2556,11 @@ static int vop2_create_crtcs(struct vop2 *vop2)
return dev_err_probe(drm->dev, PTR_ERR(vp->dclk),
"failed to get %s\n", dclk_name);
+ vp->dclk_rst = devm_reset_control_get_optional(vop2->dev, dclk_name);
+ if (IS_ERR(vp->dclk_rst))
+ return dev_err_probe(drm->dev, PTR_ERR(vp->dclk_rst),
+ "failed to get %s reset\n", dclk_name);
+
np = of_graph_get_remote_node(dev->of_node, i, -1);
if (!np) {
drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
@@ -2890,6 +2920,11 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
return dev_err_probe(drm->dev, PTR_ERR(vop2->pll_hdmiphy1),
"failed to get pll_hdmiphy1\n");
+ vop2->axi_rst = devm_reset_control_get_optional(vop2->dev, "axi");
+ if (IS_ERR(vop2->axi_rst))
+ return dev_err_probe(drm->dev, PTR_ERR(vop2->axi_rst),
+ "failed to get axi reset\n");
+
vop2->irq = platform_get_irq(pdev, 0);
if (vop2->irq < 0)
return dev_err_probe(drm->dev, vop2->irq, "cannot find irq for vop2\n");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
index ffcb39c130aa..14b437d2d088 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
@@ -165,6 +165,8 @@ enum vop2_win_regs {
VOP2_WIN_MAX_REG,
};
+struct reset_control;
+
struct vop2_regs_dump {
const char *name;
u32 base;
@@ -238,6 +240,7 @@ struct vop2_video_port {
struct vop2 *vop2;
struct clk *dclk;
struct clk *dclk_src;
+ struct reset_control *dclk_rst;
unsigned int id;
const struct vop2_video_port_data *data;
@@ -329,6 +332,7 @@ struct vop2 {
struct clk *pclk;
struct clk *pll_hdmiphy0;
struct clk *pll_hdmiphy1;
+ struct reset_control *axi_rst;
/* optional internal rgb encoder */
struct rockchip_rgb *rgb;
--
2.54.0
^ permalink raw reply related
* [PATCH 3/9] drm/rockchip: vop2: Avoid DCLK source switch for 10-bit YUV422 output
From: Cristian Ciocaltea @ 2026-06-17 18:51 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli
Cc: kernel, Andy Yan, dri-devel, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com>
Currently the color depth is always factored into the DCLK source
decision for HDMI output, which can break certain modes when operating
with depths greater than 8 bpc.
When the required transmission rate exceeds the 600 MHz limit of the
HDMI PHY PLL, e.g. for 4K@60Hz 10-bit RGB output, VOP2 will normally
fall back to using the less accurate system CRU as a DCLK source,
assuming HDMI 2.1 FRL is supported by the pipeline, otherwise the mode
will be rejected. For YUV420 output format this never happens, as it
uses half of the RGB bandwidth, hence the rate remains within the PHY
PLL limits.
On the other hand, YUV422 always transmits two 12-bit components per
clock cycle, regardless of the color depth, which from a clock-rate
perspective is equivalent to three 8-bit RGB components. For example,
4K@60Hz 10-bit YUV422 requires the same bandwidth as 4K@60Hz 8-bit RGB,
typically 594 MHz. However, VOP2 wrongly assumes it needs 742.5 MHz
(594 * 10 / 8) and ends up switching the DCLK source.
As a consequence, the modes requiring uncommon pixel clocks, such as
those corresponding to fractional refresh rates, will fail. An example
is 3840x2160@59.94Hz, which would likely rely on the 593.407 MHz clock
rate unsupported by the system CRU.
Note this only affects YUV422 with color depths greater than 8 bpc; for
8-bit YUV422 the 8/bpc factor is unity and the bandwidth check is
already correct.
Prevent the incorrect switches of DCLK source to system CRU for YUV422
output format by forcing 8 bpc when checking the bandwidth.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index 2833fb49ad81..17d21e08ad97 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -1922,8 +1922,17 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
* to 4K@60Hz, if available, otherwise keep using the system CRU.
*/
if (vop2->pll_hdmiphy0 || vop2->pll_hdmiphy1) {
- unsigned long max_dclk = DIV_ROUND_CLOSEST_ULL(VOP2_MAX_DCLK_RATE * 8,
- vcstate->output_bpc);
+ /*
+ * YUV422 always transmits two 12-bit components per clock
+ * cycle, regardless of the color depth, which from a rate
+ * perspective is equivalent to three 8-bit RGB components.
+ * Force 8 bpc here so the bandwidth check reflects the actual
+ * TMDS rate and avoids an unnecessary DCLK source switch.
+ */
+ unsigned int bpc = vcstate->output_mode == ROCKCHIP_OUT_MODE_YUV422 ?
+ 8 : vcstate->output_bpc;
+ unsigned long max_dclk = DIV_ROUND_CLOSEST_ULL(VOP2_MAX_DCLK_RATE * 8, bpc);
+
if (clock <= max_dclk) {
drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
--
2.54.0
^ permalink raw reply related
* [PATCH 4/9] drm/rockchip: vop2: Consolidate HDMI PHY PLL clock parent switch
From: Cristian Ciocaltea @ 2026-06-17 18:51 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli
Cc: kernel, Andy Yan, dri-devel, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com>
The DCLK parent switch logic for HDMI0 and HDMI1 PHY PLLs was
duplicated, with each endpoint repeating the same clk_get_parent(),
clk_set_parent() and error handling calls.
Refactor this by first selecting the appropriate PHY PLL clock handle
based on the active HDMI endpoint, then performing the parent switch in
a single shared code path.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 36 +++++++++++-----------------
1 file changed, 14 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index 17d21e08ad97..df475173dc8e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -1932,42 +1932,34 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
unsigned int bpc = vcstate->output_mode == ROCKCHIP_OUT_MODE_YUV422 ?
8 : vcstate->output_bpc;
unsigned long max_dclk = DIV_ROUND_CLOSEST_ULL(VOP2_MAX_DCLK_RATE * 8, bpc);
+ struct clk *pll_hdmiphy = NULL;
if (clock <= max_dclk) {
drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI0) {
- if (!vop2->pll_hdmiphy0)
- break;
-
- if (!vp->dclk_src)
- vp->dclk_src = clk_get_parent(vp->dclk);
-
- ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy0);
- if (ret < 0)
- drm_warn(vop2->drm,
- "Could not switch to HDMI0 PHY PLL: %d\n",
- ret);
+ pll_hdmiphy = vop2->pll_hdmiphy0;
break;
}
if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI1) {
- if (!vop2->pll_hdmiphy1)
- break;
-
- if (!vp->dclk_src)
- vp->dclk_src = clk_get_parent(vp->dclk);
-
- ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy1);
- if (ret < 0)
- drm_warn(vop2->drm,
- "Could not switch to HDMI1 PHY PLL: %d\n",
- ret);
+ pll_hdmiphy = vop2->pll_hdmiphy1;
break;
}
}
}
+
+ if (pll_hdmiphy) {
+ if (!vp->dclk_src)
+ vp->dclk_src = clk_get_parent(vp->dclk);
+
+ ret = clk_set_parent(vp->dclk, pll_hdmiphy);
+ if (ret < 0)
+ drm_warn(vop2->drm,
+ "Failed to switch DCLK to HDMI PHY PLL: %d\n",
+ ret);
+ }
}
clk_set_rate(vp->dclk, clock);
--
2.54.0
^ permalink raw reply related
* [PATCH 6/9] drm/bridge: dw-hdmi-qp: Log resolution and refresh rate in atomic_enable()
From: Cristian Ciocaltea @ 2026-06-17 18:51 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli
Cc: kernel, Andy Yan, dri-devel, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com>
The debug entry in the HDMI branch of dw_hdmi_qp_bridge_atomic_enable()
previously printed the literal string 'HDMI' as the mode field, giving
no information about the actual display timing being configured.
Extend it to include the active resolution and refresh rate by
retrieving the CRTC mode from the incoming atomic state:
dw_hdmi_qp_bridge_atomic_enable mode=1920x1080@50Hz fmt=RGB rate=185625000 bpc=10
This makes the log line self-contained and directly useful when
debugging mode-setting issues, format negotiation, or TMDS rate
mismatches without having to cross-reference a separate mode dump.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
index 13fddd5ebc82..d73307ac4232 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -752,6 +752,8 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
struct drm_connector_state *conn_state;
+ const struct drm_display_mode *mode;
+ struct drm_crtc_state *crtc_state;
struct drm_connector *connector;
unsigned int op_mode;
@@ -764,9 +766,15 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
return;
if (connector->display_info.is_hdmi) {
- dev_dbg(hdmi->dev, "%s mode=HDMI %s rate=%llu bpc=%u\n", __func__,
+ crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+ mode = &crtc_state->mode;
+ dev_dbg(hdmi->dev,
+ "%s mode=HDMI %ux%u@%uHz fmt=%s rate=%llu bpc=%u\n",
+ __func__, mode->hdisplay, mode->vdisplay,
+ drm_mode_vrefresh(mode),
drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format),
conn_state->hdmi.tmds_char_rate, conn_state->hdmi.output_bpc);
+
op_mode = 0;
hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate;
} else {
--
2.54.0
^ permalink raw reply related
* [PATCH 5/9] drm/rockchip: vop2: Switch to enum vop_csc_format
From: Cristian Ciocaltea @ 2026-06-17 18:51 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli
Cc: kernel, Andy Yan, dri-devel, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com>
Improve code readability in vop2_setup_csc_mode() by using enum
vop_csc_format for the csc_mode variable, as well as for the return type
of the vop2_convert_csc_mode() helper, which already returns CSC_*
enumerators.
While at it, replace the nonsensical 'csc_mode = false' assignment in
the no-conversion branch with the equivalent CSC_BT601L, which carries
the same value (0) but is type-correct.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index df475173dc8e..e0d6e42fedb1 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -648,7 +648,7 @@ static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win,
}
}
-static int vop2_convert_csc_mode(int csc_mode)
+static enum vop_csc_format vop2_convert_csc_mode(int csc_mode)
{
switch (csc_mode) {
case V4L2_COLORSPACE_SMPTE170M:
@@ -711,7 +711,7 @@ static void vop2_setup_csc_mode(struct vop2_video_port *vp,
int input_csc = V4L2_COLORSPACE_DEFAULT;
int output_csc = vcstate->color_space;
bool r2y_en, y2r_en;
- int csc_mode;
+ enum vop_csc_format csc_mode;
if (is_input_yuv && !is_output_yuv) {
y2r_en = true;
@@ -724,7 +724,7 @@ static void vop2_setup_csc_mode(struct vop2_video_port *vp,
} else {
y2r_en = false;
r2y_en = false;
- csc_mode = false;
+ csc_mode = CSC_BT601L;
}
vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en);
--
2.54.0
^ permalink raw reply related
* [PATCH 7/9] drm/rockchip: dw_hdmi_qp: Support 10-bit YUV422 output format
From: Cristian Ciocaltea @ 2026-06-17 18:52 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli
Cc: kernel, Andy Yan, dri-devel, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com>
Rockchip DW HDMI QP encoder supports YUV 4:2:2 output through
ROCKCHIP_OUT_MODE_YUV422, but was limited to 8-bit depth via
MEDIA_BUS_FMT_UYVY8_1X16. Add support for its 10-bit counterpart
MEDIA_BUS_FMT_UYVY10_1X20, which carries two 10-bit components per clock
cycle on a 20-bit wide bus.
YUV 4:2:2 always transmits two 12-bit components per pixel, regardless
of the color depth. From a clock-rate perspective this is equivalent to
three 8-bit RGB components, so configure the HDMI PHY with 8 bpc when
YUV 4:2:2 is in use to keep its output clock aligned with the TMDS
character rate. Otherwise the PHY PLL output would be scaled by bpc/8
for higher color depths, producing a clock rate that confuses downstream
consumers such as the VOP2 display controller.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
index 815f9ea7bcbe..3a1c027aa90b 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -216,6 +216,7 @@ dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
break;
case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
s->output_mode = ROCKCHIP_OUT_MODE_YUV422;
break;
case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
@@ -227,7 +228,14 @@ dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
}
phy_cfg.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate;
- phy_cfg.hdmi.bpc = conn_state->hdmi.output_bpc;
+ /*
+ * YUV422 always transmits two 12-bit components per clock cycle,
+ * regardless of the color depth, which from a rate perspective is
+ * equivalent to three 8-bit RGB components. Force 8 bpc here to
+ * keep the PHY PLL output aligned with the TMDS character rate.
+ */
+ phy_cfg.hdmi.bpc = (s->output_mode == ROCKCHIP_OUT_MODE_YUV422 ?
+ 8 : conn_state->hdmi.output_bpc);
ret = phy_configure(hdmi->phy, &phy_cfg);
if (!ret) {
--
2.54.0
^ permalink raw reply related
* [PATCH 8/9] drm/rockchip: dw_hdmi_qp: Enable YUV420 output format
From: Cristian Ciocaltea @ 2026-06-17 18:52 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli
Cc: kernel, Andy Yan, dri-devel, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com>
Both RK3576 and RK3588 SoCs are capable of driving the YUV420 output
color format, and the required bus-format handling and VOP2 support are
already in place. Advertise it via the platform supported formats so
the HDMI core can select it.
YUV420 halves the TMDS bandwidth compared to RGB/YUV444, which enables
high-resolution modes such as 4K@60Hz on links that cannot otherwise
carry the full-bandwidth signal.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
index 3a1c027aa90b..e29522afc6f0 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -612,7 +612,8 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
plat_data.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422);
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
encoder = &hdmi->encoder.encoder;
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
--
2.54.0
^ permalink raw reply related
* [PATCH 9/9] arm64: dts: rockchip: Add RK3588 VOP2 resets
From: Cristian Ciocaltea @ 2026-06-17 18:52 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli
Cc: kernel, Andy Yan, dri-devel, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com>
Add the missing reset properties to VOP2 on RK3588.
Co-developed-by: Andy Yan <andy.yan@rock-chips.com>
Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
index 4fb8888c281c..1404cd7ecf02 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
@@ -1453,6 +1453,18 @@ vop: vop@fdd90000 {
"pll_hdmiphy0";
iommus = <&vop_mmu>;
power-domains = <&power RK3588_PD_VOP>;
+ resets = <&cru SRST_A_VOP>,
+ <&cru SRST_H_VOP>,
+ <&cru SRST_D_VOP0>,
+ <&cru SRST_D_VOP1>,
+ <&cru SRST_D_VOP2>,
+ <&cru SRST_D_VOP3>;
+ reset-names = "axi",
+ "ahb",
+ "dclk_vp0",
+ "dclk_vp1",
+ "dclk_vp2",
+ "dclk_vp3";
rockchip,grf = <&sys_grf>;
rockchip,vop-grf = <&vop_grf>;
rockchip,vo1-grf = <&vo1_grf>;
--
2.54.0
^ permalink raw reply related
* [PATCH v4 1/2] dt-bindings: arm: xen: Convert to DT schema
From: Tejas Mutalikdesai @ 2026-06-17 18:55 UTC (permalink / raw)
To: devicetree; +Cc: robh, krzk+dt, conor+dt, sstabellini, Tejas Mutalikdesai
Convert the Xen ARM device tree binding documentation from the legacy
plain-text format (Documentation/devicetree/bindings/arm/xen.txt) to
the DT schema format, as required by the modern DT binding process.
Signed-off-by: Tejas Mutalikdesai <tejasmutalikdesai@gmail.com>
---
Changes since v3:
- Split enlighten.c comment fix into a separate patch (2/2)
Changes since v2:
- Replace 'YAML schema' with 'DT schema' in the commit description
- Drop unnecessary '|' block scalars from description fields that do
not need to preserve literal formatting
- Fix unit_address_vs_reg warning: replace $nodename const with a
pattern requiring a unit address; update example to hypervisor@b0000000
and drop the root node, GIC, and interrupt-parent
Documentation/devicetree/bindings/arm/xen.txt | 62 ----------
.../devicetree/bindings/arm/xen.yaml | 109 ++++++++++++++++++
2 files changed, 109 insertions(+), 62 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/arm/xen.txt
create mode 100644 Documentation/devicetree/bindings/arm/xen.yaml
diff --git a/Documentation/devicetree/bindings/arm/xen.txt b/Documentation/devicetree/bindings/arm/xen.txt
deleted file mode 100644
index f925290d4641..000000000000
--- a/Documentation/devicetree/bindings/arm/xen.txt
+++ /dev/null
@@ -1,62 +0,0 @@
-* Xen hypervisor device tree bindings
-
-Xen ARM virtual platforms shall have a top-level "hypervisor" node with
-the following properties:
-
-- compatible:
- compatible = "xen,xen-<version>", "xen,xen";
- where <version> is the version of the Xen ABI of the platform.
-
-- reg: specifies the base physical address and size of the regions in memory
- where the special resources should be mapped to, using an HYPERVISOR_memory_op
- hypercall.
- Region 0 is reserved for mapping grant table, it must be always present.
- The memory region is large enough to map the whole grant table (it is larger
- or equal to gnttab_max_grant_frames()).
- Regions 1...N are extended regions (unused address space) for mapping foreign
- GFNs and grants, they might be absent if there is nothing to expose.
-
-- interrupts: the interrupt used by Xen to inject event notifications.
- A GIC node is also required.
-
-To support UEFI on Xen ARM virtual platforms, Xen populates the FDT "uefi" node
-under /hypervisor with following parameters:
-
-________________________________________________________________________________
-Name | Size | Description
-================================================================================
-xen,uefi-system-table | 64-bit | Guest physical address of the UEFI System
- | | Table.
---------------------------------------------------------------------------------
-xen,uefi-mmap-start | 64-bit | Guest physical address of the UEFI memory
- | | map.
---------------------------------------------------------------------------------
-xen,uefi-mmap-size | 32-bit | Size in bytes of the UEFI memory map
- | | pointed to in previous entry.
---------------------------------------------------------------------------------
-xen,uefi-mmap-desc-size | 32-bit | Size in bytes of each entry in the UEFI
- | | memory map.
---------------------------------------------------------------------------------
-xen,uefi-mmap-desc-ver | 32-bit | Version of the mmap descriptor format.
---------------------------------------------------------------------------------
-
-Example (assuming #address-cells = <2> and #size-cells = <2>):
-
-hypervisor {
- compatible = "xen,xen-4.3", "xen,xen";
- reg = <0 0xb0000000 0 0x20000>;
- interrupts = <1 15 0xf08>;
- uefi {
- xen,uefi-system-table = <0xXXXXXXXX>;
- xen,uefi-mmap-start = <0xXXXXXXXX>;
- xen,uefi-mmap-size = <0xXXXXXXXX>;
- xen,uefi-mmap-desc-size = <0xXXXXXXXX>;
- xen,uefi-mmap-desc-ver = <0xXXXXXXXX>;
- };
-};
-
-The format and meaning of the "xen,uefi-*" parameters are similar to those in
-Documentation/arch/arm/uefi.rst, which are provided by the regular UEFI stub. However
-they differ because they are provided by the Xen hypervisor, together with a set
-of UEFI runtime services implemented via hypercalls, see
-http://xenbits.xen.org/docs/unstable/hypercall/x86_64/include,public,platform.h.html.
diff --git a/Documentation/devicetree/bindings/arm/xen.yaml b/Documentation/devicetree/bindings/arm/xen.yaml
new file mode 100644
index 000000000000..a22e950566c2
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/xen.yaml
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/xen.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Xen hypervisor
+
+maintainers:
+ - Stefano Stabellini <sstabellini@kernel.org>
+
+description:
+ Xen ARM virtual platforms shall have a top-level "hypervisor" node with
+ the properties defined below.
+
+properties:
+ $nodename:
+ pattern: "^hypervisor@[0-9a-f]+$"
+
+ compatible:
+ description:
+ Specifies the Xen hypervisor. The version of the Xen ABI is encoded
+ in the first item as "xen,xen-<version>", followed by the generic
+ "xen,xen" string.
+ items:
+ - pattern: "^xen,xen-[0-9]+\\.[0-9]+$"
+ - const: xen,xen
+
+ reg:
+ description: |
+ Base physical address and size of the regions in memory where special
+ resources should be mapped to, using a HYPERVISOR_memory_op hypercall.
+
+ Region 0 is reserved for mapping the grant table and must always be
+ present. The memory region must be large enough to map the whole grant
+ table (it is larger or equal to gnttab_max_grant_frames()).
+
+ Regions 1...N are extended regions (unused address space) for mapping
+ foreign GFNs and grants. They might be absent if there is nothing to
+ expose.
+ minItems: 1
+
+ interrupts:
+ description:
+ The interrupt used by Xen to inject event notifications.
+ A GIC node is also required.
+ maxItems: 1
+
+ uefi:
+ type: object
+ description:
+ Node populated by Xen to support UEFI on Xen ARM virtual platforms.
+ The format and meaning of the "xen,uefi-*" parameters are similar to
+ those in Documentation/arch/arm/uefi.rst, but are provided by the Xen
+ hypervisor together with a set of UEFI runtime services implemented via
+ hypercalls.
+ properties:
+ xen,uefi-system-table:
+ description: Guest physical address of the UEFI System Table.
+ $ref: /schemas/types.yaml#/definitions/uint64
+
+ xen,uefi-mmap-start:
+ description: Guest physical address of the UEFI memory map.
+ $ref: /schemas/types.yaml#/definitions/uint64
+
+ xen,uefi-mmap-size:
+ description: Size in bytes of the UEFI memory map pointed to by xen,uefi-mmap-start.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ xen,uefi-mmap-desc-size:
+ description: Size in bytes of each entry in the UEFI memory map.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ xen,uefi-mmap-desc-ver:
+ description: Version of the mmap descriptor format.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ required:
+ - xen,uefi-system-table
+ - xen,uefi-mmap-start
+ - xen,uefi-mmap-size
+ - xen,uefi-mmap-desc-size
+ - xen,uefi-mmap-desc-ver
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ hypervisor@b0000000 {
+ compatible = "xen,xen-4.3", "xen,xen";
+ reg = <0xb0000000 0x20000>;
+ interrupts = <1 15 0xf08>;
+
+ uefi {
+ xen,uefi-system-table = /bits/ 64 <0x1301415>;
+ xen,uefi-mmap-start = /bits/ 64 <0x7591400>;
+ xen,uefi-mmap-size = <0x1800>;
+ xen,uefi-mmap-desc-size = <0x30>;
+ xen,uefi-mmap-desc-ver = <1>;
+ };
+ };
+...
--
2.54.0
^ permalink raw reply related
* [PATCH v4 2/2] arm/xen: Update DT binding documentation reference
From: Tejas Mutalikdesai @ 2026-06-17 18:55 UTC (permalink / raw)
To: devicetree; +Cc: robh, krzk+dt, conor+dt, sstabellini, Tejas Mutalikdesai
In-Reply-To: <20260617185518.43500-1-tejasmutalikdesai@gmail.com>
Update the comment in xen_early_init() to reference the new xen.yaml
schema after xen.txt was converted to the DT schema format.
Signed-off-by: Tejas Mutalikdesai <tejasmutalikdesai@gmail.com>
---
New in v4: split out from 1/2 to keep the DT binding conversion separate
from the driver comment fix.
arch/arm/xen/enlighten.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
index 25a0ce3b4584..0b7b7e3417e3 100644
--- a/arch/arm/xen/enlighten.c
+++ b/arch/arm/xen/enlighten.c
@@ -251,7 +251,7 @@ static int __init fdt_find_hyper_node(unsigned long node, const char *uname,
}
/*
- * see Documentation/devicetree/bindings/arm/xen.txt for the
+ * see Documentation/devicetree/bindings/arm/xen.yaml for the
* documentation of the Xen Device Tree format.
*/
void __init xen_early_init(void)
--
2.54.0
^ permalink raw reply related
* Re: [GIT PULL] Devicetree updates for v7.2
From: pr-tracker-bot @ 2026-06-17 19:14 UTC (permalink / raw)
To: Rob Herring
Cc: Linus Torvalds, Saravana Kannan, Krzysztof Kozlowski,
Conor Dooley, linux-kernel, devicetree
In-Reply-To: <20260616213229.GA2761475-robh@kernel.org>
The pull request you sent on Tue, 16 Jun 2026 16:32:29 -0500:
> ssh://git@gitolite.kernel.org/pub/scm/linux/kernel/git/robh/linux.git tags/devicetree-for-7.2
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/09fb6892f34abdb6d9b50ae7337b7b7b56dc82d6
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* Re: [PATCH] dma-iommu: Introduce API to reserve IOVA regions for dynamically created devices
From: Jason Gunthorpe @ 2026-06-17 19:40 UTC (permalink / raw)
To: Vishnu Reddy, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
devicetree
Cc: Vikash Garodia, Robin Murphy, joro, will, m.szyprowski, iommu,
linux-kernel, dikshita.agarwal
In-Reply-To: <dcc0d02e-91fb-ddbe-399a-0191bab94c4f@oss.qualcomm.com>
On Mon, Jun 15, 2026 at 11:21:45PM +0530, Vishnu Reddy wrote:
> Hi Jason,
>
> Here, the parent node does not have an iommus property — it only has iommu-map,
> like example below:
>
> iommu-map = <0x0 &apps_smmu 0x1940 0x0 0x1>, /* function_id 0 → SID 0x1940 */
> <0x1 &apps_smmu 0x1943 0x0 0x1>, /* function_id 1 → SID 0x1943 */
> <0x2 &apps_smmu 0x1944 0x0 0x1>; /* function_id 2 → SID 0x1944 */
>
> When the parent device is probed, of_dma_configure() is called, which
> internally invokes of_dma_configure_id() with NULL as the function ID. Since
> there is no iommus entry, no stream ID gets mapped to the parent device.
Sure, but it still did of_dma_configure on the parent..
> The child devices are created at runtime and have no of_node of their own. The
> only place the iommu-map property exists is on the parent's of_node. So when
> configuring a child device, we pass the parent's of_node along with the child's
> specific function_id — this is how of_dma_configure_id() finds and maps the
> correct stream ID to the child device only.
The whole thing seems like the wrong way to use DT to me. Having an
iommu-map in the dt that no iommus property ever uses strikes me as
abusive.
Then hard coding the ID table and manually creating the missing
struct devices in C code is a throw back to board files :(
Of course it doesn't fully work, it was never intended to be used like
this.
Why the resistance to doing DT properly with actual iommus and dma
ranges for each and every stream your device needs?
Jason
^ permalink raw reply
* Re: [PATCH v2 1/5] dt-bindings: watchdog: mediatek: Add MT8127
From: Guenter Roeck @ 2026-06-17 19:46 UTC (permalink / raw)
To: zkh1, Matthias Brugger, AngeloGioacchino Del Regno, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sean Wang, Wim Van Sebroeck
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, devicetree,
linux-watchdog, Krzysztof Kozlowski
In-Reply-To: <20260617-mt8127-amazon-ford-basic-v2-1-6859e29e72a8@proton.me>
On 6/17/26 06:36, Zakariya Hadrami via B4 Relay wrote:
> From: Zakariya Hadrami <zkh1@proton.me>
>
> Add entry for MT8127 SoC's watchdog which is compatible with MT6589's
> one.
>
> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> Signed-off-by: Zakariya Hadrami <zkh1@proton.me>
I don't know if I am supposed to pick up this patch. I assume it will
be picked up with the rest of the series, so
Acked-by: Guenter Roeck <linux@roeck-us.net>
for that.
Guenter
> ---
> Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml b/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml
> index 953629cb9558..e6e4546da0aa 100644
> --- a/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml
> +++ b/Documentation/devicetree/bindings/watchdog/mediatek,mtk-wdt.yaml
> @@ -40,6 +40,7 @@ properties:
> - mediatek,mt7622-wdt
> - mediatek,mt7623-wdt
> - mediatek,mt7629-wdt
> + - mediatek,mt8127-wdt
> - mediatek,mt8173-wdt
> - mediatek,mt8188-wdt
> - mediatek,mt8189-wdt
>
^ permalink raw reply
* [PATCH v5 0/8] media: add new API simple 1to1 subdev register and add imx parallel camera support
From: Frank.Li @ 2026-06-17 19:50 UTC (permalink / raw)
To: Sakari Ailus, Mauro Carvalho Chehab, Michael Riesch,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Purism Kernel Team, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: linux-media, linux-kernel, imx, devicetree, linux-arm-kernel,
Alice Yuan, Robert Chiras, Zhipeng Wang, Krzysztof Kozlowski
This patches base on previous' thread "media: imx8qxp: add parallel camera
support".
Add new API media_async_register_subdev_1to1() to simple 1to1 subdev
register.
Many V4L2 subdev drivers implement the same registration and media pad
setup logic for simple pipelines consisting of a single sink pad and a
single source pad. As a result, the same boilerplate code is duplicated
across multiple drivers.
Introduce a common helper library for 1-to-1 subdevs to encapsulate the
registration, media entity initialization, and cleanup paths. Drivers
can embed a struct v4l2_subdev_1to1 instance and use the provided helper
APIs instead of open-coding the setup sequence.
This reduces code duplication and simplifies the implementation of
simple bridge and converter drivers.
In 1TO1 subdev driver:
struct your_device {
v4l2_subdev_1to1 sd_1to1; // instead of v4l2_subdev sd;
...
}
...
your_device_probe()
{
v4l2_subdev_init(&sd_1to1->sd, &dw_mipi_csi2rx_ops);
...
return media_async_register_subdev_1to1(sd_1to1);
}
...
your_device_remove()
{
media_async_subdev_1to1_cleanup();
}
This API help reduce over line duplcated code in synopsys/dw-mipi-csi2rx.c.
And use this API at imx8's parallel CPI driver, which over 90% code now
hardware related.
And also benefit on going pix format patch
https://lore.kernel.org/imx/20260525-csi_formatter-v8-0-6b646231224b@oss.nxp.com/
It will also reduce missed media_entity_cleanup() problem at some error path
https://lore.kernel.org/linux-media/20260614202835.11977-15-birenpandya@gmail.com/
Previous do partial simpilfy at
https://lore.kernel.org/imx/aaisdJSsFE5-PLx1@lizhi-Precision-Tower-5810/
To: Sakari Ailus <sakari.ailus@linux.intel.com>
To: Mauro Carvalho Chehab <mchehab@kernel.org>
To: Michael Riesch <michael.riesch@collabora.com>
To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: Frank Li <Frank.Li@nxp.com>
To: Martin Kepplinger-Novakovic <martink@posteo.de>
To: Rui Miguel Silva <rmfrfs@gmail.com>
To: Purism Kernel Team <kernel@puri.sm>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Sascha Hauer <s.hauer@pengutronix.de>
To: Pengutronix Kernel Team <kernel@pengutronix.de>
To: Fabio Estevam <festevam@gmail.com>
Cc: linux-media@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: imx@lists.linux.dev
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Changes in v5:
- Add media_async_register_subdev_1to1() to simple code.
- Link to v4: https://lore.kernel.org/r/20250729-imx8qxp_pcam-v4-0-4dfca4ed2f87@nxp.com
Changes in v4:
- remove imx93 driver support since have not camera sensor module to do test now.
Add it later
- Add new patch
media: v4l2-common: Add helper function v4l_get_required_align_by_bpp()
- See each patche's change log for detail.
- Link to v3: https://lore.kernel.org/r/20250708-imx8qxp_pcam-v3-0-c8533e405df1@nxp.com
Changes in v3:
- replace CSI with CPI.
- detail change see each patch's change logs
- Link to v2: https://lore.kernel.org/r/20250703-imx8qxp_pcam-v2-0-188be85f06f1@nxp.com
Changes in v2:
- remove patch media: nxp: isi: add support for UYVY8_2X8 and YUYV8_2X8 bus codes
because pcif controller convert 2x8 to 1x16 to match isi's input
- rename comaptible string to fsl,imx8qxp-pcif
- See each patches's change log for detail
- Link to v1: https://lore.kernel.org/r/20250630-imx8qxp_pcam-v1-0-eccd38d99201@nxp.com
---
Alice Yuan (2):
dt-bindings: media: add i.MX parallel CPI support
media: nxp: add V4L2 subdev driver for camera parallel interface (CPI)
Frank Li (6):
media: v4l2-fwnode: Extract common helper __v4l2_async_register_subdev_fwnode()
media: v4l2-fwnode: Add common helper library for 1-to-1 subdev registration
media: synopsys: Use v4l2_subdev_get_frame_desc_passthrough()
media: synopsys: Use V4L2 1-to-1 subdev helpers
arm64: dts: imx8: add camera parallel interface (CPI) node
arm64: dts: imx8qxp-mek: add parallel ov5640 camera support
.../devicetree/bindings/media/fsl,imx93-pcif.yaml | 126 +++++
MAINTAINERS | 2 +
arch/arm64/boot/dts/freescale/Makefile | 3 +
arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi | 13 +
.../boot/dts/freescale/imx8qxp-mek-ov5640-cpi.dtso | 83 +++
arch/arm64/boot/dts/freescale/imx8qxp-ss-img.dtsi | 27 +
drivers/media/platform/nxp/Kconfig | 12 +
drivers/media/platform/nxp/Makefile | 1 +
drivers/media/platform/nxp/imx-parallel-cpi.c | 614 +++++++++++++++++++++
drivers/media/platform/synopsys/Kconfig | 1 +
drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 194 +------
drivers/media/v4l2-core/Kconfig | 3 +
drivers/media/v4l2-core/Makefile | 1 +
drivers/media/v4l2-core/v4l2-1to1.c | 117 ++++
drivers/media/v4l2-core/v4l2-fwnode.c | 17 +-
include/media/v4l2-async.h | 6 +
include/media/v4l2-device-1to1.h | 72 +++
17 files changed, 1123 insertions(+), 169 deletions(-)
---
base-commit: 7193e493653c9c91b4be159cd919924ec6ad2392
change-id: 20250626-imx8qxp_pcam-d851238343c3
Best regards,
--
Frank Li <Frank.Li@nxp.com>
^ 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