* [PATCH v2 7/9] dt-bindings: adm1275: ROHM BD12790 hot-swap controller
From: Matti Vaittinen @ 2026-06-26 7:25 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1581 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Support ROHM BD12790 hot-swap controller which is largely compatible
with the Analog Devices adm1272.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
Revision history:
v1 => :
- Fixed double space from description
---
Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
index 503e93756584..283cd1662689 100644
--- a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
@@ -33,6 +33,9 @@ description: |
https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780muv-lb-e.pdf
https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780amuv-lb-e.pdf
+ The BD12790 is a ROHM hot-swap controller, functionally similar to the
+ ADM1272.
+
properties:
compatible:
oneOf:
@@ -47,6 +50,7 @@ properties:
- adi,adm1293
- adi,adm1294
- rohm,bd12780
+ - rohm,bd12790
- silergy,mc09c
# Require BD12780 as a fall-back for BD12780A.
@@ -103,6 +107,7 @@ allOf:
enum:
- adi,adm1272
- adi,adm1273
+ - rohm,bd12790
then:
properties:
adi,volt-curr-sample-average:
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 6/9] hwmon: adm1275: Support ROHM BD12780
From: Matti Vaittinen @ 2026-06-26 7:24 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 6290 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
ROHM BD12780 and BD12780A are hot-swap controllers. They are largely
similar to Analog Devices ADM1278. Besides the ID registers and some
added functionality, the BD12780 and BD12780A mark PMON_CONFIG bits
[15:14] as reserved. Hence TSFILT setting must be omitted on these ICs.
The BD12780 has 3 pins usable for configuring the I2C address. The
BD12780A lists the ADDR3-pin as "not connect".
Support ROHM BD12780 and BD12780A controllers.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v1 => v2: Changes based on the review by Guenter:
- Drop i2c_device_id for bd12780a
- Add own 'case' for the bd12780 instead of overloading the existing
one and still having an 'if (id == bd12780)' inside the case.
---
drivers/hwmon/pmbus/Kconfig | 2 +-
drivers/hwmon/pmbus/adm1275.c | 55 ++++++++++++++++++++++++++++++-----
2 files changed, 48 insertions(+), 9 deletions(-)
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 8f4bff375ecb..b3c27f3b2712 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -52,7 +52,7 @@ config SENSORS_ADM1275
help
If you say yes here you get hardware monitoring support for Analog
Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281,
- ADM1293, ADM1294 and SQ24905C Hot-Swap Controller and
+ ADM1293, ADM1294, ROHM BD12780, and SQ24905C Hot-Swap Controller and
Digital Power Monitors.
This driver can also be built as a module. If so, the module will
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index 1ea2037711e1..81c50fab7687 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -19,7 +19,7 @@
#include "pmbus.h"
enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
- adm1293, adm1294, sq24905c };
+ adm1293, adm1294, bd12780, sq24905c };
#define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0)
#define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5)
@@ -47,6 +47,8 @@ enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
#define ADM1278_VOUT_EN BIT(1)
#define ADM1278_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN | ADM1278_TSFILT)
+/* The BD12780 data sheets mark TSFILT bit as reserved. */
+#define BD12780_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)
#define ADM1293_IRANGE_25 0
#define ADM1293_IRANGE_50 BIT(6)
@@ -487,6 +489,7 @@ static const struct i2c_device_id adm1275_id[] = {
{ "adm1281", adm1281 },
{ "adm1293", adm1293 },
{ "adm1294", adm1294 },
+ { "bd12780", bd12780 },
{ "mc09c", sq24905c },
{ }
};
@@ -494,12 +497,13 @@ MODULE_DEVICE_TABLE(i2c, adm1275_id);
/* Enable VOUT & TEMP1 if not enabled (disabled by default) */
static int adm1275_enable_vout_temp(struct adm1275_data *data,
- struct i2c_client *client, int config)
+ struct i2c_client *client, int config,
+ u16 defconfig)
{
int ret;
- if ((config & ADM1278_PMON_DEFCONFIG) != ADM1278_PMON_DEFCONFIG) {
- config |= ADM1278_PMON_DEFCONFIG;
+ if ((config & defconfig) != defconfig) {
+ config |= defconfig;
ret = adm1275_write_pmon_config(data, client, config);
if (ret < 0) {
dev_err(&client->dev, "Failed to enable VOUT/TEMP1 monitoring\n");
@@ -535,7 +539,8 @@ static int adm1275_probe(struct i2c_client *client)
return ret;
}
if ((ret != 3 || strncmp(block_buffer, "ADI", 3)) &&
- (ret != 2 || strncmp(block_buffer, "SY", 2))) {
+ (ret != 2 || strncmp(block_buffer, "SY", 2)) &&
+ (ret != 4 || strncmp(block_buffer, "ROHM", 4))) {
dev_err(&client->dev, "Unsupported Manufacturer ID\n");
return -ENODEV;
}
@@ -562,7 +567,7 @@ static int adm1275_probe(struct i2c_client *client)
if (mid->driver_data == adm1272 || mid->driver_data == adm1273 ||
mid->driver_data == adm1278 || mid->driver_data == adm1281 ||
mid->driver_data == adm1293 || mid->driver_data == adm1294 ||
- mid->driver_data == sq24905c)
+ mid->driver_data == bd12780 || mid->driver_data == sq24905c)
config_read_fn = i2c_smbus_read_word_data;
else
config_read_fn = i2c_smbus_read_byte_data;
@@ -666,7 +671,8 @@ static int adm1275_probe(struct i2c_client *client)
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
- ret = adm1275_enable_vout_temp(data, client, config);
+ ret = adm1275_enable_vout_temp(data, client, config,
+ ADM1278_PMON_DEFCONFIG);
if (ret)
return ret;
@@ -728,13 +734,45 @@ static int adm1275_probe(struct i2c_client *client)
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
- ret = adm1275_enable_vout_temp(data, client, config);
+ ret = adm1275_enable_vout_temp(data, client, config,
+ ADM1278_PMON_DEFCONFIG);
if (ret)
return ret;
if (config & ADM1278_VIN_EN)
info->func[0] |= PMBUS_HAVE_VIN;
break;
+
+ /*
+ * The BD12780 is almost functionally identical with the adm1278 above.
+ * Only differences visible to the driver are lack of TSFILT bits and
+ * different identification register contents.
+ */
+ case bd12780:
+ data->have_vout = true;
+ data->have_pin_max = true;
+ data->have_temp_max = true;
+ data->have_power_sampling = true;
+
+ coefficients = adm1278_coefficients;
+ vindex = 0;
+ cindex = 1;
+ pindex = 2;
+ tindex = 3;
+
+ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+
+ ret = adm1275_enable_vout_temp(data, client, config,
+ BD12780_PMON_DEFCONFIG);
+ if (ret)
+ return ret;
+
+ if (config & ADM1278_VIN_EN)
+ info->func[0] |= PMBUS_HAVE_VIN;
+
+ break;
case adm1293:
case adm1294:
data->have_iout_min = true;
@@ -880,6 +918,7 @@ static const struct of_device_id adm1275_of_match[] = {
{ .compatible = "adi,adm1281", },
{ .compatible = "adi,adm1293", },
{ .compatible = "adi,adm1294", },
+ { .compatible = "rohm,bd12780", },
{ .compatible = "silergy,mc09c", },
{ }
};
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 5/9] doc: Add ROHM BD12780 and BD12780A
From: Matti Vaittinen @ 2026-06-26 7:24 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1186 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Add the ROHM BD12780 and the BD12780A to the list of the ICs supported by
the adm1275 driver.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v1 => :
- No changes
---
Documentation/hwmon/adm1275.rst | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Documentation/hwmon/adm1275.rst b/Documentation/hwmon/adm1275.rst
index cf923f20fa52..8a793dd2b412 100644
--- a/Documentation/hwmon/adm1275.rst
+++ b/Documentation/hwmon/adm1275.rst
@@ -67,6 +67,22 @@ Supported chips:
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1293_1294.pdf
+ * ROHM Semiconductor BD12780
+
+ Prefix: 'bd12780'
+
+ Addresses scanned: -
+
+ Datasheet: https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780muv-lb-e.pdf
+
+ * ROHM Semiconductor BD12780A
+
+ Prefix: 'bd12780'
+
+ Addresses scanned: -
+
+ Datasheet: https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780amuv-lb-e.pdf
+
* Silergy SQ24905C
Prefix: 'mc09c'
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 4/9] hwmon: adm1275: Support module auto-loading
From: Matti Vaittinen @ 2026-06-26 7:24 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1948 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Populating the i2c_device_id -table is not enough to make the
driver module automatically load when device-tree node for the
power-monitor is parsed at boot.
Adding the of_device_id tables causes the driver module to be
automatically load at boot. Testing has been done with rather old Debian
system.
When inspecting the generated module-aliases with the insmod, following
entries seem to be the difference:
alias: of:N*T*Cadi,adm1075C*
alias: of:N*T*Cadi,adm1075
I suspect these are required for the module loading to work.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v1 => v2:
- New patch as discussed with Guenter here:
https://lore.kernel.org/all/f080e20e-6ec7-4744-9794-0a92d03f48d8@roeck-us.net/
---
drivers/hwmon/pmbus/adm1275.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index ccc3ad21e38e..1ea2037711e1 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -870,9 +870,25 @@ static int adm1275_probe(struct i2c_client *client)
return pmbus_do_probe(client, info);
}
+static const struct of_device_id adm1275_of_match[] = {
+ { .compatible = "adi,adm1075", },
+ { .compatible = "adi,adm1272", },
+ { .compatible = "adi,adm1273", },
+ { .compatible = "adi,adm1275", },
+ { .compatible = "adi,adm1276", },
+ { .compatible = "adi,adm1278", },
+ { .compatible = "adi,adm1281", },
+ { .compatible = "adi,adm1293", },
+ { .compatible = "adi,adm1294", },
+ { .compatible = "silergy,mc09c", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adm1275_of_match);
+
static struct i2c_driver adm1275_driver = {
.driver = {
.name = "adm1275",
+ .of_match_table = adm1275_of_match,
},
.probe = adm1275_probe,
.id_table = adm1275_id,
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 3/9] hwmon: adm1275: Detect coefficient overflow
From: Matti Vaittinen @ 2026-06-26 7:23 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2645 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Sashiko detected potential coefficient overflow if large shunt resistor
is used. When going unnoticed it can cause "drastically incorrect
telemetry scaling factors" as Sashiko put it.
I am not convinced such "drastically incorrect telemetry scaling
factors" could have gone unnoticed, so I suspect such large shunt
resistors aren't really used. Well, it shouldn't hurt to detect the
error and abort the probe before Really Wrong current / power -values
are reported to user by the hwmon.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v1 => v2:
- New patch
This patch returns -EOVERFLOW with an error print if overflow is
detected. IF there really are systems where the overflow truly occurs,
then this change will cause the probe to fail - which might hurt the
boot process. It might be safer to only print the warning. One could
also try changing the order of the shunt resistor value division (/1000)
and the multiplication and see if overflow goes away - but it'll be
somewhat more complex then. Hence, I just decided to error-out if this
happens, and leave this for the people facing the real overflow to fix
(if needed)... It's still fair to mention this might cause issues.
---
drivers/hwmon/pmbus/adm1275.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index 43baa5ded35e..ccc3ad21e38e 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -839,15 +839,25 @@ static int adm1275_probe(struct i2c_client *client)
info->R[PSC_VOLTAGE_OUT] = coefficients[voindex].R;
}
if (cindex >= 0) {
+ u32 m;
+
/* Scale current with sense resistor value */
- info->m[PSC_CURRENT_OUT] =
- coefficients[cindex].m * shunt / 1000;
+ if (unlikely(check_mul_overflow(coefficients[cindex].m, shunt, &m))) {
+ dev_err(&client->dev, "Current coefficient overflow\n");
+ return -EOVERFLOW;
+ }
+ info->m[PSC_CURRENT_OUT] = m / 1000;
info->b[PSC_CURRENT_OUT] = coefficients[cindex].b;
info->R[PSC_CURRENT_OUT] = coefficients[cindex].R;
}
if (pindex >= 0) {
- info->m[PSC_POWER] =
- coefficients[pindex].m * shunt / 1000;
+ u32 m;
+
+ if (unlikely(check_mul_overflow(coefficients[pindex].m, shunt, &m))) {
+ dev_err(&client->dev, "Power coefficient overflow\n");
+ return -EOVERFLOW;
+ }
+ info->m[PSC_POWER] = m / 1000;
info->b[PSC_POWER] = coefficients[pindex].b;
info->R[PSC_POWER] = coefficients[pindex].R;
}
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* Re: [PATCH v5 1/7] dt-bindings: display: verisilicon,dc: generalize for single-output variants
From: Conor Dooley @ 2026-06-26 7:22 UTC (permalink / raw)
To: Conor Dooley
Cc: Joey Lu, zhengxingda, maarten.lankhorst, mripard, tzimmermann,
airlied, simona, robh, krzk+dt, conor+dt, ychuang3, schung, yclu4,
dri-devel, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20260625-bobbing-annotate-d1c4d6874ee2@spud>
[-- Attachment #1: Type: text/plain, Size: 4397 bytes --]
On Thu, Jun 25, 2026 at 05:33:37PM +0100, Conor Dooley wrote:
> On Thu, Jun 25, 2026 at 05:44:43PM +0800, Joey Lu wrote:
> > The verisilicon,dc binding was originally written for the T-Head TH1520
> > SoC carrying a DC8200, and hard-codes five clocks, three resets and two
> > output ports.
> >
> > Add the Nuvoton MA35D1 DCUltraLite (nuvoton,ma35d1-dcu) to the binding.
> > The DCUltraLite uses only two clocks (core, pix0) and one reset (core),
> > with a single output port.
> >
> > Use allOf/if blocks to express per-variant constraints rather than
> > hard-coding the DC8200 topology at the top level. Each compatible's
> > block constrains the clock and reset item counts; the nuvoton block
> > additionally overrides clock-names to the two names it actually uses.
> >
> > Signed-off-by: Joey Lu <a0987203069@gmail.com>
> > ---
> > .../bindings/display/verisilicon,dc.yaml | 57 +++++++++++++++++++
> > 1 file changed, 57 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
> > index 9dc35ab973f2..1e751f3c7ce8 100644
> > --- a/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
> > +++ b/Documentation/devicetree/bindings/display/verisilicon,dc.yaml
> > @@ -17,6 +17,7 @@ properties:
> > items:
> > - enum:
> > - thead,th1520-dc8200
> > + - nuvoton,ma35d1-dcu
> > - const: verisilicon,dc # DC IPs have discoverable ID/revision registers
> >
> > reg:
> > @@ -77,6 +78,62 @@ required:
> > - clock-names
> > - ports
> >
> > +allOf:
> > + - if:
> > + properties:
> > + compatible:
> > + contains:
> > + const: thead,th1520-dc8200
> > + then:
> > + properties:
> > + clocks:
> > + minItems: 5
> > + maxItems: 5
> > +
> > + clock-names:
> > + minItems: 5
> > + maxItems: 5
>
> All the maxItems here repeat the maximum constraint and do nothing.
>
> Since you didn't change the minimum constraint at the top level, your
> minItems also do nothing.
>
> > +
> > + resets:
> > + minItems: 3
> > + maxItems: 3
> > +
> > + reset-names:
> > + minItems: 3
> > + maxItems: 3
> > +
> > + required:
> > + - resets
> > + - reset-names
>
> Both conditional sections have this, but the original binding doesn't
> require these for the thead device. This is a functional change
> therefore and shouldn't be in a patch calling itself "generalise for
> single ended variants".
>
> FWIW, adding your new compatible shouldn't really be in a patch with
> that subject either, it really should say "add support for nuvoton
> ma35d1" or something.
>
> > +
> > + - if:
> > + properties:
> > + compatible:
> > + contains:
> > + const: nuvoton,ma35d1-dcu
> > + then:
> > + properties:
> > + clocks:
> > + minItems: 2
>
> Anything that updates the minimum constraint should be done at the top
> level of this schema. The conditional section should then tighten the
> constraint, in this case that means only having maxItems.
>
> > + maxItems: 2
> > +
> > + clock-names:
> > + items:
> > + - const: core
> > + - const: pix0
>
> Does this even work when the top level schema thinks clock 2 should be
> called axi?
Additionally here, only have core and pix0 seems like it might be an
oversimplification. I doubt removing the second output port means that
the axi and ahb clocks are no longer needed.
Is it the case that your device supplies the same clock to core, ahb and
axi? If so, then you should fill those clocks in in your devicetree and
this can just constrain the number of clocks/clock-names to 4.
>
> > +
> > + resets:
> > + minItems: 1
> > + maxItems: 1
> > +
> > + reset-names:
> > + items:
> > + - const: core
>
> This is just maxItems: 1.
>
> pw-bot: changes-requested
>
> Thanks,
> Conor.
>
> > +
> > + required:
> > + - resets
> > + - reset-names
> > +
> > additionalProperties: false
> >
> > examples:
> > --
> > 2.43.0
> >
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* [PATCH v2 2/9] hwmon: adm1275: Prevent reading uninitialized stack
From: Matti Vaittinen @ 2026-06-26 7:22 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1975 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
While adding support for the ROHM BD127X0 hot-swap controllers, sashiko
reported an error in device-name comparison, which can lead to reading
uninitialized stack memory.
Quoting Sashiko:
This is a pre-existing issue, but I noticed that just before this block in
adm1275_probe(), there might be an out-of-bounds stack read:
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer);
if (ret < 0) { ... }
for (mid = adm1275_id; mid->name[0]; mid++) {
if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
break;
}
Since i2c_smbus_read_block_data() reads up to 32 bytes into the
uninitialized stack array block_buffer without appending a null
terminator, strncasecmp() could read past the valid bytes returned in ret.
For example, if the device returns a shorter string like "adm12", checking
it against "adm1275" up to the length of "adm1275" will continue reading
into uninitialized stack bounds.
Prevent reading uninitialized memory by zeroing the stack array.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Fixes: 87102808d039 ("hwmon: (pmbus/adm1275) Validate device ID")
---
Revision history:
v1 => v2:
- New patch
---
drivers/hwmon/pmbus/adm1275.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index bc2a6a07dc3e..43baa5ded35e 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -512,7 +512,7 @@ static int adm1275_enable_vout_temp(struct adm1275_data *data,
static int adm1275_probe(struct i2c_client *client)
{
s32 (*config_read_fn)(const struct i2c_client *client, u8 reg);
- u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
+ u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1] = {0};
int config, device_config;
int ret;
struct pmbus_driver_info *info;
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 1/9] dt-bindings: adm1275: ROHM BD12780 hot-swap controller
From: Matti Vaittinen @ 2026-06-26 7:21 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2599 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Support ROHM BD12780 and BD12780A hot-swap controllers, which are largely
compatible with the Analog Devices adm1278. Main difference between
the BD12780 and the BD12780A is, that the BD12780 has one I2C address
configuration pin more (ADDR3) than the BD12780A.
Introduce own compatibles for both variants but require the BD12780A to
always have the BD12780 as a fall-back.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v1 => v2:
- Drop extra -items from the compatible list as suggested by Krzysztof
---
.../bindings/hwmon/adi,adm1275.yaml | 38 +++++++++++++------
1 file changed, 27 insertions(+), 11 deletions(-)
diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
index d6a7517f2a50..503e93756584 100644
--- a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
@@ -25,19 +25,34 @@ description: |
https://www.silergy.com/
download/downloadFile?id=5669&type=product&ftype=note
+ The BD12780 and BD12780A are hot-swap controllers from ROHM. They are
+ functionally compatible with the ADM1278. The main difference between
+ the BD12780A and the BD12780 is amount of configurable I2C addresses.
+
+ Datasheets:
+ https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780muv-lb-e.pdf
+ https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780amuv-lb-e.pdf
+
properties:
compatible:
- enum:
- - adi,adm1075
- - adi,adm1272
- - adi,adm1273
- - adi,adm1275
- - adi,adm1276
- - adi,adm1278
- - adi,adm1281
- - adi,adm1293
- - adi,adm1294
- - silergy,mc09c
+ oneOf:
+ - enum:
+ - adi,adm1075
+ - adi,adm1272
+ - adi,adm1273
+ - adi,adm1275
+ - adi,adm1276
+ - adi,adm1278
+ - adi,adm1281
+ - adi,adm1293
+ - adi,adm1294
+ - rohm,bd12780
+ - silergy,mc09c
+
+ # Require BD12780 as a fall-back for BD12780A.
+ - items:
+ - const: rohm,bd12780a
+ - const: rohm,bd12780
reg:
maxItems: 1
@@ -104,6 +119,7 @@ allOf:
- adi,adm1281
- adi,adm1293
- adi,adm1294
+ - rohm,bd12780
- silergy,mc09c
then:
properties:
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 0/9] Support ROHM BD127x0 hot-swap controllers
From: Matti Vaittinen @ 2026-06-26 7:21 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
[-- Attachment #1: Type: text/plain, Size: 1924 bytes --]
Support ROHM BD12780(A) and BD12790
The BD12780 and BD12780A hot-swap controllers are very similar to Analog
Devices ADM1278. There are only some minor differences in the registers.
The BD12790 is largely similar to the ADM1272, with slightly different
coefficients and minor register changes.
This series adds basic support for these ROHM ICs.
Additionally, this series contains couple of fixes, which can be applied
independently from the bd127x0 support patches. Fixes are:
Patch 2/9: Prevent read from uninitialized stack (found by Sashiko)
Patch 3/9: Prevent coefficient overflow with larget shunt resistor (found
by Sashiko)
Patch 4/9: Support module auto-loading when DT is used.
Revision history:
v1 => v2:
dt-bindings:
- Fix compatible list as suggested by Krzysztof
adm1275:
- Add patches to fix issues pointed by Sashiko.
- Add of_device_ids for all supported devices.
- Add own switch 'case's for the new ICs instead of overloading
existing ones
- Drop i2c_device_id for bd12780a
- Don't use IC-wildcard in macro name.
---
Matti Vaittinen (9):
dt-bindings: adm1275: ROHM BD12780 hot-swap controller
hwmon: adm1275: Prevent reading uninitialized stack
hwmon: adm1275: Detect coefficient overflow
hwmon: adm1275: Support module auto-loading
doc: Add ROHM BD12780 and BD12780A
hwmon: adm1275: Support ROHM BD12780
dt-bindings: adm1275: ROHM BD12790 hot-swap controller
doc: adm1275: Add ROHM BD12790
hwmon: adm1275: Support ROHM BD12790
.../bindings/hwmon/adi,adm1275.yaml | 43 +++--
Documentation/hwmon/adm1275.rst | 24 +++
drivers/hwmon/pmbus/Kconfig | 4 +-
drivers/hwmon/pmbus/adm1275.c | 162 ++++++++++++++++--
4 files changed, 208 insertions(+), 25 deletions(-)
base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v5 1/7] dt-bindings: display: verisilicon,dc: generalize for single-output variants
From: Conor Dooley @ 2026-06-26 7:19 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Conor Dooley, Joey Lu, maarten.lankhorst, mripard, tzimmermann,
airlied, simona, robh, krzk+dt, conor+dt, ychuang3, schung, yclu4,
dri-devel, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <e3fe23ddbc504879bd797bbaa595d3653fa139ff.camel@iscas.ac.cn>
[-- Attachment #1: Type: text/plain, Size: 2657 bytes --]
On Fri, Jun 26, 2026 at 01:27:21PM +0800, Icenowy Zheng wrote:
> 在 2026-06-25四的 17:33 +0100,Conor Dooley写道:
> > On Thu, Jun 25, 2026 at 05:44:43PM +0800, Joey Lu wrote:
> > > +allOf:
> > > + - if:
> > > + properties:
> > > + compatible:
> > > + contains:
> > > + const: thead,th1520-dc8200
> > > + then:
> > > + properties:
> > > + clocks:
> > > + minItems: 5
> > > + maxItems: 5
> > > +
> > > + clock-names:
> > > + minItems: 5
> > > + maxItems: 5
> >
> > All the maxItems here repeat the maximum constraint and do nothing.
> >
> > Since you didn't change the minimum constraint at the top level, your
> > minItems also do nothing.
> >
> > > +
> > > + resets:
> > > + minItems: 3
> > > + maxItems: 3
> > > +
> > > + reset-names:
> > > + minItems: 3
> > > + maxItems: 3
> > > +
> > > + required:
> > > + - resets
> > > + - reset-names
> >
> > Both conditional sections have this, but the original binding doesn't
> > require these for the thead device. This is a functional change
> > therefore and shouldn't be in a patch calling itself "generalise for
> > single ended variants".
>
> Well yes they're required.
>
> Should I send a patch adding the `thead,th1520-dc8200` part of the
> schema?
If you mean the code above, no. Adding a conditional section when
there's only that compatible doesn't make sense.
What you could do is just add it at the top level though, which would
also benefit this patch since it'd not have to be conditionally added
for the new nuvoton device.
Just note in your commit message about what the ABI impact of the change
to required properties is (effectively nothing because it's optional in
the driver and the only user has the properties).
> > > +
> > > + resets:
> > > + minItems: 1
> > > + maxItems: 1
> > > +
> > > + reset-names:
> > > + items:
> > > + - const: core
> >
> > This is just maxItems: 1.
>
> Well the implicit rules of DT binding schemas are quite weird...
I don't think it is that strange, as the binding has
reset-names:
items:
- const: core
- const: axi
- const: ahb
so just constraining to one item is the simplest way to do this without
duplication.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: Re: [PATCH 3/7] riscv: dts: eswin: eic7700: add pinctrl support
From: Conor Dooley @ 2026-06-26 7:05 UTC (permalink / raw)
To: Yulin Lu
Cc: Conor Dooley, Pinkesh Vaghela, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, devicetree, linux-kernel, linux-riscv,
Min Lin, Samuel Holland, Darshan Prajapati, Pritesh Patel
In-Reply-To: <55962658.7bea.19f02850fe2.Coremail.luyulin@eswincomputing.com>
[-- Attachment #1: Type: text/plain, Size: 4480 bytes --]
On Fri, Jun 26, 2026 at 02:01:42PM +0800, Yulin Lu wrote:
> Hi, Conor. Thanks for your review.
>
> > On Mon, Jun 15, 2026 at 05:50:12PM +0530, Pinkesh Vaghela wrote:
> > > From: Yulin Lu <luyulin@eswincomputing.com>
> > >
> > > Add pinctrl node and related pin configuration for EIC7700 SoC
> > >
> > > Co-developed-by: Pritesh Patel <pritesh.patel@einfochips.com>
> > > Signed-off-by: Pritesh Patel <pritesh.patel@einfochips.com>
> > > Signed-off-by: Yulin Lu <luyulin@eswincomputing.com>
> > > Signed-off-by: Pinkesh Vaghela <pinkesh.vaghela@einfochips.com>
> > > ---
> > > .../dts/eswin/eic7700-hifive-premier-p550.dts | 109 +++
> > > .../riscv/boot/dts/eswin/eic7700-pinctrl.dtsi | 888 ++++++++++++++++++
> > > arch/riscv/boot/dts/eswin/eic7700.dtsi | 5 +
> > > 3 files changed, 1002 insertions(+)
> > > create mode 100644 arch/riscv/boot/dts/eswin/eic7700-pinctrl.dtsi
> > >
> > > diff --git a/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts b/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > > index 1fb92f0e7c55..e7bb96e14958 100644
> > > --- a/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > > +++ b/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > > @@ -6,6 +6,7 @@
> > > /dts-v1/;
> > >
> > > #include "eic7700.dtsi"
> > > +#include "eic7700-pinctrl.dtsi"
> > >
>
> ...
>
> > > +&gpio79_pins {
> > > + bias-disable;
> > > + input-disable;
> > > +};
> > > +
> > > +&gpio80_pins {
> > > + bias-pull-up;
> > > + input-disable;
> > > +};
> > > +
> > > +&gpio82_pins {
> > > + bias-pull-up;
> > > + input-disable;
> > > +};
> > > +
> > > +&gpio84_pins {
> > > + bias-disable;
> > > + input-disable;
> > > +};
> > > +
> > > +&gpio85_pins {
> > > + bias-pull-up;
> > > + input-disable;
> > > +};
> > > +
> > > +&gpio94_pins {
> > > + bias-disable;
> > > + input-disable;
> > > +};
> > > +
> > > +&gpio106_pins {
> > > + bias-disable;
> > > + input-disable;
> > > +};
> > > +
> > > +&gpio111_pins {
> > > + bias-disable;
> > > + input-disable;
> > > +};
> > > +
> > > +&pinctrl {
> > > + vrgmii-supply = <&vcc_1v8>;
> > > +};
> > > +
> > > &uart0 {
> > > status = "okay";
> > > };
> > > diff --git a/arch/riscv/boot/dts/eswin/eic7700-pinctrl.dtsi b/arch/riscv/boot/dts/eswin/eic7700-pinctrl.dtsi
> > > new file mode 100644
> > > index 000000000000..7293df146aa7
> > > --- /dev/null
> > > +++ b/arch/riscv/boot/dts/eswin/eic7700-pinctrl.dtsi
> > > @@ -0,0 +1,888 @@
> > > +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> > > +/*
> > > + * Copyright (c) 2025 Beijing ESWIN Computing Technology Co., Ltd.
> > > + *
> > > + * ESWIN's EIC7700 SoC pin-mux and pin-config options are listed as
> > > + * device tree nodes in this file.
> > > + *
> > > + * Authors: Yulin Lu <luyulin@eswincomputing.com>
> > > + */
> > > +
> >
> > I don't really understand the groups here. I think you should make more
> > effort to put more pins in each group.
> >
> > > + gpio1_pins: gpio1-pins {
> > > + pins = "jtag0_tck";
> > > + function = "gpio";
> > > + };
> > > +
> > > + gpio2_pins: gpio2-pins {
> > > + pins = "jtag0_tms";
> > > + function = "gpio";
> > > + };
> > > +
> > > + gpio3_pins: gpio3-pins {
> > > + pins = "jtag0_tdi";
> > > + function = "gpio";
> > > + };
> > > +
> > > + gpio4_pins: gpio4-pins {
> > > + pins = "jtag0_tdo";
> > > + function = "gpio";
> > > + };
> >
> > Like these 4 for example, why not group these?
>
> The 'group' is used to correspond to the '-grp' tag in the YAML file and
> has no practical significance.
> Different board designs have different requirements for pin multiplexing.
> Therefore, eic7700-pinctrl.dtsi only provides pins for the board-level DTS.
> Pins are combined and used in the board-level DTS via pinctrl-0 property.
These 4 pins in the driver are represented as:
EIC7700_PIN(14, "jtag0_tck", [0] = F_JTAG, [1] = F_SPI, [2] = F_GPIO),
EIC7700_PIN(15, "jtag0_tms", [0] = F_JTAG, [1] = F_SPI, [2] = F_GPIO),
EIC7700_PIN(16, "jtag0_tdi", [0] = F_JTAG, [1] = F_SPI, [2] = F_GPIO),
EIC7700_PIN(17, "jtag0_tdo", [0] = F_JTAG, [1] = F_SPI, [2] = F_GPIO),
EIC7700_PIN(18, "gpio5", [0] = F_GPIO, [1] = F_SPI),
There is no reason to ever partially use these as GPIO. Either they will
be all jtag, all spi or all gpio. pin 18 on the other than makes sense to have
in a dedicated group.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH RFC 1/4] media: imx8mq-mipi-csi2: Make reset release SoC-specific
From: Philipp Zabel @ 2026-06-26 6:47 UTC (permalink / raw)
To: Vincent Cloutier, linux-media, devicetree, linux-arm-kernel
Cc: linux-kernel, linux-imx, kernel, Vincent Cloutier,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Mauro Carvalho Chehab, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, imx
In-Reply-To: <20260626000715.1111803-2-vincent.cloutier@icloud.com>
On Do, 2026-06-25 at 20:06 -0400, Vincent Cloutier wrote:
> From: Vincent Cloutier <vincent@cloutier.co>
>
> The CSI-2 software reset helper currently asserts the reset control and
> then releases it again unconditionally.
>
> That release step is required by the i.MX8QXP path, but it changes the
> reset sequence used by i.MX8MQ. On Librem 5r4, which is i.MX8MQ-based,
> the unconditional release step prevents the camera pipeline from producing
> frames after reset; captures time out waiting for EOF from the CSI bridge.
>
> This series enables the Librem 5 rear camera on the second i.MX8MQ CSI-2
> receiver. Keep the i.MX8MQ path on the known-working assert-only software
> reset sequence while preserving the explicit release step for i.MX8QXP.
>
> Make reset release opt-in through platform data.
>
> Tested on Librem 5r4 with the existing HI846 front camera and the S5K3L6
> rear camera added by this series.
I think you are missing [1] ("reset: imx7: Correct polarity of MIPI CSI
resets on i.MX8MQ") instead.
[1] https://lore.kernel.org/all/20260619073115.3778313-1-robby.cai@oss.nxp.com/
regards
Philipp
^ permalink raw reply
* Re: Re: [PATCH 3/7] riscv: dts: eswin: eic7700: add pinctrl support
From: Yulin Lu @ 2026-06-26 6:42 UTC (permalink / raw)
To: Pinkesh Vaghela, Lee Jones, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, devicetree, linux-kernel, linux-riscv, Min Lin
Cc: Yulin Lu, Samuel Holland, Darshan Prajapati, Pritesh Patel
In-Reply-To: <20260615123315.ABB4A1F00A3A@smtp.kernel.org>
Hi, sashiko.
> [Severity: Low]
> Should this node be named using the generic regulator-* pattern (such as
> regulator-1v8) to avoid dtbs_check warnings for deprecated names?
> There is also an extra leading space on the properties within this node
> that deviates from standard spacing.
I will rename this node to "regulator-1v8".
> > + regulator-name = "vcc1v8";
> > + regulator-always-on;
> > + regulator-boot-on;
> > + regulator-min-microvolt = <1800000>;
> > + regulator-max-microvolt = <1800000>;
> > + };
> > };
> [ ... ]
> > @@ -25,6 +35,105 @@ &xtal {
> clock-output-names = "xtal24m";
> > };
> >
> > +&gpio0_pins {
> > + bias-disable;
> > + input-enable;
> > +};
> [Severity: High]
> Will these pin configuration overrides be silently ignored by the pinctrl
> core during boot?
> Since these pin definitions are not referenced by a pinctrl-0 property in
> an active device node, and are not configured as pinctrl hogs in the provider,
> the intended hardware states for these pins will not be programmed.
I don't think this is an issue. These pin configurations won't be ignored by the kernel during boot.
Some pins have input-enable, pull-down, or pull-up settings that differ from the chip defaults on
certain boards, these configurations are added these configurations in the board-level DTS.
> [ ... ]
> > diff --git a/arch/riscv/boot/dts/eswin/eic7700-pinctrl.dtsi b/arch/riscv/boot/dts/eswin/eic7700-pinctrl.dtsi
> > new file mode 100644
> > index 0000000000000..7293df146aa7b
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/eswin/eic7700-pinctrl.dtsi
> > @@ -0,0 +1,888 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> [ ... ]
> > + hdmi_pins: hdmi-pins {
> > + pins = "hdmi_scl", "hdmi_sda", "hdmi_cec";
> > + function = "hdmi";
> > + };
> > +
> > + rgmii0_pins: rgmii0-pins {
> > + pins = "rgmii0_clk_125", "rgmii0_txen", "rgmii0_txclk", "rgmii0_txd0",
> > + "rgmii0_txd1", "rgmii0_txd2", "rgmii0_txd3", "rgmii0_rxclk",
> > + "rgmii0_rxdv", "rgmii0_rxd0", "rgmii0_rxd1", "rgmii0_rxd2",
> > + "rgmii0_rxd3", "rgmii0_mdc", "rgmii0_mdio", "rgmii0_intb";
> > + function = "hdmi";
> > + };
> [Severity: High]
> Is the hdmi function correct for this network pin group?
> It appears that rgmii0, rgmii1, and the i2s pin groups that follow all use
> the hdmi function. This will prevent the pin controller from routing these
> pins properly, likely leading to driver probe failures when peripherals
> attempt to request them.
I will fix the function of rgmii0_pins and rgmii1_pins to rgmii, and the function of i2s0_pins,
i2s1_pins, and i2s2_pins to i2s.
> [ ... ]
> > diff --git a/arch/riscv/boot/dts/eswin/eic7700.dtsi b/arch/riscv/boot/dts/eswin/eic7700.dtsi
> > index a7ebb11159580..8798c50f7584d 100644
> > --- a/arch/riscv/boot/dts/eswin/eic7700.dtsi
> > +++ b/arch/riscv/boot/dts/eswin/eic7700.dtsi
> > @@ -349,6 +349,11 @@ gpioD: gpio-port@3 {
> > };
> > };
> >
> > + pinctrl: pinctrl@51600080 {
> > + compatible = "eswin,eic7700-pinctrl";
> > + reg = <0x0 0x51600080 0x0 0x1fff80>;
> > + };
> > +
> [Severity: High]
> Should the existing gpio controller nodes (gpioA, gpioB, gpioC, and gpioD)
> be updated with a gpio-ranges property mapping to this new pinctrl provider?
> Without gpio-ranges, the GPIO subsystem cannot translate GPIO offsets into
> pinctrl pins. The pinctrl core's .gpio_request_enable callback will not be
> invoked when a software component requests a GPIO dynamically, preventing
> the multiplexer from automatically switching the pin to GPIO mode.
> > clk: clock-controller@51828000 {
> > compatible = "eswin,eic7700-clock";
I think add gpio-ranges is unnecessary, the driver does not implement
the .gpio_request_enable callback.
Regards,
Yulin Lu
^ permalink raw reply
* RE: [EXTERNAL] Re: [PATCH v4 1/3] perf: marvell: Add MPAM partid filtering to CN10K TAD PMU
From: Geethasowjanya Akula @ 2026-06-26 6:21 UTC (permalink / raw)
To: Ben Horgan, linux-perf-users@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org
Cc: mark.rutland@arm.com, will@kernel.org, krzk+dt@kernel.org,
james.morse@arm.com, Sunil Kovvuri Goutham, Tanmay Jagdale
In-Reply-To: <6b15d3fc-4b4e-4c6c-a96d-5817d7114d02@arm.com>
>-----Original Message-----
>From: Ben Horgan <ben.horgan@arm.com>
>Sent: Thursday, June 25, 2026 7:23 PM
>To: Geethasowjanya Akula <gakula@marvell.com>; linux-perf-
>users@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm-
>kernel@lists.infradead.org; devicetree@vger.kernel.org
>Cc: mark.rutland@arm.com; will@kernel.org; krzk+dt@kernel.org;
>james.morse@arm.com
>Subject: [EXTERNAL] Re: [PATCH v4 1/3] perf: marvell: Add MPAM partid
>filtering to CN10K TAD PMU
>Hi Geetha,
>
>+CC James
>
>On 6/18/26 16:36, Geetha sowjanya wrote:
>> From: Tanmay Jagdale <tanmay@marvell.com>
>>
>> The TAD PMU exposes counters that can be filtered by MPAM partition id
>> for a subset of allocation and hit events.
>>
>> Add a 9-bit partid format attribute (config1) and route counter
>> programming through variant-specific ops so CN10K keeps MPAM-capable
>> programming while Odyssey keeps the reduced event set without advertising
>partid in sysfs.
>>
>> Probe no longer mutates the platform_device MMIO resource (walk a
>> local map_start), rejects tad-cnt / page sizes of zero, validates the
>> memory window against tad-cnt, and registers the perf PMU before
>> hotplug with correct unwind.
>>
>> Example:
>> perf stat -e tad/tad_alloc_any,partid=0x12,partid_en=1/ -- <program>
>
>Where is the user expected to get the PARTID from? The MPAM driver
>considers the PARTID as an internal only value.
>
>resctrl does support a 'debug' mount option which will show the CLOSID
>associated with a control group. Whilst the CLOSID is often the PARTID, it is
>really a set of PARTIDs. When the cdp mount option is used, CLOSID maps to 2
>PARTIDs and if we use PARTID narrowing to give us more monitors, as in
>proposed in [1], then the set of PARTIDs may be bigger.
>Furthermore, if the PARTID narrowing scheme is made dynamic the size of the
>PARTID set may change when control or monitoring groups are created or
>deleted.
>
>It seems that a way to map from a resctrl control group to the set of PARTIDs is
>required and a mechanism to tie this to lifetime of the resctrl mount.
>
>Perhaps some helpers along the lines of:
>
>int resctrl_mount_generation(void)
>int mpam_rdtgrp_to_partid_is_static(int mount_gen) int
>resctrl_rdtgrp_generation(char *name) int
>mpam_rdtgrp_to_partid_count(char *name, int rdt_gen) int
>mpam_rdtgrp_to_partid_array(char *name, int rdt_gen, int* partids)
>
>The rdtgrp generation is to an attempt to avoid having to use a debug interface
>in anger and cope with renaming of control groups in resctrl.
>This does seem a bit unwieldly so hopefully there is better way to do this.
>
>Sorry to throw a spanner in the works.
On …, … wrote:
> Where is the user expected to get the PARTID from? The MPAM driver
> considers the PARTID as an internal only value.
> …
> Perhaps some helpers along the lines of:
> int resctrl_mount_generation(void)
> …
Hi Ben,
Thank you for the detailed feedback — the concern you raise is valid, particularly when
viewed from the perspective of resctrl-managed deployments.
However, to clarify the intent of this patch: the exposure of partid in the TAD PMU is deliberately
a low-level, hardware-facing interface, and is not intended to integrate with or mirror the
abstractions provided by resctrl. It is mainly meant for platform bring-up and low-level
performance/debug users, who already have explicit knowledge of the MPAM configuration,
typically provisioned by firmware or other privileged software layers (e.g. EL3/EL2).
In such environments, PARTIDs are known out-of-band, so the expectation is that the
user supplying partid is already aware of the MPAM IDs programmed on the system.
A proper “profile this resctrl group” path would require MPAM–resctrl support (e.g. something along the lines of the helpers you suggest)
to resolve a group to its PARTID set. This is indeed important, but it constitutes a separate design discussion that is outside the scope of this driver patch.
We will clarify this in the commit message and avoid implying that users normally obtain PARTIDs from resctrl today.
Thanks,
Geetha
>
>Thanks,
>
>Ben
>
>>
>> Signed-off-by: Tanmay Jagdale <tanmay@marvell.com>
>> Signed-off-by: Geetha sowjanya <gakula@marvell.com>
>> ---
>>
>> Changelog (since v3)
>> --------------------
>> - Restore cpuhp_state_add_instance_nocalls before perf_pmu_register in
>probe
>> so users cannot attach events before the hotplug instance exists; unwind
>> removes the hotplug instance if perf registration fails.
>> - Add perf_ready: tad_pmu_offline_cpu skips perf_pmu_migrate_context
>until after
>> successful perf_pmu_register, so a CPU offline between hotplug add and
>perf
>> register does not touch perf core state for an unregistered PMU.
>>
>> Changelog (since v2)
>> --------------------
>> - Validate the eventId using an appropriate mask to ensure
>> it is restricted to 8 bits.
>>
>> Changelog (since v1)
>> --------------------
>> - Fix config1 filter enable to use bit 9 consistently with the PMU format
>> string (partid_en) and reject reserved bits with GENMASK(9, 0).
>> - Register perf_pmu_register before cpuhp_state_add_instance_nocalls and
>> unregister on hotplug failure.
>>
>> drivers/perf/marvell_cn10k_tad_pmu.c | 220
>> +++++++++++++++++++++------
>> 1 file changed, 171 insertions(+), 49 deletions(-)
>>
>> diff --git a/drivers/perf/marvell_cn10k_tad_pmu.c
>> b/drivers/perf/marvell_cn10k_tad_pmu.c
>> index 51ccb0befa05..340be3776fe7 100644
>> --- a/drivers/perf/marvell_cn10k_tad_pmu.c
>> +++ b/drivers/perf/marvell_cn10k_tad_pmu.c
>> @@ -7,6 +7,8 @@
>> #define pr_fmt(fmt) "tad_pmu: " fmt
>>
>> #include <linux/io.h>
>> +#include <linux/bits.h>
>> +#include <linux/compiler.h>
>> #include <linux/module.h>
>> #include <linux/of.h>
>> #include <linux/cpuhotplug.h>
>> @@ -14,12 +16,20 @@
>> #include <linux/platform_device.h>
>> #include <linux/acpi.h>
>>
>> -#define TAD_PFC_OFFSET 0x800
>> -#define TAD_PFC(counter) (TAD_PFC_OFFSET | (counter << 3))
>> #define TAD_PRF_OFFSET 0x900
>> -#define TAD_PRF(counter) (TAD_PRF_OFFSET | (counter << 3))
>> +#define TAD_PFC_OFFSET 0x800
>> +#define TAD_PFC(base, counter) ((base) | ((u64)(counter) << 3))
>> +#define TAD_PRF(base, counter) ((base) | ((u64)(counter) << 3))
>> #define TAD_PRF_CNTSEL_MASK 0xFF
>> +#define TAD_PRF_MATCH_PARTID BIT(8)
>> +#define TAD_PRF_PARTID_NS BIT(10)
>> +/*
>> + * config1: bits 0..8 MPAM partition id (including 0); bit 9 requests
>> + * filtering for MPAM-capable events. All-zero config1 means no filter.
>> + */
>> +#define TAD_PARTID_FILTER_EN BIT(9)
>> #define TAD_MAX_COUNTERS 8
>> +#define TAD_EVENT_SEL_MASK GENMASK(7, 0)
>>
>> #define to_tad_pmu(p) (container_of(p, struct tad_pmu, pmu))
>>
>> @@ -27,30 +37,94 @@ struct tad_region {
>> void __iomem *base;
>> };
>>
>> +enum mrvl_tad_pmu_version {
>> + TAD_PMU_V1 = 1,
>> + TAD_PMU_V2,
>> +};
>> +
>> +struct tad_pmu_data {
>> + int id;
>> + u64 tad_prf_offset;
>> + u64 tad_pfc_offset;
>> +};
>> +
>> struct tad_pmu {
>> struct pmu pmu;
>> struct tad_region *regions;
>> u32 region_cnt;
>> unsigned int cpu;
>> + /* Set after successful perf_pmu_register(); gates offline migration. */
>> + bool perf_ready;
>> + const struct tad_pmu_ops *ops;
>> + const struct tad_pmu_data *pdata;
>> struct hlist_node node;
>> struct perf_event *events[TAD_MAX_COUNTERS];
>> DECLARE_BITMAP(counters_map, TAD_MAX_COUNTERS); };
>>
>> -enum mrvl_tad_pmu_version {
>> - TAD_PMU_V1 = 1,
>> - TAD_PMU_V2,
>> -};
>> -
>> -struct tad_pmu_data {
>> - int id;
>> +struct tad_pmu_ops {
>> + void (*start_counter)(struct tad_pmu *pmu, struct perf_event
>> +*event);
>> };
>>
>> static int tad_pmu_cpuhp_state;
>>
>> +static void tad_pmu_start_counter(struct tad_pmu *pmu,
>> + struct perf_event *event)
>> +{
>> + const struct tad_pmu_data *pdata = pmu->pdata;
>> + struct hw_perf_event *hwc = &event->hw;
>> + u32 event_idx = (u32)(event->attr.config & TAD_EVENT_SEL_MASK);
>> + u32 counter_idx = hwc->idx;
>> + u64 partid_filter = 0;
>> + u64 reg_val;
>> + u64 cfg1 = event->attr.config1;
>> + bool use_mpam = cfg1 & TAD_PARTID_FILTER_EN;
>> + u32 partid = (u32)(cfg1 & GENMASK(8, 0));
>> + int i;
>> +
>> + for (i = 0; i < pmu->region_cnt; i++)
>> + writeq_relaxed(0, pmu->regions[i].base +
>> + TAD_PFC(pdata->tad_pfc_offset, counter_idx));
>> +
>> + if (use_mpam && event_idx > 0x19 && event_idx < 0x21) {
>> + partid_filter = TAD_PRF_MATCH_PARTID |
>TAD_PRF_PARTID_NS |
>> + ((u64)partid << 11);
>> + }
>> +
>> +
>> + for (i = 0; i < pmu->region_cnt; i++) {
>> + reg_val = event_idx & 0xFF;
>> + reg_val |= partid_filter;
>> + writeq_relaxed(reg_val, pmu->regions[i].base +
>> + TAD_PRF(pdata->tad_prf_offset, counter_idx));
>> + }
>> +}
>> +
>> +static void tad_pmu_v2_start_counter(struct tad_pmu *pmu,
>> + struct perf_event *event)
>> +{
>> + const struct tad_pmu_data *pdata = pmu->pdata;
>> + struct hw_perf_event *hwc = &event->hw;
>> + u32 event_idx = (u32)(event->attr.config & TAD_EVENT_SEL_MASK);
>> + u32 counter_idx = hwc->idx;
>> + u64 reg_val;
>> + int i;
>> +
>> + for (i = 0; i < pmu->region_cnt; i++)
>> + writeq_relaxed(0, pmu->regions[i].base +
>> + TAD_PFC(pdata->tad_pfc_offset, counter_idx));
>> +
>> + for (i = 0; i < pmu->region_cnt; i++) {
>> + reg_val = event_idx & 0xFF;
>> + writeq_relaxed(reg_val, pmu->regions[i].base +
>> + TAD_PRF(pdata->tad_prf_offset, counter_idx));
>> + }
>> +}
>> +
>> static void tad_pmu_event_counter_read(struct perf_event *event) {
>> struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
>> + const struct tad_pmu_data *pdata = tad_pmu->pdata;
>> struct hw_perf_event *hwc = &event->hw;
>> u32 counter_idx = hwc->idx;
>> u64 prev, new;
>> @@ -60,7 +134,7 @@ static void tad_pmu_event_counter_read(struct
>perf_event *event)
>> prev = local64_read(&hwc->prev_count);
>> for (i = 0, new = 0; i < tad_pmu->region_cnt; i++)
>> new += readq(tad_pmu->regions[i].base +
>> - TAD_PFC(counter_idx));
>> + TAD_PFC(pdata->tad_pfc_offset,
>counter_idx));
>> } while (local64_cmpxchg(&hwc->prev_count, prev, new) != prev);
>>
>> local64_add(new - prev, &event->count); @@ -69,16 +143,14 @@
>static
>> void tad_pmu_event_counter_read(struct perf_event *event) static void
>> tad_pmu_event_counter_stop(struct perf_event *event, int flags) {
>> struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
>> + const struct tad_pmu_data *pdata = tad_pmu->pdata;
>> struct hw_perf_event *hwc = &event->hw;
>> u32 counter_idx = hwc->idx;
>> int i;
>>
>> - /* TAD()_PFC() stop counting on the write
>> - * which sets TAD()_PRF()[CNTSEL] == 0
>> - */
>> for (i = 0; i < tad_pmu->region_cnt; i++) {
>> writeq_relaxed(0, tad_pmu->regions[i].base +
>> - TAD_PRF(counter_idx));
>> + TAD_PRF(pdata->tad_prf_offset, counter_idx));
>> }
>>
>> tad_pmu_event_counter_read(event);
>> @@ -89,26 +161,10 @@ static void tad_pmu_event_counter_start(struct
>> perf_event *event, int flags) {
>> struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
>> struct hw_perf_event *hwc = &event->hw;
>> - u32 event_idx = event->attr.config;
>> - u32 counter_idx = hwc->idx;
>> - u64 reg_val;
>> - int i;
>>
>> hwc->state = 0;
>>
>> - /* Typically TAD_PFC() are zeroed to start counting */
>> - for (i = 0; i < tad_pmu->region_cnt; i++)
>> - writeq_relaxed(0, tad_pmu->regions[i].base +
>> - TAD_PFC(counter_idx));
>> -
>> - /* TAD()_PFC() start counting on the write
>> - * which sets TAD()_PRF()[CNTSEL] != 0
>> - */
>> - for (i = 0; i < tad_pmu->region_cnt; i++) {
>> - reg_val = event_idx & 0xFF;
>> - writeq_relaxed(reg_val, tad_pmu->regions[i].base +
>> - TAD_PRF(counter_idx));
>> - }
>> + tad_pmu->ops->start_counter(tad_pmu, event);
>> }
>>
>> static void tad_pmu_event_counter_del(struct perf_event *event, int
>> flags) @@ -128,7 +184,6 @@ static int tad_pmu_event_counter_add(struct
>perf_event *event, int flags)
>> struct hw_perf_event *hwc = &event->hw;
>> int idx;
>>
>> - /* Get a free counter for this event */
>> idx = find_first_zero_bit(tad_pmu->counters_map,
>TAD_MAX_COUNTERS);
>> if (idx == TAD_MAX_COUNTERS)
>> return -EAGAIN;
>> @@ -148,6 +203,9 @@ static int tad_pmu_event_counter_add(struct
>> perf_event *event, int flags) static int tad_pmu_event_init(struct
>> perf_event *event) {
>> struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
>> + const struct tad_pmu_data *pdata = tad_pmu->pdata;
>> + u32 event_idx = (u32)(event->attr.config & TAD_EVENT_SEL_MASK);
>> + u64 cfg1 = event->attr.config1;
>>
>> if (event->attr.type != event->pmu->type)
>> return -ENOENT;
>> @@ -158,6 +216,23 @@ static int tad_pmu_event_init(struct perf_event
>*event)
>> if (event->state != PERF_EVENT_STATE_OFF)
>> return -EINVAL;
>>
>> + if (event->attr.config & ~TAD_EVENT_SEL_MASK)
>> + return -EINVAL;
>> +
>> + if (pdata->id == TAD_PMU_V2) {
>> + if (cfg1)
>> + return -EINVAL;
>> + } else {
>> + if ((cfg1 & GENMASK(8, 0)) && !(cfg1 &
>TAD_PARTID_FILTER_EN))
>> + return -EINVAL;
>> + if (cfg1 & TAD_PARTID_FILTER_EN) {
>> + if (event_idx <= 0x19 || event_idx >= 0x21)
>> + return -EINVAL;
>> + }
>> + if (cfg1 & ~GENMASK(9, 0))
>> + return -EINVAL;
>> + }
>> +
>> event->cpu = tad_pmu->cpu;
>> event->hw.idx = -1;
>> event->hw.config_base = event->attr.config; @@ -232,7 +307,7 @@
>> static struct attribute *ody_tad_pmu_event_attrs[] = {
>> TAD_PMU_EVENT_ATTR(tad_hit_ltg, 0x1e),
>> TAD_PMU_EVENT_ATTR(tad_hit_any, 0x1f),
>> TAD_PMU_EVENT_ATTR(tad_tag_rd, 0x20),
>> - TAD_PMU_EVENT_ATTR(tad_tot_cycle, 0xFF),
>> + TAD_PMU_EVENT_ATTR(tad_tot_cycle, 0xff),
>> NULL
>> };
>>
>> @@ -242,9 +317,13 @@ static const struct attribute_group
>> ody_tad_pmu_events_attr_group = { };
>>
>> PMU_FORMAT_ATTR(event, "config:0-7");
>> +PMU_FORMAT_ATTR(partid, "config1:0-8"); PMU_FORMAT_ATTR(partid_en,
>> +"config1:9-9");
>>
>> static struct attribute *tad_pmu_format_attrs[] = {
>> &format_attr_event.attr,
>> + &format_attr_partid.attr,
>> + &format_attr_partid_en.attr,
>> NULL
>> };
>>
>> @@ -253,6 +332,16 @@ static struct attribute_group
>tad_pmu_format_attr_group = {
>> .attrs = tad_pmu_format_attrs,
>> };
>>
>> +static struct attribute *ody_tad_pmu_format_attrs[] = {
>> + &format_attr_event.attr,
>> + NULL
>> +};
>> +
>> +static struct attribute_group ody_tad_pmu_format_attr_group = {
>> + .name = "format",
>> + .attrs = ody_tad_pmu_format_attrs,
>> +};
>> +
>> static ssize_t tad_pmu_cpumask_show(struct device *dev,
>> struct device_attribute *attr, char *buf) { @@
>-281,16 +370,25
>> @@ static const struct attribute_group *tad_pmu_attr_groups[] = {
>>
>> static const struct attribute_group *ody_tad_pmu_attr_groups[] = {
>> &ody_tad_pmu_events_attr_group,
>> - &tad_pmu_format_attr_group,
>> + &ody_tad_pmu_format_attr_group,
>> &tad_pmu_cpumask_attr_group,
>> NULL
>> };
>>
>> +static const struct tad_pmu_ops tad_pmu_ops = {
>> + .start_counter = tad_pmu_start_counter, };
>> +
>> +static const struct tad_pmu_ops tad_pmu_v2_ops = {
>> + .start_counter = tad_pmu_v2_start_counter, };
>> +
>> static int tad_pmu_probe(struct platform_device *pdev) {
>> const struct tad_pmu_data *dev_data;
>> struct device *dev = &pdev->dev;
>> struct tad_region *regions;
>> + resource_size_t map_start;
>> struct tad_pmu *tad_pmu;
>> struct resource *res;
>> u32 tad_pmu_page_size;
>> @@ -298,7 +396,6 @@ static int tad_pmu_probe(struct platform_device
>*pdev)
>> u32 tad_cnt;
>> int version;
>> int i, ret;
>> - char *name;
>>
>> tad_pmu = devm_kzalloc(&pdev->dev, sizeof(*tad_pmu),
>GFP_KERNEL);
>> if (!tad_pmu)
>> @@ -312,6 +409,7 @@ static int tad_pmu_probe(struct platform_device
>*pdev)
>> return -ENODEV;
>> }
>> version = dev_data->id;
>> + tad_pmu->pdata = dev_data;
>>
>> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> if (!res) {
>> @@ -338,22 +436,31 @@ static int tad_pmu_probe(struct platform_device
>*pdev)
>> dev_err(&pdev->dev, "Can't find tad-cnt property\n");
>> return ret;
>> }
>> + if (!tad_cnt || !tad_page_size || !tad_pmu_page_size) {
>> + dev_err(&pdev->dev, "Invalid tad-cnt or page size\n");
>> + return -EINVAL;
>> + }
>>
>> regions = devm_kcalloc(&pdev->dev, tad_cnt,
>> sizeof(*regions), GFP_KERNEL);
>> if (!regions)
>> return -ENOMEM;
>>
>> - /* ioremap the distributed TAD pmu regions */
>> - for (i = 0; i < tad_cnt && res->start < res->end; i++) {
>> - regions[i].base = devm_ioremap(&pdev->dev,
>> - res->start,
>> + map_start = res->start;
>> + for (i = 0; i < tad_cnt; i++) {
>> + if (map_start > res->end ||
>> + tad_pmu_page_size > (resource_size_t)(res->end -
>map_start + 1)) {
>> + dev_err(&pdev->dev, "TAD PMU mem window too
>small for tad-cnt=%u\n",
>> + tad_cnt);
>> + return -EINVAL;
>> + }
>> + regions[i].base = devm_ioremap(&pdev->dev, map_start,
>> tad_pmu_page_size);
>> if (!regions[i].base) {
>> dev_err(&pdev->dev, "TAD%d ioremap fail\n", i);
>> return -ENOMEM;
>> }
>> - res->start += tad_page_size;
>> + map_start += tad_page_size;
>> }
>>
>> tad_pmu->regions = regions;
>> @@ -374,14 +481,16 @@ static int tad_pmu_probe(struct platform_device
>*pdev)
>> .read = tad_pmu_event_counter_read,
>> };
>>
>> - if (version == TAD_PMU_V1)
>> + if (version == TAD_PMU_V1) {
>> tad_pmu->pmu.attr_groups = tad_pmu_attr_groups;
>> - else
>> + tad_pmu->ops = &tad_pmu_ops;
>> + } else {
>> tad_pmu->pmu.attr_groups = ody_tad_pmu_attr_groups;
>> + tad_pmu->ops = &tad_pmu_v2_ops;
>> + }
>>
>> tad_pmu->cpu = raw_smp_processor_id();
>>
>> - /* Register pmu instance for cpu hotplug */
>> ret = cpuhp_state_add_instance_nocalls(tad_pmu_cpuhp_state,
>> &tad_pmu->node);
>> if (ret) {
>> @@ -389,19 +498,24 @@ static int tad_pmu_probe(struct platform_device
>*pdev)
>> return ret;
>> }
>>
>> - name = "tad";
>> - ret = perf_pmu_register(&tad_pmu->pmu, name, -1);
>> - if (ret)
>> + ret = perf_pmu_register(&tad_pmu->pmu, "tad", -1);
>> + if (ret) {
>> + dev_err(&pdev->dev, "Error %d registering perf PMU\n", ret);
>> cpuhp_state_remove_instance_nocalls(tad_pmu_cpuhp_state,
>> &tad_pmu->node);
>> + return ret;
>> + }
>>
>> - return ret;
>> + WRITE_ONCE(tad_pmu->perf_ready, true);
>> +
>> + return 0;
>> }
>>
>> static void tad_pmu_remove(struct platform_device *pdev) {
>> struct tad_pmu *pmu = platform_get_drvdata(pdev);
>>
>> + WRITE_ONCE(pmu->perf_ready, false);
>> cpuhp_state_remove_instance_nocalls(tad_pmu_cpuhp_state,
>> &pmu->node);
>> perf_pmu_unregister(&pmu->pmu);
>> @@ -410,12 +524,17 @@ static void tad_pmu_remove(struct
>> platform_device *pdev) #if defined(CONFIG_OF) || defined(CONFIG_ACPI)
>> static const struct tad_pmu_data tad_pmu_data = {
>> .id = TAD_PMU_V1,
>> + .tad_prf_offset = TAD_PRF_OFFSET,
>> + .tad_pfc_offset = TAD_PFC_OFFSET,
>> };
>> +
>> #endif
>>
>> #ifdef CONFIG_ACPI
>> static const struct tad_pmu_data tad_pmu_v2_data = {
>> .id = TAD_PMU_V2,
>> + .tad_prf_offset = TAD_PRF_OFFSET,
>> + .tad_pfc_offset = TAD_PFC_OFFSET,
>> };
>> #endif
>>
>> @@ -451,6 +570,9 @@ static int tad_pmu_offline_cpu(unsigned int cpu,
>struct hlist_node *node)
>> struct tad_pmu *pmu = hlist_entry_safe(node, struct tad_pmu, node);
>> unsigned int target;
>>
>> + if (!READ_ONCE(pmu->perf_ready))
>> + return 0;
>> +
>> if (cpu != pmu->cpu)
>> return 0;
>>
>> @@ -491,6 +613,6 @@ static void __exit tad_pmu_exit(void)
>> module_init(tad_pmu_init); module_exit(tad_pmu_exit);
>>
>> -MODULE_DESCRIPTION("Marvell CN10K LLC-TAD Perf driver");
>> +MODULE_DESCRIPTION("Marvell CN10K LLC-TAD perf driver");
>> MODULE_AUTHOR("Bhaskara Budiredla <bbudiredla@marvell.com>");
>> MODULE_LICENSE("GPL v2");
^ permalink raw reply
* Re: [PATCH 0/6] Additional regulator support for the Qualcomm SGMII SerDes PHY
From: Mohd Ayaan Anwar @ 2026-06-26 6:13 UTC (permalink / raw)
To: Loic Poulain
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bartosz Golaszewski,
Bjorn Andersson, Konrad Dybcio, Wasim Nazir, Mohd Ayaan Anwar,
Yijie Yang, linux-arm-msm, linux-phy, devicetree, linux-kernel,
Konrad Dybcio
In-Reply-To: <CAFEp6-3De6r3XwSmnKpMSSf8KK3K-esDAhTRKN41Bng-Yf8dXg@mail.gmail.com>
On Mon, Jun 22, 2026 at 04:08:55PM +0200, Loic Poulain wrote:
> Hi,
>
> On Mon, Nov 24, 2025 at 10:26 AM Mohd Ayaan Anwar
> <mohd.anwar@oss.qualcomm.com> wrote:
> >
> > This patch series addresses a SerDes power-up failure observed on the
> > QCS8300 Ride board using the phy-qcom-sgmii-eth driver. The issue occurs
> > because the SerDes PHY requires both L5A and L4A regulators to be
> > enabled during initialization. If either one of them is disabled, the
> > status register does not reset, and the Ethernet interface fails to come
> > up due to a timeout:
> >
> > [ 77.105651] qcom-dwmac-sgmii-phy 8909000.phy: QSERDES_COM_C_READY_STATUS timed-out
> > [ 77.113447] qcom-ethqos 23040000.ethernet eth0: __stmmac_open: Serdes powerup failed
> >
> > Presumably, the issue is not seen on all users of this PHY because both
> > regulators are often shared with other peripherals. However, the power
> > rail schematics for boards using this SerDes PHY show that it is
> > supplied by both L5A (1.2V) and L4A (0.9V) regulators.
> >
> > This series has been tested on the QCS8300 Ride board, and the reported
> > issue was resolved. All Monaco and Lemans derivative boards require this
> > new configuration as they use the same SerDes PHY.
> >
> > Signed-off-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
>
> This appears to be a real issue, but I haven’t seen a new version of
> the series or any response to the feedback so far. Could you please
> follow up on this?
>
I will shortly send out a v2. I got access to more details about the
hardware, and it has two dedicated regulators which need to be enabled.
The same requirements are there on all Lemans/Monaco-based boards.
Ayaan
^ permalink raw reply
* Re: Re: [PATCH 3/7] riscv: dts: eswin: eic7700: add pinctrl support
From: Yulin Lu @ 2026-06-26 6:01 UTC (permalink / raw)
To: Conor Dooley
Cc: Pinkesh Vaghela, Lee Jones, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, devicetree, linux-kernel, linux-riscv, Min Lin,
Samuel Holland, Darshan Prajapati, Pritesh Patel
In-Reply-To: <20260615-that-scarf-e048ef152676@spud>
Hi, Conor. Thanks for your review.
> On Mon, Jun 15, 2026 at 05:50:12PM +0530, Pinkesh Vaghela wrote:
> > From: Yulin Lu <luyulin@eswincomputing.com>
> >
> > Add pinctrl node and related pin configuration for EIC7700 SoC
> >
> > Co-developed-by: Pritesh Patel <pritesh.patel@einfochips.com>
> > Signed-off-by: Pritesh Patel <pritesh.patel@einfochips.com>
> > Signed-off-by: Yulin Lu <luyulin@eswincomputing.com>
> > Signed-off-by: Pinkesh Vaghela <pinkesh.vaghela@einfochips.com>
> > ---
> > .../dts/eswin/eic7700-hifive-premier-p550.dts | 109 +++
> > .../riscv/boot/dts/eswin/eic7700-pinctrl.dtsi | 888 ++++++++++++++++++
> > arch/riscv/boot/dts/eswin/eic7700.dtsi | 5 +
> > 3 files changed, 1002 insertions(+)
> > create mode 100644 arch/riscv/boot/dts/eswin/eic7700-pinctrl.dtsi
> >
> > diff --git a/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts b/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > index 1fb92f0e7c55..e7bb96e14958 100644
> > --- a/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > +++ b/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > @@ -6,6 +6,7 @@
> > /dts-v1/;
> >
> > #include "eic7700.dtsi"
> > +#include "eic7700-pinctrl.dtsi"
> >
...
> > +&gpio79_pins {
> > + bias-disable;
> > + input-disable;
> > +};
> > +
> > +&gpio80_pins {
> > + bias-pull-up;
> > + input-disable;
> > +};
> > +
> > +&gpio82_pins {
> > + bias-pull-up;
> > + input-disable;
> > +};
> > +
> > +&gpio84_pins {
> > + bias-disable;
> > + input-disable;
> > +};
> > +
> > +&gpio85_pins {
> > + bias-pull-up;
> > + input-disable;
> > +};
> > +
> > +&gpio94_pins {
> > + bias-disable;
> > + input-disable;
> > +};
> > +
> > +&gpio106_pins {
> > + bias-disable;
> > + input-disable;
> > +};
> > +
> > +&gpio111_pins {
> > + bias-disable;
> > + input-disable;
> > +};
> > +
> > +&pinctrl {
> > + vrgmii-supply = <&vcc_1v8>;
> > +};
> > +
> > &uart0 {
> > status = "okay";
> > };
> > diff --git a/arch/riscv/boot/dts/eswin/eic7700-pinctrl.dtsi b/arch/riscv/boot/dts/eswin/eic7700-pinctrl.dtsi
> > new file mode 100644
> > index 000000000000..7293df146aa7
> > --- /dev/null
> > +++ b/arch/riscv/boot/dts/eswin/eic7700-pinctrl.dtsi
> > @@ -0,0 +1,888 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> > +/*
> > + * Copyright (c) 2025 Beijing ESWIN Computing Technology Co., Ltd.
> > + *
> > + * ESWIN's EIC7700 SoC pin-mux and pin-config options are listed as
> > + * device tree nodes in this file.
> > + *
> > + * Authors: Yulin Lu <luyulin@eswincomputing.com>
> > + */
> > +
>
> I don't really understand the groups here. I think you should make more
> effort to put more pins in each group.
>
> > + gpio1_pins: gpio1-pins {
> > + pins = "jtag0_tck";
> > + function = "gpio";
> > + };
> > +
> > + gpio2_pins: gpio2-pins {
> > + pins = "jtag0_tms";
> > + function = "gpio";
> > + };
> > +
> > + gpio3_pins: gpio3-pins {
> > + pins = "jtag0_tdi";
> > + function = "gpio";
> > + };
> > +
> > + gpio4_pins: gpio4-pins {
> > + pins = "jtag0_tdo";
> > + function = "gpio";
> > + };
>
> Like these 4 for example, why not group these?
The 'group' is used to correspond to the '-grp' tag in the YAML file and
has no practical significance.
Different board designs have different requirements for pin multiplexing.
Therefore, eic7700-pinctrl.dtsi only provides pins for the board-level DTS.
Pins are combined and used in the board-level DTS via pinctrl-0 property.
^ permalink raw reply
* Re: [PATCH v2 2/3] arm64: dts: qcom: Add HP EliteBook X G2q 14 AI
From: sashiko-bot @ 2026-06-26 6:00 UTC (permalink / raw)
To: Jason Pettit; +Cc: conor+dt, robh, devicetree
In-Reply-To: <20260625-glymur-send-v2-2-00905324ffbf@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The `usb_2` DWC3 controller and its high-speed PHY are explicitly enabled despite having no physical connection and being completely unused.
--
commit cd918ca13e11bd1a5f9fec450e22054021db1490
Author: Jason Pettit <jason.pettit@oss.qualcomm.com>
arm64: dts: qcom: Add HP EliteBook X G2q 14 AI
Add board support for the HP EliteBook X G2q 14" Next Gen AI PC.
This enables various device tree nodes including the HDMI output,
eUSB2, NVMe, and other peripherals on the Snapdragon X2 Elite platform.
> diff --git a/arch/arm64/boot/dts/qcom/glymur-hp-elitebook-x-g2q.dts b/arch/arm64/boot/dts/qcom/glymur-hp-elitebook-x-g2q.dts
> new file mode 100644
> index 0000000000000..b3e351ed7ea7e
> --- /dev/null
> +++ b/arch/arm64/boot/dts/qcom/glymur-hp-elitebook-x-g2q.dts
[ ... ]
> +/*
> + * usb_2 is a fixed DP-to-HDMI bridge; its SID-11 eUSB2 repeater is
> + * firmware-gated and never probes, so leave smb2370_l disabled.
> + */
> +&smb2370_l_e2 {
> + status = "disabled";
> +};
[ ... ]
> +/*
> + * usb_2 is not exposed as a connector; its combo-PHY's DP lanes drive the
> + * chassis HDMI bridge (see hdmi-bridge).
> + */
> +&usb_2 {
> + maximum-speed = "high-speed";
> +
> + phys = <&usb_2_hsphy>;
> + phy-names = "usb2-phy";
> +
> + status = "okay";
> +};
> +
> +&usb_2_hsphy {
> + vdd-supply = <&vreg_l3f_e0_0p91>;
> + vdda12-supply = <&vreg_l4h_e0_1p2>;
> +
> + status = "okay";
> +};
[Severity: Medium]
Since this port is used purely for DisplayPort-to-HDMI output via the
combo-PHY DP lanes, and the eUSB2 repeater is explicitly noted as never
probing, should the usb_2 DWC3 controller and usb_2_hsphy be enabled here?
Leaving status = "okay" for the unused DWC3 controller and its high-speed PHY
binds drivers that will consume resources like memory and IRQs unnecessarily
for hardware that isn't connected.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260625-glymur-send-v2-0-00905324ffbf@oss.qualcomm.com?part=2
^ permalink raw reply
* Re: [PATCH v9 2/3] dmaengine: amlogic: Add general DMA driver for A9
From: sashiko-bot @ 2026-06-26 5:53 UTC (permalink / raw)
To: Xianwei Zhao via B4 Relay
Cc: dmaengine, neil.armstrong, Frank.Li, vkoul, conor+dt, devicetree,
robh, linux-amlogic
In-Reply-To: <20260626-amlogic-dma-v9-2-558d672c4a95@amlogic.com>
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Use-After-Free during driver removal due to missing IRQ synchronization.
- [High] Sleeping in atomic context when freeing virtual DMA descriptors.
- [High] Out-of-bounds write in `aml_dma_prep_slave_sg` when processing zero-length scatterlist elements.
- [High] Broken pause/resume state machine logic leading to channel stall and state corruption.
- [Medium] `aml_dma_terminate_all()` fails to reset channel error state, causing permanent stall.
- [Medium] `aml_dma_tx_status()` improperly handles unissued descriptors, causing false errors and incorrect residues.
- [Medium] Mismatched DMA address registers used for 64-bit physical addresses.
--
commit 7102a7639af273fc6303ccde475117debb548165
Author: Xianwei Zhao <xianwei.zhao@amlogic.com>
dmaengine: amlogic: Add general DMA driver for A9
Amlogic A9 SoCs include a general-purpose DMA controller that can be used
by multiple peripherals, such as I2C PIO and I3C. Each peripheral group
is associated with a dedicated DMA channel in hardware.
> diff --git a/drivers/dma/amlogic-dma.c b/drivers/dma/amlogic-dma.c
> index 0000000000000..bce6932f3a12e
> --- /dev/null
> +++ b/drivers/dma/amlogic-dma.c
[ ... ]
> +static void aml_dma_free_desc(struct virt_dma_desc *vd)
> +{
> + struct aml_dma_desc *aml_desc = to_aml_dma_desc(vd);
> +
> + dma_free_coherent(aml_desc->dma_device->dev,
> + aml_desc->sg_link_size,
> + aml_desc->sg_link,
> + aml_desc->sg_link_phys);
[Severity: High]
Does this code sleep in atomic context?
When the virtual DMA descriptor is freed, vchan_dma_desc_free_list() calls
this callback from within vchan_complete(), which runs in tasklet
(softirq) context. Calling a sleepable function like dma_free_coherent()
from a tasklet can result in a scheduling while atomic panic.
Could this driver use dma_pool_alloc/free or kzalloc/kfree for descriptor
management instead?
> + kfree(aml_desc);
> +}
[ ... ]
> +/* DMA transfer state update how many data reside it */
> +static enum dma_status aml_dma_tx_status(struct dma_chan *chan,
> + dma_cookie_t cookie,
> + struct dma_tx_state *txstate)
> +{
> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
> + struct aml_dma_desc *aml_desc = NULL;
> + struct virt_dma_desc *vd;
> + u32 residue = 0, done;
> + unsigned long flags;
> + enum dma_status ret;
> +
> + ret = dma_cookie_status(chan, cookie, txstate);
> + if (ret == DMA_COMPLETE || !txstate)
> + return ret;
> +
> + spin_lock_irqsave(&aml_chan->vchan.lock, flags);
> + vd = vchan_find_desc(&aml_chan->vchan, cookie);
> + if (vd) {
> + aml_desc = to_aml_dma_desc(vd);
> + residue = aml_desc->data_len;
> + } else if (aml_chan->cur_desc && aml_chan->cur_desc->vd.tx.cookie == cookie) {
> + aml_desc = aml_chan->cur_desc;
> + regmap_read(aml_dma->regmap, aml_chan->reg_offs + RCH_RD_LEN, &done);
> + residue = aml_desc->data_len - done;
> + } else {
> + dev_err(aml_dma->dma_device.dev, "cookie error\n");
[Severity: Medium]
Will this incorrectly handle unissued descriptors?
Looking at aml_dma_tx_status(), it attempts to locate a descriptor using
vchan_find_desc(), which exclusively searches the desc_issued list.
If a client queries the status of a descriptor that was submitted (residing
in desc_submitted) but not yet issued via issue_pending(), vchan_find_desc()
will return NULL.
This causes the driver to log a spurious "cookie error" and return a residue
of 0, which might incorrectly imply the pending transfer is complete.
> + }
> + spin_unlock_irqrestore(&aml_chan->vchan.lock, flags);
[ ... ]
> +static struct dma_async_tx_descriptor *aml_dma_prep_slave_sg
> + (struct dma_chan *chan, struct scatterlist *sgl,
> + unsigned int sg_len, enum dma_transfer_direction direction,
> + unsigned long flags, void *context)
> +{
> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
> + struct aml_dma_desc *aml_desc = NULL;
> + struct aml_dma_sg_link *sg_link = NULL;
> + struct scatterlist *sg = NULL;
> + u64 paddr;
> + u32 link_count, avail;
> + u32 i;
[ ... ]
> + sg_link = aml_desc->sg_link;
> + for_each_sg(sgl, sg, sg_len, i) {
> + avail = sg_dma_len(sg);
> + paddr = sg->dma_address;
> + while (avail > SG_MAX_LEN) {
> + /* set dma address and len to sglink*/
> + sg_link->addr_low = lower_32_bits(paddr);
> + sg_link->addr_high = upper_32_bits(paddr);
> + sg_link->ctl = FIELD_PREP(LINK_LEN, SG_MAX_LEN);
> + paddr = paddr + SG_MAX_LEN;
> + avail = avail - SG_MAX_LEN;
> + sg_link++;
> + }
> + /* set dma address and len to sglink*/
> + sg_link->addr_low = lower_32_bits(paddr);
> + sg_link->addr_high = upper_32_bits(paddr);
> + sg_link->ctl = FIELD_PREP(LINK_LEN, avail);
> +
> + aml_desc->data_len += sg_dma_len(sg);
> + sg_link++;
[Severity: High]
Can this write out of bounds for zero-length scatterlist elements?
Earlier in aml_dma_prep_slave_sg(), sg_nents_for_dma() calculates the
required number of link descriptors. For an SG element with length 0, it
computes DIV_ROUND_UP(0, SG_MAX_LEN), adding 0 to the link count.
However, this for_each_sg loop unconditionally consumes at least one link
slot per SG element, setting its properties and incrementing sg_link.
If a client provides an empty SG element, this mismatch might result in an
out-of-bounds write past the end of the dma_alloc_coherent array, leading
to memory corruption.
> + }
[ ... ]
> +static int aml_dma_chan_pause(struct dma_chan *chan)
> +{
> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&aml_chan->vchan.lock, flags);
> + regmap_set_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE);
> + aml_chan->pre_status = aml_chan->status;
> + aml_chan->status = DMA_PAUSED;
[Severity: High]
Is the pause and resume state tracking prone to corruption?
If aml_dma_chan_pause() is called twice, it overwrites pre_status with
DMA_PAUSED, which might brick the channel upon resume.
Also, if a client submits a new transaction while paused,
aml_dma_chan_resume() restores DMA_COMPLETE but doesn't call
aml_dma_start(), which could permanently stall the channel.
> + spin_unlock_irqrestore(&aml_chan->vchan.lock, flags);
> +
> + return 0;
> +}
[ ... ]
> +static int aml_dma_terminate_all(struct dma_chan *chan)
> +{
> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
> + int chan_id = aml_chan->chan_id;
> + struct virt_dma_desc *cur_vd;
> + unsigned long flags;
> + LIST_HEAD(head);
> +
> + spin_lock_irqsave(&aml_chan->vchan.lock, flags);
[Severity: Medium]
Will the channel stall permanently if terminated after errors?
When a channel hits 6 hardware errors, aml_dma_start() halts the channel
and abandons pending processing. To recover, a client driver typically calls
dmaengine_terminate_all().
However, aml_dma_terminate_all() doesn't reset aml_chan->status to
DMA_COMPLETE or aml_chan->err_num to 0. Any subsequent transactions
submitted to this channel might hang forever because aml_dma_start() will
immediately return due to the persistent error state.
> + regmap_set_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE);
[ ... ]
> +static void aml_dma_start(struct aml_dma_chan *aml_chan)
> +{
> + struct virt_dma_desc *vd = vchan_next_desc(&aml_chan->vchan);
> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
> + struct aml_dma_desc *aml_desc = NULL;
> + int chan_id = aml_chan->chan_id;
[ ... ]
> + if (aml_chan->direction == DMA_MEM_TO_DEV) {
> + regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_ADDR,
> + lower_32_bits(aml_desc->sg_link_phys));
> + regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_ADDR_HIGH,
> + upper_32_bits(aml_desc->sg_link_phys));
[Severity: Medium]
Are the correct DMA address registers being used for 64-bit addresses?
The hardware registers define RCH_ADDR (0xc), RCH_ADDR_LOW (0x20), and
RCH_ADDR_HIGH (0x24).
In aml_dma_start(), the lower 32 bits of the physical address are programmed
into the legacy 32-bit RCH_ADDR (0xc) rather than RCH_ADDR_LOW (0x20), while
writing the upper 32 bits to RCH_ADDR_HIGH (0x24).
If the hardware expects the lower bits of a 64-bit address at 0x20, this
might result in an invalid address and immediate DMA failures.
> + regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_LEN, aml_desc->data_len);
[ ... ]
> +static irqreturn_t aml_dma_interrupt_handler(int irq, void *dev_id)
> +{
> + struct aml_dma_dev *aml_dma = dev_id;
> + struct aml_dma_chan *aml_chan;
> + struct aml_dma_desc *aml_desc;
> + u32 done, eoc_done, err, err_l, end;
> + u32 cpl_data;
> + int i = 0;
[ ... ]
> + while (done) {
> + i = ffs(done) - 1;
> + regmap_write(aml_dma->regmap, CLEAR_RCH, BIT(i));
> + done &= ~BIT(i);
> + aml_chan = aml_dma->aml_rch[i];
> + if (!aml_chan) {
> + dev_err(aml_dma->dma_device.dev, "idx %d rch not initialized\n", i);
> + continue;
> + }
> + spin_lock(&aml_chan->vchan.lock);
> + aml_chan->status = (err & BIT(i)) ? DMA_ERROR : DMA_COMPLETE;
[Severity: High]
Can an interrupt overwrite the paused state?
If an interrupt fires while the channel is paused, this handler unconditionally
overwrites status to DMA_COMPLETE and calls aml_dma_start(). Could this unpause
the hardware and break the pause semantics?
> + aml_desc = aml_chan->cur_desc;
[ ... ]
> +static int aml_dma_probe(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct dma_device *dma_dev;
> + struct aml_dma_dev *aml_dma;
> + int ret, i, len;
> + u32 chan_nr;
[ ... ]
> + ret = devm_request_irq(&pdev->dev, aml_dma->irq, aml_dma_interrupt_handler,
> + 0, dev_name(&pdev->dev), aml_dma);
[Severity: High]
Can this cause a use-after-free during driver removal?
Since devm_request_irq() is used in aml_dma_probe(), the IRQ remains active
until after aml_dma_remove() completes.
In aml_dma_remove(), the driver loops and calls tasklet_kill(&aml_chan->vchan.task):
> +static void aml_dma_remove(struct platform_device *pdev)
> +{
> + struct aml_dma_dev *aml_dma = platform_get_drvdata(pdev);
> + struct aml_dma_chan *aml_chan = NULL;
> + int i;
> +
> + of_dma_controller_free((&pdev->dev)->of_node);
> +
> + regmap_write(aml_dma->regmap, RCH_INT_MASK, 0xffffffff);
> + regmap_write(aml_dma->regmap, WCH_INT_MASK, 0xffffffff);
> +
> + for (i = 0; i < MAX_CHAN_ID; i++) {
> + aml_chan = aml_dma->aml_rch[i];
> + if (aml_chan)
> + tasklet_kill(&aml_chan->vchan.task);
But an executing interrupt handler on another CPU can subsequently call
vchan_cookie_complete() and reschedule the tasklet.
Once remove() returns, devres unregisters the DMA engine, frees the IRQ, and
frees the driver struct via devm_kfree. The tasklet will then run and access
freed memory, causing a use-after-free panic.
Should the driver explicitly free or disable the IRQ using devm_free_irq()
before calling tasklet_kill()?
> + aml_chan = aml_dma->aml_wch[i];
> + if (aml_chan)
> + tasklet_kill(&aml_chan->vchan.task);
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-amlogic-dma-v9-0-558d672c4a95@amlogic.com?part=2
^ permalink raw reply
* Re: [PATCH v9 1/3] dt-bindings: dma: Add Amlogic A9 SoC DMA
From: sashiko-bot @ 2026-06-26 5:50 UTC (permalink / raw)
To: Xianwei Zhao via B4 Relay
Cc: Frank.Li, robh, neil.armstrong, conor+dt, vkoul, linux-amlogic,
dmaengine, devicetree
In-Reply-To: <20260626-amlogic-dma-v9-1-558d672c4a95@amlogic.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Low] Global dt-binding header defines generic 'DMA_TX' and 'DMA_RX' macros, risking namespace collisions.
- [Low] The `#dma-cells` property lacks a description explaining the layout and purpose of its 2 cells.
- [Low] Missing space before the opening brace in the device tree example node.
--
commit a7b4f5e4e6207e7134f0b399abc853db92908381
Author: Xianwei Zhao <xianwei.zhao@amlogic.com>
dt-bindings: dma: Add Amlogic A9 SoC DMA
This commit adds device tree binding documentation for the Amlogic A9 SoC
general-purpose DMA controller. It also introduces a new header file containing
specific property values for this controller.
> diff --git a/Documentation/devicetree/bindings/dma/amlogic,a9-dma.yaml b/Documentation/devicetree/bindings/dma/amlogic,a9-dma.yaml
> new file mode 100644
> index 0000000000000..efd7b2602c336
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/amlogic,a9-dma.yaml
[ ... ]
> @@ -0,0 +1,65 @@
> +properties:
> + compatible:
> + const: amlogic,a9-dma
[ ... ]
> + clock-names:
> + const: sys
> +
> + '#dma-cells':
> + const: 2
[Severity: Low]
Is a description needed for this `#dma-cells` property to explain the layout
and purpose of its 2 cells? Without it, device tree writers won't have
documentation clarifying which cell represents the channel ID and which
represents the direction.
> +
> + dma-channels:
> + maximum: 64
[ ... ]
> +examples:
> + - |
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + dma-controller@fe400000{
[Severity: Low]
This isn't a bug, but should there be a space before the opening brace here?
Normally it is formatted as `dma-controller@fe400000 {`.
> + compatible = "amlogic,a9-dma";
> + reg = <0xfe400000 0x4000>;
> + interrupts = <GIC_SPI 35 IRQ_TYPE_EDGE_RISING>;
> + clocks = <&clkc 45>;
> + #dma-cells = <2>;
> + dma-channels = <28>;
> + };
> diff --git a/include/dt-bindings/dma/amlogic,a9-dma.h b/include/dt-bindings/dma/amlogic,a9-dma.h
> new file mode 100644
> index 0000000000000..c59c2fd4b9561
> --- /dev/null
> +++ b/include/dt-bindings/dma/amlogic,a9-dma.h
> @@ -0,0 +1,8 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
> +
> +#ifndef __DT_BINDINGS_DMA_AMLOGIC_DMA_H__
> +#define __DT_BINDINGS_DMA_AMLOGIC_DMA_H__
> +
> +#define DMA_TX 0
> +#define DMA_RX 1
[Severity: Low]
Could these macro names lead to namespace collisions since they lack a vendor
or SoC prefix? These names are already widely used across the kernel, and
defining them generically in a global dt-bindings header might cause
preprocessor collisions and build failures for any driver or DTS file that
includes this header.
> +#endif /* __DT_BINDINGS_DMA_AMLOGIC_DMA_H__ */
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-amlogic-dma-v9-0-558d672c4a95@amlogic.com?part=1
^ permalink raw reply
* [PATCH v2 3/3] firmware: qcom: scm: Allow QSEECOM on HP EliteBook X G2q 14 AI
From: Jason Pettit @ 2026-06-26 5:47 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel, Akhil P Oommen,
Mahadevan P, Sibi Sankar, Jingyi Wang, Ananthu C V, Jason Pettit
In-Reply-To: <20260625-glymur-send-v2-0-00905324ffbf@oss.qualcomm.com>
The HP EliteBook X G2q 14" Next Gen AI PC is a Snapdragon X2 Elite
(Glymur) laptop whose firmware exposes EFI variables through QSEECOM.
Add its board compatible to the QSEECOM allowlist so that EFI variable
access (efivarfs) works on this machine.
Without it the kernel reports "qseecom: untested machine, skipping" and
efivarfs is empty.
Signed-off-by: Jason Pettit <jason.pettit@oss.qualcomm.com>
---
drivers/firmware/qcom/qcom_scm.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 6b601a4b89db..618437f5aac8 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -2296,6 +2296,7 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = {
{ .compatible = "dell,xps13-9345" },
{ .compatible = "ecs,liva-qc710" },
{ .compatible = "hp,elitebook-ultra-g1q" },
+ { .compatible = "hp,elitebook-x-g2q" },
{ .compatible = "hp,omnibook-x14" },
{ .compatible = "huawei,gaokun3" },
{ .compatible = "lenovo,flex-5g" },
--
2.50.1
^ permalink raw reply related
* [PATCH v2 2/3] arm64: dts: qcom: Add HP EliteBook X G2q 14 AI
From: Jason Pettit @ 2026-06-26 5:47 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel, Akhil P Oommen,
Mahadevan P, Sibi Sankar, Jingyi Wang, Ananthu C V, Jason Pettit
In-Reply-To: <20260625-glymur-send-v2-0-00905324ffbf@oss.qualcomm.com>
Add board support for the HP EliteBook X G2q 14" Next Gen AI PC
(product SKU C4JG0AV, board 8E91), a Snapdragon X2 Elite (Glymur)
laptop, using the "hp,elitebook-x-g2q" / "qcom,glymur" compatible.
Enabled by this device tree:
- internal eDP panel (samsung,atna33xc20)
- 2x USB Type-C with DisplayPort alt-mode and USB
- chassis HDMI output
- chassis USB-A host port (usb_mp multiport controller)
- internal eUSB2 host with the Elan fingerprint reader
- NVMe SSD on PCIe5
- Wi-Fi and Bluetooth
- HID-over-I2C keyboard, touchpad, touchscreen; lid switch
- Adreno GPU and GMU (Freedreno GL on Mesa)
- audio playback and capture
- real-time clock
- compute DSP (cDSP)
The HDMI jack is driven by a power-only DisplayPort-to-HDMI LSPCON on
the usb_2 combo-PHY DP lanes rather than being a third USB-C port; HPD
is on gpio126. The LSPCON is on an I/O sub-board with no I2C/AUX control
path, so it is modelled with the generic simple-bridge "parade,ps185hdm"
compatible used by the in-tree x1e80100 HDMI-bridge boards (the exact
bridge part is unconfirmed) and it needs CONFIG_DRM_SIMPLE_BRIDGE.
The &gpu/&gmu enable, the audio nodes and &remoteproc_soccp opt into
glymur.dtsi SoC nodes that are still in-flight; those series are
declared as prerequisites in the cover letter.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Jason Pettit <jason.pettit@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/Makefile | 1 +
.../boot/dts/qcom/glymur-hp-elitebook-x-g2q.dts | 1019 ++++++++++++++++++++
2 files changed, 1020 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 6f33c4e2f09c..b10629808b76 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -16,6 +16,7 @@ dtb-$(CONFIG_ARCH_QCOM) += apq8096sg-db820c.dtb
dtb-$(CONFIG_ARCH_QCOM) += apq8096-ifc6640.dtb
dtb-$(CONFIG_ARCH_QCOM) += eliza-mtp.dtb
dtb-$(CONFIG_ARCH_QCOM) += glymur-crd.dtb
+dtb-$(CONFIG_ARCH_QCOM) += glymur-hp-elitebook-x-g2q.dtb
dtb-$(CONFIG_ARCH_QCOM) += hamoa-iot-evk.dtb
hamoa-iot-evk-el2-dtbs := hamoa-iot-evk.dtb x1-el2.dtbo
diff --git a/arch/arm64/boot/dts/qcom/glymur-hp-elitebook-x-g2q.dts b/arch/arm64/boot/dts/qcom/glymur-hp-elitebook-x-g2q.dts
new file mode 100644
index 000000000000..b3e351ed7ea7
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/glymur-hp-elitebook-x-g2q.dts
@@ -0,0 +1,1019 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+/dts-v1/;
+
+#include "glymur.dtsi"
+
+#include "pmcx0102.dtsi" /* SPMI0: SID-2/3 SPMI1: SID-2/3 */
+#include "pmh0101.dtsi" /* SPMI0: SID-1 */
+#include "pmh0110-glymur.dtsi" /* SPMI0: SID-5/7 SPMI1: SID-5 */
+#include "pmh0104-glymur.dtsi" /* SPMI0: SID-8/9 SPMI1: SID-11 */
+#include "pmk8850.dtsi" /* SPMI0: SID-0 */
+#include "smb2370.dtsi" /* SPMI2: SID-9/10 */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/gpio-keys.h>
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
+
+/ {
+ model = "HP EliteBook X G2q 14 AI";
+ compatible = "hp,elitebook-x-g2q", "qcom,glymur";
+
+ aliases {
+ serial1 = &uart14;
+ };
+
+ clocks {
+ xo_board: xo-board {
+ compatible = "fixed-clock";
+ clock-frequency = <38400000>;
+ #clock-cells = <0>;
+ };
+
+ sleep_clk: sleep-clk {
+ compatible = "fixed-clock";
+ clock-frequency = <32000>;
+ #clock-cells = <0>;
+ };
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ pinctrl-0 = <&lid_default>;
+ pinctrl-names = "default";
+
+ switch-lid {
+ label = "lid";
+ gpios = <&tlmm 92 GPIO_ACTIVE_LOW>;
+ linux,input-type = <EV_SW>;
+ linux,code = <SW_LID>;
+ wakeup-source;
+ wakeup-event-action = <EV_ACT_DEASSERTED>;
+ };
+ };
+
+ hdmi-bridge {
+ compatible = "parade,ps185hdm";
+
+ pinctrl-0 = <&usb2_dp_hot_plug_detect>;
+ pinctrl-names = "default";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ hdmi_bridge_in: endpoint {
+ remote-endpoint = <&usb_2_qmpphy_out_dp>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ hdmi_bridge_out: endpoint {
+ remote-endpoint = <&hdmi_con>;
+ };
+ };
+ };
+ };
+
+ hdmi-connector {
+ compatible = "hdmi-connector";
+ type = "a";
+
+ port {
+ hdmi_con: endpoint {
+ remote-endpoint = <&hdmi_bridge_out>;
+ };
+ };
+ };
+
+ pmic-glink {
+ compatible = "qcom,glymur-pmic-glink",
+ "qcom,pmic-glink";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* Left side port */
+ connector@0 {
+ compatible = "usb-c-connector";
+ reg = <0>;
+ power-role = "dual";
+ data-role = "dual";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ pmic_glink_hs_in: endpoint {
+ remote-endpoint = <&usb_0_dwc3_hs>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ pmic_glink_ss_in: endpoint {
+ remote-endpoint = <&usb_0_qmpphy_out>;
+ };
+ };
+ };
+ };
+
+ /* Right side port */
+ connector@1 {
+ compatible = "usb-c-connector";
+ reg = <1>;
+ power-role = "dual";
+ data-role = "dual";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ pmic_glink_hs_in1: endpoint {
+ remote-endpoint = <&usb_1_dwc3_hs>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ pmic_glink_ss_in1: endpoint {
+ remote-endpoint = <&usb_1_qmpphy_out>;
+ };
+ };
+ };
+ };
+ };
+
+ vreg_edp_3p3: regulator-edp-3p3 {
+ compatible = "regulator-fixed";
+
+ regulator-name = "VREG_EDP_3P3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpio = <&tlmm 70 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+
+ pinctrl-0 = <&edp_reg_en>;
+ pinctrl-names = "default";
+
+ regulator-boot-on;
+ };
+
+ vreg_nvme: regulator-nvme {
+ compatible = "regulator-fixed";
+
+ regulator-name = "VREG_NVME_3P3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpio = <&pmh0101_gpios 14 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+
+ pinctrl-0 = <&nvme_reg_en>;
+ pinctrl-names = "default";
+
+ regulator-boot-on;
+ };
+
+ vreg_wcn_0p95: regulator-wcn-0p95 {
+ compatible = "regulator-fixed";
+
+ regulator-name = "VREG_WCN_0P95";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <950000>;
+
+ vin-supply = <&vreg_wcn_3p3>;
+ };
+
+ vreg_wcn_3p3: regulator-wcn-3p3 {
+ compatible = "regulator-fixed";
+
+ regulator-name = "VREG_WCN_3P3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpio = <&tlmm 94 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+
+ pinctrl-0 = <&wcn_sw_en>;
+ pinctrl-names = "default";
+
+ regulator-boot-on;
+ };
+
+ sound {
+ compatible = "qcom,glymur-sndcard";
+ model = "GLYMUR-HP-EliteBook-X-G2q";
+
+ audio-routing = "WooferLeft IN", "WSA WSA_SPK1 OUT",
+ "TweeterLeft IN", "WSA WSA_SPK2 OUT",
+ "WooferRight IN", "WSA2 WSA_SPK1 OUT",
+ "TweeterRight IN", "WSA2 WSA_SPK2 OUT",
+ "VA DMIC0", "vdd-micb",
+ "VA DMIC1", "vdd-micb",
+ "VA DMIC2", "vdd-micb",
+ "VA DMIC3", "vdd-micb";
+
+ va-dai-link {
+ link-name = "VA Capture";
+
+ codec {
+ sound-dai = <&lpass_vamacro 0>;
+ };
+
+ cpu {
+ sound-dai = <&q6apmbedai VA_CODEC_DMA_TX_0>;
+ };
+
+ platform {
+ sound-dai = <&q6apm>;
+ };
+ };
+
+ wsa-dai-link {
+ link-name = "WSA Playback";
+
+ codec {
+ sound-dai = <&left_woofer>, <&left_tweeter>,
+ <&swr0 0>, <&lpass_wsamacro 0>,
+ <&right_woofer>, <&right_tweeter>,
+ <&swr3 0>, <&lpass_wsa2macro 0>;
+ };
+
+ cpu {
+ sound-dai = <&q6apmbedai WSA_CODEC_DMA_RX_0>;
+ };
+
+ platform {
+ sound-dai = <&q6apm>;
+ };
+ };
+ };
+
+ wcn7850-pmu {
+ compatible = "qcom,wcn7850-pmu";
+
+ vdd-supply = <&vreg_wcn_0p95>;
+ vddio-supply = <&vreg_l15b_e0_1p8>;
+ vddaon-supply = <&vreg_l15b_e0_1p8>;
+ vdddig-supply = <&vreg_l15b_e0_1p8>;
+ vddrfa1p2-supply = <&vreg_l15b_e0_1p8>;
+ vddrfa1p8-supply = <&vreg_l15b_e0_1p8>;
+
+ wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>;
+ bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>;
+
+ pinctrl-0 = <&wcn_wlan_bt_en>;
+ pinctrl-names = "default";
+
+ regulators {
+ vreg_pmu_rfa_cmn: ldo0 {
+ regulator-name = "vreg_pmu_rfa_cmn";
+ };
+
+ vreg_pmu_aon_0p59: ldo1 {
+ regulator-name = "vreg_pmu_aon_0p59";
+ };
+
+ vreg_pmu_wlcx_0p8: ldo2 {
+ regulator-name = "vreg_pmu_wlcx_0p8";
+ };
+
+ vreg_pmu_wlmx_0p85: ldo3 {
+ regulator-name = "vreg_pmu_wlmx_0p85";
+ };
+
+ vreg_pmu_btcmx_0p85: ldo4 {
+ regulator-name = "vreg_pmu_btcmx_0p85";
+ };
+
+ vreg_pmu_rfa_0p8: ldo5 {
+ regulator-name = "vreg_pmu_rfa_0p8";
+ };
+
+ vreg_pmu_rfa_1p2: ldo6 {
+ regulator-name = "vreg_pmu_rfa_1p2";
+ };
+
+ vreg_pmu_rfa_1p8: ldo7 {
+ regulator-name = "vreg_pmu_rfa_1p8";
+ };
+
+ vreg_pmu_pcie_0p9: ldo8 {
+ regulator-name = "vreg_pmu_pcie_0p9";
+ };
+
+ vreg_pmu_pcie_1p8: ldo9 {
+ regulator-name = "vreg_pmu_pcie_1p8";
+ };
+ };
+ };
+};
+
+&apps_rsc {
+ regulators-0 {
+ compatible = "qcom,pmh0101-rpmh-regulators";
+ qcom,pmic-id = "B_E0";
+
+ vreg_l1b_e0_1p8: ldo1 {
+ regulator-name = "vreg_l1b_e0_1p8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l7b_e0_3p1: ldo7 {
+ regulator-name = "vreg_l7b_e0_3p1";
+ regulator-min-microvolt = <3072000>;
+ regulator-max-microvolt = <3072000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l15b_e0_1p8: ldo15 {
+ regulator-name = "vreg_l15b_e0_1p8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l18b_e0_1p2: ldo18 {
+ regulator-name = "vreg_l18b_e0_1p2";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+ };
+
+ regulators-1 {
+ compatible = "qcom,pmcx0102-rpmh-regulators";
+ qcom,pmic-id = "C_E1";
+
+ vreg_l1c_e1_0p91: ldo1 {
+ regulator-name = "vreg_l1c_e1_0p91";
+ regulator-min-microvolt = <912000>;
+ regulator-max-microvolt = <912000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+ };
+
+ regulators-2 {
+ compatible = "qcom,pmh0110-rpmh-regulators";
+ qcom,pmic-id = "F_E0";
+
+ vreg_l2f_e0_0p94: ldo2 {
+ regulator-name = "vreg_l2f_e0_0p94";
+ regulator-min-microvolt = <936000>;
+ regulator-max-microvolt = <936000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l3f_e0_0p91: ldo3 {
+ regulator-name = "vreg_l3f_e0_0p91";
+ regulator-min-microvolt = <912000>;
+ regulator-max-microvolt = <912000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+ };
+
+ regulators-3 {
+ compatible = "qcom,pmh0110-rpmh-regulators";
+ qcom,pmic-id = "F_E1";
+
+ vreg_l2f_e1_0p88: ldo2 {
+ regulator-name = "vreg_l2f_e1_0p88";
+ regulator-min-microvolt = <880000>;
+ regulator-max-microvolt = <880000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l4f_e1_1p2: ldo4 {
+ regulator-name = "vreg_l4f_e1_1p2";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+ };
+
+ regulators-4 {
+ compatible = "qcom,pmh0110-rpmh-regulators";
+ qcom,pmic-id = "H_E0";
+
+ vreg_l1h_e0_0p94: ldo1 {
+ regulator-name = "vreg_l1h_e0_0p94";
+ regulator-min-microvolt = <936000>;
+ regulator-max-microvolt = <936000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l2h_e0_0p88: ldo2 {
+ regulator-name = "vreg_l2h_e0_0p88";
+ regulator-min-microvolt = <880000>;
+ regulator-max-microvolt = <880000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+
+ vreg_l4h_e0_1p2: ldo4 {
+ regulator-name = "vreg_l4h_e0_1p2";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+ };
+ };
+};
+
+&gmu {
+ status = "okay";
+};
+
+&gpu {
+ status = "okay";
+};
+
+&i2c0 {
+ clock-frequency = <400000>;
+
+ status = "okay";
+
+ touchpad@2c {
+ compatible = "hid-over-i2c";
+ reg = <0x2c>;
+
+ hid-descr-addr = <0x20>;
+ interrupts-extended = <&tlmm 66 IRQ_TYPE_LEVEL_LOW>;
+
+ pinctrl-0 = <&tpad_default>;
+ pinctrl-names = "default";
+
+ wakeup-source;
+ };
+
+ keyboard@3a {
+ compatible = "hid-over-i2c";
+ reg = <0x3a>;
+
+ hid-descr-addr = <0x1>;
+ interrupts-extended = <&tlmm 67 IRQ_TYPE_LEVEL_LOW>;
+
+ pinctrl-0 = <&kybd_default>;
+ pinctrl-names = "default";
+
+ wakeup-source;
+ };
+};
+
+&i2c8 {
+ clock-frequency = <400000>;
+
+ status = "okay";
+
+ touchscreen@10 {
+ compatible = "hid-over-i2c";
+ reg = <0x10>;
+
+ hid-descr-addr = <0x1>;
+ interrupts-extended = <&tlmm 51 IRQ_TYPE_LEVEL_LOW>;
+
+ post-power-on-delay-ms = <200>;
+
+ pinctrl-0 = <&ts0_default>;
+ pinctrl-names = "default";
+ };
+};
+
+&lpass_vamacro {
+ vdd-micb-supply = <&vreg_l1b_e0_1p8>;
+
+ pinctrl-0 = <&dmic01_default>, <&dmic23_default>;
+ pinctrl-names = "default";
+
+ qcom,dmic-sample-rate = <4800000>;
+};
+
+&mdss {
+ status = "okay";
+};
+
+&mdss_dp0 {
+ status = "okay";
+};
+
+&mdss_dp0_out {
+ link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>;
+};
+
+&mdss_dp1 {
+ status = "okay";
+};
+
+&mdss_dp1_out {
+ link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>;
+};
+
+&mdss_dp2 {
+ status = "okay";
+};
+
+&mdss_dp2_out {
+ link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>;
+};
+
+&mdss_dp3 {
+ /delete-property/ #sound-dai-cells;
+
+ status = "okay";
+
+ aux-bus {
+ panel {
+ compatible = "samsung,atna33xc20";
+ enable-gpios = <&tlmm 18 GPIO_ACTIVE_HIGH>;
+ power-supply = <&vreg_edp_3p3>;
+
+ pinctrl-0 = <&edp_bl_en>;
+ pinctrl-names = "default";
+
+ port {
+ edp_panel_in: endpoint {
+ remote-endpoint = <&mdss_dp3_out>;
+ };
+ };
+ };
+ };
+};
+
+&mdss_dp3_out {
+ data-lanes = <0 1 2 3>;
+ link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>;
+
+ remote-endpoint = <&edp_panel_in>;
+};
+
+&mdss_dp3_phy {
+ vdda-phy-supply = <&vreg_l2f_e1_0p88>;
+ vdda-pll-supply = <&vreg_l4f_e1_1p2>;
+
+ status = "okay";
+};
+
+&pcie4 {
+ pinctrl-0 = <&pcie4_default>;
+ pinctrl-names = "default";
+
+ status = "okay";
+};
+
+&pcie4_phy {
+ vdda-phy-supply = <&vreg_l1c_e1_0p91>;
+ vdda-pll-supply = <&vreg_l4f_e1_1p2>;
+
+ status = "okay";
+};
+
+&pcie4_port0 {
+ reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>;
+ wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>;
+
+ wifi@0 {
+ compatible = "pci17cb,1107";
+ reg = <0x10000 0x0 0x0 0x0 0x0>;
+
+ vddaon-supply = <&vreg_pmu_aon_0p59>;
+ vddwlcx-supply = <&vreg_pmu_wlcx_0p8>;
+ vddwlmx-supply = <&vreg_pmu_wlmx_0p85>;
+ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>;
+ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>;
+ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>;
+ vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>;
+ vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>;
+ vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>;
+ };
+};
+
+&pcie5 {
+ vddpe-3v3-supply = <&vreg_nvme>;
+
+ pinctrl-0 = <&pcie5_default>;
+ pinctrl-names = "default";
+
+ status = "okay";
+};
+
+&pcie5_phy {
+ vdda-phy-supply = <&vreg_l2f_e0_0p94>;
+ vdda-pll-supply = <&vreg_l4h_e0_1p2>;
+
+ status = "okay";
+};
+
+&pcie5_port0 {
+ reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>;
+ wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>;
+
+ status = "okay";
+};
+
+&pmh0101_gpios {
+ nvme_reg_en: nvme-reg-en-state {
+ pins = "gpio14";
+ function = "normal";
+ bias-disable;
+ };
+};
+
+&pmk8850_rtc {
+ qcom,no-alarm;
+ qcom,uefi-rtc-info;
+};
+
+&remoteproc_adsp {
+ firmware-name = "qcom/glymur/hp/elitebook-x-g2q/adsp.mbn",
+ "qcom/glymur/hp/elitebook-x-g2q/adsp_dtb.mbn";
+
+ status = "okay";
+};
+
+&remoteproc_cdsp {
+ firmware-name = "qcom/glymur/hp/elitebook-x-g2q/cdsp.mbn",
+ "qcom/glymur/hp/elitebook-x-g2q/cdsp_dtb.mbn";
+
+ status = "okay";
+};
+
+&remoteproc_soccp {
+ status = "okay";
+};
+
+&smb2370_j_e2_eusb2_repeater {
+ vdd18-supply = <&vreg_l15b_e0_1p8>;
+ vdd3-supply = <&vreg_l7b_e0_3p1>;
+};
+
+&smb2370_k_e2_eusb2_repeater {
+ vdd18-supply = <&vreg_l15b_e0_1p8>;
+ vdd3-supply = <&vreg_l7b_e0_3p1>;
+};
+
+/*
+ * usb_2 is a fixed DP-to-HDMI bridge; its SID-11 eUSB2 repeater is
+ * firmware-gated and never probes, so leave smb2370_l disabled.
+ */
+&smb2370_l_e2 {
+ status = "disabled";
+};
+
+&swr0 {
+ status = "okay";
+
+ /* WSA8845, left woofer */
+ left_woofer: speaker@0,0 {
+ compatible = "sdw20217020400";
+ reg = <0 0>;
+ reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "WooferLeft";
+ vdd-1p8-supply = <&vreg_l15b_e0_1p8>;
+ vdd-io-supply = <&vreg_l18b_e0_1p2>;
+ qcom,port-mapping = <1 2 3 7 12 14>;
+ };
+
+ /* WSA8845, left tweeter */
+ left_tweeter: speaker@0,1 {
+ compatible = "sdw20217020400";
+ reg = <0 1>;
+ reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "TweeterLeft";
+ vdd-1p8-supply = <&vreg_l15b_e0_1p8>;
+ vdd-io-supply = <&vreg_l18b_e0_1p2>;
+ qcom,port-mapping = <4 5 6 7 13 15>;
+ };
+};
+
+&swr3 {
+ status = "okay";
+
+ /* WSA8845, right woofer */
+ right_woofer: speaker@0,0 {
+ compatible = "sdw20217020400";
+ reg = <0 0>;
+ reset-gpios = <&lpass_tlmm 13 GPIO_ACTIVE_LOW>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "WooferRight";
+ vdd-1p8-supply = <&vreg_l15b_e0_1p8>;
+ vdd-io-supply = <&vreg_l18b_e0_1p2>;
+ qcom,port-mapping = <1 2 3 7 12 14>;
+ };
+
+ /* WSA8845, right tweeter */
+ right_tweeter: speaker@0,1 {
+ compatible = "sdw20217020400";
+ reg = <0 1>;
+ reset-gpios = <&lpass_tlmm 13 GPIO_ACTIVE_LOW>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "TweeterRight";
+ vdd-1p8-supply = <&vreg_l15b_e0_1p8>;
+ vdd-io-supply = <&vreg_l18b_e0_1p2>;
+ qcom,port-mapping = <4 5 6 7 13 15>;
+ };
+};
+
+&tlmm {
+ gpio-reserved-ranges = <4 4>, /* EC TZ Secure I3C */
+ <10 2>, /* OOB UART */
+ <44 4>, /* Security SPI (TPM) */
+ <65 1>; /* EC reset */
+
+ edp_bl_en: edp-bl-en-state {
+ pins = "gpio18";
+ function = "gpio";
+ drive-strength = <16>;
+ bias-disable;
+ };
+
+ ts0_default: ts0-default-state {
+ pins = "gpio51";
+ function = "gpio";
+ bias-disable;
+ };
+
+ tpad_default: tpad-default-state {
+ pins = "gpio66";
+ function = "gpio";
+ bias-disable;
+ };
+
+ kybd_default: kybd-default-state {
+ pins = "gpio67";
+ function = "gpio";
+ bias-disable;
+ };
+
+ edp_reg_en: edp-reg-en-state {
+ pins = "gpio70";
+ function = "gpio";
+ drive-strength = <16>;
+ bias-disable;
+ };
+
+ lid_default: lid-default-state {
+ pins = "gpio92";
+ function = "gpio";
+ bias-disable;
+ };
+
+ wcn_sw_en: wcn-sw-en-state {
+ pins = "gpio94";
+ function = "gpio";
+ drive-strength = <2>;
+ bias-disable;
+ };
+
+ wcn_wlan_bt_en: wcn-wlan-bt-en-state {
+ pins = "gpio116", "gpio117";
+ function = "gpio";
+ drive-strength = <2>;
+ bias-disable;
+ };
+
+ usb2_dp_hot_plug_detect: usb2-dp-hot-plug-detect-state {
+ pins = "gpio126";
+ function = "usb2_dp";
+ bias-disable;
+ };
+
+ pcie4_default: pcie4-default-state {
+ clkreq-n-pins {
+ pins = "gpio147";
+ function = "pcie4_clk_req_n";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+
+ perst-n-pins {
+ pins = "gpio146";
+ function = "gpio";
+ drive-strength = <2>;
+ bias-disable;
+ };
+
+ wake-n-pins {
+ pins = "gpio148";
+ function = "gpio";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ pcie5_default: pcie5-default-state {
+ clkreq-n-pins {
+ pins = "gpio153";
+ function = "pcie5_clk_req_n";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+
+ perst-n-pins {
+ pins = "gpio152";
+ function = "gpio";
+ drive-strength = <2>;
+ bias-disable;
+ };
+
+ wake-n-pins {
+ pins = "gpio154";
+ function = "gpio";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+};
+
+&uart14 {
+ status = "okay";
+
+ bluetooth {
+ compatible = "qcom,wcn7850-bt";
+ max-speed = <3200000>;
+
+ vddaon-supply = <&vreg_pmu_aon_0p59>;
+ vddwlcx-supply = <&vreg_pmu_wlcx_0p8>;
+ vddwlmx-supply = <&vreg_pmu_wlmx_0p85>;
+ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>;
+ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>;
+ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>;
+ vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>;
+ };
+};
+
+&usb_0 {
+ status = "okay";
+};
+
+&usb_0_dwc3_hs {
+ remote-endpoint = <&pmic_glink_hs_in>;
+};
+
+&usb_0_hsphy {
+ vdd-supply = <&vreg_l3f_e0_0p91>;
+ vdda12-supply = <&vreg_l4h_e0_1p2>;
+
+ phys = <&smb2370_j_e2_eusb2_repeater>;
+
+ status = "okay";
+};
+
+&usb_0_qmpphy {
+ vdda-phy-supply = <&vreg_l4h_e0_1p2>;
+ vdda-pll-supply = <&vreg_l3f_e0_0p91>;
+ refgen-supply = <&vreg_l2f_e0_0p94>;
+
+ status = "okay";
+};
+
+&usb_0_qmpphy_out {
+ remote-endpoint = <&pmic_glink_ss_in>;
+};
+
+&usb_1 {
+ status = "okay";
+};
+
+&usb_1_dwc3_hs {
+ remote-endpoint = <&pmic_glink_hs_in1>;
+};
+
+&usb_1_hsphy {
+ vdd-supply = <&vreg_l3f_e0_0p91>;
+ vdda12-supply = <&vreg_l4h_e0_1p2>;
+
+ phys = <&smb2370_k_e2_eusb2_repeater>;
+
+ status = "okay";
+};
+
+&usb_1_qmpphy {
+ vdda-phy-supply = <&vreg_l4h_e0_1p2>;
+ vdda-pll-supply = <&vreg_l1h_e0_0p94>;
+ refgen-supply = <&vreg_l2f_e0_0p94>;
+
+ status = "okay";
+};
+
+&usb_1_qmpphy_out {
+ remote-endpoint = <&pmic_glink_ss_in1>;
+};
+
+/*
+ * usb_2 is not exposed as a connector; its combo-PHY's DP lanes drive the
+ * chassis HDMI bridge (see hdmi-bridge).
+ */
+&usb_2 {
+ maximum-speed = "high-speed";
+
+ phys = <&usb_2_hsphy>;
+ phy-names = "usb2-phy";
+
+ status = "okay";
+};
+
+&usb_2_hsphy {
+ vdd-supply = <&vreg_l3f_e0_0p91>;
+ vdda12-supply = <&vreg_l4h_e0_1p2>;
+
+ status = "okay";
+};
+
+&usb_2_qmpphy {
+ vdda-phy-supply = <&vreg_l4h_e0_1p2>;
+ vdda-pll-supply = <&vreg_l3f_e0_0p91>;
+ refgen-supply = <&vreg_l2f_e0_0p94>;
+
+ /delete-property/ mode-switch;
+ /delete-property/ orientation-switch;
+
+ status = "okay";
+
+ ports {
+ port@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /delete-node/ endpoint;
+
+ usb_2_qmpphy_out_dp: endpoint@0 {
+ reg = <0>;
+
+ data-lanes = <3 2 1 0>;
+ remote-endpoint = <&hdmi_bridge_in>;
+ };
+ };
+ };
+};
+
+/* Internal USB2 host for the Elan fingerprint reader (usb 04f3:0ca8). */
+&usb_hs {
+ status = "okay";
+};
+
+&usb_hs_phy {
+ vdd-supply = <&vreg_l2h_e0_0p88>;
+ vdda12-supply = <&vreg_l4h_e0_1p2>;
+
+ status = "okay";
+};
+
+/*
+ * Multiport USB host: port 0 is the chassis USB-A port; port 1 is an
+ * internal USB HID device.
+ */
+&usb_mp {
+ status = "okay";
+};
+
+&usb_mp_hsphy0 {
+ vdd-supply = <&vreg_l2h_e0_0p88>;
+ vdda12-supply = <&vreg_l4h_e0_1p2>;
+
+ status = "okay";
+};
+
+&usb_mp_hsphy1 {
+ vdd-supply = <&vreg_l2h_e0_0p88>;
+ vdda12-supply = <&vreg_l4h_e0_1p2>;
+
+ status = "okay";
+};
+
+&usb_mp_qmpphy0 {
+ vdda-phy-supply = <&vreg_l4h_e0_1p2>;
+ vdda-pll-supply = <&vreg_l2h_e0_0p88>;
+ refgen-supply = <&vreg_l4f_e1_1p2>;
+
+ status = "okay";
+};
+
+&usb_mp_qmpphy1 {
+ vdda-phy-supply = <&vreg_l4h_e0_1p2>;
+ vdda-pll-supply = <&vreg_l2h_e0_0p88>;
+ refgen-supply = <&vreg_l4f_e1_1p2>;
+
+ status = "okay";
+};
--
2.50.1
^ permalink raw reply related
* [PATCH v2 1/3] dt-bindings: arm: qcom: Add HP EliteBook X G2q 14 AI
From: Jason Pettit @ 2026-06-26 5:47 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel, Akhil P Oommen,
Mahadevan P, Sibi Sankar, Jingyi Wang, Ananthu C V, Jason Pettit
In-Reply-To: <20260625-glymur-send-v2-0-00905324ffbf@oss.qualcomm.com>
The HP EliteBook X G2q 14 AI is a Snapdragon X2 Elite (Glymur) laptop.
Document its top-level "hp,elitebook-x-g2q" compatible.
Signed-off-by: Jason Pettit <jason.pettit@oss.qualcomm.com>
---
Documentation/devicetree/bindings/arm/qcom.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/qcom.yaml b/Documentation/devicetree/bindings/arm/qcom.yaml
index 50cc18a6ec5e..934f62407b86 100644
--- a/Documentation/devicetree/bindings/arm/qcom.yaml
+++ b/Documentation/devicetree/bindings/arm/qcom.yaml
@@ -68,6 +68,7 @@ properties:
- items:
- enum:
+ - hp,elitebook-x-g2q
- qcom,glymur-crd
- const: qcom,glymur
--
2.50.1
^ permalink raw reply related
* [PATCH v2 0/3] X2 Elite HP EliteBook X G2q support
From: Jason Pettit @ 2026-06-26 5:47 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel, Akhil P Oommen,
Mahadevan P, Sibi Sankar, Jingyi Wang, Ananthu C V, Jason Pettit
This series adds initial device-tree support for the HP EliteBook X G2q
14" Next Gen AI PC (product SKU C4JG0AV, board 8E91), a Snapdragon X2
Elite (Glymur) laptop.
The board was brought up and validated on real hardware. The electrical
description (PMIC rails, PHY supplies, the HDMI bridge wiring) was
verified against the live unit rather than copied from a Glymur CRD.
Working on this laptop with this series applied:
- Internal eDP panel (samsung,atna33xc20)
- 2x USB Type-C with DisplayPort alt-mode and USB
- Chassis HDMI output
- Chassis USB-A host port
- Fingerprint reader
- NVMe SSD on PCIe5
- Wi-Fi, Bluetooth
- Keyboard, touchpad, touchscreen, lid switch
- Adreno GPU and GMU (Freedreno GL on Mesa)
- Audio playback and capture
- Real-time clock
- Compute DSP (cDSP)
This patch series builds upon the recent work of Qualcomm engineers to
enable another Glymur-based laptop.
Like its peer submissions, the camera support is missing today.
This has the same known dependencies as other boards on this
platform (audio, GPU, SoCCP series).
The GPU device-tree patches come from the partially merged Glymur GPU DT
v5 series at
https://lore.kernel.org/all/20260522-glymur-gpu-dt-v5-0-562c406b210c@oss.qualcomm.com/
SCMI on this configuration needs CONFIG_QCOM_CPUCP_MBOX built in (=y).
The bindings patch is independent and can be reviewed/applied on its own.
The board patch can be reviewed in parallel with the GPU, LPASS and SoCCP
SoC series. Until those land in the target tree, it will trip the expected
"Label or path ... not found" dtc error. As with the X2 Elite Lenovo Yoga
Slim 7x submission, this v1 is expected to trigger some dt-bindings warnings
that come from the in-flight dependency series, not from the board patch
itself.
Signed-off-by: Jason Pettit <jason.pettit@oss.qualcomm.com>
---
Changes in v2:
- Enable the PMIC RTC (qcom,no-alarm + qcom,uefi-rtc-info)
- Add the SMB2370 eUSB2 repeaters on usb_0/usb_1
- Enable the compute DSP (remoteproc_cdsp / fastrpc)
- Addressed comments from reviewers on v1
- Link to v1: https://lore.kernel.org/linux-arm-msm/20260620-glymur-send-v1-0-fc4a2cfd107c@oss.qualcomm.com
---
Jason Pettit (3):
dt-bindings: arm: qcom: Add HP EliteBook X G2q 14 AI
arm64: dts: qcom: Add HP EliteBook X G2q 14 AI
firmware: qcom: scm: Allow QSEECOM on HP EliteBook X G2q 14 AI
Documentation/devicetree/bindings/arm/qcom.yaml | 1 +
arch/arm64/boot/dts/qcom/Makefile | 1 +
.../boot/dts/qcom/glymur-hp-elitebook-x-g2q.dts | 1019 ++++++++++++++++++++
drivers/firmware/qcom/qcom_scm.c | 1 +
4 files changed, 1022 insertions(+)
---
base-commit: dd60bcf0ad17dd15d6d4d677315c4cf9827445f9
change-id: 20260619-glymur-send-1136d243a120
prerequisite-patch-id: 36f4bb7740fd65d808fa6685bce4b03798a547ff
prerequisite-patch-id: 054631082c45d3ab3117f541f0d4d90b660dac73
prerequisite-patch-id: af18aef027c45213c11b436746fa31b3b6ebe46b
prerequisite-message-id: <20260419-glymur_dp-v1-1-ad1067a8e8ae@oss.qualcomm.com>
prerequisite-patch-id: 7cb9a168c33491bf91ece900a2218aa7b4b68ddd
prerequisite-message-id: <20260523085511.2532669-1-sibi.sankar@oss.qualcomm.com>
prerequisite-patch-id: 43e96d8a5063f51e2b5ee75fc8c9e10682e5670c
prerequisite-patch-id: 2e71d345f84a821d89da2a6064954409fdb1277d
prerequisite-patch-id: 0b9f5995044bb3d3ffda227ac986b755fa118c63
prerequisite-message-id: <20260623-knp-soccp-v7-0-1ec7bb5c9fec@oss.qualcomm.com>
prerequisite-patch-id: 8459bcae98ac156f6576657fe9233badcd385218
prerequisite-patch-id: b5182f879cdd23c5a0c64bf09b40213b71059b51
prerequisite-patch-id: c962e3f24c435e91ae563bbba9e396bf277ff0ad
prerequisite-patch-id: b9cd65895f55e9d76ba28ef8f9aaf6cdefe1b9d3
prerequisite-patch-id: 6484d1056e30f2027e81eee375d979197754bc12
prerequisite-patch-id: fa7954f6fd2d27a6f0e805c9643b70e7e201c503
prerequisite-message-id: <20260403-glymur-soccp-v3-1-f0e8d57f11ba@oss.qualcomm.com>
prerequisite-patch-id: 48c26c428cbeaa674a615704d1abeb87e4e965c8
Best regards,
--
Jason Pettit <jason.pettit@oss.qualcomm.com>
^ permalink raw reply
* [PATCH v9 2/3] dmaengine: amlogic: Add general DMA driver for A9
From: Xianwei Zhao via B4 Relay @ 2026-06-26 5:39 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kees Cook, Gustavo A. R. Silva, Frank Li
Cc: linux-amlogic, dmaengine, devicetree, linux-kernel,
linux-hardening, Xianwei Zhao, Frank Li
In-Reply-To: <20260626-amlogic-dma-v9-0-558d672c4a95@amlogic.com>
From: Xianwei Zhao <xianwei.zhao@amlogic.com>
Amlogic A9 SoCs include a general-purpose DMA controller that can be used
by multiple peripherals, such as I2C PIO and I3C. Each peripheral group
is associated with a dedicated DMA channel in hardware.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
---
drivers/dma/Kconfig | 10 +
drivers/dma/Makefile | 1 +
drivers/dma/amlogic-dma.c | 708 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 719 insertions(+)
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index ae6a682c9f76..01f96a8257e5 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -85,6 +85,16 @@ config AMCC_PPC440SPE_ADMA
help
Enable support for the AMCC PPC440SPe RAID engines.
+config AMLOGIC_DMA
+ tristate "Amlogic general DMA support"
+ depends on ARCH_MESON || COMPILE_TEST
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ select REGMAP_MMIO
+ help
+ Enable support for the Amlogic general DMA engines. THis DMA
+ controller is used some Amlogic SoCs, such as A9.
+
config APPLE_ADMAC
tristate "Apple ADMAC support"
depends on ARCH_APPLE || COMPILE_TEST
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 14aa086629d5..f62d12b08e15 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_DMATEST) += dmatest.o
obj-$(CONFIG_ALTERA_MSGDMA) += altera-msgdma.o
obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
+obj-$(CONFIG_AMLOGIC_DMA) += amlogic-dma.o
obj-$(CONFIG_APPLE_ADMAC) += apple-admac.o
obj-$(CONFIG_ARM_DMA350) += arm-dma350.o
obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
diff --git a/drivers/dma/amlogic-dma.c b/drivers/dma/amlogic-dma.c
new file mode 100644
index 000000000000..bce6932f3a12
--- /dev/null
+++ b/drivers/dma/amlogic-dma.c
@@ -0,0 +1,708 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ * Author: Xianwei Zhao <xianwei.zhao@amlogic.com>
+ */
+
+#include <dt-bindings/dma/amlogic,a9-dma.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "virt-dma.h"
+
+#define RCH_REG_BASE 0x0
+#define WCH_REG_BASE 0x2000
+/*
+ * Each rch (read from memory) REG offset Rch_offset 0x0 each channel total 0x40
+ * rch addr = DMA_base + Rch_offset+ chan_id * 0x40 + reg_offset
+ */
+#define RCH_READY 0x0
+#define RCH_STATUS 0x4
+#define RCH_CFG 0x8
+#define CFG_CLEAR BIT(25)
+#define CFG_PAUSE BIT(26)
+#define CFG_ENABLE BIT(27)
+#define CFG_DONE BIT(28)
+#define RCH_ADDR 0xc
+#define RCH_LEN 0x10
+#define RCH_RD_LEN 0x14
+#define RCH_PRT 0x18
+#define RCH_SYCN_STAT 0x1c
+#define RCH_ADDR_LOW 0x20
+#define RCH_ADDR_HIGH 0x24
+/* if work on 64, it work with RCH_PRT */
+#define RCH_PTR_HIGH 0x28
+
+/*
+ * Each wch (write to memory) REG offset Wch_offset 0x2000 each channel total 0x40
+ * wch addr = DMA_base + Wch_offset+ chan_id * 0x40 + reg_offset
+ */
+#define WCH_READY 0x0
+#define WCH_TOTAL_LEN 0x4
+#define WCH_CFG 0x8
+#define WCH_ADDR 0xc
+#define WCH_LEN 0x10
+#define WCH_RD_LEN 0x14
+#define WCH_PRT 0x18
+#define WCH_CMD_CNT 0x1c
+#define WCH_ADDR_LOW 0x20
+#define WCH_ADDR_HIGH 0x24
+/* if work on 64, it work with RCH_PRT */
+#define WCH_PTR_HIGH 0x28
+
+/* DMA controller reg */
+#define RCH_INT_MASK 0x1000
+#define WCH_INT_MASK 0x1004
+#define CLEAR_W_BATCH 0x1014
+#define CLEAR_RCH 0x1024
+#define CLEAR_WCH 0x1028
+#define RCH_ACTIVE 0x1038
+#define WCH_ACTIVE 0x103c
+#define RCH_DONE 0x104c
+#define WCH_DONE 0x1050
+#define RCH_ERR 0x1060
+#define RCH_LEN_ERR 0x1064
+#define WCH_ERR 0x1068
+#define DMA_BATCH_END 0x1078
+#define WCH_EOC_DONE 0x1088
+#define WDMA_RESP_ERR 0x1098
+#define UPT_PKT_SYNC 0x10a8
+#define RCHN_CFG 0x10ac
+#define WCHN_CFG 0x10b0
+#define MEM_PD_CFG 0x10b4
+#define MEM_BUS_CFG 0x10b8
+#define DMA_GMV_CFG 0x10bc
+#define DMA_GMR_CFG 0x10c0
+
+#define MAX_CHAN_ID 32
+#define SG_MAX_LEN GENMASK(26, 0)
+
+struct aml_dma_sg_link {
+#define LINK_LEN GENMASK(26, 0)
+#define LINK_IRQ BIT(27)
+#define LINK_EOC BIT(28)
+#define LINK_LOOP BIT(29)
+#define LINK_ERR BIT(30)
+#define LINK_OWNER BIT(31)
+ u32 ctl;
+ u32 addr_low;
+ u32 addr_high;
+ u32 revered;
+} __packed;
+
+/* 1 page for link 256*16 */
+#define DMA_MAX_LINK 256
+/* sizeof(struct aml_dma_sg_link) */
+#define DMA_LINK_SIZE 16
+#define DMA_LINK_MAX_SIZE (DMA_LINK_SIZE * DMA_MAX_LINK)
+
+struct aml_dma_desc {
+ struct virt_dma_desc vd;
+ struct aml_dma_sg_link *sg_link;
+ struct dma_device *dma_device;
+ dma_addr_t sg_link_phys;
+ size_t sg_link_size;
+ u32 data_len;
+};
+
+struct aml_dma_chan {
+ struct virt_dma_chan vchan;
+ struct aml_dma_dev *aml_dma;
+ struct aml_dma_desc *cur_desc;
+ enum dma_status pre_status;
+ enum dma_status status;
+ enum dma_transfer_direction direction;
+ int chan_id;
+ /* reg_base (direction + chan_id) */
+ int reg_offs;
+ /* When there are multiple consecutive transmission errors, this chanel halt */
+ int err_num;
+};
+
+struct aml_dma_dev {
+ struct dma_device dma_device;
+ void __iomem *base;
+ struct regmap *regmap;
+ struct clk *clk;
+ int irq;
+ struct platform_device *pdev;
+ struct aml_dma_chan *aml_rch[MAX_CHAN_ID];
+ struct aml_dma_chan *aml_wch[MAX_CHAN_ID];
+ unsigned int chan_nr;
+ unsigned int chan_used;
+ struct aml_dma_chan aml_chans[]__counted_by(chan_nr);
+};
+
+static inline struct aml_dma_chan *to_aml_dma_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct aml_dma_chan, vchan.chan);
+}
+
+static inline struct aml_dma_desc *to_aml_dma_desc(struct virt_dma_desc *vd)
+{
+ return container_of(vd, struct aml_dma_desc, vd);
+}
+
+static void aml_dma_free_desc(struct virt_dma_desc *vd)
+{
+ struct aml_dma_desc *aml_desc = to_aml_dma_desc(vd);
+
+ dma_free_coherent(aml_desc->dma_device->dev,
+ aml_desc->sg_link_size,
+ aml_desc->sg_link,
+ aml_desc->sg_link_phys);
+ kfree(aml_desc);
+}
+
+static int aml_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
+ struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
+
+ /* offset is the same RCH_CFG and WCH_CFG */
+ regmap_set_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR);
+ regmap_clear_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE);
+ regmap_clear_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR);
+
+ aml_chan->status = DMA_COMPLETE;
+ aml_chan->cur_desc = NULL;
+ aml_chan->err_num = 0;
+
+ return 0;
+}
+
+static void aml_dma_free_chan_resources(struct dma_chan *chan)
+{
+ struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
+ struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
+ struct virt_dma_desc *cur_vd = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&aml_chan->vchan.lock, flags);
+ regmap_set_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE);
+ regmap_set_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR);
+ if (aml_chan->cur_desc)
+ cur_vd = &aml_chan->cur_desc->vd;
+ aml_chan->cur_desc = NULL;
+ spin_unlock_irqrestore(&aml_chan->vchan.lock, flags);
+ if (cur_vd)
+ aml_dma_free_desc(cur_vd);
+
+ vchan_free_chan_resources(&aml_chan->vchan);
+}
+
+/* DMA transfer state update how many data reside it */
+static enum dma_status aml_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
+ struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
+ struct aml_dma_desc *aml_desc = NULL;
+ struct virt_dma_desc *vd;
+ u32 residue = 0, done;
+ unsigned long flags;
+ enum dma_status ret;
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret == DMA_COMPLETE || !txstate)
+ return ret;
+
+ spin_lock_irqsave(&aml_chan->vchan.lock, flags);
+ vd = vchan_find_desc(&aml_chan->vchan, cookie);
+ if (vd) {
+ aml_desc = to_aml_dma_desc(vd);
+ residue = aml_desc->data_len;
+ } else if (aml_chan->cur_desc && aml_chan->cur_desc->vd.tx.cookie == cookie) {
+ aml_desc = aml_chan->cur_desc;
+ regmap_read(aml_dma->regmap, aml_chan->reg_offs + RCH_RD_LEN, &done);
+ residue = aml_desc->data_len - done;
+ } else {
+ dev_err(aml_dma->dma_device.dev, "cookie error\n");
+ }
+ spin_unlock_irqrestore(&aml_chan->vchan.lock, flags);
+
+ dma_set_residue(txstate, residue);
+
+ return ret;
+}
+
+static struct dma_async_tx_descriptor *aml_dma_prep_slave_sg
+ (struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
+ struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
+ struct aml_dma_desc *aml_desc = NULL;
+ struct aml_dma_sg_link *sg_link = NULL;
+ struct scatterlist *sg = NULL;
+ u64 paddr;
+ u32 link_count, avail;
+ u32 i;
+
+ if (aml_chan->direction != direction) {
+ dev_err(aml_dma->dma_device.dev, "direction not support\n");
+ return NULL;
+ }
+
+ link_count = sg_nents_for_dma(sgl, sg_len, SG_MAX_LEN);
+ if (link_count == 0)
+ return NULL;
+
+ aml_desc = kzalloc_obj(*aml_desc, GFP_NOWAIT);
+ if (!aml_desc)
+ return NULL;
+ aml_desc->sg_link_size = link_count * sizeof(*sg_link);
+ aml_desc->sg_link = dma_alloc_coherent(aml_dma->dma_device.dev, aml_desc->sg_link_size,
+ &aml_desc->sg_link_phys, GFP_NOWAIT);
+ if (!aml_desc->sg_link) {
+ kfree(aml_desc);
+ return NULL;
+ }
+ aml_desc->dma_device = &aml_dma->dma_device;
+
+ sg_link = aml_desc->sg_link;
+ for_each_sg(sgl, sg, sg_len, i) {
+ avail = sg_dma_len(sg);
+ paddr = sg->dma_address;
+ while (avail > SG_MAX_LEN) {
+ /* set dma address and len to sglink*/
+ sg_link->addr_low = lower_32_bits(paddr);
+ sg_link->addr_high = upper_32_bits(paddr);
+ sg_link->ctl = FIELD_PREP(LINK_LEN, SG_MAX_LEN);
+ paddr = paddr + SG_MAX_LEN;
+ avail = avail - SG_MAX_LEN;
+ sg_link++;
+ }
+ /* set dma address and len to sglink*/
+ sg_link->addr_low = lower_32_bits(paddr);
+ sg_link->addr_high = upper_32_bits(paddr);
+ sg_link->ctl = FIELD_PREP(LINK_LEN, avail);
+
+ aml_desc->data_len += sg_dma_len(sg);
+ sg_link++;
+ }
+
+ /* the last sg set eoc flag */
+ sg_link--;
+ sg_link->ctl |= LINK_EOC;
+
+ return vchan_tx_prep(&aml_chan->vchan, &aml_desc->vd, flags);
+}
+
+static int aml_dma_chan_pause(struct dma_chan *chan)
+{
+ struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
+ struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
+ unsigned long flags;
+
+ spin_lock_irqsave(&aml_chan->vchan.lock, flags);
+ regmap_set_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE);
+ aml_chan->pre_status = aml_chan->status;
+ aml_chan->status = DMA_PAUSED;
+ spin_unlock_irqrestore(&aml_chan->vchan.lock, flags);
+
+ return 0;
+}
+
+static int aml_dma_chan_resume(struct dma_chan *chan)
+{
+ struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
+ struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
+ unsigned long flags;
+
+ spin_lock_irqsave(&aml_chan->vchan.lock, flags);
+ regmap_clear_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE);
+ aml_chan->status = aml_chan->pre_status;
+ spin_unlock_irqrestore(&aml_chan->vchan.lock, flags);
+
+ return 0;
+}
+
+static int aml_dma_terminate_all(struct dma_chan *chan)
+{
+ struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
+ struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
+ int chan_id = aml_chan->chan_id;
+ struct virt_dma_desc *cur_vd;
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&aml_chan->vchan.lock, flags);
+ regmap_set_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE);
+ regmap_set_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR);
+
+ if (aml_chan->direction == DMA_MEM_TO_DEV)
+ regmap_set_bits(aml_dma->regmap, RCH_INT_MASK, BIT(chan_id));
+ else if (aml_chan->direction == DMA_DEV_TO_MEM)
+ regmap_set_bits(aml_dma->regmap, WCH_INT_MASK, BIT(chan_id));
+
+ vchan_get_all_descriptors(&aml_chan->vchan, &head);
+ cur_vd = &aml_chan->cur_desc->vd;
+ aml_chan->cur_desc = NULL;
+ spin_unlock_irqrestore(&aml_chan->vchan.lock, flags);
+ if (cur_vd)
+ aml_dma_free_desc(cur_vd);
+
+ vchan_dma_desc_free_list(&aml_chan->vchan, &head);
+
+ regmap_clear_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE);
+ regmap_clear_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR);
+
+ return 0;
+}
+
+static void aml_dma_start(struct aml_dma_chan *aml_chan)
+{
+ struct virt_dma_desc *vd = vchan_next_desc(&aml_chan->vchan);
+ struct aml_dma_dev *aml_dma = aml_chan->aml_dma;
+ struct aml_dma_desc *aml_desc = NULL;
+ int chan_id = aml_chan->chan_id;
+
+ if (aml_chan->status == DMA_ERROR) {
+ if (aml_chan->err_num > 5) {
+ dev_err(aml_dma->dma_device.dev, "hw error\n");
+ return;
+ }
+ regmap_set_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE);
+ regmap_set_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR);
+ aml_chan->err_num++;
+ regmap_clear_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE);
+ regmap_clear_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR);
+ aml_chan->status = DMA_COMPLETE;
+ } else {
+ aml_chan->err_num = 0;
+ }
+
+ if (!vd)
+ return;
+
+ if (aml_chan->status != DMA_COMPLETE)
+ return;
+
+ list_del(&vd->node);
+ aml_desc = to_aml_dma_desc(vd);
+ aml_chan->cur_desc = aml_desc;
+
+ if (aml_chan->direction == DMA_MEM_TO_DEV) {
+ regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_ADDR,
+ lower_32_bits(aml_desc->sg_link_phys));
+ regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_ADDR_HIGH,
+ upper_32_bits(aml_desc->sg_link_phys));
+ regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_LEN, aml_desc->data_len);
+ regmap_clear_bits(aml_dma->regmap, RCH_INT_MASK, BIT(chan_id));
+ /* for rch (tx) need set cfg 0 to trigger start */
+ regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, 0);
+ } else if (aml_chan->direction == DMA_DEV_TO_MEM) {
+ regmap_write(aml_dma->regmap, aml_chan->reg_offs + WCH_ADDR,
+ lower_32_bits(aml_desc->sg_link_phys));
+ regmap_write(aml_dma->regmap, aml_chan->reg_offs + WCH_ADDR_HIGH,
+ upper_32_bits(aml_desc->sg_link_phys));
+ regmap_write(aml_dma->regmap, aml_chan->reg_offs + WCH_LEN, aml_desc->data_len);
+ regmap_clear_bits(aml_dma->regmap, WCH_INT_MASK, BIT(chan_id));
+ }
+}
+
+static void aml_dma_issue_pending(struct dma_chan *chan)
+{
+ struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&aml_chan->vchan.lock, flags);
+ if (vchan_issue_pending(&aml_chan->vchan) && !aml_chan->cur_desc)
+ aml_dma_start(aml_chan);
+ spin_unlock_irqrestore(&aml_chan->vchan.lock, flags);
+}
+
+static irqreturn_t aml_dma_interrupt_handler(int irq, void *dev_id)
+{
+ struct aml_dma_dev *aml_dma = dev_id;
+ struct aml_dma_chan *aml_chan;
+ struct aml_dma_desc *aml_desc;
+ u32 done, eoc_done, err, err_l, end;
+ u32 cpl_data;
+ int i = 0;
+
+ /* deal with rch normal complete and error */
+ regmap_read(aml_dma->regmap, RCH_DONE, &done);
+ regmap_read(aml_dma->regmap, RCH_ERR, &err);
+ regmap_read(aml_dma->regmap, RCH_LEN_ERR, &err_l);
+ err = err | err_l;
+
+ done = done | err;
+
+ while (done) {
+ i = ffs(done) - 1;
+ regmap_write(aml_dma->regmap, CLEAR_RCH, BIT(i));
+ done &= ~BIT(i);
+ aml_chan = aml_dma->aml_rch[i];
+ if (!aml_chan) {
+ dev_err(aml_dma->dma_device.dev, "idx %d rch not initialized\n", i);
+ continue;
+ }
+ spin_lock(&aml_chan->vchan.lock);
+ aml_chan->status = (err & BIT(i)) ? DMA_ERROR : DMA_COMPLETE;
+ aml_desc = aml_chan->cur_desc;
+ if (!aml_desc) {
+ spin_unlock(&aml_chan->vchan.lock);
+ continue;
+ }
+ if (aml_chan->status == DMA_ERROR) {
+ aml_desc->vd.tx_result.result = DMA_TRANS_READ_FAILED;
+ regmap_read(aml_dma->regmap, aml_chan->reg_offs + RCH_RD_LEN, &cpl_data);
+ aml_desc->vd.tx_result.residue = aml_desc->data_len - cpl_data;
+ }
+ vchan_cookie_complete(&aml_desc->vd);
+ aml_chan->cur_desc = NULL;
+ aml_dma_start(aml_chan);
+ spin_unlock(&aml_chan->vchan.lock);
+ }
+
+ /* deal with wch normal complete and error */
+ regmap_read(aml_dma->regmap, DMA_BATCH_END, &end);
+ if (end)
+ regmap_write(aml_dma->regmap, CLEAR_W_BATCH, end);
+
+ regmap_read(aml_dma->regmap, WCH_DONE, &done);
+ regmap_read(aml_dma->regmap, WCH_EOC_DONE, &eoc_done);
+ done = done | eoc_done;
+
+ regmap_read(aml_dma->regmap, WCH_ERR, &err);
+ regmap_read(aml_dma->regmap, WDMA_RESP_ERR, &err_l);
+ err = err | err_l;
+
+ done = done | err;
+ i = 0;
+ while (done) {
+ i = ffs(done) - 1;
+ done &= ~BIT(i);
+ regmap_write(aml_dma->regmap, CLEAR_WCH, BIT(i));
+ aml_chan = aml_dma->aml_wch[i];
+ if (!aml_chan) {
+ dev_err(aml_dma->dma_device.dev, "idx %d wch not initialized\n", i);
+ continue;
+ }
+ spin_lock(&aml_chan->vchan.lock);
+ aml_chan->status = (err & BIT(i)) ? DMA_ERROR : DMA_COMPLETE;
+ aml_desc = aml_chan->cur_desc;
+ if (!aml_desc) {
+ spin_unlock(&aml_chan->vchan.lock);
+ continue;
+ }
+ if (aml_chan->status == DMA_ERROR) {
+ aml_desc->vd.tx_result.result = DMA_TRANS_WRITE_FAILED;
+ regmap_read(aml_dma->regmap, aml_chan->reg_offs + RCH_RD_LEN, &cpl_data);
+ aml_desc->vd.tx_result.residue = aml_desc->data_len - cpl_data;
+ }
+ vchan_cookie_complete(&aml_desc->vd);
+ aml_chan->cur_desc = NULL;
+ aml_dma_start(aml_chan);
+ spin_unlock(&aml_chan->vchan.lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct dma_chan *aml_of_dma_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma)
+{
+ struct aml_dma_dev *aml_dma = (struct aml_dma_dev *)ofdma->of_dma_data;
+ struct aml_dma_chan *aml_chan = NULL;
+ u32 type;
+ u32 phy_chan_id;
+
+ if (dma_spec->args_count != 2)
+ return NULL;
+
+ type = dma_spec->args[0];
+ phy_chan_id = dma_spec->args[1];
+
+ if (phy_chan_id >= MAX_CHAN_ID)
+ return NULL;
+
+ if (type == DMA_TX) {
+ aml_chan = aml_dma->aml_rch[phy_chan_id];
+ if (!aml_chan) {
+ if (aml_dma->chan_used >= aml_dma->chan_nr) {
+ dev_err(aml_dma->dma_device.dev, "some dma clients err used\n");
+ return NULL;
+ }
+ aml_chan = &aml_dma->aml_chans[aml_dma->chan_used];
+ aml_dma->chan_used++;
+ aml_chan->direction = DMA_MEM_TO_DEV;
+ aml_chan->chan_id = phy_chan_id;
+ aml_chan->reg_offs = RCH_REG_BASE + 0x40 * aml_chan->chan_id;
+ aml_dma->aml_rch[phy_chan_id] = aml_chan;
+ }
+ } else if (type == DMA_RX) {
+ aml_chan = aml_dma->aml_wch[phy_chan_id];
+ if (!aml_chan) {
+ if (aml_dma->chan_used >= aml_dma->chan_nr) {
+ dev_err(aml_dma->dma_device.dev, "some dma clients err used\n");
+ return NULL;
+ }
+ aml_chan = &aml_dma->aml_chans[aml_dma->chan_used];
+ aml_dma->chan_used++;
+ aml_chan->direction = DMA_DEV_TO_MEM;
+ aml_chan->chan_id = phy_chan_id;
+ aml_chan->reg_offs = WCH_REG_BASE + 0x40 * aml_chan->chan_id;
+ aml_dma->aml_wch[phy_chan_id] = aml_chan;
+ }
+ } else {
+ dev_err(aml_dma->dma_device.dev, "type %d not supported\n", type);
+ return NULL;
+ }
+
+ return dma_get_slave_channel(&aml_chan->vchan.chan);
+}
+
+static int aml_dma_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct dma_device *dma_dev;
+ struct aml_dma_dev *aml_dma;
+ int ret, i, len;
+ u32 chan_nr;
+
+ const struct regmap_config aml_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x3000,
+ };
+
+ ret = of_property_read_u32(np, "dma-channels", &chan_nr);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to read dma-channels\n");
+ if (chan_nr > (MAX_CHAN_ID * 2))
+ return dev_err_probe(&pdev->dev, -EINVAL, "dma-channels unusual\n");
+
+ len = sizeof(struct aml_dma_dev) + sizeof(struct aml_dma_chan) * chan_nr;
+ aml_dma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+ if (!aml_dma)
+ return -ENOMEM;
+
+ aml_dma->chan_nr = chan_nr;
+
+ aml_dma->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(aml_dma->base))
+ return PTR_ERR(aml_dma->base);
+
+ aml_dma->regmap = devm_regmap_init_mmio(&pdev->dev, aml_dma->base,
+ &aml_regmap_config);
+ if (IS_ERR_OR_NULL(aml_dma->regmap))
+ return PTR_ERR(aml_dma->regmap);
+
+ aml_dma->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(aml_dma->clk))
+ return PTR_ERR(aml_dma->clk);
+
+ aml_dma->irq = platform_get_irq(pdev, 0);
+
+ aml_dma->pdev = pdev;
+ aml_dma->dma_device.dev = &pdev->dev;
+
+ dma_dev = &aml_dma->dma_device;
+ INIT_LIST_HEAD(&dma_dev->channels);
+
+ /* Initialize channel parameters */
+ for (i = 0; i < chan_nr; i++) {
+ struct aml_dma_chan *aml_chan = &aml_dma->aml_chans[i];
+
+ aml_chan->aml_dma = aml_dma;
+ aml_chan->vchan.desc_free = aml_dma_free_desc;
+ vchan_init(&aml_chan->vchan, &aml_dma->dma_device);
+ }
+ aml_dma->chan_used = 0;
+
+ dma_set_max_seg_size(dma_dev->dev, SG_MAX_LEN);
+ dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
+ dma_dev->device_alloc_chan_resources = aml_dma_alloc_chan_resources;
+ dma_dev->device_free_chan_resources = aml_dma_free_chan_resources;
+ dma_dev->device_tx_status = aml_dma_tx_status;
+ dma_dev->device_prep_slave_sg = aml_dma_prep_slave_sg;
+ dma_dev->device_pause = aml_dma_chan_pause;
+ dma_dev->device_resume = aml_dma_chan_resume;
+ dma_dev->device_terminate_all = aml_dma_terminate_all;
+ dma_dev->device_issue_pending = aml_dma_issue_pending;
+ /* PIO 4 bytes and I2C 1 byte */
+ dma_dev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
+ dma_dev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+
+ regmap_write(aml_dma->regmap, RCH_INT_MASK, 0xffffffff);
+ regmap_write(aml_dma->regmap, WCH_INT_MASK, 0xffffffff);
+
+ ret = devm_request_irq(&pdev->dev, aml_dma->irq, aml_dma_interrupt_handler,
+ 0, dev_name(&pdev->dev), aml_dma);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to register irq\n");
+
+ ret = dmaenginem_async_device_register(dma_dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to register dmaenginem\n");
+
+ ret = of_dma_controller_register(np, aml_of_dma_xlate, aml_dma);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to register xlate\n");
+
+ platform_set_drvdata(pdev, aml_dma);
+
+ return 0;
+}
+
+static void aml_dma_remove(struct platform_device *pdev)
+{
+ struct aml_dma_dev *aml_dma = platform_get_drvdata(pdev);
+ struct aml_dma_chan *aml_chan = NULL;
+ int i;
+
+ of_dma_controller_free((&pdev->dev)->of_node);
+
+ regmap_write(aml_dma->regmap, RCH_INT_MASK, 0xffffffff);
+ regmap_write(aml_dma->regmap, WCH_INT_MASK, 0xffffffff);
+
+ for (i = 0; i < MAX_CHAN_ID; i++) {
+ aml_chan = aml_dma->aml_rch[i];
+ if (aml_chan)
+ tasklet_kill(&aml_chan->vchan.task);
+ aml_chan = aml_dma->aml_wch[i];
+ if (aml_chan)
+ tasklet_kill(&aml_chan->vchan.task);
+ }
+}
+
+static const struct of_device_id aml_dma_ids[] = {
+ { .compatible = "amlogic,a9-dma", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aml_dma_ids);
+
+static struct platform_driver aml_dma_driver = {
+ .probe = aml_dma_probe,
+ .remove = aml_dma_remove,
+ .driver = {
+ .name = "aml-dma",
+ .of_match_table = aml_dma_ids,
+ },
+};
+
+module_platform_driver(aml_dma_driver);
+
+MODULE_DESCRIPTION("GENERAL DMA driver for Amlogic");
+MODULE_AUTHOR("Xianwei Zhao <xianwei.zhao@amlogic.com>");
+MODULE_LICENSE("GPL");
--
2.52.0
^ permalink raw reply related
* [PATCH v9 3/3] MAINTAINERS: Add an entry for Amlogic DMA driver
From: Xianwei Zhao via B4 Relay @ 2026-06-26 5:39 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kees Cook, Gustavo A. R. Silva, Frank Li
Cc: linux-amlogic, dmaengine, devicetree, linux-kernel,
linux-hardening, Xianwei Zhao
In-Reply-To: <20260626-amlogic-dma-v9-0-558d672c4a95@amlogic.com>
From: Xianwei Zhao <xianwei.zhao@amlogic.com>
Add Amlogic DMA controller entry to MAINTAINERS to clarify
the maintainers.
Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 3e7b2d9e9c24..b4ef8d3f52cb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1307,6 +1307,13 @@ F: Documentation/devicetree/bindings/perf/amlogic,g12-ddr-pmu.yaml
F: drivers/perf/amlogic/
F: include/soc/amlogic/
+AMLOGIC DMA DRIVER
+M: Xianwei Zhao <xianwei.zhao@amlogic.com>
+L: linux-amlogic@lists.infradead.org
+S: Maintained
+F: Documentation/devicetree/bindings/dma/amlogic,a9-dma.yaml
+F: drivers/dma/amlogic-dma.c
+
AMLOGIC ISP DRIVER
M: Keke Li <keke.li@amlogic.com>
L: linux-media@vger.kernel.org
--
2.52.0
^ permalink raw reply related
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