* [PATCH v8 8/9] regulator: Add MediaTek MT6392 regulator
From: Luca Leonardo Scorcia @ 2026-06-20 19:56 UTC (permalink / raw)
To: linux-mediatek
Cc: Fabien Parent, Val Packett, Luca Leonardo Scorcia,
Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sen Chu, Sean Wang, Macpaul Lin, Lee Jones, Matthias Brugger,
AngeloGioacchino Del Regno, Liam Girdwood, Mark Brown,
Linus Walleij, Julien Massot, Louis-Alexis Eyraud,
Akari Tsuyukusa, Chen Zhong, linux-input, devicetree,
linux-kernel, linux-pm, linux-arm-kernel, linux-gpio
In-Reply-To: <20260620200032.334192-1-l.scorcia@gmail.com>
From: Fabien Parent <parent.f@gmail.com>
The MT6392 is a regulator found on boards based on the MediaTek
MT8167, MT8516, and probably other SoCs. It is a so called PMIC and
connects as a slave to a SoC using SPI, wrapped inside PWRAP.
Signed-off-by: Fabien Parent <parent.f@gmail.com>
Co-developed-by: Val Packett <val@packett.cool>
Signed-off-by: Val Packett <val@packett.cool>
Signed-off-by: Luca Leonardo Scorcia <l.scorcia@gmail.com>
---
drivers/regulator/Kconfig | 9 +
drivers/regulator/Makefile | 1 +
drivers/regulator/mt6392-regulator.c | 764 +++++++++++++++++++++
include/linux/regulator/mt6392-regulator.h | 42 ++
4 files changed, 816 insertions(+)
create mode 100644 drivers/regulator/mt6392-regulator.c
create mode 100644 include/linux/regulator/mt6392-regulator.h
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index a54a549196fe..ae375b9e6391 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -1001,6 +1001,15 @@ config REGULATOR_MT6380
This driver supports the control of different power rails of device
through regulator interface.
+config REGULATOR_MT6392
+ tristate "MediaTek MT6392 PMIC"
+ depends on MFD_MT6397
+ help
+ Say y here to select this option to enable the power regulator of
+ MediaTek MT6392 PMIC.
+ This driver supports the control of different power rails of device
+ through regulator interface.
+
config REGULATOR_MT6397
tristate "MediaTek MT6397 PMIC"
depends on MFD_MT6397
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 134eee274dbf..a8e795a1eda1 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_REGULATOR_MT6360) += mt6360-regulator.o
obj-$(CONFIG_REGULATOR_MT6363) += mt6363-regulator.o
obj-$(CONFIG_REGULATOR_MT6370) += mt6370-regulator.o
obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
+obj-$(CONFIG_REGULATOR_MT6392) += mt6392-regulator.o
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
obj-$(CONFIG_REGULATOR_MTK_DVFSRC) += mtk-dvfsrc-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_LABIBB) += qcom-labibb-regulator.o
diff --git a/drivers/regulator/mt6392-regulator.c b/drivers/regulator/mt6392-regulator.c
new file mode 100644
index 000000000000..19999d198b56
--- /dev/null
+++ b/drivers/regulator/mt6392-regulator.c
@@ -0,0 +1,764 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ * Copyright (c) 2020 BayLibre, SAS.
+ * Author: Chen Zhong <chen.zhong@mediatek.com>
+ * Author: Fabien Parent <fparent@baylibre.com>
+ * Author: Luca Leonardo Scorcia <l.scorcia@gmail.com>
+ *
+ * The data sheet for MT6392 regulators is spotty to say the least,
+ * many important registers/fields are missing and the ones that aren't
+ * lack crucial information. Some useful details have been retrieved from
+ * Android sources.
+ * The driver code is mostly based on the MT6397 one.
+ */
+
+#include <linux/module.h>
+#include <linux/linear_range.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/mfd/mt6392/registers.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/mt6392-regulator.h>
+#include <linux/regulator/of_regulator.h>
+
+/*
+ * Buck mode constants which may be used in devicetree properties (eg.
+ * regulator-initial-mode, regulator-allowed-modes).
+ * See the manufacturer's datasheet for more information on these modes.
+ */
+#define MT6392_REGULATOR_MODE_NORMAL 0
+#define MT6392_BUCK_MODE_FORCE_PWM 1
+
+/*
+ * LDO mode constants which may be used in devicetree properties (eg.
+ * regulator-initial-mode, regulator-allowed-modes).
+ * See the manufacturer's datasheet for more information on these modes.
+ */
+#define MT6392_LDO_MODE_LP 2
+
+/**
+ * MT6392 regulators' information
+ *
+ * @desc: standard fields of regulator description.
+ * @qi_status_reg: Register to query enable signal status of regulators
+ * @qi_status_mask: Mask to query enable signal status of regulators (RO)
+ * @vselctrl_reg: Vsel control mode selector register
+ * @vselctrl_mask: Vsel control mode selector mask (RO)
+ * @vsel_reg_mode_reg: Vsel register when Vsel control mode selector = 0 (Register mode)
+ * @vsel_reg_mode_mask: Vsel register mask in Register mode (RW)
+ * @vsel_normal_mode_reg: Vsel register when Vsel control mode selector = 1 (Normal mode)
+ * @vsel_normal_mode_mask: Vsel register mask in Register mode (RW)
+ * @pwm_modeset_reg: Register to control buck mode (Auto/Force PWM)
+ * @pwm_modeset_mask: Mask to control buck mode (RW)
+ * @lp_modeget_reg: Register to get LDO low-power mode
+ * @lp_modeget_mask: Mask to get LDO low-power mode (RO)
+ * @lp_modeset_reg: Register to control LDO low-power mode
+ * @lp_modeset_mask: Mask to control LDO low-power mode (WO)
+ */
+struct mt6392_regulator_info {
+ struct regulator_desc desc;
+ u32 qi_status_reg;
+ u32 qi_status_mask;
+ u32 vselctrl_reg;
+ u32 vselctrl_mask;
+ u32 vsel_reg_mode_reg;
+ u32 vsel_reg_mode_mask;
+ u32 vsel_normal_mode_reg;
+ u32 vsel_normal_mode_mask;
+ u32 pwm_modeset_reg;
+ u32 pwm_modeset_mask;
+ u32 lp_modeget_reg;
+ u32 lp_modeget_mask;
+ u32 lp_modeset_reg;
+ u32 lp_modeset_mask;
+};
+
+#define MT6392_BUCK(match, vreg, supply, min, max, step, volt_ranges, \
+ _qi_status_reg, _qi_status_mask, _enable_reg, _enable_mask, \
+ _vselctrl_reg, _vselctrl_mask, \
+ _vsel_reg_mode_reg, _vsel_reg_mode_mask, \
+ _vsel_normal_mode_reg, _vsel_normal_mode_mask, \
+ _pwm_modeset_reg, _pwm_modeset_mask, _ramp_delay) \
+[MT6392_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .supply_name = supply, \
+ .of_match = of_match_ptr(match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .ops = &mt6392_volt_range_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6392_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .linear_ranges = volt_ranges, \
+ .n_linear_ranges = ARRAY_SIZE(volt_ranges), \
+ .enable_reg = _enable_reg, \
+ .enable_mask = _enable_mask, \
+ .of_map_mode = mt6392_map_mode, \
+ .ramp_delay = _ramp_delay, \
+ }, \
+ .qi_status_reg = _qi_status_reg, \
+ .qi_status_mask = _qi_status_mask, \
+ .vselctrl_reg = _vselctrl_reg, \
+ .vselctrl_mask = _vselctrl_mask, \
+ .vsel_reg_mode_reg = _vsel_reg_mode_reg, \
+ .vsel_reg_mode_mask = _vsel_reg_mode_mask, \
+ .vsel_normal_mode_reg = _vsel_normal_mode_reg, \
+ .vsel_normal_mode_mask = _vsel_normal_mode_mask, \
+ .pwm_modeset_reg = _pwm_modeset_reg, \
+ .pwm_modeset_mask = _pwm_modeset_mask, \
+}
+
+#define MT6392_LDO(match, vreg, supply, ldo_volt_table, \
+ _qi_status_reg, _qi_status_mask, \
+ _enable_reg, _enable_mask, \
+ _vsel_reg, _vsel_mask, \
+ _lp_modeget_reg, _lp_modeget_mask, \
+ _lp_modeset_reg, _lp_modeset_mask, \
+ _enable_time) \
+[MT6392_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .supply_name = supply, \
+ .of_match = of_match_ptr(match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .ops = &mt6392_volt_table_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6392_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(ldo_volt_table), \
+ .volt_table = ldo_volt_table, \
+ .vsel_reg = _vsel_reg, \
+ .vsel_mask = _vsel_mask, \
+ .enable_reg = _enable_reg, \
+ .enable_mask = _enable_mask, \
+ .enable_time = _enable_time, \
+ .of_map_mode = mt6392_map_mode, \
+ }, \
+ .qi_status_reg = _qi_status_reg, \
+ .qi_status_mask = _qi_status_mask, \
+ .lp_modeget_reg = _lp_modeget_reg, \
+ .lp_modeget_mask = _lp_modeget_mask, \
+ .lp_modeset_reg = _lp_modeset_reg, \
+ .lp_modeset_mask = _lp_modeset_mask, \
+}
+
+#define MT6392_LDO_LINEAR(match, vreg, supply, min, max, step, \
+ volt_ranges, \
+ _qi_status_reg, _qi_status_mask, \
+ _enable_reg, _enable_mask, \
+ _vsel_reg, _vsel_mask, \
+ _lp_modeget_reg, _lp_modeget_mask, \
+ _lp_modeset_reg, _lp_modeset_mask, \
+ _enable_time) \
+[MT6392_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .supply_name = supply, \
+ .of_match = of_match_ptr(match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .ops = &mt6392_volt_ldo_range_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6392_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .linear_ranges = volt_ranges, \
+ .n_linear_ranges = ARRAY_SIZE(volt_ranges), \
+ .vsel_reg = _vsel_reg, \
+ .vsel_mask = _vsel_mask, \
+ .enable_reg = _enable_reg, \
+ .enable_mask = _enable_mask, \
+ .enable_time = _enable_time, \
+ .of_map_mode = mt6392_map_mode, \
+ }, \
+ .qi_status_reg = _qi_status_reg, \
+ .qi_status_mask = _qi_status_mask, \
+ .lp_modeget_reg = _lp_modeget_reg, \
+ .lp_modeget_mask = _lp_modeget_mask, \
+ .lp_modeset_reg = _lp_modeset_reg, \
+ .lp_modeset_mask = _lp_modeset_mask, \
+}
+
+#define MT6392_REG_FIXED(match, vreg, supply, volt, \
+ _qi_status_reg, _qi_status_mask, \
+ _enable_reg, _enable_mask, \
+ _lp_modeget_reg, _lp_modeget_mask, \
+ _lp_modeset_reg, _lp_modeset_mask, \
+ _enable_time) \
+[MT6392_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .supply_name = supply, \
+ .of_match = of_match_ptr(match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .ops = &mt6392_volt_fixed_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6392_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = 1, \
+ .min_uV = volt, \
+ .enable_reg = _enable_reg, \
+ .enable_mask = _enable_mask, \
+ .enable_time = _enable_time, \
+ .of_map_mode = mt6392_map_mode, \
+ }, \
+ .qi_status_reg = _qi_status_reg, \
+ .qi_status_mask = _qi_status_mask, \
+ .lp_modeget_reg = _lp_modeget_reg, \
+ .lp_modeget_mask = _lp_modeget_mask, \
+ .lp_modeset_reg = _lp_modeset_reg, \
+ .lp_modeset_mask = _lp_modeset_mask, \
+}
+
+#define MT6392_REG_FIXED_NO_MODE(match, vreg, supply, volt, \
+ _qi_status_reg, _qi_status_mask, \
+ _enable_reg, _enable_mask, _enable_time) \
+[MT6392_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .supply_name = supply, \
+ .of_match = of_match_ptr(match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .ops = &mt6392_volt_fixed_no_mode_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6392_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = 1, \
+ .min_uV = volt, \
+ .enable_reg = _enable_reg, \
+ .enable_mask = _enable_mask, \
+ .enable_time = _enable_time, \
+ }, \
+ .qi_status_reg = _qi_status_reg, \
+ .qi_status_mask = _qi_status_mask, \
+}
+
+#define MT6392_REG(match, vreg, supply, volt) \
+[MT6392_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .supply_name = supply, \
+ .of_match = of_match_ptr(match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .ops = &mt6392_volt_no_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6392_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = 1, \
+ .min_uV = volt, \
+ }, \
+}
+
+static const struct linear_range buck_volt_range1[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0, 0x7f, 6250),
+};
+
+static const struct linear_range buck_volt_range2[] = {
+ REGULATOR_LINEAR_RANGE(1400000, 0, 0x7f, 12500),
+};
+
+static const u32 ldo_volt_table1[] = {
+ 1800000, 1900000, 2000000, 2200000,
+};
+
+static const struct linear_range ldo_volt_range2[] = {
+ REGULATOR_LINEAR_RANGE(3300000, 0, 3, 100000),
+};
+
+static const u32 ldo_volt_table3[] = {
+ 1800000, 3300000,
+};
+
+static const u32 ldo_volt_table4[] = {
+ 3000000, 3300000,
+};
+
+static const u32 ldo_volt_table5[] = {
+ 1200000, 1300000, 1500000, 1800000, 2000000, 2800000, 3000000, 3300000,
+};
+
+static const u32 ldo_volt_table6[] = {
+ 1240000, 1390000,
+};
+
+static const u32 ldo_volt_table7[] = {
+ 1200000, 1300000, 1500000, 1800000,
+};
+
+static const u32 ldo_volt_table8[] = {
+ 1800000, 2000000,
+};
+
+static unsigned int mt6392_map_mode(unsigned int mode)
+{
+ switch (mode) {
+ case MT6392_REGULATOR_MODE_NORMAL:
+ return REGULATOR_MODE_NORMAL;
+ case MT6392_BUCK_MODE_FORCE_PWM:
+ return REGULATOR_MODE_FAST;
+ case MT6392_LDO_MODE_LP:
+ return REGULATOR_MODE_STANDBY;
+ default:
+ return REGULATOR_MODE_INVALID;
+ }
+}
+
+static int mt6392_buck_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ int ret, val = 0;
+ struct mt6392_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (!info->pwm_modeset_mask) {
+ dev_err(&rdev->dev, "regulator %s doesn't support set_mode\n", info->desc.name);
+ return -EINVAL;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = MT6392_BUCK_MODE_FORCE_PWM;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = MT6392_REGULATOR_MODE_NORMAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val <<= ffs(info->pwm_modeset_mask) - 1;
+
+ ret = regmap_update_bits(rdev->regmap, info->pwm_modeset_reg,
+ info->pwm_modeset_mask, val);
+
+ return ret;
+}
+
+static unsigned int mt6392_buck_get_mode(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ unsigned int mode;
+ int ret;
+ struct mt6392_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (!info->pwm_modeset_mask) {
+ dev_err(&rdev->dev, "regulator %s doesn't support get_mode\n", info->desc.name);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(rdev->regmap, info->pwm_modeset_reg, &val);
+ if (ret < 0)
+ return ret;
+
+ val &= info->pwm_modeset_mask;
+ val >>= ffs(info->pwm_modeset_mask) - 1;
+
+ if (val & 0x1)
+ mode = REGULATOR_MODE_FAST;
+ else
+ mode = REGULATOR_MODE_NORMAL;
+
+ return mode;
+}
+
+static int mt6392_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ int ret, val = 0;
+ struct mt6392_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (!info->lp_modeset_mask) {
+ dev_err(&rdev->dev, "regulator %s doesn't support set_mode\n",
+ info->desc.name);
+ return -EINVAL;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_STANDBY:
+ val = MT6392_LDO_MODE_LP;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = MT6392_REGULATOR_MODE_NORMAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val <<= ffs(info->lp_modeset_mask) - 1;
+
+ ret = regmap_update_bits(rdev->regmap, info->lp_modeset_reg,
+ info->lp_modeset_mask, val);
+
+ return ret;
+}
+
+static unsigned int mt6392_ldo_get_mode(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ unsigned int mode;
+ int ret;
+ struct mt6392_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (!info->lp_modeget_mask) {
+ dev_err(&rdev->dev, "regulator %s doesn't support get_mode\n",
+ info->desc.name);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(rdev->regmap, info->lp_modeget_reg, &val);
+ if (ret < 0)
+ return ret;
+
+ val &= info->lp_modeget_mask;
+ val >>= ffs(info->lp_modeget_mask) - 1;
+
+ if (val & 0x1)
+ mode = REGULATOR_MODE_STANDBY;
+ else
+ mode = REGULATOR_MODE_NORMAL;
+
+ return mode;
+}
+
+static int mt6392_get_status(struct regulator_dev *rdev)
+{
+ int ret;
+ u32 regval;
+ struct mt6392_regulator_info *info = rdev_get_drvdata(rdev);
+
+ ret = regmap_read(rdev->regmap, info->qi_status_reg, ®val);
+ if (ret != 0) {
+ dev_err(&rdev->dev, "Failed to read qi_status_reg: %d\n", ret);
+ return ret;
+ }
+
+ return (regval & info->qi_status_mask) ? REGULATOR_STATUS_ON : REGULATOR_STATUS_OFF;
+}
+
+static const struct regulator_ops mt6392_volt_range_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_status = mt6392_get_status,
+ .set_mode = mt6392_buck_set_mode,
+ .get_mode = mt6392_buck_get_mode,
+};
+
+static const struct regulator_ops mt6392_volt_table_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_iterate,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_status = mt6392_get_status,
+ .set_mode = mt6392_ldo_set_mode,
+ .get_mode = mt6392_ldo_get_mode,
+};
+
+static const struct regulator_ops mt6392_volt_ldo_range_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_status = mt6392_get_status,
+ .set_mode = mt6392_ldo_set_mode,
+ .get_mode = mt6392_ldo_get_mode,
+};
+
+static const struct regulator_ops mt6392_volt_fixed_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_status = mt6392_get_status,
+ .set_mode = mt6392_ldo_set_mode,
+ .get_mode = mt6392_ldo_get_mode,
+};
+
+static const struct regulator_ops mt6392_volt_fixed_no_mode_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_status = mt6392_get_status,
+};
+
+static const struct regulator_ops mt6392_volt_no_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+/* The array is indexed by id(MT6392_ID_XXX) */
+static struct mt6392_regulator_info mt6392_regulators[] = {
+ MT6392_BUCK("vproc", VPROC, "vproc", 700000, 1493750, 6250,
+ buck_volt_range1,
+ MT6392_VPROC_CON7, BIT(13), // Regulator status
+ MT6392_VPROC_CON7, BIT(0), // Regulator enable
+ MT6392_VPROC_CON5, BIT(0), // Vsel ctrl mode selector,not present in data sheet
+ MT6392_VPROC_CON9, GENMASK(6, 0), // Vsel when control mode = register (0)
+ MT6392_VPROC_CON10, GENMASK(6, 0), // Vsel when control mode = normal (1)
+ MT6392_VPROC_CON2, BIT(8), // Auto / Force PWM mode
+ 12500),
+ MT6392_BUCK("vsys", VSYS, "vsys", 1400000, 2987500, 12500,
+ buck_volt_range2,
+ MT6392_VSYS_CON7, BIT(13),
+ MT6392_VSYS_CON7, BIT(0),
+ MT6392_VSYS_CON5, BIT(0), // Not present in data sheet
+ MT6392_VSYS_CON9, GENMASK(6, 0),
+ MT6392_VSYS_CON10, GENMASK(6, 0),
+ MT6392_VSYS_CON2, BIT(8),
+ 25000),
+ MT6392_BUCK("vcore", VCORE, "vcore", 700000, 1493750, 6250,
+ buck_volt_range1,
+ MT6392_VCORE_CON7, BIT(13),
+ MT6392_VCORE_CON7, BIT(0),
+ MT6392_VCORE_CON5, BIT(0), // Not present in data sheet
+ MT6392_VCORE_CON9, GENMASK(6, 0),
+ MT6392_VCORE_CON10, GENMASK(6, 0),
+ MT6392_VCORE_CON2, BIT(8),
+ 12500),
+
+ MT6392_REG_FIXED("vxo22", VXO22, "ldo1", 2200000,
+ MT6392_ANALDO_CON1, BIT(15),
+ MT6392_ANALDO_CON1, BIT(10), // Not present in data sheet
+ MT6392_ANALDO_CON1, BIT(7),
+ MT6392_ANALDO_CON1, BIT(1), // Not present in data sheet
+ 110),
+ MT6392_LDO("vaud22", VAUD22, "ldo1", ldo_volt_table1,
+ MT6392_ANALDO_CON2, BIT(15),
+ MT6392_ANALDO_CON2, BIT(14), // Not present in data sheet
+ MT6392_ANALDO_CON8, GENMASK(6, 5), // Not present in data sheet
+ MT6392_ANALDO_CON2, BIT(7),
+ MT6392_ANALDO_CON2, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_REG_FIXED_NO_MODE("vcama", VCAMA, "ldo1", 2800000,
+ MT6392_ANALDO_CON4, BIT(15),
+ MT6392_ANALDO_CON4, BIT(15),
+ 264),
+ MT6392_REG_FIXED("vaud28", VAUD28, "ldo1", 2800000,
+ MT6392_ANALDO_CON23, BIT(15),
+ MT6392_ANALDO_CON23, BIT(14), // Not present in data sheet
+ MT6392_ANALDO_CON23, BIT(7),
+ MT6392_ANALDO_CON23, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_REG_FIXED("vadc18", VADC18, "ldo1", 1800000,
+ MT6392_ANALDO_CON25, BIT(15),
+ MT6392_ANALDO_CON25, BIT(14), // Not present in data sheet
+ MT6392_ANALDO_CON25, BIT(7),
+ MT6392_ANALDO_CON25, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_LDO_LINEAR("vcn35", VCN35, "ldo2", 3300000, 3600000, 100000, ldo_volt_range2,
+ MT6392_ANALDO_CON17, BIT(15), // Not present in data sheet
+ MT6392_ANALDO_CON21, BIT(12), // Not present in data sheet
+ MT6392_ANALDO_CON16, GENMASK(4, 3),
+ MT6392_ANALDO_CON21, BIT(7),
+ MT6392_ANALDO_CON21, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_REG_FIXED("vio28", VIO28, "ldo2", 2800000,
+ MT6392_DIGLDO_CON0, BIT(15),
+ MT6392_DIGLDO_CON0, BIT(14), // Not present in data sheet
+ MT6392_DIGLDO_CON0, BIT(7),
+ MT6392_DIGLDO_CON0, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_REG_FIXED("vusb", VUSB, "ldo3", 3300000,
+ MT6392_DIGLDO_CON2, BIT(15),
+ MT6392_DIGLDO_CON2, BIT(14), // Not present in data sheet
+ MT6392_DIGLDO_CON2, BIT(7),
+ MT6392_DIGLDO_CON2, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_LDO("vmc", VMC, "ldo2", ldo_volt_table3,
+ MT6392_DIGLDO_CON3, BIT(15),
+ MT6392_DIGLDO_CON3, BIT(12),
+ MT6392_DIGLDO_CON24, BIT(4),
+ MT6392_DIGLDO_CON3, BIT(7),
+ MT6392_DIGLDO_CON3, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_LDO("vmch", VMCH, "ldo2", ldo_volt_table4,
+ MT6392_DIGLDO_CON5, BIT(15),
+ MT6392_DIGLDO_CON5, BIT(14),
+ MT6392_DIGLDO_CON26, BIT(7),
+ MT6392_DIGLDO_CON5, BIT(7),
+ MT6392_DIGLDO_CON5, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_LDO("vemc3v3", VEMC3V3, "ldo3", ldo_volt_table4,
+ MT6392_DIGLDO_CON6, BIT(15),
+ MT6392_DIGLDO_CON6, BIT(14), // Not present in data sheet
+ MT6392_DIGLDO_CON27, BIT(7),
+ MT6392_DIGLDO_CON6, BIT(7),
+ MT6392_DIGLDO_CON6, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_LDO("vgp1", VGP1, "ldo3", ldo_volt_table5,
+ MT6392_DIGLDO_CON7, BIT(15),
+ MT6392_DIGLDO_CON7, BIT(15),
+ MT6392_DIGLDO_CON28, GENMASK(7, 5),
+ MT6392_DIGLDO_CON7, BIT(7),
+ MT6392_DIGLDO_CON7, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_LDO("vgp2", VGP2, "ldo3", ldo_volt_table5,
+ MT6392_DIGLDO_CON8, BIT(15),
+ MT6392_DIGLDO_CON8, BIT(15),
+ MT6392_DIGLDO_CON29, GENMASK(7, 5),
+ MT6392_DIGLDO_CON8, BIT(7),
+ MT6392_DIGLDO_CON8, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_REG_FIXED("vcn18", VCN18, "avddldo", 1800000,
+ MT6392_DIGLDO_CON11, BIT(15),
+ MT6392_DIGLDO_CON11, BIT(14), // Not present in data sheet
+ MT6392_DIGLDO_CON11, BIT(7),
+ MT6392_DIGLDO_CON11, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_LDO("vcamaf", VCAMAF, "ldo3", ldo_volt_table5,
+ MT6392_DIGLDO_CON31, BIT(15),
+ MT6392_DIGLDO_CON31, BIT(15),
+ MT6392_DIGLDO_CON32, GENMASK(7, 5),
+ MT6392_DIGLDO_CON31, BIT(7),
+ MT6392_DIGLDO_CON31, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_LDO("vm", VM, "avddldo", ldo_volt_table6,
+ MT6392_DIGLDO_CON47, BIT(15),
+ MT6392_DIGLDO_CON47, BIT(14), // Not present in data sheet
+ MT6392_DIGLDO_CON48, GENMASK(5, 4), // Not present in data sheet
+ MT6392_DIGLDO_CON47, BIT(7), // Not present in data sheet
+ MT6392_DIGLDO_CON47, BIT(1),
+ 264),
+ MT6392_REG_FIXED("vio18", VIO18, "avddldo", 1800000,
+ MT6392_DIGLDO_CON49, BIT(15),
+ MT6392_DIGLDO_CON49, BIT(14), // Not present in data sheet
+ MT6392_DIGLDO_CON49, BIT(7),
+ MT6392_DIGLDO_CON49, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_LDO("vcamd", VCAMD, "avddldo", ldo_volt_table7,
+ MT6392_DIGLDO_CON51, BIT(15),
+ MT6392_DIGLDO_CON51, BIT(14),
+ MT6392_DIGLDO_CON52, GENMASK(6, 5),
+ MT6392_DIGLDO_CON51, BIT(7),
+ MT6392_DIGLDO_CON51, BIT(1),
+ 264),
+ MT6392_REG_FIXED("vcamio", VCAMIO, "avddldo", 1800000,
+ MT6392_DIGLDO_CON53, BIT(15),
+ MT6392_DIGLDO_CON53, BIT(14),
+ MT6392_DIGLDO_CON53, BIT(7),
+ MT6392_DIGLDO_CON53, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_REG_FIXED("vm25", VM25, "ldo3", 2500000,
+ MT6392_DIGLDO_CON55, BIT(15),
+ MT6392_DIGLDO_CON55, BIT(14), // Not present in data sheet
+ MT6392_DIGLDO_CON55, BIT(7),
+ MT6392_DIGLDO_CON55, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_LDO("vefuse", VEFUSE, "ldo2", ldo_volt_table8,
+ MT6392_DIGLDO_CON57, BIT(15),
+ MT6392_DIGLDO_CON57, BIT(14), // Not present in data sheet
+ MT6392_DIGLDO_CON58, BIT(5), // Not present in data sheet
+ MT6392_DIGLDO_CON57, BIT(7),
+ MT6392_DIGLDO_CON57, BIT(1), // Not present in data sheet
+ 264),
+ MT6392_REG("vdig18", VDIG18, "ldo2", 1800000), // Internal non changeable regulator
+ MT6392_REG_FIXED_NO_MODE("vrtc", VRTC, "ldo1", 2800000,
+ MT6392_DIGLDO_CON15, BIT(15),
+ MT6392_DIGLDO_CON15, BIT(8), // Not present in data sheet
+ 264)
+};
+
+// Buck regulators can be in Register mode or Normal mode.
+// Each mode uses a different register to set the desired voltage.
+static int mt6392_set_buck_vsel_reg(struct platform_device *pdev)
+{
+ struct mt6397_chip *mt6392 = dev_get_drvdata(pdev->dev.parent);
+ int i;
+ u32 regval;
+
+ for (i = 0; i < MT6392_MAX_REGULATOR; i++) {
+ if (mt6392_regulators[i].vselctrl_reg) {
+ // Read the vselctrl_reg register
+ if (regmap_read(mt6392->regmap,
+ mt6392_regulators[i].vselctrl_reg,
+ ®val) < 0) {
+ dev_err(&pdev->dev,
+ "Failed to read buck ctrl\n");
+ return -EIO;
+ }
+
+ // vselctrl_reg[vselctrl_mask] defines the mode
+ if (regval & mt6392_regulators[i].vselctrl_mask) {
+ // Regulator in Normal mode
+ mt6392_regulators[i].desc.vsel_reg =
+ mt6392_regulators[i].vsel_normal_mode_reg;
+ mt6392_regulators[i].desc.vsel_mask =
+ mt6392_regulators[i].vsel_normal_mode_mask;
+ } else {
+ // Regulator in Register mode
+ mt6392_regulators[i].desc.vsel_reg =
+ mt6392_regulators[i].vsel_reg_mode_reg;
+ mt6392_regulators[i].desc.vsel_mask =
+ mt6392_regulators[i].vsel_reg_mode_mask;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int mt6392_regulator_probe(struct platform_device *pdev)
+{
+ struct mt6397_chip *mt6392 = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = {};
+ struct regulator_dev *rdev;
+ int i;
+
+ device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
+
+ // Initialize the bucks' vsel_reg and vsel_mask according to current HW state
+ if (mt6392_set_buck_vsel_reg(pdev))
+ return -EIO;
+
+ config.dev = mt6392->dev;
+ config.regmap = mt6392->regmap;
+ for (i = 0; i < MT6392_MAX_REGULATOR; i++) {
+ config.driver_data = &mt6392_regulators[i];
+
+ rdev = devm_regulator_register(&pdev->dev,
+ &mt6392_regulators[i].desc,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s\n",
+ mt6392_regulators[i].desc.name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id mt6392_platform_ids[] = {
+ { .name = "mt6392-regulator" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, mt6392_platform_ids);
+
+static struct platform_driver mt6392_regulator_driver = {
+ .driver = {
+ .name = "mt6392-regulator",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = mt6392_regulator_probe,
+ .id_table = mt6392_platform_ids,
+};
+
+module_platform_driver(mt6392_regulator_driver);
+
+MODULE_AUTHOR("Chen Zhong <chen.zhong@mediatek.com>");
+MODULE_DESCRIPTION("Regulator Driver for MediaTek MT6392 PMIC");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/regulator/mt6392-regulator.h b/include/linux/regulator/mt6392-regulator.h
new file mode 100644
index 000000000000..0eccd085b062
--- /dev/null
+++ b/include/linux/regulator/mt6392-regulator.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Chen Zhong <chen.zhong@mediatek.com>
+ */
+
+#ifndef __LINUX_REGULATOR_MT6392_H
+#define __LINUX_REGULATOR_MT6392_H
+
+enum {
+ MT6392_ID_VPROC = 0,
+ MT6392_ID_VSYS,
+ MT6392_ID_VCORE,
+ MT6392_ID_VXO22,
+ MT6392_ID_VAUD22,
+ MT6392_ID_VCAMA,
+ MT6392_ID_VAUD28,
+ MT6392_ID_VADC18,
+ MT6392_ID_VCN35,
+ MT6392_ID_VIO28,
+ MT6392_ID_VUSB = 10,
+ MT6392_ID_VMC,
+ MT6392_ID_VMCH,
+ MT6392_ID_VEMC3V3,
+ MT6392_ID_VGP1,
+ MT6392_ID_VGP2,
+ MT6392_ID_VCN18,
+ MT6392_ID_VCAMAF,
+ MT6392_ID_VM,
+ MT6392_ID_VIO18,
+ MT6392_ID_VCAMD,
+ MT6392_ID_VCAMIO,
+ MT6392_ID_VM25,
+ MT6392_ID_VEFUSE,
+ MT6392_ID_VDIG18,
+ MT6392_ID_VRTC,
+ MT6392_ID_RG_MAX,
+};
+
+#define MT6392_MAX_REGULATOR MT6392_ID_RG_MAX
+
+#endif /* __LINUX_REGULATOR_MT6392_H */
--
2.43.0
^ permalink raw reply related
* [PATCH v8 9/9] arm64: dts: mediatek: Add MediaTek MT6392 PMIC dtsi
From: Luca Leonardo Scorcia @ 2026-06-20 19:56 UTC (permalink / raw)
To: linux-mediatek
Cc: Val Packett, Luca Leonardo Scorcia, Dmitry Torokhov, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sen Chu, Sean Wang,
Macpaul Lin, Lee Jones, Matthias Brugger,
AngeloGioacchino Del Regno, Liam Girdwood, Mark Brown,
Linus Walleij, Julien Massot, Louis-Alexis Eyraud, Fabien Parent,
Akari Tsuyukusa, Chen Zhong, linux-input, devicetree,
linux-kernel, linux-pm, linux-arm-kernel, linux-gpio
In-Reply-To: <20260620200032.334192-1-l.scorcia@gmail.com>
From: Val Packett <val@packett.cool>
Add the dtsi to be included by all boards using the MT6392 PMIC,
providing support for regulator, keys, pinctrl and RTC.
Import the new file in the shared device tree for the Pumpkin boards.
Signed-off-by: Val Packett <val@packett.cool>
Signed-off-by: Luca Leonardo Scorcia <l.scorcia@gmail.com>
---
arch/arm64/boot/dts/mediatek/mt6392.dtsi | 148 ++++++++++++++++++
.../boot/dts/mediatek/pumpkin-common.dtsi | 2 +
2 files changed, 150 insertions(+)
create mode 100644 arch/arm64/boot/dts/mediatek/mt6392.dtsi
diff --git a/arch/arm64/boot/dts/mediatek/mt6392.dtsi b/arch/arm64/boot/dts/mediatek/mt6392.dtsi
new file mode 100644
index 000000000000..19321ae010bb
--- /dev/null
+++ b/arch/arm64/boot/dts/mediatek/mt6392.dtsi
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Copyright (c) 2024 Val Packett <val@packett.cool>
+ * Copyright (c) 2026 Luca Leonardo Scorcia <l.scorcia@gmail.com>
+ */
+
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/regulator/mediatek,mt6392-regulator.h>
+
+&pwrap {
+ pmic: pmic {
+ compatible = "mediatek,mt6392", "mediatek,mt6323";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ mt6392keys: keys {
+ compatible = "mediatek,mt6392-keys";
+
+ key-power {
+ linux,keycodes = <KEY_POWER>;
+ wakeup-source;
+ };
+
+ key-home {
+ linux,keycodes = <KEY_HOME>;
+ wakeup-source;
+ };
+ };
+
+ mt6392pio: pinctrl {
+ compatible = "mediatek,mt6392-pinctrl";
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ mt6392regulators: regulators {
+ compatible = "mediatek,mt6392-regulator";
+
+ /* Fixed supply defined in the data sheet */
+ avddldo-supply = <&mt6392_vsys_reg>;
+
+ mt6392_vcore_reg: vcore {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_BUCK_MODE_FORCE_PWM>;
+ };
+ mt6392_vproc_reg: vproc {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_BUCK_MODE_FORCE_PWM>;
+ };
+ mt6392_vsys_reg: vsys {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_BUCK_MODE_FORCE_PWM>;
+ };
+ mt6392_vaud28_reg: vaud28 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vxo22_reg: vxo22 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vaud22_reg: vaud22 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vadc18_reg: vadc18 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vcama_reg: vcama { };
+ mt6392_vcn35_reg: vcn35 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vio28_reg: vio28 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vusb_reg: vusb {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vmc_reg: vmc {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vmch_reg: vmch {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vemc3v3_reg: vemc3v3 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vcamaf_reg: vcamaf {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vgp1_reg: vgp1 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vgp2_reg: vgp2 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vefuse_reg: vefuse {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vm25_reg: vm25 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vdig18_reg: vdig18 { };
+ mt6392_vm_reg: vm {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vio18_reg: vio18 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vcn18_reg: vcn18 {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vcamd_reg: vcamd {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vcamio_reg: vcamio {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ mt6392_vrtc_reg: vrtc {
+ regulator-allowed-modes = <MT6392_REGULATOR_MODE_NORMAL
+ MT6392_LDO_MODE_LP>;
+ };
+ };
+
+ mt6392rtc: rtc {
+ compatible = "mediatek,mt6392-rtc", "mediatek,mt6323-rtc";
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
index 805fb82138a8..6bc80924cb6c 100644
--- a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
+++ b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
@@ -6,6 +6,8 @@
#include <dt-bindings/gpio/gpio.h>
+#include "mt6392.dtsi"
+
/ {
aliases {
serial0 = &uart0;
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v8 8/9] regulator: Add MediaTek MT6392 regulator
From: sashiko-bot @ 2026-06-20 20:13 UTC (permalink / raw)
To: Luca Leonardo Scorcia
Cc: conor+dt, dmitry.torokhov, linux-input, devicetree, robh
In-Reply-To: <20260620200032.334192-9-l.scorcia@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The driver modifies the globally shared static `mt6392_regulators` array during probe, leading to cross-device state corruption and data races.
- [Medium] mt6392_ldo_set_mode() clears the low-power mode bit instead of setting it when requesting STANDBY mode, due to confusing a devicetree constant with a register bit value.
--
commit 93833e30ee73cbbe6e2371cc49b2ce2c9207ea99
Author: Fabien Parent <parent.f@gmail.com>
regulator: Add MediaTek MT6392 regulator
This commit adds a new regulator driver for the MediaTek MT6392 PMIC. It
supports control over different power rails on boards based on MT8167, MT8516,
and other SoCs. The driver uses regmap for voltage and mode control across
multiple buck and LDO regulators.
> diff --git a/drivers/regulator/mt6392-regulator.c b/drivers/regulator/mt6392-regulator.c
> index 0000000000000..19999d198b56a
> --- /dev/null
> +++ b/drivers/regulator/mt6392-regulator.c
[ ... ]
> +static int mt6392_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode)
> +{
> + int ret, val = 0;
> + struct mt6392_regulator_info *info = rdev_get_drvdata(rdev);
> +
> + if (!info->lp_modeset_mask) {
> + dev_err(&rdev->dev, "regulator %s doesn't support set_mode\n",
> + info->desc.name);
> + return -EINVAL;
> + }
> +
> + switch (mode) {
> + case REGULATOR_MODE_STANDBY:
> + val = MT6392_LDO_MODE_LP;
> + break;
[Severity: Medium]
Will this clear the low-power mode bit instead of setting it?
MT6392_LDO_MODE_LP is defined as 2. If the mask is a single bit, shifting val
by ffs(mask) - 1 will result in a value that evaluates to 0 when masked in
regmap_update_bits().
Does val need to be 1 here to correctly set the bit for STANDBY mode?
> + case REGULATOR_MODE_NORMAL:
> + val = MT6392_REGULATOR_MODE_NORMAL;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + val <<= ffs(info->lp_modeset_mask) - 1;
> +
> + ret = regmap_update_bits(rdev->regmap, info->lp_modeset_reg,
> + info->lp_modeset_mask, val);
[ ... ]
> +// Buck regulators can be in Register mode or Normal mode.
> +// Each mode uses a different register to set the desired voltage.
> +static int mt6392_set_buck_vsel_reg(struct platform_device *pdev)
> +{
> + struct mt6397_chip *mt6392 = dev_get_drvdata(pdev->dev.parent);
> + int i;
> + u32 regval;
> +
> + for (i = 0; i < MT6392_MAX_REGULATOR; i++) {
> + if (mt6392_regulators[i].vselctrl_reg) {
> + // Read the vselctrl_reg register
> + if (regmap_read(mt6392->regmap,
> + mt6392_regulators[i].vselctrl_reg,
> + ®val) < 0) {
> + dev_err(&pdev->dev,
> + "Failed to read buck ctrl\n");
> + return -EIO;
> + }
> +
> + // vselctrl_reg[vselctrl_mask] defines the mode
> + if (regval & mt6392_regulators[i].vselctrl_mask) {
> + // Regulator in Normal mode
> + mt6392_regulators[i].desc.vsel_reg =
> + mt6392_regulators[i].vsel_normal_mode_reg;
> + mt6392_regulators[i].desc.vsel_mask =
> + mt6392_regulators[i].vsel_normal_mode_mask;
[Severity: High]
Does this code mutate the globally shared static mt6392_regulators array
during probe?
Since the driver uses PROBE_PREFER_ASYNCHRONOUS, concurrent probes of multiple
PMIC instances could cause a data race.
Furthermore, as devm_regulator_register() stores a pointer to the desc
structure, all instantiated PMICs would end up sharing the mutated vsel_reg
from the last probed PMIC.
Could this break voltage control for any instances whose hardware state
differs?
> + } else {
> + // Regulator in Register mode
> + mt6392_regulators[i].desc.vsel_reg =
> + mt6392_regulators[i].vsel_reg_mode_reg;
> + mt6392_regulators[i].desc.vsel_mask =
> + mt6392_regulators[i].vsel_reg_mode_mask;
> + }
> + }
> + }
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260620200032.334192-1-l.scorcia@gmail.com?part=8
^ permalink raw reply
* Re: [PATCH v8 1/9] dt-bindings: mfd: mt6397: Add MT6392 PMIC
From: sashiko-bot @ 2026-06-20 20:14 UTC (permalink / raw)
To: Luca Leonardo Scorcia
Cc: conor+dt, linux-input, robh, dmitry.torokhov, devicetree
In-Reply-To: <20260620200032.334192-2-l.scorcia@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Missing required header for input macros in device tree example
--
commit 17d3dfc19cdb75b35edbf6c7f6fe9a12ac08651b
Author: Luca Leonardo Scorcia <l.scorcia@gmail.com>
dt-bindings: mfd: mt6397: Add MT6392 PMIC
Describe the MT6392 PMIC and its RTC and regulator devices. This device
is mostly based on MT6323 with some similarities to MT6397 and is usually
found on boards using the MT8516/MT8167 SoC.
> diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
> index 3cbc0dc12c319..927df823d640b 100644
> --- a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
> +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
[ ... ]
> @@ -663,3 +672,68 @@ examples:
> compatible = "mediatek,mt6397-rtc";
> };
> };
> +
> + - |
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> + pmic {
> + compatible = "mediatek,mt6392", "mediatek,mt6323";
> +
> + interrupts-extended = <&pio 28 IRQ_TYPE_LEVEL_HIGH>;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> +
> + keys {
> + compatible = "mediatek,mt6392-keys";
> +
> + key-power {
> + linux,keycodes = <KEY_POWER>;
[Severity: Low]
Does this example miss the <dt-bindings/input/input.h> include for the
KEY_POWER and KEY_HOME macros?
Although the dt_binding_check tooling extracts and concatenates all examples
into a single file (meaning it will inadvertently compile successfully because
a previous example includes the header), omitting the header in this specific
standalone example block might cause compilation failures if copied by
developers for standalone use.
> + wakeup-source;
> + };
> +
> + key-home {
> + linux,keycodes = <KEY_HOME>;
> + wakeup-source;
> + };
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260620200032.334192-1-l.scorcia@gmail.com?part=1
^ permalink raw reply
* Re: [PATCH v8 3/9] regulator: dt-bindings: Add MediaTek MT6392 PMIC
From: sashiko-bot @ 2026-06-20 20:15 UTC (permalink / raw)
To: Luca Leonardo Scorcia
Cc: dmitry.torokhov, linux-input, robh, conor+dt, devicetree
In-Reply-To: <20260620200032.334192-4-l.scorcia@gmail.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] Invalid use of array validation keywords (`items`, `maxItems`) on the scalar `regulator-initial-mode` property, allowing any integer value to bypass the intended enum constraints.
- [Low] The dt-binding YAML file is completely missing an `examples:` section.
- [Low] Exposing an internal, hardwired connection (`avddldo-supply`) as an external device tree property.
--
commit 17d808769b24018a674d4068d9bab99043b30c55
Author: Luca Leonardo Scorcia <l.scorcia@gmail.com>
regulator: dt-bindings: Add MediaTek MT6392 PMIC
Add bindings for the regulators found in the MediaTek MT6392 PMIC,
usually found in board designs using the MediaTek MT8516/MT8167 SoCs.
> diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6392-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6392-regulator.yaml
> new file mode 100644
> index 0000000000000..f3d5a3498d184
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6392-regulator.yaml
> @@ -0,0 +1,118 @@
[ ... ]
> + vproc-supply:
> + description: Supply for buck regulator vproc
> + vcore-supply:
> + description: Supply for buck regulator vcore
> + vsys-supply:
> + description: Supply for buck regulator vsys
> + avddldo-supply:
> + description:
> + Supply for AVDD LDOs (vm, vio18, vcn18, vcamd, vcamio). According to the data sheet
> + this is an internal supply derived from vsys.
[Severity: Low]
Should this internal supply be exposed as an external device tree property?
Since the description notes this is an internal supply derived from vsys,
exposing it here requires board device trees to describe internal IC routing
instead of external hardware wiring.
[ ... ]
> +patternProperties:
> + "^v(core|proc|sys)$":
> + description: Buck regulators
> + type: object
> + $ref: regulator.yaml#
> + unevaluatedProperties: false
> + properties:
> + regulator-allowed-modes:
> + description:
> + BUCK regulators can set regulator-allowed-modes to values specified in
> + dt-bindings/regulator/mediatek,mt6392-regulator.h
> + items:
> + enum: [0, 1]
> + minItems: 1
> + maxItems: 2
> + regulator-initial-mode:
> + description:
> + BUCK regulators can set regulator-initial-mode to values specified in
> + dt-bindings/regulator/mediatek,mt6392-regulator.h
> + items:
> + enum: [0, 1]
> + maxItems: 1
[Severity: Medium]
Is it correct to use array validation keywords like items and maxItems for
regulator-initial-mode?
Since regulator-initial-mode is defined as a scalar uint32 in regulator.yaml,
applying array constraints to it causes the schema validation to ignore them.
This allows any arbitrary integer value to bypass the enum constraints.
[ ... ]
> + "^v(adc18|camio|cn18|io18|xo22|m25|aud28|io28|rtc|usb)$":
> + description: LDOs with fixed output and mode setting
> + type: object
> + $ref: regulator.yaml#
> + unevaluatedProperties: false
> + properties:
> + regulator-allowed-modes:
[ ... ]
> + regulator-initial-mode:
> + description:
> + LDO regulators can set regulator-initial-mode to values specified in
> + dt-bindings/regulator/mediatek,mt6392-regulator.h
> + items:
> + enum: [0, 2]
> + maxItems: 1
[Severity: Medium]
Does this have the same issue with array validation keywords being applied to
a scalar property?
[ ... ]
> + "^v(aud22|camaf|camd|cn35|efuse|emc3v3|gp1|gp2|m|mc|mch)$":
> + description: LDOs with adjustable output and mode setting
> + type: object
> + $ref: regulator.yaml#
> + unevaluatedProperties: false
> + properties:
> + regulator-allowed-modes:
[ ... ]
> + regulator-initial-mode:
> + description:
> + LDO regulators can set regulator-initial-mode to values specified in
> + dt-bindings/regulator/mediatek,mt6392-regulator.h
> + items:
> + enum: [0, 2]
> + maxItems: 1
[Severity: Medium]
This regulator-initial-mode property also appears to be using array validation
keywords for a scalar value.
> +
> +required:
> + - compatible
> +
> +additionalProperties: false
[Severity: Low]
Is there an examples section missing from this file?
Device tree binding guidelines require every standalone schema file to include
an examples section to provide a validated example of the device node and to
pass dt_binding_check validation.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260620200032.334192-1-l.scorcia@gmail.com?part=3
^ permalink raw reply
* Re: [PATCH v8 7/9] pinctrl: mediatek: mt6397: Add MediaTek MT6392
From: sashiko-bot @ 2026-06-20 20:16 UTC (permalink / raw)
To: Luca Leonardo Scorcia
Cc: devicetree, robh, dmitry.torokhov, linux-input, conor+dt
In-Reply-To: <20260620200032.334192-8-l.scorcia@gmail.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] NULL pointer dereference triggered on `gpio_to_irq()` for MT6397 pins.
- [High] Use-After-Free in gpiolib due to missing devres registration and `.remove` callback.
- [High] Use-After-Free in IRQ handler from non-devres chained handler registration.
--
commit b7ca497bf50208e7b29acdf520d006f2e9f79e4b
Author: Luca Leonardo Scorcia <l.scorcia@gmail.com>
pinctrl: mediatek: mt6397: Add MediaTek MT6392
Add support for the MT6392 pinctrl device, which is very similar to
MT6397 with a handful of different property values and its own pins
definition.
> diff --git a/drivers/pinctrl/mediatek/pinctrl-mt6397.c b/drivers/pinctrl/mediatek/pinctrl-mt6397.c
> index 03d0f65d7bcc1..8ba02e70595ce 100644
> --- a/drivers/pinctrl/mediatek/pinctrl-mt6397.c
> +++ b/drivers/pinctrl/mediatek/pinctrl-mt6397.c
[ ... ]
> @@ -40,13 +62,24 @@ static const struct mtk_pinctrl_devdata mt6397_pinctrl_data = {
> static int mt6397_pinctrl_probe(struct platform_device *pdev)
> {
> struct mt6397_chip *mt6397;
> + const struct mtk_pinctrl_devdata *data;
> +
> + data = device_get_match_data(&pdev->dev);
> + if (!data)
> + return -ENOENT;
>
> mt6397 = dev_get_drvdata(pdev->dev.parent);
> - return mtk_pctrl_init(pdev, &mt6397_pinctrl_data, mt6397->regmap);
> + return mtk_pctrl_init(pdev, data, mt6397->regmap);
[Severity: High]
This is a pre-existing issue, but does mtk_pctrl_init() leave pctl->eint
uninitialized for these devices, leading to a NULL pointer dereference?
Because neither mt6392_pinctrl_data nor mt6397_pinctrl_data initialize
the eint_hw field, mtk_pctrl_init() skips mtk_eint_init().
If gpio_to_irq() is later invoked on these pins, it calls down through:
mtk_gpio_to_irq()
mtk_eint_find_irq(pctl->eint, eint_n)
irq_find_mapping(eint->domain, eint_n)
Will this dereference the NULL pctl->eint pointer directly?
[Severity: High]
This isn't a bug introduced by this patch, but does the driver lack proper
devres cleanup for the gpiochip?
In mtk_pctrl_init(), pctl->chip is allocated using devm_kzalloc() and
then registered globally:
mtk_pctrl_init()
pctl->chip = devm_kzalloc(...)
gpiochip_add_data(pctl->chip, pctl)
Since gpiochip_add_data() does not use devres tracking, and the driver
lacks a .remove callback or suppress_bind_attrs, unbinding the driver
via sysfs will immediately free pctl->chip. Can this leave a dangling
pointer in gpiolib, leading to a use-after-free?
[Severity: High]
This is also a pre-existing issue, but does the IRQ handler registration
capture a devm-allocated pointer without devres tracked cleanup?
For MediaTek pinctrl devices that do initialize EINT, mtk_eint_do_init()
registers the chained handler:
mtk_eint_do_init()
irq_set_chained_handler_and_data(eint->irq, mtk_eint_irq_handler, eint)
If the driver is unbound, the devm allocated eint structure will be freed.
Does the lack of a cleanup function mean the subsystem could later trigger
mtk_eint_irq_handler() with the freed eint pointer?
> }
>
> static const struct of_device_id mt6397_pctrl_match[] = {
> - { .compatible = "mediatek,mt6397-pinctrl", },
> + {
> + .compatible = "mediatek,mt6392-pinctrl",
> + .data = &mt6392_pinctrl_data
> + }, {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260620200032.334192-1-l.scorcia@gmail.com?part=7
^ permalink raw reply
* Re: [PATCH v8 6/9] input: keyboard: mtk-pmic-keys: Add MT6392 support
From: sashiko-bot @ 2026-06-20 20:16 UTC (permalink / raw)
To: Luca Leonardo Scorcia
Cc: devicetree, robh, dmitry.torokhov, linux-input, conor+dt
In-Reply-To: <20260620200032.334192-7-l.scorcia@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Incorrect reset bitmask used for MT6357 home key, causing register corruption.
--
commit 1b3cefe34c0ab03bf79d61642de046924d4afa84
Author: Val Packett <val@packett.cool>
input: keyboard: mtk-pmic-keys: Add MT6392 support
Add support for the MT6392 PMIC to the keys driver.
> diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c
> index c78d9f6d97c4f..8b4a89fce4fb1 100644
> --- a/drivers/input/keyboard/mtk-pmic-keys.c
> +++ b/drivers/input/keyboard/mtk-pmic-keys.c
[ ... ]
> @@ -69,6 +70,19 @@ static const struct mtk_pmic_regs mt6397_regs = {
> .rst_lprst_mask = MTK_PMIC_RST_DU_MASK,
> };
>
> +static const struct mtk_pmic_regs mt6392_regs = {
> + .keys_regs[MTK_PMIC_PWRKEY_INDEX] =
> + MTK_PMIC_KEYS_REGS(MT6392_CHRSTATUS, 0x2,
> + MT6392_INT_MISC_CON, 0x10,
> + MTK_PMIC_PWRKEY_RST),
> + .keys_regs[MTK_PMIC_HOMEKEY_INDEX] =
> + MTK_PMIC_KEYS_REGS(MT6392_CHRSTATUS, 0x4,
> + MT6392_INT_MISC_CON, 0x8,
> + MTK_PMIC_HOMEKEY_RST),
[Severity: High]
This is a pre-existing issue, but while reviewing these new register
configurations I noticed a copy-paste typo in the mt6357_regs struct.
In mt6357_regs, the home key reset mask is initialized like this:
.keys_regs[MTK_PMIC_HOMEKEY_INDEX] =
MTK_PMIC_KEYS_REGS(MT6357_TOPSTATUS,
0x8, MT6357_PSC_TOP_INT_CON0, 0xa,
MTK_PMIC_HOMEKEY_INDEX),
Does this incorrectly use the array index MTK_PMIC_HOMEKEY_INDEX instead of
the reset bitmask MTK_PMIC_HOMEKEY_RST?
When mtk_pmic_keys_lp_reset_setup() is called during probe for an MT6357
device, wouldn't this inadvertently modify bit 0 of the MT6357_TOP_RST_MISC
register instead of bit 5?
It looks like this might corrupt an unrelated control bit in the PMIC register
state, while also failing to manage the long-press reset functionality for the
home key.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260620200032.334192-1-l.scorcia@gmail.com?part=6
^ permalink raw reply
* Re: [PATCH v8 9/9] arm64: dts: mediatek: Add MediaTek MT6392 PMIC dtsi
From: sashiko-bot @ 2026-06-20 20:19 UTC (permalink / raw)
To: Luca Leonardo Scorcia
Cc: linux-input, dmitry.torokhov, robh, conor+dt, devicetree
In-Reply-To: <20260620200032.334192-10-l.scorcia@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The `pmic` node lacks an upstream `interrupts` or `interrupts-extended` property, causing the MT6397/MT6392 PMIC driver to fail probing.
--
commit fc85085e7df96d6b701cb80ab653d3bbb4392af1
Author: Val Packett <val@packett.cool>
arm64: dts: mediatek: Add MediaTek MT6392 PMIC dtsi
Add the dtsi to be included by all boards using the MT6392 PMIC,
providing support for regulator, keys, pinctrl and RTC. Import the new
file in the shared device tree for the Pumpkin boards.
> diff --git a/arch/arm64/boot/dts/mediatek/mt6392.dtsi b/arch/arm64/boot/dts/mediatek/mt6392.dtsi
> new file mode 100644
> index 0000000000000..19321ae010bba
> --- /dev/null
> +++ b/arch/arm64/boot/dts/mediatek/mt6392.dtsi
> @@ -0,0 +1,148 @@
[ ... ]
> +&pwrap {
> + pmic: pmic {
> + compatible = "mediatek,mt6392", "mediatek,mt6323";
> + interrupt-controller;
> + #interrupt-cells = <2>;
[ ... ]
> diff --git a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
> index 805fb82138a80..6bc80924cb6cf 100644
> --- a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
> +++ b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
> @@ -6,6 +6,8 @@
>
> #include <dt-bindings/gpio/gpio.h>
>
> +#include "mt6392.dtsi"
> +
[Severity: High]
Does this inclusion miss the board-specific interrupts definition for the PMIC?
The new mt6392.dtsi defines the pmic node but omits the interrupts property,
as expected for a generic include. However, pumpkin-common.dtsi doesn't appear
to override the &pmic node to provide the board-specific interrupt routing.
Since the MT6397 PMIC core driver aborts probing completely if it cannot find
an interrupt via platform_get_irq(pdev, 0), will this cause the PMIC driver to
fail probing, leaving all PMIC sub-devices unavailable?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260620200032.334192-1-l.scorcia@gmail.com?part=9
^ permalink raw reply
* Re: [PATCH v8 5/9] mfd: mt6397: Add support for MT6392 PMIC
From: sashiko-bot @ 2026-06-20 20:20 UTC (permalink / raw)
To: Luca Leonardo Scorcia
Cc: conor+dt, devicetree, linux-input, dmitry.torokhov, robh
In-Reply-To: <20260620200032.334192-6-l.scorcia@gmail.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing unregister_pm_notifier() causes Use-After-Free of PM notifier block on probe error and driver unbind.
- [High] The IRQ domain is created with a hardcoded size of MT6397_IRQ_NR (32), which breaks PMICs like MT6328 that use a third interrupt register (hwirqs 32-47).
- [High] `mt6397_irq_pm_notifier` modifies IRQ mask registers concurrently with running tasks, causing race conditions.
--
commit 15d5fe2891c82970874377c126d0244fd87f5699
Author: Fabien Parent <parent.f@gmail.com>
mfd: mt6397: Add support for MT6392 PMIC
Align the MT6397 PMIC driver to other MFD drivers by passing only an
identifier through mt6397_of_match[*].data and add support for the MT6392
PMIC and its regulator, RTC, keys and pinctrl devices.
> diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
> index ccd97d66d7f1c..f683e878543e1 100644
> --- a/drivers/mfd/mt6397-core.c
> +++ b/drivers/mfd/mt6397-core.c
[ ... ]
> @@ -278,6 +311,7 @@ static int mt6397_probe(struct platform_device *pdev)
> unsigned int id = 0;
> struct mt6397_chip *pmic;
> const struct chip_data *pmic_core;
> + int chip_variant;
>
> pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
> if (!pmic)
> @@ -293,9 +327,36 @@ static int mt6397_probe(struct platform_device *pdev)
> if (!pmic->regmap)
> return -ENODEV;
>
> - pmic_core = of_device_get_match_data(&pdev->dev);
> - if (!pmic_core)
> + chip_variant = (unsigned int)(uintptr_t)device_get_match_data(&pdev->dev);
> + switch (chip_variant) {
[ ... ]
> + case MT6392_CHIP_ID:
> + pmic_core = &mt6392_core;
> + break;
[ ... ]
[Severity: High]
This isn't a bug introduced by this patch, but looking at the error path of
mt6397_probe() in drivers/mfd/mt6397-core.c:
ret = pmic_core->irq_init(pmic);
if (ret)
return ret;
ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
pmic_core->cells, pmic_core->cell_size,
NULL, 0, pmic->irq_domain);
if (ret) {
irq_domain_remove(pmic->irq_domain);
dev_err(&pdev->dev, "failed to add child devices: %d\n", ret);
}
return ret;
Since mt6397_irq_init() registers a PM notifier with register_pm_notifier(),
if devm_mfd_add_devices() fails (or when the driver is unbound), the devres
cleanup will automatically free the pmic structure containing the pm_nb
notifier block.
Because unregister_pm_notifier() is never called in this error path or in a
remove callback, will this leave a dangling pointer in the PM subsystem that
causes a Use-After-Free during the next suspend/resume cycle?
> diff --git a/drivers/mfd/mt6397-irq.c b/drivers/mfd/mt6397-irq.c
> index 5d2e5459f7444..80ea5b92d232f 100644
> --- a/drivers/mfd/mt6397-irq.c
> +++ b/drivers/mfd/mt6397-irq.c
[ ... ]
> @@ -203,6 +205,12 @@ int mt6397_irq_init(struct mt6397_chip *chip)
> chip->int_status[0] = MT6397_INT_STATUS0;
> chip->int_status[1] = MT6397_INT_STATUS1;
> break;
> + case MT6392_CHIP_ID:
> + chip->int_con[0] = MT6392_INT_CON0;
> + chip->int_con[1] = MT6392_INT_CON1;
> + chip->int_status[0] = MT6392_INT_STATUS0;
> + chip->int_status[1] = MT6392_INT_STATUS1;
> + break;
>
> default:
> dev_err(chip->dev, "unsupported chip: 0x%x\n", chip->chip_id);
[Severity: High]
This is a pre-existing issue, but further down in mt6397_irq_init() in
drivers/mfd/mt6397-irq.c, the linear IRQ domain is allocated with a hardcoded
size:
chip->irq_domain = irq_domain_create_linear(dev_fwnode(chip->dev), MT6397_IRQ_NR,
&mt6397_irq_domain_ops, chip);
Since MT6397_IRQ_NR is 32, but PMICs like MT6328 use a third interrupt
status register for hardware IRQs 32-47:
if (mt6397->int_status[2])
mt6397_irq_handle_reg(mt6397, mt6397->int_status[2], 32);
Won't irq_find_mapping() silently fail to find a mapping for any interrupts
at index 32 or higher, breaking functionality for those higher-numbered
interrupts?
[Severity: High]
This is also a pre-existing issue, but mt6397_irq_pm_notifier() in
drivers/mfd/mt6397-irq.c writes directly to the PMIC's interrupt control
registers without locking:
case PM_SUSPEND_PREPARE:
regmap_write(chip->regmap,
chip->int_con[0], chip->wake_mask[0]);
Because PM_SUSPEND_PREPARE runs before user tasks are frozen, running tasks
can concurrently request or enable an IRQ. When this happens,
mt6397_irq_sync_unlock() will acquire irqlock and write irq_masks_cur
to the same registers:
regmap_write(mt6397->regmap, mt6397->int_con[0],
mt6397->irq_masks_cur[0]);
Could this concurrent modification overwrite the suspend configuration
established by the notifier, leading to lost wakeup sources or spurious
interrupts?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260620200032.334192-1-l.scorcia@gmail.com?part=5
^ permalink raw reply
* Re: [PATCH v6 14/21] RISC-V: perf: Skip PMU SBI extension when not implemented
From: Atish Patra @ 2026-06-20 23:15 UTC (permalink / raw)
To: sashiko-reviews; +Cc: devicetree, linux-perf-users, robh, conor+dt, linux-riscv
In-Reply-To: <20260609063329.66D801F00893@smtp.kernel.org>
On 6/8/26 11:33 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> Pre-existing issues:
> - [Critical] Missing perf_pmu_unregister() on error paths leads to Use-After-Free of the pmu struct.
> - [High] Unconditional cpuhp_state_remove_instance() on an uninitialized hlist_node causes a kernel panic.
> --
>
> commit bd51c5655be0902a2fc1fd90b8d692c299e7265b
> Author: Charlie Jenkins <charlie@rivosinc.com>
>
> RISC-V: perf: Skip PMU SBI extension when not implemented
>
> When the PMU SBI extension is not implemented, sbi_v2_available should
> not be set to true. The SBI implementation for counter config matching
> and firmware counter read should also be skipped when the SBI extension
> is not implemented.
+linux-riscv
>> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
>> index 1f16df9d0dd00..5bfcd3821f572 100644
>> --- a/drivers/perf/riscv_pmu_sbi.c
>> +++ b/drivers/perf/riscv_pmu_sbi.c
> [ ... ]
>> @@ -2045,12 +2052,13 @@ static int __init rvpmu_devinit(void)
>> int ret;
>> struct platform_device *pdev;
>>
>> - if (sbi_spec_version >= sbi_mk_version(0, 3) &&
>> - sbi_probe_extension(SBI_EXT_PMU))
>> - static_branch_enable(&riscv_pmu_sbi_available);
>> + if (sbi_probe_extension(SBI_EXT_PMU)) {
>> + if (sbi_spec_version >= sbi_mk_version(0, 3))
>> + static_branch_enable(&riscv_pmu_sbi_available);
>> + if (sbi_spec_version >= sbi_mk_version(2, 0))
>> + sbi_v2_available = true;
>> + }
>>
>> - if (sbi_spec_version >= sbi_mk_version(2, 0))
>> - sbi_v2_available = true;
>> /*
>> * We need all three extensions to be present to access the counters
>> * in S-mode via Supervisor Counter delegation.
> [Severity: Critical]
> This is a pre-existing issue, but I noticed a potential use-after-free in the
> error handling path of rvpmu_device_probe() when allocating or registering
> fails after perf_pmu_register() succeeds.
>
> If an error occurs, such as pmu_sbi_snapshot_alloc() failing, the code jumps
> to out_unregister:
>
> out_unregister:
> riscv_pmu_destroy(pmu);
>
> out_free:
> kfree(pmu);
> return ret;
>
> However, neither the error path nor riscv_pmu_destroy() calls
> perf_pmu_unregister(&pmu->pmu).
>
> This leaves the PMU registered in the perf core subsystem while its backing
> memory is freed via kfree(). Any subsequent access to the PMU could result
> in a use-after-free.
>
> Should perf_pmu_unregister() be called during teardown to avoid memory
> corruption?
Yes. Same comment as other patches. Fixed.
> [Severity: High]
> This is a pre-existing issue, but the error teardown path in
> rvpmu_device_probe() seems to have a bug that could trigger a kernel panic.
>
> If rvpmu_device_probe() fails before successfully calling
> cpuhp_state_add_instance(), it jumps to out_unregister and calls
> riscv_pmu_destroy(pmu).
>
> Inside riscv_pmu_destroy(), it unconditionally removes the hotplug instance:
>
> static void riscv_pmu_destroy(struct riscv_pmu *pmu)
> {
> ...
> cpuhp_state_remove_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
> }
>
> Because the pmu struct is allocated with kzalloc() and the node hasn't been
> added to the hotplug list yet, its hlist_node is zero-initialized.
>
> When hlist_del() is called internally by cpuhp_state_remove_instance(), it
> attempts to dereference node->pprev (which is NULL), causing a NULL pointer
> dereference.
>
> Would it be better to check if the node is actually hashed before attempting
> to remove the hotplug instance?
Fixed in v7.
^ permalink raw reply
* Re: [PATCH v6 13/21] RISC-V: perf: Implement supervisor counter delegation support
From: Atish Patra @ 2026-06-20 23:25 UTC (permalink / raw)
To: sashiko-reviews; +Cc: devicetree, robh, conor+dt, linux-perf-users, linux-riscv
In-Reply-To: <20260609062339.F223A1F00893@smtp.kernel.org>
On 6/8/26 11:23 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> - [High] Bitwise shift of signed 32-bit integer causes overflow and corrupts SCOUNTINHIBIT on 64-bit systems.
> - [High] Non-sampling events are permanently inhibited from counting because HPMEVENT_OF is unconditionally set but never cleared.
> - [High] On 32-bit architectures, `rvpmu_deleg_ctr_enable_irq` writes modified event configuration to the wrong CSR (`CSR_SIREG4` instead of `CSR_SIREG5`).
> - [High] SBI PMU snapshot logic breaks supervisor counter delegation by unconditionally bypassing S-mode CSR writes and reading stale shared memory.
> --
>
> commit ff1e171673cfe94a12c443765bd5b68dacf3b687
> Author: Atish Patra <atishp@rivosinc.com>
>
> RISC-V: perf: Implement supervisor counter delegation support
>
> This patch adds support for RISC-V ISA extensions (ssccfg, sscsrind,
> smcntrpmf) that allow programming hpmcounters directly from S-mode.
> It falls back to the SBI PMU extension if the hardware extensions are
> unavailable or for firmware counters.
>
>> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
>> index 46a25979e95e9..1f16df9d0dd00 100644
>> --- a/drivers/perf/riscv_pmu_sbi.c
>> +++ b/drivers/perf/riscv_pmu_sbi.c
> [ ... ]
>
>> -static u64 rvpmu_sbi_ctr_read(struct perf_event *event)
>> +static u64 rvpmu_ctr_read(struct perf_event *event)
>> {
>> struct hw_perf_event *hwc = &event->hw;
>> int idx = hwc->idx;
> [Severity: High]
> Does this function still read stale data from the SBI shared memory when
> using supervisor counter delegation?
>
> If a platform supports both SBI PMU snapshots and S-mode counter delegation,
That's not a likely scenario as S-mode counter delegation is not
available for VS mode.
> the code unconditionally reads from the SBI shared memory for stopped
> hardware counters. Since S-mode counters are stopped via direct CSR writes,
> the SBI firmware is bypassed and the shared memory is never updated.
>
> [ ... ]
>
>> +static void rvpmu_deleg_ctr_enable_irq(struct perf_event *event)
>> +{
>> + unsigned long hpmevent_curr;
>> + unsigned long of_mask;
>> + struct hw_perf_event *hwc = &event->hw;
>> + int counter_idx = hwc->idx;
>> + unsigned long sip_val = csr_read(CSR_SIP);
>> +
>> + if (!is_sampling_event(event) || (sip_val & SIP_LCOFIP))
>> + return;
> [Severity: High]
> Will this early return cause non-sampling events to never be counted?
>
> In update_deleg_hpmevent(), the HPMEVENT_OF bit is unconditionally set for
> all events. The Sscofpmf specification states a counter does not increment
> while its OF bit is 1. If we return early here for non-sampling events
> (like perf stat), the OF bit is never cleared, which would leave these
> counters permanently inhibited.
N/A. Incorrect spec understanding.
Sscofpmf's OF bit gates the LCOFI interrupt only. it does NOT stop the
counter.
>>
>> - /* Reinitialize and start all the counter that overflowed */
>> - while (ctr_ovf_mask) {
>> - if (ctr_ovf_mask & 0x01) {
>> - event = cpu_hw_evt->events[idx];
>> - hwc = &event->hw;
>> - max_period = riscv_pmu_ctr_get_width_mask(event);
>> - init_val = local64_read(&hwc->prev_count) & max_period;
>> #if defined(CONFIG_32BIT)
>> - sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1,
>> - flag, init_val, init_val >> 32, 0);
>> + hpmevent_curr = csr_ind_read(CSR_SIREG5, SISELECT_SSCCFG_BASE, counter_idx);
>> + of_mask = (u32)~HPMEVENTH_OF;
>> #else
>> - sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1,
>> - flag, init_val, 0, 0);
>> + hpmevent_curr = csr_ind_read(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx);
>> + of_mask = ~HPMEVENT_OF;
>> #endif
>> - perf_event_update_userpage(event);
>> - }
>> - ctr_ovf_mask = ctr_ovf_mask >> 1;
>> - idx++;
>> - }
>> +
>> + hpmevent_curr &= of_mask;
>> +#if defined(CONFIG_32BIT)
>> + csr_ind_write(CSR_SIREG4, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_curr);
> [Severity: High]
> Is this writing the event configuration to the wrong CSR?
>
> The code reads the high 32 bits of the event configuration from CSR_SIREG5
> and clears the HPMEVENTH_OF bit, but it writes the modified value back to
> CSR_SIREG4. Since CSR_SIREG4 maps to hpmcounterh, this might overwrite the
> running counter's upper 32 bits with the event configuration value instead
> of clearing the OF bit in the hpmevent register.
Good catch. Fixed it in v7.
>> +#else
>> + csr_ind_write(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_curr);
>> +#endif
>> +}
>> +
>> +static void rvpmu_deleg_ctr_start(struct perf_event *event, u64 ival)
>> +{
>> + unsigned long scountinhibit_val = 0;
>> + struct hw_perf_event *hwc = &event->hw;
>> +
>> +#if defined(CONFIG_32BIT)
>> + csr_ind_write(CSR_SIREG, SISELECT_SSCCFG_BASE, hwc->idx, ival & 0xFFFFFFFF);
>> + csr_ind_write(CSR_SIREG4, SISELECT_SSCCFG_BASE, hwc->idx, ival >> BITS_PER_LONG);
>> +#else
>> + csr_ind_write(CSR_SIREG, SISELECT_SSCCFG_BASE, hwc->idx, ival);
>> +#endif
>> +
>> + rvpmu_deleg_ctr_enable_irq(event);
>> +
>> + scountinhibit_val = csr_read(CSR_SCOUNTINHIBIT);
>> + scountinhibit_val &= ~(1 << hwc->idx);
> [Severity: High]
> Could this bitwise shift result in sign-extension and corrupt SCOUNTINHIBIT
> on 64-bit systems?
>
> Because 1 is a signed 32-bit integer, (1 << 31) results in -2147483648.
> When this is negated by a bitwise NOT or cast to an unsigned long on a
> 64-bit architecture, it gets sign-extended (e.g., 0x000000007FFFFFFF).
> This could erroneously clear bits 32-63 of the SCOUNTINHIBIT CSR.
There are only 32 bit counters defined by architecture.
> Should this use 1UL << hwc->idx or the BIT() macro instead?
It's better coding standard to use BIT anyways. Fixed in v7.
> [ ... ]
>
>> -static void rvpmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
>> - u64 ctr_ovf_mask)
>> +/*
>> + * This function starts all the used counters in two step approach.
>> + * Any counter that did not overflow can be start in a single step
>> + * while the overflowed counters need to be started with updated initialization
>> + * value.
>> + */
>> +static void rvpmu_start_overflow_mask(struct riscv_pmu *pmu, u64 ctr_ovf_mask)
>> {
>> + int idx = 0, i;
>> + struct perf_event *event;
>> + unsigned long ctr_start_mask = 0;
>> + u64 max_period, init_val = 0;
>> + struct hw_perf_event *hwc;
>> struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
>>
>> if (sbi_pmu_snapshot_available())
>> - rvpmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);
>> - else
>> - rvpmu_sbi_start_ovf_ctrs_sbi(cpu_hw_evt, ctr_ovf_mask);
>> + return rvpmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);
> [Severity: High]
> Does this check bypass S-mode delegation when both snapshot and delegation
> are available?
N/A. Not a likely scenario.
> If a platform supports both, this unconditionally calls
> rvpmu_sbi_start_ovf_ctrs_snapshot(), which starts S-mode delegated hardware
> counters using SBI ECALLs rather than direct S-mode CSR writes.
>
> [ ... ]
>
>> +static void update_deleg_hpmevent(int counter_idx, uint64_t event_value, uint64_t filter_bits)
>> +{
>> + u64 hpmevent_value = 0;
>> +
>> + /* OF bit should be enable during the start if sampling is requested */
>> + hpmevent_value = (event_value & ~HPMEVENT_MASK) | filter_bits | HPMEVENT_OF;
> [Severity: High]
> Are we unconditionally setting the HPMEVENT_OF bit here?
>
> Although the comment states it should be enabled if sampling is requested,
> the bit is added for all events. Combined with the early return in
> rvpmu_deleg_ctr_enable_irq(), this causes non-sampling events to remain
> inhibited permanently.
N/A OF bit is for IRQ overflow not for inhibition.
> [ ... ]
>
>> static void rvpmu_ctr_stop(struct perf_event *event, unsigned long flag)
>> {
>> - rvpmu_sbi_ctr_stop(event, flag);
>> - /* TODO: Counter delegation implementation */
>> + struct hw_perf_event *hwc = &event->hw;
>> +
>> + if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
>> + (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
>> + rvpmu_reset_scounteren((void *)event);
>> +
>> + if (riscv_pmu_cdeleg_available() && !pmu_sbi_is_fw_event(event)) {
>> + /*
>> + * The counter is already stopped. No need to stop again. Counter
>> + * mapping will be reset in clear_idx function.
>> + */
>> + if (flag != RISCV_PMU_STOP_FLAG_RESET)
>> + rvpmu_deleg_ctr_stop_mask((1 << hwc->idx));
> [Severity: High]
> Can this 32-bit shift overflow and corrupt the upper 32 bits of SCOUNTINHIBIT?
>
> Passing (1 << 31) will sign-extend to 0xFFFFFFFF80000000 on 64-bit systems.
> When passed to rvpmu_deleg_ctr_stop_mask, it ORs this mask into SCOUNTINHIBIT,
> which erroneously sets bits 32-63 to 1.
>
> Consider using 1UL << hwc->idx or the BIT() macro to prevent overflow.
Yes. Changed to use BIT macro.
^ permalink raw reply
* [PATCH v2 0/2] iio: temperature: Add support for the STS30 temperature sensor
From: Maxwell Doose @ 2026-06-21 0:46 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
open list:IIO SUBSYSTEM AND DRIVERS,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list
Hi all,
This patch series adds support for the Sensirion STS30 temperature
sensor family. This driver currently supports non clock stretched single
shot measurements.
Given there were very little issues found with the v1 submission, I've
decided to make this a regular patch series rather than an RFC patch.
Changes since v1:
* whole series:
- Squashed MAINTAINERS updates into both the dt-bindings commit and the
driver commit.
* dt-bindings:
- Added ALERT pin as an interrupt and in the examples.
* driver:
- Fixed a mixup between the clock-stretched and non-clock stretched
commands.
- Fixed an issue where the return value of sts30_reset() was ignored.
- Removed redundant "Author" line at the top.
- Added comment at the top for the formula used to calculate the
temperature in Celsius alongside a macro.
- Added a dedicated macro for the temperature reading size.
- Separated the generic linux headers from the iio specific headers.
- Removed kernel.h from includes.
- Removed unneeded comments.
- Used named initializers for sts30_id and moved above _probe().
Maxwell Doose (2):
dt-bindings: iio: temperature: Add STS30 devicetree bindings
iio: temperature: Add STS30 temperature sensor driver
.../iio/temperature/sensirion,sts30.yaml | 55 +++
MAINTAINERS | 6 +
drivers/iio/temperature/Kconfig | 11 +
drivers/iio/temperature/Makefile | 1 +
drivers/iio/temperature/sts30.c | 329 ++++++++++++++++++
5 files changed, 402 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/temperature/sensirion,sts30.yaml
create mode 100644 drivers/iio/temperature/sts30.c
--
2.54.0
^ permalink raw reply
* [PATCH v2 1/2] dt-bindings: iio: temperature: Add STS30 devicetree bindings
From: Maxwell Doose @ 2026-06-21 0:46 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
open list:IIO SUBSYSTEM AND DRIVERS,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list
In-Reply-To: <20260621004626.66629-1-m32285159@gmail.com>
Add the devicetree bindings for the STS30 family of temperature sensors.
The STS30 family of sensors includes the STS30, STS31, and STS35.
All devices in the STS30 family share the same commands, timings, etc.
The only difference between them is their measurement accuracy and
tolerance.
Additionally add MAINTAINERS entry for the driver.
Signed-off-by: Maxwell Doose <m32285159@gmail.com>
---
Changes since v1:
- Squashed parts of the MAINTAINERS commit into this commit.
- Added ALERT pin as an interrupt and in the examples.
.../iio/temperature/sensirion,sts30.yaml | 55 +++++++++++++++++++
MAINTAINERS | 5 ++
2 files changed, 60 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/temperature/sensirion,sts30.yaml
diff --git a/Documentation/devicetree/bindings/iio/temperature/sensirion,sts30.yaml b/Documentation/devicetree/bindings/iio/temperature/sensirion,sts30.yaml
new file mode 100644
index 000000000000..9bb0f04795d4
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/temperature/sensirion,sts30.yaml
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/temperature/sensirion,sts30.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: STS30, STS31, and STS35 temperature iio sensors
+
+maintainers:
+ - Maxwell Doose <m32285159@gmail.com>
+
+description: |
+ Family of digital temperature sensors from Sensirion with I2C interface.
+ All devices are compatible with each other, their only differences are
+ their levels of accuracy.
+
+ Datasheet available at:
+ https://sensirion.com/media/documents/1DA31AFD/65D613A8/Datasheet_STS3x_DIS.pdf
+
+properties:
+ compatible:
+ enum:
+ - sensirion,sts30
+ - sensirion,sts31
+ - sensirion,sts35
+
+ reg:
+ maxItems: 1
+
+ vdd-supply: true
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ temperature@4a {
+ compatible = "sensirion,sts30";
+ reg = <0x4a>;
+ vdd-supply = <®_3v3>;
+ interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index d95d3ef77773..7f94b8cac3e2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24487,6 +24487,11 @@ F: drivers/iio/chemical/sps30.c
F: drivers/iio/chemical/sps30_i2c.c
F: drivers/iio/chemical/sps30_serial.c
+SENSIRION STS30 TEMPERATURE SENSOR DRIVER
+M: Maxwell Doose <m32285159@gmail.com>
+S: Maintained
+F: Documentation/devicetree/bindings/iio/temperature/sensirion,sts30.yaml
+
SERIAL DEVICE BUS
M: Rob Herring <robh@kernel.org>
L: linux-serial@vger.kernel.org
--
2.54.0
^ permalink raw reply related
* [PATCH v2 2/2] iio: temperature: Add STS30 temperature sensor driver
From: Maxwell Doose @ 2026-06-21 0:46 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
open list:IIO SUBSYSTEM AND DRIVERS,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list
In-Reply-To: <20260621004626.66629-1-m32285159@gmail.com>
Add a driver for the Sensirion STS30 family of temperature sensor
drivers over I2C. The STS30 family of sensors includes the STS30, STS31,
and STS35, all of which are supported by this driver, since they all
share the same commands, etc. and only differ in accuracy and tolerance.
The driver currently supports single-shot non-clock stretched readings,
by using a specified delay based on the repeatability/delay specified
by the user. The repeatability/delay can be changed at any time through
sysfs.
Additionally add Kconfig and Makefile entries for the driver as well as
a MAINTAINERS entry.
Signed-off-by: Maxwell Doose <m32285159@gmail.com>
---
Changes since v1:
- Squashed parts of the MAINTAINERS commit into this commit.
- Fixed a mixup between the clock-stretched and non-clock stretched
commands.
- Fixed an issue where the return value of sts30_reset() was ignored.
- Removed redundant "Author" line at the top.
- Added comment at the top for the formula used to calculate the
temperature in Celsius alongside a macro.
- Added a dedicated macro for the temperature reading size.
- Separated the generic linux headers from the iio specific headers.
- Removed kernel.h from includes.
- Removed unneeded comments.
- Used named initializers for sts30_id and moved above _probe().
MAINTAINERS | 1 +
drivers/iio/temperature/Kconfig | 11 ++
drivers/iio/temperature/Makefile | 1 +
drivers/iio/temperature/sts30.c | 329 +++++++++++++++++++++++++++++++
4 files changed, 342 insertions(+)
create mode 100644 drivers/iio/temperature/sts30.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 7f94b8cac3e2..6bb361ac213f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24491,6 +24491,7 @@ SENSIRION STS30 TEMPERATURE SENSOR DRIVER
M: Maxwell Doose <m32285159@gmail.com>
S: Maintained
F: Documentation/devicetree/bindings/iio/temperature/sensirion,sts30.yaml
+F: drivers/iio/temperature/sts30.c
SERIAL DEVICE BUS
M: Rob Herring <robh@kernel.org>
diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig
index 9328b2250ace..c23a9c40c725 100644
--- a/drivers/iio/temperature/Kconfig
+++ b/drivers/iio/temperature/Kconfig
@@ -88,6 +88,17 @@ config MLX90635
This driver can also be built as a module. If so, the module will
be called mlx90635.
+config STS30
+ tristate "STS30/STS31/STS35 temperature sensor"
+ depends on I2C
+ select CRC8
+ help
+ If you say yes here you get support for the Sensirion STS30,
+ STS31, and STS35 temperature sensors over I2C.
+
+ This driver can also be built as a module. If so, the module will
+ be called sts30.
+
config TMP006
tristate "TMP006 infrared thermopile sensor"
depends on I2C
diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile
index 07d6e65709f7..9c9bf8d1b70e 100644
--- a/drivers/iio/temperature/Makefile
+++ b/drivers/iio/temperature/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_MCP9600) += mcp9600.o
obj-$(CONFIG_MLX90614) += mlx90614.o
obj-$(CONFIG_MLX90632) += mlx90632.o
obj-$(CONFIG_MLX90632) += mlx90635.o
+obj-$(CONFIG_STS30) += sts30.o
obj-$(CONFIG_TMP006) += tmp006.o
obj-$(CONFIG_TMP007) += tmp007.o
obj-$(CONFIG_TMP117) += tmp117.o
diff --git a/drivers/iio/temperature/sts30.c b/drivers/iio/temperature/sts30.c
new file mode 100644
index 000000000000..dcfe3435ae5a
--- /dev/null
+++ b/drivers/iio/temperature/sts30.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 Maxwell Doose
+ *
+ * Sensirion STS30 temperature sensor driver
+ *
+ * Datasheet: https://sensirion.com/media/documents/1DA31AFD/65D613A8/Datasheet_STS3x_DIS.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+/*
+ * STS30 measurement to Celsius conversion formula:
+ *
+ * T = -45 + 175(S[T]/(2^16 - 1)
+ *
+ * Where S[T] is the raw sensor output. We use this formula to calculate the offset.
+ * Using this we can calculate the offset to be -16852.
+ *
+ * See section 4.12 in the datasheet for more info.
+ */
+#define STS30_TEMP_OFFSET -16852
+
+/* Amount of bytes received from the STS30 after a read command */
+#define STS30_MEAS_SIZE 3
+
+/* Size of the temperature measurement data received after a read command */
+#define STS30_TEMP_MEAS_SIZE 2
+
+#define STS30_COMMAND_READ_HIGH_REPEAT 0x2400
+#define STS30_COMMAND_READ_MED_REPEAT 0x240B
+#define STS30_COMMAND_READ_LOW_REPEAT 0x2416
+
+#define STS30_COMMAND_RESET 0x30A2
+
+/*
+ * sts30 includes a CRC8 checksum at the end of its i2c responses. The polynomial
+ * is used to generate the CRC8 table and the seed is the starting value.
+ */
+#define STS30_CRC8_POLYNOMIAL 0x31
+#define STS30_CRC8_SEED 0xFF
+
+DECLARE_CRC8_TABLE(sts30_crc_table);
+
+enum sts30_read_delays {
+ STS30_REPEAT_LOW = 4500,
+ STS30_REPEAT_MED = 6000,
+ STS30_REPEAT_HIGH = 15000
+};
+
+struct sts30_data {
+ struct i2c_client *client;
+ struct mutex lock; /* Mutex for serialized communication on the i2c bus */
+ /*
+ * sts30 has three potential repeatability/measurement durations. We need to
+ * account for them while reading the i2c bus.
+ *
+ * See section 2.2 in the datasheet for more info on processing times.
+ */
+ enum sts30_read_delays delay;
+};
+
+static int sts30_verify_crc8(struct sts30_data *data, u8 buf[STS30_MEAS_SIZE])
+{
+ int crc;
+
+ crc = crc8(sts30_crc_table, buf, STS30_TEMP_MEAS_SIZE, STS30_CRC8_SEED);
+ if (crc != buf[2]) {
+ dev_err(&data->client->dev, "Expected CRC8 value of 0x%02x, got 0x%02x\n",
+ buf[2], crc);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sts30_read(struct sts30_data *data, u16 command, u16 *val)
+{
+ u8 buf[STS30_MEAS_SIZE];
+ u8 tmp[2];
+ int ret;
+
+ put_unaligned_be16(command, tmp);
+
+ ret = i2c_master_send(data->client, tmp, sizeof(tmp));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(tmp))
+ return -EIO;
+
+ fsleep(data->delay);
+
+ ret = i2c_master_recv(data->client, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(buf))
+ return -EIO;
+
+ *val = get_unaligned_be16(buf);
+
+ ret = sts30_verify_crc8(data, buf);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int sts30_write(struct sts30_data *data, u16 command)
+{
+ u8 buf[2];
+ int ret;
+
+ put_unaligned_be16(command, buf);
+
+ ret = i2c_master_send(data->client, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(buf))
+ return -EIO;
+
+ return 0;
+}
+
+static int sts30_reset(struct sts30_data *data)
+{
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ ret = sts30_write(data, STS30_COMMAND_RESET);
+ if (ret)
+ return ret;
+
+ /*
+ * Datasheet dictates that the maximum time for a soft reset is 1.5ms.
+ *
+ * See section 2.2 for more info on timing.
+ */
+ fsleep(1500);
+
+ return 0;
+}
+
+static int sts30_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct sts30_data *data = iio_priv(indio_dev);
+ int ret;
+ u16 tmp;
+
+ guard(mutex)(&data->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (data->delay) {
+ case STS30_REPEAT_LOW:
+ ret = sts30_read(data, STS30_COMMAND_READ_LOW_REPEAT, &tmp);
+ break;
+ case STS30_REPEAT_MED:
+ ret = sts30_read(data, STS30_COMMAND_READ_MED_REPEAT, &tmp);
+ break;
+ case STS30_REPEAT_HIGH:
+ ret = sts30_read(data, STS30_COMMAND_READ_HIGH_REPEAT, &tmp);
+ break;
+ default:
+ dev_warn(&data->client->dev, "Repeatability state corrupted, got: %d\n",
+ data->delay);
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ *val = tmp;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = STS30_TEMP_OFFSET;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 175000;
+ *val2 = 65535;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 0;
+ *val2 = data->delay;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sts30_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long mask)
+{
+ struct sts30_data *data = iio_priv(indio_dev);
+
+ if (val)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME: {
+ guard(mutex)(&data->lock);
+
+ switch (val2) {
+ case STS30_REPEAT_LOW:
+ data->delay = STS30_REPEAT_LOW;
+ break;
+ case STS30_REPEAT_MED:
+ data->delay = STS30_REPEAT_MED;
+ break;
+ case STS30_REPEAT_HIGH:
+ data->delay = STS30_REPEAT_HIGH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info sts30_info = {
+ .read_raw = sts30_read_raw,
+ .write_raw = sts30_write_raw
+};
+
+static const struct iio_chan_spec sts30_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_INT_TIME)
+ },
+};
+
+static int sts30_probe(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev;
+ struct sts30_data *data;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->name = client->name;
+ indio_dev->info = &sts30_info;
+ indio_dev->channels = sts30_channels;
+ indio_dev->num_channels = ARRAY_SIZE(sts30_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ data->delay = STS30_REPEAT_HIGH;
+
+ ret = devm_mutex_init(&client->dev, &data->lock);
+ if (ret)
+ return ret;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ ret = sts30_reset(data);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id sts30_id[] = {
+ { .name = "sts30" },
+ { .name = "sts31" },
+ { .name = "sts35" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sts30_id);
+
+static const struct of_device_id sts30_of_match[] = {
+ { .compatible = "sensirion,sts30" },
+ { .compatible = "sensirion,sts31" },
+ { .compatible = "sensirion,sts35" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sts30_of_match);
+
+static struct i2c_driver sts30_driver = {
+ .driver = {
+ .name = "sts30",
+ .of_match_table = sts30_of_match,
+ },
+ .probe = sts30_probe,
+ .id_table = sts30_id,
+};
+
+static int __init sts30_init(void)
+{
+ crc8_populate_msb(sts30_crc_table, STS30_CRC8_POLYNOMIAL);
+
+ return i2c_add_driver(&sts30_driver);
+}
+module_init(sts30_init);
+
+static void __exit sts30_exit(void)
+{
+ i2c_del_driver(&sts30_driver);
+}
+module_exit(sts30_exit);
+
+MODULE_AUTHOR("Maxwell Doose <m32285159@gmail.com>");
+MODULE_DESCRIPTION("Sensirion STS30 temperature sensor driver");
+MODULE_LICENSE("GPL");
--
2.54.0
^ permalink raw reply related
* [PATCH 1/9] arm64: dts: renesas: r8a774a1: Add soc: label to soc node
From: Marek Vasut @ 2026-06-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Marek Vasut, Conor Dooley, Geert Uytterhoeven,
Krzysztof Kozlowski, Rob Herring, devicetree, linux-kernel,
linux-renesas-soc
Add soc: label to the /soc {} node to align the DT with r8a77951.dtsi
which already has that soc: label. The soc: label is useful in U-Boot
where it is used in U-Boot extras DT fragments.
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
---
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
---
arch/arm64/boot/dts/renesas/r8a774a1.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a774a1.dtsi b/arch/arm64/boot/dts/renesas/r8a774a1.dtsi
index 36675f5bcdeaf..e66d86db6e6c4 100644
--- a/arch/arm64/boot/dts/renesas/r8a774a1.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a774a1.dtsi
@@ -262,7 +262,7 @@ scif_clk: scif {
clock-frequency = <0>;
};
- soc {
+ soc: soc {
compatible = "simple-bus";
bootph-all;
--
2.53.0
^ permalink raw reply related
* [PATCH 2/9] arm64: dts: renesas: r8a774b1: Add soc: label to soc node
From: Marek Vasut @ 2026-06-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Marek Vasut, Conor Dooley, Geert Uytterhoeven,
Krzysztof Kozlowski, Rob Herring, devicetree, linux-kernel,
linux-renesas-soc
In-Reply-To: <20260621025052.406507-1-marek.vasut+renesas@mailbox.org>
Add soc: label to the /soc {} node to align the DT with r8a77951.dtsi
which already has that soc: label. The soc: label is useful in U-Boot
where it is used in U-Boot extras DT fragments.
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
---
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
---
arch/arm64/boot/dts/renesas/r8a774b1.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a774b1.dtsi b/arch/arm64/boot/dts/renesas/r8a774b1.dtsi
index ceef0104f75e8..62c6703917db4 100644
--- a/arch/arm64/boot/dts/renesas/r8a774b1.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a774b1.dtsi
@@ -146,7 +146,7 @@ scif_clk: scif {
clock-frequency = <0>;
};
- soc {
+ soc: soc {
compatible = "simple-bus";
bootph-all;
--
2.53.0
^ permalink raw reply related
* [PATCH 3/9] arm64: dts: renesas: r8a774e1: Add soc: label to soc node
From: Marek Vasut @ 2026-06-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Marek Vasut, Conor Dooley, Geert Uytterhoeven,
Krzysztof Kozlowski, Rob Herring, devicetree, linux-kernel,
linux-renesas-soc
In-Reply-To: <20260621025052.406507-1-marek.vasut+renesas@mailbox.org>
Add soc: label to the /soc {} node to align the DT with r8a77951.dtsi
which already has that soc: label. The soc: label is useful in U-Boot
where it is used in U-Boot extras DT fragments.
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
---
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
---
arch/arm64/boot/dts/renesas/r8a774e1.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a774e1.dtsi b/arch/arm64/boot/dts/renesas/r8a774e1.dtsi
index 9df5f1a424004..0ae9bb72d2dda 100644
--- a/arch/arm64/boot/dts/renesas/r8a774e1.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a774e1.dtsi
@@ -326,7 +326,7 @@ scif_clk: scif {
clock-frequency = <0>;
};
- soc {
+ soc: soc {
compatible = "simple-bus";
bootph-all;
--
2.53.0
^ permalink raw reply related
* [PATCH 4/9] arm64: dts: renesas: r8a77960: Add soc: label to soc node
From: Marek Vasut @ 2026-06-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Marek Vasut, Conor Dooley, Geert Uytterhoeven,
Krzysztof Kozlowski, Rob Herring, devicetree, linux-kernel,
linux-renesas-soc
In-Reply-To: <20260621025052.406507-1-marek.vasut+renesas@mailbox.org>
Add soc: label to the /soc {} node to align the DT with r8a77951.dtsi
which already has that soc: label. The soc: label is useful in U-Boot
where it is used in U-Boot extras DT fragments.
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
---
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
---
arch/arm64/boot/dts/renesas/r8a77960.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a77960.dtsi b/arch/arm64/boot/dts/renesas/r8a77960.dtsi
index ad36aa8e75435..4f9989b5e77a8 100644
--- a/arch/arm64/boot/dts/renesas/r8a77960.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a77960.dtsi
@@ -311,7 +311,7 @@ scif_clk: scif {
clock-frequency = <0>;
};
- soc {
+ soc: soc {
compatible = "simple-bus";
bootph-all;
--
2.53.0
^ permalink raw reply related
* [PATCH 6/9] arm64: dts: renesas: r8a77965: Add soc: label to soc node
From: Marek Vasut @ 2026-06-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Marek Vasut, Conor Dooley, Geert Uytterhoeven,
Krzysztof Kozlowski, Rob Herring, devicetree, linux-kernel,
linux-renesas-soc
In-Reply-To: <20260621025052.406507-1-marek.vasut+renesas@mailbox.org>
Add soc: label to the /soc {} node to align the DT with r8a77951.dtsi
which already has that soc: label. The soc: label is useful in U-Boot
where it is used in U-Boot extras DT fragments.
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
---
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
---
arch/arm64/boot/dts/renesas/r8a77965.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a77965.dtsi b/arch/arm64/boot/dts/renesas/r8a77965.dtsi
index 611a9335c63ad..70708f5cf7467 100644
--- a/arch/arm64/boot/dts/renesas/r8a77965.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a77965.dtsi
@@ -182,7 +182,7 @@ scif_clk: scif {
clock-frequency = <0>;
};
- soc {
+ soc: soc {
compatible = "simple-bus";
bootph-all;
--
2.53.0
^ permalink raw reply related
* [PATCH 7/9] arm64: dts: renesas: r8a77970: Add soc: label to soc node
From: Marek Vasut @ 2026-06-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Marek Vasut, Conor Dooley, Geert Uytterhoeven,
Krzysztof Kozlowski, Rob Herring, devicetree, linux-kernel,
linux-renesas-soc
In-Reply-To: <20260621025052.406507-1-marek.vasut+renesas@mailbox.org>
Add soc: label to the /soc {} node to align the DT with r8a77951.dtsi
which already has that soc: label. The soc: label is useful in U-Boot
where it is used in U-Boot extras DT fragments.
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
---
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
---
arch/arm64/boot/dts/renesas/r8a77970.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a77970.dtsi b/arch/arm64/boot/dts/renesas/r8a77970.dtsi
index 1f6676e2795a4..f7f1f280fa0b6 100644
--- a/arch/arm64/boot/dts/renesas/r8a77970.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a77970.dtsi
@@ -91,7 +91,7 @@ scif_clk: scif {
clock-frequency = <0>;
};
- soc {
+ soc: soc {
compatible = "simple-bus";
bootph-all;
--
2.53.0
^ permalink raw reply related
* [PATCH 5/9] arm64: dts: renesas: r8a77961: Add soc: label to soc node
From: Marek Vasut @ 2026-06-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Marek Vasut, Conor Dooley, Geert Uytterhoeven,
Krzysztof Kozlowski, Rob Herring, devicetree, linux-kernel,
linux-renesas-soc
In-Reply-To: <20260621025052.406507-1-marek.vasut+renesas@mailbox.org>
Add soc: label to the /soc {} node to align the DT with r8a77951.dtsi
which already has that soc: label. The soc: label is useful in U-Boot
where it is used in U-Boot extras DT fragments.
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
---
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
---
arch/arm64/boot/dts/renesas/r8a77961.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a77961.dtsi b/arch/arm64/boot/dts/renesas/r8a77961.dtsi
index 9d76e39eab72e..ad4491ba948f2 100644
--- a/arch/arm64/boot/dts/renesas/r8a77961.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a77961.dtsi
@@ -311,7 +311,7 @@ scif_clk: scif {
clock-frequency = <0>;
};
- soc {
+ soc: soc {
compatible = "simple-bus";
bootph-all;
--
2.53.0
^ permalink raw reply related
* [PATCH 8/9] arm64: dts: renesas: r8a77980: Add soc: label to soc node
From: Marek Vasut @ 2026-06-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Marek Vasut, Conor Dooley, Geert Uytterhoeven,
Krzysztof Kozlowski, Rob Herring, devicetree, linux-kernel,
linux-renesas-soc
In-Reply-To: <20260621025052.406507-1-marek.vasut+renesas@mailbox.org>
Add soc: label to the /soc {} node to align the DT with r8a77951.dtsi
which already has that soc: label. The soc: label is useful in U-Boot
where it is used in U-Boot extras DT fragments.
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
---
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
---
arch/arm64/boot/dts/renesas/r8a77980.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a77980.dtsi b/arch/arm64/boot/dts/renesas/r8a77980.dtsi
index 86b7792d68fac..514dafe344786 100644
--- a/arch/arm64/boot/dts/renesas/r8a77980.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a77980.dtsi
@@ -120,7 +120,7 @@ scif_clk: scif {
clock-frequency = <0>;
};
- soc {
+ soc: soc {
compatible = "simple-bus";
bootph-all;
--
2.53.0
^ permalink raw reply related
* [PATCH 9/9] arm64: dts: renesas: r8a77995: Add soc: label to soc node
From: Marek Vasut @ 2026-06-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Marek Vasut, Conor Dooley, Geert Uytterhoeven,
Krzysztof Kozlowski, Rob Herring, devicetree, linux-kernel,
linux-renesas-soc
In-Reply-To: <20260621025052.406507-1-marek.vasut+renesas@mailbox.org>
Add soc: label to the /soc {} node to align the DT with r8a77951.dtsi
which already has that soc: label. The soc: label is useful in U-Boot
where it is used in U-Boot extras DT fragments.
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
---
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
---
arch/arm64/boot/dts/renesas/r8a77995.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a77995.dtsi b/arch/arm64/boot/dts/renesas/r8a77995.dtsi
index 5f3fcef7560cb..522a49db02587 100644
--- a/arch/arm64/boot/dts/renesas/r8a77995.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a77995.dtsi
@@ -85,7 +85,7 @@ scif_clk: scif {
clock-frequency = <0>;
};
- soc {
+ soc: soc {
compatible = "simple-bus";
bootph-all;
--
2.53.0
^ permalink raw reply related
* [PATCH 0/3] X2 Elite HP EliteBook X G2q support
From: Jason Pettit @ 2026-06-21 4:50 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel, Akhil P Oommen,
Mahadevan P, Sibi Sankar, Jingyi Wang, Ananthu C V, Jason Pettit
This series adds initial device-tree support for the HP EliteBook X G2q
14" Next Gen AI PC (product SKU C4JG0AV, board 8E91), a Snapdragon X2
Elite (Glymur) laptop.
The board was brought up and validated on real hardware. The electrical
description (PMIC rails, PHY supplies, the HDMI bridge wiring) was
verified against the live unit rather than copied from a Glymur CRD.
Working on this laptop with this series applied:
- Internal eDP panel (samsung,atna33xc20)
- 2x USB Type-C with DisplayPort alt-mode and USB
- Chassis HDMI output
- Chassis USB-A host port
- Fingerprint reader
- NVMe SSD on PCIe5
- Wi-Fi, Bluetooth
- Keyboard, touchpad, touchscreen, lid switch
- Adreno GPU and GMU (Freedreno GL on Mesa)
- Audio playback and capture
This patch series builds upon the recent work of Qualcomm engineers to
enable another Glymur-based laptop.
Like its peer submissions, the camera support is missing today.
This has the same known dependencies as other boards on this
platform (audio, GPU, SoCCP series).
The GPU device-tree patches come from the partially merged Glymur GPU DT
v5 series at
https://lore.kernel.org/all/20260522-glymur-gpu-dt-v5-0-562c406b210c@oss.qualcomm.com/
SCMI on this configuration needs CONFIG_QCOM_CPUCP_MBOX built in (=y).
The bindings patch is independent and can be reviewed/applied on its own.
The board patch can be reviewed in parallel with the GPU, LPASS and SoCCP
SoC series. Until those land in the target tree, it will trip the expected
"Label or path ... not found" dtc error. As with the X2 Elite Lenovo Yoga
Slim 7x submission, this v1 is expected to trigger some dt-bindings warnings
that come from the in-flight dependency series, not from the board patch
itself.
Signed-off-by: Jason Pettit <jason.pettit@oss.qualcomm.com>
---
Jason Pettit (3):
dt-bindings: arm: qcom: Add HP EliteBook X G2q 14 AI
arm64: dts: qcom: Add HP EliteBook X G2q 14 AI
firmware: qcom: scm: Allow QSEECOM on HP EliteBook X G2q 14 AI
Documentation/devicetree/bindings/arm/qcom.yaml | 1 +
arch/arm64/boot/dts/qcom/Makefile | 1 +
.../boot/dts/qcom/glymur-hp-elitebook-x-g2q.dts | 977 +++++++++++++++++++++
drivers/firmware/qcom/qcom_scm.c | 1 +
4 files changed, 980 insertions(+)
---
base-commit: c52dce2340e79e98cf4aa05606edc8db1759e026
change-id: 20260619-glymur-send-1136d243a120
prerequisite-patch-id: 36f4bb7740fd65d808fa6685bce4b03798a547ff
prerequisite-patch-id: 054631082c45d3ab3117f541f0d4d90b660dac73
prerequisite-patch-id: af18aef027c45213c11b436746fa31b3b6ebe46b
prerequisite-message-id: <20260419-glymur_dp-v1-1-ad1067a8e8ae@oss.qualcomm.com>
prerequisite-patch-id: 7cb9a168c33491bf91ece900a2218aa7b4b68ddd
prerequisite-message-id: <20260523085511.2532669-1-sibi.sankar@oss.qualcomm.com>
prerequisite-patch-id: 43e96d8a5063f51e2b5ee75fc8c9e10682e5670c
prerequisite-patch-id: 2e71d345f84a821d89da2a6064954409fdb1277d
prerequisite-patch-id: 0b9f5995044bb3d3ffda227ac986b755fa118c63
prerequisite-message-id: <20260519-knp-soccp-v6-0-cf5d0e194b5f@oss.qualcomm.com>
prerequisite-patch-id: 8459bcae98ac156f6576657fe9233badcd385218
prerequisite-patch-id: 9ab2b37c0a11c3a9590be921fefc8709ebebf6a3
prerequisite-patch-id: 21440dddcc340d35558010fe88bad9265e43c6ac
prerequisite-patch-id: 162f8b373ead5344ae0f29c92432db57a0d75f39
prerequisite-patch-id: bf5532cd5c8b66127e20be0677adbbbb3bd9578f
prerequisite-patch-id: fa7954f6fd2d27a6f0e805c9643b70e7e201c503
prerequisite-message-id: <20260403-glymur-soccp-v3-1-f0e8d57f11ba@oss.qualcomm.com>
prerequisite-patch-id: 48c26c428cbeaa674a615704d1abeb87e4e965c8
Best regards,
--
Jason Pettit <jason.pettit@oss.qualcomm.com>
^ permalink raw reply
* [PATCH 1/3] dt-bindings: arm: qcom: Add HP EliteBook X G2q 14 AI
From: Jason Pettit @ 2026-06-21 4:50 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel, Akhil P Oommen,
Mahadevan P, Sibi Sankar, Jingyi Wang, Ananthu C V, Jason Pettit
In-Reply-To: <20260620-glymur-send-v1-0-fc4a2cfd107c@oss.qualcomm.com>
The HP EliteBook X G2q 14 AI is a Snapdragon X2 Elite (Glymur) laptop.
Document its top-level "hp,elitebook-x-g2q" compatible, falling back to
the existing "qcom,glymur" SoC compatible.
Signed-off-by: Jason Pettit <jason.pettit@oss.qualcomm.com>
Assisted-by: Claude:claude-opus-4-8
---
Documentation/devicetree/bindings/arm/qcom.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/qcom.yaml b/Documentation/devicetree/bindings/arm/qcom.yaml
index 50cc18a6ec5e..934f62407b86 100644
--- a/Documentation/devicetree/bindings/arm/qcom.yaml
+++ b/Documentation/devicetree/bindings/arm/qcom.yaml
@@ -68,6 +68,7 @@ properties:
- items:
- enum:
+ - hp,elitebook-x-g2q
- qcom,glymur-crd
- const: qcom,glymur
--
2.50.1
^ permalink raw reply related
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