* Re: [PATCH v7 3/4] iio: adc: ltc2378: Enable high-speed data capture
From: sashiko-bot @ 2026-07-16 21:24 UTC (permalink / raw)
To: Marcelo Schmitt; +Cc: robh, conor+dt, devicetree
In-Reply-To: <564b975c2c62d28ee6733f86bd5632c41a7c80e4.1784235595.git.marcelo.schmitt@analog.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Integer divide-by-zero leading to kernel panic in `ltc2378_update_conversion_rate` when a sampling frequency of 1 Hz is requested and the PWM driver rounds the period length above 1 second.
--
--- Patch [3]: [PATCH v7 3/4] iio: adc: ltc2378: Enable high-speed data capture ---
commit ef54f2c79f2cac1649922032814adc7fcc5f9684
Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
Subject: iio: adc: ltc2378: Enable high-speed data capture
This commit enables high-speed data capture for the ltc2378 ADC driver by
utilizing SPI transfer offloading. It allows data acquisition at faster sample
rates up to 2 MSPS.
> diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c
> index 5600c820c7f45..7d29f021cb7b4 100644
> --- a/drivers/iio/adc/ltc2378.c
> +++ b/drivers/iio/adc/ltc2378.c
[ ... ]
> + /*
> + * Configure SPI offload PWM trigger.
> + * The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL.
> + * Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns)
> + *
> + * Use the same period as CNV PWM to avoid timing issues.
> + * Convert back from period to frequency for the SPI offload API.
> + */
> + offload_period_ns = cnv_wf.period_length_ns;
> + config.periodic.frequency_hz = div_u64(HZ_PER_GHZ, offload_period_ns);
> + min_read_offset = LTC2378_TBUSYLH_NS + st->info->tconv_ns + LTC2378_TDSDOBUSYL_NS;
> + offload_offset_ns = min_read_offset;
> + count = 100;
> + do {
> + config.periodic.offset_ns = offload_offset_ns;
> + ret = spi_offload_trigger_validate(st->offload_trigger, &config);
> + if (ret)
> + return ret;
> + offload_offset_ns += 10;
> + } while (count-- && config.periodic.offset_ns < min_read_offset);
> +
> + /* Check the minimum CNV to SCLK delay is met */
> + if (config.periodic.offset_ns < min_read_offset)
> + return -EDOM;
> +
> + /* Check the PWM periods remain the same */
> + offload_period_ns = div64_u64(HZ_PER_GHZ, config.periodic.frequency_hz);
[Severity: High]
Could this result in a kernel panic due to an integer division by zero?
If a user writes 1 to the IIO in_voltage_sampling_frequency sysfs attribute,
ltc2378_update_conversion_rate() is called with freq_Hz = 1, and the initial
requested PWM period is calculated as 1,000,000,000 ns.
If the underlying hardware PWM driver rounds the period slightly upwards
(e.g., to 1,000,000,001 ns) during pwm_round_waveform_might_sleep() due to
clock granularity, offload_period_ns will exceed HZ_PER_GHZ.
This causes config.periodic.frequency_hz = div_u64(HZ_PER_GHZ, offload_period_ns)
to evaluate to 0.
When div64_u64(HZ_PER_GHZ, config.periodic.frequency_hz) is subsequently called,
it divides by zero.
> + if (cnv_wf.period_length_ns != offload_period_ns)
> + return -EDOM;
> +
> + st->offload_trigger_config = config;
> + st->cnv_wf = cnv_wf;
> + st->cnv_Hz = DIV_ROUND_CLOSEST_ULL(HZ_PER_GHZ, cnv_wf.period_length_ns);
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1784235595.git.marcelo.schmitt@analog.com?part=3
^ permalink raw reply
* Re: [PATCH v2] dt-bindings: nvmem: qfprom: Add ipq5210 & ipq9650 compatible
From: Srinivas Kandagatla @ 2026-07-16 21:24 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Varadarajan Narayanan
Cc: linux-arm-msm, devicetree, linux-kernel, Krzysztof Kozlowski
In-Reply-To: <20260706-qfprom-v2-1-f5d9eaf12d55@oss.qualcomm.com>
On Mon, 06 Jul 2026 11:59:38 +0530, Varadarajan Narayanan wrote:
> Document compatible string for the QFPROM on ipq5210 & ipq9650 platforms.
>
>
Applied, thanks!
[1/1] dt-bindings: nvmem: qfprom: Add ipq5210 & ipq9650 compatible
commit: 2a65e177f219f8abefd5eeca6a348963840ed67d
Best regards,
--
Srinivas Kandagatla <srini@kernel.org>
^ permalink raw reply
* [PATCH] regulator: dt-bindings: tps51632: Convert to DT schema
From: Bhargav Joshi @ 2026-07-16 21:24 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Laxman Dewangan
Cc: linux-kernel, devicetree, goledhruva, m-chawdhry, daniel.baluta,
simona.toaca, j.bhargav.u
Convert Texas Instruments TPS51632 Voltage regulators from legacy text
to DT schema. No functional changes are introduced.
Signed-off-by: Bhargav Joshi <j.bhargav.u@gmail.com>
---
.../devicetree/bindings/regulator/ti,tps51632.yaml | 55 ++++++++++++++++++++++
.../bindings/regulator/tps51632-regulator.txt | 27 -----------
2 files changed, 55 insertions(+), 27 deletions(-)
diff --git a/Documentation/devicetree/bindings/regulator/ti,tps51632.yaml b/Documentation/devicetree/bindings/regulator/ti,tps51632.yaml
new file mode 100644
index 000000000000..67ac40e3ec00
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/ti,tps51632.yaml
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/ti,tps51632.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TPS51632 Voltage regulators
+
+maintainers:
+ - Laxman Dewangan <ldewangan@nvidia.com>
+
+allOf:
+ - $ref: regulator.yaml#
+
+properties:
+ compatible:
+ const: ti,tps51632
+
+ reg:
+ maxItems: 1
+
+ ti,enable-pwm-dvfs:
+ description: Enable the DVFS voltage control through the PWM interface.
+ type: boolean
+
+ ti,dvfs-step-20mV:
+ description:
+ The 20mV step voltage when PWM DVFS enabled. Missing this will set 10mV
+ step voltage in PWM DVFS mode. In normal mode, the voltage step is 10mV
+ as per datasheet.
+ type: boolean
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ tps51632@43 {
+ compatible = "ti,tps51632";
+ reg = <0x43>;
+ regulator-name = "tps51632-vout";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-boot-on;
+ ti,enable-pwm-dvfs;
+ ti,dvfs-step-20mV;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/regulator/tps51632-regulator.txt b/Documentation/devicetree/bindings/regulator/tps51632-regulator.txt
deleted file mode 100644
index 2f7e44a96414..000000000000
--- a/Documentation/devicetree/bindings/regulator/tps51632-regulator.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-TPS51632 Voltage regulators
-
-Required properties:
-- compatible: Must be "ti,tps51632"
-- reg: I2C slave address
-
-Optional properties:
-- ti,enable-pwm-dvfs: Enable the DVFS voltage control through the PWM interface.
-- ti,dvfs-step-20mV: The 20mV step voltage when PWM DVFS enabled. Missing this
- will set 10mV step voltage in PWM DVFS mode. In normal mode, the voltage
- step is 10mV as per datasheet.
-
-Any property defined as part of the core regulator binding, defined in
-regulator.txt, can also be used.
-
-Example:
-
- tps51632 {
- compatible = "ti,tps51632";
- reg = <0x43>;
- regulator-name = "tps51632-vout";
- regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <1500000>;
- regulator-boot-on;
- ti,enable-pwm-dvfs;
- ti,dvfs-step-20mV;
- };
---
base-commit: 962528fef90253aeded29cee20a9b6ff3595fed4
change-id: 20260626-ti-regulator-tp-f641a9f12c30
Best regards,
--
Bhargav
^ permalink raw reply related
* [PATCH 4/4] hwmon: (adt7470) Add thermal zone sensor support
From: Luiz Angelo Daros de Luca @ 2026-07-16 21:21 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
de Luca
Cc: linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20260716-adt7470_thermalzone-v1-0-cc55ef35edde@gmail.com>
Expose the ADT7470 external temperature sensors to the thermal framework
via Device Tree. The thermal callbacks use the driver's cached sensor data,
avoiding any I2C bus traffic during frequent polling by the thermal core.
Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
---
drivers/hwmon/adt7470.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index b865f4bd50f8..44fe310f0650 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -150,6 +150,11 @@ struct adt7470_cooling_device {
int pwm_index;
};
+struct adt7470_thermal_sensor {
+ struct adt7470_data *data;
+ int id;
+};
+
struct adt7470_data {
struct regmap *regmap;
struct mutex lock;
@@ -181,6 +186,7 @@ struct adt7470_data {
unsigned int auto_update_interval;
struct adt7470_cooling_device cooling_devices[ADT7470_PWM_COUNT];
+ struct adt7470_thermal_sensor thermal_sensors[ADT7470_TEMP_COUNT];
};
/*
@@ -957,6 +963,47 @@ static int adt7470_register_cooling_devices(struct device *dev,
return 0;
}
+static int adt7470_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ struct adt7470_thermal_sensor *sensor = thermal_zone_device_priv(tz);
+ struct adt7470_data *data = sensor->data;
+
+ mutex_lock(&data->lock);
+ *temp = 1000 * data->temp[sensor->id];
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
+static const struct thermal_zone_device_ops adt7470_thermal_ops = {
+ .get_temp = adt7470_get_temp,
+};
+
+static int adt7470_register_thermal_sensors(struct device *dev,
+ struct adt7470_data *data)
+{
+ int i;
+
+ for (i = 0; i < ADT7470_TEMP_COUNT; i++) {
+ struct thermal_zone_device *tz;
+
+ data->thermal_sensors[i].data = data;
+ data->thermal_sensors[i].id = i;
+
+ tz = devm_thermal_of_zone_register(dev, i, &data->thermal_sensors[i],
+ &adt7470_thermal_ops);
+ if (IS_ERR(tz)) {
+ if (PTR_ERR(tz) == -ENODEV)
+ continue;
+ return dev_warn_probe(dev, PTR_ERR(tz),
+ "failed to register thermal zone %d\n",
+ i);
+ }
+ }
+
+ return 0;
+}
+
static ssize_t pwm_max_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
@@ -1401,6 +1448,10 @@ static int adt7470_probe(struct i2c_client *client)
err = adt7470_register_cooling_devices(dev, data);
if (err)
return err;
+
+ err = adt7470_register_thermal_sensors(dev, data);
+ if (err)
+ return err;
}
data->auto_update = kthread_run(adt7470_update_thread, client, "%s",
--
2.55.0
^ permalink raw reply related
* [PATCH 3/4] hwmon: (adt7470) Add thermal cooling device support
From: Luiz Angelo Daros de Luca @ 2026-07-16 21:21 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
de Luca
Cc: linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20260716-adt7470_thermalzone-v1-0-cc55ef35edde@gmail.com>
The ADT7470 has four PWM outputs that can be used to control fans.
Register each PWM output as an optional thermal cooling device via
Device Tree so it can be referenced by thermal zones.
When the thermal subsystem sets a cooling state, the driver switches
the corresponding PWM channel to manual mode so that the requested duty
cycle takes effect.
Registration is optional: when the "#cooling-cells" property is absent,
devm_thermal_of_child_cooling_device_register() returns -ENODEV and the
driver silently skips that PWM channel.
The PWM cache is populated with an explicit adt7470_update_sensors()
call before the cooling devices are registered, so that an early
get_cur_state() call from the thermal core does not observe stale
(zeroed) values.
Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
---
drivers/hwmon/adt7470.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+)
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index 4ac292cd7f47..b865f4bd50f8 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -22,6 +22,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/util_macros.h>
+#include <linux/thermal.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
@@ -142,6 +143,13 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_FREQ_MASK 0x70
#define ADT7470_FREQ_SHIFT 4
+struct adt7470_data;
+
+struct adt7470_cooling_device {
+ struct adt7470_data *data;
+ int pwm_index;
+};
+
struct adt7470_data {
struct regmap *regmap;
struct mutex lock;
@@ -171,6 +179,8 @@ struct adt7470_data {
struct task_struct *auto_update;
unsigned int auto_update_interval;
+
+ struct adt7470_cooling_device cooling_devices[ADT7470_PWM_COUNT];
};
/*
@@ -846,6 +856,107 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
return err;
}
+static int adt7470_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = ADT7470_PWM_MAX;
+ return 0;
+}
+
+static int adt7470_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct adt7470_cooling_device *cooling = cdev->devdata;
+ struct adt7470_data *data = cooling->data;
+
+ mutex_lock(&data->lock);
+ *state = data->pwm[cooling->pwm_index];
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
+static int adt7470_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ struct adt7470_cooling_device *cooling = cdev->devdata;
+ struct adt7470_data *data = cooling->data;
+ unsigned int pwm_auto_reg_mask;
+ int err;
+
+ if (cooling->pwm_index & 1)
+ pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK;
+ else
+ pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK;
+
+ state = clamp_val(state, 0, ADT7470_PWM_MAX);
+
+ mutex_lock(&data->lock);
+
+ if (data->pwm[cooling->pwm_index] == state &&
+ data->pwm_automatic[cooling->pwm_index] == 0) {
+ mutex_unlock(&data->lock);
+ return 0;
+ }
+
+ /* Put the PWM channel in manual mode before updating it. */
+ err = regmap_update_bits(data->regmap,
+ ADT7470_REG_PWM_CFG(cooling->pwm_index),
+ pwm_auto_reg_mask, 0);
+ if (err < 0)
+ goto out;
+
+ data->pwm_automatic[cooling->pwm_index] = 0;
+
+ err = regmap_write(data->regmap,
+ ADT7470_REG_PWM(cooling->pwm_index), state);
+
+ if (err < 0)
+ goto out;
+
+ data->pwm[cooling->pwm_index] = state;
+out:
+ mutex_unlock(&data->lock);
+
+ return err;
+}
+
+static const struct thermal_cooling_device_ops adt7470_cooling_ops = {
+ .get_max_state = adt7470_get_max_state,
+ .get_cur_state = adt7470_get_cur_state,
+ .set_cur_state = adt7470_set_cur_state,
+};
+
+static int adt7470_register_cooling_devices(struct device *dev,
+ struct adt7470_data *data)
+{
+ int i;
+
+ for (i = 0; i < ADT7470_PWM_COUNT; i++) {
+ struct thermal_cooling_device *cdev;
+ char cdev_name[THERMAL_NAME_LENGTH];
+
+ data->cooling_devices[i].data = data;
+ data->cooling_devices[i].pwm_index = i;
+
+ snprintf(cdev_name, sizeof(cdev_name), "adt7470-pwm%d", i);
+ cdev = devm_thermal_of_child_cooling_device_register(dev,
+ dev->of_node, cdev_name,
+ &data->cooling_devices[i],
+ &adt7470_cooling_ops);
+
+ if (IS_ERR(cdev)) {
+ if (PTR_ERR(cdev) == -ENODEV)
+ continue;
+ return dev_warn_probe(dev, PTR_ERR(cdev),
+ "failed to register cooling device %s\n",
+ cdev_name);
+ }
+ }
+
+ return 0;
+}
+
static ssize_t pwm_max_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
@@ -1281,6 +1392,17 @@ static int adt7470_probe(struct i2c_client *client)
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
+ if (IS_ENABLED(CONFIG_THERMAL)) {
+ /* fill the cache before registering the cooling devices */
+ err = adt7470_update_sensors(data);
+ if (err)
+ return err;
+
+ err = adt7470_register_cooling_devices(dev, data);
+ if (err)
+ return err;
+ }
+
data->auto_update = kthread_run(adt7470_update_thread, client, "%s",
dev_name(hwmon_dev));
if (IS_ERR(data->auto_update))
--
2.55.0
^ permalink raw reply related
* Re: [PATCH v2 7/7] arm64: tegra: Add Host1x and VIC on Tegra264
From: Thierry Reding @ 2026-07-16 21:21 UTC (permalink / raw)
To: Mikko Perttunen
Cc: Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-tegra, dri-devel,
devicetree, linux-kernel
In-Reply-To: <20260622-t264-host1x-v2-7-ff7364d9ff7b@nvidia.com>
[-- Attachment #1: Type: text/plain, Size: 613 bytes --]
On Mon, Jun 22, 2026 at 03:57:44PM +0900, Mikko Perttunen wrote:
> Tegra264 has a host1x instance with a VIC (video image compositor).
> Other multimedia engines have moved outside host1x. Stream IDs are
> now namespaced by device rather than being defined globally --
> however, the only engine we have using context isolation is VIC so
> we only define VIC's range of context devices.
>
> Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
> ---
> arch/arm64/boot/dts/nvidia/tegra264.dtsi | 63 ++++++++++++++++++++++++++++++++
> 1 file changed, 63 insertions(+)
Applied, thanks.
Thierry
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [PATCH 2/4] hwmon: (adt7470) Add ADT7470_PWM_MAX macro
From: Luiz Angelo Daros de Luca @ 2026-07-16 21:21 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
de Luca
Cc: linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20260716-adt7470_thermalzone-v1-0-cc55ef35edde@gmail.com>
Instead of a magic 255, use a macro to refer to it.
Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
---
drivers/hwmon/adt7470.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index 664349756dc2..4ac292cd7f47 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -100,6 +100,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_REG_FAN_MAX(x) (ADT7470_REG_FAN_MAX_BASE_ADDR + ((x) * 2))
#define ADT7470_PWM_COUNT 4
+#define ADT7470_PWM_MAX 255
#define ADT7470_REG_PWM(x) (ADT7470_REG_PWM_BASE_ADDR + (x))
#define ADT7470_REG_PWM_MAX(x) (ADT7470_REG_PWM_MAX_BASE_ADDR + (x))
#define ADT7470_REG_PWM_MIN(x) (ADT7470_REG_PWM_MIN_BASE_ADDR + (x))
@@ -811,7 +812,7 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
switch (attr) {
case hwmon_pwm_input:
- val = clamp_val(val, 0, 255);
+ val = clamp_val(val, 0, ADT7470_PWM_MAX);
mutex_lock(&data->lock);
data->pwm[channel] = val;
err = regmap_write(data->regmap, ADT7470_REG_PWM(channel),
@@ -869,7 +870,7 @@ static ssize_t pwm_max_store(struct device *dev,
if (kstrtol(buf, 10, &temp))
return -EINVAL;
- temp = clamp_val(temp, 0, 255);
+ temp = clamp_val(temp, 0, ADT7470_PWM_MAX);
mutex_lock(&data->lock);
data->pwm_max[attr->index] = temp;
@@ -904,7 +905,7 @@ static ssize_t pwm_min_store(struct device *dev,
if (kstrtol(buf, 10, &temp))
return -EINVAL;
- temp = clamp_val(temp, 0, 255);
+ temp = clamp_val(temp, 0, ADT7470_PWM_MAX);
mutex_lock(&data->lock);
data->pwm_min[attr->index] = temp;
--
2.55.0
^ permalink raw reply related
* [PATCH 1/4] dt-bindings: hwmon: add binding for adi,adt7470
From: Luiz Angelo Daros de Luca @ 2026-07-16 21:21 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
de Luca
Cc: linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20260716-adt7470_thermalzone-v1-0-cc55ef35edde@gmail.com>
The Analog Devices ADT7470 is a temperature monitor and PWM fan
controller. It supports up to 10 external temperature sensors and
up to 4 PWM fan outputs.
Add the YAML device tree binding documentation for it. This includes
support for the thermal framework by defining the "#cooling-cells"
and "#thermal-sensor-cells" properties.
Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
---
.../devicetree/bindings/hwmon/adi,adt7470.yaml | 53 ++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/adi,adt7470.yaml b/Documentation/devicetree/bindings/hwmon/adi,adt7470.yaml
new file mode 100644
index 000000000000..e0c0fdadd1cf
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/adi,adt7470.yaml
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/adi,adt7470.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADT7470 hwmon sensor
+
+maintainers:
+ - Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+description: |
+ The ADT7470 is a temperature monitor and multiple PWM outputs.
+ It supports monitoring up to 10 external temperature sensors and controlling
+ up to four fans.
+
+properties:
+ compatible:
+ const: adi,adt7470
+
+ reg:
+ maxItems: 1
+
+ "#cooling-cells":
+ const: 2
+ description: |
+ Cooling device exposing the 4 PWM channels.
+
+ "#thermal-sensor-cells":
+ const: 1
+ description: |
+ Number of cells required to uniquely identify the temperature sensors.
+ Valid index values are 0 to 9, corresponding to temp1 through temp10.
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hwmon@2f {
+ compatible = "adi,adt7470";
+ reg = <0x2f>;
+ #cooling-cells = <2>;
+ #thermal-sensor-cells = <1>;
+ };
+ };
--
2.55.0
^ permalink raw reply related
* [PATCH 0/4] hwmon: (adt7470) Add thermal framework support
From: Luiz Angelo Daros de Luca @ 2026-07-16 21:21 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
de Luca
Cc: linux-hwmon, devicetree, linux-kernel
The ADT7470 is a temperature monitor and PWM fan controller capable of
monitoring up to 10 external temperature sensors and controlling up to 4
PWM outputs.
Currently, the driver exposes these sensors and PWM controls solely via
the standard hwmon sysfs interface. This patch series integrates the
ADT7470 driver with the kernel's thermal subsystem, allowing its
temperature sensors to be referenced by Device Tree thermal zones.
To avoid saturating the I2C bus during frequent polling by the thermal
core, the thermal callbacks are designed to read from the driver's
internal cache.
Patch 1 adds the YAML device tree binding documentation, defining
the necessary #cooling-cells and #thermal-sensor-cells properties.
Patch 2 performs a minor refactoring to introduce the ADT7470_PWM_MAX
macro, replacing hardcoded values.
Patch 3 registers the 4 PWM channels as optional thermal cooling
devices.
Patch 4 registers the external temperature sensors as optional thermal
zones.
Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
---
Luiz Angelo Daros de Luca (4):
dt-bindings: hwmon: add binding for adi,adt7470
hwmon: (adt7470) Add ADT7470_PWM_MAX macro
hwmon: (adt7470) Add thermal cooling device support
hwmon: (adt7470) Add thermal zone sensor support
.../devicetree/bindings/hwmon/adi,adt7470.yaml | 53 ++++++
drivers/hwmon/adt7470.c | 180 ++++++++++++++++++++-
2 files changed, 230 insertions(+), 3 deletions(-)
---
base-commit: ca078d004cf58137bcf8cb24a8b271397431ba58
change-id: 20260716-adt7470_thermalzone-59a102278d72
Best regards,
--
Luiz Angelo Daros de Luca <luizluca@gmail.com>
^ permalink raw reply
* [PATCH] dt-bindings: usb: ti,da830-ohci: Convert to DT schema
From: Bhargav Joshi @ 2026-07-16 21:19 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Axel Haslam
Cc: linux-usb, devicetree, linux-kernel, goledhruva, m-chawdhry,
daniel.baluta, simona.toaca, j.bhargav.u
Convert Texas Instruments da830-ohci bindings from legacy text to DT
schema. Name of binding is changed to match primary compatible property.
Signed-off-by: Bhargav Joshi <j.bhargav.u@gmail.com>
---
.../devicetree/bindings/usb/ohci-da8xx.txt | 23 ----------
.../devicetree/bindings/usb/ti,da830-ohci.yaml | 50 ++++++++++++++++++++++
2 files changed, 50 insertions(+), 23 deletions(-)
diff --git a/Documentation/devicetree/bindings/usb/ohci-da8xx.txt b/Documentation/devicetree/bindings/usb/ohci-da8xx.txt
deleted file mode 100644
index 24a826d5015e..000000000000
--- a/Documentation/devicetree/bindings/usb/ohci-da8xx.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-DA8XX USB OHCI controller
-
-Required properties:
-
- - compatible: Should be "ti,da830-ohci"
- - reg: Should contain one register range i.e. start and length
- - interrupts: Description of the interrupt line
- - phys: Phandle for the PHY device
- - phy-names: Should be "usb-phy"
-
-Optional properties:
- - vbus-supply: phandle of regulator that controls vbus power / over-current
-
-Example:
-
-ohci: usb@225000 {
- compatible = "ti,da830-ohci";
- reg = <0x225000 0x1000>;
- interrupts = <59>;
- phys = <&usb_phy 1>;
- phy-names = "usb-phy";
- vbus-supply = <®_usb_ohci>;
-};
diff --git a/Documentation/devicetree/bindings/usb/ti,da830-ohci.yaml b/Documentation/devicetree/bindings/usb/ti,da830-ohci.yaml
new file mode 100644
index 000000000000..ad23da646648
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/ti,da830-ohci.yaml
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/ti,da830-ohci.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: DA8XX USB OHCI controller
+
+maintainers:
+ - Axel Haslam <ahaslam@baylibre.com>
+
+properties:
+ compatible:
+ const: ti,da830-ohci
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ phys:
+ maxItems: 1
+
+ phy-names:
+ const: usb-phy
+
+ vbus-supply:
+ description:
+ phandle of regulator that controls vbus power / over-current
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - phys
+ - phy-names
+
+additionalProperties: false
+
+examples:
+ - |
+ usb@225000 {
+ compatible = "ti,da830-ohci";
+ reg = <0x225000 0x1000>;
+ interrupts = <59>;
+ phys = <&usb_phy 1>;
+ phy-names = "usb-phy";
+ vbus-supply = <®_usb_ohci>;
+ };
---
base-commit: ef0c9f75a19532d7675384708fc8621e10850104
change-id: 20260622-ti-da830-ohci-2a2299d045e2
Best regards,
--
Bhargav
^ permalink raw reply related
* [PATCH] dt-bindings: net: nvidia,tegra234-mgbe: Add missing properties
From: Thierry Reding @ 2026-07-16 21:20 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Jonathan Hunter, netdev, devicetree, linux-tegra,
linux-kernel
From: Thierry Reding <treding@nvidia.com>
Being a DWMAC derivative, the Tegra234 MGBE supports AXI configuration
nodes named stmmac-axi-config and phandle references to them using the
snps,axi-config property.
While at it, add the 10gbase-r PHY mode.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
This gets rid of the remaining warnings on half of the Tegra234 boards.
.../devicetree/bindings/net/nvidia,tegra234-mgbe.yaml | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml b/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml
index 215f14d1897d..dc897e312c55 100644
--- a/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml
+++ b/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml
@@ -81,8 +81,9 @@ properties:
phy-mode:
contains:
enum:
- - usxgmii
- 10gbase-kr
+ - 10gbase-r
+ - usxgmii
mdio:
$ref: mdio.yaml#
@@ -90,6 +91,12 @@ properties:
description:
Optional node for embedded MDIO controller.
+ snps,axi-config:
+ $ref: snps,dwmac.yaml#/properties/snps,axi-config
+
+ stmmac-axi-config:
+ $ref: snps,dwmac.yaml#/properties/stmmac-axi-config
+
required:
- compatible
- reg
--
2.54.0
^ permalink raw reply related
* Re: [RFC PATCH 0/3] drm: Add DRM driver for GlandaGPU (VHDL soft-IP GPU)
From: Leander Kieweg @ 2026-07-16 21:19 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: dri-devel, devicetree, airlied, simona, maarten.lankhorst,
mripard, robh, krzk+dt, conor+dt
In-Reply-To: <69a8c2a6-d0f3-4362-bfa1-04923db6f3cc@suse.de>
Hi Thomas,
Thanks a lot for the detailed feedback, this is exactly the kind of
guidance I was hoping for.
> We already have a driver for a RasPi-based USB display that someone made
> in their spare time. So being a hobbyist project is not a problem per se.
Good to know, thanks for clarifying that.
> 2d primitives are likely not very useful for DRM. The canonical
> reference of why is at [1]. The tl;dr is that there's no standard API,
> and GPU-CPU transfers and setup costs are too high to make it
> significantly faster than software rendering.
>
> Various people have proposed to add some form of 2d pipeline to DRM, but
> nothing concrete has ever emerged.
>
> Conceptually, DRM doesn't really render anything. It composes the screen
> from already-rendered buffers. Rendering to these buffers is mostly done
> by Mesa drivers with some help from DRM's kernel drivers. If you want
> hardware rendering, you'd need memory management for off-screen
> rendering that Mesa can use independently from display output. Therefore
> these simple draw and clear primitives aren't that useful. You need to
> design a full rendering pipeline instead.
>
> [1] https://blog.ffwll.ch/2018/08/no-2d-in-drm.html
Sima's blog post that you linked makes the reasoning much clearer than what
I'd pieced together on my own. I understand this means the current
fixed-function
ioctls are really only a placeholder, not something to build on further as is.
> Using a single ioctl per command will kill performance. So, if anything,
> you'd want the command-buffer model. To make it fast for 2d primitives,
> you'd likely have to model it like a 3d pipeline: have all 2d graphics
> buffers in the display memory already and submit a large batch of
> rendering commands that generate the entire screen at once.
That's a helpful concrete direction, thank you. I'll look into what a
command-buffer submission model would need to look like for this.
> Rule of thumb is that you need a working user-space side for ioctls.
> Mesa would be the premier target for 3d.
>
> I'm not much involved in Mesa, but I think Mesa is quickly moving
> towards programmable pipelines. Getting drivers for fixed-function
> hardware merged might be hard.
>
> How complicated is it to model a stream processor (i.e. GPU core) in VHDL?
I think it's doable at a small scope. Some memory to hold uploaded
bytecode, a minimal CPU core (I'd probably base the ISA on a reduced
RISC-V subset, mainly to get an existing compiler toolchain for free)
that processes it and writes the result into the framebuffer. That
part sounds genuinely fun to build, and I'm actually looking forward
to it, though it's a fair amount of work and I'll need to find the
time for it.
For throughput I'd design the core with multiple instances in mind
from the start, e.g. 4 cores each responsible for a quarter of the
screen, and scale that further once the basic version works. I'd
still get a single instance working first before parallelizing it.
> Can you use a PCI device for that and let the kernel do all the work?
> See [2] for how to get a PCI device id.
>
> [2] https://www.qemu.org/docs/master/specs/pci-ids.html
I want to make sure I understand this correctly before I start on
it. My real hardware (DE10-Standard) stays exactly as it is, still
using platform_driver via devicetree, completely unchanged. Only the
QEMU device would change, from the current fixed-address platform
device to a proper virtual PCI device (with a reserved vendor/device
ID and BARs for VRAM/MMIO). On the driver side, that would mean
adding pci_driver support alongside the existing platform_driver, so
the exact same driver would run on both, just with two different
probe functions depending on whether it's found via devicetree (real
hardware) or via PCI enumeration (QEMU). Is that right?
> That's indeed a good thing to have in hardware.
>
> I mentioned that the 2d/3d rendering is probably complicated to get
> done. If I may suggest an alternative, you could implement additional
> features of the mode-setting pipeline. Besides the primary plane that
> your hardware already supports, you could add a cursor plane. Or you
> could add overlay planes for displaying YUV formats (i.e., video
> frames). Or you could implement existing DRM properties, such as
> scaling, background colors, or HDR. These features are already
> supported by user space. Your device would be usable immediately.
This is a great suggestion, thank you. A hardware cursor plane
actually fits something I'd already been considering for the hardware
side, so that'll be my next bigger hardware update.
Scaling looks approachable too, and I like that it wouldn't need
extra memory on the hardware side, so I'm adding that to the list as
well. Background color I think I can already cover with the existing
CLEAR command.
YUV overlay support is more future work for me, my current memory
layout would need a bigger restructuring first to fit that in given
how limited memory is on this hardware, but the idea itself sounds
appealing since the VHDL side looks fairly contained. HDR isn't
realistic here either way, my test display can't do it and I'd
expect to hit memory limits before anything else.
> Glad to hear it didn't work. drm_simple_display_pipe is obsolete and on
> its way out. Please don't use it.
Good to know, I'll drop that idea entirely then.
Given all this, my plan for v2 is to drop the ioctl-based UAPI
entirely (removing the custom ioctls means there's no UAPI to keep
stable, which takes the pressure off getting the command-buffer
design right immediately), address the style/review comments from
Uwe and from your code review on patch 2, switch the QEMU test path
from the fixed-address platform device to a proper PCI device as
discussed above, and possibly add background color support since
that doesn't require any hardware changes. The cursor plane, scaling,
and the stream processor work will be separate follow-ups once the
hardware side catches up.
Thanks again for taking the time, this gives me a lot to work with.
Best regards,
Leander
^ permalink raw reply
* Re: [PATCH v2 3/4] dt-bindings: arm: qcom-soc: Validate nodes with fallbacks
From: Rob Herring @ 2026-07-16 21:14 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Bjorn Andersson, Konrad Dybcio, Krzysztof Kozlowski, Conor Dooley,
linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <20260707-dt-bindings-qcom-soc-naming-v2-3-1b7d695be2e1@oss.qualcomm.com>
On Tue, Jul 07, 2026 at 03:36:01PM +0200, Krzysztof Kozlowski wrote:
> The schema checking for expected naming patterns for SoC IP block nodes
> was really incomplete and was checking only nodes with single
> compatible. Fixing this and applying schema for nodes with fallbacks is
> not trivial, because obvious solution like:
>
> minItems: 1
> items:
> - oneOf:
> - pattern: ...
> - pattern: ...
> - {}
> - {}
>
> is not allowed by dtschema. The binding should also skip root nodes,
> which have SoC-based fallback, but completely random front compatibles.
>
> Solve this all by:
> 1. Extending the select pattern to match all nodes except root node.
> 2. Apply schema to all items of "compatible" property, which means it
> will be evaluating also all sorts of generic compatibles like
> "syscon". List them all and let's hope that fallback list will not
> grow too much, because generic compatible fallbacks are discouraged.
>
> The benefit is that this schema is finally very specific and evaluates
> all nodes for desired naming.
>
> Diff is a bit obfuscated, due to indentation change so briefly
> explaining:
> 1. None of the patterns are changed (neither in "Preferred naming style"
> group nor in "Legacy namings").
> 2. None of the enums with "Legacy namings" and "Legacy compatibles with
> wild-cards" are changed.
> 3. Add pattern for sound cards.
> 4. Add enum with qcom,hamoa-crd-ec, because it is used as fallback to
> SoC-codename (Glymur).
> 5. Add list with all used generic fallbacks.
>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> ---
> .../devicetree/bindings/arm/qcom-soc.yaml | 190 ++++++++++++++++-----
> 1 file changed, 143 insertions(+), 47 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/arm/qcom-soc.yaml b/Documentation/devicetree/bindings/arm/qcom-soc.yaml
> index d934afd59df1..ffdaff073c92 100644
> --- a/Documentation/devicetree/bindings/arm/qcom-soc.yaml
> +++ b/Documentation/devicetree/bindings/arm/qcom-soc.yaml
> @@ -22,60 +22,156 @@ description: |
>
> select:
> properties:
> + # Select all nodes which have SoC-based compatibles, but not the root
> + # "/" node, because its front compatibles do not follow any SoC patterns
> + $nodename:
> + pattern: "[^/]"
> compatible:
> - oneOf:
> - - pattern: "^qcom,.*(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sar|sc|sd[amx]|sm|x1[ep])[0-9]+.*$"
> - - pattern: "^qcom,.*(eliza|kaanapali|glymur|hawi|mahua|maili|milos|nord|shikra).*$"
> + contains:
> + oneOf:
> + - pattern: "^qcom,.*(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sar|sc|sd[amx]|sm|x1[ep])[0-9]+.*$"
> + - pattern: "^qcom,.*(eliza|kaanapali|glymur|hawi|mahua|maili|milos|nord|shikra).*$"
> required:
> - compatible
>
> properties:
> compatible:
> - oneOf:
> - # Preferred naming style for compatibles of SoC components:
> - - pattern: "^qcom,(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm|x1[ep])[0-9]+(pro)?-.*$"
> - - pattern: "^qcom,sar[0-9]+[a-z]?-.*$"
> - - pattern: "^qcom,(sa|sc)8[0-9]+[a-z][a-z]?-.*$"
> - - pattern: "^qcom,(eliza|kaanapali|glymur|hawi|mahua|maili|milos|nord|shikra)-.*$"
> + # SoC specific compatibles can appear alone or be followed by another SoC
> + # specific compatible or generic fallbacks, therefore check for compatible
> + # should use ideally:
> + #
> + # minItems: 1
> + # items:
> + # - oneOf:
> + # - pattern: ...
> + # - pattern: ...
> + # - {}
> + # - {}
> + #
> + # but that is not allowed by dtschema ("oneOf" cannot be used as an item
> + # for a compatible). Work around this by defining schema for all items,
> + # but drawback is that we need also to list all known generic fallbacks
> + # like "syscon" etc.
> + minItems: 1
> + maxItems: 4
> + items:
> + # Use anyOf, because soundcards will match few patterns
> + anyOf:
> + # Preferred naming style for compatibles of SoC components:
> + - pattern: "^qcom,(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm|x1[ep])[0-9]+(pro)?-.*$"
> + - pattern: "^qcom,sar[0-9]+[a-z]?-.*$"
> + - pattern: "^qcom,(sa|sc)8[0-9]+[a-z][a-z]?-.*$"
> + - pattern: "^qcom,(eliza|kaanapali|glymur|hawi|mahua|maili|milos|nord|shikra)-.*$"
> + # SoC codenames used for non-SoC components:
> + - enum:
> + - qcom,hamoa-crd-ec
>
> - # Legacy namings - variations of existing patterns/compatibles are OK,
> - # but do not add completely new entries to these:
> - - pattern: "^qcom,[ak]pss-wdt-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm|x1[ep])[0-9]+.*$"
> - # qcom,apss-wdt with codenames
> - - pattern: "^qcom,apss-wdt-[a-z]+$"
> - - pattern: "^qcom,kpss-gcc-(apq|ipq|mdm|msm)[0-9]+.*$"
> - - pattern: "^qcom,gcc-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm)[0-9]+.*$"
> - - pattern: "^qcom,mmcc-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm)[0-9]+.*$"
> - - pattern: "^qcom,pcie-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm|x1[ep])[0-9]+.*$"
> - - pattern: "^qcom,rpm-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm)[0-9]+.*$"
> - - pattern: "^qcom,rpmcc-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm)[0-9]+.*$"
> - - pattern: "^qcom,scm-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sar?|sc|sd[amx]|sm|x1[ep])[0-9]+.*$"
> - # qcom,scm with codenames
> - - pattern: "^qcom,scm-[a-z]+$"
> - - pattern: "^qcom,tcsr-(apq|ipq|mdm|msm)[0-9]+.*$"
> - - pattern: "^qcom,usb-hs-phy-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm|x1[ep])[0-9]+.*$"
> - - enum:
> - - qcom,dsi-ctrl-6g-qcm2290
> - - qcom,gpucc-sdm630
> - - qcom,gpucc-sdm660
> - - qcom,lcc-apq8064
> - - qcom,lcc-ipq8064
> - - qcom,lcc-mdm9615
> - - qcom,lcc-msm8960
> - - qcom,lpass-cpu-apq8016
> - - qcom,usb-hs-ipq4019-phy
> - - qcom,usb-hsic-phy-mdm9615
> - - qcom,usb-hsic-phy-msm8974
> - - qcom,usb-ss-ipq4019-phy
> - - qcom,vqmmc-ipq4019-regulator
> + # Legacy namings - variations of existing patterns/compatibles are OK,
> + # but do not add completely new entries to these:
> + - pattern: "^qcom,[ak]pss-wdt-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm|x1[ep])[0-9]+.*$"
> + # qcom,apss-wdt with codenames
> + - pattern: "^qcom,apss-wdt-[a-z]+$"
> + - pattern: "^qcom,kpss-gcc-(apq|ipq|mdm|msm)[0-9]+.*$"
> + - pattern: "^qcom,gcc-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm)[0-9]+.*$"
> + - pattern: "^qcom,mmcc-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm)[0-9]+.*$"
> + - pattern: "^qcom,pcie-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm|x1[ep])[0-9]+.*$"
> + - pattern: "^qcom,rpm-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm)[0-9]+.*$"
> + - pattern: "^qcom,rpmcc-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm)[0-9]+.*$"
> + - pattern: "^qcom,scm-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sar?|sc|sd[amx]|sm|x1[ep])[0-9]+.*$"
> + # qcom,scm with codenames
> + - pattern: "^qcom,scm-[a-z]+$"
> + - pattern: "^qcom,tcsr-(apq|ipq|mdm|msm)[0-9]+.*$"
> + - pattern: "^qcom,usb-hs-phy-(apq|ipq|mdm|msm|qcm|qcs|q[dr]u|sa|sc|sd[amx]|sm|x1[ep])[0-9]+.*$"
>
> - # Legacy compatibles with wild-cards - list cannot grow with new bindings:
> - - enum:
> - - qcom,ipq806x-ahci
> - - qcom,ipq806x-gmac
> - - qcom,ipq806x-nand
> - - qcom,ipq806x-sata-phy
> - - qcom,ipq806x-usb-phy-ss
> - - qcom,ipq806x-usb-phy-hs
> + - enum:
> + - qcom,dsi-ctrl-6g-qcm2290
> + - qcom,gpucc-sdm630
> + - qcom,gpucc-sdm660
> + - qcom,lcc-apq8064
> + - qcom,lcc-ipq8064
> + - qcom,lcc-mdm9615
> + - qcom,lcc-msm8960
> + - qcom,lpass-cpu-apq8016
> + - qcom,usb-hs-ipq4019-phy
> + - qcom,usb-hsic-phy-mdm9615
> + - qcom,usb-hsic-phy-msm8974
> + - qcom,usb-ss-ipq4019-phy
> + - qcom,vqmmc-ipq4019-regulator
> +
> + # Legacy compatibles with wild-cards - list cannot grow with new bindings:
> + - enum:
> + - qcom,ipq806x-ahci
> + - qcom,ipq806x-gmac
> + - qcom,ipq806x-nand
> + - qcom,ipq806x-sata-phy
> + - qcom,ipq806x-usb-phy-ss
> + - qcom,ipq806x-usb-phy-hs
> +
> + # Schema matches also sound card nodes and its front compatibles can be anything
> + - pattern: "^.*,.*sndcard$"
> +
> + # List all used generic fallbacks. The list can grow, but in practice
> + # it is not expected, because specific compatibles are preferred for
> + # fallbacks as well.
> + - enum:
> + - arm,gic-v3
> + - arm,mmu-500
> + - generic-ahci
> + - jedec,ufs-2.0
> + - mmio-sram
> + - qcom,adreno-smmu
> + - qcom,aoss-qmp
> + - qcom,cpr
> + - qcom,cpufreq-epss
> + - qcom,cpufreq-hw
> + - qcom,dcc
> + - qcom,dwc3
> + - qcom,epss-l3
> + - qcom,eud
> + - qcom,glink-smd-rpm
> + - qcom,inline-crypto-engine
> + - qcom,ipcc
> + - qcom,kpss-gcc
> + - qcom,kpss-timer
> + - qcom,kpss-wdt
> + - qcom,mdp5
> + - qcom,mdss-dsi-ctrl
> + - qcom,msm-iommu-v1
> + - qcom,msm-iommu-v2
> + - qcom,msm-timer
> + - qcom,osm-l3
> + - qcom,pcie2-phy
> + - qcom,pdc
> + - qcom,pmic-glink
> + - qcom,qce
> + - qcom,qfprom
> + - qcom,qspi-v1
> + - qcom,qusb2-v2-phy
> + - qcom,rpm-proc
> + - qcom,rpmh-rsc
> + - qcom,rpmcc
> + - qcom,saw2
> + - qcom,scm
> + - qcom,sdhci-msm-v4
> + - qcom,sdhci-msm-v5
> + - qcom,sec-qfprom
> + - qcom,smd-rpm
> + - qcom,smmu-500
> + - qcom,smmu-v2
> + - qcom,snps-dwc3
> + - qcom,ssc-block-bus
> + - qcom,tcsr-mutex
> + - qcom,trng
> + - qcom,tsens-v0_1
> + - qcom,tsens-v1
> + - qcom,tsens-v2
> + - qcom,ufshc
> + - qcom,usb-hs-phy
> + - qcom,usb-hsic-phy
> + - qcom,usb-snps-hs-5nm-phy
> + - qcom,usb-snps-hs-7nm-phy
> + - simple-mfd
> + - snps,dwmac
> + - syscon
You lost me here...
This all looks pretty unmaintainable. Perhaps you should give up on old
SoCs and only do something on new(er) ones:
select:
properties:
compatible:
contains:
pattern: '^qcom,'
allOf:
- if:
properties:
compatible:
contains:
pattern: '^qcom,.+foosoc'
then:
properties:
compatible:
contains:
pattern: '^qcom,foosoc'
Rob
^ permalink raw reply
* [PATCH v7 4/4] iio: adc: ltc2378: Enable triggered buffer data capture
From: Marcelo Schmitt @ 2026-07-16 21:10 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, julianbraha, marcelo.schmitt1
In-Reply-To: <cover.1784235595.git.marcelo.schmitt@analog.com>
Enable users to run triggered data captures with LTC2378 and similar ADCs.
Reviewed-by: David Lechner <dlechner@baylibre.com>
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
Change log v6 -> v7:
No changes in v7.
drivers/iio/adc/Kconfig | 1 +
drivers/iio/adc/ltc2378.c | 75 +++++++++++++++++++++++++++------------
2 files changed, 53 insertions(+), 23 deletions(-)
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 96df8453b826..6cf67f7a6eed 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -952,6 +952,7 @@ config LTC2378
select IIO_BUFFER
select IIO_BUFFER_DMA
select IIO_BUFFER_DMAENGINE
+ select IIO_TRIGGERED_BUFFER
select SPI_OFFLOAD
select SPI_OFFLOAD_TRIGGER_PWM
help
diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c
index 7d29f021cb7b..25db69edee4d 100644
--- a/drivers/iio/adc/ltc2378.c
+++ b/drivers/iio/adc/ltc2378.c
@@ -6,6 +6,7 @@
* Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
*/
+#include <linux/array_size.h>
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/byteorder/generic.h>
@@ -32,6 +33,8 @@
#include <linux/iio/buffer.h>
#include <linux/iio/buffer-dmaengine.h>
#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
#include <linux/iio/types.h>
#define LTC2378_TDSDOBUSYL_NS 5
@@ -97,7 +100,7 @@
struct ltc2378_chip_info {
const char *name;
unsigned int internal_ref_uV;
- struct iio_chan_spec chan;
+ struct iio_chan_spec chan[2]; /* 1 physical chan + 1 timestamp chan */
struct iio_chan_spec offload_chan;
unsigned int max_sample_rate_Hz;
unsigned int tconv_ns;
@@ -138,7 +141,7 @@ struct ltc2378_state {
static const struct ltc2378_chip_info ltc2338_18_chip_info = {
.name = "ltc2338-18",
.internal_ref_uV = 2048000,
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(18),
.max_sample_rate_Hz = 1 * HZ_PER_MHZ,
.tconv_ns = 527,
@@ -146,7 +149,7 @@ static const struct ltc2378_chip_info ltc2338_18_chip_info = {
static const struct ltc2378_chip_info ltc2364_16_chip_info = {
.name = "ltc2364-16",
- .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+ .chan = { LTC2378_UNIPOLAR_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(16),
.max_sample_rate_Hz = 250 * HZ_PER_KHZ,
.tconv_ns = 3000,
@@ -154,7 +157,7 @@ static const struct ltc2378_chip_info ltc2364_16_chip_info = {
static const struct ltc2378_chip_info ltc2364_18_chip_info = {
.name = "ltc2364-18",
- .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+ .chan = { LTC2378_UNIPOLAR_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(18),
.max_sample_rate_Hz = 250 * HZ_PER_KHZ,
.tconv_ns = 3000,
@@ -162,7 +165,7 @@ static const struct ltc2378_chip_info ltc2364_18_chip_info = {
static const struct ltc2378_chip_info ltc2367_16_chip_info = {
.name = "ltc2367-16",
- .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+ .chan = { LTC2378_UNIPOLAR_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(16),
.max_sample_rate_Hz = 500 * HZ_PER_KHZ,
.tconv_ns = 1500,
@@ -170,7 +173,7 @@ static const struct ltc2378_chip_info ltc2367_16_chip_info = {
static const struct ltc2378_chip_info ltc2367_18_chip_info = {
.name = "ltc2367-18",
- .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+ .chan = { LTC2378_UNIPOLAR_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(18),
.max_sample_rate_Hz = 500 * HZ_PER_KHZ,
.tconv_ns = 1500,
@@ -178,7 +181,7 @@ static const struct ltc2378_chip_info ltc2367_18_chip_info = {
static const struct ltc2378_chip_info ltc2368_16_chip_info = {
.name = "ltc2368-16",
- .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+ .chan = { LTC2378_UNIPOLAR_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(16),
.max_sample_rate_Hz = 1 * HZ_PER_MHZ,
.tconv_ns = 527,
@@ -186,7 +189,7 @@ static const struct ltc2378_chip_info ltc2368_16_chip_info = {
static const struct ltc2378_chip_info ltc2368_18_chip_info = {
.name = "ltc2368-18",
- .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+ .chan = { LTC2378_UNIPOLAR_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(18),
.max_sample_rate_Hz = 1 * HZ_PER_MHZ,
.tconv_ns = 527,
@@ -194,7 +197,7 @@ static const struct ltc2378_chip_info ltc2368_18_chip_info = {
static const struct ltc2378_chip_info ltc2369_18_chip_info = {
.name = "ltc2369-18",
- .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+ .chan = { LTC2378_UNIPOLAR_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(18),
.max_sample_rate_Hz = 1600 * HZ_PER_KHZ,
.tconv_ns = 412,
@@ -202,7 +205,7 @@ static const struct ltc2378_chip_info ltc2369_18_chip_info = {
static const struct ltc2378_chip_info ltc2370_16_chip_info = {
.name = "ltc2370-16",
- .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+ .chan = { LTC2378_UNIPOLAR_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(16),
.max_sample_rate_Hz = 2 * HZ_PER_MHZ,
.tconv_ns = 322,
@@ -210,7 +213,7 @@ static const struct ltc2378_chip_info ltc2370_16_chip_info = {
static const struct ltc2378_chip_info ltc2376_16_chip_info = {
.name = "ltc2376-16",
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(16),
.max_sample_rate_Hz = 250 * HZ_PER_KHZ,
.tconv_ns = 3000,
@@ -218,7 +221,7 @@ static const struct ltc2378_chip_info ltc2376_16_chip_info = {
static const struct ltc2378_chip_info ltc2376_18_chip_info = {
.name = "ltc2376-18",
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(18),
.max_sample_rate_Hz = 250 * HZ_PER_KHZ,
.tconv_ns = 3000,
@@ -226,7 +229,7 @@ static const struct ltc2378_chip_info ltc2376_18_chip_info = {
static const struct ltc2378_chip_info ltc2376_20_chip_info = {
.name = "ltc2376-20",
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(20),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(20),
.max_sample_rate_Hz = 250 * HZ_PER_KHZ,
.tconv_ns = 3000,
@@ -234,7 +237,7 @@ static const struct ltc2378_chip_info ltc2376_20_chip_info = {
static const struct ltc2378_chip_info ltc2377_16_chip_info = {
.name = "ltc2377-16",
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(16),
.max_sample_rate_Hz = 500 * HZ_PER_KHZ,
.tconv_ns = 1500,
@@ -242,7 +245,7 @@ static const struct ltc2378_chip_info ltc2377_16_chip_info = {
static const struct ltc2378_chip_info ltc2377_18_chip_info = {
.name = "ltc2377-18",
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(18),
.max_sample_rate_Hz = 500 * HZ_PER_KHZ,
.tconv_ns = 1500,
@@ -250,7 +253,7 @@ static const struct ltc2378_chip_info ltc2377_18_chip_info = {
static const struct ltc2378_chip_info ltc2377_20_chip_info = {
.name = "ltc2377-20",
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(20),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(20),
.max_sample_rate_Hz = 500 * HZ_PER_KHZ,
.tconv_ns = 1500,
@@ -258,7 +261,7 @@ static const struct ltc2378_chip_info ltc2377_20_chip_info = {
static const struct ltc2378_chip_info ltc2378_16_chip_info = {
.name = "ltc2378-16",
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(16),
.max_sample_rate_Hz = 1 * HZ_PER_MHZ,
.tconv_ns = 527,
@@ -266,7 +269,7 @@ static const struct ltc2378_chip_info ltc2378_16_chip_info = {
static const struct ltc2378_chip_info ltc2378_18_chip_info = {
.name = "ltc2378-18",
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(18),
.max_sample_rate_Hz = 1 * HZ_PER_MHZ,
.tconv_ns = 527,
@@ -274,7 +277,7 @@ static const struct ltc2378_chip_info ltc2378_18_chip_info = {
static const struct ltc2378_chip_info ltc2378_20_chip_info = {
.name = "ltc2378-20",
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(20),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(20),
.max_sample_rate_Hz = 1 * HZ_PER_MHZ,
.tconv_ns = 675,
@@ -282,7 +285,7 @@ static const struct ltc2378_chip_info ltc2378_20_chip_info = {
static const struct ltc2378_chip_info ltc2379_18_chip_info = {
.name = "ltc2379-18",
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(18),
.max_sample_rate_Hz = 1600 * HZ_PER_KHZ,
.tconv_ns = 412,
@@ -290,7 +293,7 @@ static const struct ltc2378_chip_info ltc2379_18_chip_info = {
static const struct ltc2378_chip_info ltc2380_16_chip_info = {
.name = "ltc2380-16",
- .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+ .chan = { LTC2378_BIPOLAR_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
.offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(16),
.max_sample_rate_Hz = 2 * HZ_PER_MHZ,
.tconv_ns = 322,
@@ -309,6 +312,25 @@ static int ltc2378_convert_and_acquire(struct ltc2378_state *st)
return ret;
}
+static irqreturn_t ltc2378_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ltc2378_convert_and_acquire(st);
+ if (ret < 0)
+ goto err_out;
+
+ iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),
+ pf->timestamp);
+
+err_out:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
static int ltc2378_channel_single_read(const struct iio_chan_spec *chan,
struct ltc2378_state *st, int *val)
{
@@ -697,8 +719,15 @@ static int ltc2378_probe(struct spi_device *spi)
/* Fall back to low speed usage when no SPI offload is available. */
if (ret == -ENODEV) {
indio_dev->info = <c2378_iio_info;
- indio_dev->channels = &st->info->chan;
- indio_dev->num_channels = 1;
+ indio_dev->channels = st->info->chan;
+ indio_dev->num_channels = ARRAY_SIZE(st->info->chan);
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ltc2378_trigger_handler,
+ NULL);
+ if (ret)
+ return ret;
} else if (ret) {
return dev_err_probe(dev, ret, "failed to get offload\n");
} else {
--
2.53.0
^ permalink raw reply related
* [PATCH v7 3/4] iio: adc: ltc2378: Enable high-speed data capture
From: Marcelo Schmitt @ 2026-07-16 21:10 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, julianbraha, marcelo.schmitt1
In-Reply-To: <cover.1784235595.git.marcelo.schmitt@analog.com>
Make use of SPI transfer offloading to speed up data capture, enabling data
acquisition at faster sample rates (up to 2 MSPS).
Reviewed-by: David Lechner <dlechner@baylibre.com>
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
Change log v6 -> v7:
- Now keeping validated offload configurations if conversion rate update fails.
- Imported SPI_OFFLOAD name space.
- Replaced DIV_ROUND_UP() by div_u64() to mitigate config check fail due to
different integer rounding.
- Separate macros for offload and non-offload channels.
- Moved ltc2378_prepare_offload_message() downwards in source code.
drivers/iio/adc/Kconfig | 6 +
drivers/iio/adc/ltc2378.c | 403 +++++++++++++++++++++++++++++++++++++-
2 files changed, 406 insertions(+), 3 deletions(-)
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 220c6739a8f2..96df8453b826 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -948,6 +948,12 @@ config LTC2378
depends on SPI
depends on REGULATOR || COMPILE_TEST
depends on GPIOLIB
+ depends on PWM
+ select IIO_BUFFER
+ select IIO_BUFFER_DMA
+ select IIO_BUFFER_DMAENGINE
+ select SPI_OFFLOAD
+ select SPI_OFFLOAD_TRIGGER_PWM
help
Say yes here to build support for Analog Devices LTC2378-20 and
similar analog to digital converters.
diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c
index 5600c820c7f4..7d29f021cb7b 100644
--- a/drivers/iio/adc/ltc2378.c
+++ b/drivers/iio/adc/ltc2378.c
@@ -16,16 +16,27 @@
#include <linux/device-id/of.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regulator/consumer.h>
+#include <linux/pwm.h>
#include <linux/spi/spi.h>
+#include <linux/spi/offload/consumer.h>
+#include <linux/spi/offload/types.h>
+#include <linux/time64.h>
#include <linux/types.h>
#include <linux/units.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dmaengine.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
+#define LTC2378_TDSDOBUSYL_NS 5
+#define LTC2378_TBUSYLH_NS 13
+#define LTC2378_TCNV_HIGH_NS 20
#define LTC2378_MAX_DATA_WAIT_US 4 /* max(TBUSYLH + TCONV + TDSDOBUSYL) */
#define LTC2378_DIFF_CHANNEL(_sign, _real_bits, _storage_bits) \
@@ -50,10 +61,46 @@
#define LTC2378_UNIPOLAR_DIFF_CHANNEL(_real_bits) \
LTC2378_DIFF_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16))
+#define LTC2378_DIFF_CHANNEL_OFFLOAD(_sign, _real_bits, _storage_bits) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = 0, \
+ .scan_type = { \
+ .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \
+ IIO_SCAN_FORMAT_UNSIGNED_INT, \
+ .realbits = _real_bits, \
+ .storagebits = _storage_bits, \
+ .shift = 0, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+/*
+ * Currently, the available offload hardware + DMA configuration only supports
+ * pushing 32-bit data elements to DMA IIO buffers in CPU endianness. For 16-bit
+ * precision parts, those 32-bit elements (in CPU endianness) contain 2 bytes
+ * with data and 2 bytes always zeroed out. Nevertheless, for the offload use
+ * case, the IIO buffer is configured for 32 storage bits in CPU endianness so
+ * data is correctly aligned in user space despite 2 out of the 4 bytes being
+ * zeros.
+ */
+#define LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(_real_bits) \
+ LTC2378_DIFF_CHANNEL_OFFLOAD(1, (_real_bits), 32)
+
+#define LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(_real_bits) \
+ LTC2378_DIFF_CHANNEL_OFFLOAD(0, (_real_bits), 32)
+
struct ltc2378_chip_info {
const char *name;
unsigned int internal_ref_uV;
struct iio_chan_spec chan;
+ struct iio_chan_spec offload_chan;
+ unsigned int max_sample_rate_Hz;
+ unsigned int tconv_ns;
};
struct ltc2378_state {
@@ -63,6 +110,15 @@ struct ltc2378_state {
struct mutex lock; /* Protect data acquisition cycle */
int ref_uV;
struct spi_transfer xfer;
+ struct spi_transfer offload_xfer;
+ struct spi_offload *offload;
+ struct spi_offload_trigger *offload_trigger;
+ struct pwm_waveform cnv_wf;
+ struct spi_message offload_msg;
+ struct spi_offload_trigger_config offload_trigger_config;
+ struct pwm_device *cnv_trigger;
+ unsigned int cnv_Hz;
+ unsigned int sample_freq_range[3];
/*
* DMA (thus cache coherency maintenance) requires the transfer buffers
@@ -83,101 +139,161 @@ static const struct ltc2378_chip_info ltc2338_18_chip_info = {
.name = "ltc2338-18",
.internal_ref_uV = 2048000,
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
};
static const struct ltc2378_chip_info ltc2364_16_chip_info = {
.name = "ltc2364-16",
.chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+ .offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
};
static const struct ltc2378_chip_info ltc2364_18_chip_info = {
.name = "ltc2364-18",
.chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+ .offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
};
static const struct ltc2378_chip_info ltc2367_16_chip_info = {
.name = "ltc2367-16",
.chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+ .offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
};
static const struct ltc2378_chip_info ltc2367_18_chip_info = {
.name = "ltc2367-18",
.chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+ .offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
};
static const struct ltc2378_chip_info ltc2368_16_chip_info = {
.name = "ltc2368-16",
.chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+ .offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
};
static const struct ltc2378_chip_info ltc2368_18_chip_info = {
.name = "ltc2368-18",
.chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+ .offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
};
static const struct ltc2378_chip_info ltc2369_18_chip_info = {
.name = "ltc2369-18",
.chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+ .offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1600 * HZ_PER_KHZ,
+ .tconv_ns = 412,
};
static const struct ltc2378_chip_info ltc2370_16_chip_info = {
.name = "ltc2370-16",
.chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+ .offload_chan = LTC2378_OFFLOAD_UNIPOLAR_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 2 * HZ_PER_MHZ,
+ .tconv_ns = 322,
};
static const struct ltc2378_chip_info ltc2376_16_chip_info = {
.name = "ltc2376-16",
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
};
static const struct ltc2378_chip_info ltc2376_18_chip_info = {
.name = "ltc2376-18",
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
};
static const struct ltc2378_chip_info ltc2376_20_chip_info = {
.name = "ltc2376-20",
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(20),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(20),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
};
static const struct ltc2378_chip_info ltc2377_16_chip_info = {
.name = "ltc2377-16",
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
};
static const struct ltc2378_chip_info ltc2377_18_chip_info = {
.name = "ltc2377-18",
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
};
static const struct ltc2378_chip_info ltc2377_20_chip_info = {
.name = "ltc2377-20",
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(20),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(20),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
};
static const struct ltc2378_chip_info ltc2378_16_chip_info = {
.name = "ltc2378-16",
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
};
static const struct ltc2378_chip_info ltc2378_18_chip_info = {
.name = "ltc2378-18",
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
};
static const struct ltc2378_chip_info ltc2378_20_chip_info = {
.name = "ltc2378-20",
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(20),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(20),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 675,
};
static const struct ltc2378_chip_info ltc2379_18_chip_info = {
.name = "ltc2379-18",
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1600 * HZ_PER_KHZ,
+ .tconv_ns = 412,
};
static const struct ltc2378_chip_info ltc2380_16_chip_info = {
.name = "ltc2380-16",
.chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+ .offload_chan = LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 2 * HZ_PER_MHZ,
+ .tconv_ns = 322,
};
static int ltc2378_convert_and_acquire(struct ltc2378_state *st)
@@ -260,7 +376,137 @@ static int ltc2378_read_raw(struct iio_dev *indio_dev,
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->cnv_Hz;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+static int ltc2378_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length, long mask)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = st->sample_freq_range;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * SPI offload wiring schema
+ *
+ * +-------------+ +-------------+
+ * | CNV |<-----+--| GPIO |
+ * | | +--| PWM0 |
+ * | | | |
+ * | | +--| PWM1 |
+ * | | | +-------------+
+ * | | +->| TRIGGER |
+ * | | | |
+ * | ADC | | SPI |
+ * | | | controller |
+ * | | | |
+ * | SDI |<--------| SDO |
+ * | SDO |-------->| SDI |
+ * | SCLK |<--------| SCLK |
+ * +-------------+ +-------------+
+ *
+ */
+static int ltc2378_update_conversion_rate(struct ltc2378_state *st, int freq_Hz)
+{
+ struct spi_offload_trigger_config config = st->offload_trigger_config;
+ unsigned int min_read_offset, offload_period_ns;
+ struct pwm_waveform cnv_wf = { };
+ u64 target = LTC2378_TCNV_HIGH_NS;
+ unsigned int count;
+ u64 offload_offset_ns;
+ int ret;
+
+ if (freq_Hz == 0)
+ return -EINVAL;
+
+ if (!in_range(freq_Hz, 1, st->info->max_sample_rate_Hz))
+ return -ERANGE;
+
+ /* Configure CNV PWM waveform */
+ cnv_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq_Hz);
+
+ /*
+ * Ensure CNV high time meets minimum requirement (20ns). The PWM
+ * hardware may round the duty cycle, so iterate until we get at least
+ * the minimum required high time (or reach a try count limit).
+ */
+ count = 100;
+ do {
+ cnv_wf.duty_length_ns = target;
+ ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf);
+ if (ret)
+ return ret;
+ target += 10; /* Increment by PWM duty cycle period */
+ } while (count-- && cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS);
+
+ /* Check the minimum CNV high time is met */
+ if (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS)
+ return -EDOM;
+
+ /*
+ * Configure SPI offload PWM trigger.
+ * The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL.
+ * Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns)
+ *
+ * Use the same period as CNV PWM to avoid timing issues.
+ * Convert back from period to frequency for the SPI offload API.
+ */
+ offload_period_ns = cnv_wf.period_length_ns;
+ config.periodic.frequency_hz = div_u64(HZ_PER_GHZ, offload_period_ns);
+ min_read_offset = LTC2378_TBUSYLH_NS + st->info->tconv_ns + LTC2378_TDSDOBUSYL_NS;
+ offload_offset_ns = min_read_offset;
+ count = 100;
+ do {
+ config.periodic.offset_ns = offload_offset_ns;
+ ret = spi_offload_trigger_validate(st->offload_trigger, &config);
+ if (ret)
+ return ret;
+ offload_offset_ns += 10;
+ } while (count-- && config.periodic.offset_ns < min_read_offset);
+
+ /* Check the minimum CNV to SCLK delay is met */
+ if (config.periodic.offset_ns < min_read_offset)
+ return -EDOM;
+
+ /* Check the PWM periods remain the same */
+ offload_period_ns = div64_u64(HZ_PER_GHZ, config.periodic.frequency_hz);
+ if (cnv_wf.period_length_ns != offload_period_ns)
+ return -EDOM;
+
+ st->offload_trigger_config = config;
+ st->cnv_wf = cnv_wf;
+ st->cnv_Hz = DIV_ROUND_CLOSEST_ULL(HZ_PER_GHZ, cnv_wf.period_length_ns);
+
+ return 0;
+}
+
+static int ltc2378_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ltc2378_update_conversion_rate(st, val);
default:
return -EINVAL;
}
@@ -270,6 +516,111 @@ static const struct iio_info ltc2378_iio_info = {
.read_raw = <c2378_read_raw,
};
+static const struct iio_info ltc2378_offload_iio_info = {
+ .read_raw = <c2378_read_raw,
+ .read_avail = <c2378_read_avail,
+ .write_raw = <c2378_write_raw,
+};
+
+static int ltc2378_offload_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = pwm_set_waveform_might_sleep(st->cnv_trigger, &st->cnv_wf, true);
+ if (ret)
+ return ret;
+
+ ret = spi_offload_trigger_enable(st->offload, st->offload_trigger,
+ &st->offload_trigger_config);
+ if (ret)
+ goto out_pwm_disable;
+
+ return 0;
+
+out_pwm_disable:
+ pwm_disable(st->cnv_trigger);
+ return ret;
+}
+
+static int ltc2378_offload_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+
+ spi_offload_trigger_disable(st->offload, st->offload_trigger);
+ pwm_disable(st->cnv_trigger);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ltc2378_offload_buffer_ops = {
+ .postenable = <c2378_offload_buffer_postenable,
+ .predisable = <c2378_offload_buffer_predisable,
+};
+
+static int ltc2378_prepare_offload_message(struct device *dev,
+ struct ltc2378_state *st)
+{
+ unsigned int resolution = st->info->offload_chan.scan_type.realbits;
+
+ st->offload_xfer.bits_per_word = resolution;
+ st->offload_xfer.len = spi_bpw_to_bytes(resolution);
+ st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
+
+ /* Initialize message with offload */
+ spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1);
+ st->offload_msg.offload = st->offload;
+
+ return devm_spi_optimize_message(dev, st->spi, &st->offload_msg);
+}
+
+static int ltc2378_spi_offload_setup(struct iio_dev *indio_dev,
+ struct ltc2378_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct dma_chan *rx_dma;
+
+ indio_dev->setup_ops = <c2378_offload_buffer_ops;
+
+ st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload,
+ SPI_OFFLOAD_TRIGGER_PERIODIC);
+ if (IS_ERR(st->offload_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->offload_trigger),
+ "failed to get offload trigger\n");
+
+ st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC;
+
+ rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload);
+ if (IS_ERR(rx_dma))
+ return dev_err_probe(dev, PTR_ERR(rx_dma), "failed to get offload RX DMA\n");
+
+ return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma,
+ IIO_BUFFER_DIRECTION_IN);
+}
+
+static int ltc2378_pwm_get(struct ltc2378_state *st)
+{
+ struct device *dev = &st->spi->dev;
+
+ st->cnv_trigger = devm_pwm_get(dev, NULL);
+ if (IS_ERR(st->cnv_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_trigger),
+ "failed to get cnv pwm\n");
+
+ /*
+ * Disable the PWM connected to CNV in case it was left running by
+ * something else.
+ */
+ pwm_disable(st->cnv_trigger);
+
+ return 0;
+}
+
+static const struct spi_offload_config ltc2378_offload_config = {
+ .capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
+ SPI_OFFLOAD_CAP_RX_STREAM_DMA,
+};
+
static int ltc2378_refin_setup(struct device *dev, struct ltc2378_state *st)
{
int ret;
@@ -334,7 +685,6 @@ static int ltc2378_probe(struct spi_device *spi)
return ret;
indio_dev->name = st->info->name;
- indio_dev->info = <c2378_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW);
@@ -342,8 +692,53 @@ static int ltc2378_probe(struct spi_device *spi)
return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
"failed to get CNV GPIO");
- indio_dev->channels = &st->info->chan;
- indio_dev->num_channels = 1;
+ st->offload = devm_spi_offload_get(dev, spi, <c2378_offload_config);
+ ret = PTR_ERR_OR_ZERO(st->offload);
+ /* Fall back to low speed usage when no SPI offload is available. */
+ if (ret == -ENODEV) {
+ indio_dev->info = <c2378_iio_info;
+ indio_dev->channels = &st->info->chan;
+ indio_dev->num_channels = 1;
+ } else if (ret) {
+ return dev_err_probe(dev, ret, "failed to get offload\n");
+ } else {
+ indio_dev->info = <c2378_offload_iio_info;
+ indio_dev->channels = &st->info->offload_chan;
+ indio_dev->num_channels = 1;
+ ret = ltc2378_spi_offload_setup(indio_dev, st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to setup SPI offload\n");
+
+ ret = ltc2378_pwm_get(st);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get PWM\n");
+
+ st->sample_freq_range[0] = 1; /* min */
+ st->sample_freq_range[1] = 1; /* step */
+ st->sample_freq_range[2] = st->info->max_sample_rate_Hz; /* max */
+
+ /*
+ * Start with a slower sampling rate so there is some room for
+ * adjusting the sample averaging and the sampling frequency
+ * without hitting the maximum conversion rate.
+ */
+ ret = ltc2378_update_conversion_rate(st, st->info->max_sample_rate_Hz >> 4);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set offload samp freq\n");
+
+ ret = ltc2378_prepare_offload_message(&spi->dev, st);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to optimize SPI message\n");
+
+ /*
+ * Set single-read transfer bits_per_word so the SPI subsystem
+ * rearranges data to CPU endianness, enabling us to reuse
+ * offload_chan specifications for single-shot reads.
+ */
+ st->xfer.bits_per_word = st->info->offload_chan.scan_type.realbits;
+ }
st->xfer.rx_buf = &st->scan.data;
st->xfer.len = spi_bpw_to_bytes(indio_dev->channels[0].scan_type.realbits);
@@ -414,3 +809,5 @@ module_spi_driver(ltc2378_driver);
MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
+MODULE_IMPORT_NS("SPI_OFFLOAD");
--
2.53.0
^ permalink raw reply related
* [PATCH v7 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
From: Marcelo Schmitt @ 2026-07-16 21:09 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, julianbraha, marcelo.schmitt1
In-Reply-To: <cover.1784235595.git.marcelo.schmitt@analog.com>
Support for LTC2378-20 and similar analog-to-digital converters.
Reviewed-by: David Lechner <dlechner@baylibre.com>
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
Change log v6 -> v7:
- Extended single-read critical section scope (holding mutex lock longer).
- Added comment about conversion wait time.
- When applicable, return early from regulator setup.
- Wrapped transfer buffer declaration comment closer to 80 characters.
Some bits related to the SPI transfer buffer are introduced early to minimize
the change diff of the followup offload enablement patch.
MAINTAINERS | 1 +
drivers/iio/adc/Kconfig | 12 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ltc2378.c | 416 ++++++++++++++++++++++++++++++++++++++
4 files changed, 430 insertions(+)
create mode 100644 drivers/iio/adc/ltc2378.c
diff --git a/MAINTAINERS b/MAINTAINERS
index f0ae26699c99..25821b771692 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15349,6 +15349,7 @@ L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
+F: drivers/iio/adc/ltc2378.c
LTC2664 IIO DAC DRIVER
M: Michael Hennerich <michael.hennerich@analog.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index d1b198cb8a80..220c6739a8f2 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -943,6 +943,18 @@ config LTC2309
This driver can also be built as a module. If so, the module will
be called ltc2309.
+config LTC2378
+ tristate "Analog Devices LTC2378 ADC driver"
+ depends on SPI
+ depends on REGULATOR || COMPILE_TEST
+ depends on GPIOLIB
+ help
+ Say yes here to build support for Analog Devices LTC2378-20 and
+ similar analog to digital converters.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2378.
+
config LTC2471
tristate "Linear Technology LTC2471 and LTC2473 ADC driver"
depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 0f90b75577ff..6058a8be6ccd 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
obj-$(CONFIG_LTC2309) += ltc2309.o
+obj-$(CONFIG_LTC2378) += ltc2378.o
obj-$(CONFIG_LTC2471) += ltc2471.o
obj-$(CONFIG_LTC2485) += ltc2485.o
obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c
new file mode 100644
index 000000000000..5600c820c7f4
--- /dev/null
+++ b/drivers/iio/adc/ltc2378.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Analog Devices LTC2378 ADC series driver
+ *
+ * Copyright (C) 2026 Analog Devices Inc.
+ * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/device-id/spi.h>
+#include <linux/device-id/of.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#define LTC2378_MAX_DATA_WAIT_US 4 /* max(TBUSYLH + TCONV + TDSDOBUSYL) */
+
+#define LTC2378_DIFF_CHANNEL(_sign, _real_bits, _storage_bits) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = 0, \
+ .scan_type = { \
+ .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \
+ IIO_SCAN_FORMAT_UNSIGNED_INT, \
+ .realbits = _real_bits, \
+ .storagebits = _storage_bits, \
+ .shift = _storage_bits - _real_bits, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define LTC2378_BIPOLAR_DIFF_CHANNEL(_real_bits) \
+ LTC2378_DIFF_CHANNEL(1, _real_bits, (((_real_bits) > 16) ? 32 : 16))
+
+#define LTC2378_UNIPOLAR_DIFF_CHANNEL(_real_bits) \
+ LTC2378_DIFF_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16))
+
+struct ltc2378_chip_info {
+ const char *name;
+ unsigned int internal_ref_uV;
+ struct iio_chan_spec chan;
+};
+
+struct ltc2378_state {
+ const struct ltc2378_chip_info *info;
+ struct gpio_desc *cnv_gpio;
+ struct spi_device *spi;
+ struct mutex lock; /* Protect data acquisition cycle */
+ int ref_uV;
+ struct spi_transfer xfer;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the transfer buffers
+ * to live in their own cache lines.
+ */
+ struct {
+ union {
+ __be16 sample_buf16_be;
+ __be32 sample_buf32_be;
+ u16 sample_buf16;
+ u32 sample_buf32;
+ } data;
+ aligned_s64 timestamp;
+ } scan __aligned(IIO_DMA_MINALIGN);
+};
+
+static const struct ltc2378_chip_info ltc2338_18_chip_info = {
+ .name = "ltc2338-18",
+ .internal_ref_uV = 2048000,
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+};
+
+static const struct ltc2378_chip_info ltc2364_16_chip_info = {
+ .name = "ltc2364-16",
+ .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+};
+
+static const struct ltc2378_chip_info ltc2364_18_chip_info = {
+ .name = "ltc2364-18",
+ .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+};
+
+static const struct ltc2378_chip_info ltc2367_16_chip_info = {
+ .name = "ltc2367-16",
+ .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+};
+
+static const struct ltc2378_chip_info ltc2367_18_chip_info = {
+ .name = "ltc2367-18",
+ .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+};
+
+static const struct ltc2378_chip_info ltc2368_16_chip_info = {
+ .name = "ltc2368-16",
+ .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+};
+
+static const struct ltc2378_chip_info ltc2368_18_chip_info = {
+ .name = "ltc2368-18",
+ .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+};
+
+static const struct ltc2378_chip_info ltc2369_18_chip_info = {
+ .name = "ltc2369-18",
+ .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(18),
+};
+
+static const struct ltc2378_chip_info ltc2370_16_chip_info = {
+ .name = "ltc2370-16",
+ .chan = LTC2378_UNIPOLAR_DIFF_CHANNEL(16),
+};
+
+static const struct ltc2378_chip_info ltc2376_16_chip_info = {
+ .name = "ltc2376-16",
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+};
+
+static const struct ltc2378_chip_info ltc2376_18_chip_info = {
+ .name = "ltc2376-18",
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+};
+
+static const struct ltc2378_chip_info ltc2376_20_chip_info = {
+ .name = "ltc2376-20",
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(20),
+};
+
+static const struct ltc2378_chip_info ltc2377_16_chip_info = {
+ .name = "ltc2377-16",
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+};
+
+static const struct ltc2378_chip_info ltc2377_18_chip_info = {
+ .name = "ltc2377-18",
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+};
+
+static const struct ltc2378_chip_info ltc2377_20_chip_info = {
+ .name = "ltc2377-20",
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(20),
+};
+
+static const struct ltc2378_chip_info ltc2378_16_chip_info = {
+ .name = "ltc2378-16",
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+};
+
+static const struct ltc2378_chip_info ltc2378_18_chip_info = {
+ .name = "ltc2378-18",
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+};
+
+static const struct ltc2378_chip_info ltc2378_20_chip_info = {
+ .name = "ltc2378-20",
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(20),
+};
+
+static const struct ltc2378_chip_info ltc2379_18_chip_info = {
+ .name = "ltc2379-18",
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(18),
+};
+
+static const struct ltc2378_chip_info ltc2380_16_chip_info = {
+ .name = "ltc2380-16",
+ .chan = LTC2378_BIPOLAR_DIFF_CHANNEL(16),
+};
+
+static int ltc2378_convert_and_acquire(struct ltc2378_state *st)
+{
+ int ret;
+
+ /* Cause a rising edge of CNV to initiate a new ADC conversion */
+ gpiod_set_value_cansleep(st->cnv_gpio, 1);
+ fsleep(LTC2378_MAX_DATA_WAIT_US);
+ ret = spi_sync_transfer(st->spi, &st->xfer, 1);
+ gpiod_set_value_cansleep(st->cnv_gpio, 0);
+
+ return ret;
+}
+
+static int ltc2378_channel_single_read(const struct iio_chan_spec *chan,
+ struct ltc2378_state *st, int *val)
+{
+ const struct iio_scan_type *scan_type = &chan->scan_type;
+ u32 sample;
+ int ret;
+
+ guard(mutex)(&st->lock);
+ ret = ltc2378_convert_and_acquire(st);
+ if (ret)
+ return ret;
+
+ if (chan->scan_type.endianness == IIO_BE) {
+ if (chan->scan_type.realbits > 16)
+ sample = be32_to_cpu(st->scan.data.sample_buf32_be);
+ else
+ sample = be16_to_cpu(st->scan.data.sample_buf16_be);
+ } else { /* IIO_CPU */
+ if (chan->scan_type.realbits > 16)
+ sample = st->scan.data.sample_buf32;
+ else
+ sample = st->scan.data.sample_buf16;
+ }
+
+ sample >>= chan->scan_type.shift;
+
+ if (scan_type->format == IIO_SCAN_FORMAT_SIGNED_INT)
+ *val = sign_extend32(sample, scan_type->realbits - 1);
+ else
+ *val = sample;
+
+ return 0;
+}
+
+static int ltc2378_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ ret = ltc2378_channel_single_read(chan, st, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->ref_uV / MILLI;
+ /*
+ * For all LTC2378-like devices, the amount of bits that express
+ * voltage magnitude depend on the polarity / output code format:
+ * - straight binary: All precision/resolution bits are used.
+ * - 2's complement: One of the precision bits is used for sign.
+ */
+ if (chan->scan_type.format == IIO_SCAN_FORMAT_SIGNED_INT)
+ *val2 = chan->scan_type.realbits - 1;
+ else
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ltc2378_iio_info = {
+ .read_raw = <c2378_read_raw,
+};
+
+static int ltc2378_refin_setup(struct device *dev, struct ltc2378_state *st)
+{
+ int ret;
+
+ /*
+ * The internal reference buffer amplifies both the internal reference
+ * and REFIN by a factor of 2.
+ */
+ ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+ if (ret == -ENODEV) { /* refin is optional */
+ st->ref_uV = st->info->internal_ref_uV * 2;
+ return 0;
+ } else if (ret < 0) {
+ return dev_err_probe(dev, ret, "failed to read refin regulator\n");
+ }
+
+ st->ref_uV = ret * 2;
+
+ return 0;
+}
+
+static int ltc2378_ref_setup(struct device *dev, struct ltc2378_state *st)
+{
+ int ret;
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "ref");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read ref regulator\n");
+
+ st->ref_uV = ret;
+
+ return 0;
+}
+
+static int ltc2378_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ltc2378_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ st->info = spi_get_device_match_data(spi);
+ if (!st->info)
+ return -EINVAL;
+
+ if (st->info->internal_ref_uV)
+ ret = ltc2378_refin_setup(dev, st);
+ else
+ ret = ltc2378_ref_setup(dev, st);
+ if (ret)
+ return ret;
+
+ indio_dev->name = st->info->name;
+ indio_dev->info = <c2378_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW);
+ if (IS_ERR(st->cnv_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
+ "failed to get CNV GPIO");
+
+ indio_dev->channels = &st->info->chan;
+ indio_dev->num_channels = 1;
+
+ st->xfer.rx_buf = &st->scan.data;
+ st->xfer.len = spi_bpw_to_bytes(indio_dev->channels[0].scan_type.realbits);
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ltc2378_of_match[] = {
+ { .compatible = "adi,ltc2338-18", .data = <c2338_18_chip_info },
+ { .compatible = "adi,ltc2364-16", .data = <c2364_16_chip_info },
+ { .compatible = "adi,ltc2364-18", .data = <c2364_18_chip_info },
+ { .compatible = "adi,ltc2367-16", .data = <c2367_16_chip_info },
+ { .compatible = "adi,ltc2367-18", .data = <c2367_18_chip_info },
+ { .compatible = "adi,ltc2368-16", .data = <c2368_16_chip_info },
+ { .compatible = "adi,ltc2368-18", .data = <c2368_18_chip_info },
+ { .compatible = "adi,ltc2369-18", .data = <c2369_18_chip_info },
+ { .compatible = "adi,ltc2370-16", .data = <c2370_16_chip_info },
+ { .compatible = "adi,ltc2376-16", .data = <c2376_16_chip_info },
+ { .compatible = "adi,ltc2376-18", .data = <c2376_18_chip_info },
+ { .compatible = "adi,ltc2376-20", .data = <c2376_20_chip_info },
+ { .compatible = "adi,ltc2377-16", .data = <c2377_16_chip_info },
+ { .compatible = "adi,ltc2377-18", .data = <c2377_18_chip_info },
+ { .compatible = "adi,ltc2377-20", .data = <c2377_20_chip_info },
+ { .compatible = "adi,ltc2378-16", .data = <c2378_16_chip_info },
+ { .compatible = "adi,ltc2378-18", .data = <c2378_18_chip_info },
+ { .compatible = "adi,ltc2378-20", .data = <c2378_20_chip_info },
+ { .compatible = "adi,ltc2379-18", .data = <c2379_18_chip_info },
+ { .compatible = "adi,ltc2380-16", .data = <c2380_16_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ltc2378_of_match);
+
+static const struct spi_device_id ltc2378_spi_id[] = {
+ { .name = "ltc2338-18", .driver_data = (kernel_ulong_t)<c2338_18_chip_info },
+ { .name = "ltc2364-16", .driver_data = (kernel_ulong_t)<c2364_16_chip_info },
+ { .name = "ltc2364-18", .driver_data = (kernel_ulong_t)<c2364_18_chip_info },
+ { .name = "ltc2367-16", .driver_data = (kernel_ulong_t)<c2367_16_chip_info },
+ { .name = "ltc2367-18", .driver_data = (kernel_ulong_t)<c2367_18_chip_info },
+ { .name = "ltc2368-16", .driver_data = (kernel_ulong_t)<c2368_16_chip_info },
+ { .name = "ltc2368-18", .driver_data = (kernel_ulong_t)<c2368_18_chip_info },
+ { .name = "ltc2369-18", .driver_data = (kernel_ulong_t)<c2369_18_chip_info },
+ { .name = "ltc2370-16", .driver_data = (kernel_ulong_t)<c2370_16_chip_info },
+ { .name = "ltc2376-16", .driver_data = (kernel_ulong_t)<c2376_16_chip_info },
+ { .name = "ltc2376-18", .driver_data = (kernel_ulong_t)<c2376_18_chip_info },
+ { .name = "ltc2376-20", .driver_data = (kernel_ulong_t)<c2376_20_chip_info },
+ { .name = "ltc2377-16", .driver_data = (kernel_ulong_t)<c2377_16_chip_info },
+ { .name = "ltc2377-18", .driver_data = (kernel_ulong_t)<c2377_18_chip_info },
+ { .name = "ltc2377-20", .driver_data = (kernel_ulong_t)<c2377_20_chip_info },
+ { .name = "ltc2378-16", .driver_data = (kernel_ulong_t)<c2378_16_chip_info },
+ { .name = "ltc2378-18", .driver_data = (kernel_ulong_t)<c2378_18_chip_info },
+ { .name = "ltc2378-20", .driver_data = (kernel_ulong_t)<c2378_20_chip_info },
+ { .name = "ltc2379-18", .driver_data = (kernel_ulong_t)<c2379_18_chip_info },
+ { .name = "ltc2380-16", .driver_data = (kernel_ulong_t)<c2380_16_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ltc2378_spi_id);
+
+static struct spi_driver ltc2378_driver = {
+ .driver = {
+ .name = "ltc2378",
+ .of_match_table = ltc2378_of_match
+ },
+ .probe = ltc2378_probe,
+ .id_table = ltc2378_spi_id,
+};
+module_spi_driver(ltc2378_driver);
+
+MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
+MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related
* [PATCH v7 0/4] iio: adc: Add support for LTC2378 and similar ADCs
From: Marcelo Schmitt @ 2026-07-16 21:08 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, julianbraha, marcelo.schmitt1
This patch series adds support for LTC2378 and similar low noise, low power,
high speed, successive approximation register (SAR) ADCs. These ADCs are similar
among each other, varying mainly on the amount of precision bits, maximum sample
rate, and input configuration (either fully differential or pseudo-differential).
Patch 1 adds device tree documentation for LTC2378.
Patch 2 enables single-shot sample read with a GPIO connected to the LTC2378 CNV pin.
Patch 3 enables high-speed data captures with SPI offloading.
The setup is similar to AD4030, with a specialized PWM generator being used both
for SPI offload triggering and conversion start signaling.
Patch 4 enables running buffered data captures without SPI offloading.
Even though these parts are somewhat similar to AD4000, the wiring configuration
for LTC parts is different as well as the available HDL for high speed sample
rate mode. Because of that, I propose creating a new device driver for
supporting LTC2378-like devices.
Specifications can be found at:
https://www.analog.com/media/en/technical-documentation/data-sheets/233818fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236416fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236418f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236716fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236718f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236816f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236818f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236918fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237016fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237616fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237618fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237620fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237716fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237718fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237720fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237816fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237818fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237820fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237918fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/238016fb.pdf
Previous submissions:
v6: https://lore.kernel.org/linux-iio/cover.1783629101.git.marcelo.schmitt@analog.com/
v5: https://lore.kernel.org/linux-iio/cover.1783028033.git.marcelo.schmitt@analog.com/
v4: https://lore.kernel.org/linux-iio/cover.1782397418.git.marcelo.schmitt@analog.com/
v3: https://lore.kernel.org/linux-iio/cover.1781661028.git.marcelo.schmitt@analog.com/
v2: https://lore.kernel.org/linux-iio/cover.1779976379.git.marcelo.schmitt@analog.com/
v1: https://lore.kernel.org/linux-iio/cover.1779117444.git.marcelo.schmitt1@gmail.com/
Change log v6 -> v7:
[DT]
No changes in device tree doc.
[IIO]
- Extended single-read critical section scope (holding mutex lock longer).
- Added comment about conversion wait time.
- Now keeping validated offload configurations if conversion rate update fails.
- Imported SPI_OFFLOAD name space.
- Replaced DIV_ROUND_UP() by div_u64() to mitigate config check fail due to
different integer rounding.
- When applicable, return early from regulator setup.
- Separate macros for offload and non-offload channels.
- Moved ltc2378_prepare_offload_message() downwards in source code.
- Wrapped transfer buffer declaration comment closer to 80 characters.
- Picked up review tags.
With best regards,
Marcelo
Marcelo Schmitt (4):
dt-bindings: iio: adc: Add ltc2378
iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
iio: adc: ltc2378: Enable high-speed data capture
iio: adc: ltc2378: Enable triggered buffer data capture
.../bindings/iio/adc/adi,ltc2378.yaml | 170 ++++
MAINTAINERS | 8 +
drivers/iio/adc/Kconfig | 19 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ltc2378.c | 842 ++++++++++++++++++
5 files changed, 1040 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
create mode 100644 drivers/iio/adc/ltc2378.c
base-commit: aa58ecc73466d0cb8c418de98e2225490bf600e3
--
2.53.0
^ permalink raw reply
* [PATCH v7 1/4] dt-bindings: iio: adc: Add ltc2378
From: Marcelo Schmitt @ 2026-07-16 21:09 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, julianbraha, marcelo.schmitt1, Conor Dooley
In-Reply-To: <cover.1784235595.git.marcelo.schmitt@analog.com>
Document how to describe LTC2378-20 and similar ADCs in device tree.
Reviewed-by: David Lechner <dlechner@baylibre.com>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
Change log v6 -> v7:
No changes in v7.
.../bindings/iio/adc/adi,ltc2378.yaml | 170 ++++++++++++++++++
MAINTAINERS | 7 +
2 files changed, 177 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
new file mode 100644
index 000000000000..33de1dc4c256
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
@@ -0,0 +1,170 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ltc2378.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices LTC2378 and similar Analog to Digital Converters
+
+maintainers:
+ - Marcelo Schmitt <marcelo.schmitt@analog.com>
+
+description: |
+ Analog Devices LTC2378 series of ADCs.
+ Specifications can be found at:
+ https://www.analog.com/media/en/technical-documentation/data-sheets/233818fa.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/236416fa.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/236418f.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/236716fa.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/236718f.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/236816f.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/236818f.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/236918fa.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/237016fa.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/237616fa.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/237618fa.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/237620fb.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/237716fa.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/237718fa.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/237720fb.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/237816fa.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/237818fa.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/237820fb.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/237918fb.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/238016fb.pdf
+
+$ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+ compatible:
+ oneOf:
+ # Single compatible string match.
+ - enum:
+ - adi,ltc2338-18
+ - adi,ltc2364-16
+ - adi,ltc2364-18
+ - adi,ltc2376-16
+ - adi,ltc2376-18
+ - adi,ltc2376-20
+
+ # Low sample rate fallback for 16-bit unipolar sensors.
+ - items:
+ - enum:
+ - adi,ltc2370-16 # 2 MSPS
+ - adi,ltc2368-16 # 1 MSPS
+ - adi,ltc2367-16 # 500 kSPS
+ - const: adi,ltc2364-16 # fallback (250 kSPS)
+
+ # Low sample rate fallback for 18-bit unipolar sensors.
+ - items:
+ - enum:
+ - adi,ltc2369-18 # 1.6 MSPS
+ - adi,ltc2368-18 # 1 MSPS
+ - adi,ltc2367-18 # 500 kSPS
+ - const: adi,ltc2364-18 # fallback (250 kSPS)
+
+ # Low sample rate fallback for 16-bit bipolar sensors.
+ - items:
+ - enum:
+ - adi,ltc2380-16 # 2 MSPS
+ - adi,ltc2378-16 # 1 MSPS
+ - adi,ltc2377-16 # 500 kSPS
+ - const: adi,ltc2376-16 # fallback (250 kSPS)
+
+ # Low sample rate fallback for 18-bit bipolar sensors.
+ - items:
+ - enum:
+ - adi,ltc2379-18 # 1.6 MSPS
+ - adi,ltc2378-18 # 1 MSPS
+ - adi,ltc2377-18 # 500 kSPS
+ - const: adi,ltc2376-18 # fallback (250 kSPS)
+
+ # Low sample rate fallback for 20-bit bipolar sensors.
+ - items:
+ - enum:
+ - adi,ltc2378-20 # 1 MSPS
+ - adi,ltc2377-20 # 500 kSPS
+ - const: adi,ltc2376-20 # fallback (250 kSPS)
+
+ reg:
+ maxItems: 1
+
+ spi-max-frequency:
+ maximum: 100000000
+
+ vdd-supply:
+ description: A 2.5V supply that powers the chip (VDD).
+
+ ovdd-supply:
+ description:
+ A 1.71V to 5.25V supply that sets the logic level for digital interface.
+
+ ref-supply:
+ description:
+ Voltage reference input that determines the scale of ADC conversions.
+
+ refin-supply:
+ description:
+ Alternative voltage reference input.
+
+ cnv-gpios:
+ description:
+ When provided, this property indicates the GPIO that is connected to the
+ CNV pin.
+ maxItems: 1
+
+ pwms:
+ description: PWM signal connected to the CNV pin.
+ maxItems: 1
+
+ interrupts:
+ description:
+ Interrupt for signaling the completion of conversion results. The active
+ low signal provided on the BUSY pin asserts when ADC conversions finish.
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+ - ovdd-supply
+
+allOf:
+ # Except for LTC2338, all designs require a voltage reference input
+ - if:
+ properties:
+ compatible:
+ not:
+ contains:
+ enum:
+ - adi,ltc2338-18
+ then:
+ required:
+ - ref-supply
+ properties:
+ refin-supply: false
+ else:
+ properties:
+ ref-supply: false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ adc@0 {
+ compatible = "adi,ltc2378-20", "adi,ltc2376-20";
+ reg = <0>;
+ spi-max-frequency = <71000000>;
+ vdd-supply = <&supply_2_5V>;
+ ovdd-supply = <&supply_3_3V>;
+ ref-supply = <&supply_5V>;
+ cnv-gpios = <&gpio0 88 GPIO_ACTIVE_HIGH>;
+ interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
+ interrupt-parent = <&gpio>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 2b1ec46c5919..f0ae26699c99 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15343,6 +15343,13 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml
F: drivers/iio/dac/ltc1660.c
+LTC2378 IIO ADC DRIVER
+M: Marcelo Schmitt <marcelo.schmitt@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
+
LTC2664 IIO DAC DRIVER
M: Michael Hennerich <michael.hennerich@analog.com>
M: Kim Seer Paller <kimseer.paller@analog.com>
--
2.53.0
^ permalink raw reply related
* Re: (subset) [PATCH v3 0/3] nvmem: add support for the MediaTek mt6323 PMIC
From: Srinivas Kandagatla @ 2026-07-16 21:07 UTC (permalink / raw)
To: Sen Chu, Sean Wang, Macpaul Lin, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Roman Vivchar
Cc: Andy Shevchenko, linux-pm, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, Ben Grisdale, Conor Dooley
In-Reply-To: <20260713-mt6323-nvmem-v3-0-205c39267a24@protonmail.com>
On Mon, 13 Jul 2026 13:11:34 +0300, Roman Vivchar wrote:
> This series adds support for the EFUSE found on the MediaTek mt6323 PMIC.
>
> The previous version of the series for all AUXADC, EFUSE and thermal
> drivers was split after Krzysztof's comment [1].
>
> Tested on the MediaTek mt6572 and mt8163 SoCs (Ben), both paired with a
> mt6323.
>
> [...]
Applied, thanks!
[1/3] dt-bindings: mfd: mediatek: mt6397: add mt6323 PMIC EFUSE
commit: 8852dfee93d3f8a54399060052e68792ba35f1b1
[2/3] nvmem: add mt6323 PMIC EFUSE driver
commit: b3c090960d86901bd4e41447744fd532d6e1d7f9
Best regards,
--
Srinivas Kandagatla <srini@kernel.org>
^ permalink raw reply
* Re: [PATCH v5 02/16] device property: Add fwnode_graph_get_next_port_endpoint()
From: Andy Shevchenko @ 2026-07-16 21:04 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: sashiko-reviews, devicetree, driver-core, Bartosz Golaszewski
In-Reply-To: <CAGXv+5Ffsrc9r6d_RrgL1mBW1Zmt1RxCrMCjaD7vuF3+EaeAqw@mail.gmail.com>
On Thu, Jul 16, 2026 at 04:52:27PM +0800, Chen-Yu Tsai wrote:
> On Wed, Jul 15, 2026 at 5:09 PM <sashiko-bot@kernel.org> wrote:
...
> > > +struct fwnode_handle *fwnode_graph_get_next_port_endpoint(const struct fwnode_handle *port,
> > > + struct fwnode_handle *prev)
> > > +{
> > > + do {
> > > + prev = fwnode_get_next_child_node(port, prev);
> >
> > [Severity: High]
> > This is a pre-existing issue, but iterating over endpoints using
> > fwnode_get_next_child_node() can cause an infinite loop and a use-after-free
> > if a device has secondary fwnodes attached.
> >
> > If a device port has both primary children and a secondary fwnode, the primary
> > ops finish and drop the reference to the last primary child, returning NULL.
> > Then, fwnode_get_next_child_node() incorrectly passes this same dropped child
> > pointer to the secondary ops:
> >
> > drivers/base/property.c:fwnode_get_next_child_node() {
> > /* Try to find a child in primary fwnode */
> > next = fwnode_call_ptr_op(fwnode, get_next_child_node, child);
> > if (next)
> > return next;
> >
> > /* When no more children in primary, continue with secondary */
> > return fwnode_call_ptr_op(fwnode->secondary, get_next_child_node, child);
> > }
> >
> > This can result in a double-put. Furthermore, when traversing secondary
> > children, the primary ops don't recognize the software node, treat it as NULL,
> > and return the first primary child again, creating an infinite loop.
Yeah, it has to be implemented in a way how fwnode_graph_get_next_endpoint() is done.
> > [Severity: Medium]
> > Does this fail to unconditionally put the prev reference if the provided port
> > is invalid?
> >
> > If a caller passes a valid prev node but port is NULL or an error pointer,
> > fwnode_get_next_child_node() returns early without dropping prev:
> >
> > drivers/base/property.c:fwnode_get_next_child_node() {
> > struct fwnode_handle *next;
> >
> > if (IS_ERR_OR_NULL(fwnode))
> > return NULL;
> > ...
> > }
> >
> > Could this lead to a reference count leak, violating the documented API
> > contract that prev is unconditionally put?
>
> This seems like what the other fwnode_get_next_*() functions are doing.
> I'd say this is a pre-existing issue as well.
See this series (not yet applied, you even have chance to review it!):
20260611203537.1786399-1-andriy.shevchenko@linux.intel.com
> > > + if (fwnode_name_eq(prev, "endpoint"))
> >
> > [Severity: High]
> > Does hardcoding this string match break the fwnode graph abstraction for
> > non-DeviceTree platforms?
> >
> > By strictly matching the "endpoint" prefix, this bypasses the provider-specific
> > fwnode_operations, which could silently ignore valid ACPI and software node
> > endpoints that don't follow this exact naming scheme. Shouldn't this rely on
> > the backend-specific graph_get_next_endpoint operations instead?
>
> From drivers/acpi/property.c it seems that ACPI graphs follow the same
> structure. I don't have visibility into ACPI implementations though.
Sashiko might be right. ACPI has device and data nodes, for device nodes the
name will be FourCC, so never longer than 4 characters. For data nodes, it
takes their names, which are arbitrary strings and seems should follow the given
schema. You need Sakari Ailus to review this patch.
> We also have the following in include/linux/fwnode.h:
>
> #define SWNODE_GRAPH_PORT_NAME_FMT "port@%u"
> #define SWNODE_GRAPH_ENDPOINT_NAME_FMT "endpoint@%u"
>
> So this should not be a problem.
>
> > > + break;
> > > + } while (prev);
> > > +
> > > + return prev;
> > > +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v2 4/4] dt-bindings: arm: qcom-soc: Allow WSA88xx speaker compatible
From: Rob Herring (Arm) @ 2026-07-16 20:59 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Bjorn Andersson, Conor Dooley, Krzysztof Kozlowski, linux-arm-msm,
devicetree, linux-kernel, Konrad Dybcio
In-Reply-To: <20260707-dt-bindings-qcom-soc-naming-v2-4-1b7d695be2e1@oss.qualcomm.com>
On Tue, 07 Jul 2026 15:36:02 +0200, Krzysztof Kozlowski wrote:
> One selects in this schema captures "^qcom,.*sa[0-9]+.*$" compatibles,
> because there are SAxxxx Qualcomm SoCs. Unfortunately there is also
> WSA8810 and WSA8855 I2C speakers, so they need listing too.
>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
>
> ---
>
> https://lore.kernel.org/all/20250522-rb2_audio_v3-v3-3-9eeb08cab9dc@linaro.org/
> https://lore.kernel.org/all/20260701135913.1641328-2-prasad.kumpatla@oss.qualcomm.com/
> ---
> Documentation/devicetree/bindings/arm/qcom-soc.yaml | 4 ++++
> 1 file changed, 4 insertions(+)
>
Acked-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH v2 2/4] dt-bindings: arm: qcom-soc: Document more of existing legacy style compatibles
From: Rob Herring (Arm) @ 2026-07-16 20:59 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio, linux-arm-msm,
Bjorn Andersson, devicetree, linux-kernel
In-Reply-To: <20260707-dt-bindings-qcom-soc-naming-v2-2-1b7d695be2e1@oss.qualcomm.com>
On Tue, 07 Jul 2026 15:36:00 +0200, Krzysztof Kozlowski wrote:
> The schema misses several old style compatibles with SoC component
> trailing in the compatible (qcom,IP-SoC):
> - qcom,apss-wdt-x1e80100
> - qcom,apss-wdt-xxx and qcom,scm-xxx for SoC with code names
> - qcom,kpss-gcc-xxx
> - qcom,rpmcc-xxx
> - qcom,tcsr-xxx
> - qcom,usb-hs-phy-xxx
>
> It also missed qcom,ipq806x-ahci.
>
> None of these were flagged by schema, because they were used with
> fallbacks and the schema checks only for single compatibles.
> Nevertheless document them for complete picture and for future change,
> which will apply the schema to fallbacks as well.
>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> ---
> Documentation/devicetree/bindings/arm/qcom-soc.yaml | 17 ++++++++++++++---
> 1 file changed, 14 insertions(+), 3 deletions(-)
>
Acked-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH v2 1/4] dt-bindings: arm: qcom-soc: Include Eliza, Kaanapali and others in SoC names
From: Rob Herring (Arm) @ 2026-07-16 20:58 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: linux-kernel, Krzysztof Kozlowski, linux-arm-msm, devicetree,
Konrad Dybcio, Bjorn Andersson, Conor Dooley
In-Reply-To: <20260707-dt-bindings-qcom-soc-naming-v2-1-1b7d695be2e1@oss.qualcomm.com>
On Tue, 07 Jul 2026 15:35:59 +0200, Krzysztof Kozlowski wrote:
> Grow the schema checking for proper SoC compatible naming style with
> recently upstreamed new Qualcomm SoCs: Eliza, Kaanapali, Hawi, Mahua,
> Maili, Nord and Shikra. The list covers only SoC IP blocks, thus no
> Hamoa or Talos in the names, because these are codenames but not used
> for SoC IP block naming.
>
> Since switching from model numbers to codenames, this list with explicit
> codenames will have to grow and list them all in order for the schema to
> work. It feels like a churn, but the compatible naming is still mess,
> for example, the schema pci/qcom,pcie-x1e80100.yaml with a legacy naming
> vendor,IP-SoC (qcom,pcie-x1e80100) received a new compatible with new
> style (qcom,glymur-pcie).
>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> ---
> Documentation/devicetree/bindings/arm/qcom-soc.yaml | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
Acked-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH 2/3] ASoC: dt-bindings: add the Qualcomm WCD9378 audio codec
From: Rob Herring @ 2026-07-16 20:54 UTC (permalink / raw)
To: Jorijn van der Graaf
Cc: Mark Brown, Srinivas Kandagatla, Liam Girdwood,
Krzysztof Kozlowski, Luca Weiss, Conor Dooley, Jaroslav Kysela,
Takashi Iwai, Mohammad Rafi Shaik, linux-sound, devicetree,
linux-arm-msm, linux-kernel
In-Reply-To: <20260706192229.144137-3-jorijnvdgraaf@catcrafts.net>
On Mon, Jul 06, 2026 at 09:22:28PM +0200, Jorijn van der Graaf wrote:
> The Qualcomm WCD9378 is a standalone audio codec IC found on SM7635
> boards such as the Fairphone 6. Like the WCD937x/938x/939x codecs it
> presents RX and TX SoundWire slave devices controlled by a common
> parent node, so the parent binding references qcom,wcd93xx-common.yaml;
> unlike those codecs it has three ADCs and three mic bias supplies
> rather than four, hence qcom,micbias4-microvolt is rejected.
>
> The slave devices enumerate with manufacturer ID 0x0217 and part ID
> 0x0110, hence the sdw20217011000 compatible. The TX slave carries five
> device ports (ADC1, ADC2, ADC3, DMIC0/1 plus MBHC, DMIC2-5) and the RX
> slave the usual five (HPH, CLSH, COMP, LO, DSD).
>
> The -codec suffix in the parent compatible follows the existing
> qcom,wcd93xx/pm4125 family compatibles and matches the compatible
> shipped by production devicetrees for this chip.
>
> Assisted-by: Claude:claude-fable-5
> Signed-off-by: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
> ---
> .../bindings/sound/qcom,wcd9378-codec.yaml | 76 +++++++++++
> .../bindings/sound/qcom,wcd9378-sdw.yaml | 122 ++++++++++++++++++
> 2 files changed, 198 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/sound/qcom,wcd9378-codec.yaml
> create mode 100644 Documentation/devicetree/bindings/sound/qcom,wcd9378-sdw.yaml
>
> diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd9378-codec.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd9378-codec.yaml
> new file mode 100644
> index 000000000000..32554541b279
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/qcom,wcd9378-codec.yaml
> @@ -0,0 +1,76 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/sound/qcom,wcd9378-codec.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm WCD9378 Audio Codec
> +
> +maintainers:
> + - Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
> +
> +description:
> + The Qualcomm WCD9378 is a standalone Hi-Fi audio codec IC with three
> + ADCs, three mic bias supplies, headphone/earpiece/line outputs and
> + MBHC. It has RX and TX SoundWire slave devices; the control registers
> + live in the SDCA control address space accessed through the TX slave.
> +
> +allOf:
> + - $ref: dai-common.yaml#
> + - $ref: qcom,wcd93xx-common.yaml#
> +
> +properties:
> + compatible:
> + const: qcom,wcd9378-codec
> +
> + qcom,micbias4-microvolt: false
> +
> +required:
> + - compatible
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> + audio-codec {
> + compatible = "qcom,wcd9378-codec";
> + reset-gpios = <&tlmm 162 GPIO_ACTIVE_LOW>;
> + vdd-buck-supply = <&vreg_l8b>;
> + vdd-rxtx-supply = <&vreg_l7b>;
> + vdd-io-supply = <&vreg_l7b>;
> + vdd-mic-bias-supply = <&vreg_bob>;
> + qcom,micbias1-microvolt = <1800000>;
> + qcom,micbias2-microvolt = <1800000>;
> + qcom,micbias3-microvolt = <1800000>;
> + qcom,rx-device = <&wcd9378_rx>;
> + qcom,tx-device = <&wcd9378_tx>;
> + #sound-dai-cells = <1>;
> + };
> +
> + /* ... */
> +
> + soundwire@3210000 {
> + reg = <0x03210000 0x2000>;
> + #address-cells = <2>;
> + #size-cells = <0>;
> +
> + wcd9378_rx: audio-codec@0,4 {
> + compatible = "sdw20217011000";
> + reg = <0 4>;
> + qcom,rx-port-mapping = <1 2 3 4 5>;
> + };
> + };
> +
> + soundwire@33b0000 {
> + reg = <0x033b0000 0x2000>;
> + #address-cells = <2>;
> + #size-cells = <0>;
> +
> + wcd9378_tx: audio-codec@0,3 {
> + compatible = "sdw20217011000";
> + reg = <0 3>;
> + qcom,tx-port-mapping = <1 1 1 2 3>;
> + };
> + };
Drop these 2. They are just duplicated from qcom,wcd9378-sdw.yaml.
Rob
^ permalink raw reply
* Re: [PATCH v2 4/5] riscv: dts: thead: Add IO labels for the IO expansion on I2C1
From: Drew Fustini @ 2026-07-16 20:52 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Guo Ren, Fu Wei, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
MoeLeak, Paul Walmsley, Palmer Dabbelt, Albert Ou, linux-riscv,
devicetree, linux-kernel, Icenowy Zheng, Emil Renner Berthing
In-Reply-To: <20260714074515.1959352-5-zhengxingda@iscas.ac.cn>
On Tue, Jul 14, 2026 at 03:45:14PM +0800, Icenowy Zheng wrote:
> From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
>
> Add labels for GPIOs on the IO expansion chip on the I2C1 bus.
>
> Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
> [Icenowy: Splitted off the patch adding all 3 IO expansions]
> Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
> ---
> arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts
> index 74b120c6b063a..78f5979a56c14 100644
> --- a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts
> +++ b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts
> @@ -128,6 +128,14 @@ ioexp2: gpio@18 {
> reg = <0x18>;
> gpio-controller;
> #gpio-cells = <2>;
> + gpio-line-names = "",
> + "cam0_reset",
> + "cam1_reset",
> + "cam2_reset",
> + "wl_host_wake",
I am not sure the line name really matters all that, but it seems that
pin 4 is also used as a reset gpio:
wifi_pwrseq: wifi-pwrseq {
compatible = "mmc-pwrseq-simple";
reset-gpios = <&ioexp2 4 GPIO_ACTIVE_LOW>;
post-power-on-delay-ms = <200>;
};
Did you notice that too?
Thanks,
Drew
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox