* Re: [PATCH 2/2] hwmon: raspberrypi: Add voltage input support
From: Guenter Roeck @ 2026-05-16 17:13 UTC (permalink / raw)
To: Shubham Chakraborty, Florian Fainelli,
Broadcom internal kernel review list, linux-hwmon,
linux-rpi-kernel, linux-arm-kernel, linux-kernel
In-Reply-To: <20260516164407.25255-2-chakrabortyshubham66@gmail.com>
On 5/16/26 09:44, Shubham Chakraborty wrote:
> Extend the raspberrypi-hwmon driver to expose firmware-provided
> voltage measurements through the hwmon subsystem.
>
> The driver now exports the following voltage inputs:
>
> - in0_input (core)
> - in1_input (sdram_c)
> - in2_input (sdram_i)
> - in3_input (sdram_p)
>
> Voltage values returned by firmware are converted from microvolts
> to millivolts as expected by the hwmon subsystem.
>
> The existing undervoltage sticky alarm handling is preserved and
> associated with the first voltage channel.
>
> Tested in -
> - Raspberry Pi 3b+ (Linux raspberrypi 6.12.75+rpt-rpi-v8 #1 SMP PREEMPT
> Debian 1:6.12.75-1+rpt1 (2026-03-11) aarch64 GNU/Linux)
>
> Signed-off-by: Shubham Chakraborty <chakrabortyshubham66@gmail.com>
I wasn't copied on patch 1/2, so I have no idea what is in there
and how it is related to this patch. Either way it seems unlikely that
the new functionality is supported by all versions of Raspberry Pi supported
by this driver. Just returning an error when the user tries to read a sensor
that is not supported is unacceptable. This needs either evidence that the
sensors are supported by all board variants and firmware versions supported
by this driver, or the is_visible function needs to selectively enable the
supported sensors.
> ---
> drivers/hwmon/raspberrypi-hwmon.c | 112 +++++++++++++++++++++++++++++-
Documentation/hwmon/raspberrypi-hwmon.rst needs to be updated as well.
> 1 file changed, 109 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c
> index a2938881ccd2..c73a970db025 100644
> --- a/drivers/hwmon/raspberrypi-hwmon.c
> +++ b/drivers/hwmon/raspberrypi-hwmon.c
> @@ -5,6 +5,7 @@
> * Based on firmware/raspberrypi.c by Noralf Trønnes
> *
> * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com>
> + * Copyright (C) 2026 Shubham Chakraborty <chakrabortyshubham66@gmail.com>
> */
> #include <linux/device.h>
> #include <linux/devm-helpers.h>
> @@ -18,6 +19,11 @@
>
> #define UNDERVOLTAGE_STICKY_BIT BIT(16)
>
> +struct rpi_firmware_get_value {
> + __le32 id;
> + __le32 val;
> +} __packed;
> +
> struct rpi_hwmon_data {
> struct device *hwmon_dev;
> struct rpi_firmware *fw;
> @@ -56,6 +62,23 @@ static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data)
> hwmon_notify_event(data->hwmon_dev, hwmon_in, hwmon_in_lcrit_alarm, 0);
> }
>
> +static int rpi_firmware_get_voltage(struct rpi_hwmon_data *data, u32 id,
> + long *val)
> +{
> + struct rpi_firmware_get_value packet;
> + int ret;
> +
> + packet.id = cpu_to_le32(id);
> + packet.val = 0;
> + ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_VOLTAGE,
> + &packet, sizeof(packet));
> + if (ret)
> + return ret;
> +
> + *val = le32_to_cpu(packet.val) / 1000;
> + return 0;
> +}
> +
> static void get_values_poll(struct work_struct *work)
> {
> struct rpi_hwmon_data *data;
> @@ -77,19 +100,101 @@ static int rpi_read(struct device *dev, enum hwmon_sensor_types type,
> {
> struct rpi_hwmon_data *data = dev_get_drvdata(dev);
>
> - *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
> + if (type == hwmon_in) {
> + switch (attr) {
> + case hwmon_in_input:
> + switch (channel) {
> + case 0:
> + return rpi_firmware_get_voltage(data,
> + RPI_FIRMWARE_VOLT_ID_CORE,
> + val);
> + case 1:
> + return rpi_firmware_get_voltage(data,
> + RPI_FIRMWARE_VOLT_ID_SDRAM_C,
> + val);
> + case 2:
> + return rpi_firmware_get_voltage(data,
> + RPI_FIRMWARE_VOLT_ID_SDRAM_I,
> + val);
> + case 3:
> + return rpi_firmware_get_voltage(data,
> + RPI_FIRMWARE_VOLT_ID_SDRAM_P,
> + val);
> + default:
> + return -EOPNOTSUPP;
> + }
> + case hwmon_in_lcrit_alarm:
> + if (channel == 0) {
> + *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
> + return 0;
> + }
> + return -EOPNOTSUPP;
> + default:
> + return -EOPNOTSUPP;
> + }
> + }
> +
> + return -EOPNOTSUPP;
> +}
> +
> +static int rpi_read_string(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, const char **str)
> +{
> + if (type == hwmon_in && attr == hwmon_in_label) {
> + switch (channel) {
> + case 0:
> + *str = "core";
> + return 0;
> + case 1:
> + *str = "sdram_c";
> + return 0;
> + case 2:
> + *str = "sdram_i";
> + return 0;
> + case 3:
> + *str = "sdram_p";
> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
This can be implemented as string array.
> + }
> +
> + return -EOPNOTSUPP;
> +}
> +
> +static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + if (type == hwmon_in) {
> + switch (attr) {
> + case hwmon_in_input:
> + case hwmon_in_label:
> + return 0444;
> + case hwmon_in_lcrit_alarm:
> + if (channel == 0)
> + return 0444;
> + return 0;
> + default:
> + return 0;
> + }
> + }
> +
> return 0;
> }
>
> static const struct hwmon_channel_info * const rpi_info[] = {
> HWMON_CHANNEL_INFO(in,
> - HWMON_I_LCRIT_ALARM),
> + HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT_ALARM,
> + HWMON_I_INPUT | HWMON_I_LABEL,
> + HWMON_I_INPUT | HWMON_I_LABEL,
> + HWMON_I_INPUT | HWMON_I_LABEL),
> NULL
> };
>
> static const struct hwmon_ops rpi_hwmon_ops = {
> - .visible = 0444,
> + .is_visible = rpi_is_visible,
> .read = rpi_read,
> + .read_string = rpi_read_string,
> };
>
> static const struct hwmon_chip_info rpi_chip_info = {
> @@ -159,6 +264,7 @@ static struct platform_driver rpi_hwmon_driver = {
> module_platform_driver(rpi_hwmon_driver);
>
> MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>");
> +MODULE_AUTHOR("Shubham Chakraborty <chakrabortyshubham66@gmail.com>");
> MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
> MODULE_LICENSE("GPL v2");
> MODULE_ALIAS("platform:raspberrypi-hwmon");
^ permalink raw reply
* Re: [PATCH v3 0/4] Update gmac clocks and devicetree for sam9x7 mpu
From: Claudiu Beznea @ 2026-05-16 16:58 UTC (permalink / raw)
To: Mihai Sain, mturquette, sboyd, nicolas.ferre, alexandre.belloni,
varshini.rajendran, cristian.birsan, balamanikandan.gunasundar,
robh, krzk+dt, conor+dt
Cc: linux-clk, linux-arm-kernel, linux-kernel, devicetree,
ryan.wanner
In-Reply-To: <20260309075329.1528-1-mihai.sain@microchip.com>
On 3/9/26 09:53, Mihai Sain wrote:
> Mihai Sain (4):
> clk: at91: sam9x7: Remove gmac peripheral clock with ID 67
> clk: at91: sam9x7: Rename macb0_clk to gmac_clk
Applied to clk-microchip, thanks!
> clk: at91: sam9x7: Fix gmac_gclk clock definition
Applied to clk-microchip-fixes, thanks!
> ARM: dts: microchip: sam9x7: fix GMAC clock configuration
Applied to at91-fixes, thanks!
^ permalink raw reply
* [PATCH 2/2] hwmon: raspberrypi: Add voltage input support
From: Shubham Chakraborty @ 2026-05-16 16:44 UTC (permalink / raw)
To: Guenter Roeck, Florian Fainelli,
Broadcom internal kernel review list, linux-hwmon,
linux-rpi-kernel, linux-arm-kernel, linux-kernel
In-Reply-To: <20260516164407.25255-1-chakrabortyshubham66@gmail.com>
Extend the raspberrypi-hwmon driver to expose firmware-provided
voltage measurements through the hwmon subsystem.
The driver now exports the following voltage inputs:
- in0_input (core)
- in1_input (sdram_c)
- in2_input (sdram_i)
- in3_input (sdram_p)
Voltage values returned by firmware are converted from microvolts
to millivolts as expected by the hwmon subsystem.
The existing undervoltage sticky alarm handling is preserved and
associated with the first voltage channel.
Tested in -
- Raspberry Pi 3b+ (Linux raspberrypi 6.12.75+rpt-rpi-v8 #1 SMP PREEMPT
Debian 1:6.12.75-1+rpt1 (2026-03-11) aarch64 GNU/Linux)
Signed-off-by: Shubham Chakraborty <chakrabortyshubham66@gmail.com>
---
drivers/hwmon/raspberrypi-hwmon.c | 112 +++++++++++++++++++++++++++++-
1 file changed, 109 insertions(+), 3 deletions(-)
diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c
index a2938881ccd2..c73a970db025 100644
--- a/drivers/hwmon/raspberrypi-hwmon.c
+++ b/drivers/hwmon/raspberrypi-hwmon.c
@@ -5,6 +5,7 @@
* Based on firmware/raspberrypi.c by Noralf Trønnes
*
* Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com>
+ * Copyright (C) 2026 Shubham Chakraborty <chakrabortyshubham66@gmail.com>
*/
#include <linux/device.h>
#include <linux/devm-helpers.h>
@@ -18,6 +19,11 @@
#define UNDERVOLTAGE_STICKY_BIT BIT(16)
+struct rpi_firmware_get_value {
+ __le32 id;
+ __le32 val;
+} __packed;
+
struct rpi_hwmon_data {
struct device *hwmon_dev;
struct rpi_firmware *fw;
@@ -56,6 +62,23 @@ static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data)
hwmon_notify_event(data->hwmon_dev, hwmon_in, hwmon_in_lcrit_alarm, 0);
}
+static int rpi_firmware_get_voltage(struct rpi_hwmon_data *data, u32 id,
+ long *val)
+{
+ struct rpi_firmware_get_value packet;
+ int ret;
+
+ packet.id = cpu_to_le32(id);
+ packet.val = 0;
+ ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_VOLTAGE,
+ &packet, sizeof(packet));
+ if (ret)
+ return ret;
+
+ *val = le32_to_cpu(packet.val) / 1000;
+ return 0;
+}
+
static void get_values_poll(struct work_struct *work)
{
struct rpi_hwmon_data *data;
@@ -77,19 +100,101 @@ static int rpi_read(struct device *dev, enum hwmon_sensor_types type,
{
struct rpi_hwmon_data *data = dev_get_drvdata(dev);
- *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
+ if (type == hwmon_in) {
+ switch (attr) {
+ case hwmon_in_input:
+ switch (channel) {
+ case 0:
+ return rpi_firmware_get_voltage(data,
+ RPI_FIRMWARE_VOLT_ID_CORE,
+ val);
+ case 1:
+ return rpi_firmware_get_voltage(data,
+ RPI_FIRMWARE_VOLT_ID_SDRAM_C,
+ val);
+ case 2:
+ return rpi_firmware_get_voltage(data,
+ RPI_FIRMWARE_VOLT_ID_SDRAM_I,
+ val);
+ case 3:
+ return rpi_firmware_get_voltage(data,
+ RPI_FIRMWARE_VOLT_ID_SDRAM_P,
+ val);
+ default:
+ return -EOPNOTSUPP;
+ }
+ case hwmon_in_lcrit_alarm:
+ if (channel == 0) {
+ *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
+ return 0;
+ }
+ return -EOPNOTSUPP;
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int rpi_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ if (type == hwmon_in && attr == hwmon_in_label) {
+ switch (channel) {
+ case 0:
+ *str = "core";
+ return 0;
+ case 1:
+ *str = "sdram_c";
+ return 0;
+ case 2:
+ *str = "sdram_i";
+ return 0;
+ case 3:
+ *str = "sdram_p";
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type == hwmon_in) {
+ switch (attr) {
+ case hwmon_in_input:
+ case hwmon_in_label:
+ return 0444;
+ case hwmon_in_lcrit_alarm:
+ if (channel == 0)
+ return 0444;
+ return 0;
+ default:
+ return 0;
+ }
+ }
+
return 0;
}
static const struct hwmon_channel_info * const rpi_info[] = {
HWMON_CHANNEL_INFO(in,
- HWMON_I_LCRIT_ALARM),
+ HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT_ALARM,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL),
NULL
};
static const struct hwmon_ops rpi_hwmon_ops = {
- .visible = 0444,
+ .is_visible = rpi_is_visible,
.read = rpi_read,
+ .read_string = rpi_read_string,
};
static const struct hwmon_chip_info rpi_chip_info = {
@@ -159,6 +264,7 @@ static struct platform_driver rpi_hwmon_driver = {
module_platform_driver(rpi_hwmon_driver);
MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>");
+MODULE_AUTHOR("Shubham Chakraborty <chakrabortyshubham66@gmail.com>");
MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:raspberrypi-hwmon");
--
2.54.0
^ permalink raw reply related
* [PATCH 1/2] soc: bcm2835: raspberrypi-firmware: Add voltage domain IDs
From: Shubham Chakraborty @ 2026-05-16 16:44 UTC (permalink / raw)
To: Florian Fainelli, Ray Jui, Scott Branden,
Broadcom internal kernel review list, linux-rpi-kernel,
linux-arm-kernel, linux-kernel
Add firmware voltage domain identifiers for the Raspberry Pi
mailbox property interface.
These IDs are used by firmware clients to query voltage rails
through the RPI_FIRMWARE_GET_VOLTAGE property.
Signed-off-by: Shubham Chakraborty <chakrabortyshubham66@gmail.com>
---
include/soc/bcm2835/raspberrypi-firmware.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h
index e1f87fbfe554..fd2e051ce05b 100644
--- a/include/soc/bcm2835/raspberrypi-firmware.h
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
@@ -156,6 +156,14 @@ enum rpi_firmware_clk_id {
RPI_FIRMWARE_NUM_CLK_ID,
};
+enum rpi_firmware_volt_id {
+ RPI_FIRMWARE_VOLT_ID_RESERVED = 0,
+ RPI_FIRMWARE_VOLT_ID_CORE = 1,
+ RPI_FIRMWARE_VOLT_ID_SDRAM_C = 2,
+ RPI_FIRMWARE_VOLT_ID_SDRAM_I = 3,
+ RPI_FIRMWARE_VOLT_ID_SDRAM_P = 4,
+};
+
/**
* struct rpi_firmware_clk_rate_request - Firmware Request for a rate
* @id: ID of the clock being queried
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v3 1/3] dt-bindings: nvmem: lan9662-otpc: Add LAN969x series
From: Claudiu Beznea @ 2026-05-16 16:27 UTC (permalink / raw)
To: Robert Marko, srini, robh, krzk+dt, conor+dt, nicolas.ferre,
horatiu.vultur, daniel.machon, devicetree, linux-kernel,
linux-arm-kernel
Cc: luka.perkov, Conor Dooley
In-Reply-To: <20260515115954.701155-1-robimarko@gmail.com>
On 5/15/26 14:59, Robert Marko wrote:
> From: Robert Marko <robert.marko@sartura.hr>
>
> Unlike LAN966x series which has 8K of OTP space, LAN969x series has 16K of
> OTP space, so document the compatible.
>
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> Signed-off-by: Robert Marko <robert.marko@sartura.hr>
Reviewed-by: Claudiu Beznea <claudiu.beznea@tuxon.dev>
^ permalink raw reply
* Re: [PATCH v3 2/3] nvmem: lan9662-otp: add support for LAN969x
From: Claudiu Beznea @ 2026-05-16 16:27 UTC (permalink / raw)
To: Robert Marko, srini, robh, krzk+dt, conor+dt, nicolas.ferre,
horatiu.vultur, daniel.machon, devicetree, linux-kernel,
linux-arm-kernel
Cc: luka.perkov
In-Reply-To: <20260515115954.701155-2-robimarko@gmail.com>
Hi, Robert,
> static int lan9662_otp_probe(struct platform_device *pdev)
> @@ -196,6 +194,7 @@ static int lan9662_otp_probe(struct platform_device *pdev)
>
> otp_config.priv = otp;
> otp_config.dev = dev;
> + otp_config.size = (uintptr_t) device_get_match_data(dev);
>
> nvmem = devm_nvmem_register(dev, &otp_config);
>
> @@ -203,7 +202,14 @@ static int lan9662_otp_probe(struct platform_device *pdev)
> }
>
> static const struct of_device_id lan9662_otp_match[] = {
> - { .compatible = "microchip,lan9662-otpc", },
> + {
> + .compatible = "microchip,lan9662-otpc",
> + .data = (const void *) SZ_8K,
> + },
> + {
> + .compatible = "microchip,lan9691-otpc",
> + .data = (const void *) SZ_16K,
> + },
Some checks from checkpatch:
[Checkpatch] CHECK: No space is necessary after a cast
#51: FILE: drivers/nvmem/lan9662-otpc.c:197:
+ otp_config.size = (uintptr_t) device_get_match_data(dev);
CHECK: No space is necessary after a cast
#62: FILE: drivers/nvmem/lan9662-otpc.c:207:
+ .data = (const void *) SZ_8K,
CHECK: No space is necessary after a cast
#66: FILE: drivers/nvmem/lan9662-otpc.c:211:
+ .data = (const void *) SZ_16K,
total: 0 errors, 0 warnings, 3 checks, 44 lines checked
^ permalink raw reply
* Re: [PATCH v3 3/3] arm64: dts: microchip: lan969x: add OTP node
From: Claudiu Beznea @ 2026-05-16 16:26 UTC (permalink / raw)
To: Robert Marko, srini, robh, krzk+dt, conor+dt, nicolas.ferre,
horatiu.vultur, daniel.machon, devicetree, linux-kernel,
linux-arm-kernel
Cc: luka.perkov
In-Reply-To: <20260515115954.701155-3-robimarko@gmail.com>
On 5/15/26 14:59, Robert Marko wrote:
> From: Robert Marko<robert.marko@sartura.hr>
>
> Add the required OTP on LAN969x.
>
> Signed-off-by: Robert Marko<robert.marko@sartura.hr>
Reviewed-by: Claudiu Beznea <claudiu.beznea@tuxon.dev>
^ permalink raw reply
* Re: [PATCH v6 5/5] ARM: configs: at91: sama7: add sama7d65 i3c-hci
From: Claudiu Beznea @ 2026-05-16 16:07 UTC (permalink / raw)
To: Manikandan Muralidharan, alexandre.belloni, Frank.Li, robh,
krzk+dt, conor+dt, nicolas.ferre, linux, mturquette, sboyd, tytso,
aubin.constans, Ryan.Wanner, romain.sioen, durai.manickamkr,
cristian.birsan, adrian.hunter, jarkko.nikula, npitre, linux-i3c,
devicetree, linux-kernel, linux-arm-kernel, linux-clk
In-Reply-To: <20260507084805.481737-6-manikandan.m@microchip.com>
On 5/7/26 11:48, Manikandan Muralidharan wrote:
> Enable the configs needed for I3C framework and microchip
> sama7d65 i3c-hci driver.
>
> Signed-off-by: Durai Manickam KR <durai.manickamkr@microchip.com>
> Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
Reviewed-by: Claudiu Beznea <claudiu.beznea@tuxon.dev>
^ permalink raw reply
* Re: [PATCH v6 4/5] ARM: dts: microchip: add I3C controller
From: Claudiu Beznea @ 2026-05-16 16:07 UTC (permalink / raw)
To: Manikandan Muralidharan, alexandre.belloni, Frank.Li, robh,
krzk+dt, conor+dt, nicolas.ferre, linux, mturquette, sboyd, tytso,
aubin.constans, Ryan.Wanner, romain.sioen, durai.manickamkr,
cristian.birsan, adrian.hunter, jarkko.nikula, npitre, linux-i3c,
devicetree, linux-kernel, linux-arm-kernel, linux-clk
In-Reply-To: <20260507084805.481737-5-manikandan.m@microchip.com>
Hi, Manikandan,
On 5/7/26 11:48, Manikandan Muralidharan wrote:
> From: Durai Manickam KR <durai.manickamkr@microchip.com>
>
> Add I3C controller for sama7d65 SoC.
>
> Signed-off-by: Durai Manickam KR <durai.manickamkr@microchip.com>
> Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
> ---
> Changes in v3:
> - Remove clock-names property as driver enables the clk in bulk
>
> arch/arm/boot/dts/microchip/sama7d65.dtsi | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/arch/arm/boot/dts/microchip/sama7d65.dtsi b/arch/arm/boot/dts/microchip/sama7d65.dtsi
> index 67253bbc08df..ec200848c153 100644
> --- a/arch/arm/boot/dts/microchip/sama7d65.dtsi
> +++ b/arch/arm/boot/dts/microchip/sama7d65.dtsi
> @@ -1055,5 +1055,13 @@ gic: interrupt-controller@e8c11000 {
> #address-cells = <0>;
> interrupt-controller;
> };
> +
> + i3c: i3c@e9000000 {
> + compatible = "microchip,sama7d65-i3c-hci";
> + reg = <0xe9000000 0x300>;
From manual at [1] I see the size of I3CC region is 0x1000. Unless that is
wrong I think we should use 0x1000 to properly describe de HW. Please let me
know and I can do it while applying.
Thank you,
Claudiu
[1]
https://ww1.microchip.com/downloads/aemDocuments/documents/MPU32/ProductDocuments/DataSheets/SAMA7D6-Series-Data-Sheet-DS60001851.pdf
> + interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&pmc PMC_TYPE_PERIPHERAL 105>, <&pmc PMC_TYPE_GCK 105>;
> + status = "disabled";
> + };
> };
> };
^ permalink raw reply
* Re: [PATCH v6 2/5] clk: at91: sama7d65: add peripheral clock for I3C
From: Claudiu Beznea @ 2026-05-16 16:06 UTC (permalink / raw)
To: Manikandan Muralidharan, alexandre.belloni, Frank.Li, robh,
krzk+dt, conor+dt, nicolas.ferre, linux, mturquette, sboyd, tytso,
aubin.constans, Ryan.Wanner, romain.sioen, durai.manickamkr,
cristian.birsan, adrian.hunter, jarkko.nikula, npitre, linux-i3c,
devicetree, linux-kernel, linux-arm-kernel, linux-clk
In-Reply-To: <20260507084805.481737-3-manikandan.m@microchip.com>
On 5/7/26 11:48, Manikandan Muralidharan wrote:
> From: Durai Manickam KR<durai.manickamkr@microchip.com>
>
> Add peripheral clock description for I3C.
>
> Signed-off-by: Durai Manickam KR<durai.manickamkr@microchip.com>
> Signed-off-by: Manikandan Muralidharan<manikandan.m@microchip.com>
Reviewed-by: Claudiu Beznea <claudiu.beznea@tuxon.dev>
^ permalink raw reply
* Re: [PATCH 1/2] spi: atmel: fix resource leak on DMA buffer allocation failure
From: Claudiu Beznea @ 2026-05-16 16:05 UTC (permalink / raw)
To: Felix Gu, Ryan Wanner, Mark Brown, Nicolas Ferre,
Alexandre Belloni, Radu Pirea, Richard Genoud, Wenyou Yang
Cc: linux-spi, linux-arm-kernel, linux-kernel, Mark Brown
In-Reply-To: <20260516-atmel-v1-1-674fb4707af6@gmail.com>
Hi, Felix,
On 5/15/26 20:20, Felix Gu wrote:
> The original code set use_dma to false when dma_alloc_coherent() fails,
> so DMA channels allocated earlier were never freed, causing a resource
> leak.
>
> Fix by moving the bounce buffer allocation into
> atmel_spi_configure_dma() and extending atmel_spi_release_dma() to
> also free the bounce buffers. Any allocation failure in the DMA
> configuration path now rolls back both channels and buffers through
> the same release function.
>
> Fixes: a9889ed62d06 ("spi: atmel: Implements transfers with bounce buffer")
> Signed-off-by: Felix Gu<ustc.gu@gmail.com>
> ---
> drivers/spi/spi-atmel.c | 113 ++++++++++++++++++++++++------------------------
> 1 file changed, 57 insertions(+), 56 deletions(-)
>
> diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
> index 25aa294631c8..e519a86a2b45 100644
> --- a/drivers/spi/spi-atmel.c
> +++ b/drivers/spi/spi-atmel.c
> @@ -559,6 +559,34 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, u8 bits_per_word)
> return err;
> }
>
> +static void atmel_spi_release_dma(struct spi_controller *host,
> + struct atmel_spi *as)
> +{
> + if (host->dma_rx) {
> + dma_release_channel(host->dma_rx);
> + host->dma_rx = NULL;
> + }
> + if (host->dma_tx) {
> + dma_release_channel(host->dma_tx);
> + host->dma_tx = NULL;
> + }
> +
> + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) {
> + if (as->addr_tx_bbuf) {
> + dma_free_coherent(&as->pdev->dev, SPI_MAX_DMA_XFER,
> + as->addr_tx_bbuf,
> + as->dma_addr_tx_bbuf);
> + as->addr_tx_bbuf = NULL;
> + }
> + if (as->addr_rx_bbuf) {
> + dma_free_coherent(&as->pdev->dev, SPI_MAX_DMA_XFER,
> + as->addr_rx_bbuf,
> + as->dma_addr_rx_bbuf);
> + as->addr_rx_bbuf = NULL;
> + }
> + }
> +}
> +
> static int atmel_spi_configure_dma(struct spi_controller *host,
> struct atmel_spi *as)
> {
> @@ -569,7 +597,8 @@ static int atmel_spi_configure_dma(struct spi_controller *host,
> if (IS_ERR(host->dma_tx)) {
> err = PTR_ERR(host->dma_tx);
> dev_dbg(dev, "No TX DMA channel, DMA is disabled\n");
> - goto error_clear;
> + host->dma_tx = NULL;
> + return err;
> }
>
> host->dma_rx = dma_request_chan(dev, "rx");
> @@ -580,12 +609,31 @@ static int atmel_spi_configure_dma(struct spi_controller *host,
> * requested tx channel.
> */
> dev_dbg(dev, "No RX DMA channel, DMA is disabled\n");
> - goto error;
> + host->dma_rx = NULL;
> + goto err_release_dma;
> }
>
> err = atmel_spi_dma_slave_config(as, 8);
> if (err)
> - goto error;
> + goto err_release_dma;
> +
> + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) {
> + as->addr_tx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER,
> + &as->dma_addr_tx_bbuf,
> + GFP_KERNEL | GFP_DMA);
You could use dmam_alloc_coherent() and avoid bulking the failure path.
Thank you,
Claudiu
^ permalink raw reply
* Re: [PATCH v5 4/8] mfd: khadas-mcu: Add support for VIM4 MCU variant
From: Ronald Claveau @ 2026-05-16 15:17 UTC (permalink / raw)
To: Lee Jones
Cc: Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Andi Shyti, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
Beniamino Galvani, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui,
Lukasz Luba, Liam Girdwood, Mark Brown, linux-amlogic, devicetree,
linux-kernel, linux-i2c, linux-arm-kernel, linux-pm
In-Reply-To: <20260514105459.GJ305027@google.com>
Thanks for your review Lee.
On 5/14/26 12:54 PM, Lee Jones wrote:
> On Fri, 24 Apr 2026, Ronald Claveau via B4 Relay wrote:
>
>> From: Ronald Claveau <linux-kernel-dev@aliel.fr>
>>
>> Refactor probe() to use per-variant khadas_mcu_data
>> instead of hardcoded globals.
>>
>> Add dedicated regmap configuration and device data for the VIM4 MCU,
>> with its own volatile/writeable registers.
>>
>> Add the fan control register
>> (0–100 levels vs 0–3 for previous supported boards).
>>
>> Add a new compatible string "khadas,vim4-mcu".
>>
>> Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
>> Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
>> ---
>> drivers/mfd/khadas-mcu.c | 106 ++++++++++++++++++++++++++++++++++++++++++-----
>> 1 file changed, 95 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/mfd/khadas-mcu.c b/drivers/mfd/khadas-mcu.c
>> index ba981a7886921..b36b3b3ab73c0 100644
>> --- a/drivers/mfd/khadas-mcu.c
>> +++ b/drivers/mfd/khadas-mcu.c
>> @@ -75,15 +75,91 @@ static const struct regmap_config khadas_mcu_regmap_config = {
>> .cache_type = REGCACHE_MAPLE,
>> };
>>
>> +static const struct khadas_mcu_fan_pdata khadas_mcu_fan_pdata = {
>> + .fan_reg = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG,
>> + .max_level = 3,
>> +};
>
> What is 3?
>
This is the max fan speed level as defined in the MCU register.
I will add it as a comment.
/* Fan speed: 0 = off, 1 = low, 2 = medium, 3 = high */
>> +
>> static struct mfd_cell khadas_mcu_fan_cells[] = {
>> /* VIM1/2 Rev13+ and VIM3 only */
>> - { .name = "khadas-mcu-fan-ctrl", },
>> + {
>> + .name = "khadas-mcu-fan-ctrl",
>> + .platform_data = &khadas_mcu_fan_pdata,
>> + .pdata_size = sizeof(khadas_mcu_fan_pdata),
>> + },
>> };
>
> Worth making this const at one point.
>
Yes I will.
>>
>> static struct mfd_cell khadas_mcu_cells[] = {
>> { .name = "khadas-mcu-user-mem", },
>> };
>>
I will for that one too.
>> +static const struct khadas_mcu_data khadas_mcu_data = {
>> + .regmap_config = &khadas_mcu_regmap_config,
>> + .cells = khadas_mcu_cells,
>> + .ncells = ARRAY_SIZE(khadas_mcu_cells),
>> + .fan_cells = khadas_mcu_fan_cells,
>> + .nfan_cells = ARRAY_SIZE(khadas_mcu_fan_cells),
>> +};
>
> This is a red flag!
>
>> +static bool khadas_mcu_vim4_reg_volatile(struct device *dev, unsigned int reg)
>> +{
>> + switch (reg) {
>> + case KHADAS_MCU_PWR_OFF_CMD_REG:
>> + case KHADAS_MCU_VIM4_REST_CONF_REG:
>> + case KHADAS_MCU_WOL_INIT_START_REG:
>> + case KHADAS_MCU_VIM4_LED_ON_RAM_REG:
>> + case KHADAS_MCU_VIM4_FAN_CTRL_REG:
>> + case KHADAS_MCU_VIM4_WDT_EN_REG:
>> + case KHADAS_MCU_VIM4_SYS_RST_REG:
>> + return true;
>> + default:
>> + return false;
>> + }
>> +}
>> +
>> +static bool khadas_mcu_vim4_reg_writeable(struct device *dev, unsigned int reg)
>> +{
>> + switch (reg) {
>> + case KHADAS_MCU_VERSION_0_REG:
>> + case KHADAS_MCU_VERSION_1_REG:
>> + case KHADAS_MCU_SHUTDOWN_NORMAL_STATUS_REG:
>> + return false;
>> + default:
>> + return true;
>> + }
>> +}
>> +
>> +static const struct regmap_config khadas_mcu_vim4_regmap_config = {
>> + .reg_bits = 8,
>> + .reg_stride = 1,
>> + .val_bits = 8,
>> + .max_register = KHADAS_MCU_VIM4_SYS_RST_REG,
>> + .volatile_reg = khadas_mcu_vim4_reg_volatile,
>> + .writeable_reg = khadas_mcu_vim4_reg_writeable,
>> + .cache_type = REGCACHE_MAPLE,
>> +};
>> +
>> +static const struct khadas_mcu_fan_pdata khadas_vim4_fan_pdata = {
>> + .fan_reg = KHADAS_MCU_VIM4_FAN_CTRL_REG,
>> + .max_level = 0x64,
>> +};
>> +
>> +static const struct mfd_cell khadas_mcu_vim4_cells[] = {
>> + {
>> + .name = "khadas-mcu-fan-ctrl",
>> + .platform_data = &khadas_vim4_fan_pdata,
>> + .pdata_size = sizeof(khadas_vim4_fan_pdata),
>> + },
>> +};
>> +
>> +static const struct khadas_mcu_data khadas_vim4_mcu_data = {
>> + .regmap_config = &khadas_mcu_vim4_regmap_config,
>> + .cells = NULL,
>> + .ncells = 0,
>> + .fan_cells = khadas_mcu_vim4_cells,
>> + .nfan_cells = ARRAY_SIZE(khadas_mcu_vim4_cells),
>> +};
>> +
>> static int khadas_mcu_probe(struct i2c_client *client)
>> {
>> struct device *dev = &client->dev;
>> @@ -94,28 +170,35 @@ static int khadas_mcu_probe(struct i2c_client *client)
>> if (!ddata)
>> return -ENOMEM;
>>
>> + ddata->data = i2c_get_match_data(client);
>> + if (!ddata->data)
>> + return -EINVAL;
>
> Shouldn't this be -ENODEV?
>
I will change to -ENODEV for the switch default.
>> i2c_set_clientdata(client, ddata);
>>
>> ddata->dev = dev;
>>
>> - ddata->regmap = devm_regmap_init_i2c(client, &khadas_mcu_regmap_config);
>> + ddata->regmap = devm_regmap_init_i2c(client,
>> + ddata->data->regmap_config);
>
> Use up to 100-chars to prevent this kind of wrapping.
>
Ok will do, is it a general rule or depends on maintainer ?
>> if (IS_ERR(ddata->regmap)) {
>> ret = PTR_ERR(ddata->regmap);
>> dev_err(dev, "Failed to allocate register map: %d\n", ret);
>> return ret;
>> }
>
> Maybe convert this to dev_err_probe() at one point.
>
Ok I change that.
>> - ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
>> - khadas_mcu_cells,
>> - ARRAY_SIZE(khadas_mcu_cells),
>> - NULL, 0, NULL);
>> - if (ret)
>> - return ret;
>> + if (ddata->data->cells && ddata->data->ncells) {
>> + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
>> + ddata->data->cells,
>> + ddata->data->ncells,
>> + NULL, 0, NULL);
>> + if (ret)
>> + return ret;
>> + }
>>
>> if (of_property_present(dev->of_node, "#cooling-cells"))
>> return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
>> - khadas_mcu_fan_cells,
>> - ARRAY_SIZE(khadas_mcu_fan_cells),
>> + ddata->data->fan_cells,
>> + ddata->data->nfan_cells,
>> NULL, 0, NULL);
>>
>> return 0;
>> @@ -123,7 +206,8 @@ static int khadas_mcu_probe(struct i2c_client *client)
>>
>> #ifdef CONFIG_OF
>> static const struct of_device_id khadas_mcu_of_match[] = {
>> - { .compatible = "khadas,mcu", },
>> + { .compatible = "khadas,mcu", .data = &khadas_mcu_data },
>> + { .compatible = "khadas,vim4-mcu", .data = &khadas_vim4_mcu_data },
>
> We don't allow data from one registration API (MFD) to be shoved through
> another (DT). Pass a value to match on instead, then use a switch()
> statement or similar to populate or register the devices.
>
Thanks I'm on it.
>> {},
>> };
>> MODULE_DEVICE_TABLE(of, khadas_mcu_of_match);
>>
>> --
>> 2.49.0
>>
>>
--
Best regards,
Ronald
^ permalink raw reply
* Re: [PATCH v3] iio: adc: sun20i-gpadc: support non-contiguous channel lookups
From: Jonathan Cameron @ 2026-05-16 15:11 UTC (permalink / raw)
To: Michal Piekos
Cc: David Lechner, Nuno Sá, Andy Shevchenko, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, linux-iio, linux-arm-kernel,
linux-sunxi, linux-kernel, Andy Shevchenko, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt, llvm
In-Reply-To: <20260516-fix-sunxi-gpadc-sparse-channels-v3-1-4d229d18ff3b@mmpsystems.pl>
On Sat, 16 May 2026 07:48:37 +0200
Michal Piekos <michal.piekos@mmpsystems.pl> wrote:
> Using consumer driver like iio-hwmon which resolve channels through
> io-channels phandles will fail for sparse channels because IIO core by
> default treats phandle argument as index into channel array.
> eg. <&gpadc 1> will fail if there is only channel@1 specified
>
> Add .fwnode_xlate() which maps DT phandle to the registered channel
> whose chan->channel matches the hardware channel number. It allows
> sparse channel maps to be consumed by drivers like iio-hwmon.
>
> Tested on Radxa Cubie A5E.
>
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
> Signed-off-by: Michal Piekos <michal.piekos@mmpsystems.pl>
Applied to the testing branch of iio.git.
Thanks,
Jonathan
> ---
> Changes in v3:
> - Add iiospec->nargs validation in sun20i_gpadc_fwnode_xlate()
> - Keep Andy's Reviewed-by tag since change is narrow validation fix
> - Fix spelling issues in commit message
> - Link to v2: https://patch.msgid.link/20260514-fix-sunxi-gpadc-sparse-channels-v2-1-d4a66b70c7a7@mmpsystems.pl
>
> Changes in v2:
> - Move loop variable declaration into the for statement
> - Fix indentation using clang-format
> - Correct commit wording
> - Link to v1: https://patch.msgid.link/20260513-fix-sunxi-gpadc-sparse-channels-v1-1-6c21e290bcee@mmpsystems.pl
>
> To: Jonathan Cameron <jic23@kernel.org>
> To: David Lechner <dlechner@baylibre.com>
> To: Nuno Sá <nuno.sa@analog.com>
> To: Andy Shevchenko <andy@kernel.org>
> To: Chen-Yu Tsai <wens@kernel.org>
> To: Jernej Skrabec <jernej.skrabec@gmail.com>
> To: Samuel Holland <samuel@sholland.org>
> To: Nathan Chancellor <nathan@kernel.org>
> To: Nick Desaulniers <nick.desaulniers+lkml@gmail.com>
> To: Bill Wendling <morbo@google.com>
> To: Justin Stitt <justinstitt@google.com>
> Cc: linux-iio@vger.kernel.org
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-sunxi@lists.linux.dev
> Cc: linux-kernel@vger.kernel.org
> Cc: llvm@lists.linux.dev
> ---
> drivers/iio/adc/sun20i-gpadc-iio.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/drivers/iio/adc/sun20i-gpadc-iio.c b/drivers/iio/adc/sun20i-gpadc-iio.c
> index 861c14da75ad..8a75498557ff 100644
> --- a/drivers/iio/adc/sun20i-gpadc-iio.c
> +++ b/drivers/iio/adc/sun20i-gpadc-iio.c
> @@ -139,8 +139,23 @@ static irqreturn_t sun20i_gpadc_irq_handler(int irq, void *data)
> return IRQ_HANDLED;
> }
>
> +static int
> +sun20i_gpadc_fwnode_xlate(struct iio_dev *indio_dev,
> + const struct fwnode_reference_args *iiospec)
> +{
> + if (iiospec->nargs != 1)
> + return -EINVAL;
> +
> + for (unsigned int i = 0; i < indio_dev->num_channels; i++)
> + if (indio_dev->channels[i].channel == iiospec->args[0])
> + return i;
> +
> + return -EINVAL;
> +}
> +
> static const struct iio_info sun20i_gpadc_iio_info = {
> .read_raw = sun20i_gpadc_read_raw,
> + .fwnode_xlate = sun20i_gpadc_fwnode_xlate,
> };
>
> static void sun20i_gpadc_reset_assert(void *data)
>
> ---
> base-commit: 6916d5703ddf9a38f1f6c2cc793381a24ee914c6
> change-id: 20260513-fix-sunxi-gpadc-sparse-channels-2b6b2063bd49
>
> Best regards,
> --
> Michal Piekos <michal.piekos@mmpsystems.pl>
>
>
^ permalink raw reply
* Re: [PATCH v3 2/3] iio: adc: sun20i-gpadc: add A523 gpadc support
From: Jonathan Cameron @ 2026-05-16 15:02 UTC (permalink / raw)
To: Michal Piekos
Cc: David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Maksim Kiselev, linux-iio, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel
In-Reply-To: <20260516-sunxi-a523-gpadc-v3-2-a3a04cff2620@mmpsystems.pl>
On Sat, 16 May 2026 07:34:15 +0200
Michal Piekos <michal.piekos@mmpsystems.pl> wrote:
> A523 differs from existing sun20i-gpadc-iio by having two clocks; bus
> clock and module clock.
>
> Change driver to enable all clocks.
>
> Signed-off-by: Michal Piekos <michal.piekos@mmpsystems.pl>
Applied patches 1 and 2 to the testing branch of iio.git.
Given some other folk were involved in reviewing earlier versions I'm fine
adding tags for nor next few days (or dropping it if I missed anything!)
Thanks
Jonathan
^ permalink raw reply
* [PATCH mt76] wifi: mt76: mt7915: configure noise floor reporting on reset
From: David Bauer @ 2026-05-16 14:49 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek
When performing a full system recovery of the MCU on a dual-phy
platform, band 0 (usually 2.4GHz) stops reading correct noise floor
data.
This is due to noise floor reporting only being configured correctly
for the second device PHY.
Configure the respective registers correctly after restarting the MCU
firmware to fix reported noise-floor values.
Signed-off-by: David Bauer <mail@david-bauer.net>
---
drivers/net/wireless/mediatek/mt76/mt7915/main.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
index e1d83052aa6dd..b42c26d0d09a4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
@@ -25,11 +25,13 @@ int mt7915_run(struct ieee80211_hw *hw)
struct mt7915_dev *dev = mt7915_hw_dev(hw);
struct mt7915_phy *phy = mt7915_hw_phy(hw);
bool running;
+ bool reset;
int ret;
running = mt7915_dev_running(dev);
+ reset = test_bit(MT76_RESET, &phy->mt76->state);
- if (!running) {
+ if (!running || (reset && phy == &dev->phy)) {
ret = mt76_connac_mcu_set_pm(&dev->mt76,
dev->phy.mt76->band_idx, 0);
if (ret)
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v4 03/13] dma-pool: track decrypted atomic pools and select them via attrs
From: Alexey Kardashevskiy @ 2026-05-16 12:53 UTC (permalink / raw)
To: Aneesh Kumar K.V (Arm), iommu, linux-arm-kernel, linux-kernel,
linux-coco
Cc: Robin Murphy, Marek Szyprowski, Will Deacon, Marc Zyngier,
Steven Price, Suzuki K Poulose, Catalin Marinas, Jiri Pirko,
Jason Gunthorpe, Mostafa Saleh, Petr Tesarik, Dan Williams,
Xu Yilun, linuxppc-dev, linux-s390, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Alexander Gordeev, Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260512090408.794195-4-aneesh.kumar@kernel.org>
On 12/5/26 19:03, Aneesh Kumar K.V (Arm) wrote:
> Teach the atomic DMA pool code to distinguish between encrypted and
> decrypted pools, and make pool allocation select the matching pool based
> on DMA attributes.
>
> Introduce a dma_gen_pool wrapper that records whether a pool is
> decrypted, initialize that state when the atomic pools are created, and
> use it when expanding and resizing the pools. Update dma_alloc_from_pool()
> to take attrs and skip pools whose encrypted/decrypted state does not
> match DMA_ATTR_CC_SHARED. Update dma_free_from_pool() accordingly.
>
> Also pass DMA_ATTR_CC_SHARED from the swiotlb atomic allocation path
> so decrypted swiotlb allocations are taken from the correct atomic pool.
>
> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
> ---
> drivers/iommu/dma-iommu.c | 2 +-
> include/linux/dma-map-ops.h | 2 +-
> kernel/dma/direct.c | 11 ++-
> kernel/dma/pool.c | 163 +++++++++++++++++++++++-------------
> kernel/dma/swiotlb.c | 7 +-
> 5 files changed, 122 insertions(+), 63 deletions(-)
>
> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> index 54d96e847f16..c2595bee3d41 100644
> --- a/drivers/iommu/dma-iommu.c
> +++ b/drivers/iommu/dma-iommu.c
> @@ -1673,7 +1673,7 @@ void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
> if (IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
> !gfpflags_allow_blocking(gfp) && !coherent)
> page = dma_alloc_from_pool(dev, PAGE_ALIGN(size), &cpu_addr,
> - gfp, NULL);
> + gfp, attrs, NULL);
> else
> cpu_addr = iommu_dma_alloc_pages(dev, size, &page, gfp, attrs);
> if (!cpu_addr)
> diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
> index 6a1832a73cad..696b2c3a2305 100644
> --- a/include/linux/dma-map-ops.h
> +++ b/include/linux/dma-map-ops.h
> @@ -212,7 +212,7 @@ void *dma_common_pages_remap(struct page **pages, size_t size, pgprot_t prot,
> void dma_common_free_remap(void *cpu_addr, size_t size);
>
> struct page *dma_alloc_from_pool(struct device *dev, size_t size,
> - void **cpu_addr, gfp_t flags,
> + void **cpu_addr, gfp_t flags, unsigned long attrs,
> bool (*phys_addr_ok)(struct device *, phys_addr_t, size_t));
> bool dma_free_from_pool(struct device *dev, void *start, size_t size);
>
> diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
> index 0c2e1f8436ce..dc2907439b3d 100644
> --- a/kernel/dma/direct.c
> +++ b/kernel/dma/direct.c
> @@ -162,7 +162,7 @@ static bool dma_direct_use_pool(struct device *dev, gfp_t gfp)
> }
>
> static void *dma_direct_alloc_from_pool(struct device *dev, size_t size,
> - dma_addr_t *dma_handle, gfp_t gfp)
> + dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
> {
> struct page *page;
> u64 phys_limit;
> @@ -172,7 +172,8 @@ static void *dma_direct_alloc_from_pool(struct device *dev, size_t size,
> return NULL;
>
> gfp |= dma_direct_optimal_gfp_mask(dev, &phys_limit);
> - page = dma_alloc_from_pool(dev, size, &ret, gfp, dma_coherent_ok);
> + page = dma_alloc_from_pool(dev, size, &ret, gfp, attrs,
> + dma_coherent_ok);
> if (!page)
> return NULL;
> *dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
> @@ -261,7 +262,8 @@ void *dma_direct_alloc(struct device *dev, size_t size,
> */
> if ((remap || (attrs & DMA_ATTR_CC_SHARED)) &&
> dma_direct_use_pool(dev, gfp))
> - return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
> + return dma_direct_alloc_from_pool(dev, size, dma_handle,
> + gfp, attrs);
>
> if (is_swiotlb_for_alloc(dev)) {
> page = dma_direct_alloc_swiotlb(dev, size);
> @@ -397,7 +399,8 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
> attrs |= DMA_ATTR_CC_SHARED;
>
> if ((attrs & DMA_ATTR_CC_SHARED) && dma_direct_use_pool(dev, gfp))
> - return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
> + return dma_direct_alloc_from_pool(dev, size, dma_handle,
> + gfp, attrs);
>
> if (is_swiotlb_for_alloc(dev)) {
> page = dma_direct_alloc_swiotlb(dev, size);
> diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c
> index 2b2fbb709242..75f0eba48a23 100644
> --- a/kernel/dma/pool.c
> +++ b/kernel/dma/pool.c
> @@ -12,12 +12,18 @@
> #include <linux/set_memory.h>
> #include <linux/slab.h>
> #include <linux/workqueue.h>
> +#include <linux/cc_platform.h>
>
> -static struct gen_pool *atomic_pool_dma __ro_after_init;
> +struct dma_gen_pool {
> + bool unencrypted;
> + struct gen_pool *pool;
> +};
> +
> +static struct dma_gen_pool atomic_pool_dma __ro_after_init;
> static unsigned long pool_size_dma;
> -static struct gen_pool *atomic_pool_dma32 __ro_after_init;
> +static struct dma_gen_pool atomic_pool_dma32 __ro_after_init;
> static unsigned long pool_size_dma32;
> -static struct gen_pool *atomic_pool_kernel __ro_after_init;
> +static struct dma_gen_pool atomic_pool_kernel __ro_after_init;
> static unsigned long pool_size_kernel;
>
> /* Size can be defined by the coherent_pool command line */
> @@ -76,7 +82,7 @@ static bool cma_in_zone(gfp_t gfp)
> return true;
> }
>
> -static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
> +static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
> gfp_t gfp)
> {
> unsigned int order;
> @@ -113,12 +119,15 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
> * Memory in the atomic DMA pools must be unencrypted, the pools do not
> * shrink so no re-encryption occurs in dma_direct_free().
> */
> - ret = set_memory_decrypted((unsigned long)page_to_virt(page),
> + if (dma_pool->unencrypted) {
> + ret = set_memory_decrypted((unsigned long)page_to_virt(page),
> 1 << order);
> - if (ret)
> - goto remove_mapping;
> - ret = gen_pool_add_virt(pool, (unsigned long)addr, page_to_phys(page),
> - pool_size, NUMA_NO_NODE);
> + if (ret)
> + goto remove_mapping;
> + }
> +
> + ret = gen_pool_add_virt(dma_pool->pool, (unsigned long)addr,
> + page_to_phys(page), pool_size, NUMA_NO_NODE);
> if (ret)
> goto encrypt_mapping;
>
> @@ -126,11 +135,15 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
> return 0;
>
> encrypt_mapping:
> - ret = set_memory_encrypted((unsigned long)page_to_virt(page),
> - 1 << order);
> - if (WARN_ON_ONCE(ret)) {
> - /* Decrypt succeeded but encrypt failed, purposely leak */
> - goto out;
> + if (dma_pool->unencrypted) {
> + int rc;
> +
> + rc = set_memory_encrypted((unsigned long)page_to_virt(page),
> + 1 << order);
> + if (WARN_ON_ONCE(rc)) {
> + /* Decrypt succeeded but encrypt failed, purposely leak */
> + goto out;
> + }
> }
> remove_mapping:
> #ifdef CONFIG_DMA_DIRECT_REMAP
> @@ -142,46 +155,52 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
> return ret;
> }
>
> -static void atomic_pool_resize(struct gen_pool *pool, gfp_t gfp)
> +static void atomic_pool_resize(struct dma_gen_pool *dma_pool, gfp_t gfp)
> {
> - if (pool && gen_pool_avail(pool) < atomic_pool_size)
> - atomic_pool_expand(pool, gen_pool_size(pool), gfp);
> + if (dma_pool->pool && gen_pool_avail(dma_pool->pool) < atomic_pool_size)
> + atomic_pool_expand(dma_pool, gen_pool_size(dma_pool->pool), gfp);
> }
>
> static void atomic_pool_work_fn(struct work_struct *work)
> {
> if (IS_ENABLED(CONFIG_ZONE_DMA))
> - atomic_pool_resize(atomic_pool_dma,
> + atomic_pool_resize(&atomic_pool_dma,
> GFP_KERNEL | GFP_DMA);
> if (IS_ENABLED(CONFIG_ZONE_DMA32))
> - atomic_pool_resize(atomic_pool_dma32,
> + atomic_pool_resize(&atomic_pool_dma32,
> GFP_KERNEL | GFP_DMA32);
> - atomic_pool_resize(atomic_pool_kernel, GFP_KERNEL);
> + atomic_pool_resize(&atomic_pool_kernel, GFP_KERNEL);
> }
>
> -static __init struct gen_pool *__dma_atomic_pool_init(size_t pool_size,
> - gfp_t gfp)
> +static __init struct dma_gen_pool *__dma_atomic_pool_init(struct dma_gen_pool *dma_pool,
> + size_t pool_size, gfp_t gfp)
> {
> - struct gen_pool *pool;
> int ret;
>
> - pool = gen_pool_create(PAGE_SHIFT, NUMA_NO_NODE);
> - if (!pool)
> + dma_pool->pool = gen_pool_create(PAGE_SHIFT, NUMA_NO_NODE);
> + if (!dma_pool->pool)
> return NULL;
>
> - gen_pool_set_algo(pool, gen_pool_first_fit_order_align, NULL);
> + gen_pool_set_algo(dma_pool->pool, gen_pool_first_fit_order_align, NULL);
> +
> + /* if platform is using memory encryption atomic pools are by default decrypted. */
> + if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
> + dma_pool->unencrypted = true;
> + else
> + dma_pool->unencrypted = false;
>
> - ret = atomic_pool_expand(pool, pool_size, gfp);
> + ret = atomic_pool_expand(dma_pool, pool_size, gfp);
> if (ret) {
> - gen_pool_destroy(pool);
> + gen_pool_destroy(dma_pool->pool);
> + dma_pool->pool = NULL;
> pr_err("DMA: failed to allocate %zu KiB %pGg pool for atomic allocation\n",
> pool_size >> 10, &gfp);
> return NULL;
> }
>
> pr_info("DMA: preallocated %zu KiB %pGg pool for atomic allocations\n",
> - gen_pool_size(pool) >> 10, &gfp);
> - return pool;
> + gen_pool_size(dma_pool->pool) >> 10, &gfp);
> + return dma_pool;
> }
>
> #ifdef CONFIG_ZONE_DMA32
> @@ -207,21 +226,22 @@ static int __init dma_atomic_pool_init(void)
>
> /* All memory might be in the DMA zone(s) to begin with */
> if (has_managed_zone(ZONE_NORMAL)) {
> - atomic_pool_kernel = __dma_atomic_pool_init(atomic_pool_size,
> - GFP_KERNEL);
> - if (!atomic_pool_kernel)
> + __dma_atomic_pool_init(&atomic_pool_kernel, atomic_pool_size, GFP_KERNEL);
> + if (!atomic_pool_kernel.pool)
> ret = -ENOMEM;
> }
> +
> if (has_managed_dma()) {
> - atomic_pool_dma = __dma_atomic_pool_init(atomic_pool_size,
> - GFP_KERNEL | GFP_DMA);
> - if (!atomic_pool_dma)
> + __dma_atomic_pool_init(&atomic_pool_dma, atomic_pool_size,
> + GFP_KERNEL | GFP_DMA);
> + if (!atomic_pool_dma.pool)
> ret = -ENOMEM;
> }
> +
> if (has_managed_dma32) {
> - atomic_pool_dma32 = __dma_atomic_pool_init(atomic_pool_size,
> - GFP_KERNEL | GFP_DMA32);
> - if (!atomic_pool_dma32)
> + __dma_atomic_pool_init(&atomic_pool_dma32, atomic_pool_size,
> + GFP_KERNEL | GFP_DMA32);
> + if (!atomic_pool_dma32.pool)
> ret = -ENOMEM;
> }
>
> @@ -230,19 +250,44 @@ static int __init dma_atomic_pool_init(void)
> }
> postcore_initcall(dma_atomic_pool_init);
>
> -static inline struct gen_pool *dma_guess_pool(struct gen_pool *prev, gfp_t gfp)
> +static inline struct dma_gen_pool *__dma_guess_pool(struct dma_gen_pool *first,
> + struct dma_gen_pool *second, struct dma_gen_pool *third)
> +{
> + if (first->pool)
> + return first;
> + if (second && second->pool)
> + return second;
> + if (third && third->pool)
> + return third;
> + return NULL;
> +}
> +
> +static inline struct dma_gen_pool *dma_guess_pool(struct dma_gen_pool *prev,
> + gfp_t gfp)
> {
> - if (prev == NULL) {
> + if (!prev) {
> if (gfp & GFP_DMA)
> - return atomic_pool_dma ?: atomic_pool_dma32 ?: atomic_pool_kernel;
> + return __dma_guess_pool(&atomic_pool_dma,
> + &atomic_pool_dma32,
> + &atomic_pool_kernel);
> +
> if (gfp & GFP_DMA32)
> - return atomic_pool_dma32 ?: atomic_pool_dma ?: atomic_pool_kernel;
> - return atomic_pool_kernel ?: atomic_pool_dma32 ?: atomic_pool_dma;
> + return __dma_guess_pool(&atomic_pool_dma32,
> + &atomic_pool_dma,
> + &atomic_pool_kernel);
> +
> + return __dma_guess_pool(&atomic_pool_kernel,
> + &atomic_pool_dma32,
> + &atomic_pool_dma);
> }
> - if (prev == atomic_pool_kernel)
> - return atomic_pool_dma32 ? atomic_pool_dma32 : atomic_pool_dma;
> - if (prev == atomic_pool_dma32)
> - return atomic_pool_dma;
> +
> + if (prev == &atomic_pool_kernel)
> + return __dma_guess_pool(&atomic_pool_dma32,
> + &atomic_pool_dma, NULL);
> +
> + if (prev == &atomic_pool_dma32)
> + return __dma_guess_pool(&atomic_pool_dma, NULL, NULL);
> +
> return NULL;
> }
>
> @@ -272,16 +317,20 @@ static struct page *__dma_alloc_from_pool(struct device *dev, size_t size,
> }
>
> struct page *dma_alloc_from_pool(struct device *dev, size_t size,
> - void **cpu_addr, gfp_t gfp,
> + void **cpu_addr, gfp_t gfp, unsigned long attrs,
> bool (*phys_addr_ok)(struct device *, phys_addr_t, size_t))
> {
> - struct gen_pool *pool = NULL;
> + struct dma_gen_pool *dma_pool = NULL;
> struct page *page;
> bool pool_found = false;
>
> - while ((pool = dma_guess_pool(pool, gfp))) {
> + while ((dma_pool = dma_guess_pool(dma_pool, gfp))) {
> +
> + if (dma_pool->unencrypted != !!(attrs & DMA_ATTR_CC_SHARED))
> + continue;
> +
> pool_found = true;
> - page = __dma_alloc_from_pool(dev, size, pool, cpu_addr,
> + page = __dma_alloc_from_pool(dev, size, dma_pool->pool, cpu_addr,
> phys_addr_ok);
> if (page)
> return page;
> @@ -296,12 +345,14 @@ struct page *dma_alloc_from_pool(struct device *dev, size_t size,
>
> bool dma_free_from_pool(struct device *dev, void *start, size_t size)
> {
> - struct gen_pool *pool = NULL;
> + struct dma_gen_pool *dma_pool = NULL;
> +
> + while ((dma_pool = dma_guess_pool(dma_pool, 0))) {
>
> - while ((pool = dma_guess_pool(pool, 0))) {
> - if (!gen_pool_has_addr(pool, (unsigned long)start, size))
> + if (!gen_pool_has_addr(dma_pool->pool, (unsigned long)start, size))
v3 of this just crashed here with dma_pool!=NULL but dma_pool->pool==NULL. continuing debugging... Thanks,
> continue;
> - gen_pool_free(pool, (unsigned long)start, size);
> +
> + gen_pool_free(dma_pool->pool, (unsigned long)start, size);
> return true;
> }
>
> diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
> index 1abd3e6146f4..ab4eccbaa076 100644
> --- a/kernel/dma/swiotlb.c
> +++ b/kernel/dma/swiotlb.c
> @@ -612,6 +612,7 @@ static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
> u64 phys_limit, gfp_t gfp)
> {
> struct page *page;
> + unsigned long attrs = 0;
>
> /*
> * Allocate from the atomic pools if memory is encrypted and
> @@ -623,8 +624,12 @@ static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
> if (!IS_ENABLED(CONFIG_DMA_COHERENT_POOL))
> return NULL;
>
> + /* swiotlb considered decrypted by default */
> + if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
> + attrs = DMA_ATTR_CC_SHARED;
> +
> return dma_alloc_from_pool(dev, bytes, &vaddr, gfp,
> - dma_coherent_ok);
> + attrs, dma_coherent_ok);
> }
>
> gfp &= ~GFP_ZONEMASK;
--
Alexey
^ permalink raw reply
* [PATCH 2/2] iommu/arm-smmu: Add interconnect bandwidth voting support
From: Bibek Kumar Patro @ 2026-05-16 12:34 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-kernel, iommu, devicetree, linux-kernel,
Bibek Kumar Patro
In-Reply-To: <20260516-smmu_interconnect_addition-v1-0-f889d933f5c1@oss.qualcomm.com>
On some SoCs the SMMU registers require an active interconnect
bandwidth vote to be accessible. While other clients typically
satisfy this requirement implicitly, certain corner cases (e.g.
during sleep/wakeup transitions) can leave the SMMU without a
vote, causing intermittent register access failures.
Add support for an optional interconnect path to the arm-smmu
driver and vote for bandwidth while the SMMU is active. The path
is acquired from DT if present and ignored otherwise.
The bandwidth vote is enabled before accessing SMMU registers
during probe and runtime resume, and released during runtime
suspend and on error paths.
Generally, from an architectural perspective, GEM_NOC and DDR are
expected to have an active vote whenever the adreno_smmu block is
powered on. In most common use cases, this requirement is implicitly
satisfied because other GPU-related clients (for example, the GMU
device) already hold a GEM_NOC vote when adreno_smmu is enabled.
However, there are certain corner cases, such as during sleep/wakeup
transitions, where the GEM_NOC vote can be removed before adreno_smmu
is powered down. If adreno_smmu is then accessed while the interconnect
vote is missing, it can lead to the observed failures. Because of the
precise ordering involved, this scenario is difficult to reproduce
consistently.
(also GDSC is involved in adreno usecases can have an independent vote)
Signed-off-by: Bibek Kumar Patro <bibek.patro@oss.qualcomm.com>
---
drivers/iommu/arm/arm-smmu/arm-smmu.c | 53 ++++++++++++++++++++++++++++++++++-
drivers/iommu/arm/arm-smmu/arm-smmu.h | 2 ++
2 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index 0bd21d206eb3e75c3b9fb1364cdc92e82c5aa499..aedf5edf8f9b2b75f80a61af66727b52a5b3ad49 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -53,6 +53,11 @@
#define MSI_IOVA_BASE 0x8000000
#define MSI_IOVA_LENGTH 0x100000
+/* Interconnect bandwidth vote values for the SMMU register access path */
+#define ARM_SMMU_ICC_AVG_BW 0
+#define ARM_SMMU_ICC_PEAK_BW_HIGH 1000
+#define ARM_SMMU_ICC_PEAK_BW_LOW 0
+
static int force_stage;
module_param(force_stage, int, S_IRUGO);
MODULE_PARM_DESC(force_stage,
@@ -86,6 +91,36 @@ static inline void arm_smmu_rpm_put(struct arm_smmu_device *smmu)
}
}
+static int arm_smmu_icc_get(struct arm_smmu_device *smmu)
+{
+ smmu->icc_path = devm_of_icc_get(smmu->dev, NULL);
+ if (IS_ERR(smmu->icc_path)) {
+ int err = PTR_ERR(smmu->icc_path);
+
+ if (err == -ENODATA) {
+ smmu->icc_path = NULL;
+ return 0;
+ }
+ return dev_err_probe(smmu->dev, err,
+ "failed to get interconnect path\n");
+ }
+ return 0;
+}
+
+static void arm_smmu_icc_enable(struct arm_smmu_device *smmu)
+{
+ if (smmu->icc_path)
+ WARN_ON(icc_set_bw(smmu->icc_path, ARM_SMMU_ICC_AVG_BW,
+ ARM_SMMU_ICC_PEAK_BW_HIGH));
+}
+
+static void arm_smmu_icc_disable(struct arm_smmu_device *smmu)
+{
+ if (smmu->icc_path)
+ WARN_ON(icc_set_bw(smmu->icc_path, ARM_SMMU_ICC_AVG_BW,
+ ARM_SMMU_ICC_PEAK_BW_LOW));
+}
+
static void arm_smmu_rpm_use_autosuspend(struct arm_smmu_device *smmu)
{
/*
@@ -2189,6 +2224,17 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
if (err)
return err;
+ /*
+ * Acquire and vote the interconnect path before accessing any SMMU
+ * registers (including ARM_SMMU_GR0_ID0 in arm_smmu_device_cfg_probe).
+ */
+ err = arm_smmu_icc_get(smmu);
+ if (err) {
+ clk_bulk_disable_unprepare(smmu->num_clks, smmu->clks);
+ return err;
+ }
+ arm_smmu_icc_enable(smmu);
+
err = arm_smmu_device_cfg_probe(smmu);
if (err)
return err;
@@ -2294,9 +2340,13 @@ static int __maybe_unused arm_smmu_runtime_resume(struct device *dev)
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
int ret;
+ arm_smmu_icc_enable(smmu);
+
ret = clk_bulk_enable(smmu->num_clks, smmu->clks);
- if (ret)
+ if (ret) {
+ arm_smmu_icc_disable(smmu);
return ret;
+ }
arm_smmu_device_reset(smmu);
@@ -2308,6 +2358,7 @@ static int __maybe_unused arm_smmu_runtime_suspend(struct device *dev)
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
clk_bulk_disable(smmu->num_clks, smmu->clks);
+ arm_smmu_icc_disable(smmu);
return 0;
}
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.h b/drivers/iommu/arm/arm-smmu/arm-smmu.h
index 26d2e33cd328b8278888585fc07a31485d9397e2..c00606a416b2f4bb44a35e5d67f6ef801df68e1c 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.h
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.h
@@ -15,6 +15,7 @@
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/device.h>
+#include <linux/interconnect.h>
#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/io-pgtable.h>
#include <linux/iommu.h>
@@ -335,6 +336,7 @@ struct arm_smmu_device {
int num_clks;
unsigned int *irqs;
struct clk_bulk_data *clks;
+ struct icc_path *icc_path;
spinlock_t global_sync_lock;
--
2.34.1
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: iommu: arm,smmu: Document optional interconnects property
From: Bibek Kumar Patro @ 2026-05-16 12:34 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-kernel, iommu, devicetree, linux-kernel,
Bibek Kumar Patro
In-Reply-To: <20260516-smmu_interconnect_addition-v1-0-f889d933f5c1@oss.qualcomm.com>
Some SoC implementations require a bandwidth vote on an interconnect
path before the SMMU register space is accessible. Add the optional
'interconnects' property to the binding to allow platform DT nodes
to describe this path.
The arm-smmu driver uses these properties to vote for bandwidth before
accessing any SMMU registers and releases the vote on runtime suspend.
Signed-off-by: Bibek Kumar Patro <bibek.patro@oss.qualcomm.com>
---
Documentation/devicetree/bindings/iommu/arm,smmu.yaml | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml
index 06fb5c8e7547cb7a92823adc2772b94f747376a6..5cbf944f2d3e178b3723d4dbaa19ee0d33446979 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml
@@ -243,6 +243,15 @@ properties:
minItems: 1
maxItems: 3
+ interconnects:
+ maxItems: 1
+ description:
+ Optional interconnect path to the SMMU register space. On some SoCs
+ the SMMU registers are only accessible after a bandwidth vote has been
+ placed on the interconnect fabric. When present the driver votes for
+ bandwidth on this path before accessing any SMMU registers and releases
+ the vote on runtime suspend.
+
nvidia,memory-controller:
description: |
A phandle to the memory controller on NVIDIA Tegra186 and later SoCs.
--
2.34.1
^ permalink raw reply related
* [PATCH 0/2] iommu/arm-smmu: Add interconnect bandwidth voting support
From: Bibek Kumar Patro @ 2026-05-16 12:34 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-kernel, iommu, devicetree, linux-kernel,
Bibek Kumar Patro
On some Qualcomm SoCs the SMMU register space is gated behind an
interconnect fabric that requires an active bandwidth vote before
registers can be accessed. In the common case this vote is held
implicitly by other clients (e.g. the GMU device holds a GEM_NOC
vote whenever the GPU is active), so the SMMU works without any
explicit vote from the driver.
However, during certain power transitions — specifically sleep/wakeup
sequences — the interconnect vote can be dropped before the SMMU is
powered down. If the SMMU is then accessed (e.g. during runtime
resume) while the vote is absent, register reads fail intermittently.
The precise ordering makes this difficult to reproduce consistently.
This series adds support for an optional interconnect path in the
arm-smmu driver. When an 'interconnects' property is present in the
SMMU device node, the driver acquires the path and votes for bandwidth
before any register access, releasing the vote on runtime suspend and
on error paths. Platforms that do not describe an interconnect path
are unaffected.
[PATCH 1/2] documents the optional 'interconnects' properties in the
arm,smmu DT binding.
[PATCH 2/2] implements the ICC path acquisition and bandwidth voting in
the arm-smmu driver.
Signed-off-by: Bibek Kumar Patro <bibek.patro@oss.qualcomm.com>
---
Bibek Kumar Patro (2):
dt-bindings: iommu: arm,smmu: Document optional interconnects property
iommu/arm-smmu: Add interconnect bandwidth voting support
.../devicetree/bindings/iommu/arm,smmu.yaml | 9 ++++
drivers/iommu/arm/arm-smmu/arm-smmu.c | 53 +++++++++++++++++++++-
drivers/iommu/arm/arm-smmu/arm-smmu.h | 2 +
3 files changed, 63 insertions(+), 1 deletion(-)
---
base-commit: e98d21c170b01ddef366f023bbfcf6b31509fa83
change-id: 20260516-smmu_interconnect_addition-d9567535e9d7
Best regards,
--
Bibek Kumar Patro <bibek.patro@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH v4 0/3] iio: adc: xilinx-ams: refactor alarm handling to table-driven design
From: Jonathan Cameron @ 2026-05-16 12:29 UTC (permalink / raw)
To: Salih Erim
Cc: Guilherme Ivo Bozi, anand.ashok.dumbre, andy, conall.ogriofa,
dlechner, manish.narani, michal.simek, nuno.sa, Jonathan.Cameron,
linux-arm-kernel, linux-iio
In-Reply-To: <0ae1a3d0-cc51-4d5f-997c-04d3114121ca@amd.com>
On Thu, 14 May 2026 02:21:37 +0100
Salih Erim <salih.erim@amd.com> wrote:
> Hi,
>
> On 5/13/2026 1:31 AM, Guilherme Ivo Bozi wrote:
>
> > v3 -> v4:
> > - Removed unnecessary 'event < 0' check for type e32
>
> Thanks for addressing the feedback.
> Tested on ZCU102 Rev1.0. Driver probes successfully with no errors.
>
> For the series:
> Reviewed-by: Salih Erim <salih.erim@amd.com>
> Tested-by: Salih Erim <salih.erim@amd.com>
>
Given I think the 1st patch is a defensive fix rather than one we
actually expect to ever see (and I don't want to delay the other two),
I've applied the whole series to the testing branch of iio.git.
Once I've caught up with the review backlog and the bots have finished
playing with it, I'll push that out as togreg which gets picked up for
linux-next.
Thanks,
Jonathan
> Salih
>
^ permalink raw reply
* Re: [PATCH] ASoC: sun4i-spdif: Use guard() for spin locks
From: Bui Duc Phuc @ 2026-05-16 11:56 UTC (permalink / raw)
To: sashiko-reviews, Mark Brown
Cc: linux-sunxi, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Marcus Cooper,
Chen Ni, linux-sound, linux-arm-kernel, linux-kernel
In-Reply-To: <CAABR9nGhwtBu9Yhbn-BTD1Scykp2h6eQbtQtT13DcnekUdum1A@mail.gmail.com>
Hi,
I am not entirely sure whether the issue is exactly as
described in the Sashiko review.
However, I also looked at a similar case in the
micfil_rate_set() function from the fsl_micfil driver:
https://elixir.bootlin.com/linux/v7.1-rc3/source/sound/soc/fsl/fsl_micfil.c
There, pm_runtime_resume_and_get() is also used to ensure
that the device is runtime-resumed before accessing the
registers.
To address the review comments and avoid mixing functional
changes with the cleanup/refactoring using guard/scoped_guard,
I am planning to split this into two separate patches:
Patch 1: replace spin_lock_irqsave() /
spin_unlock_irqrestore() with scoped_guard() / guard()
+ scoped_guard(spinlock_irqsave, &host->lock) {
regmap_read(...);
+ }
Patch 2: add runtime PM handling around register accesses
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
scoped_guard(spinlock_irqsave, &host->lock) {
regmap_read(...);
}
+ pm_runtime_put(dev);
This should make the review easier and clearly separate the
functional change from the refactoring.
Does this approach look reasonable to you?
If so, please let me know and I will prepare the
corresponding patches.
Best Regard,
Phuc
On Thu, May 14, 2026 at 6:22 PM Bui Duc Phuc <phucduc.bui@gmail.com> wrote:
>
> Thanks for the review.
>
> This seems to be pre-existing behavior rather than something
> introduced by this patch, since the current change only converts
> the locking to use guard() helpers and does not modify the
> runtime PM handling.
>
> If this turns out to be a real issue, it would probably be better
> addressed separately from this cleanup patch.
>
>
> On Thu, May 14, 2026 at 11:33 AM <sashiko-bot@kernel.org> wrote:
> >
> > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > - [High] Hardware State / PM Violation leading to bus hang
> > --
> >
> > commit 0686001584788d984a0f16403183bcad78fe4176
> > Author: bui duc phuc <phucduc.bui@gmail.com>
> >
> > ASoC: sun4i-spdif: Use guard() for spin locks
> >
> > This commit refactors the locking in the sun4i-spdif driver to use the
> > guard() macro for spinlocks. It replaces manual spin_lock_irqsave() and
> > spin_unlock_irqrestore() calls with scope-based cleanup.
> >
> > > diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
> > > --- a/sound/soc/sunxi/sun4i-spdif.c
> > > +++ b/sound/soc/sunxi/sun4i-spdif.c
> > > @@ -427,10 +427,9 @@ static int sun4i_spdif_get_status(struct snd_kcontrol *kcontrol,
> > > struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
> > > struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
> > > u8 *status = ucontrol->value.iec958.status;
> > > - unsigned long flags;
> > > unsigned int reg;
> > >
> > > - spin_lock_irqsave(&host->lock, flags);
> > > + guard(spinlock_irqsave)(&host->lock);
> > >
> > > regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA0, ®);
> >
> > While this issue isn't new to this patch, does this code risk a bus hang if
> > the device is runtime-suspended?
> >
> > The regmap configuration lacks a cache, and these ALSA kcontrols can be
> > accessed by userspace at any time. If the audio stream is idle, the device
> > will be runtime-suspended and its APB bus clock (apb_clk) will be disabled.
> >
> > Accessing the physical registers with the clock disabled will trigger an
> > asynchronous external abort.
> >
> > Would it make sense to ensure the device is runtime-resumed (perhaps via
> > pm_runtime_get_sync() outside the spinlock guard) before touching the
> > hardware?
> >
> > [ ... ]
> >
> > --
> > Sashiko AI review · https://sashiko.dev/#/patchset/20260513105003.81880-1-phucduc.bui@gmail.com?part=1
^ permalink raw reply
* Re: [RFC PATCH net-next 0/3] net: macb: candidate fixes for silent TX stall on BCM2712/RP1
From: Théo Lebrun @ 2026-05-16 9:36 UTC (permalink / raw)
To: Lukasz Raczylo, netdev
Cc: Andrea della Porta, Nicolas Ferre, Claudiu Beznea, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-kernel, linux-arm-kernel, linux-rpi-kernel
In-Reply-To: <20260514215129.33559-1-lukasz@raczylo.com>
Hello Lukasz,
On Thu May 14, 2026 at 11:51 PM CEST, Lukasz Raczylo wrote:
> Andrea, Théo --
>
> Thanks both. Replying to multiple points in one mail since they
> intersect.
Hum, you forgot some parts of my email to address. The good thing with
interleaved replies is that you see what you need to reply to.
https://www.kernel.org/doc/html/latest/process/submitting-patches.html#use-trimmed-interleaved-replies-in-email-discussions
No big deal.
> First a correction to the v1 cover: the "zero events post-patch"
> claim was true only at the user-space watchdog visibility level.
> With patch 3's warn made unconditional (which is what Andrea's
> review re-tested with on v6.19.10), kernel-level evidence on my
> side now matches what Andrea saw -- patches 1 and 2 are partial,
> patch 3 is empirically the load-bearing fix on this platform.
> The v2 cover acknowledges this and reframes.
[...]
> ## Théo -- the four questions on the cover
>
> Welcome to macb maintenance. Quick answers; raw 1 Hz traces,
> dmesg dumps, and event logs available on request for any of
> these.
Thanks!
> 1. Tx-only or Tx+Rx broken?
[...]
ACK, makes sense
> 2. Recovery: link down/up vs module reload vs power cycle?
[...]
ACK, makes sense
> 3. TSO / SG / EEE disabling helped some reporters?
>
> Mixed picture, with one gap in my matrix.
>
> Tested fleet-wide since 2026-04-24 as baseline before this
> series:
> * EEE off (--set-eee end0 eee off + advertise 0x0)
> * TSO off (-K tso off)
> * GSO off (-K gso off)
> * RX/TX rings 4096/2048 (default 512)
> * IRQ affinity moved off CPU0 to CPU3
> * CPU governor schedutil (default) -- earlier tested
> performance, no change
> * qdisc fq -> pfifo_fast
>
> With all of those, the stall still fires. Pre-patch
> fleet rate was multiple per hour with these knobs already
> applied.
>
> Untested by me: TSO + SG (scatter-gather, NETIF_F_SG) off
> *together*. That is the specific combination rtheobald
> (cilium#43198 comment 4188846955) and the launchpad
> commenter (#34) report as masking the stall -- "must be
> both, not just one". I tested TSO + GSO, not TSO + SG.
>
> Both patch 1 (PCIe-fabric race on TSTART) and patch 2
> (peripheral DMA retirement race on TX_USED) are
> consistent with descriptor-fragment-path interactions
> that SG-off would mask, so the workaround being real
> isn't surprising. Closing the race rather than masking
> it should still be the right thing for mainline. Happy
> to canary-test TSO+SG-off on one node if you want the
> data point before v2 review.
Honestly I was surprised by that comment and doubted it. I don't see how
those could be related. Don't bother testing the full matrix too much.
> 4. cdns,*-max-pipe DT props -- lead dead?
[...]
ACK makes sense
> ## My own audit -- two issues v2 fixes
>
> Worth disclosing before someone else catches them.
>
> * Patch 2 (v1) reads ISR in macb_tx_poll(), masks off
> everything except TCOMP, and discards the rest.
> raspberrypi_rp1_config does not set
> MACB_CAPS_ISR_CLEAR_ON_WRITE -- the driver assumes
> read-clear ISR semantics on RP1, and macb_interrupt()
> relies on processing every bit it reads in one pass for
> that case to be correct. My v1 patch breaks that contract:
> any RCOMP / ROVR / TXUBR / etc. set in ISR at the moment of
> my read is silently consumed and never processed. RCOMP
> being lost is the worst case (RX completion never scheduled
> until something else asserts the line). Race window is
> ~200-500 ns per macb_tx_poll completion; significant at
> moderate-to-heavy RX load on a level-triggered IRQ where
> the consumed bit drops the line before GIC delivery.
Careful, you mention your raspberrypi_rp1_config doesn't set
MACB_CAPS_ISR_CLEAR_ON_WRITE but there is a catch: we expect that cap
to come from runtime discovery, see macb_configure_caps() and
register DCFG1/0x0280.
My understanding is that read-to-clear is only for old variants and all
modern MACB/GEM instances use write-to-clear.
I don't expect your RP1 HW uses R2C; my past comment was about making
sure you support both W2C and R2C to avoid breaking the driver for
some.
We have `dev_dbg(..., "Cadence caps 0x%08x\n", bp->caps)` to dump caps,
including the runtime detected ones.
> v2 patch 2 drops the ISR read entirely and substitutes
> (void)queue_readl(queue, IMR). IMR is the read-only mask
> mirror, no side effects, still flushes prior peripheral
> DMA writes via PCIe ordering. Loses the "directly sample
> latched TCOMP" half of v1's claim; keeps the PCIe-barrier
> half (which is the half that actually addresses the
> documented race in the existing macb_tx_complete_pending()
> rmb() comment).
>
> * Patch 3 (v1) boot-time false positive described above to
> Andrea. v2 fixes:
>
> - netif_carrier_ok() gate -- no carrier, no completion is
> possible, don't fire.
>
> - tracks tail movement via a bool tx_stall_tail_moved set
> by macb_tx_complete() under tx_ptr_lock when tail
> advances, cleared by the watchdog tick on the same
> lock. Form suggested by pelwell when he reviewed the
> same series anchored against rpi-6.18.y at
> raspberrypi/linux#7340 (merged 2026-05-08); 13 days of
> fleet runtime in this form since 2026-05-02 across 24
> nodes. Folded into mainline v2 patch 3 directly rather
> than carried as a fix-up.
>
> - netdev_warn_once -> netdev_warn_ratelimited per
> Andrea's ask -- operator visibility doesn't disappear
> after the first fire.
>
>
> ## v2 follows
>
> Sending [PATCH net-next v2 0/3] threaded under the v1 cover
> shortly. Plan to drop "RFC" from the subject prefix (~3 weeks
> production runtime + the rpi in-tree merge tip the balance
> toward a regular PATCH); happy to revert to RFC if you'd
> prefer the iterative-review cadence.
[...]
So you skimmed over two parts of my initial comment:
- Upstream kernel? From your messages it seems you only build test.
My question is two-fold:
- (1) Can we confirm the bug reproduces on upstream?
- (2) For my personal knowledge, can you tell me why everyone uses
the Raspberry Pi vendor kernel? I see an upstream DT for rpi5.
I'll boot one on my desk next week (hopefully).
- .ndo_tx_timeout() is probably the right approach for [3/3].
---
My recommandation would be this:
Let's get a .ndo_tx_timeout() implementation merged that will mitigate
the issue. If you can prove [1/3] and/or [2/3] are useful, then we can
get those in as well. Otherwise we drop them and keep digging to find
the Tx stall reason(s).
Those are bug-fixes, HW is broken-ish for a good chunk of users, so they
should target net and not net-next. You need a `Fixes:` trailer, it
will probably date back a long way.
Thanks Lukasz!
--
Théo Lebrun, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* Re: [PATCH] firmware: imx: scu-irq: accumulate wakeup sources via sysfs_emit_at()
From: Greg KH @ 2026-05-16 9:20 UTC (permalink / raw)
To: Stepan Ionichev
Cc: Frank.Li, s.hauer, kernel, festevam, shawnguo, hcazarim, imx,
linux-arm-kernel, linux-kernel
In-Reply-To: <20260516090726.38362-1-sozdayvek@gmail.com>
On Sat, May 16, 2026 at 02:07:26PM +0500, Stepan Ionichev wrote:
> On Sat, May 16, 2026 at 09:15:54AM +0200, Greg Kroah-Hartman wrote:
> > This is a horrible sysfs file, and breaks all the rules. Why not just
> > delete it and use the proper api for it instead?
>
> Yeah, fair. The sprintf change just papers over the real issue, which
> is the interface itself.
>
> Quick check: no Documentation/ABI/ entry, github code search returns
> nothing outside the kernel tree itself
> (https://github.com/search?q=scu_wakeup_irqs&type=code), and
> codesearch.debian.net also shows zero hits. So removing the attribute
> looks doable.
>
> Frank, Sascha -- any out-of-tree readers of
> /sys/firmware/scu_wakeup_irqs/wakeup_src I should worry about? If
> not, I'll send v2 that drops the attribute and logs the wakeup source
> via dev_info() instead.
Close, but no, don't use dev_info(), when drivers work properly, they
are quiet. Make it dev_dbg() so that if anyone wants to see it, they
can dynamically turn it on. Or make it a debugfs file.
thanks,
greg k-h
^ permalink raw reply
* Re: [RFC PATCH 0/2] Optimize S2 page splitting
From: Marc Zyngier @ 2026-05-16 9:15 UTC (permalink / raw)
To: Leonardo Bras
Cc: Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, Fuad Tabba, Raghavendra Rao Ananta,
linux-arm-kernel, kvmarm, linux-kernel
In-Reply-To: <20260515195904.2466381-1-leo.bras@arm.com>
On Fri, 15 May 2026 20:59:01 +0100,
Leonardo Bras <leo.bras@arm.com> wrote:
>
> While playing with dirty-bit tracking, I decided to take a look on how page
> splitting works. Found out all entries are walked, even though we can infer,
> for instance that:
> - If a level-3 entry is walked, it means the parent level-2 entry is split
> - If a split just succeeded in an table entry, it means all children nodes
> are already split
>
> So I tried to optimize it in a way that it does not break other users.
>
> My main idea is to introduce positive return values that hint to the
> pagetable walking mechanism that either siblings or children can be
> skipped. That should be contained to the visitor function, that returns
> zero if no error was detected.
>
> Numbers on above optimization are promising:
> A 1GB VM, running on the model, splitting all at the beginning
> (no manual protect):
> - Memory was already split (4k pages): -97.33% runtime (-172ms) - 20 runs
> - THP backed memory: -19.82% runtime (-153ms) - 10 runs
> - 1x1GB hugetlb memory: -20.65% runtime (-150ms) - 10 runs
>
I haven't looked at the changes in details, but the methodology is
quite flawed. For a start, measuring anything on a software model
(QEMU or FVP) doesn't mean anything performance-wise. The trade-offs
are completely different from a HW implementation, and even the notion
of time is pretty inconsistent.
Please run this on actual HW. I'm sure your employer can give you
access to one of these mythical arm64 toys. Measure things from
userspace, not from the kernel, so that you have all the overheads.
Don't add console output, because that will make things far worse.
I'm sure you can hack one of the selftests for this purpose.
Thanks,
M.
--
Jazz isn't dead. It just smells funny.
^ permalink raw reply
* Re: [PATCH] firmware: imx: scu-irq: accumulate wakeup sources via sysfs_emit_at()
From: Stepan Ionichev @ 2026-05-16 9:07 UTC (permalink / raw)
To: gregkh
Cc: Frank.Li, s.hauer, kernel, festevam, shawnguo, hcazarim, imx,
linux-arm-kernel, linux-kernel, Stepan Ionichev
In-Reply-To: <2026051633-doorstep-sequence-6782@gregkh>
On Sat, May 16, 2026 at 09:15:54AM +0200, Greg Kroah-Hartman wrote:
> This is a horrible sysfs file, and breaks all the rules. Why not just
> delete it and use the proper api for it instead?
Yeah, fair. The sprintf change just papers over the real issue, which
is the interface itself.
Quick check: no Documentation/ABI/ entry, github code search returns
nothing outside the kernel tree itself
(https://github.com/search?q=scu_wakeup_irqs&type=code), and
codesearch.debian.net also shows zero hits. So removing the attribute
looks doable.
Frank, Sascha -- any out-of-tree readers of
/sys/firmware/scu_wakeup_irqs/wakeup_src I should worry about? If
not, I'll send v2 that drops the attribute and logs the wakeup source
via dev_info() instead.
Stepan
^ 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