* [PATCH V9 3/4] iio: adc: Add support for QCOM PMIC5 Gen3 ADC
From: Jishnu Prakash @ 2026-01-28 11:24 UTC (permalink / raw)
To: jic23, robh, krzk+dt, conor+dt, agross, andersson, lumag,
dmitry.baryshkov, konradybcio, daniel.lezcano, sboyd, amitk,
thara.gopinath, lee, rafael, subbaraman.narayanamurthy,
david.collins, anjelique.melendez, kamal.wadhwa
Cc: rui.zhang, lukasz.luba, devicetree, linux-arm-msm, linux-iio,
linux-kernel, linux-pm, cros-qcom-dts-watchers, jishnu.prakash,
quic_kotarake, neil.armstrong, stephan.gerhold
In-Reply-To: <20260128112420.695518-1-jishnu.prakash@oss.qualcomm.com>
The ADC architecture on PMIC5 Gen3 is similar to that on PMIC5 Gen2,
with all SW communication to ADC going through PMK8550 which
communicates with other PMICs through PBS.
One major difference is that the register interface used here is that
of an SDAM (Shared Direct Access Memory) peripheral present on PMK8550.
There may be more than one SDAM used for ADC5 Gen3 and each has eight
channels, which may be used for either immediate reads (same functionality
as previous PMIC5 and PMIC5 Gen2 ADC peripherals) or recurring measurements
(same as ADC_TM functionality).
By convention, we reserve the first channel of the first SDAM for all
immediate reads and use the remaining channels across all SDAMs for
ADC_TM monitoring functionality.
Add support for PMIC5 Gen3 ADC driver for immediate read functionality.
ADC_TM is implemented as an auxiliary thermal driver under this ADC
driver.
Signed-off-by: Jishnu Prakash <jishnu.prakash@oss.qualcomm.com>
---
Changes since v8:
- Dropped the common module (drivers/iio/adc/qcom-adc5-gen3-common.c) and moved
all of its contents to drivers/iio/adc/qcom-spmi-adc5-gen3.c as suggested by Dmitry.
- Made following changes to address Dmitry's comment to use module_auxiliary_driver()
in auxiliary driver patch, by simplifying auxiliary device structures:
- Added function pointer for TM interrupt handler callback under struct adc5_chip
(to be called in case of TM interrupt on first SDAM), to replace the
tm_event_notify() callback.
- Add new exported function (adc5_gen3_register_tm_event_notifier()) to be called
by TM auxiliary driver in its probe to initialize the above callback function.
- Updated adc5_gen3_isr() to call this TM callback function instead of
tm_event_notify() callback from the wrapper struct adc_tm5_auxiliary_drv.
- Completely dropped the above wrapper struct definition.
- Made following changes to address Jonathan's comments:
- Updated header files included in drivers/iio/adc/qcom-spmi-adc5-gen3.c and
include/linux/iio/adc/qcom-adc5-gen3-common.h to follow IWYU (include-what-you-use)
principles.
- Dropped comment inside adc5_chip struct definition describing mutex lock.
- Dropped ADC5_GEN3_TEMP_ALARM_LITE channel as it had some inaccuracy issue, which
is being debugged internally. Will add it in a separate patch along with channel user.
- Replaced dev_err() with dev_err_probe() in adc5_get_fw_data.
Changes since v7:
- Addressed following comments from Jonathan:
- Included regmap header file in drivers/iio/adc/qcom-adc5-gen3-common.c.
- Increased comment wrap length in adc5_gen3_configure() and
struct adc5_chip definition.
- Updated error checks in adc5_gen3_isr() to remove NULL check for
adrv_tm and keep (!adrv_tm->tm_event_notify) error check alone
within if() condition.
- Removed sid initialization in adc5_gen3_get_fw_channel_data()
- Added definitions for ADC channel macros used in adc5_gen3_chans_pmic[]
in include/linux/iio/adc/qcom-adc5-gen3-common.h instead of
include/dt-bindings/iio/adc/qcom,spmi-vadc.h, as this latter file
will be moved out of bindings folder in a separate change. Also
removed its inclusion in drivers/iio/adc/qcom-spmi-adc5-gen3.c.
- Cleaned up local variable declarations in adc5_gen3_isr() and
adc5_gen3_get_fw_channel_data() and added local variable for
adc->dev in adc5_get_fw_data().
- Fixed error message after platform_get_irq() call in adc5_gen3_probe()
to print IRQ number correctly.
- Added a check in adc5_gen3_get_fw_channel_data() to exit with error
if ADC channel value obtained from `reg` channel property is not
among the supported ones in the array adc5_gen3_chans_pmic[].
- Corrected the value used in checking for max valid ADC channel value,
in adc5_gen3_get_fw_channel_data().
Changes since v6:
- Addressed following comments from Jonathan:
- Moved functions exported in drivers/iio/adc/qcom-adc5-gen3-common.c
into namespace "QCOM_SPMI_ADC5_GEN3".
- Increased line wrap length for comments.
- Added local variable for adc->dev in adc5_gen3_isr().
- Shifted debug print showing IRQ status registers in adc5_gen3_isr()
to before tm_status[] check.
- Fixed indentation and brackets in adc5_gen3_get_fw_channel_data().
- Cleaned up array formatting in adc5_gen3_data_pmic struct.
- Used scoped variant of device_for_each_child_node() in adc5_get_fw_data().
- Updated auxiliary device cleanup handling to fix memory freeing
issues, by adding empty auxiliary device release function.
- Used devm_mutex_init() in adc5_gen3_probe().
- Updated virtual channel macro name from V_CHAN to ADC5_GEN3_V_CHAN.
- Set IIO device name to "spmi-adc5-gen3".
- Added __acquires and __releases macros for exported mutex lock
and unlock functions in drivers/iio/adc/qcom-spmi-adc5-gen3.c.
- Added error check to fail probe in case adding auxiliary TM device fails.
- Replaced 2025 copyright in newly added files with yearless copyright,
following new internal guidelines.
Changes since v5:
- Addressed following comments from Jonathan:
- Corrected line wrap length in Kconfig and driver files.
- Replaced usleep_range() with fsleep() in adc5_gen3_poll_wait_hs()
- Corrected all files to follow kernel-doc formatting fully.
- Removed IIO_CHAN_INFO_RAW case in adc5_gen3_read_raw()
- Cleaned up formatting in adc5_gen3_data_pmic struct and in other
struct definitions.
- Updated adc5_gen3_add_aux_tm_device() to keep errors alone out of line.
- Split mutex function exported to ADC_TM driver into separate functions
for acquiring and releasing mutex.
- Removed num_sdams member from struct adc5_chip.
- Fixed dev_err_probe() print in adc5_gen3_probe().
- Updated logic for acquiring IRQ numbers to account for removing
"interrupt-names" DT property.
- Included bitfield.h header file in drivers/iio/adc/qcom-adc5-gen3-common.c
to fix kernel bot error.
Changes since v4:
- Moved out common funtions from newly added .h file into a separate .c
file to avoid duplicating them and updated interrupt name, as suggested
by Krzysztof. Updated namespace export symbol statement to have a string
as second argument to follow framework change.
Changes since v3:
- Split out TM functionality into auxiliary driver in separate patch and
added required changes in main driver, as suggested by Dmitry.
- Addressed other reviewer comments in main driver patch.
Changes since v1:
- Removed datashet_name usage and implemented read_label() function
- In probe, updated channel property in iio_chan_spec from individual
channel to virtual channel and set indexed property to 1, due to the
above change.
- Updated order of checks in ISR
- Removed the driver remove callback and replaced with callbacks in a
devm_add_action call in probe.
- Addressed other comments from reviewers.
drivers/iio/adc/Kconfig | 26 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/qcom-spmi-adc5-gen3.c | 860 ++++++++++++++++++
include/linux/iio/adc/qcom-adc5-gen3-common.h | 211 +++++
4 files changed, 1098 insertions(+)
create mode 100644 drivers/iio/adc/qcom-spmi-adc5-gen3.c
create mode 100644 include/linux/iio/adc/qcom-adc5-gen3-common.h
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 58da8255525e..5300e9236ba8 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1329,6 +1329,32 @@ config QCOM_SPMI_ADC5
To compile this driver as a module, choose M here: the module will
be called qcom-spmi-adc5.
+config QCOM_SPMI_ADC5_GEN3
+ tristate "Qualcomm Technologies Inc. SPMI PMIC5 GEN3 ADC"
+ depends on SPMI && THERMAL
+ select REGMAP_SPMI
+ select QCOM_VADC_COMMON
+ select AUXILIARY_BUS
+ help
+ IIO Voltage PMIC5 Gen3 ADC driver for Qualcomm Technologies Inc.
+
+ The driver supports reading multiple channels. The ADC is a 16-bit
+ sigma-delta ADC. The hardware supports calibrated results for
+ conversion requests and clients include reading phone power supply
+ voltage, on board system thermistors connected to the PMIC ADC,
+ PMIC die temperature, charger temperature, battery current, USB
+ voltage input and voltage signals connected to supported PMIC GPIO
+ pins. The hardware supports internal pull-up for thermistors and can
+ choose between a 30k, 100k or 400k ohm pull up using the ADC channels.
+
+ In addition, the same driver supports ADC thermal monitoring devices
+ too. They appear as thermal zones with multiple trip points. A thermal
+ client sets threshold temperature for both warm and cool trips and
+ gets updated when a threshold is reached.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-spmi-adc5-gen3.
+
config RCAR_GYRO_ADC
tristate "Renesas R-Car GyroADC driver"
depends on ARCH_RCAR_GEN2 || COMPILE_TEST
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7cc8f9a12f76..cb1874fd7912 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -113,6 +113,7 @@ obj-$(CONFIG_PAC1934) += pac1934.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
+obj-$(CONFIG_QCOM_SPMI_ADC5_GEN3) += qcom-spmi-adc5-gen3.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_RRADC) += qcom-spmi-rradc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c
new file mode 100644
index 000000000000..f8168a14b907
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c
@@ -0,0 +1,860 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/device/devres.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/iio/adc/qcom-adc5-gen3-common.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#define ADC5_GEN3_VADC_SDAM 0x0
+
+struct adc5_chip;
+
+/**
+ * struct adc5_channel_prop - ADC channel structure
+ * @common_props: structure with ADC channel properties (common to TM usage).
+ * @adc_tm: indicates TM type if the channel is used for TM measurements.
+ * @chip: pointer to top-level ADC device structure.
+ */
+struct adc5_channel_prop {
+ struct adc5_channel_common_prop common_props;
+ int adc_tm;
+ struct adc5_chip *chip;
+};
+
+/**
+ * struct adc5_chip - ADC private structure.
+ * @dev: SPMI ADC5 Gen3 device.
+ * @dev_data: Top-level ADC device data.
+ * @nchannels: number of ADC channels.
+ * @chan_props: array of ADC channel properties.
+ * @iio_chans: array of IIO channels specification.
+ * @complete: ADC result notification after interrupt is received.
+ * @lock: ADC lock for access to the peripheral, to prevent concurrent
+ * requests from multiple clients.
+ * @data: software configuration data.
+ * @n_tm_channels: number of ADC channels used for TM measurements.
+ * @handler: TM callback to be called for threshold violation interrupt
+ * on first SDAM.
+ * @tm_aux: pointer to auxiliary TM device.
+ */
+struct adc5_chip {
+ struct device *dev;
+ struct adc5_device_data dev_data;
+ unsigned int nchannels;
+ struct adc5_channel_prop *chan_props;
+ struct iio_chan_spec *iio_chans;
+ struct completion complete;
+ struct mutex lock;
+ const struct adc5_data *data;
+ unsigned int n_tm_channels;
+ void (*handler)(struct auxiliary_device *tm_aux);
+ struct auxiliary_device *tm_aux;
+};
+
+int adc5_gen3_read(struct adc5_device_data *adc, unsigned int sdam_index,
+ u16 offset, u8 *data, int len)
+{
+ return regmap_bulk_read(adc->regmap,
+ adc->base[sdam_index].base_addr + offset,
+ data, len);
+}
+EXPORT_SYMBOL_NS_GPL(adc5_gen3_read, "QCOM_SPMI_ADC5_GEN3");
+
+int adc5_gen3_write(struct adc5_device_data *adc, unsigned int sdam_index,
+ u16 offset, u8 *data, int len)
+{
+ return regmap_bulk_write(adc->regmap,
+ adc->base[sdam_index].base_addr + offset,
+ data, len);
+}
+EXPORT_SYMBOL_NS_GPL(adc5_gen3_write, "QCOM_SPMI_ADC5_GEN3");
+
+static int adc5_gen3_read_voltage_data(struct adc5_chip *adc, u16 *data)
+{
+ u8 rslt[2];
+ int ret;
+
+ ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM,
+ ADC5_GEN3_CH_DATA0(0), rslt, sizeof(rslt));
+ if (ret)
+ return ret;
+
+ *data = get_unaligned_le16(rslt);
+
+ if (*data == ADC5_USR_DATA_CHECK) {
+ dev_err(adc->dev, "Invalid data:%#x\n", *data);
+ return -EINVAL;
+ }
+
+ dev_dbg(adc->dev, "voltage raw code:%#x\n", *data);
+
+ return 0;
+}
+
+void adc5_gen3_update_dig_param(struct adc5_channel_common_prop *prop, u8 *data)
+{
+ /* Update calibration select and decimation ratio select */
+ *data &= ~(ADC5_GEN3_DIG_PARAM_CAL_SEL_MASK | ADC5_GEN3_DIG_PARAM_DEC_RATIO_SEL_MASK);
+ *data |= FIELD_PREP(ADC5_GEN3_DIG_PARAM_CAL_SEL_MASK, prop->cal_method);
+ *data |= FIELD_PREP(ADC5_GEN3_DIG_PARAM_DEC_RATIO_SEL_MASK, prop->decimation);
+}
+EXPORT_SYMBOL_NS_GPL(adc5_gen3_update_dig_param, "QCOM_SPMI_ADC5_GEN3");
+
+#define ADC5_GEN3_READ_CONFIG_REGS 7
+
+static int adc5_gen3_configure(struct adc5_chip *adc,
+ struct adc5_channel_common_prop *prop)
+{
+ u8 buf[ADC5_GEN3_READ_CONFIG_REGS];
+ u8 conv_req = 0;
+ int ret;
+
+ ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, ADC5_GEN3_SID,
+ buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ /* Write SID */
+ buf[0] = FIELD_PREP(ADC5_GEN3_SID_MASK, prop->sid);
+
+ /*
+ * Use channel 0 by default for immediate conversion and to indicate
+ * there is an actual conversion request
+ */
+ buf[1] = ADC5_GEN3_CHAN_CONV_REQ | 0;
+
+ buf[2] = ADC5_GEN3_TIME_IMMEDIATE;
+
+ /* Digital param selection */
+ adc5_gen3_update_dig_param(prop, &buf[3]);
+
+ /* Update fast average sample value */
+ buf[4] = FIELD_PREP(ADC5_GEN3_FAST_AVG_CTL_SAMPLES_MASK,
+ prop->avg_samples) | ADC5_GEN3_FAST_AVG_CTL_EN;
+
+ /* Select ADC channel */
+ buf[5] = prop->channel;
+
+ /* Select HW settle delay for channel */
+ buf[6] = FIELD_PREP(ADC5_GEN3_HW_SETTLE_DELAY_MASK,
+ prop->hw_settle_time_us);
+
+ reinit_completion(&adc->complete);
+
+ ret = adc5_gen3_write(&adc->dev_data, ADC5_GEN3_VADC_SDAM, ADC5_GEN3_SID,
+ buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ conv_req = ADC5_GEN3_CONV_REQ_REQ;
+ return adc5_gen3_write(&adc->dev_data, ADC5_GEN3_VADC_SDAM,
+ ADC5_GEN3_CONV_REQ, &conv_req, sizeof(conv_req));
+}
+
+/*
+ * Worst case delay from PBS in readying handshake bit can be up to 15ms, when
+ * PBS is busy running other simultaneous transactions, while in the best case,
+ * it is already ready at this point. Assigning polling delay and retry count
+ * accordingly.
+ */
+
+#define ADC5_GEN3_HS_DELAY_US 100
+#define ADC5_GEN3_HS_RETRY_COUNT 150
+
+int adc5_gen3_poll_wait_hs(struct adc5_device_data *adc,
+ unsigned int sdam_index)
+{
+ u8 conv_req = ADC5_GEN3_CONV_REQ_REQ;
+ int ret, count;
+ u8 status = 0;
+
+ for (count = 0; count < ADC5_GEN3_HS_RETRY_COUNT; count++) {
+ ret = adc5_gen3_read(adc, sdam_index, ADC5_GEN3_HS, &status, sizeof(status));
+ if (ret)
+ return ret;
+
+ if (status == ADC5_GEN3_HS_READY) {
+ ret = adc5_gen3_read(adc, sdam_index, ADC5_GEN3_CONV_REQ,
+ &conv_req, sizeof(conv_req));
+ if (ret)
+ return ret;
+
+ if (!conv_req)
+ return 0;
+ }
+
+ fsleep(ADC5_GEN3_HS_DELAY_US);
+ }
+
+ pr_err("Setting HS ready bit timed out, sdam_index:%d, status:%#x\n",
+ sdam_index, status);
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_NS_GPL(adc5_gen3_poll_wait_hs, "QCOM_SPMI_ADC5_GEN3");
+
+int adc5_gen3_status_clear(struct adc5_device_data *adc,
+ int sdam_index, u16 offset, u8 *val, int len)
+{
+ u8 value;
+ int ret;
+
+ ret = adc5_gen3_write(adc, sdam_index, offset, val, len);
+ if (ret)
+ return ret;
+
+ /* To indicate conversion request is only to clear a status */
+ value = 0;
+ ret = adc5_gen3_write(adc, sdam_index, ADC5_GEN3_PERPH_CH, &value,
+ sizeof(value));
+ if (ret)
+ return ret;
+
+ value = ADC5_GEN3_CONV_REQ_REQ;
+ return adc5_gen3_write(adc, sdam_index, ADC5_GEN3_CONV_REQ, &value,
+ sizeof(value));
+}
+EXPORT_SYMBOL_NS_GPL(adc5_gen3_status_clear, "QCOM_SPMI_ADC5_GEN3");
+
+/*
+ * Worst case delay from PBS for conversion time can be up to 500ms, when PBS
+ * has timed out twice, once for the initial attempt and once for a retry of
+ * the same transaction.
+ */
+
+#define ADC5_GEN3_CONV_TIMEOUT_MS 501
+
+static int adc5_gen3_do_conversion(struct adc5_chip *adc,
+ struct adc5_channel_common_prop *prop,
+ u16 *data_volt)
+{
+ unsigned long rc;
+ int ret;
+ u8 val;
+
+ guard(mutex)(&adc->lock);
+ ret = adc5_gen3_poll_wait_hs(&adc->dev_data, ADC5_GEN3_VADC_SDAM);
+ if (ret)
+ return ret;
+
+ ret = adc5_gen3_configure(adc, prop);
+ if (ret) {
+ dev_err(adc->dev, "ADC configure failed with %d\n", ret);
+ return ret;
+ }
+
+ /* No support for polling mode at present */
+ rc = wait_for_completion_timeout(&adc->complete,
+ msecs_to_jiffies(ADC5_GEN3_CONV_TIMEOUT_MS));
+ if (!rc) {
+ dev_err(adc->dev, "Reading ADC channel %s timed out\n",
+ prop->label);
+ return -ETIMEDOUT;
+ }
+
+ ret = adc5_gen3_read_voltage_data(adc, data_volt);
+ if (ret)
+ return ret;
+
+ val = BIT(0);
+ return adc5_gen3_status_clear(&adc->dev_data, ADC5_GEN3_VADC_SDAM,
+ ADC5_GEN3_EOC_CLR, &val, 1);
+}
+
+static irqreturn_t adc5_gen3_isr(int irq, void *dev_id)
+{
+ struct adc5_chip *adc = dev_id;
+ struct device *dev = adc->dev;
+ struct auxiliary_device *adev;
+ u8 status, eoc_status, val;
+ u8 tm_status[2];
+ int ret;
+
+ ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM,
+ ADC5_GEN3_STATUS1, &status, sizeof(status));
+ if (ret) {
+ dev_err(dev, "adc read status1 failed with %d\n", ret);
+ return IRQ_HANDLED;
+ }
+
+ ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM,
+ ADC5_GEN3_EOC_STS, &eoc_status, sizeof(eoc_status));
+ if (ret) {
+ dev_err(dev, "adc read eoc status failed with %d\n", ret);
+ return IRQ_HANDLED;
+ }
+
+ if (status & ADC5_GEN3_STATUS1_CONV_FAULT) {
+ dev_err_ratelimited(dev,
+ "Unexpected conversion fault, status:%#x, eoc_status:%#x\n",
+ status, eoc_status);
+ val = ADC5_GEN3_CONV_ERR_CLR_REQ;
+ adc5_gen3_status_clear(&adc->dev_data, ADC5_GEN3_VADC_SDAM,
+ ADC5_GEN3_CONV_ERR_CLR, &val, 1);
+ return IRQ_HANDLED;
+ }
+
+ /* CHAN0 is the preconfigured channel for immediate conversion */
+ if (eoc_status & ADC5_GEN3_EOC_CHAN_0)
+ complete(&adc->complete);
+
+ ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM,
+ ADC5_GEN3_TM_HIGH_STS, tm_status, sizeof(tm_status));
+ if (ret) {
+ dev_err(dev, "adc read TM status failed with %d\n", ret);
+ return IRQ_HANDLED;
+ }
+
+ dev_dbg(dev, "Interrupt status:%#x, EOC status:%#x, high:%#x, low:%#x\n",
+ status, eoc_status, tm_status[0], tm_status[1]);
+
+ if (tm_status[0] || tm_status[1]) {
+ adev = adc->tm_aux;
+ if (!adev || !adev->dev.driver) {
+ dev_err(dev, "adc_tm auxiliary device not initialized\n");
+ return IRQ_HANDLED;
+ }
+
+ adc->handler(adev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int adc5_gen3_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ int i, v_channel;
+
+ for (i = 0; i < adc->nchannels; i++) {
+ v_channel = ADC5_GEN3_V_CHAN(adc->chan_props[i].common_props);
+ if (v_channel == iiospec->args[0])
+ return i;
+ }
+
+ return -ENOENT;
+}
+
+static int adc5_gen3_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ struct adc5_channel_common_prop *prop;
+ u16 adc_code_volt;
+ int ret;
+
+ prop = &adc->chan_props[chan->address].common_props;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = adc5_gen3_do_conversion(adc, prop, &adc_code_volt);
+ if (ret)
+ return ret;
+
+ ret = qcom_adc5_hw_scale(prop->scale_fn_type, prop->prescale,
+ adc->data, adc_code_volt, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adc5_gen3_read_label(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, char *label)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ struct adc5_channel_prop *prop;
+
+ prop = &adc->chan_props[chan->address];
+ return sprintf(label, "%s\n", prop->common_props.label);
+}
+
+static const struct iio_info adc5_gen3_info = {
+ .read_raw = adc5_gen3_read_raw,
+ .read_label = adc5_gen3_read_label,
+ .fwnode_xlate = adc5_gen3_fwnode_xlate,
+};
+
+struct adc5_channels {
+ unsigned int prescale_index;
+ enum iio_chan_type type;
+ long info_mask;
+ enum vadc_scale_fn_type scale_fn_type;
+};
+
+/* In these definitions, _pre refers to an index into adc5_prescale_ratios. */
+#define ADC5_CHAN(_type, _mask, _pre, _scale) \
+ { \
+ .prescale_index = _pre, \
+ .type = _type, \
+ .info_mask = _mask, \
+ .scale_fn_type = _scale, \
+ }, \
+
+#define ADC5_CHAN_TEMP(_pre, _scale) \
+ ADC5_CHAN(IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \
+
+#define ADC5_CHAN_VOLT(_pre, _scale) \
+ ADC5_CHAN(IIO_VOLTAGE, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \
+
+#define ADC5_CHAN_CUR(_pre, _scale) \
+ ADC5_CHAN(IIO_CURRENT, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \
+
+static const struct adc5_channels adc5_gen3_chans_pmic[ADC5_MAX_CHANNEL] = {
+ [ADC5_GEN3_REF_GND] = ADC5_CHAN_VOLT(0, SCALE_HW_CALIB_DEFAULT)
+ [ADC5_GEN3_1P25VREF] = ADC5_CHAN_VOLT(0, SCALE_HW_CALIB_DEFAULT)
+ [ADC5_GEN3_VPH_PWR] = ADC5_CHAN_VOLT(1, SCALE_HW_CALIB_DEFAULT)
+ [ADC5_GEN3_VBAT_SNS_QBG] = ADC5_CHAN_VOLT(1, SCALE_HW_CALIB_DEFAULT)
+ [ADC5_GEN3_USB_SNS_V_16] = ADC5_CHAN_TEMP(8, SCALE_HW_CALIB_DEFAULT)
+ [ADC5_GEN3_VIN_DIV16_MUX] = ADC5_CHAN_TEMP(8, SCALE_HW_CALIB_DEFAULT)
+ [ADC5_GEN3_DIE_TEMP] = ADC5_CHAN_TEMP(0,
+ SCALE_HW_CALIB_PMIC_THERM_PM7)
+ [ADC5_GEN3_AMUX1_THM_100K_PU] = ADC5_CHAN_TEMP(0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC5_GEN3_AMUX2_THM_100K_PU] = ADC5_CHAN_TEMP(0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC5_GEN3_AMUX3_THM_100K_PU] = ADC5_CHAN_TEMP(0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC5_GEN3_AMUX4_THM_100K_PU] = ADC5_CHAN_TEMP(0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC5_GEN3_AMUX5_THM_100K_PU] = ADC5_CHAN_TEMP(0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC5_GEN3_AMUX6_THM_100K_PU] = ADC5_CHAN_TEMP(0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC5_GEN3_AMUX1_GPIO_100K_PU] = ADC5_CHAN_TEMP(0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC5_GEN3_AMUX2_GPIO_100K_PU] = ADC5_CHAN_TEMP(0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC5_GEN3_AMUX3_GPIO_100K_PU] = ADC5_CHAN_TEMP(0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC5_GEN3_AMUX4_GPIO_100K_PU] = ADC5_CHAN_TEMP(0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+};
+
+static int adc5_gen3_get_fw_channel_data(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop,
+ struct fwnode_handle *fwnode)
+{
+ const char *name = fwnode_get_name(fwnode);
+ const struct adc5_data *data = adc->data;
+ struct device *dev = adc->dev;
+ const char *channel_name;
+ u32 chan, value, sid;
+ u32 varr[2];
+ int ret;
+
+ ret = fwnode_property_read_u32(fwnode, "reg", &chan);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "invalid channel number %s\n",
+ name);
+
+ /*
+ * Value read from "reg" is virtual channel number
+ * virtual channel number = sid << 8 | channel number
+ */
+ sid = FIELD_GET(ADC5_GEN3_VIRTUAL_SID_MASK, chan);
+ chan = FIELD_GET(ADC5_GEN3_CHANNEL_MASK, chan);
+
+ if (chan > ADC5_MAX_CHANNEL)
+ return dev_err_probe(dev, -EINVAL,
+ "%s invalid channel number %d\n",
+ name, chan);
+
+ prop->common_props.channel = chan;
+ prop->common_props.sid = sid;
+
+ if (!adc->data->adc_chans[chan].info_mask)
+ return dev_err_probe(dev, -EINVAL, "Channel %#x not supported\n", chan);
+
+ channel_name = name;
+ fwnode_property_read_string(fwnode, "label", &channel_name);
+ prop->common_props.label = channel_name;
+
+ value = data->decimation[ADC5_DECIMATION_DEFAULT];
+ fwnode_property_read_u32(fwnode, "qcom,decimation", &value);
+ ret = qcom_adc5_decimation_from_dt(value, data->decimation);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "%#x invalid decimation %d\n",
+ chan, value);
+ prop->common_props.decimation = ret;
+
+ prop->common_props.prescale = adc->data->adc_chans[chan].prescale_index;
+ ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2);
+ if (!ret) {
+ ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "%#x invalid pre-scaling <%d %d>\n",
+ chan, varr[0], varr[1]);
+ prop->common_props.prescale = ret;
+ }
+
+ value = data->hw_settle_1[VADC_DEF_HW_SETTLE_TIME];
+ fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value);
+ ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "%#x invalid hw-settle-time %d us\n",
+ chan, value);
+ prop->common_props.hw_settle_time_us = ret;
+
+ value = BIT(VADC_DEF_AVG_SAMPLES);
+ fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value);
+ ret = qcom_adc5_avg_samples_from_dt(value);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "%#x invalid avg-samples %d\n",
+ chan, value);
+ prop->common_props.avg_samples = ret;
+
+ if (fwnode_property_read_bool(fwnode, "qcom,ratiometric"))
+ prop->common_props.cal_method = ADC5_RATIOMETRIC_CAL;
+ else
+ prop->common_props.cal_method = ADC5_ABSOLUTE_CAL;
+
+ prop->adc_tm = fwnode_property_read_bool(fwnode, "qcom,adc-tm");
+ if (prop->adc_tm) {
+ adc->n_tm_channels++;
+ if (adc->n_tm_channels > (adc->dev_data.num_sdams * 8 - 1))
+ return dev_err_probe(dev, -EINVAL,
+ "Number of TM nodes %u greater than channels supported:%u\n",
+ adc->n_tm_channels,
+ adc->dev_data.num_sdams * 8 - 1);
+ }
+
+ return 0;
+}
+
+static const struct adc5_data adc5_gen3_data_pmic = {
+ .full_scale_code_volt = 0x70e4,
+ .adc_chans = adc5_gen3_chans_pmic,
+ .info = &adc5_gen3_info,
+ .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
+ { 85, 340, 1360 },
+ .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ { 15, 100, 200, 300,
+ 400, 500, 600, 700,
+ 1000, 2000, 4000, 8000,
+ 16000, 32000, 64000, 128000 },
+};
+
+static const struct of_device_id adc5_match_table[] = {
+ {
+ .compatible = "qcom,spmi-adc5-gen3",
+ .data = &adc5_gen3_data_pmic,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adc5_match_table);
+
+static int adc5_get_fw_data(struct adc5_chip *adc)
+{
+ const struct adc5_channels *adc_chan;
+ struct adc5_channel_prop *chan_props;
+ struct iio_chan_spec *iio_chan;
+ struct device *dev = adc->dev;
+ unsigned int index = 0;
+ int ret;
+
+ adc->nchannels = device_get_child_node_count(dev);
+ if (!adc->nchannels)
+ return dev_err_probe(dev, -EINVAL, "No ADC channels found\n");
+
+ adc->iio_chans = devm_kcalloc(dev, adc->nchannels,
+ sizeof(*adc->iio_chans), GFP_KERNEL);
+ if (!adc->iio_chans)
+ return -ENOMEM;
+
+ adc->chan_props = devm_kcalloc(dev, adc->nchannels,
+ sizeof(*adc->chan_props), GFP_KERNEL);
+ if (!adc->chan_props)
+ return -ENOMEM;
+
+ chan_props = adc->chan_props;
+ adc->n_tm_channels = 0;
+ iio_chan = adc->iio_chans;
+ adc->data = device_get_match_data(dev);
+
+ device_for_each_child_node_scoped(dev, child) {
+ ret = adc5_gen3_get_fw_channel_data(adc, chan_props, child);
+ if (ret)
+ return ret;
+
+ chan_props->chip = adc;
+ adc_chan = &adc->data->adc_chans[chan_props->common_props.channel];
+ chan_props->common_props.scale_fn_type = adc_chan->scale_fn_type;
+
+ iio_chan->channel = ADC5_GEN3_V_CHAN(chan_props->common_props);
+ iio_chan->info_mask_separate = adc_chan->info_mask;
+ iio_chan->type = adc_chan->type;
+ iio_chan->address = index;
+ iio_chan->indexed = 1;
+ iio_chan++;
+ chan_props++;
+ index++;
+ }
+
+ return 0;
+}
+
+static void adc5_gen3_uninit_aux(void *data)
+{
+ auxiliary_device_uninit(data);
+}
+
+static void adc5_gen3_delete_aux(void *data)
+{
+ auxiliary_device_delete(data);
+}
+
+static void adc5_gen3_aux_device_release(struct device *dev) {}
+
+static int adc5_gen3_add_aux_tm_device(struct adc5_chip *adc)
+{
+ struct tm5_aux_dev_wrapper *aux_device;
+ int i, ret, i_tm = 0;
+
+ aux_device = devm_kzalloc(adc->dev, sizeof(*aux_device), GFP_KERNEL);
+ if (!aux_device)
+ return -ENOMEM;
+
+ aux_device->aux_dev.name = "adc5_tm_gen3";
+ aux_device->aux_dev.dev.parent = adc->dev;
+ aux_device->aux_dev.dev.release = adc5_gen3_aux_device_release;
+
+ aux_device->tm_props = devm_kcalloc(adc->dev, adc->n_tm_channels,
+ sizeof(*aux_device->tm_props),
+ GFP_KERNEL);
+ if (!aux_device->tm_props)
+ return -ENOMEM;
+
+ aux_device->dev_data = &adc->dev_data;
+
+ for (i = 0; i < adc->nchannels; i++) {
+ if (!adc->chan_props[i].adc_tm)
+ continue;
+ aux_device->tm_props[i_tm] = adc->chan_props[i].common_props;
+ i_tm++;
+ }
+
+ device_set_of_node_from_dev(&aux_device->aux_dev.dev, adc->dev);
+
+ aux_device->n_tm_channels = adc->n_tm_channels;
+
+ ret = auxiliary_device_init(&aux_device->aux_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(adc->dev, adc5_gen3_uninit_aux,
+ &aux_device->aux_dev);
+ if (ret)
+ return ret;
+
+ ret = auxiliary_device_add(&aux_device->aux_dev);
+ if (ret)
+ return ret;
+ ret = devm_add_action_or_reset(adc->dev, adc5_gen3_delete_aux,
+ &aux_device->aux_dev);
+ if (ret)
+ return ret;
+
+ adc->tm_aux = &aux_device->aux_dev;
+
+ return 0;
+}
+
+void adc5_gen3_mutex_lock(struct device *dev)
+ __acquires(&adc->lock)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev->parent);
+ struct adc5_chip *adc = iio_priv(indio_dev);
+
+ mutex_lock(&adc->lock);
+}
+EXPORT_SYMBOL_NS_GPL(adc5_gen3_mutex_lock, "QCOM_SPMI_ADC5_GEN3");
+
+void adc5_gen3_mutex_unlock(struct device *dev)
+ __releases(&adc->lock)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev->parent);
+ struct adc5_chip *adc = iio_priv(indio_dev);
+
+ mutex_unlock(&adc->lock);
+}
+EXPORT_SYMBOL_NS_GPL(adc5_gen3_mutex_unlock, "QCOM_SPMI_ADC5_GEN3");
+
+int adc5_gen3_get_scaled_reading(struct device *dev,
+ struct adc5_channel_common_prop *common_props,
+ int *val)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev->parent);
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ u16 adc_code_volt;
+ int ret;
+
+ ret = adc5_gen3_do_conversion(adc, common_props, &adc_code_volt);
+ if (ret)
+ return ret;
+
+ return qcom_adc5_hw_scale(common_props->scale_fn_type,
+ common_props->prescale,
+ adc->data, adc_code_volt, val);
+}
+EXPORT_SYMBOL_NS_GPL(adc5_gen3_get_scaled_reading, "QCOM_SPMI_ADC5_GEN3");
+
+int adc5_gen3_therm_code_to_temp(struct device *dev,
+ struct adc5_channel_common_prop *common_props,
+ u16 code, int *val)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev->parent);
+ struct adc5_chip *adc = iio_priv(indio_dev);
+
+ return qcom_adc5_hw_scale(common_props->scale_fn_type,
+ common_props->prescale,
+ adc->data, code, val);
+}
+EXPORT_SYMBOL_NS_GPL(adc5_gen3_therm_code_to_temp, "QCOM_SPMI_ADC5_GEN3");
+
+void adc5_gen3_register_tm_event_notifier(struct device *dev,
+ void (*handler)(struct auxiliary_device *))
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev->parent);
+ struct adc5_chip *adc = iio_priv(indio_dev);
+
+ adc->handler = handler;
+}
+EXPORT_SYMBOL_NS_GPL(adc5_gen3_register_tm_event_notifier, "QCOM_SPMI_ADC5_GEN3");
+
+static int adc5_gen3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct adc5_chip *adc;
+ struct regmap *regmap;
+ int ret, i;
+ u32 *reg;
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->dev_data.regmap = regmap;
+ adc->dev = dev;
+
+ ret = device_property_count_u32(dev, "reg");
+ if (ret < 0)
+ return ret;
+
+ adc->dev_data.num_sdams = ret;
+
+ reg = devm_kcalloc(dev, adc->dev_data.num_sdams, sizeof(u32),
+ GFP_KERNEL);
+ if (!reg)
+ return -ENOMEM;
+
+ ret = device_property_read_u32_array(dev, "reg", reg,
+ adc->dev_data.num_sdams);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to read reg property\n");
+
+ adc->dev_data.base = devm_kcalloc(dev, adc->dev_data.num_sdams,
+ sizeof(*adc->dev_data.base),
+ GFP_KERNEL);
+ if (!adc->dev_data.base)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, indio_dev);
+ init_completion(&adc->complete);
+ ret = devm_mutex_init(dev, &adc->lock);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < adc->dev_data.num_sdams; i++) {
+ adc->dev_data.base[i].base_addr = reg[i];
+
+ ret = platform_get_irq(pdev, i);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Getting IRQ %d failed\n", i);
+
+ adc->dev_data.base[i].irq = ret;
+
+ adc->dev_data.base[i].irq_name = devm_kasprintf(dev, GFP_KERNEL,
+ "sdam%d", i);
+ if (!adc->dev_data.base[i].irq_name)
+ return -ENOMEM;
+ }
+
+ ret = devm_request_irq(dev, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq,
+ adc5_gen3_isr, 0,
+ adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq_name,
+ adc);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to request SDAM%d irq\n",
+ ADC5_GEN3_VADC_SDAM);
+
+ ret = adc5_get_fw_data(adc);
+ if (ret)
+ return ret;
+
+ if (adc->n_tm_channels > 0) {
+ ret = adc5_gen3_add_aux_tm_device(adc);
+ if (ret)
+ dev_err_probe(dev, ret,
+ "Failed to add auxiliary TM device\n");
+ }
+
+ indio_dev->name = "spmi-adc5-gen3";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &adc5_gen3_info;
+ indio_dev->channels = adc->iio_chans;
+ indio_dev->num_channels = adc->nchannels;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct platform_driver adc5_gen3_driver = {
+ .driver = {
+ .name = "qcom-spmi-adc5-gen3",
+ .of_match_table = adc5_match_table,
+ },
+ .probe = adc5_gen3_probe,
+};
+module_platform_driver(adc5_gen3_driver);
+
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 Gen3 ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("QCOM_SPMI_ADC5_GEN3");
diff --git a/include/linux/iio/adc/qcom-adc5-gen3-common.h b/include/linux/iio/adc/qcom-adc5-gen3-common.h
new file mode 100644
index 000000000000..89c552a77341
--- /dev/null
+++ b/include/linux/iio/adc/qcom-adc5-gen3-common.h
@@ -0,0 +1,211 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * Code used in the main and auxiliary Qualcomm PMIC voltage ADCs
+ * of type ADC5 Gen3.
+ */
+
+#ifndef QCOM_ADC5_GEN3_COMMON_H
+#define QCOM_ADC5_GEN3_COMMON_H
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/iio/adc/qcom-vadc-common.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define ADC5_GEN3_HS 0x45
+#define ADC5_GEN3_HS_BUSY BIT(7)
+#define ADC5_GEN3_HS_READY BIT(0)
+
+#define ADC5_GEN3_STATUS1 0x46
+#define ADC5_GEN3_STATUS1_CONV_FAULT BIT(7)
+#define ADC5_GEN3_STATUS1_THR_CROSS BIT(6)
+#define ADC5_GEN3_STATUS1_EOC BIT(0)
+
+#define ADC5_GEN3_TM_EN_STS 0x47
+#define ADC5_GEN3_TM_HIGH_STS 0x48
+#define ADC5_GEN3_TM_LOW_STS 0x49
+
+#define ADC5_GEN3_EOC_STS 0x4a
+#define ADC5_GEN3_EOC_CHAN_0 BIT(0)
+
+#define ADC5_GEN3_EOC_CLR 0x4b
+#define ADC5_GEN3_TM_HIGH_STS_CLR 0x4c
+#define ADC5_GEN3_TM_LOW_STS_CLR 0x4d
+#define ADC5_GEN3_CONV_ERR_CLR 0x4e
+#define ADC5_GEN3_CONV_ERR_CLR_REQ BIT(0)
+
+#define ADC5_GEN3_SID 0x4f
+#define ADC5_GEN3_SID_MASK GENMASK(3, 0)
+
+#define ADC5_GEN3_PERPH_CH 0x50
+#define ADC5_GEN3_CHAN_CONV_REQ BIT(7)
+
+#define ADC5_GEN3_TIMER_SEL 0x51
+#define ADC5_GEN3_TIME_IMMEDIATE 0x1
+
+#define ADC5_GEN3_DIG_PARAM 0x52
+#define ADC5_GEN3_DIG_PARAM_CAL_SEL_MASK GENMASK(5, 4)
+#define ADC5_GEN3_DIG_PARAM_DEC_RATIO_SEL_MASK GENMASK(3, 2)
+
+#define ADC5_GEN3_FAST_AVG 0x53
+#define ADC5_GEN3_FAST_AVG_CTL_EN BIT(7)
+#define ADC5_GEN3_FAST_AVG_CTL_SAMPLES_MASK GENMASK(2, 0)
+
+#define ADC5_GEN3_ADC_CH_SEL_CTL 0x54
+#define ADC5_GEN3_DELAY_CTL 0x55
+#define ADC5_GEN3_HW_SETTLE_DELAY_MASK GENMASK(3, 0)
+
+#define ADC5_GEN3_CH_EN 0x56
+#define ADC5_GEN3_HIGH_THR_INT_EN BIT(1)
+#define ADC5_GEN3_LOW_THR_INT_EN BIT(0)
+
+#define ADC5_GEN3_LOW_THR0 0x57
+#define ADC5_GEN3_LOW_THR1 0x58
+#define ADC5_GEN3_HIGH_THR0 0x59
+#define ADC5_GEN3_HIGH_THR1 0x5a
+
+#define ADC5_GEN3_CH_DATA0(channel) (0x5c + (channel) * 2)
+#define ADC5_GEN3_CH_DATA1(channel) (0x5d + (channel) * 2)
+
+#define ADC5_GEN3_CONV_REQ 0xe5
+#define ADC5_GEN3_CONV_REQ_REQ BIT(0)
+
+#define ADC5_GEN3_VIRTUAL_SID_MASK GENMASK(15, 8)
+#define ADC5_GEN3_CHANNEL_MASK GENMASK(7, 0)
+#define ADC5_GEN3_V_CHAN(x) \
+ (FIELD_PREP(ADC5_GEN3_VIRTUAL_SID_MASK, (x).sid) | (x).channel)
+
+/* ADC channels for PMIC5 Gen3 */
+#define ADC5_GEN3_REF_GND 0x00
+#define ADC5_GEN3_1P25VREF 0x01
+#define ADC5_GEN3_DIE_TEMP 0x03
+#define ADC5_GEN3_USB_SNS_V_16 0x11
+#define ADC5_GEN3_VIN_DIV16_MUX 0x12
+#define ADC5_GEN3_VPH_PWR 0x8e
+#define ADC5_GEN3_VBAT_SNS_QBG 0x8f
+/* 100k pull-up channels */
+#define ADC5_GEN3_AMUX1_THM_100K_PU 0x44
+#define ADC5_GEN3_AMUX2_THM_100K_PU 0x45
+#define ADC5_GEN3_AMUX3_THM_100K_PU 0x46
+#define ADC5_GEN3_AMUX4_THM_100K_PU 0x47
+#define ADC5_GEN3_AMUX5_THM_100K_PU 0x48
+#define ADC5_GEN3_AMUX6_THM_100K_PU 0x49
+#define ADC5_GEN3_AMUX1_GPIO_100K_PU 0x4a
+#define ADC5_GEN3_AMUX2_GPIO_100K_PU 0x4b
+#define ADC5_GEN3_AMUX3_GPIO_100K_PU 0x4c
+#define ADC5_GEN3_AMUX4_GPIO_100K_PU 0x4d
+
+#define ADC5_MAX_CHANNEL 0xc0
+
+enum adc5_cal_method {
+ ADC5_NO_CAL = 0,
+ ADC5_RATIOMETRIC_CAL,
+ ADC5_ABSOLUTE_CAL,
+};
+
+enum adc5_time_select {
+ MEAS_INT_DISABLE = 0,
+ MEAS_INT_IMMEDIATE,
+ MEAS_INT_50MS,
+ MEAS_INT_100MS,
+ MEAS_INT_1S,
+ MEAS_INT_NONE,
+};
+
+/**
+ * struct adc5_sdam_data - data per SDAM allocated for adc usage
+ * @base_addr: base address for the ADC SDAM peripheral.
+ * @irq_name: ADC IRQ name.
+ * @irq: ADC IRQ number.
+ */
+struct adc5_sdam_data {
+ u16 base_addr;
+ const char *irq_name;
+ int irq;
+};
+
+/**
+ * struct adc5_device_data - Top-level ADC device data
+ * @regmap: ADC peripheral register map field.
+ * @base: array of SDAM data.
+ * @num_sdams: number of ADC SDAM peripherals.
+ */
+struct adc5_device_data {
+ struct regmap *regmap;
+ struct adc5_sdam_data *base;
+ int num_sdams;
+};
+
+/**
+ * struct adc5_channel_common_prop - ADC channel properties (common to ADC and TM).
+ * @channel: channel number, refer to the channel list.
+ * @cal_method: calibration method.
+ * @decimation: sampling rate supported for the channel.
+ * @sid: ID of PMIC owning the channel.
+ * @label: Channel name used in device tree.
+ * @prescale: channel scaling performed on the input signal.
+ * @hw_settle_time_us: the time between AMUX being configured and the
+ * start of conversion in uS.
+ * @avg_samples: ability to provide single result from the ADC
+ * that is an average of multiple measurements.
+ * @scale_fn_type: Represents the scaling function to convert voltage
+ * physical units desired by the client for the channel.
+ */
+struct adc5_channel_common_prop {
+ unsigned int channel;
+ enum adc5_cal_method cal_method;
+ unsigned int decimation;
+ unsigned int sid;
+ const char *label;
+ unsigned int prescale;
+ unsigned int hw_settle_time_us;
+ unsigned int avg_samples;
+ enum vadc_scale_fn_type scale_fn_type;
+};
+
+/**
+ * struct tm5_aux_dev_wrapper - wrapper structure around TM auxiliary device
+ * @aux_dev: TM auxiliary device structure.
+ * @dev_data: Top-level ADC device data.
+ * @tm_props: Array of common ADC channel properties for TM channels.
+ * @n_tm_channels: number of TM channels.
+ */
+struct tm5_aux_dev_wrapper {
+ struct auxiliary_device aux_dev;
+ struct adc5_device_data *dev_data;
+ struct adc5_channel_common_prop *tm_props;
+ unsigned int n_tm_channels;
+};
+
+int adc5_gen3_read(struct adc5_device_data *adc, unsigned int sdam_index,
+ u16 offset, u8 *data, int len);
+
+int adc5_gen3_write(struct adc5_device_data *adc, unsigned int sdam_index,
+ u16 offset, u8 *data, int len);
+
+int adc5_gen3_poll_wait_hs(struct adc5_device_data *adc,
+ unsigned int sdam_index);
+
+void adc5_gen3_update_dig_param(struct adc5_channel_common_prop *prop,
+ u8 *data);
+
+int adc5_gen3_status_clear(struct adc5_device_data *adc,
+ int sdam_index, u16 offset, u8 *val, int len);
+
+void adc5_gen3_mutex_lock(struct device *dev);
+void adc5_gen3_mutex_unlock(struct device *dev);
+int adc5_gen3_get_scaled_reading(struct device *dev,
+ struct adc5_channel_common_prop *common_props,
+ int *val);
+int adc5_gen3_therm_code_to_temp(struct device *dev,
+ struct adc5_channel_common_prop *common_props,
+ u16 code, int *val);
+void adc5_gen3_register_tm_event_notifier(struct device *dev,
+ void (*handler)(struct auxiliary_device *));
+
+#endif /* QCOM_ADC5_GEN3_COMMON_H */
--
2.25.1
^ permalink raw reply related
* [PATCH V9 2/4] dt-bindings: iio: adc: Add support for QCOM PMIC5 Gen3 ADC
From: Jishnu Prakash @ 2026-01-28 11:24 UTC (permalink / raw)
To: jic23, robh, krzk+dt, conor+dt, agross, andersson, lumag,
dmitry.baryshkov, konradybcio, daniel.lezcano, sboyd, amitk,
thara.gopinath, lee, rafael, subbaraman.narayanamurthy,
david.collins, anjelique.melendez, kamal.wadhwa
Cc: rui.zhang, lukasz.luba, devicetree, linux-arm-msm, linux-iio,
linux-kernel, linux-pm, cros-qcom-dts-watchers, jishnu.prakash,
quic_kotarake, neil.armstrong, stephan.gerhold, Jonathan Cameron,
Krzysztof Kozlowski
In-Reply-To: <20260128112420.695518-1-jishnu.prakash@oss.qualcomm.com>
For the PMIC5-Gen3 type PMICs, ADC peripheral is present in HW for the
following PMICs: PMK8550, PM8550, PM8550B and PM8550VX PMICs.
It is similar to PMIC5-Gen2, with SW communication to ADCs on all PMICs
going through PBS(Programmable Boot Sequence) firmware through a single
register interface. This interface is implemented on SDAM (Shared
Direct Access Memory) peripherals on the master PMIC PMK8550 rather
than a dedicated ADC peripheral.
Add documentation for PMIC5 Gen3 ADC and update SPMI PMIC bindings to
allow ADC5 Gen3 as adc@ subnode.
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Jishnu Prakash <jishnu.prakash@oss.qualcomm.com>
---
Changes since v7:
- Dropped ADC5 GEN3 channel macro definitions from bindings, based
on discussion with Krzysztof concluded here:
https://lore.kernel.org/all/d10e2eea-4b86-4e1a-b7a0-54c55907a605@oss.qualcomm.com/,
to be added separately in other patches.
- Fixed quotes to use only double quotes for "#address-cells",
"#size-cells" and "#io-channel-cells" properties, to address Krzysztof's
comment.
- Removed inclusion of ADC channel macro header files from ADC5 Gen3 example
and replaced the macros used in the "reg" properties in channel nodes
with the actual hex values.
- Removed update made under `reg` property in
Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml
which referenced ADC macro binding files, to align with change made
in patch 1 of this series.
Changes since v6:
- Updated SPMI PMIC bindings to allow ADC5 Gen3 as adc@ subnode, to address
Neil's comment.
- Replaced 2025 copyright in newly added files with yearless copyright,
following new internal guidelines.
- Collected Acked-by tag from Jonathan.
Changes since v5:
- Addressed following comments from Krzysztof:
- Increased line wrap length for top-level device description.
- Added more details in binding description explaining how number
of SDAM peripherals used for ADC is allocated per SoC.
- Dropped "interrupt-names" property.
- Moved `required` block to after the list of all properties.
- Dropped | from patternProperties description.
- Renamed per-PMIC binding files listing ADC channel macro names.
- Addressed following comments from Jonathan:
- Moved ref before description, under patternProperties.
- Arranged enum under qcom,hw-settle-time as groups of 8.
Changes since v4:
- Added ADC5 Gen3 documentation in a separate new file to avoid complicating
existing VADC documentation file further to accomodate this device, as
suggested by reviewers.
Changes since v3:
- Added ADC5 Gen3 documentation changes in existing qcom,spmi-vadc.yaml file
instead of adding separate file and updated top-level constraints in documentation
file based on discussion with reviewers.
- Dropped default SID definitions.
- Addressed other reviewer comments.
Changes since v2:
- Moved ADC5 Gen3 documentation into a separate new file.
Changes since v1:
- Updated properties separately for all compatibles to clarify usage
of new properties and updates in usage of old properties for ADC5 Gen3.
- Avoided updating 'adc7' name to 'adc5 gen2' and just left a comment
mentioning this convention.
- Used predefined channel IDs in individual PMIC channel definitions
instead of numeric IDs.
- Addressed other comments from reviewers.
.../bindings/iio/adc/qcom,spmi-adc5-gen3.yaml | 151 ++++++++++++++++++
.../bindings/iio/adc/qcom,spmi-vadc.yaml | 2 +
.../bindings/mfd/qcom,spmi-pmic.yaml | 1 +
3 files changed, 154 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml
diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml
new file mode 100644
index 000000000000..149f4af8f4b8
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml
@@ -0,0 +1,151 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/qcom,spmi-adc5-gen3.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm's SPMI PMIC ADC5 Gen3
+
+maintainers:
+ - Jishnu Prakash <jishnu.prakash@oss.qualcomm.com>
+
+description: |
+ SPMI PMIC5 Gen3 voltage ADC (ADC) provides interface to clients to read
+ voltage. It is a 16-bit sigma-delta ADC. It also performs the same thermal
+ monitoring function as the existing ADC_TM devices.
+
+ The interface is implemented on SDAM (Shared Direct Access Memory) peripherals
+ on the master PMIC rather than a dedicated ADC peripheral. The number of PMIC
+ SDAM peripherals allocated for ADC is not correlated with the PMIC used, it is
+ programmed in FW (PBS) and is fixed per SOC, based on the SOC requirements.
+ All boards using a particular (SOC + master PMIC) combination will have the
+ same number of ADC SDAMs supported on that PMIC.
+
+properties:
+ compatible:
+ const: qcom,spmi-adc5-gen3
+
+ reg:
+ items:
+ - description: SDAM0 base address in the SPMI PMIC register map
+ - description: SDAM1 base address
+ minItems: 1
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ "#io-channel-cells":
+ const: 1
+
+ "#thermal-sensor-cells":
+ const: 1
+
+ interrupts:
+ items:
+ - description: SDAM0 end of conversion (EOC) interrupt
+ - description: SDAM1 EOC interrupt
+ minItems: 1
+
+patternProperties:
+ "^channel@[0-9a-f]+$":
+ type: object
+ unevaluatedProperties: false
+ $ref: /schemas/iio/adc/qcom,spmi-vadc-common.yaml
+ description:
+ Represents the external channels which are connected to the ADC.
+
+ properties:
+ qcom,decimation:
+ enum: [ 85, 340, 1360 ]
+ default: 1360
+
+ qcom,hw-settle-time:
+ enum: [ 15, 100, 200, 300, 400, 500, 600, 700,
+ 1000, 2000, 4000, 8000, 16000, 32000, 64000, 128000 ]
+ default: 15
+
+ qcom,avg-samples:
+ enum: [ 1, 2, 4, 8, 16 ]
+ default: 1
+
+ qcom,adc-tm:
+ description:
+ ADC_TM is a threshold monitoring feature in HW which can be enabled
+ on any ADC channel, to trigger an IRQ for threshold violation. In
+ earlier ADC generations, it was implemented in a separate device
+ (documented in Documentation/devicetree/bindings/thermal/qcom-spmi-adc-tm5.yaml.)
+ In Gen3, this feature can be enabled in the same ADC device for any
+ channel and threshold monitoring and IRQ triggering are handled in FW
+ (PBS) instead of another dedicated HW block.
+ This property indicates ADC_TM monitoring is done on this channel.
+ type: boolean
+
+required:
+ - compatible
+ - reg
+ - "#address-cells"
+ - "#size-cells"
+ - "#io-channel-cells"
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ pmic {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@9000 {
+ compatible = "qcom,spmi-adc5-gen3";
+ reg = <0x9000>, <0x9100>;
+ interrupts = <0x0 0x90 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x91 0x1 IRQ_TYPE_EDGE_RISING>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #io-channel-cells = <1>;
+ #thermal-sensor-cells = <1>;
+
+ /* PMK8550 Channel nodes */
+ channel@3 {
+ reg = <0x3>;
+ label = "pmk8550_die_temp";
+ qcom,pre-scaling = <1 1>;
+ };
+
+ channel@44 {
+ reg = <0x44>;
+ label = "pmk8550_xo_therm";
+ qcom,pre-scaling = <1 1>;
+ qcom,ratiometric;
+ qcom,hw-settle-time = <200>;
+ qcom,adc-tm;
+ };
+
+ /* PM8550 Channel nodes */
+ channel@103 {
+ reg = <0x103>;
+ label = "pm8550_die_temp";
+ qcom,pre-scaling = <1 1>;
+ };
+
+ /* PM8550B Channel nodes */
+ channel@78f {
+ reg = <0x78f>;
+ label = "pm8550b_vbat_sns_qbg";
+ qcom,pre-scaling = <1 3>;
+ };
+
+ /* PM8550VS_C Channel nodes */
+ channel@203 {
+ reg = <0x203>;
+ label = "pm8550vs_c_die_temp";
+ qcom,pre-scaling = <1 1>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
index 16c80709a3ee..72188041e8b5 100644
--- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
@@ -15,6 +15,8 @@ description: |
voltage. The VADC is a 15-bit sigma-delta ADC.
SPMI PMIC5/PMIC7 voltage ADC (ADC) provides interface to clients to read
voltage. The VADC is a 16-bit sigma-delta ADC.
+ Note that PMIC7 ADC is the generation between PMIC5 and PMIC5 Gen3 ADC,
+ it can be considered like PMIC5 Gen2.
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
index 65c80e3b4500..cc5de26bbf57 100644
--- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
+++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
@@ -129,6 +129,7 @@ patternProperties:
"^adc@[0-9a-f]+$":
type: object
oneOf:
+ - $ref: /schemas/iio/adc/qcom,spmi-adc5-gen3.yaml#
- $ref: /schemas/iio/adc/qcom,spmi-iadc.yaml#
- $ref: /schemas/iio/adc/qcom,spmi-rradc.yaml#
- $ref: /schemas/iio/adc/qcom,spmi-vadc.yaml#
--
2.25.1
^ permalink raw reply related
* [PATCH V9 1/4] dt-bindings: iio: adc: Split out QCOM VADC channel properties
From: Jishnu Prakash @ 2026-01-28 11:24 UTC (permalink / raw)
To: jic23, robh, krzk+dt, conor+dt, agross, andersson, lumag,
dmitry.baryshkov, konradybcio, daniel.lezcano, sboyd, amitk,
thara.gopinath, lee, rafael, subbaraman.narayanamurthy,
david.collins, anjelique.melendez, kamal.wadhwa
Cc: rui.zhang, lukasz.luba, devicetree, linux-arm-msm, linux-iio,
linux-kernel, linux-pm, cros-qcom-dts-watchers, jishnu.prakash,
quic_kotarake, neil.armstrong, stephan.gerhold,
Krzysztof Kozlowski, Jonathan Cameron
In-Reply-To: <20260128112420.695518-1-jishnu.prakash@oss.qualcomm.com>
Split out the common channel properties for QCOM VADC devices into a
separate file so that it can be included as a reference for devices
using them. This will be needed for the upcoming ADC5 Gen3 binding
support patch, as ADC5 Gen3 also uses all of these common properties.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Jishnu Prakash <jishnu.prakash@oss.qualcomm.com>
---
Changes since v7:
- Removed binding file paths mentioned under `reg` property description in
Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml, and
updated the description slightly, to simplify it and avoid any dependencies
on patch 1 from the earlier series.
- Removed an extra blank line present in
Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
in previous versions.
Changes since v6:
- Collected Acked-by tag from Jonathan.
Changes since v5:
- Collected Reviewed-by tag from Krzysztof.
.../iio/adc/qcom,spmi-vadc-common.yaml | 84 +++++++++++++++++++
.../bindings/iio/adc/qcom,spmi-vadc.yaml | 76 +----------------
2 files changed, 86 insertions(+), 74 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml
diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml
new file mode 100644
index 000000000000..3ae252c17b91
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml
@@ -0,0 +1,84 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/qcom,spmi-vadc-common.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Technologies, Inc. SPMI PMIC ADC channels
+
+maintainers:
+ - Jishnu Prakash <jishnu.prakash@oss.qualcomm.com>
+
+description:
+ This defines the common properties used to define Qualcomm VADC channels.
+
+properties:
+ reg:
+ description:
+ ADC channel number (PMIC-specific for versions after PMIC5 ADC).
+ maxItems: 1
+
+ label:
+ description:
+ ADC input of the platform as seen in the schematics.
+ For thermistor inputs connected to generic AMUX or GPIO inputs
+ these can vary across platform for the same pins. Hence select
+ the platform schematics name for this channel.
+
+ qcom,decimation:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ This parameter is used to decrease ADC sampling rate.
+ Quicker measurements can be made by reducing decimation ratio.
+
+ qcom,pre-scaling:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ description:
+ Used for scaling the channel input signal before the signal is
+ fed to VADC. The configuration for this node is to know the
+ pre-determined ratio and use it for post scaling. It is a pair of
+ integers, denoting the numerator and denominator of the fraction by which
+ input signal is multiplied. For example, <1 3> indicates the signal is scaled
+ down to 1/3 of its value before ADC measurement.
+ If property is not found default value depending on chip will be used.
+ oneOf:
+ - items:
+ - const: 1
+ - enum: [ 1, 3, 4, 6, 20, 8, 10, 16 ]
+ - items:
+ - const: 10
+ - const: 81
+
+ qcom,ratiometric:
+ type: boolean
+ description: |
+ Channel calibration type.
+ - For compatible property "qcom,spmi-vadc", if this property is
+ specified VADC will use the VDD reference (1.8V) and GND for
+ channel calibration. If property is not found, channel will be
+ calibrated with 0.625V and 1.25V reference channels, also
+ known as absolute calibration.
+ - For other compatible properties, if this property is specified
+ VADC will use the VDD reference (1.875V) and GND for channel
+ calibration. If property is not found, channel will be calibrated
+ with 0V and 1.25V reference channels, also known as absolute calibration.
+
+ qcom,hw-settle-time:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Time between AMUX getting configured and the ADC starting
+ conversion. The 'hw_settle_time' is an index used from valid values
+ and programmed in hardware to achieve the hardware settling delay.
+
+ qcom,avg-samples:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Number of samples to be used for measurement.
+ Averaging provides the option to obtain a single measurement
+ from the ADC that is an average of multiple samples. The value
+ selected is 2^(value).
+
+required:
+ - reg
+
+additionalProperties: true
diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
index b9dc04b0d307..16c80709a3ee 100644
--- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
@@ -56,7 +56,7 @@ required:
patternProperties:
"^channel@[0-9a-f]+$":
type: object
- additionalProperties: false
+ unevaluatedProperties: false
description: |
Represents the external channels which are connected to the ADC.
For compatible property "qcom,spmi-vadc" following channels, also known as
@@ -64,79 +64,7 @@ patternProperties:
configuration nodes should be defined:
VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV,
VADC_GND_REF and VADC_VDD_VADC.
-
- properties:
- reg:
- maxItems: 1
- description: |
- ADC channel number.
- See include/dt-bindings/iio/qcom,spmi-vadc.h
- For PMIC7 ADC, the channel numbers are specified separately per PMIC
- in the PMIC-specific files in include/dt-bindings/iio/.
-
- label:
- description: |
- ADC input of the platform as seen in the schematics.
- For thermistor inputs connected to generic AMUX or GPIO inputs
- these can vary across platform for the same pins. Hence select
- the platform schematics name for this channel.
-
- qcom,decimation:
- $ref: /schemas/types.yaml#/definitions/uint32
- description: |
- This parameter is used to decrease ADC sampling rate.
- Quicker measurements can be made by reducing decimation ratio.
-
- qcom,pre-scaling:
- description: |
- Used for scaling the channel input signal before the signal is
- fed to VADC. The configuration for this node is to know the
- pre-determined ratio and use it for post scaling. It is a pair of
- integers, denoting the numerator and denominator of the fraction by which
- input signal is multiplied. For example, <1 3> indicates the signal is scaled
- down to 1/3 of its value before ADC measurement.
- If property is not found default value depending on chip will be used.
- $ref: /schemas/types.yaml#/definitions/uint32-array
- oneOf:
- - items:
- - const: 1
- - enum: [ 1, 3, 4, 6, 20, 8, 10, 16 ]
- - items:
- - const: 10
- - const: 81
-
- qcom,ratiometric:
- description: |
- Channel calibration type.
- - For compatible property "qcom,spmi-vadc", if this property is
- specified VADC will use the VDD reference (1.8V) and GND for
- channel calibration. If property is not found, channel will be
- calibrated with 0.625V and 1.25V reference channels, also
- known as absolute calibration.
- - For compatible property "qcom,spmi-adc5", "qcom,spmi-adc7" and
- "qcom,spmi-adc-rev2", if this property is specified VADC will use
- the VDD reference (1.875V) and GND for channel calibration. If
- property is not found, channel will be calibrated with 0V and 1.25V
- reference channels, also known as absolute calibration.
- type: boolean
-
- qcom,hw-settle-time:
- $ref: /schemas/types.yaml#/definitions/uint32
- description: |
- Time between AMUX getting configured and the ADC starting
- conversion. The 'hw_settle_time' is an index used from valid values
- and programmed in hardware to achieve the hardware settling delay.
-
- qcom,avg-samples:
- $ref: /schemas/types.yaml#/definitions/uint32
- description: |
- Number of samples to be used for measurement.
- Averaging provides the option to obtain a single measurement
- from the ADC that is an average of multiple samples. The value
- selected is 2^(value).
-
- required:
- - reg
+ $ref: /schemas/iio/adc/qcom,spmi-vadc-common.yaml
allOf:
- if:
--
2.25.1
^ permalink raw reply related
* [PATCH V9 0/4] Add support for QCOM SPMI PMIC5 Gen3 ADC
From: Jishnu Prakash @ 2026-01-28 11:24 UTC (permalink / raw)
To: jic23, robh, krzk+dt, conor+dt, agross, andersson, lumag,
dmitry.baryshkov, konradybcio, daniel.lezcano, sboyd, amitk,
thara.gopinath, lee, rafael, subbaraman.narayanamurthy,
david.collins, anjelique.melendez, kamal.wadhwa
Cc: rui.zhang, lukasz.luba, devicetree, linux-arm-msm, linux-iio,
linux-kernel, linux-pm, cros-qcom-dts-watchers, jishnu.prakash,
quic_kotarake, neil.armstrong, stephan.gerhold
PMIC5 Gen3 has a similar ADC architecture to that on PMIC5 Gen2,
with all SW communication to ADC going through PMK8550 which
communicates with other PMICs through PBS. The major difference is
that the register interface used here is that of an SDAM present on
PMK8550, rather than a dedicated ADC peripheral. There may be more than one
SDAM used for ADC5 Gen3. Each ADC SDAM has eight channels, each of which may
be used for either immediate reads (same functionality as previous PMIC5 and
PMIC5 Gen2 ADC peripherals) or recurring measurements (same as PMIC5 and PMIC5
Gen2 ADC_TM functionality). In this case, we have VADC and ADC_TM functionality
combined into the same module.
Patch 1 splits out the common ADC channel properties used on older
VADC devices, which would also be reused on ADC5 Gen3, into a common
binding file, to be referenced in Gen3 and other ADC binding files.
Patch 2 adds bindings for ADC5 Gen3 peripheral.
Patch 3 adds the main driver for ADC5 Gen3.
Patch 4 adds the auxiliary thermal driver which supports the ADC_TM
functionality of ADC5 Gen3.
Changes since v8:
- Collected Reviewed-by tag from Krzysztof on patch 2.
- Dropped the common module (drivers/iio/adc/qcom-adc5-gen3-common.c) as suggested
by Dmitry.
- Made changes in patches 3 and 4 to simplify auxiliary device structure to use
module_auxiliary_driver() as requested by Dmitry.
- Updated header files included in all newly added files to follow IWYU
(include-what-you-use) principles as requested by Jonathan.
- Addressed all remaining comments from Jonathan in patches 3 and 4, for
refactoring some code.
- More details about all changes are present in per-patch change logs.
- Link to v8: https://lore.kernel.org/all/20251127134036.209905-1-jishnu.prakash@oss.qualcomm.com/
Changes since v7:
- Based on a discussion with Krzysztof concluded here:
https://lore.kernel.org/all/d10e2eea-4b86-4e1a-b7a0-54c55907a605@oss.qualcomm.com/,
patch 1 in the previous series is updated to move ADC header files from binding to
devicetree folders, as they contain HW constants, not exactly bindings.
Earlier in series v7, patches 3 (Gen3 binding) and 4 (driver) were dependent on
patch 1, as it updated the location of include/dt-bindings/iio/qcom,spmi-vadc.h,
which was used in patches 3 and 4 for Gen3 channel macros. In v8, these macros
will be added in other new files, so the former patch 1 will now be a standalone
change separate from this series, as the other patches here are no longer
dependent on it.
- Patches 2/3/4/5 of v7 are now patches 1/2/3/4 in v8. Below comments refer to
v7 patch numbers.
- Updated description of `reg` property in patch 2 to completely drop mentions of binding
file paths, to simplify it and avoid any dependencies with patch 1.
- Dropped all ADC channel macro definitions from patch 3, updated binding example
correspondingly and fixed formatting inconsistancies in property names, to address
Krzysztof's comments.
- Addressed all comments from Jonathan in driver patches 4 and 5, for fixing formatting
and refactoring some code.
- In patch 4, added ADC5 Gen3 channel macros in qcom-adc5-gen3-common.h, for use in main
driver file. Also added a check to fail probe in case ADC channel value obtained
from devicetree is not among list of supported channels.
- More details about all changes are present in per-patch change logs.
- Link to v7: https://lore.kernel.org/all/20250826083657.4005727-1-jishnu.prakash@oss.qualcomm.com/
Changes since v6:
- Collected Acked-by tags from Jonathan on patches 1, 2 and 3.
- Addressed Neil's comment in patch 3.
- Updated copyright to yearless one in newly added files, following new internal guidelines.
- Addressed all comments from Jonathan on patches 4 and 5 (most importantly for updating
auxiliary device cleanup handling to fix memory freeing issues).
- More details about all changes are present in per-patch change logs.
- Link to v6: https://lore.kernel.org/all/20250509110959.3384306-1-jishnu.prakash@oss.qualcomm.com/
Changes since v5:
- Collected Reviewed-by tag from Krzysztof on patch 2.
- Addressed all comments from Krzysztof and Jonathan on patch 3.
- Addressed all comments from Jonathan on patches 4 and 5.
- More details about all changes are present in per-patch change logs.
- Link to v5: https://lore.kernel.org/all/20250131183242.3653595-1-jishnu.prakash@oss.qualcomm.com/
Changes since v4:
- Split common ADC channel properties out into a separate file to use as
ref for ADC5 Gen3 and moved ADC5 Gen3 documentation into a separate
file as suggested by reviewers.
- Addressed few reviewer comments in driver patches.
- Link to v4: https://lore.kernel.org/all/20241030185854.4015348-1-quic_jprakash@quicinc.com/
Changes since v3:
- Updated files affected by adc file path change in /arch/arm folder,
which were missed earlier.
- Added ADC5 Gen3 documentation changes in existing qcom,spmi-vadc.yaml file
instead of adding separate file and addressed reviewer comments for all bindings.
- Addressed review comments in driver patch. Split out TM functionality into
auxiliary driver in separate patch and added required changes in main driver, as
suggested by Dmitry.
- Link to v3: https://lore.kernel.org/all/20231231171237.3322376-1-quic_jprakash@quicinc.com/
Changes since v2:
- Reordered patches to keep cleanup change for ADC files first.
- Moved ADC5 Gen3 documentation into a separate file
Changes since v1:
- Dropped patches 1-5 for changing 'ADC7' peripheral name to 'ADC5 Gen2'.
- Addressed reviewer comments for binding and driver patches for ADC5 Gen3.
- Combined patches 8-11 into a single patch as requested by reviewers to make
the change clearer and made all fixes required in same patch.
Jishnu Prakash (4):
dt-bindings: iio: adc: Split out QCOM VADC channel properties
dt-bindings: iio: adc: Add support for QCOM PMIC5 Gen3 ADC
iio: adc: Add support for QCOM PMIC5 Gen3 ADC
thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring
.../bindings/iio/adc/qcom,spmi-adc5-gen3.yaml | 151 +++
.../iio/adc/qcom,spmi-vadc-common.yaml | 84 ++
.../bindings/iio/adc/qcom,spmi-vadc.yaml | 78 +-
.../bindings/mfd/qcom,spmi-pmic.yaml | 1 +
drivers/iio/adc/Kconfig | 26 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/qcom-spmi-adc5-gen3.c | 860 ++++++++++++++++++
drivers/thermal/qcom/Kconfig | 9 +
drivers/thermal/qcom/Makefile | 1 +
drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c | 515 +++++++++++
include/linux/iio/adc/qcom-adc5-gen3-common.h | 211 +++++
11 files changed, 1863 insertions(+), 74 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml
create mode 100644 Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml
create mode 100644 drivers/iio/adc/qcom-spmi-adc5-gen3.c
create mode 100644 drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c
create mode 100644 include/linux/iio/adc/qcom-adc5-gen3-common.h
base-commit: 63804fed149a6750ffd28610c5c1c98cce6bd377
--
2.25.1
^ permalink raw reply
* Re: [PATCH v2 1/2] dt-bindings: interconnect: document the RPMh Network-On-Chip interconnect in Eliza SoC
From: Krzysztof Kozlowski @ 2026-01-28 11:17 UTC (permalink / raw)
To: Abel Vesa
Cc: Georgi Djakov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Odelu Kukatla, linux-arm-msm, linux-pm, devicetree, linux-kernel
In-Reply-To: <20260127-eliza-interconnect-v2-1-b238a8e04976@oss.qualcomm.com>
On Tue, Jan 27, 2026 at 05:26:17PM +0200, Abel Vesa wrote:
> From: Odelu Kukatla <odelu.kukatla@oss.qualcomm.com>
>
> Document the RPMh Network-On-Chip Interconnect of the Eliza platform.
>
> Signed-off-by: Odelu Kukatla <odelu.kukatla@oss.qualcomm.com>
> Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
> ---
> .../bindings/interconnect/qcom,eliza-rpmh.yaml | 141 +++++++++++++++++++++
> include/dt-bindings/interconnect/qcom,eliza-rpmh.h | 136 ++++++++++++++++++++
> 2 files changed, 277 insertions(+)
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH] arm64: topology: Fix false warning in counters_read_on_cpu() for same-CPU reads
From: Jie Zhan @ 2026-01-28 10:50 UTC (permalink / raw)
To: Sumit Gupta, catalin.marinas, will, zhenglifeng1, viresh.kumar,
rafael, beata.michalska, pierre.gondois, ionela.voinescu,
linux-arm-kernel, linux-pm, linux-kernel, linux-tegra
Cc: treding, jonathanh, bbasu
In-Reply-To: <20260127080700.3565546-1-sumitg@nvidia.com>
Hi Sumit,
On 1/27/2026 4:07 PM, Sumit Gupta wrote:
> The counters_read_on_cpu() function warns when called with IRQs disabled
> to prevent deadlock in smp_call_function_single(). However, this warning
> is spurious when reading counters on the current CPU since no IPI is
> needed for same-CPU reads.
>
> Commit 12eb8f4fff24 ("cpufreq: CPPC: Update FIE arch_freq_scale in ticks
> for non-PCC regs") changed the CPPC Frequency Invariance Engine to read
> AMU counters directly from the scheduler tick for non-PCC register
> spaces (like FFH), instead of deferring to a kthread. This means
> counters_read_on_cpu() is now called with IRQs disabled from the tick
> handler, triggering the warning:
>
> | WARNING: arch/arm64/kernel/topology.c:410 at counters_read_on_cpu
> | ...
> | Call trace:
> | counters_read_on_cpu+0x88/0xa8 (P)
> | cpc_read_ffh+0xdc/0x148
> | cpc_read+0x260/0x518
> | cppc_get_perf_ctrs+0xf0/0x398
> | __cppc_scale_freq_tick+0x4c/0x148 [cppc_cpufreq]
> | cppc_scale_freq_tick+0x44/0x88 [cppc_cpufreq]
> | topology_scale_freq_tick+0x34/0x58
> | sched_tick+0x58/0x300
> | update_process_times+0xcc/0x120
> | tick_nohz_handler+0xa8/0x260
> | __hrtimer_run_queues+0x154/0x360
> | hrtimer_interrupt+0xf4/0x2b0
> | arch_timer_handler_phys+0x4c/0x78
> | ....
> | CPPC Cpufreq:__cppc_scale_freq_tick: failed to read perf counters
> | ....
>
> Fix this by calling the counter read function directly for same-CPU
> case, bypassing smp_call_function_single() entirely. Use get_cpu() to
> disable preemption as the counter read functions call this_cpu_has_cap()
> which requires a non-preemptible context.
>
> Fixes: 12eb8f4fff24 ("cpufreq: CPPC: Update FIE arch_freq_scale in ticks for non-PCC regs")
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
Reviewed-by: Jie Zhan <zhanjie9@hisilicon.com>
Looks fine for me except for the minor comment wrapping.
Thanks for spotting this.
I may have missed the warning log in the FFH test.
This happens during the short window in cpufreq_policy_online() between
driver->init() and the CREATE_POLICY notifier that gets AMU FIE ready.
After that, CPPC FIE will be stopped.
Ideally this can be merged together with Viresh's PR since the CPPC FIE
changes are there.
https://lore.kernel.org/all/j4qdid7iqmng4gzb5ozefemjkep3wx2b5z2yki5tnqc3vzvzf4@kvrnarvdod5p/
Jie
> ---
> arch/arm64/kernel/topology.c | 21 +++++++++++++++++++--
> 1 file changed, 19 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
> index 539b38935182..57b71f403007 100644
> --- a/arch/arm64/kernel/topology.c
> +++ b/arch/arm64/kernel/topology.c
> @@ -401,12 +401,29 @@ static inline
> int counters_read_on_cpu(int cpu, smp_call_func_t func, u64 *val)
> {
> /*
> - * Abort call on counterless CPU or when interrupts are
> - * disabled - can lead to deadlock in smp sync call.
> + * Abort call on counterless CPU.
> */
> if (!cpu_has_amu_feat(cpu))
> return -EOPNOTSUPP;
>
> + /*
> + * For same-CPU reads, call the function directly since no IPI
> + * is needed and this is safe even with IRQs disabled.
> + * Use get_cpu() to disable preemption as the counter read
> + * functions call this_cpu_has_cap() which requires a
> + * non-preemptible context.
> + */
Wrap at 80 chars?
> + if (cpu == get_cpu()) {
> + func(val);
> + put_cpu();
> + return 0;
> + }
> + put_cpu();
> +
> + /*
> + * Reading from a remote CPU requires IRQs enabled to avoid
> + * deadlock in smp_call_function_single().
> + */
> if (WARN_ON_ONCE(irqs_disabled()))
> return -EPERM;
>
^ permalink raw reply
* Re: [PATCH] thermal/of: Fix reference leak in thermal_of_cm_lookup()
From: Lukasz Luba @ 2026-01-28 10:42 UTC (permalink / raw)
To: Rafael J. Wysocki, Felix Gu
Cc: Zhang Rui, Yu-Che Cheng, Daniel Lezcano, linux-pm, linux-kernel
In-Reply-To: <CAJZ5v0gL92f3g4oj47jaYQKG_V6vd_oBawbwQVWjUP7u9QKS7g@mail.gmail.com>
On 1/27/26 16:22, Rafael J. Wysocki wrote:
> On Fri, Jan 23, 2026 at 8:06 PM Felix Gu <ustc.gu@gmail.com> wrote:
>>
>> In thermal_of_cm_lookup(), tr_np is obtained via of_parse_phandle(). But
>> it never be released.
>> Use the __free(device_node) cleanup attribute to automatically release
>> the node and fix the leak.
>>
>> Fixes: 423de5b5bc5b ("thermal/of: Fix cdev lookup in thermal_of_should_bind()")
>>
>> Signed-off-by: Felix Gu <ustc.gu@gmail.com>
>> ---
>> drivers/thermal/thermal_of.c | 4 ++--
>> 1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c
>> index 1a51a4d240ff..b6d0c92f5522 100644
>> --- a/drivers/thermal/thermal_of.c
>> +++ b/drivers/thermal/thermal_of.c
>> @@ -280,10 +280,10 @@ static bool thermal_of_cm_lookup(struct device_node *cm_np,
>> struct cooling_spec *c)
>> {
>> for_each_child_of_node_scoped(cm_np, child) {
>> - struct device_node *tr_np;
>> int count, i;
>>
>> - tr_np = of_parse_phandle(child, "trip", 0);
>> + struct device_node *tr_np __free(device_node) =
>> + of_parse_phandle(child, "trip", 0);
>> if (tr_np != trip->priv)
>> continue;
>>
>>
>> ---
>
> This looks good to me.
>
> Lukasz, Daniel?
Good catch thanks! That looks good. This scoped device node
handling approach simplifies a lot the error paths (and is less
error-prone).
Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
^ permalink raw reply
* Re: [PATCH 0/2] PM: QoS/pmdomains: support resume latencies for system-wide PM
From: Ulf Hansson @ 2026-01-28 10:33 UTC (permalink / raw)
To: Kevin Hilman (TI); +Cc: Rafael J. Wysocki, linux-pm, Dhruva Gole, linux-kernel
In-Reply-To: <20260120-topic-lpm-pmdomain-device-constraints-v1-0-108fc4cfafce@baylibre.com>
On Wed, 21 Jan 2026 at 02:54, Kevin Hilman (TI) <khilman@baylibre.com> wrote:
>
> Currently QoS resume latencies are only considered for runtime PM
> transitions of pmdomains, which remains the default.
>
> In order to also support QoS resume latencies during system-wide PM,
> add a new flag to indicate a resume latency should be used for
> system-wide PM *instead of* runtime PM.
>
> For example, by doing this:
>
> # echo 500000 > /sys/devices/.../<dev0>/power/pm_qos_resume_latency_us
>
> dev0 now has a resume latency of 500000 usec for runtime PM
> transitions.
>
> Then, if the new flag is also set:
>
> # echo 1 > /sys/devices/.../<dev0>/power/pm_qos_latency_sys
>
> That 500000 usec delay now applies to system-wide PM (and not to
> runtime PM).
>
> If a user requires a different latency value for system-wide PM
> compared to runtime PM, then the runtime PM value can be set for
> normal operations, and the system-wide value (and flag) can be set by
> userspace before suspend, and the runtime PM value can be restored
> after resume.
That's sounds complicated for user-space to manage - and causes churns
during every suspend/resume cycle. Why don't we just add a new latency
value instead, that applies both to runtime PM and system-wide PM,
similar and consistent to what we did for CPU QoS?
Kind regards
Uffe
>
> To: Rafael J. Wysocki <rafael@kernel.org>
> To: Ulf Hansson <ulf.hansson@linaro.org>
> To: linux-pm@vger.kernel.org
>
> Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
> ---
> Kevin Hilman (TI) (2):
> PM / QoS: add flag to indicate latency applies system-wide
> pmdommain: add support system-wide resume latency constraints
>
> drivers/base/power/sysfs.c | 27 +++++++++++++++++++++++++++
> drivers/pmdomain/governor.c | 42 +++++++++++++++++++++++++++++++++++++++++-
> include/linux/pm_qos.h | 2 ++
> 3 files changed, 70 insertions(+), 1 deletion(-)
> ---
> base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
> change-id: 20260120-topic-lpm-pmdomain-device-constraints-e5e78ce48502
>
> Best regards,
> --
> Kevin Hilman (TI) <khilman@baylibre.com>
>
^ permalink raw reply
* Re: [PATCH 1/2] PM / QoS: add flag to indicate latency applies system-wide
From: Ulf Hansson @ 2026-01-28 10:25 UTC (permalink / raw)
To: Kevin Hilman (TI); +Cc: Rafael J. Wysocki, linux-pm, Dhruva Gole, linux-kernel
In-Reply-To: <20260120-topic-lpm-pmdomain-device-constraints-v1-1-108fc4cfafce@baylibre.com>
On Wed, 21 Jan 2026 at 02:54, Kevin Hilman (TI) <khilman@baylibre.com> wrote:
>
> By default, the QoS resume latency currenly only applied to runtime PM
> decisions.
>
> Add new PM_QOS_FLAG_LATENCY_SYS flag to indicate that the
> resume latency QoS constraint should be applied to system-wide
> PM, and *not* runtime PM.
What if we need latency constraints to be applied for both runtime PM
and system-wide PM? Can that be done?
>
> Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
> ---
> drivers/base/power/sysfs.c | 27 +++++++++++++++++++++++++++
> include/linux/pm_qos.h | 2 ++
> 2 files changed, 29 insertions(+)
>
> diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
> index 13b31a3adc77..9136c13c17e4 100644
> --- a/drivers/base/power/sysfs.c
> +++ b/drivers/base/power/sysfs.c
> @@ -316,6 +316,32 @@ static ssize_t pm_qos_no_power_off_store(struct device *dev,
>
> static DEVICE_ATTR_RW(pm_qos_no_power_off);
>
> +static ssize_t pm_qos_latency_sys_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + return sysfs_emit(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev)
> + & PM_QOS_FLAG_LATENCY_SYS));
> +}
> +
> +static ssize_t pm_qos_latency_sys_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t n)
> +{
> + int ret;
> +
> + if (kstrtoint(buf, 0, &ret))
> + return -EINVAL;
> +
> + if (ret != 0 && ret != 1)
> + return -EINVAL;
> +
> + ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_LATENCY_SYS, ret);
> + return ret < 0 ? ret : n;
> +}
> +
> +static DEVICE_ATTR_RW(pm_qos_latency_sys);
Shouldn't this code be moved below, inside the #ifdef CONFIG_PM_SLEEP?
I also wonder if "pm_qos_latency_sys" may be slightly too short to
allow the user to understand what it's used for. Perhaps we should
rename it to something along the lines of
"pm_qos_latency_wakeup_enabled". Just a thought, no strong opinions.
> +
> #ifdef CONFIG_PM_SLEEP
> static const char _enabled[] = "enabled";
> static const char _disabled[] = "disabled";
> @@ -681,6 +707,7 @@ static const struct attribute_group pm_qos_latency_tolerance_attr_group = {
>
> static struct attribute *pm_qos_flags_attrs[] = {
> &dev_attr_pm_qos_no_power_off.attr,
> + &dev_attr_pm_qos_latency_sys.attr,
> NULL,
> };
> static const struct attribute_group pm_qos_flags_attr_group = {
> diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
> index 6cea4455f867..aededda52b6b 100644
> --- a/include/linux/pm_qos.h
> +++ b/include/linux/pm_qos.h
> @@ -37,6 +37,8 @@ enum pm_qos_flags_status {
> #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1)
>
> #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0)
> +/* latency value applies to system-wide suspend/s2idle */
> +#define PM_QOS_FLAG_LATENCY_SYS (2 << 0)
>
> enum pm_qos_type {
> PM_QOS_UNITIALIZED,
>
> --
> 2.51.0
>
What about documentation?
Kind regards
Uffe
^ permalink raw reply
* Re: [PATCH] thermal: core: thermal_core.h: fix all kernel-doc warnings
From: Lukasz Luba @ 2026-01-28 10:09 UTC (permalink / raw)
To: Randy Dunlap, linux-kernel
Cc: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, linux-pm
In-Reply-To: <20260128062446.402175-1-rdunlap@infradead.org>
On 1/28/26 06:24, Randy Dunlap wrote:
> Resolve all kernel-doc warnings in thermal_core.h:
>
> Warning: drivers/thermal/thermal_core.h:99 bad line: trip point.
> Warning: drivers/thermal/thermal_core.h:101 bad line: passive trip point.
> Warning: drivers/thermal/thermal_core.h:152 struct member 'trips_attribute_group' not described in 'thermal_zone_device'
> Warning: drivers/thermal/thermal_core.h:152 struct member 'debugfs' not described in 'thermal_zone_device'
> Warning: drivers/thermal/thermal_core.h:152 struct member 'user_thresholds' not described in 'thermal_zone_device'
>
> Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
> ---
> Cc: Rafael J. Wysocki <rafael@kernel.org>
> Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
> Cc: Zhang Rui <rui.zhang@intel.com>
> Cc: Lukasz Luba <lukasz.luba@arm.com>
> Cc: linux-pm@vger.kernel.org
>
> drivers/thermal/thermal_core.h | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> --- linux-next-20260126.orig/drivers/thermal/thermal_core.h
> +++ linux-next-20260126/drivers/thermal/thermal_core.h
> @@ -77,6 +77,7 @@ struct thermal_governor {
> * @device: &struct device for this thermal zone
> * @removal: removal completion
> * @resume: resume completion
> + * @trips_attribute_group: trip point sysfs attributes
> * @trips_high: trips above the current zone temperature
> * @trips_reached: trips below or at the current zone temperature
> * @trips_invalid: trips with invalid temperature
> @@ -97,9 +98,9 @@ struct thermal_governor {
> * @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION
> * @passive: 1 if you've crossed a passive trip point, 0 otherwise.
> * @prev_low_trip: the low current temperature if you've crossed a passive
> - trip point.
> + * trip point.
> * @prev_high_trip: the above current temperature if you've crossed a
> - passive trip point.
> + * passive trip point.
> * @ops: operations this &thermal_zone_device supports
> * @tzp: thermal zone parameters
> * @governor: pointer to the governor for this thermal zone
> @@ -111,6 +112,8 @@ struct thermal_governor {
> * @poll_queue: delayed work for polling
> * @notify_event: Last notification event
> * @state: current state of the thermal zone
> + * @debugfs: this thermal zone device's thermal zone debug info
> + * @user_thresholds: list of userspace thresholds for temp. limit notifications
> * @trips: array of struct thermal_trip objects
> */
> struct thermal_zone_device {
Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
^ permalink raw reply
* [PATCH v2 13/20] thermal/qcom/lmh: Replace IRQF_ONESHOT with IRQF_NO_THREAD
From: Sebastian Andrzej Siewior @ 2026-01-28 9:55 UTC (permalink / raw)
To: linux-kernel
Cc: Thomas Gleixner, Sebastian Andrzej Siewior, Amit Kucheria,
Thara Gopinath, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui,
Lukasz Luba, linux-pm, linux-arm-msm
In-Reply-To: <20260128095540.863589-1-bigeasy@linutronix.de>
Passing IRQF_ONESHOT ensures that the interrupt source is masked until
the secondary (threaded) handler is done. If only a primary handler is
used then the flag makes no sense because the interrupt can not fire
(again) while its handler is running.
The flag also disallows force-threading of the primary handler and the
irq-core will warn about this.
The intention here was probably not allowing forced-threading.
Replace IRQF_ONESHOT with IRQF_NO_THREAD.
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
Cc: Amit Kucheria <amitk@kernel.org>
Cc: Thara Gopinath <thara.gopinath@gmail.com>
Cc: Rafael J. Wysocki <rafael@kernel.org>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Zhang Rui <rui.zhang@intel.com>
Cc: Lukasz Luba <lukasz.luba@arm.com>
Cc: linux-pm@vger.kernel.org
Cc: linux-arm-msm@vger.kernel.org
---
drivers/thermal/qcom/lmh.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/thermal/qcom/lmh.c b/drivers/thermal/qcom/lmh.c
index ddadcfada5136..3d072b7a4a6dd 100644
--- a/drivers/thermal/qcom/lmh.c
+++ b/drivers/thermal/qcom/lmh.c
@@ -220,7 +220,7 @@ static int lmh_probe(struct platform_device *pdev)
/* Disable the irq and let cpufreq enable it when ready to handle the interrupt */
irq_set_status_flags(lmh_data->irq, IRQ_NOAUTOEN);
ret = devm_request_irq(dev, lmh_data->irq, lmh_handle_irq,
- IRQF_ONESHOT | IRQF_NO_SUSPEND,
+ IRQF_NO_THREAD | IRQF_NO_SUSPEND,
"lmh-irq", lmh_data);
if (ret) {
dev_err(dev, "Error %d registering irq %x\n", ret, lmh_data->irq);
--
2.51.0
^ permalink raw reply related
* Re: [PATCH v1 2/8] remoteproc: qcom: probe all child devices
From: Konrad Dybcio @ 2026-01-28 9:45 UTC (permalink / raw)
To: Gaurav Kohli, Dmitry Baryshkov
Cc: Bjorn Andersson, mathieu.poirier, robh, krzk+dt, conor+dt, rafael,
daniel.lezcano, rui.zhang, lukasz.luba, konradybcio, amitk, mani,
casey.connolly, linux-arm-msm, devicetree, linux-kernel, linux-pm
In-Reply-To: <ef1911f5-2d96-428c-93f1-3d1815710894@oss.qualcomm.com>
On 1/28/26 10:39 AM, Gaurav Kohli wrote:
>
> On 1/27/2026 10:11 PM, Dmitry Baryshkov wrote:
>> On Tue, Jan 27, 2026 at 09:42:10PM +0530, Gaurav Kohli wrote:
>>> On 1/24/2026 12:33 AM, Dmitry Baryshkov wrote:
>>>> On Fri, Jan 23, 2026 at 07:23:39PM +0530, Gaurav Kohli wrote:
>>>>> On 1/8/2026 12:37 PM, Gaurav Kohli wrote:
>>>>>> On 1/3/2026 8:26 PM, Bjorn Andersson wrote:
>>>>>>> On Tue, Dec 23, 2025 at 06:02:21PM +0530, Gaurav Kohli wrote:
>>>>>>>> From: Casey Connolly <casey.connolly@linaro.org>
>>>>>>>>
>>>>>>>> Generalise the qcom,bam-dmux child node support by probing all
>>>>>>>> remoteproc children with of_platform_populate(). This will be used to
>>>>>>>> enable support for devices which are best represented as
>>>>>>>> subnodes of the
>>>>>>>> remoteproc, such as those representing QMI clients.
>>>>>>> Please flip this around, start with the description of the problem
>>>>>>> you're trying to solve.
>>>>>>>
>>>>>>>> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
>>>>>>> This must have your signed-off-by, where you certifies the origin of
>>>>>>> this patch.
>>>>>>>
>>>>>>>> ---
>>>>>>>> drivers/remoteproc/qcom_q6v5.c | 4 ++++
>>>>>>>> drivers/remoteproc/qcom_q6v5_mss.c | 8 --------
>>>>>>>> 2 files changed, 4 insertions(+), 8 deletions(-)
>>>>>>>>
>>>>>>>> diff --git a/drivers/remoteproc/qcom_q6v5.c
>>>>>>>> b/drivers/remoteproc/qcom_q6v5.c
>>>>>>>> index 58d5b85e58cd..a02839c7ed8c 100644
>>>>>>>> --- a/drivers/remoteproc/qcom_q6v5.c
>>>>>>>> +++ b/drivers/remoteproc/qcom_q6v5.c
>>>>>>>> @@ -6,6 +6,7 @@
>>>>>>>> * Copyright (C) 2014 Sony Mobile Communications AB
>>>>>>>> * Copyright (c) 2012-2013, The Linux Foundation. All rights
>>>>>>>> reserved.
>>>>>>>> */
>>>>>>>> +#include <linux/of_platform.h>
>>>>>>>> #include <linux/kernel.h>
>>>>>>>> #include <linux/platform_device.h>
>>>>>>>> #include <linux/interconnect.h>
>>>>>>>> @@ -351,6 +352,8 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5,
>>>>>>>> struct platform_device *pdev,
>>>>>>>> return dev_err_probe(&pdev->dev, PTR_ERR(q6v5->path),
>>>>>>>> "failed to acquire interconnect path\n");
>>>>>>>> + of_platform_populate(q6v5->dev->of_node, NULL, NULL, q6v5->dev);
>>>>>>> There are other child nodes here, in particular the GLINK and SMD edges.
>>>>>>> Do we really want platform_devices registered for them?
>>>>>>>
>>>>>>> Regards,
>>>>>>> Bjorn
>>>>>> thanks for pointing this, can you please suggest the right approach.
>>>>>>
>>>>>> This should not impact glink, as that is registering as rproc sub node,
>>>>>> And we need rproc cooling as child node
>>>>>>
>>>>>> of remote proc subsytem to create probe dependency only.
>>>>>>
>>>>>>
>>>>>> Can we do platform populate for specific child, would that be right
>>>>>> approach. or we should create rproc cooling as independent of parent ?
>>>>>>
>>>>> HI Bjorn,
>>>>>
>>>>> I’d like to highlight the impact and details of placement of remoteproc
>>>>> cooling dt node:
>>>>>
>>>>>
>>>>> ->As a child of the remote proc subsystem node:
>>>>> In this configuration, the cooling device will only be probed once the
>>>>> corresponding remote proc subsystem itself is probed.
>>>>>
>>>>> ->Outside the remote proc subsystem, may be part of soc node:
>>>>> In this setup, the cooling device will be probed independently. It will
>>>>> wait until the remoteproc subsystem is brought up
>>>>> before completing cooling registration.
>>>>> The drawback here is that if the parent remoteproc subsystem is
>>>>> disabled, the cooling device will still undergo an
>>>>> unnecessary probe, even though it cannot be registered.
>>>> Bjorns question was different. It wasn't about pushing cooling device
>>>> outside of the remoteproc node. It is about not registering the devices.
>>>>
>>>> Can we follow the approach outlined by qcom_add_smd_subdev() /
>>>> qcom_add_glink_subdev()?
>>>
>>> Hi Dmitry,
>>>
>>> Thanks for the review. Since the remoteproc cooling is a QMI-based driver,
>>> it will receive the
>>> subsystem up notification directly. Therefore, there’s no need to make it a
>>> subdev node or
>>> tie it into the init/reset sequence of remoteproc subsytem.
>> But you've added a subnode for it (and we are discussing exactly
>> of_platform_populate()) call. So, you are tying it to the remoteproc
>> device lifecycle instead of the remoteproc subsys, which seems strange
>> to me. There is no cooling device if the DSP is not running.
>
>
> For the cooling feature, we don’t need to define it as a subnode. The cooling subsystem becomes relevant only
> after the remote subsystem is up, at which point it will receive add/delete notifications from the QMI server.
>
>
> If child nodes must be modeled as subnodes for rproc, we can move the CDSP TMD out of the remoteproc and add in soc.
> Is there currently a way for the remoteproc core layer to call of_platform_populate() without requiring a subnode?
I think the question is "why can't you register the remoteproc device
as a cooling_device, with perhaps #cooling-cells = <1>; instead of
any form of children?"
Konrad
^ permalink raw reply
* Re: [PATCH v1 2/8] remoteproc: qcom: probe all child devices
From: Gaurav Kohli @ 2026-01-28 9:39 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Bjorn Andersson, mathieu.poirier, robh, krzk+dt, conor+dt, rafael,
daniel.lezcano, rui.zhang, lukasz.luba, konradybcio, amitk, mani,
casey.connolly, linux-arm-msm, devicetree, linux-kernel, linux-pm
In-Reply-To: <ctrpymbvjlchp3djnsqq4bghkq2zvqnf5bebszi74f3d36l5dv@icvnkdwgdxmi>
On 1/27/2026 10:11 PM, Dmitry Baryshkov wrote:
> On Tue, Jan 27, 2026 at 09:42:10PM +0530, Gaurav Kohli wrote:
>> On 1/24/2026 12:33 AM, Dmitry Baryshkov wrote:
>>> On Fri, Jan 23, 2026 at 07:23:39PM +0530, Gaurav Kohli wrote:
>>>> On 1/8/2026 12:37 PM, Gaurav Kohli wrote:
>>>>> On 1/3/2026 8:26 PM, Bjorn Andersson wrote:
>>>>>> On Tue, Dec 23, 2025 at 06:02:21PM +0530, Gaurav Kohli wrote:
>>>>>>> From: Casey Connolly <casey.connolly@linaro.org>
>>>>>>>
>>>>>>> Generalise the qcom,bam-dmux child node support by probing all
>>>>>>> remoteproc children with of_platform_populate(). This will be used to
>>>>>>> enable support for devices which are best represented as
>>>>>>> subnodes of the
>>>>>>> remoteproc, such as those representing QMI clients.
>>>>>> Please flip this around, start with the description of the problem
>>>>>> you're trying to solve.
>>>>>>
>>>>>>> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
>>>>>> This must have your signed-off-by, where you certifies the origin of
>>>>>> this patch.
>>>>>>
>>>>>>> ---
>>>>>>> drivers/remoteproc/qcom_q6v5.c | 4 ++++
>>>>>>> drivers/remoteproc/qcom_q6v5_mss.c | 8 --------
>>>>>>> 2 files changed, 4 insertions(+), 8 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/remoteproc/qcom_q6v5.c
>>>>>>> b/drivers/remoteproc/qcom_q6v5.c
>>>>>>> index 58d5b85e58cd..a02839c7ed8c 100644
>>>>>>> --- a/drivers/remoteproc/qcom_q6v5.c
>>>>>>> +++ b/drivers/remoteproc/qcom_q6v5.c
>>>>>>> @@ -6,6 +6,7 @@
>>>>>>> * Copyright (C) 2014 Sony Mobile Communications AB
>>>>>>> * Copyright (c) 2012-2013, The Linux Foundation. All rights
>>>>>>> reserved.
>>>>>>> */
>>>>>>> +#include <linux/of_platform.h>
>>>>>>> #include <linux/kernel.h>
>>>>>>> #include <linux/platform_device.h>
>>>>>>> #include <linux/interconnect.h>
>>>>>>> @@ -351,6 +352,8 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5,
>>>>>>> struct platform_device *pdev,
>>>>>>> return dev_err_probe(&pdev->dev, PTR_ERR(q6v5->path),
>>>>>>> "failed to acquire interconnect path\n");
>>>>>>> + of_platform_populate(q6v5->dev->of_node, NULL, NULL, q6v5->dev);
>>>>>> There are other child nodes here, in particular the GLINK and SMD edges.
>>>>>> Do we really want platform_devices registered for them?
>>>>>>
>>>>>> Regards,
>>>>>> Bjorn
>>>>> thanks for pointing this, can you please suggest the right approach.
>>>>>
>>>>> This should not impact glink, as that is registering as rproc sub node,
>>>>> And we need rproc cooling as child node
>>>>>
>>>>> of remote proc subsytem to create probe dependency only.
>>>>>
>>>>>
>>>>> Can we do platform populate for specific child, would that be right
>>>>> approach. or we should create rproc cooling as independent of parent ?
>>>>>
>>>> HI Bjorn,
>>>>
>>>> I’d like to highlight the impact and details of placement of remoteproc
>>>> cooling dt node:
>>>>
>>>>
>>>> ->As a child of the remote proc subsystem node:
>>>> In this configuration, the cooling device will only be probed once the
>>>> corresponding remote proc subsystem itself is probed.
>>>>
>>>> ->Outside the remote proc subsystem, may be part of soc node:
>>>> In this setup, the cooling device will be probed independently. It will
>>>> wait until the remoteproc subsystem is brought up
>>>> before completing cooling registration.
>>>> The drawback here is that if the parent remoteproc subsystem is
>>>> disabled, the cooling device will still undergo an
>>>> unnecessary probe, even though it cannot be registered.
>>> Bjorns question was different. It wasn't about pushing cooling device
>>> outside of the remoteproc node. It is about not registering the devices.
>>>
>>> Can we follow the approach outlined by qcom_add_smd_subdev() /
>>> qcom_add_glink_subdev()?
>>
>> Hi Dmitry,
>>
>> Thanks for the review. Since the remoteproc cooling is a QMI-based driver,
>> it will receive the
>> subsystem up notification directly. Therefore, there’s no need to make it a
>> subdev node or
>> tie it into the init/reset sequence of remoteproc subsytem.
> But you've added a subnode for it (and we are discussing exactly
> of_platform_populate()) call. So, you are tying it to the remoteproc
> device lifecycle instead of the remoteproc subsys, which seems strange
> to me. There is no cooling device if the DSP is not running.
For the cooling feature, we don’t need to define it as a subnode. The
cooling subsystem becomes relevant only
after the remote subsystem is up, at which point it will receive
add/delete notifications from the QMI server.
If child nodes must be modeled as subnodes for rproc, we can move the
CDSP TMD out of the remoteproc and add in soc.
Is there currently a way for the remoteproc core layer to call
of_platform_populate() without requiring a subnode?
^ permalink raw reply
* [PATCH] thermal: core: thermal_core.h: fix all kernel-doc warnings
From: Randy Dunlap @ 2026-01-28 6:24 UTC (permalink / raw)
To: linux-kernel
Cc: Randy Dunlap, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui,
Lukasz Luba, linux-pm
Resolve all kernel-doc warnings in thermal_core.h:
Warning: drivers/thermal/thermal_core.h:99 bad line: trip point.
Warning: drivers/thermal/thermal_core.h:101 bad line: passive trip point.
Warning: drivers/thermal/thermal_core.h:152 struct member 'trips_attribute_group' not described in 'thermal_zone_device'
Warning: drivers/thermal/thermal_core.h:152 struct member 'debugfs' not described in 'thermal_zone_device'
Warning: drivers/thermal/thermal_core.h:152 struct member 'user_thresholds' not described in 'thermal_zone_device'
Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
---
Cc: Rafael J. Wysocki <rafael@kernel.org>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Zhang Rui <rui.zhang@intel.com>
Cc: Lukasz Luba <lukasz.luba@arm.com>
Cc: linux-pm@vger.kernel.org
drivers/thermal/thermal_core.h | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
--- linux-next-20260126.orig/drivers/thermal/thermal_core.h
+++ linux-next-20260126/drivers/thermal/thermal_core.h
@@ -77,6 +77,7 @@ struct thermal_governor {
* @device: &struct device for this thermal zone
* @removal: removal completion
* @resume: resume completion
+ * @trips_attribute_group: trip point sysfs attributes
* @trips_high: trips above the current zone temperature
* @trips_reached: trips below or at the current zone temperature
* @trips_invalid: trips with invalid temperature
@@ -97,9 +98,9 @@ struct thermal_governor {
* @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION
* @passive: 1 if you've crossed a passive trip point, 0 otherwise.
* @prev_low_trip: the low current temperature if you've crossed a passive
- trip point.
+ * trip point.
* @prev_high_trip: the above current temperature if you've crossed a
- passive trip point.
+ * passive trip point.
* @ops: operations this &thermal_zone_device supports
* @tzp: thermal zone parameters
* @governor: pointer to the governor for this thermal zone
@@ -111,6 +112,8 @@ struct thermal_governor {
* @poll_queue: delayed work for polling
* @notify_event: Last notification event
* @state: current state of the thermal zone
+ * @debugfs: this thermal zone device's thermal zone debug info
+ * @user_thresholds: list of userspace thresholds for temp. limit notifications
* @trips: array of struct thermal_trip objects
*/
struct thermal_zone_device {
^ permalink raw reply
* RE: [PATCH v1] cpuidle: governors: menu: Always check timers with tick stopped
From: Doug Smythies @ 2026-01-28 5:19 UTC (permalink / raw)
To: 'Rafael J. Wysocki', 'Christian Loehle'
Cc: 'LKML', 'Linux PM', Doug Smythies
In-Reply-To: <5959091.DvuYhMxLoT@rafael.j.wysocki>
[-- Attachment #1: Type: text/plain, Size: 2001 bytes --]
On 2026.01.20 07:26 Rafael J. Wysocki wrote:
>From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>
> After commit 5484e31bbbff ("cpuidle: menu: Skip tick_nohz_get_sleep_length()
> call in some cases"), if the return value of get_typical_interval()
> multiplied by NSEC_PER_USEC is not greater than RESIDENCY_THRESHOLD_NS,
> the menu governor will skip computing the time till the closest timer.
> If that happens when the tick has been stopped already, the selected
> idle state may be too deep due to the subsequent check comparing
> predicted_ns with TICK_NSEC and causing its value to be replaced with
> the expected time till the closest timer, which is KTIME_MAX in that
> case. That will cause the deepest enabled idle state to be selected,
> but the time till the closest timer very well may be shorter than the
> target residency of that state, in which case a shallower state should
> be used.
>
> Address this by making menu_select() always compute the time till the
> closest timer when the tick has been stopped.
>
> Also move the predicted_ns check mentioned above into the branch in
> which the time till the closest timer is determined because it only
> needs to be done in that case.
>
> Fixes: 5484e31bbbff ("cpuidle: menu: Skip tick_nohz_get_sleep_length() call in some cases")
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
... snip ...
I have been testing with this patch, and have not observed any difference
in test results and energy use with and without this patch.
A couple of graphs are attached.
I did this work on top of the teo work, so those results are on the graphs also.
Legend:
rc5 = kernel 6.19-rc5
rjw = kernel 6.19-rc5 + original teo 5 patch set
rjw-1-1 = kernel 6.19-rc5 + current teo 5 patch set
(note: Not including the most recent V2 of patch 5 of 5,
now a newer 2 patch set.)
menu = kernel 6.19-rc5
menu-rjw = kernel 6.19-rc5 + this patch.
disable = all idle states except state 0 disabled, as a reference plot, at the cost of energy.
[-- Attachment #2: misses.png --]
[-- Type: image/png, Size: 41201 bytes --]
[-- Attachment #3: loop-times.png --]
[-- Type: image/png, Size: 49731 bytes --]
^ permalink raw reply
* RE: Performance regressions introduced via Revert "cpuidle: menu: Avoid discarding useful information" on 5.15 LTS
From: Doug Smythies @ 2026-01-28 5:06 UTC (permalink / raw)
To: 'Harshvardhan Jha', 'Christian Loehle'
Cc: 'Sasha Levin', 'Greg Kroah-Hartman', linux-pm,
stable, 'Rafael J. Wysocki', 'Daniel Lezcano',
Doug Smythies
In-Reply-To: <849ee0ff-e15b-4b69-84de-6503e3b3168d@oracle.com>
[-- Attachment #1: Type: text/plain, Size: 8076 bytes --]
On 2026.01.27 07:45 Harshvardhan Jha wrote:
>On 08/12/25 6:17 PM, Christian Loehle wrote:
>> On 12/8/25 11:33, Harshvardhan Jha wrote:
>>> On 04/12/25 4:00 AM, Doug Smythies wrote:
>>>> On 2025.12.03 08:45 Christian Loehle wrote:
>>>>> On 12/3/25 16:18, Harshvardhan Jha wrote:
>>>>>>
>>>>>> While running performance benchmarks for the 5.15.196 LTS tags , it was
>>>>>> observed that several regressions across different benchmarks is being
>>>>>> introduced when compared to the previous 5.15.193 kernel tag. Running an
>>>>>> automated bisect on both of them narrowed down the culprit commit to:
>>>>>> - 5666bcc3c00f7 Revert "cpuidle: menu: Avoid discarding useful
>>>>>> information" for 5.15
>>>>>>
>>>>>> Regressions on 5.15.196 include:
>>>>>> -9.3% : Phoronix pts/sqlite using 2 processes on OnPrem X6-2
>>>>>> -6.3% : Phoronix system/sqlite on OnPrem X6-2
>>>>>> -18% : rds-stress -M 1 (readonly rdma-mode) metrics with 1 depth & 1
>>>>>> thread & 1M buffer size on OnPrem X6-2
>>>>>> -4 -> -8% : rds-stress -M 2 (writeonly rdma-mode) metrics with 1 depth &
>>>>>> 1 thread & 1M buffer size on OnPrem X6-2
>>>>>> Up to -30% : Some Netpipe metrics on OnPrem X5-2
>>>>>>
>>>>>> The culprit commits' messages mention that these reverts were done due
>>>>>> to performance regressions introduced in Intel Jasper Lake systems but
>>>>>> this revert is causing issues in other systems unfortunately. I wanted
>>>>>> to know the maintainers' opinion on how we should proceed in order to
>>>>>> fix this. If we reapply it'll bring back the previous regressions on
>>>>>> Jasper Lake systems and if we don't revert it then it's stuck with
>>>>>> current regressions. If this problem has been reported before and a fix
>>>>>> is in the works then please let me know I shall follow developments to
>>>>>> that mail thread.
>>>>> The discussion regarding this can be found here:
>>>>> https://urldefense.com/v3/__https://lore.kernel.org/lkml/36iykr223vmcfsoysexug6s274nq2oimcu55ybn6ww4il3g3cv@cohflgdbpnq7/__;!!ACWV5N9M2RV99hQ!MWXEz_wRbaLyJxDign2EXci2qNzAPpCyhi8qIORMdReh0g_yIVIt-Oqov23KT23A_rGBRRxJ4bHb_e6UQA-b9PW7hw$
>>>>> we explored an alternative to the full revert here:
>>>>> https://urldefense.com/v3/__https://lore.kernel.org/lkml/4687373.LvFx2qVVIh@rafael.j.wysocki/__;!!ACWV5N9M2RV99hQ!MWXEz_wRbaLyJxDign2EXci2qNzAPpCyhi8qIORMdReh0g_yIVIt-Oqov23KT23A_rGBRRxJ4bHb_e6UQA9PSf_uMQ$
>>>>> unfortunately that didn't lead anywhere useful, so Rafael went with the
>>>>> full revert you're seeing now.
>>>>>
>>>>> Ultimately it seems to me that this "aggressiveness" on deep idle tradeoffs
>>>>> will highly depend on your platform, but also your workload, Jasper Lake
>>>>> in particular seems to favor deep idle states even when they don't seem
>>>>> to be a 'good' choice from a purely cpuidle (governor) perspective, so
>>>>> we're kind of stuck with that.
>>>>>
>>>>> For teo we've discussed a tunable knob in the past, which comes naturally with
>>>>> the logic, for menu there's nothing obvious that would be comparable.
>>>>> But for teo such a knob didn't generate any further interest (so far).
>>>>>
>>>>> That's the status, unless I missed anything?
>>>> By reading everything in the links Chrsitian provided, you can see
>>>> that we had difficulties repeating test results on other platforms.
>>>>
>>>> Of the tests listed herein, the only one that was easy to repeat on my
>>>> test server, was the " Phoronix pts/sqlite" one. I got (summary: no difference):
>>>>
>>>> Kernel 6.18 Reverted
>>>> pts/sqlite-2.3.0 menu rc4 menu rc1 menu rc1 menu rc3
>>>> performance performance performance performance
>>>> test what ave ave ave ave
>>>> 1 T/C 1 2.147 -0.2% 2.143 0.0% 2.16 -0.8% 2.156 -0.6%
>>>> 2 T/C 2 3.468 0.1% 3.473 0.0% 3.486 -0.4% 3.478 -0.1%
>>>> 3 T/C 4 4.336 0.3% 4.35 0.0% 4.355 -0.1% 4.354 -0.1%
>>>> 4 T/C 8 5.438 -0.1% 5.434 0.0% 5.456 -0.4% 5.45 -0.3%
>>>> 5 T/C 12 6.314 -0.2% 6.299 0.0% 6.307 -0.1% 6.29 0.1%
>>>>
>>>> Where:
>>>> T/C means: Threads / Copies
>>>> performance means: intel_pstate CPU frequency scaling driver and the performance CPU frequencay scaling governor.
>>>> Data points are in Seconds.
>>>> Ave means the average test result. The number of runs per test was increased from the default of 3 to 10.
>>>> The reversion was manually applied to kernel 6.18-rc1 for that test.
>>>> The reversion was included in kernel 6.18-rc3.
>>>> Kernel 6.18-rc4 had another code change to menu.c
>>>>
>>>> In case the formatting gets messed up, the table is also attached.
>>>>
>>>> Processor: Intel(R) Core(TM) i5-10600K CPU @ 4.10GHz, 6 cores 12 CPUs.
>>>> HWP: Enabled.
>>> I was able to recover performance on 5.15 and 5.4 LTS based kernels
>>> after reapplying the revert on X6-2 systems.
>>>
>>> Architecture: x86_64
>>> CPU op-mode(s): 32-bit, 64-bit
>>> Address sizes: 46 bits physical, 48 bits virtual
>>> Byte Order: Little Endian
>>> CPU(s): 56
>>> On-line CPU(s) list: 0-55
>>> Vendor ID: GenuineIntel
>>> Model name: Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz
>>> CPU family: 6
>>> Model: 79
>>> Thread(s) per core: 2
>>> Core(s) per socket: 14
>>> Socket(s): 2
>>> Stepping: 1
>>> CPU(s) scaling MHz: 98%
>>> CPU max MHz: 2600.0000
>>> CPU min MHz: 1200.0000
>>> BogoMIPS: 5188.26
... snip ...
>> It would be nice to get the idle states here, ideally how the states' usage changed
>> from base to revert.
>> The mentioned thread did this and should show how it can be done, but a dump of
>> cat /sys/devices/system/cpu/cpu*/cpuidle/state*/*
>> before and after the workload is usually fine to work with:
>> https://urldefense.com/v3/__https://lore.kernel.org/linux-pm/8da42386-282e-4f97-af93-4715ae206361@arm.com/__;!!ACWV5N9M2RV99hQ!PEhkFcO7emFLMaNxWEoE2Gtnw3zSkpghP17iuEvZM3W6KUpmkbgKw_tr91FwGfpzm4oA5f7c5sz8PkYvKiEVwI_iLIPpMt53$
> Apologies for the late reply, I'm attaching a tar ball which has the cpu
> states for the test suites before and after tests. The folders with the
> name of the test contain two folders good-kernel and bad-kernel
> containing two files having the before and after states. Please note
> that different machines were used for different test suites due to
> compatibility reasons. The jbb test was run using containers.
It is a considerable amount of work to manually extract and summarize the data.
I have only done it for the phoronix-sqlite data.
There seems to be 40 CPUs, 5 idle states, with idle state 3 defaulting to disabled.
I remember seeing a Linux-pm email about why but couldn't find it just now.
Summary (also attached as a PNG file, in case the formatting gets messed up):
The total idle entries (usage) and time seem low to me, which is why the ???.
phoronix-sqlite
Good Kernel: Time between samples 4 seconds (estimated and ???)
Usage Above Below Above Below
state 0 220 0 218 0.00% 99.09%
state 1 70212 5213 34602 7.42% 49.28%
state 2 30273 5237 1806 17.30% 5.97%
state 3 0 0 0 0.00% 0.00%
state 4 11824 2120 0 17.93% 0.00%
total 112529 12570 36626 43.72% <<< Misses %
Bad Kernel: Time between samples 3.8 seconds (estimated and ???)
Usage Above Below Above Below
state 0 262 0 260 0.00% 99.24%
state 1 62751 3985 35588 6.35% 56.71%
state 2 24941 7896 1433 31.66% 5.75%
state 3 0 0 0 0.00% 0.00%
state 4 24489 11543 0 47.14% 0.00%
total 112443 23424 37281 53.99% <<< Misses %
Observe 2X use of idle state 4 for the "Bad Kernel"
I have a template now, and can summarize the other 40 CPU data
faster, but I would have to rework the template for the 56 CPU data,
and is it a 64 CPU data set at 4 idle states per CPU?
... Doug
[-- Attachment #2: sqlite-summary.png --]
[-- Type: image/png, Size: 37171 bytes --]
^ permalink raw reply
* [rafael-pm:fixes] BUILD SUCCESS 1730daa3b425ea8c88ae599af6e1a4957bd2d81a
From: kernel test robot @ 2026-01-28 4:41 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: linux-acpi, linux-pm
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git fixes
branch HEAD: 1730daa3b425ea8c88ae599af6e1a4957bd2d81a Merge tag 'cpufreq-arm-fixes-6.19-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm
elapsed time: 870m
configs tested: 230
configs skipped: 3
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-15.2.0
alpha allyesconfig gcc-15.2.0
alpha defconfig gcc-15.2.0
arc allmodconfig clang-16
arc allmodconfig gcc-15.2.0
arc allnoconfig gcc-15.2.0
arc allyesconfig clang-22
arc allyesconfig gcc-15.2.0
arc defconfig gcc-15.2.0
arc randconfig-001-20260128 gcc-8.5.0
arc randconfig-002-20260128 gcc-8.5.0
arc vdk_hs38_smp_defconfig gcc-15.2.0
arm allnoconfig clang-22
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-16
arm allyesconfig gcc-15.2.0
arm defconfig gcc-15.2.0
arm randconfig-001-20260128 gcc-8.5.0
arm randconfig-002-20260128 gcc-8.5.0
arm randconfig-003-20260128 gcc-8.5.0
arm randconfig-004-20260128 gcc-8.5.0
arm vexpress_defconfig gcc-15.2.0
arm64 alldefconfig gcc-15.2.0
arm64 allmodconfig clang-19
arm64 allmodconfig clang-22
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-15.2.0
arm64 randconfig-001-20260128 gcc-14.3.0
arm64 randconfig-002-20260128 gcc-14.3.0
arm64 randconfig-003-20260128 gcc-14.3.0
arm64 randconfig-004-20260128 gcc-14.3.0
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-15.2.0
csky randconfig-001-20260128 gcc-14.3.0
csky randconfig-002-20260128 gcc-14.3.0
hexagon allmodconfig clang-17
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig clang-22
hexagon allnoconfig gcc-15.2.0
hexagon defconfig gcc-15.2.0
hexagon randconfig-001-20260128 clang-22
hexagon randconfig-002-20260128 clang-22
i386 allmodconfig clang-20
i386 allmodconfig gcc-14
i386 allnoconfig gcc-14
i386 allnoconfig gcc-15.2.0
i386 allyesconfig clang-20
i386 allyesconfig gcc-14
i386 buildonly-randconfig-001-20260128 clang-20
i386 buildonly-randconfig-002-20260128 clang-20
i386 buildonly-randconfig-003-20260128 clang-20
i386 buildonly-randconfig-004-20260128 clang-20
i386 buildonly-randconfig-005-20260128 clang-20
i386 buildonly-randconfig-006-20260128 clang-20
i386 defconfig gcc-15.2.0
i386 randconfig-001-20260128 gcc-14
i386 randconfig-002-20260128 gcc-14
i386 randconfig-003-20260128 gcc-14
i386 randconfig-004-20260128 gcc-14
i386 randconfig-005-20260128 gcc-14
i386 randconfig-006-20260128 gcc-14
i386 randconfig-007-20260128 gcc-14
i386 randconfig-011-20260128 clang-20
i386 randconfig-012-20260128 clang-20
i386 randconfig-012-20260128 gcc-14
i386 randconfig-013-20260128 clang-20
i386 randconfig-013-20260128 gcc-14
i386 randconfig-014-20260128 clang-20
i386 randconfig-015-20260128 clang-20
i386 randconfig-016-20260128 clang-20
i386 randconfig-016-20260128 gcc-14
i386 randconfig-017-20260128 clang-20
loongarch allmodconfig clang-19
loongarch allmodconfig clang-22
loongarch allnoconfig clang-22
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-19
loongarch randconfig-001-20260128 clang-22
loongarch randconfig-002-20260128 clang-22
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-16
m68k allyesconfig gcc-15.2.0
m68k defconfig clang-19
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze defconfig clang-19
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
mips cavium_octeon_defconfig gcc-15.2.0
mips cu1830-neo_defconfig gcc-15.2.0
mips gpr_defconfig clang-18
mips ip32_defconfig gcc-15.2.0
mips jazz_defconfig clang-17
mips loongson3_defconfig gcc-15.2.0
mips malta_defconfig gcc-15.2.0
mips malta_kvm_defconfig gcc-15.2.0
mips qi_lb60_defconfig clang-22
nios2 allmodconfig clang-22
nios2 allmodconfig gcc-11.5.0
nios2 allnoconfig clang-22
nios2 allnoconfig gcc-11.5.0
nios2 defconfig clang-19
nios2 randconfig-001-20260128 clang-22
nios2 randconfig-002-20260128 clang-22
openrisc allmodconfig clang-22
openrisc allmodconfig gcc-15.2.0
openrisc allnoconfig clang-22
openrisc allnoconfig gcc-15.2.0
openrisc defconfig gcc-15.2.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-22
parisc allnoconfig gcc-15.2.0
parisc allyesconfig clang-19
parisc allyesconfig gcc-15.2.0
parisc defconfig gcc-15.2.0
parisc randconfig-001-20260128 gcc-11.5.0
parisc randconfig-002-20260128 gcc-11.5.0
parisc randconfig-002-20260128 gcc-12.5.0
parisc64 defconfig clang-19
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-22
powerpc allnoconfig gcc-15.2.0
powerpc randconfig-001-20260128 clang-22
powerpc randconfig-001-20260128 gcc-11.5.0
powerpc randconfig-002-20260128 gcc-11.5.0
powerpc randconfig-002-20260128 gcc-8.5.0
powerpc64 randconfig-001-20260128 clang-22
powerpc64 randconfig-001-20260128 gcc-11.5.0
powerpc64 randconfig-002-20260128 clang-22
powerpc64 randconfig-002-20260128 gcc-11.5.0
riscv allmodconfig clang-22
riscv allnoconfig clang-22
riscv allnoconfig gcc-15.2.0
riscv allyesconfig clang-16
riscv defconfig gcc-15.2.0
riscv randconfig-001-20260128 gcc-13.4.0
riscv randconfig-002-20260128 gcc-13.4.0
s390 allmodconfig clang-18
s390 allmodconfig clang-19
s390 allnoconfig clang-22
s390 allyesconfig gcc-15.2.0
s390 defconfig gcc-15.2.0
s390 randconfig-001-20260128 gcc-13.4.0
s390 randconfig-002-20260128 gcc-13.4.0
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-22
sh allnoconfig gcc-15.2.0
sh allyesconfig clang-19
sh allyesconfig gcc-15.2.0
sh defconfig gcc-14
sh randconfig-001-20260128 gcc-13.4.0
sh randconfig-002-20260128 gcc-13.4.0
sh sh2007_defconfig gcc-15.2.0
sh shx3_defconfig gcc-15.2.0
sparc allnoconfig clang-22
sparc allnoconfig gcc-15.2.0
sparc defconfig gcc-15.2.0
sparc randconfig-001-20260128 gcc-11.5.0
sparc randconfig-001-20260128 gcc-8.5.0
sparc randconfig-002-20260128 gcc-11.5.0
sparc randconfig-002-20260128 gcc-8.5.0
sparc64 allmodconfig clang-22
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260128 gcc-11.5.0
sparc64 randconfig-001-20260128 gcc-12.5.0
sparc64 randconfig-002-20260128 gcc-10.5.0
sparc64 randconfig-002-20260128 gcc-11.5.0
um allmodconfig clang-19
um allnoconfig clang-22
um allyesconfig gcc-14
um allyesconfig gcc-15.2.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260128 gcc-11.5.0
um randconfig-001-20260128 gcc-12
um randconfig-002-20260128 clang-22
um randconfig-002-20260128 gcc-11.5.0
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-20
x86_64 allnoconfig clang-22
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001-20260128 gcc-14
x86_64 buildonly-randconfig-002-20260128 gcc-14
x86_64 buildonly-randconfig-003-20260128 gcc-14
x86_64 buildonly-randconfig-004-20260128 gcc-14
x86_64 buildonly-randconfig-005-20260128 gcc-14
x86_64 buildonly-randconfig-006-20260128 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-20
x86_64 randconfig-001-20260128 gcc-13
x86_64 randconfig-002-20260128 gcc-13
x86_64 randconfig-003-20260128 gcc-13
x86_64 randconfig-004-20260128 gcc-13
x86_64 randconfig-005-20260128 gcc-13
x86_64 randconfig-006-20260128 gcc-13
x86_64 randconfig-011-20260128 clang-20
x86_64 randconfig-012-20260128 clang-20
x86_64 randconfig-013-20260128 clang-20
x86_64 randconfig-014-20260128 clang-20
x86_64 randconfig-015-20260128 clang-20
x86_64 randconfig-016-20260128 clang-20
x86_64 randconfig-071-20260128 clang-20
x86_64 randconfig-072-20260128 clang-20
x86_64 randconfig-073-20260128 clang-20
x86_64 randconfig-073-20260128 gcc-12
x86_64 randconfig-074-20260128 clang-20
x86_64 randconfig-074-20260128 gcc-14
x86_64 randconfig-075-20260128 clang-20
x86_64 randconfig-075-20260128 gcc-14
x86_64 randconfig-076-20260128 clang-20
x86_64 randconfig-076-20260128 gcc-14
x86_64 rhel-9.4 clang-20
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-20
x86_64 rhel-9.4-kselftests clang-20
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-20
xtensa allnoconfig clang-22
xtensa allnoconfig gcc-15.2.0
xtensa allyesconfig clang-22
xtensa cadence_csp_defconfig gcc-15.2.0
xtensa generic_kc705_defconfig gcc-15.2.0
xtensa randconfig-001-20260128 gcc-11.5.0
xtensa randconfig-001-20260128 gcc-8.5.0
xtensa randconfig-002-20260128 gcc-11.5.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [GIT PULL] cpufreq/arm updates for 7.0
From: Viresh Kumar @ 2026-01-28 4:07 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM
In-Reply-To: <CAJZ5v0gH-g5H4fqowtoNs1ZTDWR4xa4Wd9Db2uSJxjWW_KsZVg@mail.gmail.com>
On 27-01-26, 14:52, Rafael J. Wysocki wrote:
> Hi Viresh,
>
> On Tue, Jan 27, 2026 at 7:56 AM Viresh Kumar <viresh.kumar@linaro.org> wrote:
> >
> > Hi Rafael,
> >
> > The following changes since commit 7e3debb4c72fe840d60014192cf93950871fb3be:
> >
> > cpufreq: qcom-nvmem: add sentinel to qcom_cpufreq_ipq806x_match_list (2026-01-27 11:21:22 +0530)
> >
> > are available in the Git repository at:
> >
> > git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm.git tags/cpufreq-arm-updates-7.0-rc1
> >
> > for you to fetch changes up to 0b7fbf9333fa4699a53145bad8ce74ea986caa13:
> >
> > cpufreq: scmi: Fix device_node reference leak in scmi_cpu_domain_id() (2026-01-27 11:21:24 +0530)
> >
> > ----------------------------------------------------------------
> > CPUFreq arm updates for 7.0
>
> Is the 7.0 thing official already?
I looked at other pull requests and many of them were marked like
this. Moreover I did hear some discussion happen around it at LPC or
maintainers summit (not sure which one).
--
viresh
^ permalink raw reply
* [v2 PATCH 1/1] PM: QoS: Introduce boot parameter pm_qos_resume_latency_us
From: Aaron Tomlin @ 2026-01-28 3:31 UTC (permalink / raw)
To: rafael, dakr, pavel, lenb
Cc: akpm, bp, pmladek, rdunlap, feng.tang, pawan.kumar.gupta, kees,
elver, arnd, fvdl, lirongqing, bhelgaas, neelx, sean, mproche,
chjohnst, nick.lange, linux-kernel, linux-pm, linux-doc
In-Reply-To: <20260128033143.3456074-1-atomlin@atomlin.com>
Users currently lack a mechanism to define granular, per-CPU PM QoS
resume latency constraints during the early boot phase.
While the idle=poll boot parameter exists, it enforces a global
override, forcing all CPUs in the system to "poll". This global approach
is not suitable for asymmetric workloads where strict latency guarantees
are required only on specific critical CPUs, while housekeeping or
non-critical CPUs should be allowed to enter deeper idle states to save
energy.
Additionally, the existing sysfs interface
(/sys/devices/system/cpu/cpuN/power/pm_qos_resume_latency_us) becomes
available only after userspace initialisation. This is too late to
prevent deep C-state entry during the early kernel boot phase, which may
be required for debugging early boot hangs related to C-state
transitions or for workloads requiring strict latency guarantees
immediately upon system start.
This patch introduces the pm_qos_resume_latency_us kernel boot
parameter, which allows users to specify distinct resume latency
constraints for specific CPU ranges.
Syntax: pm_qos_resume_latency_us=range:value,range:value...
Unlike the sysfs interface which accepts the special string "n/a" to
remove a constraint, this boot parameter strictly requires integer
values. The special value "n/a" is not supported; the integer 0 must be
used to represent a 0 us latency constraint (polling).
For example:
"pm_qos_resume_latency_us=0:0,1-15:20"
Forces CPU 0 to poll on idle; constrains CPUs 1-15 to not enter a sleep
state that takes longer than 20 us to wake up. All other CPUs will have
the default (no resume latency) applied.
Implementation Details:
- The parameter string is captured via __setup() and parsed in
an early_initcall() to ensure suitable memory allocators are
available.
- Constraints are stored in a read-only linked list.
- The constraints are queried and applied in register_cpu().
This ensures the latency requirement is active immediately
upon CPU registration, effectively acting as a "birth"
constraint before the cpuidle governor takes over.
- The parsing logic enforces a "First Match Wins" policy: if a
CPU falls into multiple specified ranges, the latency value
from the first matching entry is used.
- The constraints persist across CPU hotplug events.
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
.../admin-guide/kernel-parameters.txt | 23 +++
drivers/base/cpu.c | 5 +-
include/linux/pm_qos.h | 5 +
kernel/power/qos.c | 141 ++++++++++++++++++
4 files changed, 172 insertions(+), 2 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 6a3d6bd0746c..afba39ecfdee 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2238,6 +2238,29 @@ Kernel parameters
icn= [HW,ISDN]
Format: <io>[,<membase>[,<icn_id>[,<icn_id2>]]]
+ pm_qos_resume_latency_us= [KNL,EARLY]
+ Format: <cpu-list>:<value>[,<cpu-list>:<value>...]
+
+ Establish per-CPU resume latency constraints. These constraints
+ are applied immediately upon CPU registration and persist
+ across CPU hotplug events.
+
+ For example:
+ "pm_qos_resume_latency_us=0:0,1-15:20"
+
+ This restricts CPU 0 to a 0us resume latency (effectively
+ forcing polling) and limits CPUs 1-15 to C-states with a
+ maximum exit latency of 20us. All other CPUs remain
+ unconstrained by this parameter.
+
+ Unlike the sysfs interface, which accepts the string "n/a" to
+ remove a constraint, this boot parameter strictly requires
+ integer values. To specify a 0us latency constraint (polling),
+ the integer 0 must be used.
+
+ NOTE: The parsing logic enforces a "First Match Wins" policy.
+ If a CPU is included in multiple specified ranges, the latency
+ value from the first matching entry takes precedence.
idle= [X86,EARLY]
Format: idle=poll, idle=halt, idle=nomwait
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index c6c57b6f61c6..1dea5bcd76a0 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -416,6 +416,7 @@ EXPORT_SYMBOL_GPL(cpu_subsys);
int register_cpu(struct cpu *cpu, int num)
{
int error;
+ s32 resume_latency;
cpu->node_id = cpu_to_node(num);
memset(&cpu->dev, 0x00, sizeof(struct device));
@@ -436,8 +437,8 @@ int register_cpu(struct cpu *cpu, int num)
per_cpu(cpu_sys_devices, num) = &cpu->dev;
register_cpu_under_node(num, cpu_to_node(num));
- dev_pm_qos_expose_latency_limit(&cpu->dev,
- PM_QOS_RESUME_LATENCY_NO_CONSTRAINT);
+ resume_latency = pm_qos_get_boot_cpu_latency_limit(num);
+ dev_pm_qos_expose_latency_limit(&cpu->dev, resume_latency);
set_cpu_enabled(num, true);
return 0;
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 6cea4455f867..556a7dff1419 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -174,6 +174,7 @@ static inline s32 cpu_wakeup_latency_qos_limit(void)
#ifdef CONFIG_PM
enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask);
enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask);
+s32 pm_qos_get_boot_cpu_latency_limit(unsigned int cpu);
s32 __dev_pm_qos_resume_latency(struct device *dev);
s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type);
int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
@@ -218,6 +219,10 @@ static inline s32 dev_pm_qos_raw_resume_latency(struct device *dev)
pm_qos_read_value(&dev->power.qos->resume_latency);
}
#else
+static inline s32 pm_qos_get_boot_cpu_latency_limit(unsigned int cpu)
+{
+ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
+}
static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev,
s32 mask)
{ return PM_QOS_FLAGS_UNDEFINED; }
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index f7d8064e9adc..e23223e3c7e8 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -34,6 +34,11 @@
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/cpumask.h>
+#include <linux/cpu.h>
+#include <linux/list.h>
+
+#include <asm/setup.h>
#include <linux/uaccess.h>
#include <linux/export.h>
@@ -46,6 +51,10 @@
*/
static DEFINE_SPINLOCK(pm_qos_lock);
+static LIST_HEAD(pm_qos_boot_list);
+
+static char pm_qos_resume_latency_cmdline[COMMAND_LINE_SIZE] __initdata;
+
/**
* pm_qos_read_value - Return the current effective constraint value.
* @c: List of PM QoS constraint requests.
@@ -209,6 +218,138 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf,
return prev_value != curr_value;
}
+struct pm_qos_boot_entry {
+ struct list_head node;
+ struct cpumask mask;
+ s32 latency;
+};
+
+static int __init pm_qos_resume_latency_us_setup(char *str)
+{
+ strscpy(pm_qos_resume_latency_cmdline, str,
+ sizeof(pm_qos_resume_latency_cmdline));
+ return 1;
+}
+__setup("pm_qos_resume_latency_us=", pm_qos_resume_latency_us_setup);
+
+/* init_pm_qos_resume_latency_us_setup - Parse the pm_qos_resume_latency_us boot parameter.
+ *
+ * Parses the kernel command line option "pm_qos_resume_resume_latency_us=" to establish
+ * per-CPU resume latency constraints. These constraints are applied
+ * immediately when a CPU is registered.
+ *
+ * Syntax: pm_qos_resume_latency_us=<cpu-list>:<value>[,<cpu-list>:<value>...]
+ * Example: pm_qos_resume_latency_us=0-3:0,4-7:20
+ *
+ * The parsing logic enforces a "First Match Wins" policy. If a CPU is
+ * covered by multiple entries in the list, only the first valid entry
+ * applies. Any subsequent overlapping ranges for that CPU are ignored.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int __init init_pm_qos_resume_latency_us_setup(void)
+{
+ char *token, *cmd = pm_qos_resume_latency_cmdline;
+ struct pm_qos_boot_entry *entry, *tentry;
+ cpumask_var_t covered;
+
+ if (!zalloc_cpumask_var(&covered, GFP_KERNEL)) {
+ pr_warn("pm_qos: Failed to allocate memory for parsing boot parameter\n");
+ return -ENOMEM;
+ }
+
+ while ((token = strsep(&cmd, ",")) != NULL) {
+ char *str_range, *str_val;
+
+ str_range = strsep(&token, ":");
+ str_val = token;
+
+ if (!str_val) {
+ pr_warn("pm_qos: Missing value range %s\n",
+ str_range);
+ continue;
+ }
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ pr_warn("pm_qos: Failed to allocate memory for boot entry\n");
+ goto cleanup;
+ }
+
+ if (cpulist_parse(str_range, &entry->mask)) {
+ pr_warn("pm_qos: Failed to parse cpulist range %s\n",
+ str_range);
+ kfree(entry);
+ continue;
+ }
+
+ cpumask_andnot(&entry->mask, &entry->mask, covered);
+ if (cpumask_empty(&entry->mask)) {
+ pr_warn("pm_qos: Entry %s already covered, ignoring\n",
+ str_range);
+ kfree(entry);
+ continue;
+ }
+ cpumask_or(covered, covered, &entry->mask);
+
+ if (kstrtos32(str_val, 0, &entry->latency)) {
+ pr_warn("pm_qos: Invalid latency requirement value %s\n",
+ str_val);
+ kfree(entry);
+ continue;
+ }
+
+ if (entry->latency < 0) {
+ pr_warn("pm_qos: Latency requirement cannot be negative: %d\n",
+ entry->latency);
+ kfree(entry);
+ continue;
+ }
+
+ list_add_tail(&entry->node, &pm_qos_boot_list);
+ }
+
+ free_cpumask_var(covered);
+ return 0;
+
+cleanup:
+ list_for_each_entry_safe(entry, tentry, &pm_qos_boot_list, node) {
+ list_del(&entry->node);
+ kfree(entry);
+ }
+
+ free_cpumask_var(covered);
+ return 0;
+}
+early_initcall(init_pm_qos_resume_latency_us_setup);
+
+/**
+ * pm_qos_get_boot_cpu_latency_limit - Get boot-time latency limit for a CPU.
+ * @cpu: Logical CPU number to check.
+ *
+ * Checks the read-only boot-time constraints list to see if a specific
+ * PM QoS latency override was requested for this CPU via the kernel
+ * command line.
+ *
+ * Return: The latency limit in microseconds if a constraint exists,
+ * or PM_QOS_RESUME_LATENCY_NO_CONSTRAINT if no boot override applies.
+ */
+s32 pm_qos_get_boot_cpu_latency_limit(unsigned int cpu)
+{
+ struct pm_qos_boot_entry *entry;
+
+ if (list_empty(&pm_qos_boot_list))
+ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
+
+ list_for_each_entry(entry, &pm_qos_boot_list, node) {
+ if (cpumask_test_cpu(cpu, &entry->mask))
+ return entry->latency;
+ }
+
+ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
+}
+EXPORT_SYMBOL_GPL(pm_qos_get_boot_cpu_latency_limit);
+
#ifdef CONFIG_CPU_IDLE
/* Definitions related to the CPU latency QoS. */
--
2.51.0
^ permalink raw reply related
* [v2 PATCH 0/1] PM: QoS: Introduce boot parameter pm_qos_resume_latency_us
From: Aaron Tomlin @ 2026-01-28 3:31 UTC (permalink / raw)
To: rafael, dakr, pavel, lenb
Cc: akpm, bp, pmladek, rdunlap, feng.tang, pawan.kumar.gupta, kees,
elver, arnd, fvdl, lirongqing, bhelgaas, neelx, sean, mproche,
chjohnst, nick.lange, linux-kernel, linux-pm, linux-doc
Hi Rafael, Danilo, Pavel, Len,
Users currently lack a mechanism to define granular, per-CPU PM QoS
resume latency constraints during the early boot phase.
While the "idle=poll" boot parameter exists, it enforces a global
override, forcing all CPUs in the system to "poll". This global approach
is not suitable for asymmetric workloads where strict latency guarantees
are required only on specific critical CPUs, while housekeeping or
non-critical CPUs should be allowed to enter deeper power states to save
energy.
Additionally, the existing sysfs interface
(/sys/devices/system/cpu/cpuN/power/pm_qos_resume_latency_us) becomes
available only after userspace initialisation. This is too late to
prevent deep C-state entry during the early kernel boot phase, which may
be required for debugging early boot hangs related to C-state
transitions or for workloads requiring strict latency guarantees
immediately upon system start.
This patch introduces the pm_qos_resume_latency_us kernel boot
parameter, which allows users to specify distinct resume latency
constraints for specific CPU ranges.
Syntax: pm_qos_resume_latency_us=range:value,range:value...
Unlike the sysfs interface which accepts the special string "n/a" to
remove a constraint, this boot parameter strictly requires integer
values. The special value "n/a" is not supported; the integer 0 must be
used to represent a 0 us latency constraint (polling).
For example:
"pm_qos_resume_latency_us=0:0,1-15:20"
Forces CPU 0 to poll on idle; constrains CPUs 1-15 to not enter a sleep
state that takes longer than 20 us to wake up. All other CPUs will have
the default (no resume latency) applied.
Implementation Details:
- The parameter string is captured via __setup() and parsed in
an early_initcall() to ensure suitable memory allocators are
available.
- Constraints are stored in a read-only linked list.
- The constraints are queried and applied in register_cpu().
This ensures the latency requirement is active immediately
upon CPU registration, effectively acting as a "birth"
constraint before the cpuidle governor takes over.
- The parsing logic enforces a "First Match Wins" policy: if a
CPU falls into multiple specified ranges, the latency value
from the first matching entry is used.
- The constraints persist across CPU hotplug events.
Please let me know your thoughts.
Changes since v1 [1]:
- Removed boot_option_idle_override == IDLE_POLL check
- Decoupled implementation from CONFIG_CPU_IDLE
- Added kernel-parameters.txt documentation
- Renamed internal setup functions for consistency
[1]: https://lore.kernel.org/lkml/20260123010024.3301276-1-atomlin@atomlin.com/
Aaron Tomlin (1):
PM: QoS: Introduce boot parameter pm_qos_resume_latency_us
.../admin-guide/kernel-parameters.txt | 23 +++
drivers/base/cpu.c | 5 +-
include/linux/pm_qos.h | 5 +
kernel/power/qos.c | 141 ++++++++++++++++++
4 files changed, 172 insertions(+), 2 deletions(-)
--
2.51.0
^ permalink raw reply
* Re: [PATCH] cpufreq: intel_pstate: Disable SMT when hybrid systems are enabled
From: Yaxiong Tian @ 2026-01-28 3:24 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Ricardo Neri, srinivas.pandruvada, lenb, viresh.kumar, linux-pm,
linux-kernel
In-Reply-To: <CAJZ5v0h-Kw8WLJZO5s5NsHTdGC5gomxeXDcbRLFhFoVmm43s9A@mail.gmail.com>
在 2026/1/28 00:08, Rafael J. Wysocki 写道:
> On Mon, Jan 26, 2026 at 3:15 AM Yaxiong Tian <tianyaxiong@kylinos.cn> wrote:
>>
>> 在 2026/1/24 04:12, Rafael J. Wysocki 写道:
>>> On Fri, Jan 23, 2026 at 2:20 AM Yaxiong Tian <tianyaxiong@kylinos.cn> wrote:
>>>> 在 2026/1/22 23:55, Ricardo Neri 写道:
>>>>> On Mon, Jan 19, 2026 at 03:41:18PM +0800, Yaxiong Tian wrote:
>>>>>> When hwp_is_hybrid && !sched_smt_active(), the driver enables
>>>>>> hybrid_capacity_scale and disables ITMT. According to the original code
>>>>>> logic, these related actions are one-time operations; therefore, I
>>>>>> believe the original design intent did not support dynamic runtime
>>>>>> switching.
>>>>>>
>>>>>> However, SMT can be toggled via related interfaces in /sys. When SMT is
>>>>>> enabled, the system is no longer hybrid, and the original settings become
>>>>>> incorrect.
>>>>> Indeed I was able to enable SMT siblings:
>>>>>
>>>>> $ echo on > /sys/devices/system/cpu/smt
>>>>> $ echo 1 > /sys/devices/system/cpu/cpu1/online
>>>>>
>>>>>> To resolve this confusion, permanently disable SMT by calling
>>>>>> cpuhp_smt_disable().
>>>>> IMHO, the user should be able to enable SMT back if she or he chooses to. Instead,
>>>>> the sched domains should be rebuilt with asym packing and without asymmetric
>>>>> capacity.
>>>> Yes, I also agree with this viewpoint.
>>> I don't agree though.
>>>
>>>> I think a better solution is to
>>>> place it within cpufreq online for heterogeneous judgment and to clearly
>>>> address sched domain rebuilt and asymmetry issues.
>>> Maybe in theory, but in practice the EAS-related code in intel_pstate
>>> really only targets systems with no SMT.
>>>
>>> While it is possible to use it on SMT systems with SMT disabled, I
>>> really wouldn't recommend doing that beyond debug/diagnostics and no,
>>> it is not sufficient to disable stuff when SMT is re-enabled, you'd
>>> basically need to unregister the driver and register it from scratch
>>> in that case.
>> Thank you for your explanation. It seems I didn’t read the document
>> carefully enough before.
>>
>>>> I ran some tests the
>>>> day before yesterday and encountered a few locking problems. I will
>>>> publish the patch a bit later.
>>> Given the complexity involved, I don't think it is worth the effort.
>>>
>>> I can apply this patch though.
>> Um, although I've already done it, I agree with your point.
>>
>> Besides, I don't think this patch is very suitable either, because I
>> supposethat on a n system without SMT, the SMT-related|/sys|interface
>> should indicate unsupported (though I don't have such a machine).
>>
>> Moreover, based on the previous discussion, would it be more appropriate
>> to use !cpu_smt_possible() instead of !sched_smt_active()? After all,
>> the original design was intended for systems without SMT. If you also
>> agree with this perspective, I can test it on a machine with SMT.
> Yes, please!
>
> Using cpu_smt_possible() instead of sched_smt_active() would be the
> most straightforward way to address this, provided that it works.
Okay, I've tested after making the modifications, and everything works fine.
On a machine with SMT (Simultaneous Multithreading):
1.
With|nosmt|added to grub, CAS and EAS are not enabled.
2.
With|nosmt=force|added to grub, CAS and EAS are enabled, and users
cannot change the SMT setting.
Since I don't have access to a machine that does not support SMT, I
analyzed the scenario for non-SMT machines. There should be no issues at
all—when SMT is not supported, CAS and EAS will be enabled, and users
cannot switch to SMT. If any problems arise, they likely need to be
addressed by fixing the|cpu_smt_possible()|function.
I’ve already released the relevant patch:
https://lore.kernel.org/all/20260128031521.389765-1-tianyaxiong@kylinos.cn/
^ permalink raw reply
* [PATCH] cpufreq: intel_pstate: Enable asym capacity only when cpu smt is not possible
From: Yaxiong Tian @ 2026-01-28 3:15 UTC (permalink / raw)
To: rafael, srinivas.pandruvada, lenb, viresh.kumar,
ricardo.neri-calderon
Cc: linux-pm, linux-kernel, Yaxiong Tian
According to the description in the intel_pstate.rst documentation,
Capacity-Aware Scheduling and Energy-Aware Scheduling are only
supported on a hybrid processor without SMT. Previously, the system
used sched_smt_active() for judgment, which is not a strict condition
because users can switch it on or off via /sys at any time.
This could lead to incorrect driver settings in certain scenarios.
For example, on a CPU that supports SMT, a user can disable SMT
via the nosmt parameter to enable asym capacity, and then re-enable
SMT via /sys. In such cases, some settings in the driver would no
longer be correct.
To address this issue, replace sched_smt_active() with cpu_smt_possible(),
and only enable asym capacity when cpu smt is not possible.
Fixes: 929ebc93ccaa ("cpufreq: intel_pstate: Set asymmetric CPU capacity on hybrid systems")
Signed-off-by: Yaxiong Tian <tianyaxiong@kylinos.cn>
---
drivers/cpufreq/intel_pstate.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index ec4abe374573..8105c41861a3 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -1142,8 +1142,11 @@ static void hybrid_refresh_cpu_capacity_scaling(void)
static void hybrid_init_cpu_capacity_scaling(bool refresh)
{
- /* Bail out if enabling capacity-aware scheduling is prohibited. */
- if (no_cas)
+ /*
+ * Bail out if capacity-aware scheduling is prohibited, or if SMT is
+ * possible, as the capacity of SMT threads cannot be determined reliably.
+ */
+ if (no_cas || cpu_smt_possible())
return;
/*
@@ -1156,12 +1159,8 @@ static void hybrid_init_cpu_capacity_scaling(bool refresh)
return;
}
- /*
- * On hybrid systems, use asym capacity instead of ITMT, but because
- * the capacity of SMT threads is not deterministic even approximately,
- * do not do that when SMT is in use.
- */
- if (hwp_is_hybrid && !sched_smt_active() && arch_enable_hybrid_capacity_scale()) {
+ /* On hybrid systems, use asym capacity instead of ITMT */
+ if (hwp_is_hybrid && arch_enable_hybrid_capacity_scale()) {
hybrid_refresh_cpu_capacity_scaling();
/*
* Disabling ITMT causes sched domains to be rebuilt to disable asym
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v5 6/7] dax/hmem, cxl: Defer and resolve ownership of Soft Reserved memory ranges
From: dan.j.williams @ 2026-01-27 23:41 UTC (permalink / raw)
To: Alejandro Lucero Palau, dan.j.williams,
Koralahalli Channabasappa, Smita, Smita Koralahalli, linux-cxl,
linux-kernel, nvdimm, linux-fsdevel, linux-pm
Cc: Ard Biesheuvel, Alison Schofield, Vishal Verma, Ira Weiny,
Jonathan Cameron, Yazen Ghannam, Dave Jiang, Davidlohr Bueso,
Matthew Wilcox, Jan Kara, Rafael J . Wysocki, Len Brown,
Pavel Machek, Li Ming, Jeff Johnson, Ying Huang, Yao Xingtao,
Peter Zijlstra, Greg Kroah-Hartman, Nathan Fontenot, Terry Bowman,
Robert Richter, Benjamin Cheatham, Zhijian Li, Borislav Petkov,
Tomasz Wolski
In-Reply-To: <f4bdf04d-7481-4282-b9da-ce5fcf911af9@amd.com>
Alejandro Lucero Palau wrote:
[..]
> I will take a look at this presentation, but I think there could be
> another option where accelerators information is obtained during pci
> enumeration by the kernel and using this information by this
> functionality skipping those ranges allocated to them. Forcing them to
> be compiled with the kernel would go against what distributions
> currently and widely do with initramfs. Not sure if some current "early"
> stubs could be used for this though but the information needs to be
> recollected before this code does the checks.
The simple path is "do not use EFI_MEMORY_SP for accelerator memory".
However, if the accelerator wants to publish memory as EFI_MEMORY_SP
then it needs to coordinate with the kernel's default behavior somehow.
That means expanding the list of drivers that dax_hmem needs to await
before it can make a determination, or teaching dax_hmem to look for a
secondary indication that it should never fall back to the default
behavior.
Talk to your AMD peers Paul and Rajneesh about their needs. I took it on
faith that the use case was required.
^ permalink raw reply
* Re: [PATCH v5 3/7] cxl/region: Skip decoder reset on detach for autodiscovered regions
From: dan.j.williams @ 2026-01-27 23:37 UTC (permalink / raw)
To: Koralahalli Channabasappa, Smita, Jonathan Cameron,
Smita Koralahalli
Cc: linux-cxl, linux-kernel, nvdimm, linux-fsdevel, linux-pm,
Ard Biesheuvel, Alison Schofield, Vishal Verma, Ira Weiny,
Dan Williams, Yazen Ghannam, Dave Jiang, Davidlohr Bueso,
Matthew Wilcox, Jan Kara, Rafael J . Wysocki, Len Brown,
Pavel Machek, Li Ming, Jeff Johnson, Ying Huang, Yao Xingtao,
Peter Zijlstra, Greg Kroah-Hartman, Nathan Fontenot, Terry Bowman,
Robert Richter, Benjamin Cheatham, Zhijian Li, Borislav Petkov,
Tomasz Wolski
In-Reply-To: <9c5150ba-c443-4ce1-a750-57736f0dabf0@amd.com>
Koralahalli Channabasappa, Smita wrote:
[..]
> I’m re reading Dan’s note here:
> https://lore.kernel.org/all/6930dacd6510f_198110020@dwillia2-mobl4.notmuch/
>
> Specifically this part:
> "If the administrator actually wants to destroy and reclaim that
> physical address space then they need to forcefully de-commit that
> auto-assembled region via the @commit sysfs attribute. So that means
> commit_store() needs to clear CXL_REGION_F_AUTO to get the decoder reset
> to happen."
>
> Today the sysfs commit=0 path inside commit_store() resets decoders
> without the AUTO check whereas the detach path now skips the reset when
> CXL_REGION_F_AUTO is set.
>
> I think the same rationale should apply to the sysfs de-commit path as
> well? I’m trying to understand the implications of not guarding the
> reset with AUTO in commit_store().
Linux tends to give the administrator the ability to know better than the
kernel. So if the root forcefully decommits the region, root gets to
keep the pieces.
^ permalink raw reply
* Re: [PATCH v5 4/7] cxl/region: Add helper to check Soft Reserved containment by CXL regions
From: dan.j.williams @ 2026-01-27 21:59 UTC (permalink / raw)
To: Smita Koralahalli, linux-cxl, linux-kernel, nvdimm, linux-fsdevel,
linux-pm
Cc: Ard Biesheuvel, Alison Schofield, Vishal Verma, Ira Weiny,
Dan Williams, Jonathan Cameron, Yazen Ghannam, Dave Jiang,
Davidlohr Bueso, Matthew Wilcox, Jan Kara, Rafael J . Wysocki,
Len Brown, Pavel Machek, Li Ming, Jeff Johnson, Ying Huang,
Yao Xingtao, Peter Zijlstra, Greg Kroah-Hartman, Nathan Fontenot,
Terry Bowman, Robert Richter, Benjamin Cheatham, Zhijian Li,
Borislav Petkov, Smita Koralahalli, Tomasz Wolski
In-Reply-To: <20260122045543.218194-5-Smita.KoralahalliChannabasappa@amd.com>
Smita Koralahalli wrote:
> Add a helper to determine whether a given Soft Reserved memory range is
> fully contained within the committed CXL region.
>
> This helper provides a primitive for policy decisions in subsequent
> patches such as co-ordination with dax_hmem to determine whether CXL has
> fully claimed ownership of Soft Reserved memory ranges.
>
> No functional changes are introduced by this patch.
>
> Signed-off-by: Smita Koralahalli <Smita.KoralahalliChannabasappa@amd.com>
> ---
> drivers/cxl/core/region.c | 29 +++++++++++++++++++++++++++++
> drivers/cxl/cxl.h | 5 +++++
> 2 files changed, 34 insertions(+)
>
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 45ee598daf95..9827a6dd3187 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3875,6 +3875,35 @@ static int cxl_region_debugfs_poison_clear(void *data, u64 offset)
> DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_clear_fops, NULL,
> cxl_region_debugfs_poison_clear, "%llx\n");
>
> +static int cxl_region_contains_sr_cb(struct device *dev, void *data)
> +{
> + struct resource *res = data;
> + struct cxl_region *cxlr;
> + struct cxl_region_params *p;
> +
> + if (!is_cxl_region(dev))
> + return 0;
> +
> + cxlr = to_cxl_region(dev);
> + p = &cxlr->params;
> +
> + if (p->state != CXL_CONFIG_COMMIT)
> + return 0;
> +
> + if (!p->res)
> + return 0;
> +
> + return resource_contains(p->res, res) ? 1 : 0;
I suspect this is too precise and this should instead be
resource_overlaps(). Because, in the case where the platform is taking
liberties with the specification, there is a high likelihood that
driver's view of the region does not neatly contain the range published
in the memory map.
There is also the problem with the fact that firmware does not
necessarily need to split memory map entries on region boundaries. That
gets slightly better if this @res argument is the range boundary that
has been adjusted by HMAT, but still not a guarantee per the spec.
> +}
> +
> +bool cxl_region_contains_soft_reserve(const struct resource *res)
> +{
> + guard(rwsem_read)(&cxl_rwsem.region);
> + return bus_for_each_dev(&cxl_bus_type, NULL, (void *)res,
No, need to cast pointers to 'void *'.
^ 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