Linux RTC
 help / color / mirror / Atom feed
* [PATCH 11/13] Documentation: leds: document pattern behavior of Samsung S2M series PMIC RGB LEDs
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty
In-Reply-To: <20251114-s2mu005-pmic-v1-0-9e3184d3a0c9@disroot.org>

Add documentation to describe how hardware patterns (as defined by the
documentation of led-class-multicolor) are parsed and implemented by the
Samsung S2M series PMIC RGB LED driver.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 Documentation/leds/index.rst        |  1 +
 Documentation/leds/leds-s2m-rgb.rst | 60 +++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)

diff --git a/Documentation/leds/index.rst b/Documentation/leds/index.rst
index 76fae171039c..05d8e8517a80 100644
--- a/Documentation/leds/index.rst
+++ b/Documentation/leds/index.rst
@@ -27,6 +27,7 @@ LEDs
    leds-lp55xx
    leds-mlxcpld
    leds-mt6370-rgb
+   leds-s2m-rgb
    leds-sc27xx
    leds-st1202
    leds-qcom-lpg
diff --git a/Documentation/leds/leds-s2m-rgb.rst b/Documentation/leds/leds-s2m-rgb.rst
new file mode 100644
index 000000000000..cf91f0238093
--- /dev/null
+++ b/Documentation/leds/leds-s2m-rgb.rst
@@ -0,0 +1,60 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================
+Samsung S2M Series PMIC RGB LED Driver
+======================================
+
+Description
+-----------
+
+The RGB LED on the S2M series PMIC hardware features a three-channel LED that is
+grouped together as a single device. Furthermore, the it supports 8-bit
+brightness control for each channel. This LED is typically used as a status
+indicator in mobile devices. It also supports various parameters for hardware
+patterns.
+
+The hardware pattern can be programmed using the "pattern" trigger, using the
+hw_pattern attribute.
+
+/sys/class/leds/<led>/repeat
+----------------------------
+
+The hardware supports only indefinitely repeating patterns. The repeat
+attribute must be set to -1 for hardware patterns to function.
+
+/sys/class/leds/<led>/hw_pattern
+--------------------------------
+
+Specify a hardware pattern for the RGB LEDs.
+
+The pattern is a series of brightness levels and durations in milliseconds.
+There should be only one non-zero brightness level. Unlike the results
+described in leds-trigger-pattern, the transitions between on and off states
+are smoothed out by the hardware.
+
+Simple pattern::
+
+    "255 3000 0 1000"
+
+    255 -+ ''''''-.                     .-'''''''-.
+         |         '.                 .'           '.
+         |           \               /               \
+         |            '.           .'                 '.
+         |              '-.......-'                     '-
+      0 -+-------+-------+-------+-------+-------+-------+--> time (s)
+         0       1       2       3       4       5       6
+
+As described in leds-trigger-pattern, it is also possible to use zero-length
+entries to disable the ramping mechanism.
+
+On-Off pattern::
+
+    "255 1000 255 0 0 1000 0 0"
+
+    255 -+ ------+       +-------+       +-------+
+         |       |       |       |       |       |
+         |       |       |       |       |       |
+         |       |       |       |       |       |
+         |       +-------+       +-------+       +-------
+      0 -+-------+-------+-------+-------+-------+-------+--> time (s)
+         0       1       2       3       4       5       6

-- 
2.51.2


^ permalink raw reply related

* [PATCH 10/13] leds: rgb: add support for Samsung S2M series PMIC RGB LED device
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty
In-Reply-To: <20251114-s2mu005-pmic-v1-0-9e3184d3a0c9@disroot.org>

Add support for the RGB LEDs found in certain Samsung S2M series PMICs.
The device has three LED channels, controlled as a single device. These
LEDs are typically used as status indicators in mobile phones.

The driver includes initial support for the S2MU005 PMIC RGB LEDs.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 drivers/leds/rgb/Kconfig        |  11 +
 drivers/leds/rgb/Makefile       |   1 +
 drivers/leds/rgb/leds-s2m-rgb.c | 462 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 474 insertions(+)

diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig
index 222d943d826a..e38ba1bd434e 100644
--- a/drivers/leds/rgb/Kconfig
+++ b/drivers/leds/rgb/Kconfig
@@ -62,6 +62,17 @@ config LEDS_QCOM_LPG
 
 	  If compiled as a module, the module will be named leds-qcom-lpg.
 
+config LEDS_S2M_RGB
+	tristate "Samsung S2M series PMICs RGB LED support"
+	depends on LEDS_CLASS
+	depends on MFD_SEC_CORE
+	select REGMAP_IRQ
+	help
+	  This option enables support for the S2MU005 RGB LEDs. These
+	  devices have three LED channels, with 8-bit brightness control
+	  for each channel. It's usually found in mobile phones as
+	  status indicators.
+
 config LEDS_MT6370_RGB
 	tristate "LED Support for MediaTek MT6370 PMIC"
 	depends on MFD_MT6370
diff --git a/drivers/leds/rgb/Makefile b/drivers/leds/rgb/Makefile
index a501fd27f179..fc9d38fa60e1 100644
--- a/drivers/leds/rgb/Makefile
+++ b/drivers/leds/rgb/Makefile
@@ -5,4 +5,5 @@ obj-$(CONFIG_LEDS_KTD202X)		+= leds-ktd202x.o
 obj-$(CONFIG_LEDS_NCP5623)		+= leds-ncp5623.o
 obj-$(CONFIG_LEDS_PWM_MULTICOLOR)	+= leds-pwm-multicolor.o
 obj-$(CONFIG_LEDS_QCOM_LPG)		+= leds-qcom-lpg.o
+obj-$(CONFIG_LEDS_S2M_RGB)		+= leds-s2m-rgb.o
 obj-$(CONFIG_LEDS_MT6370_RGB)		+= leds-mt6370-rgb.o
diff --git a/drivers/leds/rgb/leds-s2m-rgb.c b/drivers/leds/rgb/leds-s2m-rgb.c
new file mode 100644
index 000000000000..2184ae0aec16
--- /dev/null
+++ b/drivers/leds/rgb/leds-s2m-rgb.c
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RGB LED Driver for Samsung S2M series PMICs.
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd
+ * Copyright (c) 2025 Kaustabh Chakraborty <kauschluss@disroot.org>
+ */
+
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/led-class-multicolor.h>
+#include <linux/math.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/s2mu005.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+struct s2m_rgb {
+	struct device *dev;
+	struct regmap *regmap;
+	struct led_classdev_mc cdev;
+	struct mutex lock;
+	const struct s2m_rgb_spec *spec;
+	u8 ramp_up;
+	u8 ramp_dn;
+	u8 stay_hi;
+	u8 stay_lo;
+};
+
+struct s2m_rgb_spec {
+	int (*params_apply)(struct s2m_rgb *priv);
+	int (*params_reset)(struct s2m_rgb *priv);
+	const u32 *lut_ramp_up;
+	const size_t lut_ramp_up_len;
+	const u32 *lut_ramp_dn;
+	const size_t lut_ramp_dn_len;
+	const u32 *lut_stay_hi;
+	const size_t lut_stay_hi_len;
+	const u32 *lut_stay_lo;
+	const size_t lut_stay_lo_len;
+	const unsigned int max_brightness;
+};
+
+static struct led_classdev_mc *to_cdev_mc(struct led_classdev *cdev)
+{
+	return container_of(cdev, struct led_classdev_mc, led_cdev);
+}
+
+static struct s2m_rgb *to_rgb_priv(struct led_classdev_mc *cdev)
+{
+	return container_of(cdev, struct s2m_rgb, cdev);
+}
+
+static int s2m_rgb_lut_calc_timing(const u32 *lut, const size_t len,
+				   const u32 req_time, u8 *idx)
+{
+	int lo = 0;
+	int hi = len - 2;
+
+	/* Bounds checking */
+	if (req_time < lut[0] || req_time > lut[len - 1])
+		return -EINVAL;
+
+	/*
+	 * Perform a binary search to pick the best timing from the LUT.
+	 *
+	 * The search algorithm picks two consecutive elements of the
+	 * LUT and tries to search the pair between which the requested
+	 * time lies.
+	 */
+	while (lo <= hi) {
+		*idx = (lo + hi) / 2;
+
+		if ((lut[*idx] <= req_time) && (req_time <= lut[*idx + 1]))
+			break;
+
+		if ((req_time < lut[*idx]) && (req_time < lut[*idx + 1]))
+			hi = *idx - 1;
+		else
+			lo = *idx + 1;
+	}
+
+	/*
+	 * The searched timing is always less than the requested time. At
+	 * times, the succeeding timing in the LUT is closer thus more
+	 * accurate. Adjust the resulting value if that's the case.
+	 */
+	if (abs(req_time - lut[*idx]) > abs(lut[*idx + 1] - req_time))
+		(*idx)++;
+
+	return 0;
+}
+
+static int s2m_rgb_brightness_set(struct led_classdev *cdev,
+				  enum led_brightness value)
+{
+	struct s2m_rgb *priv = to_rgb_priv(to_cdev_mc(cdev));
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	led_mc_calc_color_components(&priv->cdev, value);
+
+	if (value == LED_OFF)
+		ret = priv->spec->params_reset(priv);
+	else
+		ret = priv->spec->params_apply(priv);
+
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static int s2m_rgb_pattern_set(struct led_classdev *cdev,
+			       struct led_pattern *pattern, u32 len, int repeat)
+{
+	struct s2m_rgb *priv = to_rgb_priv(to_cdev_mc(cdev));
+	int brightness_peak = 0;
+	u32 time_hi = 0;
+	u32 time_lo = 0;
+	bool ramp_up_en;
+	bool ramp_dn_en;
+	int ret;
+	int i;
+
+	/*
+	 * The typical pattern supported by this device can be
+	 * represented with the following graph:
+	 *
+	 *  255 T ''''''-.                         .-'''''''-.
+	 *      |         '.                     .'           '.
+	 *      |           \                   /               \
+	 *      |            '.               .'                 '.
+	 *      |              '-...........-'                     '-
+	 *    0 +----------------------------------------------------> time (s)
+	 *
+	 *       <---- HIGH ----><-- LOW --><-------- HIGH --------->
+	 *       <-----><-------><---------><-------><-----><------->
+	 *       stay_hi ramp_dn   stay_lo   ramp_up stay_hi ramp_dn
+	 *
+	 * There are two states, named HIGH and LOW. HIGH has a non-zero
+	 * brightness level, while LOW is of zero brightness. The
+	 * pattern provided should mention only one zero and non-zero
+	 * brightness level. The hardware always starts the pattern from
+	 * the HIGH state, as shown in the graph.
+	 *
+	 * The HIGH state can be divided in three somewhat equal timings:
+	 * ramp_up, stay_hi, and ramp_dn. The LOW state has only one
+	 * timing: stay_lo.
+	 */
+
+	/* Only indefinitely looping patterns are supported. */
+	if (repeat != -1)
+		return -EINVAL;
+
+	/* Pattern should consist of at least two tuples. */
+	if (len < 2)
+		return -EINVAL;
+
+	for (i = 0; i < len; i++) {
+		int brightness = pattern[i].brightness;
+		u32 delta_t = pattern[i].delta_t;
+
+		if (brightness) {
+			/*
+			 * The pattern shold define only one non-zero
+			 * brightness in the HIGH state. The device
+			 * doesn't have any provisions to handle
+			 * multiple peak brightness levels.
+			 */
+			if (brightness_peak && brightness_peak != brightness)
+				return -EINVAL;
+
+			brightness_peak = brightness;
+			time_hi += delta_t;
+			ramp_dn_en = !!delta_t;
+		} else {
+			time_lo += delta_t;
+			ramp_up_en = !!delta_t;
+		}
+	}
+
+	mutex_lock(&priv->lock);
+
+	/*
+	 * The timings ramp_up, stay_hi, and ramp_dn of the HIGH state
+	 * are roughly equal. Firstly, calculate and set timings for
+	 * ramp_up and ramp_dn (making sure they're exactly equal).
+	 */
+	priv->ramp_up = 0;
+	priv->ramp_dn = 0;
+
+	if (ramp_up_en) {
+		ret = s2m_rgb_lut_calc_timing(priv->spec->lut_ramp_up,
+					      priv->spec->lut_ramp_up_len,
+					      time_hi / 3, &priv->ramp_up);
+		if (ret < 0)
+			goto param_fail;
+	}
+
+	if (ramp_dn_en) {
+		ret = s2m_rgb_lut_calc_timing(priv->spec->lut_ramp_dn,
+					      priv->spec->lut_ramp_dn_len,
+					      time_hi / 3, &priv->ramp_dn);
+		if (ret < 0)
+			goto param_fail;
+	}
+
+	/*
+	 * Subtract the allocated ramp timings from time_hi (and also
+	 * making sure it doesn't underflow!). The remaining time is
+	 * allocated to stay_hi.
+	 */
+	time_hi -= min(time_hi, priv->spec->lut_ramp_up[priv->ramp_up]);
+	time_hi -= min(time_hi, priv->spec->lut_ramp_dn[priv->ramp_dn]);
+
+	ret = s2m_rgb_lut_calc_timing(priv->spec->lut_stay_hi,
+				      priv->spec->lut_stay_hi_len, time_hi,
+				      &priv->stay_hi);
+	if (ret < 0)
+		goto param_fail;
+
+	ret = s2m_rgb_lut_calc_timing(priv->spec->lut_stay_lo,
+				      priv->spec->lut_stay_lo_len, time_lo,
+				      &priv->stay_lo);
+	if (ret < 0)
+		goto param_fail;
+
+	led_mc_calc_color_components(&priv->cdev, brightness_peak);
+	ret = priv->spec->params_apply(priv);
+	if (ret < 0)
+		goto param_fail;
+
+	mutex_unlock(&priv->lock);
+
+	return 0;
+
+param_fail:
+	mutex_unlock(&priv->lock);
+	priv->ramp_up = 0;
+	priv->ramp_dn = 0;
+	priv->stay_hi = 0;
+	priv->stay_lo = 0;
+
+	return ret;
+}
+
+static int s2m_rgb_pattern_clear(struct led_classdev *cdev)
+{
+	struct s2m_rgb *priv = to_rgb_priv(to_cdev_mc(cdev));
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	ret = priv->spec->params_reset(priv);
+
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static int s2mu005_rgb_apply_params(struct s2m_rgb *priv)
+{
+	struct regmap *regmap = priv->regmap;
+	unsigned int ramp_val = 0;
+	unsigned int stay_val = 0;
+	int ret;
+	int i;
+
+	ramp_val |= FIELD_PREP(S2MU005_RGB_CH_RAMP_UP, priv->ramp_up);
+	ramp_val |= FIELD_PREP(S2MU005_RGB_CH_RAMP_DN, priv->ramp_dn);
+
+	stay_val |= FIELD_PREP(S2MU005_RGB_CH_STAY_HI, priv->stay_hi);
+	stay_val |= FIELD_PREP(S2MU005_RGB_CH_STAY_LO, priv->stay_lo);
+
+	ret = regmap_write(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_RESET);
+	if (ret < 0) {
+		dev_err(priv->dev, "failed to reset RGB LEDs\n");
+		return ret;
+	}
+
+	for (i = 0; i < priv->cdev.num_colors; i++) {
+		ret = regmap_write(regmap, S2MU005_REG_RGB_CH_CTRL(i),
+				   priv->cdev.subled_info[i].brightness);
+		if (ret < 0) {
+			dev_err(priv->dev, "failed to set LED brightness\n");
+			return ret;
+		}
+
+		ret = regmap_write(regmap, S2MU005_REG_RGB_CH_RAMP(i), ramp_val);
+		if (ret < 0) {
+			dev_err(priv->dev, "failed to set ramp timings\n");
+			return ret;
+		}
+
+		ret = regmap_write(regmap, S2MU005_REG_RGB_CH_STAY(i), stay_val);
+		if (ret < 0) {
+			dev_err(priv->dev, "failed to set stay timings\n");
+			return ret;
+		}
+	}
+
+	ret = regmap_update_bits(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_SLOPE,
+				 S2MU005_RGB_SLOPE_SMOOTH);
+	if (ret < 0) {
+		dev_err(priv->dev, "failed to set ramp slope\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int s2mu005_rgb_reset_params(struct s2m_rgb *priv)
+{
+	struct regmap *regmap = priv->regmap;
+	int ret;
+
+	ret = regmap_write(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_RESET);
+	if (ret < 0) {
+		dev_err(priv->dev, "failed to reset RGB LEDs\n");
+		return ret;
+	}
+
+	priv->ramp_up = 0;
+	priv->ramp_dn = 0;
+	priv->stay_hi = 0;
+	priv->stay_lo = 0;
+
+	return 0;
+}
+
+static const u32 s2mu005_rgb_lut_ramp[] = {
+	0,	100,	200,	300,	400,	500,	600,	700,
+	800,	1000,	1200,	1400,	1600,	1800,	2000,	2200,
+};
+
+static const u32 s2mu005_rgb_lut_stay_hi[] = {
+	100,	200,	300,	400,	500,	750,	1000,	1250,
+	1500,	1750,	2000,	2250,	2500,	2750,	3000,	3250,
+};
+
+static const u32 s2mu005_rgb_lut_stay_lo[] = {
+	0,	500,	1000,	1500,	2000,	2500,	3000,	3500,
+	4000,	4500,	5000,	6000,	7000,	8000,	10000,	12000,
+};
+
+static const struct s2m_rgb_spec s2mu005_rgb_spec = {
+	.params_apply = s2mu005_rgb_apply_params,
+	.params_reset = s2mu005_rgb_reset_params,
+	.lut_ramp_up = s2mu005_rgb_lut_ramp,
+	.lut_ramp_up_len = ARRAY_SIZE(s2mu005_rgb_lut_ramp),
+	.lut_ramp_dn = s2mu005_rgb_lut_ramp,
+	.lut_ramp_dn_len = ARRAY_SIZE(s2mu005_rgb_lut_ramp),
+	.lut_stay_hi = s2mu005_rgb_lut_stay_hi,
+	.lut_stay_hi_len = ARRAY_SIZE(s2mu005_rgb_lut_stay_hi),
+	.lut_stay_lo = s2mu005_rgb_lut_stay_lo,
+	.lut_stay_lo_len = ARRAY_SIZE(s2mu005_rgb_lut_stay_lo),
+	.max_brightness = 255,
+};
+
+static struct mc_subled s2mu005_rgb_subled_info[] = {
+	{
+		.channel = 0,
+		.color_index = LED_COLOR_ID_BLUE,
+	}, {
+		.channel = 1,
+		.color_index = LED_COLOR_ID_GREEN,
+	}, {
+		.channel = 2,
+		.color_index = LED_COLOR_ID_RED,
+	},
+};
+
+static int s2m_rgb_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sec_pmic_dev *pmic_drvdata = dev_get_drvdata(dev->parent);
+	struct s2m_rgb *priv;
+	struct led_init_data init_data = {};
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return dev_err_probe(dev, -ENOMEM, "failed to allocate driver private\n");
+
+	platform_set_drvdata(pdev, priv);
+	priv->dev = dev;
+	priv->regmap = pmic_drvdata->regmap_pmic;
+
+	switch (platform_get_device_id(pdev)->driver_data) {
+	case S2MU005:
+		priv->spec = &s2mu005_rgb_spec;
+		priv->cdev.subled_info = s2mu005_rgb_subled_info;
+		priv->cdev.num_colors = ARRAY_SIZE(s2mu005_rgb_subled_info);
+		break;
+	default:
+		return dev_err_probe(dev, -ENODEV,
+				     "device type %d is not supported by driver\n",
+				     pmic_drvdata->device_type);
+	}
+
+	priv->cdev.led_cdev.max_brightness = priv->spec->max_brightness;
+	priv->cdev.led_cdev.brightness_set_blocking = s2m_rgb_brightness_set;
+	priv->cdev.led_cdev.pattern_set = s2m_rgb_pattern_set;
+	priv->cdev.led_cdev.pattern_clear = s2m_rgb_pattern_clear;
+
+	ret = devm_mutex_init(dev, &priv->lock);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to create mutex lock\n");
+
+	init_data.fwnode = of_fwnode_handle(dev->of_node);
+	ret = devm_led_classdev_multicolor_register_ext(dev, &priv->cdev,
+							&init_data);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "failed to create LED device\n");
+
+	return 0;
+}
+
+static const struct platform_device_id s2m_rgb_id_table[] = {
+	{ "s2mu005-rgb", S2MU005 },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, s2m_rgb_id_table);
+
+#ifdef CONFIG_OF
+/*
+ * Device is instantiated through parent MFD device and device matching
+ * is done through platform_device_id.
+ *
+ * However if device's DT node contains proper compatible and driver is
+ * built as a module, then the *module* matching will be done through DT
+ * aliases. This requires of_device_id table. In the same time this will
+ * not change the actual *device* matching so do not add .of_match_table.
+ */
+static const struct of_device_id s2m_rgb_of_match_table[] = {
+	{
+		.compatible = "samsung,s2mu005-rgb",
+		.data = (void *)S2MU005,
+	}, {
+		/* sentinel */
+	},
+};
+MODULE_DEVICE_TABLE(of, s2m_rgb_of_match_table);
+#endif
+
+static struct platform_driver s2m_rgb_driver = {
+	.driver = {
+		.name = "s2m-rgb",
+	},
+	.probe = s2m_rgb_probe,
+	.id_table = s2m_rgb_id_table,
+};
+module_platform_driver(s2m_rgb_driver);
+
+MODULE_DESCRIPTION("RGB LED Driver For Samsung S2M Series PMICs");
+MODULE_AUTHOR("Kaustabh Chakraborty <kauschluss@disroot.org>");
+MODULE_LICENSE("GPL");

-- 
2.51.2


^ permalink raw reply related

* [PATCH 09/13] leds: flash: add support for Samsung S2M series PMIC flash LED device
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty
In-Reply-To: <20251114-s2mu005-pmic-v1-0-9e3184d3a0c9@disroot.org>

Add support for flash LEDs found in certain Samsung S2M series PMICs.
The device has two channels for LEDs, typically for the back and front
cameras in mobile devices. Both channels can be independently
controlled, and can be operated in torch or flash modes.

The driver includes initial support for the S2MU005 PMIC flash LEDs.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 drivers/leds/flash/Kconfig          |  12 ++
 drivers/leds/flash/Makefile         |   1 +
 drivers/leds/flash/leds-s2m-flash.c | 413 ++++++++++++++++++++++++++++++++++++
 3 files changed, 426 insertions(+)

diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig
index 5e08102a6784..be62e0527742 100644
--- a/drivers/leds/flash/Kconfig
+++ b/drivers/leds/flash/Kconfig
@@ -114,6 +114,18 @@ config LEDS_RT8515
 	  To compile this driver as a module, choose M here: the module
 	  will be called leds-rt8515.
 
+config LEDS_S2M_FLASH
+	tristate "Samsung S2M series PMICs flash/torch LED support"
+	depends on LEDS_CLASS
+	depends on MFD_SEC_CORE
+	depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
+	select REGMAP_IRQ
+	help
+	  This option enables support for the flash/torch LEDs found in
+	  certain Samsung S2M series PMICs, such as the S2MU005. It has
+	  a LED channel dedicated for every physical LED. The LEDs can
+	  be controlled in flash and torch modes.
+
 config LEDS_SGM3140
 	tristate "LED support for the SGM3140"
 	depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile
index 712fb737a428..44e6c1b4beb3 100644
--- a/drivers/leds/flash/Makefile
+++ b/drivers/leds/flash/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_LEDS_MAX77693)	+= leds-max77693.o
 obj-$(CONFIG_LEDS_QCOM_FLASH)	+= leds-qcom-flash.o
 obj-$(CONFIG_LEDS_RT4505)	+= leds-rt4505.o
 obj-$(CONFIG_LEDS_RT8515)	+= leds-rt8515.o
+obj-$(CONFIG_LEDS_S2M_FLASH)	+= leds-s2m-flash.o
 obj-$(CONFIG_LEDS_SGM3140)	+= leds-sgm3140.o
 obj-$(CONFIG_LEDS_SY7802)	+= leds-sy7802.o
 obj-$(CONFIG_LEDS_TPS6131X)	+= leds-tps6131x.o
diff --git a/drivers/leds/flash/leds-s2m-flash.c b/drivers/leds/flash/leds-s2m-flash.c
new file mode 100644
index 000000000000..7ea033403b43
--- /dev/null
+++ b/drivers/leds/flash/leds-s2m-flash.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Flash and Torch LED Driver for Samsung S2M series PMICs.
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd
+ * Copyright (c) 2025 Kaustabh Chakraborty <kauschluss@disroot.org>
+ */
+
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/led-class-flash.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/s2mu005.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <media/v4l2-flash-led-class.h>
+
+#define MAX_CHANNELS	2
+
+struct s2m_fled {
+	struct device *dev;
+	struct regmap *regmap;
+	struct led_classdev_flash cdev;
+	struct v4l2_flash *v4l2_flash;
+	struct mutex lock;
+	const struct s2m_fled_spec *spec;
+	unsigned int pmic_revision;
+	u8 channel;
+	u8 flash_brightness;
+	u8 flash_timeout;
+};
+
+struct s2m_fled_spec {
+	u8 num_channels;
+	u32 torch_max_brightness;
+	u32 flash_min_current_ua;
+	u32 flash_max_current_ua;
+	u32 flash_min_timeout_us;
+	u32 flash_max_timeout_us;
+	int (*torch_brightness_set_blocking)(struct led_classdev *led_cdev,
+					     enum led_brightness brightness);
+	const struct led_flash_ops *flash_ops;
+};
+
+static struct led_classdev_flash *to_cdev_flash(struct led_classdev *cdev)
+{
+	return container_of(cdev, struct led_classdev_flash, led_cdev);
+}
+
+static struct s2m_fled *to_led_priv(struct led_classdev_flash *cdev)
+{
+	return container_of(cdev, struct s2m_fled, cdev);
+}
+
+static int s2m_fled_flash_brightness_set(struct led_classdev_flash *cdev,
+					 u32 brightness)
+{
+	struct s2m_fled *priv = to_led_priv(cdev);
+	struct led_flash_setting *setting = &cdev->brightness;
+
+	priv->flash_brightness = (brightness - setting->min) / setting->step;
+
+	return 0;
+}
+
+static int s2m_fled_flash_timeout_set(struct led_classdev_flash *cdev,
+				      u32 timeout)
+{
+	struct s2m_fled *priv = to_led_priv(cdev);
+	struct led_flash_setting *setting = &cdev->timeout;
+
+	priv->flash_timeout = (timeout - setting->min) / setting->step;
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+static int s2m_fled_flash_external_strobe_set(struct v4l2_flash *v4l2_flash,
+					      bool enable)
+{
+	struct s2m_fled *priv = to_led_priv(v4l2_flash->fled_cdev);
+
+	mutex_lock(&priv->lock);
+
+	priv->cdev.ops->strobe_set(&priv->cdev, enable);
+
+	mutex_unlock(&priv->lock);
+
+	return 0;
+}
+
+static const struct v4l2_flash_ops s2m_fled_v4l2_flash_ops = {
+	.external_strobe_set = s2m_fled_flash_external_strobe_set,
+};
+#else
+static const struct v4l2_flash_ops s2m_fled_v4l2_flash_ops;
+#endif
+
+static int s2mu005_fled_torch_brightness_set(struct led_classdev *cdev,
+					     enum led_brightness value)
+{
+	struct s2m_fled *priv = to_led_priv(to_cdev_flash(cdev));
+	struct regmap *regmap = priv->regmap;
+	u8 channel = priv->channel;
+	unsigned int reg_enable;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	/*
+	 * Get the LED enable register address. Revision EVT0 has the
+	 * register at CTRL4, while EVT1 and higher have it at CTRL6.
+	 */
+	if (priv->pmic_revision == 0)
+		reg_enable = S2MU005_REG_FLED_CTRL4;
+	else
+		reg_enable = S2MU005_REG_FLED_CTRL6;
+
+	if (value == LED_OFF) {
+		ret = regmap_clear_bits(regmap, reg_enable,
+					S2MU005_FLED_TORCH_EN(channel));
+		if (ret < 0)
+			dev_err(priv->dev, "failed to disable torch LED\n");
+		goto unlock;
+	}
+
+	ret = regmap_update_bits(regmap, S2MU005_REG_FLED_CH_CTRL1(channel),
+				 S2MU005_FLED_TORCH_IOUT,
+				 FIELD_PREP(S2MU005_FLED_TORCH_IOUT, value - 1));
+	if (ret < 0) {
+		dev_err(priv->dev, "failed to set torch current\n");
+		goto unlock;
+	}
+
+	ret = regmap_set_bits(regmap, reg_enable, S2MU005_FLED_TORCH_EN(channel));
+	if (ret < 0) {
+		dev_err(priv->dev, "failed to enable torch LED\n");
+		goto unlock;
+	}
+
+unlock:
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static int s2mu005_fled_flash_strobe_set(struct led_classdev_flash *cdev,
+					 bool state)
+{
+	struct s2m_fled *priv = to_led_priv(cdev);
+	struct regmap *regmap = priv->regmap;
+	u8 channel = priv->channel;
+	unsigned int reg_enable;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	/*
+	 * Get the LED enable register address. Revision EVT0 has the
+	 * register at CTRL4, while EVT1 and higher have it at CTRL6.
+	 */
+	if (priv->pmic_revision == 0)
+		reg_enable = S2MU005_REG_FLED_CTRL4;
+	else
+		reg_enable = S2MU005_REG_FLED_CTRL6;
+
+	ret = regmap_clear_bits(regmap, reg_enable, S2MU005_FLED_FLASH_EN(channel));
+	if (ret < 0) {
+		dev_err(priv->dev, "failed to disable flash LED\n");
+		goto unlock;
+	}
+
+	if (!state)
+		goto unlock;
+
+	ret = regmap_update_bits(regmap, S2MU005_REG_FLED_CH_CTRL0(channel),
+				 S2MU005_FLED_FLASH_IOUT,
+				 FIELD_PREP(S2MU005_FLED_FLASH_IOUT,
+					    priv->flash_brightness));
+	if (ret < 0) {
+		dev_err(priv->dev, "failed to set flash brightness\n");
+		goto unlock;
+	}
+
+	ret = regmap_update_bits(regmap, S2MU005_REG_FLED_CH_CTRL3(channel),
+				 S2MU005_FLED_FLASH_TIMEOUT,
+				 FIELD_PREP(S2MU005_FLED_FLASH_TIMEOUT,
+					    priv->flash_timeout));
+	if (ret < 0) {
+		dev_err(priv->dev, "failed to set flash timeout\n");
+		goto unlock;
+	}
+
+	ret = regmap_set_bits(regmap, reg_enable, S2MU005_FLED_FLASH_EN(channel));
+	if (ret < 0) {
+		dev_err(priv->dev, "failed to enable flash LED\n");
+		goto unlock;
+	}
+
+unlock:
+	mutex_unlock(&priv->lock);
+
+	return 0;
+}
+
+static int s2mu005_fled_flash_strobe_get(struct led_classdev_flash *cdev,
+					 bool *state)
+{
+	struct s2m_fled *priv = to_led_priv(cdev);
+	struct regmap *regmap = priv->regmap;
+	u8 channel = priv->channel;
+	u32 val;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	ret = regmap_read(regmap, S2MU005_REG_FLED_STATUS, &val);
+	if (ret < 0) {
+		dev_err(priv->dev, "failed to fetch LED status");
+		goto unlock;
+	}
+
+	*state = !!(val & S2MU005_FLED_FLASH_STATUS(channel));
+
+unlock:
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static const struct led_flash_ops s2mu005_fled_flash_ops = {
+	.flash_brightness_set = s2m_fled_flash_brightness_set,
+	.timeout_set = s2m_fled_flash_timeout_set,
+	.strobe_set = s2mu005_fled_flash_strobe_set,
+	.strobe_get = s2mu005_fled_flash_strobe_get,
+};
+
+static const struct s2m_fled_spec s2mu005_fled_spec = {
+	.num_channels = 2,
+	.torch_max_brightness = 16,
+	.flash_min_current_ua = 25000,
+	.flash_max_current_ua = 375000, /* 400000 causes flickering */
+	.flash_min_timeout_us = 62000,
+	.flash_max_timeout_us = 992000,
+	.torch_brightness_set_blocking = s2mu005_fled_torch_brightness_set,
+	.flash_ops = &s2mu005_fled_flash_ops,
+};
+
+static int s2m_fled_init_channel(struct device *dev, struct fwnode_handle *fwnp,
+				 struct s2m_fled *priv)
+{
+	struct led_classdev *led = &priv->cdev.led_cdev;
+	struct led_init_data init_data = {};
+	struct v4l2_flash_config v4l2_cfg = {};
+	int ret;
+
+	led->max_brightness = priv->spec->torch_max_brightness;
+	led->brightness_set_blocking = priv->spec->torch_brightness_set_blocking;
+	led->flags |= LED_DEV_CAP_FLASH;
+
+	priv->cdev.timeout.min = priv->spec->flash_min_timeout_us;
+	priv->cdev.timeout.step = priv->spec->flash_min_timeout_us;
+	priv->cdev.timeout.max = priv->spec->flash_max_timeout_us;
+	priv->cdev.timeout.val = priv->spec->flash_max_timeout_us;
+
+	priv->cdev.brightness.min = priv->spec->flash_min_current_ua;
+	priv->cdev.brightness.step = priv->spec->flash_min_current_ua;
+	priv->cdev.brightness.max = priv->spec->flash_max_current_ua;
+	priv->cdev.brightness.val = priv->spec->flash_max_current_ua;
+
+	s2m_fled_flash_timeout_set(&priv->cdev, priv->cdev.timeout.val);
+	s2m_fled_flash_brightness_set(&priv->cdev, priv->cdev.brightness.val);
+
+	priv->cdev.ops = priv->spec->flash_ops;
+
+	init_data.fwnode = fwnp;
+	ret = devm_led_classdev_flash_register_ext(dev, &priv->cdev, &init_data);
+	if (ret < 0) {
+		dev_err(dev, "failed to create LED flash device\n");
+		return ret;
+	}
+
+	v4l2_cfg.intensity.min = priv->spec->flash_min_current_ua;
+	v4l2_cfg.intensity.step = priv->spec->flash_min_current_ua;
+	v4l2_cfg.intensity.max = priv->spec->flash_max_current_ua;
+	v4l2_cfg.intensity.val = priv->spec->flash_max_current_ua;
+
+	v4l2_cfg.has_external_strobe = true;
+
+	priv->v4l2_flash = v4l2_flash_init(dev, fwnp, &priv->cdev,
+					   &s2m_fled_v4l2_flash_ops, &v4l2_cfg);
+	if (IS_ERR(priv->v4l2_flash)) {
+		dev_err(dev, "failed to create V4L2 flash device\n");
+		v4l2_flash_release(priv->v4l2_flash);
+		return PTR_ERR(priv->v4l2_flash);
+	}
+
+	return devm_add_action_or_reset(dev, (void *)v4l2_flash_release,
+					priv->v4l2_flash);
+}
+
+static int s2m_fled_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sec_pmic_dev *pmic_drvdata = dev_get_drvdata(dev->parent);
+	struct s2m_fled *priv;
+	struct fwnode_handle *child;
+	struct regmap *regmap;
+	const struct s2m_fled_spec *spec;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv) * MAX_CHANNELS, GFP_KERNEL);
+	if (!priv)
+		return dev_err_probe(dev, -ENOMEM, "failed to allocate driver private\n");
+
+	platform_set_drvdata(pdev, priv);
+	regmap = pmic_drvdata->regmap_pmic;
+
+	switch (platform_get_device_id(pdev)->driver_data) {
+	case S2MU005:
+		spec = &s2mu005_fled_spec;
+		/* Enable the LED channels. */
+		ret = regmap_set_bits(regmap, S2MU005_REG_FLED_CTRL1,
+				      S2MU005_FLED_CH_EN);
+		if (ret < 0)
+			return dev_err_probe(dev, ret, "failed to enable LED channels\n");
+		break;
+	default:
+		return dev_err_probe(dev, -ENODEV,
+				     "device type %d is not supported by driver\n",
+				     pmic_drvdata->device_type);
+	}
+
+	device_for_each_child_node(dev, child) {
+		u32 reg;
+
+		if (fwnode_property_read_u32(child, "reg", &reg))
+			goto next_child;
+
+		if (reg >= spec->num_channels) {
+			dev_warn(dev, "channel %d is non-existent\n", reg);
+			goto next_child;
+		}
+
+		if (priv[reg].dev) {
+			dev_warn(dev, "duplicate node for channel %d\n", reg);
+			goto next_child;
+		}
+
+		priv[reg].dev = dev;
+		priv[reg].regmap = regmap;
+		priv[reg].channel = (u8)reg;
+		priv[reg].spec = spec;
+		priv[reg].pmic_revision = pmic_drvdata->revision;
+
+		ret = devm_mutex_init(dev, &priv[reg].lock);
+		if (ret)
+			return dev_err_probe(dev, ret, "failed to create mutex lock\n");
+
+		ret = s2m_fled_init_channel(dev, child, &priv[reg]);
+		if (ret < 0)
+			dev_warn(dev, "channel init failed (%d)\n", ret);
+
+next_child:
+		fwnode_handle_put(child);
+	}
+
+	return 0;
+}
+
+static const struct platform_device_id s2m_fled_id_table[] = {
+	{ "s2mu005-flash", S2MU005 },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, s2m_fled_id_table);
+
+#ifdef CONFIG_OF
+/*
+ * Device is instantiated through parent MFD device and device matching
+ * is done through platform_device_id.
+ *
+ * However if device's DT node contains proper compatible and driver is
+ * built as a module, then the *module* matching will be done through DT
+ * aliases. This requires of_device_id table. In the same time this will
+ * not change the actual *device* matching so do not add .of_match_table.
+ */
+static const struct of_device_id s2m_fled_of_match_table[] = {
+	{
+		.compatible = "samsung,s2mu005-flash",
+		.data = (void *)S2MU005,
+	}, {
+		/* sentinel */
+	},
+};
+MODULE_DEVICE_TABLE(of, s2m_fled_of_match_table);
+#endif
+
+static struct platform_driver s2m_fled_driver = {
+	.driver = {
+		.name = "s2m-flash",
+	},
+	.probe = s2m_fled_probe,
+	.id_table = s2m_fled_id_table,
+};
+module_platform_driver(s2m_fled_driver);
+
+MODULE_DESCRIPTION("Flash/Torch LED Driver For Samsung S2M Series PMICs");
+MODULE_AUTHOR("Kaustabh Chakraborty <kauschluss@disroot.org>");
+MODULE_LICENSE("GPL");

-- 
2.51.2


^ permalink raw reply related

* [PATCH 08/13] mfd: sec: store hardware revision in sec_pmic_dev and add S2MU005 support
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty
In-Reply-To: <20251114-s2mu005-pmic-v1-0-9e3184d3a0c9@disroot.org>

The device revision matters in cases when in some PMICs, the correct
register offsets very in different revisions. Instead of just debug
printing the value, store it in the driver data struct.

Unlike other devices, S2MU005 has its hardware revision ID in register
offset 0x73. Allow handling different devices and add support for S2MU005.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 drivers/mfd/sec-common.c         | 30 ++++++++++++++++++++++++------
 include/linux/mfd/samsung/core.h |  3 +++
 2 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/drivers/mfd/sec-common.c b/drivers/mfd/sec-common.c
index 4c5f4dc2905b..f51c53e7a164 100644
--- a/drivers/mfd/sec-common.c
+++ b/drivers/mfd/sec-common.c
@@ -16,6 +16,7 @@
 #include <linux/mfd/samsung/irq.h>
 #include <linux/mfd/samsung/s2mps11.h>
 #include <linux/mfd/samsung/s2mps13.h>
+#include <linux/mfd/samsung/s2mu005.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/pm.h>
@@ -86,17 +87,34 @@ static const struct mfd_cell s2mu005_devs[] = {
 	MFD_CELL_OF("s2mu005-rgb", NULL, NULL, 0, 0, "samsung,s2mu005-rgb"),
 };
 
-static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic)
+static void sec_pmic_store_rev(struct sec_pmic_dev *sec_pmic)
 {
-	unsigned int val;
+	unsigned int reg, mask, shift;
 
 	/* For s2mpg1x, the revision is in a different regmap */
 	if (sec_pmic->device_type == S2MPG10)
 		return;
 
-	/* For each device type, the REG_ID is always the first register */
-	if (!regmap_read(sec_pmic->regmap_pmic, S2MPS11_REG_ID, &val))
-		dev_dbg(sec_pmic->dev, "Revision: 0x%x\n", val);
+	switch (sec_pmic->device_type) {
+	case S2MU005:
+		reg = S2MU005_REG_ID;
+		mask = S2MU005_ID_MASK;
+		shift = S2MU005_ID_SHIFT;
+		break;
+	default:
+		/* For other device types, the REG_ID is always the first register. */
+		reg = S2MPS11_REG_ID;
+		mask = ~0;
+		shift = 0;
+	}
+
+	if (!regmap_read(sec_pmic->regmap_pmic, reg, &sec_pmic->revision))
+		return;
+
+	sec_pmic->revision &= mask;
+	sec_pmic->revision >>= shift;
+
+	dev_dbg(sec_pmic->dev, "Revision: 0x%x\n", sec_pmic->revision);
 }
 
 static void sec_pmic_configure(struct sec_pmic_dev *sec_pmic)
@@ -236,7 +254,7 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
 		return ret;
 
 	sec_pmic_configure(sec_pmic);
-	sec_pmic_dump_rev(sec_pmic);
+	sec_pmic_store_rev(sec_pmic);
 
 	return ret;
 }
diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h
index fc07f7944dcd..ccd1bfa15b85 100644
--- a/include/linux/mfd/samsung/core.h
+++ b/include/linux/mfd/samsung/core.h
@@ -63,6 +63,7 @@ enum sec_device_type {
  * @irq_base:		Base IRQ number for device, required for IRQs
  * @irq:		Generic IRQ number for device
  * @irq_data:		Runtime data structure for IRQ controller
+ * @revision:		Revision number of the device
  * @wakeup:		Whether or not this is a wakeup device
  */
 struct sec_pmic_dev {
@@ -74,6 +75,8 @@ struct sec_pmic_dev {
 	int device_type;
 	int irq;
 	struct regmap_irq_chip_data *irq_data[MAX_IRQ_CHIPS];
+
+	unsigned int revision;
 };
 
 struct sec_platform_data {

-- 
2.51.2


^ permalink raw reply related

* [PATCH 07/13] mfd: sec: add support for S2MU005 PMIC
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty
In-Reply-To: <20251114-s2mu005-pmic-v1-0-9e3184d3a0c9@disroot.org>

Samsung's S2MU005 PMIC includes subdevices for a charger, an MUIC (Micro
USB Interface Controller), and flash and RGB LED controllers.

S2MU005's interrupt registers can be properly divided into three regmap
IRQ chips, one each for the charger, flash LEDs, and the MUIC.

Add initial support for S2MU005 in the PMIC driver, along with it's three
interrupt chips.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 drivers/mfd/sec-common.c            |  11 ++
 drivers/mfd/sec-i2c.c               |  29 ++++
 drivers/mfd/sec-irq.c               |  71 ++++++++
 include/linux/mfd/samsung/core.h    |   3 +-
 include/linux/mfd/samsung/irq.h     |  80 +++++++++
 include/linux/mfd/samsung/s2mu005.h | 328 ++++++++++++++++++++++++++++++++++++
 6 files changed, 521 insertions(+), 1 deletion(-)

diff --git a/drivers/mfd/sec-common.c b/drivers/mfd/sec-common.c
index 42d55e70e34c..4c5f4dc2905b 100644
--- a/drivers/mfd/sec-common.c
+++ b/drivers/mfd/sec-common.c
@@ -79,6 +79,13 @@ static const struct mfd_cell s2mpu05_devs[] = {
 	MFD_CELL_NAME("s2mps15-rtc"),
 };
 
+static const struct mfd_cell s2mu005_devs[] = {
+	MFD_CELL_OF("s2mu005-charger", NULL, NULL, 0, 0, "samsung,s2mu005-charger"),
+	MFD_CELL_OF("s2mu005-flash", NULL, NULL, 0, 0, "samsung,s2mu005-flash"),
+	MFD_CELL_OF("s2mu005-muic", NULL, NULL, 0, 0, "samsung,s2mu005-muic"),
+	MFD_CELL_OF("s2mu005-rgb", NULL, NULL, 0, 0, "samsung,s2mu005-rgb"),
+};
+
 static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic)
 {
 	unsigned int val;
@@ -214,6 +221,10 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
 		sec_devs = s2mpu05_devs;
 		num_sec_devs = ARRAY_SIZE(s2mpu05_devs);
 		break;
+	case S2MU005:
+		sec_devs = s2mu005_devs;
+		num_sec_devs = ARRAY_SIZE(s2mu005_devs);
+		break;
 	default:
 		return dev_err_probe(sec_pmic->dev, -EINVAL,
 				     "Unsupported device type %d\n",
diff --git a/drivers/mfd/sec-i2c.c b/drivers/mfd/sec-i2c.c
index 3132b849b4bc..bc5ad8b1c432 100644
--- a/drivers/mfd/sec-i2c.c
+++ b/drivers/mfd/sec-i2c.c
@@ -17,6 +17,7 @@
 #include <linux/mfd/samsung/s2mps14.h>
 #include <linux/mfd/samsung/s2mps15.h>
 #include <linux/mfd/samsung/s2mpu02.h>
+#include <linux/mfd/samsung/s2mu005.h>
 #include <linux/mfd/samsung/s5m8767.h>
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
@@ -66,6 +67,19 @@ static bool s2mpu02_volatile(struct device *dev, unsigned int reg)
 	}
 }
 
+static bool s2mu005_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case S2MU005_REG_CHGR_INT1M:
+	case S2MU005_REG_FLED_INT1M:
+	case S2MU005_REG_MUIC_INT1M:
+	case S2MU005_REG_MUIC_INT2M:
+		return false;
+	default:
+		return true;
+	}
+}
+
 static const struct regmap_config s2dos05_regmap_config = {
 	.reg_bits = 8,
 	.val_bits = 8,
@@ -130,6 +144,15 @@ static const struct regmap_config s2mpu05_regmap_config = {
 	.val_bits = 8,
 };
 
+static const struct regmap_config s2mu005_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = S2MU005_REG_MUIC_LDOADC_H,
+	.volatile_reg = s2mu005_volatile,
+	.cache_type = REGCACHE_FLAT,
+};
+
 static const struct regmap_config s5m8767_regmap_config = {
 	.reg_bits = 8,
 	.val_bits = 8,
@@ -203,6 +226,11 @@ static const struct sec_pmic_i2c_platform_data s2mpu05_data = {
 	.device_type = S2MPU05,
 };
 
+static const struct sec_pmic_i2c_platform_data s2mu005_data = {
+	.regmap_cfg = &s2mu005_regmap_config,
+	.device_type = S2MU005,
+};
+
 static const struct sec_pmic_i2c_platform_data s5m8767_data = {
 	.regmap_cfg = &s5m8767_regmap_config,
 	.device_type = S5M8767X,
@@ -217,6 +245,7 @@ static const struct of_device_id sec_pmic_i2c_of_match[] = {
 	{ .compatible = "samsung,s2mps15-pmic", .data = &s2mps15_data, },
 	{ .compatible = "samsung,s2mpu02-pmic", .data = &s2mpu02_data, },
 	{ .compatible = "samsung,s2mpu05-pmic", .data = &s2mpu05_data, },
+	{ .compatible = "samsung,s2mu005-pmic", .data = &s2mu005_data, },
 	{ .compatible = "samsung,s5m8767-pmic", .data = &s5m8767_data, },
 	{ },
 };
diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
index 053c28f31ec9..fc82893fe6b7 100644
--- a/drivers/mfd/sec-irq.c
+++ b/drivers/mfd/sec-irq.c
@@ -15,6 +15,7 @@
 #include <linux/mfd/samsung/s2mps14.h>
 #include <linux/mfd/samsung/s2mpu02.h>
 #include <linux/mfd/samsung/s2mpu05.h>
+#include <linux/mfd/samsung/s2mu005.h>
 #include <linux/mfd/samsung/s5m8767.h>
 #include <linux/regmap.h>
 #include "sec-core.h"
@@ -158,6 +159,42 @@ static const struct regmap_irq s2mpu05_irqs[] = {
 	REGMAP_IRQ_REG(S2MPU05_IRQ_TSD, 2, S2MPU05_IRQ_TSD_MASK),
 };
 
+static const struct regmap_irq s2mu005_chgr_irqs[] = {
+	REGMAP_IRQ_REG(S2MU005_CHGR_IRQ_DETBAT, 0, S2MU005_CHGR_IRQ_DETBAT_MASK),
+	REGMAP_IRQ_REG(S2MU005_CHGR_IRQ_BAT, 0, S2MU005_CHGR_IRQ_BAT_MASK),
+	REGMAP_IRQ_REG(S2MU005_CHGR_IRQ_IVR, 0, S2MU005_CHGR_IRQ_IVR_MASK),
+	REGMAP_IRQ_REG(S2MU005_CHGR_IRQ_EVENT, 0, S2MU005_CHGR_IRQ_EVENT_MASK),
+	REGMAP_IRQ_REG(S2MU005_CHGR_IRQ_CHG, 0, S2MU005_CHGR_IRQ_CHG_MASK),
+	REGMAP_IRQ_REG(S2MU005_CHGR_IRQ_VMID, 0, S2MU005_CHGR_IRQ_VMID_MASK),
+	REGMAP_IRQ_REG(S2MU005_CHGR_IRQ_WCIN, 0, S2MU005_CHGR_IRQ_WCIN_MASK),
+	REGMAP_IRQ_REG(S2MU005_CHGR_IRQ_VBUS, 0, S2MU005_CHGR_IRQ_VBUS_MASK),
+};
+
+static const struct regmap_irq s2mu005_fled_irqs[] = {
+	REGMAP_IRQ_REG(S2MU005_FLED_IRQ_LBPROT, 0, S2MU005_FLED_IRQ_LBPROT_MASK),
+	REGMAP_IRQ_REG(S2MU005_FLED_IRQ_OPENCH2, 0, S2MU005_FLED_IRQ_OPENCH2_MASK),
+	REGMAP_IRQ_REG(S2MU005_FLED_IRQ_OPENCH1, 0, S2MU005_FLED_IRQ_OPENCH1_MASK),
+	REGMAP_IRQ_REG(S2MU005_FLED_IRQ_SHORTCH2, 0, S2MU005_FLED_IRQ_SHORTCH2_MASK),
+	REGMAP_IRQ_REG(S2MU005_FLED_IRQ_SHORTCH1, 0, S2MU005_FLED_IRQ_SHORTCH1_MASK),
+};
+
+static const struct regmap_irq s2mu005_muic_irqs[] = {
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_ATTACH, 0, S2MU005_MUIC_IRQ_ATTACH_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_DETACH, 0, S2MU005_MUIC_IRQ_DETACH_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_KP, 0, S2MU005_MUIC_IRQ_KP_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_LKP, 0, S2MU005_MUIC_IRQ_LKP_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_LKR, 0, S2MU005_MUIC_IRQ_LKR_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_RIDCHG, 0, S2MU005_MUIC_IRQ_RIDCHG_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_VBUSON, 1, S2MU005_MUIC_IRQ_VBUSON_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_RSVD, 1, S2MU005_MUIC_IRQ_RSVD_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_ADC, 1, S2MU005_MUIC_IRQ_ADC_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_STUCK, 1, S2MU005_MUIC_IRQ_STUCK_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_STUCKRCV, 1, S2MU005_MUIC_IRQ_STUCKRCV_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_MHDL, 1, S2MU005_MUIC_IRQ_MHDL_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_AVCHG, 1, S2MU005_MUIC_IRQ_AVCHG_MASK),
+	REGMAP_IRQ_REG(S2MU005_MUIC_IRQ_VBUSOFF, 1, S2MU005_MUIC_IRQ_VBUSOFF_MASK),
+};
+
 static const struct regmap_irq s5m8767_irqs[] = {
 	REGMAP_IRQ_REG(S5M8767_IRQ_PWRR, 0, S5M8767_IRQ_PWRR_MASK),
 	REGMAP_IRQ_REG(S5M8767_IRQ_PWRF, 0, S5M8767_IRQ_PWRF_MASK),
@@ -259,6 +296,36 @@ static const struct regmap_irq_chip s2mpu05_irq_chip[] = {
 	},
 };
 
+static const struct regmap_irq_chip s2mu005_irq_chip[] = {
+	[S2MU005_CHGR_IRQ_CHIP] = {
+		.name = "s2mu005-chgr",
+		.irqs = s2mu005_chgr_irqs,
+		.num_irqs = ARRAY_SIZE(s2mu005_chgr_irqs),
+		.num_regs = 1,
+		.status_base = S2MU005_REG_CHGR_INT1,
+		.mask_base = S2MU005_REG_CHGR_INT1M,
+		.ack_base = S2MU005_REG_CHGR_INT1,
+	},
+	[S2MU005_FLED_IRQ_CHIP] = {
+		.name = "s2mu005-fled",
+		.irqs = s2mu005_fled_irqs,
+		.num_irqs = ARRAY_SIZE(s2mu005_fled_irqs),
+		.num_regs = 1,
+		.status_base = S2MU005_REG_FLED_INT1,
+		.mask_base = S2MU005_REG_FLED_INT1M,
+		.ack_base = S2MU005_REG_FLED_INT1,
+	},
+	[S2MU005_MUIC_IRQ_CHIP] = {
+		.name = "s2mu005-muic",
+		.irqs = s2mu005_muic_irqs,
+		.num_irqs = ARRAY_SIZE(s2mu005_muic_irqs),
+		.num_regs = 2,
+		.status_base = S2MU005_REG_MUIC_INT1,
+		.mask_base = S2MU005_REG_MUIC_INT1M,
+		.ack_base = S2MU005_REG_MUIC_INT1,
+	},
+};
+
 static const struct regmap_irq_chip s5m8767_irq_chip[] = {
 	[S5M8767_IRQ_CHIP] = {
 		.name = "s5m8767",
@@ -315,6 +382,10 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
 		sec_irq_chip = s2mpu05_irq_chip;
 		sec_irq_chip_num = ARRAY_SIZE(s2mpu05_irq_chip);
 		break;
+	case S2MU005:
+		sec_irq_chip = s2mu005_irq_chip;
+		sec_irq_chip_num = ARRAY_SIZE(s2mu005_irq_chip);
+		break;
 	default:
 		return dev_err_probe(sec_pmic->dev, -EINVAL,
 				     "Unsupported device type %d\n",
diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h
index dcd741c4f0d6..fc07f7944dcd 100644
--- a/include/linux/mfd/samsung/core.h
+++ b/include/linux/mfd/samsung/core.h
@@ -34,7 +34,7 @@
 #define STEP_6_25_MV		6250
 
 /* Maximum number of IRQ chips in a PMIC */
-#define MAX_IRQ_CHIPS		1
+#define MAX_IRQ_CHIPS		3
 
 struct gpio_desc;
 
@@ -49,6 +49,7 @@ enum sec_device_type {
 	S2MPS15X,
 	S2MPU02,
 	S2MPU05,
+	S2MU005,
 };
 
 /**
diff --git a/include/linux/mfd/samsung/irq.h b/include/linux/mfd/samsung/irq.h
index 78eb894e350e..b3a51b74aa59 100644
--- a/include/linux/mfd/samsung/irq.h
+++ b/include/linux/mfd/samsung/irq.h
@@ -309,6 +309,86 @@ enum s2mpu05_irq {
 #define S2MPU05_IRQ_INT140C_MASK	BIT(1)
 #define S2MPU05_IRQ_TSD_MASK		BIT(2)
 
+enum s2mu005_chgr_irq {
+	S2MU005_CHGR_IRQ_DETBAT,
+	S2MU005_CHGR_IRQ_BAT,
+	S2MU005_CHGR_IRQ_IVR,
+	S2MU005_CHGR_IRQ_EVENT,
+	S2MU005_CHGR_IRQ_CHG,
+	S2MU005_CHGR_IRQ_VMID,
+	S2MU005_CHGR_IRQ_WCIN,
+	S2MU005_CHGR_IRQ_VBUS,
+
+	S2MU005_CHGR_IRQ_NR,
+};
+
+#define S2MU005_CHGR_IRQ_CHIP		0
+
+#define S2MU005_CHGR_IRQ_DETBAT_MASK	BIT(0)
+#define S2MU005_CHGR_IRQ_BAT_MASK	BIT(1)
+#define S2MU005_CHGR_IRQ_IVR_MASK	BIT(2)
+#define S2MU005_CHGR_IRQ_EVENT_MASK	BIT(3)
+#define S2MU005_CHGR_IRQ_CHG_MASK	BIT(4)
+#define S2MU005_CHGR_IRQ_VMID_MASK	BIT(5)
+#define S2MU005_CHGR_IRQ_WCIN_MASK	BIT(6)
+#define S2MU005_CHGR_IRQ_VBUS_MASK	BIT(7)
+
+enum s2mu005_fled_irq {
+	S2MU005_FLED_IRQ_LBPROT,
+	S2MU005_FLED_IRQ_OPENCH2,
+	S2MU005_FLED_IRQ_OPENCH1,
+	S2MU005_FLED_IRQ_SHORTCH2,
+	S2MU005_FLED_IRQ_SHORTCH1,
+
+	S2MU005_FLED_IRQ_NR,
+};
+
+#define S2MU005_FLED_IRQ_CHIP			1
+
+#define S2MU005_FLED_IRQ_LBPROT_MASK		BIT(2)
+#define S2MU005_FLED_IRQ_OPENCH2_MASK		BIT(4)
+#define S2MU005_FLED_IRQ_OPENCH1_MASK		BIT(5)
+#define S2MU005_FLED_IRQ_SHORTCH2_MASK		BIT(6)
+#define S2MU005_FLED_IRQ_SHORTCH1_MASK		BIT(7)
+
+enum s2mu005_muic_irq {
+	S2MU005_MUIC_IRQ_ATTACH,
+	S2MU005_MUIC_IRQ_DETACH,
+	S2MU005_MUIC_IRQ_KP,
+	S2MU005_MUIC_IRQ_LKP,
+	S2MU005_MUIC_IRQ_LKR,
+	S2MU005_MUIC_IRQ_RIDCHG,
+
+	S2MU005_MUIC_IRQ_VBUSON,
+	S2MU005_MUIC_IRQ_RSVD,
+	S2MU005_MUIC_IRQ_ADC,
+	S2MU005_MUIC_IRQ_STUCK,
+	S2MU005_MUIC_IRQ_STUCKRCV,
+	S2MU005_MUIC_IRQ_MHDL,
+	S2MU005_MUIC_IRQ_AVCHG,
+	S2MU005_MUIC_IRQ_VBUSOFF,
+
+	S2MU005_IRQ_NR,
+};
+
+#define S2MU005_MUIC_IRQ_CHIP			2
+
+#define S2MU005_MUIC_IRQ_ATTACH_MASK		BIT(0)
+#define S2MU005_MUIC_IRQ_DETACH_MASK		BIT(1)
+#define S2MU005_MUIC_IRQ_KP_MASK		BIT(2)
+#define S2MU005_MUIC_IRQ_LKP_MASK		BIT(3)
+#define S2MU005_MUIC_IRQ_LKR_MASK		BIT(4)
+#define S2MU005_MUIC_IRQ_RIDCHG_MASK		BIT(5)
+
+#define S2MU005_MUIC_IRQ_VBUSON_MASK		BIT(0)
+#define S2MU005_MUIC_IRQ_RSVD_MASK		BIT(1)
+#define S2MU005_MUIC_IRQ_ADC_MASK		BIT(2)
+#define S2MU005_MUIC_IRQ_STUCK_MASK		BIT(3)
+#define S2MU005_MUIC_IRQ_STUCKRCV_MASK		BIT(4)
+#define S2MU005_MUIC_IRQ_MHDL_MASK		BIT(5)
+#define S2MU005_MUIC_IRQ_AVCHG_MASK		BIT(6)
+#define S2MU005_MUIC_IRQ_VBUSOFF_MASK		BIT(7)
+
 enum s5m8767_irq {
 	S5M8767_IRQ_PWRR,
 	S5M8767_IRQ_PWRF,
diff --git a/include/linux/mfd/samsung/s2mu005.h b/include/linux/mfd/samsung/s2mu005.h
new file mode 100644
index 000000000000..32ad35dda661
--- /dev/null
+++ b/include/linux/mfd/samsung/s2mu005.h
@@ -0,0 +1,328 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd
+ * Copyright (c) 2025 Kaustabh Chakraborty <kauschluss@disroot.org>
+ */
+
+#ifndef __LINUX_MFD_S2MU005_H
+#define __LINUX_MFD_S2MU005_H
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+
+/* S2MU005 registers */
+enum s2mu005_reg {
+	S2MU005_REG_CHGR_INT1,
+	S2MU005_REG_CHGR_INT1M,
+
+	S2MU005_REG_FLED_INT1,
+	S2MU005_REG_FLED_INT1M,
+
+	S2MU005_REG_MUIC_INT1,
+	S2MU005_REG_MUIC_INT2,
+	S2MU005_REG_MUIC_INT1M,
+	S2MU005_REG_MUIC_INT2M,
+
+	S2MU005_REG_CHGR_STATUS0,
+	S2MU005_REG_CHGR_STATUS1,
+	S2MU005_REG_CHGR_STATUS2,
+	S2MU005_REG_CHGR_STATUS3,
+	S2MU005_REG_CHGR_STATUS4,
+	S2MU005_REG_CHGR_STATUS5,
+	S2MU005_REG_CHGR_CTRL0,
+	S2MU005_REG_CHGR_CTRL1,
+	S2MU005_REG_CHGR_CTRL2,
+	S2MU005_REG_CHGR_CTRL3,
+	S2MU005_REG_CHGR_CTRL4,
+	S2MU005_REG_CHGR_CTRL5,
+	S2MU005_REG_CHGR_CTRL6,
+	S2MU005_REG_CHGR_CTRL7,
+	S2MU005_REG_CHGR_CTRL8,
+	S2MU005_REG_CHGR_CTRL9,
+	S2MU005_REG_CHGR_CTRL10,
+	S2MU005_REG_CHGR_CTRL11,
+	S2MU005_REG_CHGR_CTRL12,
+	S2MU005_REG_CHGR_CTRL13,
+	S2MU005_REG_CHGR_CTRL14,
+	S2MU005_REG_CHGR_CTRL15,
+	S2MU005_REG_CHGR_CTRL16,
+	S2MU005_REG_CHGR_CTRL17,
+	S2MU005_REG_CHGR_CTRL18,
+	S2MU005_REG_CHGR_CTRL19,
+	S2MU005_REG_CHGR_TEST0,
+	S2MU005_REG_CHGR_TEST1,
+	S2MU005_REG_CHGR_TEST2,
+	S2MU005_REG_CHGR_TEST3,
+	S2MU005_REG_CHGR_TEST4,
+	S2MU005_REG_CHGR_TEST5,
+	S2MU005_REG_CHGR_TEST6,
+	S2MU005_REG_CHGR_TEST7,
+	S2MU005_REG_CHGR_TEST8,
+	S2MU005_REG_CHGR_TEST9,
+	S2MU005_REG_CHGR_TEST10,
+
+	S2MU005_REG_FLED_STATUS,
+	S2MU005_REG_FLED_CH0_CTRL0,
+	S2MU005_REG_FLED_CH0_CTRL1,
+	S2MU005_REG_FLED_CH0_CTRL2,
+	S2MU005_REG_FLED_CH0_CTRL3,
+	S2MU005_REG_FLED_CH1_CTRL0,
+	S2MU005_REG_FLED_CH1_CTRL1,
+	S2MU005_REG_FLED_CH1_CTRL2,
+	S2MU005_REG_FLED_CH1_CTRL3,
+	S2MU005_REG_FLED_CTRL0,
+	S2MU005_REG_FLED_CTRL1,
+	S2MU005_REG_FLED_CTRL2,
+	S2MU005_REG_FLED_CTRL3,
+	S2MU005_REG_FLED_CTRL4,
+	S2MU005_REG_FLED_CTRL5,
+	S2MU005_REG_FLED_CTRL6,
+
+	S2MU005_REG_RGB_EN,
+	S2MU005_REG_RGB_CH0_CTRL,
+	S2MU005_REG_RGB_CH1_CTRL,
+	S2MU005_REG_RGB_CH2_CTRL,
+	S2MU005_REG_RGB_CH0_RAMP,
+	S2MU005_REG_RGB_CH0_STAY,
+	S2MU005_REG_RGB_CH1_RAMP,
+	S2MU005_REG_RGB_CH1_STAY,
+	S2MU005_REG_RGB_CH2_RAMP,
+	S2MU005_REG_RGB_CH2_STAY,
+	S2MU005_REG_RGB_TEST0,
+	S2MU005_REG_RGB_CTRL0,
+
+	S2MU005_REG_MUIC_ADC,
+	S2MU005_REG_MUIC_DEV1,
+	S2MU005_REG_MUIC_DEV2,
+	S2MU005_REG_MUIC_DEV3,
+	S2MU005_REG_MUIC_BUTTON1,
+	S2MU005_REG_MUIC_BUTTON2,
+	S2MU005_REG_MUIC_RESET,
+	S2MU005_REG_MUIC_CHGTYPE,
+	S2MU005_REG_MUIC_DEVAPPLE,
+	S2MU005_REG_MUIC_BCDRESCAN,
+	S2MU005_REG_MUIC_TEST1,
+	S2MU005_REG_MUIC_TEST2,
+	S2MU005_REG_MUIC_TEST3,
+
+	S2MU005_REG_ID = 0x73,
+
+	S2MU005_REG_MUIC_CTRL1 = 0xb2,
+	S2MU005_REG_MUIC_TIMERSET1,
+	S2MU005_REG_MUIC_TIMERSET2,
+	S2MU005_REG_MUIC_SWCTRL,
+	S2MU005_REG_MUIC_TIMERSET3,
+	S2MU005_REG_MUIC_CTRL2,
+	S2MU005_REG_MUIC_CTRL3,
+
+	S2MU005_REG_MUIC_LDOADC_L = 0xbf,
+	S2MU005_REG_MUIC_LDOADC_H,
+};
+
+#define S2MU005_REG_FLED_CH_CTRL0(x)	(S2MU005_REG_FLED_CH0_CTRL0 + 4 * (x))
+#define S2MU005_REG_FLED_CH_CTRL1(x)	(S2MU005_REG_FLED_CH0_CTRL1 + 4 * (x))
+#define S2MU005_REG_FLED_CH_CTRL2(x)	(S2MU005_REG_FLED_CH0_CTRL2 + 4 * (x))
+#define S2MU005_REG_FLED_CH_CTRL3(x)	(S2MU005_REG_FLED_CH0_CTRL3 + 4 * (x))
+
+#define S2MU005_REG_RGB_CH_CTRL(x)	(S2MU005_REG_RGB_CH0_CTRL + 1 * (x))
+#define S2MU005_REG_RGB_CH_RAMP(x)	(S2MU005_REG_RGB_CH0_RAMP + 2 * (x))
+#define S2MU005_REG_RGB_CH_STAY(x)	(S2MU005_REG_RGB_CH0_STAY + 2 * (x))
+
+/* S2MU005_REG_CHGR_STATUS0 */
+#define S2MU005_CHGR_VBUS		BIT(7)
+#define S2MU005_CHGR_WCIN		BIT(6)
+#define S2MU005_CHGR_VMID		BIT(5)
+#define S2MU005_CHGR_CHG		BIT(4)
+#define S2MU005_CHGR_STAT		GENMASK(3, 0)
+
+#define S2MU005_CHGR_STAT_DONE		FIELD_PREP(S2MU005_CHGR_STAT, 8)
+#define S2MU005_CHGR_STAT_TOPOFF	FIELD_PREP(S2MU005_CHGR_STAT, 7)
+#define S2MU005_CHGR_STAT_DONE_FLAG	FIELD_PREP(S2MU005_CHGR_STAT, 6)
+#define S2MU005_CHGR_STAT_CV		FIELD_PREP(S2MU005_CHGR_STAT, 5)
+#define S2MU005_CHGR_STAT_CC		FIELD_PREP(S2MU005_CHGR_STAT, 4)
+#define	S2MU005_CHGR_STAT_COOL_CHG	FIELD_PREP(S2MU005_CHGR_STAT, 3)
+#define S2MU005_CHGR_STAT_PRE_CHG	FIELD_PREP(S2MU005_CHGR_STAT, 2)
+
+/* S2MU005_REG_CHGR_STATUS1 */
+#define S2MU005_CHGR_DETBAT		BIT(7)
+#define S2MU005_CHGR_VBUSOVP		GENMASK(6, 4)
+
+#define S2MU005_CHGR_VBUS_OVP_OVERVOLT	FIELD_PREP(S2MU005_CHGR_OVP, 2)
+
+/* S2MU005_REG_CHGR_STATUS2 */
+#define S2MU005_CHGR_BAT		GENMASK(6, 4)
+
+#define	S2MU005_CHGR_BAT_VOLT_DET	FIELD_PREP(S2MU005_CHGR_BAT, 7)
+#define S2MU005_CHGR_BAT_FAST_CHG_DET	FIELD_PREP(S2MU005_CHGR_BAT, 6)
+#define	S2MU005_CHGR_BAT_COOL_CHG_DET	FIELD_PREP(S2MU005_CHGR_BAT, 5)
+#define S2MU005_CHGR_BAT_LOW_CHG	FIELD_PREP(S2MU005_CHGR_BAT, 2)
+#define S2MU005_CHGR_BAT_SELF_DISCHG	FIELD_PREP(S2MU005_CHGR_BAT, 1)
+#define S2MU005_CHGR_BAT_OVP_DET	FIELD_PREP(S2MU005_CHGR_BAT, 0)
+
+/* S2MU005_REG_CHGR_STATUS3 */
+#define S2MU005_CHGR_EVT		GENMASK(3, 0)
+
+#define S2MU005_CHGR_EVT_WDT_RST	FIELD_PREP(S2MU005_CHGR_EVT, 6)
+#define S2MU005_CHGR_EVT_WDT_SUSP	FIELD_PREP(S2MU005_CHGR_EVT, 5)
+#define S2MU005_CHGR_EVT_VSYS_VUVLO	FIELD_PREP(S2MU005_CHGR_EVT, 4)
+#define S2MU005_CHGR_EVT_VSYS_VOVP	FIELD_PREP(S2MU005_CHGR_EVT, 3)
+#define S2MU005_CHGR_EVT_THERM_FOLDBACK	FIELD_PREP(S2MU005_CHGR_EVT, 2)
+#define S2MU005_CHGR_EVT_THERM_SHUTDOWN	FIELD_PREP(S2MU005_CHGR_EVT, 1)
+
+/* S2MU005_REG_CHGR_CTRL0 */
+#define S2MU005_CHGR_CHG_EN		BIT(4)
+#define S2MU005_CHGR_OP_MODE		GENMASK(2, 0)
+
+#define S2MU005_CHGR_OP_MODE_OTG	FIELD_PREP(S2MU005_CHGR_OP_MODE, BIT(2))
+#define S2MU005_CHGR_OP_MODE_CHG	FIELD_PREP(S2MU005_CHGR_OP_MODE, BIT(1))
+
+/* S2MU005_REG_CHGR_CTRL1 */
+#define S2MU005_CHGR_VIN_DROP		GENMASK(6, 4)
+
+/* S2MU005_REG_CHGR_CTRL2 */
+#define S2MU005_CHGR_IN_CURR_LIM	GENMASK(5, 0)
+
+/* S2MU005_REG_CHGR_CTRL4 */
+#define S2MU005_CHGR_OTG_OCP_ON		BIT(5)
+#define S2MU005_CHGR_OTG_OCP_OFF	BIT(4)
+#define S2MU005_CHGR_OTG_OCP		GENMASK(3, 2)
+
+/* S2MU005_REG_CHGR_CTRL5 */
+#define S2MU005_CHGR_VMID_BOOST		GENMASK(4, 0)
+
+/* S2MU005_REG_CHGR_CTRL6 */
+#define S2MU005_CHGR_COOL_CHG_CURR	GENMASK(5, 0)
+
+/* S2MU005_REG_CHGR_CTRL7 */
+#define S2MU005_CHGR_FAST_CHG_CURR	GENMASK(5, 0)
+
+/* S2MU005_REG_CHGR_CTRL8 */
+#define S2MU005_CHGR_VF_VBAT		GENMASK(6, 1)
+
+/* S2MU005_REG_CHGR_CTRL10 */
+#define S2MU005_CHGR_TOPOFF_CURR(x)	(GENMASK(3, 0) << 4 * (x))
+
+/* S2MU005_REG_CHGR_CTRL11 */
+#define S2MU005_CHGR_OSC_BOOST		GENMASK(6, 5)
+#define S2MU005_CHGR_OSC_BUCK		GENMASK(4, 3)
+
+/* S2MU005_REG_CHGR_CTRL12 */
+#define S2MU005_CHGR_WDT		GENMASK(2, 0)
+
+#define S2MU005_CHGR_WDT_ON		FIELD_PREP(S2MU005_CHGR_WDT, BIT(2))
+#define S2MU005_CHGR_WDT_OFF		FIELD_PREP(S2MU005_CHGR_WDT, BIT(1))
+
+/* S2MU005_REG_CHGR_CTRL15 */
+#define S2MU005_CHGR_OTG_EN		GENMASK(3, 2)
+
+/* S2MU005_REG_FLED_STATUS */
+#define S2MU005_FLED_FLASH_STATUS(x)	(BIT(7) >> 2 * (x))
+#define S2MU005_FLED_TORCH_STATUS(x)	(BIT(6) >> 2 * (x))
+
+/* S2MU005_REG_FLED_CHx_CTRL0 */
+#define S2MU005_FLED_FLASH_IOUT		GENMASK(3, 0)
+
+/* S2MU005_REG_FLED_CHx_CTRL1 */
+#define S2MU005_FLED_TORCH_IOUT		GENMASK(3, 0)
+
+/* S2MU005_REG_FLED_CHx_CTRL2 */
+#define S2MU005_FLED_TORCH_TIMEOUT	GENMASK(3, 0)
+
+/* S2MU005_REG_FLED_CHx_CTRL3 */
+#define S2MU005_FLED_FLASH_TIMEOUT	GENMASK(3, 0)
+
+/* S2MU005_REG_FLED_CTRL1 */
+#define S2MU005_FLED_CH_EN		BIT(7)
+
+/*
+ * S2MU005_REG_FLED_CTRL4 - Rev. EVT0
+ * S2MU005_REG_FLED_CTRL6 - Rev. EVT1 and later
+ */
+#define S2MU005_FLED_FLASH_EN(x)	(GENMASK(7, 6) >> 4 * (x))
+#define S2MU005_FLED_TORCH_EN(x)	(GENMASK(5, 4) >> 4 * (x))
+
+/* S2MU005_REG_RGB_EN */
+#define S2MU005_RGB_RESET		BIT(6)
+#define S2MU005_RGB_SLOPE		GENMASK(5, 0)
+
+#define S2MU005_RGB_SLOPE_CONST		(BIT(4) | BIT(2) | BIT(0))
+#define S2MU005_RGB_SLOPE_SMOOTH	(BIT(5) | BIT(3) | BIT(1))
+
+/* S2MU005_REG_RGB_CHx_RAMP */
+#define S2MU005_RGB_CH_RAMP_UP		GENMASK(7, 4)
+#define S2MU005_RGB_CH_RAMP_DN		GENMASK(3, 0)
+
+/* S2MU005_REG_RGB_CHx_STAY */
+#define S2MU005_RGB_CH_STAY_HI		GENMASK(7, 4)
+#define S2MU005_RGB_CH_STAY_LO		GENMASK(3, 0)
+
+/* S2MU005_REG_MUIC_DEV1 */
+#define S2MU005_MUIC_OTG		BIT(7)
+#define S2MU005_MUIC_DCP		BIT(6)
+#define S2MU005_MUIC_CDP		BIT(5)
+#define S2MU005_MUIC_T1_T2_CHG		BIT(4)
+#define S2MU005_MUIC_UART		BIT(3)
+#define S2MU005_MUIC_SDP		BIT(2)
+#define S2MU005_MUIC_LANHUB		BIT(1)
+#define S2MU005_MUIC_AUDIO		BIT(0)
+
+/* S2MU005_REG_MUIC_DEV2 */
+#define S2MU005_MUIC_SDP_1P8S		BIT(7)
+#define S2MU005_MUIC_AV			BIT(6)
+#define S2MU005_MUIC_TTY		BIT(5)
+#define S2MU005_MUIC_PPD		BIT(4)
+#define S2MU005_MUIC_JIG_UART_OFF	BIT(3)
+#define S2MU005_MUIC_JIG_UART_ON	BIT(2)
+#define S2MU005_MUIC_JIG_USB_OFF	BIT(1)
+#define S2MU005_MUIC_JIG_USB_ON		BIT(0)
+
+/* S2MU005_REG_MUIC_DEV3 */
+#define S2MU005_MUIC_U200_CHG		BIT(7)
+#define S2MU005_MUIC_VBUS_AV		BIT(4)
+#define S2MU005_MUIC_VBUS_R255		BIT(1)
+#define S2MU005_MUIC_MHL		BIT(0)
+
+/* S2MU005_REG_MUIC_DEVAPPLE */
+#define S2MU005_MUIC_APPLE_CHG_0P5A	BIT(7)
+#define S2MU005_MUIC_APPLE_CHG_1P0A	BIT(6)
+#define S2MU005_MUIC_APPLE_CHG_2P0A	BIT(5)
+#define S2MU005_MUIC_APPLE_CHG_2P4A	BIT(4)
+#define S2MU005_MUIC_SDP_DCD_OUT	BIT(3)
+#define S2MU005_MUIC_RID_WAKEUP		BIT(2)
+#define S2MU005_MUIC_VBUS_WAKEUP	BIT(1)
+#define S2MU005_MUIC_BCV1P2_OR_OPEN	BIT(0)
+
+/* S2MU005_REG_ID */
+#define S2MU005_ID_MASK			GENMASK(3, 0)
+#define S2MU005_ID_SHIFT		0
+
+/* S2MU005_REG_MUIC_SWCTRL */
+#define S2MU005_MUIC_DM_DP		GENMASK(7, 2)
+#define S2MU005_MUIC_JIG		BIT(0)
+
+#define S2MU005_MUIC_DM_DP_UART		FIELD_PREP(S2MU005_MUIC_DM_DP, 0x12)
+#define S2MU005_MUIC_DM_DP_USB		FIELD_PREP(S2MU005_MUIC_DM_DP, 0x09)
+
+/* S2MU005_REG_MUIC_CTRL1 */
+#define S2MU005_MUIC_OPEN		BIT(4)
+#define S2MU005_MUIC_RAW_DATA		BIT(3)
+#define S2MU005_MUIC_MAN_SW		BIT(2)
+#define S2MU005_MUIC_WAIT		BIT(1)
+#define S2MU005_MUIC_IRQ		BIT(0)
+
+/* S2MU005_REG_MUIC_CTRL3 */
+#define S2MU005_MUIC_ONESHOT_ADC	BIT(2)
+
+/* S2MU005_REG_MUIC_LDOADC_L and S2MU005_REG_MUIC_LDOADC_H */
+#define S2MU005_MUIC_VSET		GENMASK(4, 0)
+
+#define S2MU005_MUIC_VSET_3P0V		FIELD_PREP(S2MU005_MUIC_VSET, 0x1f)
+#define S2MU005_MUIC_VSET_2P6V		FIELD_PREP(S2MU005_MUIC_VSET, 0x0e)
+#define S2MU005_MUIC_VSET_2P4V		FIELD_PREP(S2MU005_MUIC_VSET, 0x0c)
+#define S2MU005_MUIC_VSET_2P2V		FIELD_PREP(S2MU005_MUIC_VSET, 0x0a)
+#define S2MU005_MUIC_VSET_2P0V		FIELD_PREP(S2MU005_MUIC_VSET, 0x08)
+#define S2MU005_MUIC_VSET_1P5V		FIELD_PREP(S2MU005_MUIC_VSET, 0x03)
+#define S2MU005_MUIC_VSET_1P4V		FIELD_PREP(S2MU005_MUIC_VSET, 0x02)
+#define S2MU005_MUIC_VSET_1P2V		FIELD_PREP(S2MU005_MUIC_VSET, 0x00)
+
+#endif	/* __LINUX_MFD_S2MU005_H */

-- 
2.51.2


^ permalink raw reply related

* [PATCH 06/13] mfd: sec-irq: add support for creating multiple IRQ chips
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty
In-Reply-To: <20251114-s2mu005-pmic-v1-0-9e3184d3a0c9@disroot.org>

The current state of the driver only allows creating only one IRQ chip
per PMIC. On some PMICs, such as Samsung's S2MU005, there are multiple
interrupt blocks, for which the current implementation stands insufficient.

Add support for creating multiple IRQ chips for a PMIC. Every IRQ chip is
given it's own index, which is used by sub-device drivers to request IRQs.

A macro is defined which states the maximum number of chips supported.
It's set to 1 as currently, no PMIC requires more than one IRQ chip. The
value must be changed accordingly on adding new PMICs requiring multiple
IRQ chips.

Moreover, adjust the s5m RTC driver to initialize IRQs with the
appropriate IRQ chip index.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 drivers/mfd/sec-irq.c            | 163 +++++++++++++++++++++++----------------
 drivers/rtc/rtc-s5m.c            |  15 +++-
 include/linux/mfd/samsung/core.h |   5 +-
 include/linux/mfd/samsung/irq.h  |  14 ++++
 4 files changed, 127 insertions(+), 70 deletions(-)

diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
index c5c80b1ba104..053c28f31ec9 100644
--- a/drivers/mfd/sec-irq.c
+++ b/drivers/mfd/sec-irq.c
@@ -181,25 +181,31 @@ static const struct regmap_irq s5m8767_irqs[] = {
 };
 
 /* All S2MPG10 interrupt sources are read-only and don't require clearing */
-static const struct regmap_irq_chip s2mpg10_irq_chip = {
-	.name = "s2mpg10",
-	.irqs = s2mpg10_irqs,
-	.num_irqs = ARRAY_SIZE(s2mpg10_irqs),
-	.num_regs = 6,
-	.status_base = S2MPG10_PMIC_INT1,
-	.mask_base = S2MPG10_PMIC_INT1M,
+static const struct regmap_irq_chip s2mpg10_irq_chip[] = {
+	[S2MPG10_IRQ_CHIP] = {
+		.name = "s2mpg10",
+		.irqs = s2mpg10_irqs,
+		.num_irqs = ARRAY_SIZE(s2mpg10_irqs),
+		.num_regs = 6,
+		.status_base = S2MPG10_PMIC_INT1,
+		.mask_base = S2MPG10_PMIC_INT1M,
+	},
 };
 
-static const struct regmap_irq_chip s2mps11_irq_chip = {
-	.name = "s2mps11",
-	.irqs = s2mps11_irqs,
-	.num_irqs = ARRAY_SIZE(s2mps11_irqs),
-	.num_regs = 3,
-	.status_base = S2MPS11_REG_INT1,
-	.mask_base = S2MPS11_REG_INT1M,
-	.ack_base = S2MPS11_REG_INT1,
+static const struct regmap_irq_chip s2mps11_irq_chip[] = {
+	[S2MPS11_IRQ_CHIP] = {
+		.name = "s2mps11",
+		.irqs = s2mps11_irqs,
+		.num_irqs = ARRAY_SIZE(s2mps11_irqs),
+		.num_regs = 3,
+		.status_base = S2MPS11_REG_INT1,
+		.mask_base = S2MPS11_REG_INT1M,
+		.ack_base = S2MPS11_REG_INT1,
+	},
 };
 
+#define S2MPS1X_IRQ_CHIP		S2MPS14_IRQ_CHIP
+
 #define S2MPS1X_IRQ_CHIP_COMMON_DATA		\
 	.irqs = s2mps14_irqs,			\
 	.num_irqs = ARRAY_SIZE(s2mps14_irqs),	\
@@ -208,85 +214,106 @@ static const struct regmap_irq_chip s2mps11_irq_chip = {
 	.mask_base = S2MPS14_REG_INT1M,		\
 	.ack_base = S2MPS14_REG_INT1		\
 
-static const struct regmap_irq_chip s2mps13_irq_chip = {
-	.name = "s2mps13",
-	S2MPS1X_IRQ_CHIP_COMMON_DATA,
+static const struct regmap_irq_chip s2mps13_irq_chip[] = {
+	[S2MPS1X_IRQ_CHIP] = {
+		.name = "s2mps13",
+		S2MPS1X_IRQ_CHIP_COMMON_DATA,
+	},
 };
 
-static const struct regmap_irq_chip s2mps14_irq_chip = {
-	.name = "s2mps14",
-	S2MPS1X_IRQ_CHIP_COMMON_DATA,
+static const struct regmap_irq_chip s2mps14_irq_chip[] = {
+	[S2MPS1X_IRQ_CHIP] = {
+		.name = "s2mps14",
+		S2MPS1X_IRQ_CHIP_COMMON_DATA,
+	},
 };
 
-static const struct regmap_irq_chip s2mps15_irq_chip = {
-	.name = "s2mps15",
-	S2MPS1X_IRQ_CHIP_COMMON_DATA,
+static const struct regmap_irq_chip s2mps15_irq_chip[] = {
+	[S2MPS1X_IRQ_CHIP] = {
+		.name = "s2mps15",
+		S2MPS1X_IRQ_CHIP_COMMON_DATA,
+	},
 };
 
-static const struct regmap_irq_chip s2mpu02_irq_chip = {
-	.name = "s2mpu02",
-	.irqs = s2mpu02_irqs,
-	.num_irqs = ARRAY_SIZE(s2mpu02_irqs),
-	.num_regs = 3,
-	.status_base = S2MPU02_REG_INT1,
-	.mask_base = S2MPU02_REG_INT1M,
-	.ack_base = S2MPU02_REG_INT1,
+static const struct regmap_irq_chip s2mpu02_irq_chip[] = {
+	[S2MPU02_IRQ_CHIP] = {
+		.name = "s2mpu02",
+		.irqs = s2mpu02_irqs,
+		.num_irqs = ARRAY_SIZE(s2mpu02_irqs),
+		.num_regs = 3,
+		.status_base = S2MPU02_REG_INT1,
+		.mask_base = S2MPU02_REG_INT1M,
+		.ack_base = S2MPU02_REG_INT1,
+	},
 };
 
-static const struct regmap_irq_chip s2mpu05_irq_chip = {
-	.name = "s2mpu05",
-	.irqs = s2mpu05_irqs,
-	.num_irqs = ARRAY_SIZE(s2mpu05_irqs),
-	.num_regs = 3,
-	.status_base = S2MPU05_REG_INT1,
-	.mask_base = S2MPU05_REG_INT1M,
-	.ack_base = S2MPU05_REG_INT1,
+static const struct regmap_irq_chip s2mpu05_irq_chip[] = {
+	[S2MPU05_IRQ_CHIP] = {
+		.name = "s2mpu05",
+		.irqs = s2mpu05_irqs,
+		.num_irqs = ARRAY_SIZE(s2mpu05_irqs),
+		.num_regs = 3,
+		.status_base = S2MPU05_REG_INT1,
+		.mask_base = S2MPU05_REG_INT1M,
+		.ack_base = S2MPU05_REG_INT1,
+	},
 };
 
-static const struct regmap_irq_chip s5m8767_irq_chip = {
-	.name = "s5m8767",
-	.irqs = s5m8767_irqs,
-	.num_irqs = ARRAY_SIZE(s5m8767_irqs),
-	.num_regs = 3,
-	.status_base = S5M8767_REG_INT1,
-	.mask_base = S5M8767_REG_INT1M,
-	.ack_base = S5M8767_REG_INT1,
+static const struct regmap_irq_chip s5m8767_irq_chip[] = {
+	[S5M8767_IRQ_CHIP] = {
+		.name = "s5m8767",
+		.irqs = s5m8767_irqs,
+		.num_irqs = ARRAY_SIZE(s5m8767_irqs),
+		.num_regs = 3,
+		.status_base = S5M8767_REG_INT1,
+		.mask_base = S5M8767_REG_INT1M,
+		.ack_base = S5M8767_REG_INT1,
+	},
 };
 
 int sec_irq_init(struct sec_pmic_dev *sec_pmic)
 {
 	const struct regmap_irq_chip *sec_irq_chip;
-	int ret;
+	int sec_irq_chip_num, i, ret;
 
 	switch (sec_pmic->device_type) {
 	case S5M8767X:
-		sec_irq_chip = &s5m8767_irq_chip;
+		sec_irq_chip = s5m8767_irq_chip;
+		sec_irq_chip_num = ARRAY_SIZE(s5m8767_irq_chip);
 		break;
 	case S2DOS05:
 		return 0;
 	case S2MPA01:
-		sec_irq_chip = &s2mps14_irq_chip;
+		sec_irq_chip = s2mps14_irq_chip;
+		sec_irq_chip_num = ARRAY_SIZE(s2mps14_irq_chip);
 		break;
 	case S2MPG10:
-		sec_irq_chip = &s2mpg10_irq_chip;
+		sec_irq_chip = s2mpg10_irq_chip;
+		sec_irq_chip_num = ARRAY_SIZE(s2mpg10_irq_chip);
 		break;
 	case S2MPS11X:
-		sec_irq_chip = &s2mps11_irq_chip;
+		sec_irq_chip = s2mps11_irq_chip;
+		sec_irq_chip_num = ARRAY_SIZE(s2mps11_irq_chip);
 		break;
 	case S2MPS13X:
-		sec_irq_chip = &s2mps13_irq_chip;
+		sec_irq_chip = s2mps13_irq_chip;
+		sec_irq_chip_num = ARRAY_SIZE(s2mps13_irq_chip);
 		break;
 	case S2MPS14X:
-		sec_irq_chip = &s2mps14_irq_chip;
+		sec_irq_chip = s2mps14_irq_chip;
+		sec_irq_chip_num = ARRAY_SIZE(s2mps14_irq_chip);
 		break;
 	case S2MPS15X:
-		sec_irq_chip = &s2mps15_irq_chip;
+		sec_irq_chip = s2mps15_irq_chip;
+		sec_irq_chip_num = ARRAY_SIZE(s2mps15_irq_chip);
 		break;
 	case S2MPU02:
-		sec_irq_chip = &s2mpu02_irq_chip;
+		sec_irq_chip = s2mpu02_irq_chip;
+		sec_irq_chip_num = ARRAY_SIZE(s2mpu02_irq_chip);
 		break;
 	case S2MPU05:
-		sec_irq_chip = &s2mpu05_irq_chip;
+		sec_irq_chip = s2mpu05_irq_chip;
+		sec_irq_chip_num = ARRAY_SIZE(s2mpu05_irq_chip);
 		break;
 	default:
 		return dev_err_probe(sec_pmic->dev, -EINVAL,
@@ -300,13 +327,19 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
 		return 0;
 	}
 
-	ret = devm_regmap_add_irq_chip(sec_pmic->dev, sec_pmic->regmap_pmic,
-				       sec_pmic->irq, IRQF_ONESHOT,
-				       0, sec_irq_chip, &sec_pmic->irq_data);
-	if (ret)
-		return dev_err_probe(sec_pmic->dev, ret,
-				     "Failed to add %s IRQ chip\n",
-				     sec_irq_chip->name);
+	for (i = 0; i < sec_irq_chip_num; i++) {
+		ret = devm_regmap_add_irq_chip(sec_pmic->dev,
+					       sec_pmic->regmap_pmic,
+					       sec_pmic->irq,
+					       IRQF_ONESHOT | IRQF_SHARED, 0,
+					       sec_irq_chip + i,
+					       sec_pmic->irq_data + i);
+		if (ret) {
+			return dev_err_probe(sec_pmic->dev, ret,
+					     "Failed to add %s IRQ chip\n",
+					     sec_irq_chip->name);
+		}
+	}
 
 	/*
 	 * The rtc-s5m driver requests S2MPS14_IRQ_RTCA0 also for S2MPS11
diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c
index a7220b4d0e8d..726915deff7a 100644
--- a/drivers/rtc/rtc-s5m.c
+++ b/drivers/rtc/rtc-s5m.c
@@ -668,7 +668,7 @@ static int s5m_rtc_probe(struct platform_device *pdev)
 	enum sec_device_type device_type =
 		platform_get_device_id(pdev)->driver_data;
 	struct s5m_rtc_info *info;
-	int ret, alarm_irq;
+	int ret, alarm_irq, irq_chip;
 
 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 	if (!info)
@@ -684,21 +684,25 @@ static int s5m_rtc_probe(struct platform_device *pdev)
 			regmap_cfg = &s2mps14_rtc_regmap_config;
 			info->regs = &s2mps15_rtc_regs;
 			alarm_irq = S2MPS14_IRQ_RTCA0;
+			irq_chip = S2MPS11_IRQ_CHIP;
 			break;
 		case S2MPS14X:
 			regmap_cfg = &s2mps14_rtc_regmap_config;
 			info->regs = &s2mps14_rtc_regs;
 			alarm_irq = S2MPS14_IRQ_RTCA0;
+			irq_chip = S2MPS14_IRQ_CHIP;
 			break;
 		case S2MPS13X:
 			regmap_cfg = &s2mps14_rtc_regmap_config;
 			info->regs = &s2mps13_rtc_regs;
 			alarm_irq = S2MPS14_IRQ_RTCA0;
+			irq_chip = S2MPS14_IRQ_CHIP;
 			break;
 		case S5M8767X:
 			regmap_cfg = &s5m_rtc_regmap_config;
 			info->regs = &s5m_rtc_regs;
 			alarm_irq = S5M8767_IRQ_RTCA1;
+			irq_chip = S5M8767_IRQ_CHIP;
 			break;
 		default:
 			return dev_err_probe(&pdev->dev, -ENODEV,
@@ -720,6 +724,7 @@ static int s5m_rtc_probe(struct platform_device *pdev)
 	} else if (device_type == S2MPG10) {
 		info->regs = &s2mpg10_rtc_regs;
 		alarm_irq = S2MPG10_IRQ_RTCA0;
+		irq_chip = S2MPG10_IRQ_CHIP;
 	} else {
 		return dev_err_probe(&pdev->dev, -ENODEV,
 				     "Unsupported device type %d\n",
@@ -730,12 +735,14 @@ static int s5m_rtc_probe(struct platform_device *pdev)
 	info->s5m87xx = s5m87xx;
 	info->device_type = device_type;
 
-	if (s5m87xx->irq_data) {
-		info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq);
-		if (info->irq <= 0)
+	if (s5m87xx->irq_data[irq_chip]) {
+		info->irq = regmap_irq_get_virq(s5m87xx->irq_data[irq_chip],
+						alarm_irq);
+		if (info->irq <= 0) {
 			return dev_err_probe(&pdev->dev, -EINVAL,
 					     "Failed to get virtual IRQ %d\n",
 					     alarm_irq);
+		}
 	}
 
 	platform_set_drvdata(pdev, info);
diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h
index d785e101fe79..dcd741c4f0d6 100644
--- a/include/linux/mfd/samsung/core.h
+++ b/include/linux/mfd/samsung/core.h
@@ -33,6 +33,9 @@
 #define STEP_12_5_MV		12500
 #define STEP_6_25_MV		6250
 
+/* Maximum number of IRQ chips in a PMIC */
+#define MAX_IRQ_CHIPS		1
+
 struct gpio_desc;
 
 enum sec_device_type {
@@ -69,7 +72,7 @@ struct sec_pmic_dev {
 
 	int device_type;
 	int irq;
-	struct regmap_irq_chip_data *irq_data;
+	struct regmap_irq_chip_data *irq_data[MAX_IRQ_CHIPS];
 };
 
 struct sec_platform_data {
diff --git a/include/linux/mfd/samsung/irq.h b/include/linux/mfd/samsung/irq.h
index b4805cbd949b..78eb894e350e 100644
--- a/include/linux/mfd/samsung/irq.h
+++ b/include/linux/mfd/samsung/irq.h
@@ -34,6 +34,8 @@ enum s2mpa01_irq {
 	S2MPA01_IRQ_NR,
 };
 
+#define S2MPA01_IRQ_CHIP		0
+
 #define S2MPA01_IRQ_PWRONF_MASK		(1 << 0)
 #define S2MPA01_IRQ_PWRONR_MASK		(1 << 1)
 #define S2MPA01_IRQ_JIGONBF_MASK	(1 << 2)
@@ -58,6 +60,8 @@ enum s2mpa01_irq {
 #define S2MPA01_IRQ_B35_TSD_MASK	(1 << 5)
 
 enum s2mpg10_irq {
+#define S2MPG10_IRQ_CHIP		0
+
 	/* PMIC */
 	S2MPG10_IRQ_PWRONF,
 	S2MPG10_IRQ_PWRONR,
@@ -183,6 +187,8 @@ enum s2mps11_irq {
 	S2MPS11_IRQ_NR,
 };
 
+#define S2MPS11_IRQ_CHIP		0
+
 #define S2MPS11_IRQ_PWRONF_MASK		(1 << 0)
 #define S2MPS11_IRQ_PWRONR_MASK		(1 << 1)
 #define S2MPS11_IRQ_JIGONBF_MASK	(1 << 2)
@@ -226,6 +232,8 @@ enum s2mps14_irq {
 	S2MPS14_IRQ_NR,
 };
 
+#define S2MPS14_IRQ_CHIP		0
+
 enum s2mpu02_irq {
 	S2MPU02_IRQ_PWRONF,
 	S2MPU02_IRQ_PWRONR,
@@ -250,6 +258,8 @@ enum s2mpu02_irq {
 	S2MPU02_IRQ_NR,
 };
 
+#define S2MPU02_IRQ_CHIP		0
+
 /* Masks for interrupts are the same as in s2mps11 */
 #define S2MPS14_IRQ_TSD_MASK		(1 << 2)
 
@@ -277,6 +287,8 @@ enum s2mpu05_irq {
 	S2MPU05_IRQ_NR,
 };
 
+#define S2MPU05_IRQ_CHIP		0
+
 #define S2MPU05_IRQ_PWRONF_MASK		BIT(0)
 #define S2MPU05_IRQ_PWRONR_MASK		BIT(1)
 #define S2MPU05_IRQ_JIGONBF_MASK	BIT(2)
@@ -321,6 +333,8 @@ enum s5m8767_irq {
 	S5M8767_IRQ_NR,
 };
 
+#define S5M8767_IRQ_CHIP		0
+
 #define S5M8767_IRQ_PWRR_MASK		(1 << 0)
 #define S5M8767_IRQ_PWRF_MASK		(1 << 1)
 #define S5M8767_IRQ_PWR1S_MASK		(1 << 3)

-- 
2.51.2


^ permalink raw reply related

* [PATCH 05/13] dt-bindings: mfd: s2mps11: add documentation for S2MU005 PMIC
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty
In-Reply-To: <20251114-s2mu005-pmic-v1-0-9e3184d3a0c9@disroot.org>

Samsung's S2MU005 PMIC includes subdevices for a charger, an MUIC (Micro
USB Interface Controller), and flash and RGB LED controllers.

Since regulators are not supported by this device, unmark this property
as required and instead set this in a per-device basis for ones which
need it.

Add the compatible and documentation for the S2MU005 PMIC. Also, add an
example for nodes for supported sub-devices, i.e. charger, extcon,
flash, and rgb.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 .../devicetree/bindings/mfd/samsung,s2mps11.yaml   | 103 ++++++++++++++++++++-
 1 file changed, 102 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml b/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml
index 31d544a9c05c..aef634ca2e36 100644
--- a/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml
+++ b/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml
@@ -27,12 +27,28 @@ properties:
       - samsung,s2mps15-pmic
       - samsung,s2mpu02-pmic
       - samsung,s2mpu05-pmic
+      - samsung,s2mu005-pmic
 
   clocks:
     $ref: /schemas/clock/samsung,s2mps11.yaml
     description:
       Child node describing clock provider.
 
+  charger:
+    $ref: /schemas/power/supply/samsung,s2m-charger.yaml
+    description:
+      Child node describing battery charger device.
+
+  extcon:
+    $ref: /schemas/extcon/samsung,s2m-muic.yaml
+    description:
+      Child node describing extcon device.
+
+  flash:
+    $ref: /schemas/leds/samsung,s2m-flash.yaml
+    description:
+      Child node describing flash LEDs.
+
   interrupts:
     maxItems: 1
 
@@ -44,6 +60,11 @@ properties:
     description:
       List of child nodes that specify the regulators.
 
+  rgb:
+    $ref: /schemas/leds/samsung,s2m-rgb.yaml
+    description:
+      Child node describing RGB LEDs.
+
   samsung,s2mps11-acokb-ground:
     description: |
       Indicates that ACOKB pin of S2MPS11 PMIC is connected to the ground so
@@ -65,7 +86,6 @@ properties:
 
 required:
   - compatible
-  - regulators
 
 additionalProperties: false
 
@@ -105,6 +125,8 @@ allOf:
         regulators:
           $ref: /schemas/regulator/samsung,s2mps11.yaml
         samsung,s2mps11-wrstbi-ground: false
+      required:
+        - regulators
 
   - if:
       properties:
@@ -116,6 +138,8 @@ allOf:
         regulators:
           $ref: /schemas/regulator/samsung,s2mps13.yaml
         samsung,s2mps11-acokb-ground: false
+      required:
+        - regulators
 
   - if:
       properties:
@@ -128,6 +152,8 @@ allOf:
           $ref: /schemas/regulator/samsung,s2mps14.yaml
         samsung,s2mps11-acokb-ground: false
         samsung,s2mps11-wrstbi-ground: false
+      required:
+        - regulators
 
   - if:
       properties:
@@ -140,6 +166,8 @@ allOf:
           $ref: /schemas/regulator/samsung,s2mps15.yaml
         samsung,s2mps11-acokb-ground: false
         samsung,s2mps11-wrstbi-ground: false
+      required:
+        - regulators
 
   - if:
       properties:
@@ -152,6 +180,8 @@ allOf:
           $ref: /schemas/regulator/samsung,s2mpu02.yaml
         samsung,s2mps11-acokb-ground: false
         samsung,s2mps11-wrstbi-ground: false
+      required:
+        - regulators
 
   - if:
       properties:
@@ -164,6 +194,18 @@ allOf:
           $ref: /schemas/regulator/samsung,s2mpu05.yaml
         samsung,s2mps11-acokb-ground: false
         samsung,s2mps11-wrstbi-ground: false
+      required:
+        - regulators
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: samsung,s2mu005-pmic
+    then:
+      properties:
+        samsung,s2mps11-acokb-ground: false
+        samsung,s2mps11-wrstbi-ground: false
 
 examples:
   - |
@@ -305,3 +347,62 @@ examples:
             };
         };
     };
+
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/leds/common.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        pmic@3d {
+            compatible = "samsung,s2mu005-pmic";
+            reg = <0x3d>;
+            interrupt-parent = <&gpa2>;
+            interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
+
+            charger {
+                compatible = "samsung,s2mu005-charger";
+                monitored-battery = <&battery>;
+            };
+
+            extcon {
+                compatible = "samsung,s2mu005-muic";
+
+                port {
+                    muic_to_usb: endpoint {
+                        remote-endpoint = <&usb_to_muic>;
+                    };
+                };
+            };
+
+            flash {
+                compatible = "samsung,s2mu005-flash";
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                led@0 {
+                    reg = <0>;
+                    label = "back-cam:white:flash";
+                    color = <LED_COLOR_ID_WHITE>;
+                    function = LED_FUNCTION_FLASH;
+                };
+
+                led@1 {
+                    reg = <1>;
+                    label = "front-cam:white:flash";
+                    color = <LED_COLOR_ID_WHITE>;
+                    function = LED_FUNCTION_FLASH;
+                };
+            };
+
+            rgb {
+                compatible = "samsung,s2mu005-rgb";
+                label = "notification:rgb:indicator";
+                color = <LED_COLOR_ID_RGB>;
+                function = LED_FUNCTION_INDICATOR;
+                linux,default-trigger = "pattern";
+            };
+        };
+    };

-- 
2.51.2


^ permalink raw reply related

* [PATCH 04/13] dt-bindings: power: supply: document Samsung S2M series PMIC charger device
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty
In-Reply-To: <20251114-s2mu005-pmic-v1-0-9e3184d3a0c9@disroot.org>

Certain Samsung S2M series PMICs have a battery charger device which,
among other things, manages power interfacing of the USB port. It may
supply power, as done in USB OTG operation mode, or it may accept power
and redirect it to the battery fuelgauge for charging.

This driver depends on the MUIC device present in the same PMIC block.

The initial driver introduced has support for S2MU005, add its
compatible as well.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 .../power/supply/samsung,s2mu005-charger.yaml      | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/Documentation/devicetree/bindings/power/supply/samsung,s2mu005-charger.yaml b/Documentation/devicetree/bindings/power/supply/samsung,s2mu005-charger.yaml
new file mode 100644
index 000000000000..80292d6e2562
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/samsung,s2mu005-charger.yaml
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/supply/samsung,s2mu005-charger.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Battery Charger Driver for Samsung S2M series PMICs
+
+maintainers:
+  - Kaustabh Chakraborty <kauschluss@disroot.org>
+
+description: |
+  The Samsung S2M series PMIC battery charger manages power interfacing
+  of the USB port. It may supply power, as done in USB OTG operation
+  mode, or it may accept power and redirect it to the battery fuelgauge
+  for charging.
+
+  This is a part of device tree bindings for S2M and S5M family of Power
+  Management IC (PMIC).
+
+  See also Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml for
+  additional information and example.
+
+allOf:
+  - $ref: power-supply.yaml#
+
+properties:
+  compatible:
+    enum:
+      - samsung,s2mu005-charger
+
+required:
+  - compatible
+
+unevaluatedProperties: false

-- 
2.51.2


^ permalink raw reply related

* [PATCH 03/13] dt-bindings: extcon: document Samsung S2M series PMIC extcon device
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty
In-Reply-To: <20251114-s2mu005-pmic-v1-0-9e3184d3a0c9@disroot.org>

Certain Samsung S2M series PMICs have a MUIC device which reports
various cable states by measuring the ID-GND resistance with an internal
ADC. Document the devicetree schema for this device.

The initial driver introduced has support for S2MU005, add its
compatible as well.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 .../bindings/extcon/samsung,s2mu005-muic.yaml      | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/Documentation/devicetree/bindings/extcon/samsung,s2mu005-muic.yaml b/Documentation/devicetree/bindings/extcon/samsung,s2mu005-muic.yaml
new file mode 100644
index 000000000000..8511bb96b47a
--- /dev/null
+++ b/Documentation/devicetree/bindings/extcon/samsung,s2mu005-muic.yaml
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/extcon/samsung,s2mu005-muic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Extcon Driver for Samsung S2M series PMICs
+
+maintainers:
+  - Kaustabh Chakraborty <kauschluss@disroot.org>
+
+description: |
+  The Samsung S2M series PMIC extcon device is a USB port accessory
+  detector. It reports multiple states depending on the ID-GND
+  resistance measured by an internal ADC.
+
+  This is a part of device tree bindings for S2M and S5M family of Power
+  Management IC (PMIC).
+
+  See also Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml for
+  additional information and example.
+
+properties:
+  compatible:
+    enum:
+      - samsung,s2mu005-muic
+
+  port:
+    $ref: /schemas/graph.yaml#/properties/port
+
+required:
+  - compatible
+  - port
+
+additionalProperties: false

-- 
2.51.2


^ permalink raw reply related

* [PATCH 02/13] dt-bindings: leds: document Samsung S2M series PMIC RGB LED device
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty
In-Reply-To: <20251114-s2mu005-pmic-v1-0-9e3184d3a0c9@disroot.org>

Certain Samsung S2M series PMICs have a three-channel LED device with
independent brightness control for each channel, typically used as
status indicators in mobile phones. Document the devicetree schema from
this driver.

The initial driver introduced has support for S2MU005, add its
compatible as well.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 .../bindings/leds/samsung,s2mu005-rgb.yaml         | 34 ++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/Documentation/devicetree/bindings/leds/samsung,s2mu005-rgb.yaml b/Documentation/devicetree/bindings/leds/samsung,s2mu005-rgb.yaml
new file mode 100644
index 000000000000..bad7080ff8f5
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/samsung,s2mu005-rgb.yaml
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/samsung,s2mu005-rgb.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RGB LED Driver for Samsung S2M series PMICs
+
+maintainers:
+  - Kaustabh Chakraborty <kauschluss@disroot.org>
+
+description: |
+  The Samsung S2M series PMIC RGB LED is a three-channel LED device with
+  8-bit brightness control for each channel, typically used as status
+  indicators in mobile phones.
+
+  This is a part of device tree bindings for S2M and S5M family of Power
+  Management IC (PMIC).
+
+  See also Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml for
+  additional information and example.
+
+allOf:
+  - $ref: common.yaml#
+
+properties:
+  compatible:
+    enum:
+      - samsung,s2mu005-rgb
+
+required:
+  - compatible
+
+unevaluatedProperties: false

-- 
2.51.2


^ permalink raw reply related

* [PATCH 01/13] dt-bindings: leds: document Samsung S2M series PMIC flash LED device
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty
In-Reply-To: <20251114-s2mu005-pmic-v1-0-9e3184d3a0c9@disroot.org>

Certain Samsung S2M series PMICs have a flash LED controller with
two LED channels, and with torch and flash control modes. Document the
devicetree schema for the device.

The initial driver introduced has support for S2MU005, add its
compatible as well.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 .../bindings/leds/samsung,s2mu005-flash.yaml       | 52 ++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/Documentation/devicetree/bindings/leds/samsung,s2mu005-flash.yaml b/Documentation/devicetree/bindings/leds/samsung,s2mu005-flash.yaml
new file mode 100644
index 000000000000..496385003a03
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/samsung,s2mu005-flash.yaml
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/samsung,s2mu005-flash.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Flash and Torch LED Driver for Samsung S2M series PMICs
+
+maintainers:
+  - Kaustabh Chakraborty <kauschluss@disroot.org>
+
+description: |
+  The Samsung S2M series PMIC flash LED has two led channels (typically
+  as back and front camera flashes), with support for both torch and
+  flash modes.
+
+  This is a part of device tree bindings for S2M and S5M family of Power
+  Management IC (PMIC).
+
+  See also Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml for
+  additional information and example.
+
+properties:
+  compatible:
+    enum:
+      - samsung,s2mu005-flash
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+patternProperties:
+  "^led@[0-1]$":
+    type: object
+    $ref: common.yaml#
+    unevaluatedProperties: false
+
+    properties:
+      reg:
+        enum: [0, 1]
+
+    required:
+      - reg
+
+required:
+  - compatible
+  - "#address-cells"
+  - "#size-cells"
+
+additionalProperties: false

-- 
2.51.2


^ permalink raw reply related

* [PATCH 00/13] Support for Samsung S2MU005 PMIC and its sub-devices
From: Kaustabh Chakraborty @ 2025-11-13 19:05 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
	Krzysztof Kozlowski, André Draszik, Alexandre Belloni,
	Jonathan Corbet
  Cc: linux-leds, devicetree, linux-kernel, linux-pm, linux-samsung-soc,
	linux-rtc, linux-doc, Kaustabh Chakraborty

S2MU005 is an MFD chip manufactured by Samsung Electronics. This is
found in various devices manufactured by Samsung and others, including
all Exynos 7870 devices. It is known to have the following features:

1. Two LED channels with adjustable brightness for use as a torch, or a
   flash strobe.
2. An RGB LED with 8-bit channels. Usually programmed as a notification
   indicator.
3. An MUIC, which works with USB micro-B (and USB-C?). For the micro-B
   variant though, it measures the ID-GND resistance using an internal
   ADC.
4. A charger device, which reports if charger is online, voltage,
   resistance, etc.

This patch series implements a lot of these features. Naturally, this
series touches upon a lot of subsystems. The 'parent' is the MFD driver,
so the subsystems have some form of dependency to the MFD driver, so
they are not separable.

Here are the subsystems corresponding to the patch numbers:
dt-bindings - 01, 02, 03, 04, 05
mfd         - 05, 06, 07, 08
rtc         - 06
led         - 01, 02, 09, 10, 11
extcon      - 03, 12
power       - 04, 13

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
Kaustabh Chakraborty (13):
      dt-bindings: leds: document Samsung S2M series PMIC flash LED device
      dt-bindings: leds: document Samsung S2M series PMIC RGB LED device
      dt-bindings: extcon: document Samsung S2M series PMIC extcon device
      dt-bindings: power: supply: document Samsung S2M series PMIC charger device
      dt-bindings: mfd: s2mps11: add documentation for S2MU005 PMIC
      mfd: sec-irq: add support for creating multiple IRQ chips
      mfd: sec: add support for S2MU005 PMIC
      mfd: sec: store hardware revision in sec_pmic_dev and add S2MU005 support
      leds: flash: add support for Samsung S2M series PMIC flash LED device
      leds: rgb: add support for Samsung S2M series PMIC RGB LED device
      Documentation: leds: document pattern behavior of Samsung S2M series PMIC RGB LEDs
      extcon: add support for Samsung S2M series PMIC extcon devices
      power: supply: add support for Samsung S2M series PMIC charger device

 .../bindings/extcon/samsung,s2mu005-muic.yaml      |  35 ++
 .../bindings/leds/samsung,s2mu005-flash.yaml       |  52 +++
 .../bindings/leds/samsung,s2mu005-rgb.yaml         |  34 ++
 .../devicetree/bindings/mfd/samsung,s2mps11.yaml   | 103 ++++-
 .../power/supply/samsung,s2mu005-charger.yaml      |  35 ++
 Documentation/leds/index.rst                       |   1 +
 Documentation/leds/leds-s2m-rgb.rst                |  60 +++
 drivers/extcon/Kconfig                             |  10 +
 drivers/extcon/Makefile                            |   1 +
 drivers/extcon/extcon-s2m.c                        | 355 ++++++++++++++++
 drivers/leds/flash/Kconfig                         |  12 +
 drivers/leds/flash/Makefile                        |   1 +
 drivers/leds/flash/leds-s2m-flash.c                | 413 ++++++++++++++++++
 drivers/leds/rgb/Kconfig                           |  11 +
 drivers/leds/rgb/Makefile                          |   1 +
 drivers/leds/rgb/leds-s2m-rgb.c                    | 462 +++++++++++++++++++++
 drivers/mfd/sec-common.c                           |  41 +-
 drivers/mfd/sec-i2c.c                              |  29 ++
 drivers/mfd/sec-irq.c                              | 234 ++++++++---
 drivers/power/supply/Kconfig                       |  11 +
 drivers/power/supply/Makefile                      |   1 +
 drivers/power/supply/s2m-charger.c                 | 216 ++++++++++
 drivers/rtc/rtc-s5m.c                              |  15 +-
 include/linux/mfd/samsung/core.h                   |   9 +-
 include/linux/mfd/samsung/irq.h                    |  94 +++++
 include/linux/mfd/samsung/s2mu005.h                | 328 +++++++++++++++
 26 files changed, 2487 insertions(+), 77 deletions(-)
---
base-commit: 131f3d9446a6075192cdd91f197989d98302faa6
change-id: 20251112-s2mu005-pmic-0c67fa6bac3c

Best regards,
-- 
Kaustabh Chakraborty <kauschluss@disroot.org>


^ permalink raw reply

* Re: [PATCH v4 1/4] dt-bindings: rtc: renesas,rz-rtca3: Add RZ/V2H support
From: Geert Uytterhoeven @ 2025-11-13 14:12 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Ovidiu Panait, claudiu.beznea.uj, alexandre.belloni, robh,
	krzk+dt, conor+dt, geert+renesas, magnus.damm, p.zabel, linux-rtc,
	linux-renesas-soc, devicetree, linux-kernel
In-Reply-To: <20251110-hardhead-upside-54baa149f453@spud>

Hi Conor,

On Mon, 10 Nov 2025 at 19:50, Conor Dooley <conor@kernel.org> wrote:
> On Fri, Nov 07, 2025 at 09:07:03PM +0000, Ovidiu Panait wrote:
> > The Renesas RZ/V2H RTC IP is based on the same RTCA3 IP as RZ/G3S
> > (r9a08g045), with the following differences:
> > - It lacks the time capture functionality
> > - The maximum supported periodic interrupt frequency is 128Hz instead
> >   of 256Hz
> > - It requires two reset lines instead of one
> >
> > Add new compatible string "renesas,r9a09g057-rtca3" for RZ/V2H and update
> > the binding accordingly:
> > - Allow "resets" to contain one or two entries depending on the SoC.
> > - Add "reset-names" property, but make it required only for RZ/V2H.
> >
> > Signed-off-by: Ovidiu Panait <ovidiu.panait.rb@renesas.com>
>
> Didn't Alexandre already apply this? The changed version is
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> in case this replaces the other.

Alexandre dropped it, so it is no longer found in next-20251107
and later.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH v4 4/4] arm64: dts: renesas: r9a09g057h44-rzv2h-evk: Enable RTC
From: Geert Uytterhoeven @ 2025-11-13 14:18 UTC (permalink / raw)
  To: Ovidiu Panait
  Cc: claudiu.beznea.uj, alexandre.belloni, robh, krzk+dt, conor+dt,
	magnus.damm, p.zabel, linux-rtc, linux-renesas-soc, devicetree,
	linux-kernel
In-Reply-To: <20251107210706.45044-5-ovidiu.panait.rb@renesas.com>

On Fri, 7 Nov 2025 at 22:07, Ovidiu Panait <ovidiu.panait.rb@renesas.com> wrote:
> Enable RTC.
>
> Signed-off-by: Ovidiu Panait <ovidiu.panait.rb@renesas.com>

Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
i.e. will queue in renesas-devel for v6.19.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH v4 3/4] arm64: dts: renesas: r9a09g057: Add RTC node
From: Geert Uytterhoeven @ 2025-11-13 14:18 UTC (permalink / raw)
  To: Ovidiu Panait
  Cc: claudiu.beznea.uj, alexandre.belloni, robh, krzk+dt, conor+dt,
	magnus.damm, p.zabel, linux-rtc, linux-renesas-soc, devicetree,
	linux-kernel
In-Reply-To: <20251107210706.45044-4-ovidiu.panait.rb@renesas.com>

On Fri, 7 Nov 2025 at 22:07, Ovidiu Panait <ovidiu.panait.rb@renesas.com> wrote:
> Add RTC node to Renesas RZ/V2H ("R9A09G057") SoC DTSI.
>
> Signed-off-by: Ovidiu Panait <ovidiu.panait.rb@renesas.com>

Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
i.e. will queue in renesas-devel for v6.19.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH v4 04/16] dt-bindings: power: supply: BD72720 managed battery
From: Rob Herring (Arm) @ 2025-11-13 10:53 UTC (permalink / raw)
  To: Matti Vaittinen
  Cc: Krzysztof Kozlowski, Mark Brown, Linus Walleij, linux-kernel,
	Sebastian Reichel, Bartosz Golaszewski, Alexandre Belloni,
	linux-clk, Michael Turquette, Matti Vaittinen, linux-leds,
	Pavel Machek, Liam Girdwood, linux-gpio, linux-pm,
	Andreas Kemnade, Conor Dooley, devicetree, linux-rtc, Lee Jones,
	Stephen Boyd, Matti Vaittinen
In-Reply-To: <ac5a4e992e4fb9c7bffb1e641a7cd61f74af4cba.1763022807.git.mazziesaccount@gmail.com>


On Thu, 13 Nov 2025 10:52:19 +0200, Matti Vaittinen wrote:
> From: Matti Vaittinen <mazziesaccount@gmail.com>
> 
> The BD72720 PMIC has a battery charger + coulomb counter block. These
> can be used to manage charging of a lithium-ion battery and to do fuel
> gauging.
> 
> ROHM has developed a so called "zero-correction" -algorithm to improve
> the fuel-gauging accuracy close to the point where battery is depleted.
> This relies on battery specific "VDR" tables, which are measured from
> the battery, and which describe the voltage drop rate. More thorough
> explanation about the "zero correction" and "VDR" parameters is here:
> https://lore.kernel.org/all/676253b9-ff69-7891-1f26-a8b5bb5a421b@fi.rohmeurope.com/
> 
> Document the VDR zero-correction specific battery properties used by the
> BD72720 and some other ROHM chargers.
> 
> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> 
> ---
> NOTE:
> Linus' rb-tag holds only if there's no further comments from Rob.
> 
> Revision history:
>  v3 =>:
>  - No changes
> 
>  v2 => v3:
>  - Constrain VDR threshold voltage to 48V
>  - Use standard '-bp' -suffix for the rohm,volt-drop-soc
> 
>  RFCv1 => v2:
>  - Add units to rohm,volt-drop-soc (tenths of %)
>  - Give real temperatures matching the VDR tables, instead of vague
>    'high', 'normal', 'low', 'very low'. (Add table of temperatures and
>    use number matching the right temperature index in the VDR table name).
>  - Fix typoed 'algorithm' in commit message.
> 
> The parameters are describing the battery voltage drop rates - so they
> are properties of the battery, not the charger. Thus they do not belong
> in the charger node.
> 
> The right place for them is the battery node, which is described by the
> generic "battery.yaml". I was not comfortable with adding these
> properties to the generic battery.yaml because they are:
>   - Meaningful only for those charger drivers which have the VDR
>     algorithm implemented. (And even though the algorithm is not charger
>     specific, AFAICS, it is currently only used by some ROHM PMIC
>     drivers).
>   - Technique of measuring the VDR tables for a battery is not widely
>     known. AFAICS, only folks at ROHM are measuring those for some
>     customer products. We do have those tables available for some of the
>     products though (Kobo?).
> ---
>  .../power/supply/rohm,vdr-battery.yaml        | 80 +++++++++++++++++++
>  1 file changed, 80 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/power/supply/rohm,vdr-battery.yaml
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/power/supply/rohm,vdr-battery.example.dtb: battery (simple-battery): 'degrade-cycle-microamp-hours', 'rohm,volt-drop-0-microvolt', 'rohm,volt-drop-1-microvolt', 'rohm,volt-drop-2-microvolt', 'rohm,volt-drop-3-temp-microvolt', 'rohm,volt-drop-soc-bp', 'rohm,volt-drop-temperatures-millicelsius', 'rohm,voltage-vdr-thresh-microvolt' do not match any of the regexes: '^ocv-capacity-table-[0-9]+$', '^pinctrl-[0-9]+$'
	from schema $id: http://devicetree.org/schemas/power/supply/battery.yaml

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/ac5a4e992e4fb9c7bffb1e641a7cd61f74af4cba.1763022807.git.mazziesaccount@gmail.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


^ permalink raw reply

* Re: [PATCH v4 01/16] dt-bindings: regulator: ROHM BD72720
From: Rob Herring (Arm) @ 2025-11-13 10:53 UTC (permalink / raw)
  To: Matti Vaittinen
  Cc: Liam Girdwood, linux-kernel, linux-pm, Stephen Boyd, Conor Dooley,
	Sebastian Reichel, Pavel Machek, devicetree, Linus Walleij,
	Mark Brown, linux-clk, Andreas Kemnade, Lee Jones, linux-gpio,
	Matti Vaittinen, linux-leds, Bartosz Golaszewski,
	Krzysztof Kozlowski, linux-rtc, Matti Vaittinen,
	Michael Turquette, Alexandre Belloni
In-Reply-To: <21e83fccf2d2422f4bea1c482dcd3cb1aeda4085.1763022807.git.mazziesaccount@gmail.com>


On Thu, 13 Nov 2025 10:51:32 +0200, Matti Vaittinen wrote:
> From: Matti Vaittinen <mazziesaccount@gmail.com>
> 
> The ROHM BD72720 is a new PMIC with 10 BUCk and 11 LDO regulators.
> 
> The BD72720 is designed to support using the BUCK10 as a supply for
> the LDOs 1 to 4. When the BUCK10 is used for this, it can be set to a
> LDON_HEAD mode. In this mode, the BUCK10 voltage can't be controlled by
> software, but the voltage is adjusted by PMIC to match the LDO1 .. LDO4
> voltages with a given offset. Offset can be 50mV .. 300mV and is
> changeable at 50mV steps.
> 
> Add 'ldon-head-microvolt' property to denote a board which is designed
> to utilize the LDON_HEAD mode.
> 
> All other properties are already existing.
> 
> Add dt-binding doc for ROHM BD72720 regulators to make it usable.
> 
> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
> 
> ---
> Revision history:
>  v3 => v4:
>  - Drop type from ldon-head
>  - Fix the name patterns for regulator nodes and names
> 
>  v2 => v3:
>  - drop unnecessary descriptions
>  - use microvolts for the 'ldon-head' dt-property
> 
>  RFCv1 => v2:
>  - No changes
> ---
>  .../regulator/rohm,bd72720-regulator.yaml     | 148 ++++++++++++++++++
>  1 file changed, 148 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/regulator/rohm,bd72720-regulator.yaml
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:


doc reference errors (make refcheckdocs):
Warning: Documentation/devicetree/bindings/regulator/rohm,bd72720-regulator.yaml references a file that doesn't exist: Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml
Documentation/devicetree/bindings/regulator/rohm,bd72720-regulator.yaml: Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/21e83fccf2d2422f4bea1c482dcd3cb1aeda4085.1763022807.git.mazziesaccount@gmail.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


^ permalink raw reply

* [PATCH v4 16/16] MAINTAINERS: Add ROHM BD72720 PMIC
From: Matti Vaittinen @ 2025-11-13  8:56 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen
  Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
	linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
	Andreas Kemnade
In-Reply-To: <cover.1763022807.git.mazziesaccount@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1022 bytes --]

From: Matti Vaittinen <mazziesaccount@gmail.com>

Add the ROHM BD72720 PMIC driver files to be maintained by undersigned.

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>

---
Revision history:
 RFCv1 =>:
 - No changes
---
 MAINTAINERS | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index fe01aa31c58b..7e3c1eac7cda 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22353,6 +22353,7 @@ S:	Supported
 F:	drivers/clk/clk-bd718x7.c
 F:	drivers/gpio/gpio-bd71815.c
 F:	drivers/gpio/gpio-bd71828.c
+F:	drivers/gpio/gpio-bd72720.c
 F:	drivers/mfd/rohm-bd71828.c
 F:	drivers/mfd/rohm-bd718x7.c
 F:	drivers/mfd/rohm-bd9576.c
@@ -22369,6 +22370,7 @@ F:	drivers/watchdog/bd96801_wdt.c
 F:	include/linux/mfd/rohm-bd71815.h
 F:	include/linux/mfd/rohm-bd71828.h
 F:	include/linux/mfd/rohm-bd718x7.h
+F:	include/linux/mfd/rohm-bd72720.h
 F:	include/linux/mfd/rohm-bd957x.h
 F:	include/linux/mfd/rohm-bd96801.h
 F:	include/linux/mfd/rohm-bd96802.h
-- 
2.51.1


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 484 bytes --]

^ permalink raw reply related

* [PATCH v4 15/16] power: supply: bd71828-power: Support ROHM BD72720
From: Matti Vaittinen @ 2025-11-13  8:55 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen
  Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
	linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
	Andreas Kemnade
In-Reply-To: <cover.1763022807.git.mazziesaccount@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 11744 bytes --]

From: Matti Vaittinen <mazziesaccount@gmail.com>

The ROHM BD72720 is a power management IC with a charger and coulomb
counter block which is closely related to the charger / coulomb counter
found from the BD71815, BD71828, BD71879 which are all supported by the
bd71828-power driver. Due to the similarities it makes sense to support
also the BD72720 with the same driver.

Add basic support for the charger logic on ROHM BD72720.

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>

---
Revision history:
 v2 => :
 - No changes

 RFCv1 => v2:
 - Support using 9-bit register addresses (offset of 0x100) with the
   BD72720
 - Simplify probe and IC data as we don't need two regmaps
 - Drop two BD72720 specific functions as we no longer need different
   regmap for it.

Note: This patch depends on the series: "power: supply: add charger for
BD71828" by Andreas:
https://lore.kernel.org/all/20250918-bd71828-charger-v5-0-851164839c28@kemnade.info/

NOTE: Fuel-gauging is not supported. You can find an unmaintained
downstream reference-driver with a fuel-gauge example from:
https://github.com/RohmSemiconductor/Linux-Kernel-PMIC-Drivers/releases/tag/bd72720-reference-driver-v1
---
 drivers/power/supply/bd71828-power.c | 134 +++++++++++++++++++++++----
 1 file changed, 116 insertions(+), 18 deletions(-)

diff --git a/drivers/power/supply/bd71828-power.c b/drivers/power/supply/bd71828-power.c
index ce73c0f48397..438e220a9cb7 100644
--- a/drivers/power/supply/bd71828-power.c
+++ b/drivers/power/supply/bd71828-power.c
@@ -5,6 +5,7 @@
 #include <linux/kernel.h>
 #include <linux/mfd/rohm-bd71815.h>
 #include <linux/mfd/rohm-bd71828.h>
+#include <linux/mfd/rohm-bd72720.h>
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
 #include <linux/platform_device.h>
@@ -51,12 +52,14 @@ struct pwr_regs {
 	unsigned int chg_state;
 	unsigned int bat_temp;
 	unsigned int dcin_stat;
+	unsigned int dcin_online_mask;
 	unsigned int dcin_collapse_limit;
 	unsigned int chg_set1;
 	unsigned int chg_en;
 	unsigned int vbat_alm_limit_u;
 	unsigned int conf;
 	unsigned int vdcin;
+	unsigned int vdcin_himask;
 };
 
 static const struct pwr_regs pwr_regs_bd71828 = {
@@ -67,12 +70,14 @@ static const struct pwr_regs pwr_regs_bd71828 = {
 	.chg_state = BD71828_REG_CHG_STATE,
 	.bat_temp = BD71828_REG_BAT_TEMP,
 	.dcin_stat = BD71828_REG_DCIN_STAT,
+	.dcin_online_mask = BD7182x_MASK_DCIN_DET,
 	.dcin_collapse_limit = BD71828_REG_DCIN_CLPS,
 	.chg_set1 = BD71828_REG_CHG_SET1,
 	.chg_en   = BD71828_REG_CHG_EN,
 	.vbat_alm_limit_u = BD71828_REG_ALM_VBAT_LIMIT_U,
 	.conf = BD71828_REG_CONF,
 	.vdcin = BD71828_REG_VDCIN_U,
+	.vdcin_himask = BD7182x_MASK_VDCIN_U,
 };
 
 static const struct pwr_regs pwr_regs_bd71815 = {
@@ -85,6 +90,7 @@ static const struct pwr_regs pwr_regs_bd71815 = {
 	.chg_state = BD71815_REG_CHG_STATE,
 	.bat_temp = BD71815_REG_BAT_TEMP,
 	.dcin_stat = BD71815_REG_DCIN_STAT,
+	.dcin_online_mask = BD7182x_MASK_DCIN_DET,
 	.dcin_collapse_limit = BD71815_REG_DCIN_CLPS,
 	.chg_set1 = BD71815_REG_CHG_SET1,
 	.chg_en   = BD71815_REG_CHG_SET1,
@@ -92,6 +98,31 @@ static const struct pwr_regs pwr_regs_bd71815 = {
 	.conf = BD71815_REG_CONF,
 
 	.vdcin = BD71815_REG_VM_DCIN_U,
+	.vdcin_himask = BD7182x_MASK_VDCIN_U,
+};
+
+static struct pwr_regs pwr_regs_bd72720 = {
+	.vbat_avg = BD72720_REG_VM_SA_VBAT_U,
+	.ibat = BD72720_REG_CC_CURCD_U,
+	.ibat_avg = BD72720_REG_CC_SA_CURCD_U,
+	.btemp_vth = BD72720_REG_VM_BTMP_U,
+	/*
+	 * Note, state 0x40 IMP_CHK. not documented
+	 * on other variants but was still handled in
+	 * existing code. No memory traces as to why.
+	 */
+	.chg_state = BD72720_REG_CHG_STATE,
+	.bat_temp = BD72720_REG_CHG_BAT_TEMP_STAT,
+	.dcin_stat = BD72720_REG_INT_VBUS_SRC,
+	.dcin_online_mask = BD72720_MASK_DCIN_DET,
+	.dcin_collapse_limit = -1, /* Automatic. Setting not supported */
+	.chg_set1 = BD72720_REG_CHG_SET_1,
+	.chg_en = BD72720_REG_CHG_EN,
+	/* 15mV note in data-sheet */
+	.vbat_alm_limit_u = BD72720_REG_ALM_VBAT_TH_U,
+	.conf = BD72720_REG_CONF, /* o XSTB, only PON. Seprate slave addr */
+	.vdcin = BD72720_REG_VM_VBUS_U, /* 10 bits not 11 as with other ICs */
+	.vdcin_himask = BD72720_MASK_VDCIN_U,
 };
 
 struct bd71828_power {
@@ -298,7 +329,7 @@ static int get_chg_online(struct bd71828_power *pwr, int *chg_online)
 		dev_err(pwr->dev, "Failed to read DCIN status\n");
 		return ret;
 	}
-	*chg_online = ((r & BD7182x_MASK_DCIN_DET) != 0);
+	*chg_online = ((r & pwr->regs->dcin_online_mask) != 0);
 
 	return 0;
 }
@@ -329,8 +360,8 @@ static int bd71828_bat_inserted(struct bd71828_power *pwr)
 	ret = val & BD7182x_MASK_CONF_PON;
 
 	if (ret)
-		regmap_update_bits(pwr->regmap, pwr->regs->conf,
-				   BD7182x_MASK_CONF_PON, 0);
+		if (regmap_update_bits(pwr->regmap, pwr->regs->conf, BD7182x_MASK_CONF_PON, 0))
+			dev_err(pwr->dev, "Failed to write CONF register\n");
 
 	return ret;
 }
@@ -358,11 +389,13 @@ static int bd71828_init_hardware(struct bd71828_power *pwr)
 	int ret;
 
 	/* TODO: Collapse limit should come from device-tree ? */
-	ret = regmap_write(pwr->regmap, pwr->regs->dcin_collapse_limit,
-			   BD7182x_DCIN_COLLAPSE_DEFAULT);
-	if (ret) {
-		dev_err(pwr->dev, "Failed to write DCIN collapse limit\n");
-		return ret;
+	if (pwr->regs->dcin_collapse_limit != (unsigned int)-1) {
+		ret = regmap_write(pwr->regmap, pwr->regs->dcin_collapse_limit,
+				   BD7182x_DCIN_COLLAPSE_DEFAULT);
+		if (ret) {
+			dev_err(pwr->dev, "Failed to write DCIN collapse limit\n");
+			return ret;
+		}
 	}
 
 	ret = pwr->bat_inserted(pwr);
@@ -419,7 +452,7 @@ static int bd71828_charger_get_property(struct power_supply *psy,
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 		ret = bd7182x_read16_himask(pwr, pwr->regs->vdcin,
-					    BD7182x_MASK_VDCIN_U, &tmp);
+					    pwr->regs->vdcin_himask, &tmp);
 		if (ret)
 			return ret;
 
@@ -630,6 +663,9 @@ BD_ISR_AC(dcin_ovp_det, "DCIN OVER VOLTAGE", true)
 BD_ISR_DUMMY(dcin_mon_det, "DCIN voltage below threshold")
 BD_ISR_DUMMY(dcin_mon_res, "DCIN voltage above threshold")
 
+BD_ISR_DUMMY(vbus_curr_limit, "VBUS current limited")
+BD_ISR_DUMMY(vsys_ov_res, "VSYS over-voltage cleared")
+BD_ISR_DUMMY(vsys_ov_det, "VSYS over-voltage")
 BD_ISR_DUMMY(vsys_uv_res, "VSYS under-voltage cleared")
 BD_ISR_DUMMY(vsys_uv_det, "VSYS under-voltage")
 BD_ISR_DUMMY(vsys_low_res, "'VSYS low' cleared")
@@ -878,6 +914,51 @@ static int bd7182x_get_irqs(struct platform_device *pdev,
 		BDIRQ("bd71828-temp-125-over", bd71828_temp_vf125_det),
 		BDIRQ("bd71828-temp-125-under", bd71828_temp_vf125_res),
 	};
+	static const struct bd7182x_irq_res bd72720_irqs[] = {
+		BDIRQ("bd72720_int_vbus_rmv", BD_ISR_NAME(dcin_removed)),
+		BDIRQ("bd72720_int_vbus_det", bd7182x_dcin_detected),
+		BDIRQ("bd72720_int_vbus_mon_res", BD_ISR_NAME(dcin_mon_res)),
+		BDIRQ("bd72720_int_vbus_mon_det", BD_ISR_NAME(dcin_mon_det)),
+		BDIRQ("bd72720_int_vsys_mon_res", BD_ISR_NAME(vsys_mon_res)),
+		BDIRQ("bd72720_int_vsys_mon_det", BD_ISR_NAME(vsys_mon_det)),
+		BDIRQ("bd72720_int_vsys_uv_res", BD_ISR_NAME(vsys_uv_res)),
+		BDIRQ("bd72720_int_vsys_uv_det", BD_ISR_NAME(vsys_uv_det)),
+		BDIRQ("bd72720_int_vsys_lo_res", BD_ISR_NAME(vsys_low_res)),
+		BDIRQ("bd72720_int_vsys_lo_det", BD_ISR_NAME(vsys_low_det)),
+		BDIRQ("bd72720_int_vsys_ov_res", BD_ISR_NAME(vsys_ov_res)),
+		BDIRQ("bd72720_int_vsys_ov_det", BD_ISR_NAME(vsys_ov_det)),
+		BDIRQ("bd72720_int_bat_ilim", BD_ISR_NAME(vbus_curr_limit)),
+		BDIRQ("bd72720_int_chg_done", bd718x7_chg_done),
+		BDIRQ("bd72720_int_extemp_tout", BD_ISR_NAME(chg_wdg_temp)),
+		BDIRQ("bd72720_int_chg_wdt_exp", BD_ISR_NAME(chg_wdg)),
+		BDIRQ("bd72720_int_bat_mnt_out", BD_ISR_NAME(rechg_res)),
+		BDIRQ("bd72720_int_bat_mnt_in", BD_ISR_NAME(rechg_det)),
+		BDIRQ("bd72720_int_chg_trns", BD_ISR_NAME(chg_state_changed)),
+
+		BDIRQ("bd72720_int_vbat_mon_res", BD_ISR_NAME(bat_mon_res)),
+		BDIRQ("bd72720_int_vbat_mon_det", BD_ISR_NAME(bat_mon)),
+		BDIRQ("bd72720_int_vbat_sht_res", BD_ISR_NAME(bat_short_res)),
+		BDIRQ("bd72720_int_vbat_sht_det", BD_ISR_NAME(bat_short)),
+		BDIRQ("bd72720_int_vbat_lo_res", BD_ISR_NAME(bat_low_res)),
+		BDIRQ("bd72720_int_vbat_lo_det", BD_ISR_NAME(bat_low)),
+		BDIRQ("bd72720_int_vbat_ov_res", BD_ISR_NAME(bat_ov_res)),
+		BDIRQ("bd72720_int_vbat_ov_det", BD_ISR_NAME(bat_ov)),
+		BDIRQ("bd72720_int_bat_rmv", BD_ISR_NAME(bat_removed)),
+		BDIRQ("bd72720_int_bat_det", BD_ISR_NAME(bat_det)),
+		BDIRQ("bd72720_int_dbat_det", BD_ISR_NAME(bat_dead)),
+		BDIRQ("bd72720_int_bat_temp_trns", BD_ISR_NAME(temp_transit)),
+		BDIRQ("bd72720_int_lobtmp_res", BD_ISR_NAME(temp_bat_low_res)),
+		BDIRQ("bd72720_int_lobtmp_det", BD_ISR_NAME(temp_bat_low)),
+		BDIRQ("bd72720_int_ovbtmp_res", BD_ISR_NAME(temp_bat_hi_res)),
+		BDIRQ("bd72720_int_ovbtmp_det", BD_ISR_NAME(temp_bat_hi)),
+		BDIRQ("bd72720_int_ocur1_res", BD_ISR_NAME(bat_oc1_res)),
+		BDIRQ("bd72720_int_ocur1_det", BD_ISR_NAME(bat_oc1)),
+		BDIRQ("bd72720_int_ocur2_res", BD_ISR_NAME(bat_oc2_res)),
+		BDIRQ("bd72720_int_ocur2_det", BD_ISR_NAME(bat_oc2)),
+		BDIRQ("bd72720_int_ocur3_res", BD_ISR_NAME(bat_oc3_res)),
+		BDIRQ("bd72720_int_ocur3_det", BD_ISR_NAME(bat_oc3)),
+		BDIRQ("bd72720_int_cc_mon2_det", BD_ISR_NAME(bat_cc_mon)),
+	};
 	int num_irqs;
 	const struct bd7182x_irq_res *irqs;
 
@@ -890,6 +971,10 @@ static int bd7182x_get_irqs(struct platform_device *pdev,
 		irqs = &bd71815_irqs[0];
 		num_irqs = ARRAY_SIZE(bd71815_irqs);
 		break;
+	case ROHM_CHIP_TYPE_BD72720:
+		irqs = &bd72720_irqs[0];
+		num_irqs = ARRAY_SIZE(bd72720_irqs);
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -958,21 +1043,27 @@ static int bd71828_power_probe(struct platform_device *pdev)
 	struct power_supply_config ac_cfg = {};
 	struct power_supply_config bat_cfg = {};
 	int ret;
-	struct regmap *regmap;
-
-	regmap = dev_get_regmap(pdev->dev.parent, NULL);
-	if (!regmap) {
-		dev_err(&pdev->dev, "No parent regmap\n");
-		return -EINVAL;
-	}
 
 	pwr = devm_kzalloc(&pdev->dev, sizeof(*pwr), GFP_KERNEL);
 	if (!pwr)
 		return -ENOMEM;
 
-	pwr->regmap = regmap;
-	pwr->dev = &pdev->dev;
+	/*
+	 * The BD72720 MFD device registers two regmaps. Power-supply driver
+	 * uses the "wrap-map", which provides access to both of the I2C slave
+	 * addresses used by the BD72720
+	 */
 	pwr->chip_type = platform_get_device_id(pdev)->driver_data;
+	if (pwr->chip_type != ROHM_CHIP_TYPE_BD72720)
+		pwr->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	else
+		pwr->regmap = dev_get_regmap(pdev->dev.parent, "wrap-map");
+	if (!pwr->regmap) {
+		dev_err(&pdev->dev, "No parent regmap\n");
+		return -EINVAL;
+	}
+
+	pwr->dev = &pdev->dev;
 
 	switch (pwr->chip_type) {
 	case ROHM_CHIP_TYPE_BD71828:
@@ -985,6 +1076,12 @@ static int bd71828_power_probe(struct platform_device *pdev)
 		pwr->get_temp = bd71815_get_temp;
 		pwr->regs = &pwr_regs_bd71815;
 		break;
+	case ROHM_CHIP_TYPE_BD72720:
+		pwr->bat_inserted = bd71828_bat_inserted;
+		pwr->regs = &pwr_regs_bd72720;
+		pwr->get_temp = bd71828_get_temp;
+		dev_dbg(pwr->dev, "Found ROHM BD72720\n");
+		break;
 	default:
 		dev_err(pwr->dev, "Unknown PMIC\n");
 		return -EINVAL;
@@ -1030,6 +1127,7 @@ static int bd71828_power_probe(struct platform_device *pdev)
 static const struct platform_device_id bd71828_charger_id[] = {
 	{ "bd71815-power", ROHM_CHIP_TYPE_BD71815 },
 	{ "bd71828-power", ROHM_CHIP_TYPE_BD71828 },
+	{ "bd72720-power", ROHM_CHIP_TYPE_BD72720 },
 	{ },
 };
 MODULE_DEVICE_TABLE(platform, bd71828_charger_id);
-- 
2.51.1


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply related

* [PATCH v4 14/16] power: supply: bd71828: Support wider register addresses
From: Matti Vaittinen @ 2025-11-13  8:55 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen
  Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
	linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
	Andreas Kemnade
In-Reply-To: <cover.1763022807.git.mazziesaccount@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 2492 bytes --]

From: Matti Vaittinen <mazziesaccount@gmail.com>

The BD71828 power-supply driver assumes register addresses to be 8-bit.
The new BD72720 will use stacked register maps to hide paging which is
done using secondary I2C slave address. This requires use of 9-bit
register addresses in the power-supply driver (added offset 0x100 to
the 8-bit hardware register addresses).

The cost is slightly used memory consumption as the members in the
struct pwr_regs will be changed from u8 to unsigned int, which means 3
byte increase / member / instance.
This is currently 14 members (expected to possibly be increased when
adding new variants / new functionality which may introduce new
registers, but not expected to grow much) and 2 instances (will be 3
instances when BD72720 gets added).

So, even if the number of registers grew to 50 it'd be 150 bytes /
instance. Assuming we eventually supported 5 variants, it'd be
5 * 150 bytes, which stays very reasonable considering systems we are
dealing with.

As a side note, we can reduce the "wasted space / member / instance" from
3 bytes to 1 byte, by using u16 instead of the unsigned int if needed. I
rather use unsigned int to be initially prepared for devices with 32 bit
registers if there is no need to count bytes.

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
 v2 => :
 - No changes

 RFCv1 => v2:
 - New patch
---
 drivers/power/supply/bd71828-power.c | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/drivers/power/supply/bd71828-power.c b/drivers/power/supply/bd71828-power.c
index f667baedeb77..ce73c0f48397 100644
--- a/drivers/power/supply/bd71828-power.c
+++ b/drivers/power/supply/bd71828-power.c
@@ -44,19 +44,19 @@
 #define VBAT_LOW_TH			0x00D4
 
 struct pwr_regs {
-	u8 vbat_avg;
-	u8 ibat;
-	u8 ibat_avg;
-	u8 btemp_vth;
-	u8 chg_state;
-	u8 bat_temp;
-	u8 dcin_stat;
-	u8 dcin_collapse_limit;
-	u8 chg_set1;
-	u8 chg_en;
-	u8 vbat_alm_limit_u;
-	u8 conf;
-	u8 vdcin;
+	unsigned int vbat_avg;
+	unsigned int ibat;
+	unsigned int ibat_avg;
+	unsigned int btemp_vth;
+	unsigned int chg_state;
+	unsigned int bat_temp;
+	unsigned int dcin_stat;
+	unsigned int dcin_collapse_limit;
+	unsigned int chg_set1;
+	unsigned int chg_en;
+	unsigned int vbat_alm_limit_u;
+	unsigned int conf;
+	unsigned int vdcin;
 };
 
 static const struct pwr_regs pwr_regs_bd71828 = {
-- 
2.51.1


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply related

* [PATCH v4 13/16] rtc: bd70528: Support BD72720 rtc
From: Matti Vaittinen @ 2025-11-13  8:55 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen
  Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
	linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
	Andreas Kemnade
In-Reply-To: <cover.1763022807.git.mazziesaccount@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 3329 bytes --]

From: Matti Vaittinen <mazziesaccount@gmail.com>

The BD72720 has similar RTC block as a few other ROHM PMICs.

Add support for BD72720 RTC.

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

---
Revision history:
 RFCv1 =>:
 - No changes
---
 drivers/rtc/Kconfig       |  3 ++-
 drivers/rtc/rtc-bd70528.c | 21 ++++++++++++++-------
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 2933c41c77c8..418f6c28847a 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -561,7 +561,8 @@ config RTC_DRV_BD70528
 	depends on MFD_ROHM_BD71828
 	help
 	  If you say Y here you will get support for the RTC
-	  block on ROHM BD71815 and BD71828 Power Management IC.
+	  block on ROHM BD71815, BD71828 and BD72720 Power
+	  Management ICs.
 
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-bd70528.
diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c
index 954ac4ef53e8..4c8599761b2e 100644
--- a/drivers/rtc/rtc-bd70528.c
+++ b/drivers/rtc/rtc-bd70528.c
@@ -7,6 +7,7 @@
 #include <linux/bcd.h>
 #include <linux/mfd/rohm-bd71815.h>
 #include <linux/mfd/rohm-bd71828.h>
+#include <linux/mfd/rohm-bd72720.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -262,13 +263,13 @@ static int bd70528_probe(struct platform_device *pdev)
 
 		/*
 		 * See also BD718XX_ALM_EN_OFFSET:
-		 * This works for BD71828 and BD71815 as they have same offset
-		 * between ALM0 start and ALM0_MASK. If new ICs are to be
-		 * added this requires proper check as ALM0_MASK is not located
-		 * at the end of ALM0 block - but after all ALM blocks so if
-		 * amount of ALMs differ the offset to enable/disable is likely
-		 * to be incorrect and enable/disable must be given as own
-		 * reg address here.
+		 * This works for BD71828, BD71815, and BD72720 as they all
+		 * have same offset between the ALM0 start and the ALM0_MASK.
+		 * If new ICs are to be added this requires proper check as
+		 * the  ALM0_MASK is not located at the end of ALM0 block -
+		 * but after all ALM blocks. If amount of ALMs differ, the
+		 * offset to enable/disable is likely to be incorrect and
+		 * enable/disable must be given as own reg address here.
 		 */
 		bd_rtc->bd718xx_alm_block_start = BD71815_REG_RTC_ALM_START;
 		hour_reg = BD71815_REG_HOUR;
@@ -278,6 +279,11 @@ static int bd70528_probe(struct platform_device *pdev)
 		bd_rtc->bd718xx_alm_block_start = BD71828_REG_RTC_ALM_START;
 		hour_reg = BD71828_REG_RTC_HOUR;
 		break;
+	case ROHM_CHIP_TYPE_BD72720:
+		bd_rtc->reg_time_start = BD72720_REG_RTC_START;
+		bd_rtc->bd718xx_alm_block_start = BD72720_REG_RTC_ALM_START;
+		hour_reg = BD72720_REG_RTC_HOUR;
+		break;
 	default:
 		dev_err(&pdev->dev, "Unknown chip\n");
 		return -ENOENT;
@@ -337,6 +343,7 @@ static int bd70528_probe(struct platform_device *pdev)
 static const struct platform_device_id bd718x7_rtc_id[] = {
 	{ "bd71828-rtc", ROHM_CHIP_TYPE_BD71828 },
 	{ "bd71815-rtc", ROHM_CHIP_TYPE_BD71815 },
+	{ "bd72720-rtc", ROHM_CHIP_TYPE_BD72720 },
 	{ },
 };
 MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id);
-- 
2.51.1


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply related

* [PATCH v4 12/16] clk: clk-bd718x7: Support BD72720 clk gate
From: Matti Vaittinen @ 2025-11-13  8:55 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen
  Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
	linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
	Andreas Kemnade
In-Reply-To: <cover.1763022807.git.mazziesaccount@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 2535 bytes --]

From: Matti Vaittinen <mazziesaccount@gmail.com>

The BD72720 has similar simple clk gate as a few other ROHM PMICs.

Add support for BD72720 clk gate.

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>

---
Revision history:
 RFCv1 =>:
 - No changes
---
 drivers/clk/Kconfig       |  4 ++--
 drivers/clk/clk-bd718x7.c | 10 ++++++++--
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 3a1611008e48..619bd63a3c77 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -475,8 +475,8 @@ config COMMON_CLK_BD718XX
 	tristate "Clock driver for 32K clk gates on ROHM PMICs"
 	depends on MFD_ROHM_BD718XX || MFD_ROHM_BD71828
 	help
-	  This driver supports ROHM BD71837, BD71847, BD71850, BD71815
-	  and BD71828 PMICs clock gates.
+	  This driver supports ROHM BD71837, BD71847, BD71850, BD71815,
+	  BD71828, and BD72720 PMICs clock gates.
 
 config COMMON_CLK_FIXED_MMIO
 	bool "Clock driver for Memory Mapped Fixed values"
diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c
index ac40b669d60b..1cae974e6d1d 100644
--- a/drivers/clk/clk-bd718x7.c
+++ b/drivers/clk/clk-bd718x7.c
@@ -19,7 +19,8 @@
 #define BD71828_REG_OUT32K	0x4B
 /* BD71837 and BD71847 */
 #define BD718XX_REG_OUT32K	0x2E
-
+/* BD72720 */
+#define BD72720_REG_OUT32K	0x9a
 /*
  * BD71837, BD71847, and BD71828 all use bit [0] to clk output control
  */
@@ -118,6 +119,10 @@ static int bd71837_clk_probe(struct platform_device *pdev)
 		c->reg = BD71815_REG_OUT32K;
 		c->mask = CLK_OUT_EN_MASK;
 		break;
+	case ROHM_CHIP_TYPE_BD72720:
+		c->reg = BD72720_REG_OUT32K;
+		c->mask = CLK_OUT_EN_MASK;
+		break;
 	default:
 		dev_err(&pdev->dev, "Unknown clk chip\n");
 		return -EINVAL;
@@ -146,6 +151,7 @@ static const struct platform_device_id bd718x7_clk_id[] = {
 	{ "bd71847-clk", ROHM_CHIP_TYPE_BD71847 },
 	{ "bd71828-clk", ROHM_CHIP_TYPE_BD71828 },
 	{ "bd71815-clk", ROHM_CHIP_TYPE_BD71815 },
+	{ "bd72720-clk", ROHM_CHIP_TYPE_BD72720 },
 	{ },
 };
 MODULE_DEVICE_TABLE(platform, bd718x7_clk_id);
@@ -161,6 +167,6 @@ static struct platform_driver bd71837_clk = {
 module_platform_driver(bd71837_clk);
 
 MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
-MODULE_DESCRIPTION("BD718(15/18/28/37/47/50) and chip clk driver");
+MODULE_DESCRIPTION("BD718(15/18/28/37/47/50) and BD72720 chip clk driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:bd718xx-clk");
-- 
2.51.1


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply related

* [PATCH v4 11/16] gpio: Support ROHM BD72720 gpios
From: Matti Vaittinen @ 2025-11-13  8:54 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen
  Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
	linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
	Andreas Kemnade
In-Reply-To: <cover.1763022807.git.mazziesaccount@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 10901 bytes --]

From: Matti Vaittinen <mazziesaccount@gmail.com>

The ROHM BD72720 has 6 pins which may be configured as GPIOs. The
GPIO1 ... GPIO5 and EPDEN pins. The configuration is done to OTP at the
manufacturing, and it can't be read at runtime. The device-tree is
required to tell the software which of the pins are used as GPIOs.

Keep the pin mapping static regardless the OTP. This way the user-space
can always access the BASE+N for GPIO(N+1) (N = 0 to 4), and BASE + 5
for the EPDEN pin. Do this by setting always the number of GPIOs to 6,
and by using the valid-mask to invalidate the pins which aren't configured
as GPIOs.

First two pins can be set to be either input or output by OTP. Direction
can't be changed by software. Rest of the pins can be set as outputs
only. All of the pins support generating interrupts.

Support the Input/Output state getting/setting and the output mode
configuration (open-drain/push-pull).

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

---
Revision history:
 RFCv1 => :
 - No changes
---
 drivers/gpio/Kconfig        |   9 ++
 drivers/gpio/Makefile       |   1 +
 drivers/gpio/gpio-bd72720.c | 281 ++++++++++++++++++++++++++++++++++++
 3 files changed, 291 insertions(+)
 create mode 100644 drivers/gpio/gpio-bd72720.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7ee3afbc2b05..0c612c5163c5 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1319,6 +1319,15 @@ config GPIO_BD71828
 	  This driver can also be built as a module. If so, the module
 	  will be called gpio-bd71828.
 
+config GPIO_BD72720
+	tristate "ROHM BD72720 and BD73900 PMIC GPIO support"
+	depends on MFD_ROHM_BD71828
+	help
+	  Support for GPIO on ROHM BD72720 and BD73900 PMICs. There are two
+	  pins which can be configured to GPI or GPO, and three pins which can
+	  be configured to GPO on the ROHM PMIC. The pin configuration is done
+	  on OTP at manufacturing.
+
 config GPIO_BD9571MWV
 	tristate "ROHM BD9571 GPIO support"
 	depends on MFD_BD9571MWV
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ec296fa14bfd..7a5d03db3021 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_BCM_KONA)		+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BCM_XGS_IPROC)	+= gpio-xgs-iproc.o
 obj-$(CONFIG_GPIO_BD71815)		+= gpio-bd71815.o
 obj-$(CONFIG_GPIO_BD71828)		+= gpio-bd71828.o
+obj-$(CONFIG_GPIO_BD72720)		+= gpio-bd72720.o
 obj-$(CONFIG_GPIO_BD9571MWV)		+= gpio-bd9571mwv.o
 obj-$(CONFIG_GPIO_BLZP1600)		+= gpio-blzp1600.o
 obj-$(CONFIG_GPIO_BRCMSTB)		+= gpio-brcmstb.o
diff --git a/drivers/gpio/gpio-bd72720.c b/drivers/gpio/gpio-bd72720.c
new file mode 100644
index 000000000000..6549dbf4c7ad
--- /dev/null
+++ b/drivers/gpio/gpio-bd72720.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support to GPIOs on ROHM BD72720 and BD79300
+ * Copyright 2025 ROHM Semiconductors.
+ * Author: Matti Vaittinen <mazziesaccount@gmail.com>
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/rohm-bd72720.h>
+
+#define BD72720_GPIO_OPEN_DRAIN		0
+#define BD72720_GPIO_CMOS		BIT(1)
+#define BD72720_INT_GPIO1_IN_SRC	4
+/*
+ * The BD72720 has several "one time programmable" (OTP) configurations which
+ * can be set at manufacturing phase. A set of these options allow using pins
+ * as GPIO. The OTP configuration can't be read at run-time, so drivers rely on
+ * device-tree to advertise the correct options.
+ *
+ * Both DVS[0,1] pins can be configured to be used for:
+ *  - OTP0: regulator RUN state control
+ *  - OTP1: GPI
+ *  - OTP2: GPO
+ *  - OTP3: Power sequencer output
+ *  Data-sheet also states that these PINs can always be used for IRQ but the
+ *  driver limits this by allowing them to be used for IRQs with OTP1 only.
+ *
+ * Pins GPIO_EXTEN0 (GPIO3), GPIO_EXTEN1 (GPIO4), GPIO_FAULT_B (GPIO5) have OTP
+ * options for a specific (non GPIO) purposes, but also an option to configure
+ * them to be used as a GPO.
+ *
+ * OTP settings can be separately configured for each pin.
+ *
+ * DT properties:
+ * "rohm,pin-dvs0" and "rohm,pin-dvs1" can be set to one of the values:
+ * "dvs-input", "gpi", "gpo".
+ *
+ * "rohm,pin-exten0", "rohm,pin-exten1" and "rohm,pin-fault_b" can be set to:
+ * "gpo"
+ */
+
+enum bd72720_gpio_state {
+	BD72720_PIN_UNKNOWN,
+	BD72720_PIN_GPI,
+	BD72720_PIN_GPO,
+};
+
+enum {
+	BD72720_GPIO1,
+	BD72720_GPIO2,
+	BD72720_GPIO3,
+	BD72720_GPIO4,
+	BD72720_GPIO5,
+	BD72720_GPIO_EPDEN,
+	BD72720_NUM_GPIOS
+};
+
+struct bd72720_gpio {
+	/* chip.parent points the MFD which provides DT node and regmap */
+	struct gpio_chip chip;
+	/* dev points to the platform device for devm and prints */
+	struct device *dev;
+	struct regmap *regmap;
+	int gpio_is_input;
+};
+
+static int bd72720gpi_get(struct bd72720_gpio *bdgpio, unsigned int reg_offset)
+{
+	int ret, val, shift;
+
+	ret = regmap_read(bdgpio->regmap, BD72720_REG_INT_ETC1_SRC, &val);
+	if (ret)
+		return ret;
+
+	shift = BD72720_INT_GPIO1_IN_SRC + reg_offset;
+
+	return (val >> shift) & 1;
+}
+
+static int bd72720gpo_get(struct bd72720_gpio *bdgpio,
+			  unsigned int offset)
+{
+	const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+			     BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+			     BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+	int ret, val;
+
+	ret = regmap_read(bdgpio->regmap, regs[offset], &val);
+	if (ret)
+		return ret;
+
+	return val & BD72720_GPIO_HIGH;
+}
+
+static int bd72720gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+
+	if (BIT(offset) & bdgpio->gpio_is_input)
+		return bd72720gpi_get(bdgpio, offset);
+
+	return bd72720gpo_get(bdgpio, offset);
+}
+
+static int bd72720gpo_set(struct gpio_chip *chip, unsigned int offset,
+			  int value)
+{
+	struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+	const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+			     BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+			     BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+
+	if (BIT(offset) & bdgpio->gpio_is_input) {
+		dev_dbg(bdgpio->dev, "pin %d not output.\n", offset);
+		return -EINVAL;
+	}
+
+	if (value)
+		return regmap_set_bits(bdgpio->regmap, regs[offset],
+				      BD72720_GPIO_HIGH);
+
+	return regmap_clear_bits(bdgpio->regmap, regs[offset],
+					BD72720_GPIO_HIGH);
+}
+
+static int bd72720_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+				   unsigned long config)
+{
+	struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+	const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+			     BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+			     BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+
+	/*
+	 * We can only set the output mode, which makes sense only when output
+	 * OTP configuration is used.
+	 */
+	if (BIT(offset) & bdgpio->gpio_is_input)
+		return -ENOTSUPP;
+
+	switch (pinconf_to_config_param(config)) {
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		return regmap_update_bits(bdgpio->regmap,
+					  regs[offset],
+					  BD72720_GPIO_DRIVE_MASK,
+					  BD72720_GPIO_OPEN_DRAIN);
+	case PIN_CONFIG_DRIVE_PUSH_PULL:
+		return regmap_update_bits(bdgpio->regmap,
+					  regs[offset],
+					  BD72720_GPIO_DRIVE_MASK,
+					  BD72720_GPIO_CMOS);
+	default:
+		break;
+	}
+
+	return -ENOTSUPP;
+}
+
+static int bd72720gpo_direction_get(struct gpio_chip *chip,
+				    unsigned int offset)
+{
+	struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+
+	if (BIT(offset) & bdgpio->gpio_is_input)
+		return GPIO_LINE_DIRECTION_IN;
+
+	return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int bd72720_valid_mask(struct gpio_chip *gc,
+			      unsigned long *valid_mask,
+			      unsigned int ngpios)
+{
+	static const char * const properties[] = {
+		"rohm,pin-dvs0", "rohm,pin-dvs1", "rohm,pin-exten0",
+		"rohm,pin-exten1", "rohm,pin-fault_b"
+	};
+	struct bd72720_gpio *g = gpiochip_get_data(gc);
+	const char *val;
+	int i, ret;
+
+	*valid_mask = BIT(BD72720_GPIO_EPDEN);
+
+	if (!gc->parent)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(properties); i++) {
+		ret = fwnode_property_read_string(dev_fwnode(gc->parent),
+						  properties[i], &val);
+
+		if (ret) {
+			if (ret == -EINVAL)
+				continue;
+
+			dev_err(g->dev, "pin %d (%s), bad configuration\n", i,
+				properties[i]);
+
+			return ret;
+		}
+
+		if (strcmp(val, "gpi") == 0) {
+			if (i != BD72720_GPIO1 && i != BD72720_GPIO2) {
+				dev_warn(g->dev,
+					 "pin %d (%s) does not support INPUT mode",
+					 i, properties[i]);
+				continue;
+			}
+
+			*valid_mask |= BIT(i);
+			g->gpio_is_input |= BIT(i);
+		} else if (strcmp(val, "gpo") == 0) {
+			*valid_mask |= BIT(i);
+		}
+	}
+
+	return 0;
+}
+
+/* Template for GPIO chip */
+static const struct gpio_chip bd72720gpo_chip = {
+	.label			= "bd72720",
+	.owner			= THIS_MODULE,
+	.get			= bd72720gpio_get,
+	.get_direction		= bd72720gpo_direction_get,
+	.set			= bd72720gpo_set,
+	.set_config		= bd72720_gpio_set_config,
+	.init_valid_mask	= bd72720_valid_mask,
+	.can_sleep		= true,
+	.ngpio			= BD72720_NUM_GPIOS,
+	.base			= -1,
+};
+
+static int gpo_bd72720_probe(struct platform_device *pdev)
+{
+	struct bd72720_gpio *g;
+	struct device *parent, *dev;
+
+	/*
+	 * Bind devm lifetime to this platform device => use dev for devm.
+	 * also the prints should originate from this device.
+	 */
+	dev = &pdev->dev;
+	/* The device-tree and regmap come from MFD => use parent for that */
+	parent = dev->parent;
+
+	g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL);
+	if (!g)
+		return -ENOMEM;
+
+	g->chip = bd72720gpo_chip;
+	g->dev = dev;
+	g->chip.parent = parent;
+	g->regmap = dev_get_regmap(parent, NULL);
+
+	return devm_gpiochip_add_data(dev, &g->chip, g);
+}
+
+static const struct platform_device_id bd72720_gpio_id[] = {
+	{ "bd72720-gpio" },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, bd72720_gpio_id);
+
+static struct platform_driver gpo_bd72720_driver = {
+	.driver = {
+		.name = "bd72720-gpio",
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+	.probe = gpo_bd72720_probe,
+	.id_table = bd72720_gpio_id,
+};
+module_platform_driver(gpo_bd72720_driver);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("GPIO interface for BD72720 and BD73900");
+MODULE_LICENSE("GPL");
-- 
2.51.1


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply related

* [PATCH v4 10/16] regulator: bd71828: Support ROHM BD72720
From: Matti Vaittinen @ 2025-11-13  8:54 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen
  Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
	linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
	Andreas Kemnade
In-Reply-To: <cover.1763022807.git.mazziesaccount@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 40951 bytes --]

From: Matti Vaittinen <mazziesaccount@gmail.com>

ROHM BD72720 is a power management IC which integrates 10 buck and 11 LDO
regulators. This PMIC has plenty of commonalities with the BD71828 and
BD71879.

The BD72720 does also have similar 'run-level'-concept as the BD71828 had.
It allows controlling the regulator's 'en masse', although only BUCK1
and LDO1 can utilize this in BD72720. Similar to BD71828, this 'en
masse' -control is not supported by this driver.

Support the voltage and enable/disable state control for the BD72720.

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Reviewed-by: Mark Brown <broonie@kernel.org>

---
Revision history:
 v3 =>:
 - No changes

 v2 => v3:
 - The ldon-head dt-property was changed to microvolts. Adapt the driver
   to that
 RFCv1 => v2:
 - No changes

There are some new variants planned. Most notably, the BD73900 should be
similar to the BD72720 what comes to the regulator control logic.

If the run-level control is needed, there are some downstream extensions
available at:
https://rohmsemiconductor.github.io/Linux-Kernel-PMIC-Drivers/BD72720/
---
 drivers/regulator/Kconfig             |   8 +-
 drivers/regulator/bd71828-regulator.c | 993 +++++++++++++++++++++++++-
 2 files changed, 992 insertions(+), 9 deletions(-)

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index d84f3d054c59..660863f096e3 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -241,13 +241,13 @@ config REGULATOR_BD71815
 	  will be called bd71815-regulator.
 
 config REGULATOR_BD71828
-	tristate "ROHM BD71828 Power Regulator"
+	tristate "ROHM BD71828, BD72720 and BD73900 Power Regulators"
 	depends on MFD_ROHM_BD71828
 	select REGULATOR_ROHM
 	help
-	  This driver supports voltage regulators on ROHM BD71828 PMIC.
-	  This will enable support for the software controllable buck
-	  and LDO regulators.
+	  This driver supports voltage regulators on ROHM BD71828,
+	  BD71879, BD72720 and BD73900 PMICs. This will enable
+	  support for the software controllable buck and LDO regulators.
 
 	  This driver can also be built as a module. If so, the module
 	  will be called bd71828-regulator.
diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c
index 3d18dbfdb84e..ba16671ece42 100644
--- a/drivers/regulator/bd71828-regulator.c
+++ b/drivers/regulator/bd71828-regulator.c
@@ -3,12 +3,15 @@
 // bd71828-regulator.c ROHM BD71828GW-DS1 regulator driver
 //
 
+#include <linux/cleanup.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/mfd/rohm-bd71828.h>
+#include <linux/mfd/rohm-bd72720.h>
 #include <linux/module.h>
+#include <linux/mod_devicetable.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
@@ -16,6 +19,7 @@
 #include <linux/regulator/machine.h>
 #include <linux/regulator/of_regulator.h>
 
+#define BD72720_MASK_LDON_HEAD GENMASK(2, 0)
 struct reg_init {
 	unsigned int reg;
 	unsigned int mask;
@@ -64,6 +68,26 @@ static const struct reg_init bd71828_buck7_inits[] = {
 	},
 };
 
+#define BD72720_MASK_DVS_BUCK1_CTRL BIT(4)
+#define BD72720_MASK_DVS_LDO1_CTRL BIT(5)
+
+static const struct reg_init bd72720_buck1_inits[] = {
+	{
+		.reg = BD72720_REG_PS_CTRL_2,
+		.mask = BD72720_MASK_DVS_BUCK1_CTRL,
+		.val = 0, /* Disable "run-level" control */
+	},
+};
+
+static const struct reg_init bd72720_ldo1_inits[] = {
+	{
+		.reg = BD72720_REG_PS_CTRL_2,
+		.mask = BD72720_MASK_DVS_LDO1_CTRL,
+		.val = 0, /* Disable "run-level" control */
+	},
+};
+
+/* BD71828 Buck voltages */
 static const struct linear_range bd71828_buck1267_volts[] = {
 	REGULATOR_LINEAR_RANGE(500000, 0x00, 0xef, 6250),
 	REGULATOR_LINEAR_RANGE(2000000, 0xf0, 0xff, 0),
@@ -84,13 +108,79 @@ static const struct linear_range bd71828_buck5_volts[] = {
 	REGULATOR_LINEAR_RANGE(3300000, 0x10, 0x1f, 0),
 };
 
+/* BD71828 LDO voltages */
 static const struct linear_range bd71828_ldo_volts[] = {
 	REGULATOR_LINEAR_RANGE(800000, 0x00, 0x31, 50000),
 	REGULATOR_LINEAR_RANGE(3300000, 0x32, 0x3f, 0),
 };
 
+/* BD72720 Buck voltages */
+static const struct linear_range bd72720_buck1234_volts[] = {
+	REGULATOR_LINEAR_RANGE(500000, 0x00, 0xc0, 6250),
+	REGULATOR_LINEAR_RANGE(1700000, 0xc1, 0xff, 0),
+};
+
+static const struct linear_range bd72720_buck589_volts[] = {
+	REGULATOR_LINEAR_RANGE(500000, 0x00, 0x78, 10000),
+	REGULATOR_LINEAR_RANGE(1700000, 0x79, 0xff, 0),
+};
+
+static const struct linear_range bd72720_buck67_volts[] = {
+	REGULATOR_LINEAR_RANGE(1500000, 0x00, 0xb4, 10000),
+	REGULATOR_LINEAR_RANGE(3300000, 0xb5, 0xff, 0),
+};
+
+/*
+ * The BUCK10 on BD72720 has two modes of operation, depending on a LDON_HEAD
+ * setting. When LDON_HEAD is 0x0, the behaviour is as with other bucks, eg.
+ * voltage can be set to a values indicated below using the VSEL register.
+ *
+ * However, when LDON_HEAD is set to 0x1 ... 0x7, BUCK 10 voltage is, according
+ * to the data-sheet, "automatically adjusted following LDON_HEAD setting and
+ * clamped to BUCK10_VID setting".
+ *
+ * Again, reading the data-sheet shows a "typical connection" where the BUCK10
+ * is used to supply the LDOs 1-4. My assumption is that in practice, this
+ * means that the BUCK10 voltage will be adjusted based on the maximum output
+ * of the LDO 1-4 (to minimize power loss). This makes sense.
+ *
+ * Auto-adjusting regulators aren't something I really like to model in the
+ * driver though - and, if the auto-adjustment works as intended, then there
+ * should really be no need to software to care about the buck10 voltages.
+ * If enable/disable control is still needed, we can implement buck10 as a
+ * regulator with only the enable/disable ops - and device-tree can be used
+ * to model the supply-relations. I believe this could allow the regulator
+ * framework to automagically disable the BUCK10 if all LDOs that are being
+ * supplied by it are disabled.
+ */
+static const struct linear_range bd72720_buck10_volts[] = {
+	REGULATOR_LINEAR_RANGE(500000, 0x00, 0xc0, 6250),
+	REGULATOR_LINEAR_RANGE(1700000, 0xc1, 0xff, 0),
+};
+
+/* BD72720 LDO voltages */
+static const struct linear_range bd72720_ldo1234_volts[] = {
+	REGULATOR_LINEAR_RANGE(500000, 0x00, 0x50, 6250),
+	REGULATOR_LINEAR_RANGE(1000000, 0x51, 0x7f, 0),
+};
+
+static const struct linear_range bd72720_ldo57891011_volts[] = {
+	REGULATOR_LINEAR_RANGE(750000, 0x00, 0xff, 10000),
+};
+
+static const struct linear_range bd72720_ldo6_volts[] = {
+	REGULATOR_LINEAR_RANGE(600000, 0x00, 0x78, 10000),
+	REGULATOR_LINEAR_RANGE(1800000, 0x79, 0x7f, 0),
+};
+
 static const unsigned int bd71828_ramp_delay[] = { 2500, 5000, 10000, 20000 };
 
+/*
+ * BD72720 supports setting both the ramp-up and ramp-down values
+ * separately. Do we need to support ramp-down setting?
+ */
+static const unsigned int bd72720_ramp_delay[] = { 5000, 7500, 10000, 12500 };
+
 static int buck_set_hw_dvs_levels(struct device_node *np,
 				  const struct regulator_desc *desc,
 				  struct regulator_config *cfg)
@@ -171,6 +261,24 @@ static const struct regulator_ops bd71828_ldo6_ops = {
 	.is_enabled = regulator_is_enabled_regmap,
 };
 
+static const struct regulator_ops bd72720_regulator_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.list_voltage = regulator_list_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,
+	.set_ramp_delay = regulator_set_ramp_delay_regmap,
+};
+
+static const struct regulator_ops bd72720_buck10_ldon_head_op = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.set_ramp_delay = regulator_set_ramp_delay_regmap,
+};
+
 static const struct bd71828_regulator_data bd71828_rdata[] = {
 	{
 		.desc = {
@@ -677,22 +785,890 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
 	},
 };
 
+#define BD72720_BUCK10_DESC_INDEX 10
+#define BD72720_NUM_BUCK_VOLTS 0x100
+#define BD72720_NUM_LDO_VOLTS 0x100
+#define BD72720_NUM_LDO12346_VOLTS 0x80
+
+static const struct bd71828_regulator_data bd72720_rdata[] = {
+	{
+		.desc = {
+			.name = "buck1",
+			.of_match = of_match_ptr("buck1"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_BUCK1,
+			.type = REGULATOR_VOLTAGE,
+
+			/*
+			 * The BD72720 BUCK1 and LDO1 support GPIO toggled
+			 * sub-RUN states called RUN0, RUN1, RUN2 and RUN3.
+			 * The "operating mode" (sub-RUN states or normal)
+			 * can be changed by a register.
+			 *
+			 * When the sub-RUN states are used, the voltage and
+			 * enable state depend on a state specific
+			 * configuration. The voltage and enable configuration
+			 * for BUCK1 and LDO1 can be defined for each sub-RUN
+			 * state using BD72720_REG_[BUCK,LDO]1_VSEL_R[0,1,2,3]
+			 * voltage selection registers and the bits
+			 * BD72720_MASK_RUN_[0,1,2,3]_EN in the enable registers.
+			 * The PMIC will change both the BUCK1 and LDO1 voltages
+			 * to the states defined in these registers when
+			 * "DVS GPIOs" are toggled.
+			 *
+			 * If RUN 0 .. RUN 4 states are to be used, the normal
+			 * voltage configuration mechanisms do not apply
+			 * and we should overwrite the ops and ignore the
+			 * voltage setting/getting registers which are setup
+			 * here. This is not supported for now. If you need
+			 * this functionality, you may try merging functionality
+			 * from a downstream driver:
+			 * https://rohmsemiconductor.github.io/Linux-Kernel-PMIC-Drivers/BD72720/
+			 */
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_buck1234_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_buck1234_volts),
+			.n_voltages = BD72720_NUM_BUCK_VOLTS,
+			.enable_reg = BD72720_REG_BUCK1_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_BUCK1_VSEL_RB,
+			.vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_BUCK1_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR, /* Deep idle in data-sheet */
+			.run_reg = BD72720_REG_BUCK1_VSEL_RB,
+			.run_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_reg = BD72720_REG_BUCK1_VSEL_I,
+			.idle_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_reg = BD72720_REG_BUCK1_VSEL_S,
+			.suspend_mask = BD72720_MASK_BUCK_VSEL,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_reg = BD72720_REG_BUCK1_VSEL_DI,
+			.lpsr_mask = BD72720_MASK_BUCK_VSEL,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+		.reg_inits = bd72720_buck1_inits,
+		.reg_init_amnt = ARRAY_SIZE(bd72720_buck1_inits),
+	}, {
+		.desc = {
+			.name = "buck2",
+			.of_match = of_match_ptr("buck2"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_BUCK2,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_buck1234_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_buck1234_volts),
+			.n_voltages = BD72720_NUM_BUCK_VOLTS,
+			.enable_reg = BD72720_REG_BUCK2_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_BUCK2_VSEL_R,
+			.vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_BUCK2_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_BUCK2_VSEL_R,
+			.run_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_reg = BD72720_REG_BUCK2_VSEL_I,
+			.idle_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_reg = BD72720_REG_BUCK2_VSEL_S,
+			.suspend_mask = BD72720_MASK_BUCK_VSEL,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_reg = BD72720_REG_BUCK2_VSEL_DI,
+			.lpsr_mask = BD72720_MASK_BUCK_VSEL,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "buck3",
+			.of_match = of_match_ptr("buck3"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_BUCK3,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_buck1234_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_buck1234_volts),
+			.n_voltages = BD72720_NUM_BUCK_VOLTS,
+			.enable_reg = BD72720_REG_BUCK3_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_BUCK3_VSEL_R,
+			.vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_BUCK3_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_BUCK3_VSEL_R,
+			.run_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_reg = BD72720_REG_BUCK3_VSEL_I,
+			.idle_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_reg = BD72720_REG_BUCK3_VSEL_S,
+			.suspend_mask = BD72720_MASK_BUCK_VSEL,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_reg = BD72720_REG_BUCK3_VSEL_DI,
+			.lpsr_mask = BD72720_MASK_BUCK_VSEL,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "buck4",
+			.of_match = of_match_ptr("buck4"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_BUCK4,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_buck1234_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_buck1234_volts),
+			.n_voltages = BD72720_NUM_BUCK_VOLTS,
+			.enable_reg = BD72720_REG_BUCK4_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_BUCK4_VSEL_R,
+			.vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_BUCK4_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_BUCK4_VSEL_R,
+			.run_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_reg = BD72720_REG_BUCK4_VSEL_I,
+			.idle_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_reg = BD72720_REG_BUCK4_VSEL_S,
+			.suspend_mask = BD72720_MASK_BUCK_VSEL,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_reg = BD72720_REG_BUCK4_VSEL_DI,
+			.lpsr_mask = BD72720_MASK_BUCK_VSEL,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "buck5",
+			.of_match = of_match_ptr("buck5"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_BUCK5,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_buck589_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_buck589_volts),
+			.n_voltages = BD72720_NUM_BUCK_VOLTS,
+			.enable_reg = BD72720_REG_BUCK5_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_BUCK5_VSEL,
+			.vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_BUCK5_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_BUCK5_VSEL,
+			.run_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "buck6",
+			.of_match = of_match_ptr("buck6"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_BUCK6,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_buck67_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_buck67_volts),
+			.n_voltages = BD72720_NUM_BUCK_VOLTS,
+			.enable_reg = BD72720_REG_BUCK6_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_BUCK6_VSEL,
+			.vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_BUCK6_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_BUCK6_VSEL,
+			.run_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "buck7",
+			.of_match = of_match_ptr("buck7"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_BUCK7,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_buck67_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_buck67_volts),
+			.n_voltages = BD72720_NUM_BUCK_VOLTS,
+			.enable_reg = BD72720_REG_BUCK7_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_BUCK7_VSEL,
+			.vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_BUCK7_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_BUCK7_VSEL,
+			.run_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "buck8",
+			.of_match = of_match_ptr("buck8"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_BUCK8,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_buck589_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_buck589_volts),
+			.n_voltages = BD72720_NUM_BUCK_VOLTS,
+			.enable_reg = BD72720_REG_BUCK8_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_BUCK8_VSEL,
+			.vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_BUCK8_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_BUCK8_VSEL,
+			.run_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "buck9",
+			.of_match = of_match_ptr("buck9"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_BUCK9,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_buck589_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_buck589_volts),
+			.n_voltages = BD72720_NUM_BUCK_VOLTS,
+			.enable_reg = BD72720_REG_BUCK9_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_BUCK9_VSEL,
+			.vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_BUCK9_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_BUCK9_VSEL,
+			.run_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "buck10",
+			.of_match = of_match_ptr("buck10"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_BUCK10,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_buck10_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_buck10_volts),
+			.n_voltages = BD72720_NUM_BUCK_VOLTS,
+			.enable_reg = BD72720_REG_BUCK10_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_BUCK10_VSEL,
+			.vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_BUCK10_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_BUCK10_VSEL,
+			.run_mask = BD72720_MASK_BUCK_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo1",
+			.of_match = of_match_ptr("ldo1"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_LDO1,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_ldo1234_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_ldo1234_volts),
+			.n_voltages = BD72720_NUM_LDO12346_VOLTS,
+			.enable_reg = BD72720_REG_LDO1_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_LDO1_VSEL_RB,
+			.vsel_mask = BD72720_MASK_LDO12346_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_LDO1_MODE1,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_LDO1_VSEL_RB,
+			.run_mask = BD72720_MASK_LDO12346_VSEL,
+			.idle_reg = BD72720_REG_LDO1_VSEL_I,
+			.idle_mask = BD72720_MASK_LDO12346_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_reg = BD72720_REG_LDO1_VSEL_S,
+			.suspend_mask = BD72720_MASK_LDO12346_VSEL,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_reg = BD72720_REG_LDO1_VSEL_DI,
+			.lpsr_mask = BD72720_MASK_LDO12346_VSEL,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+		.reg_inits = bd72720_ldo1_inits,
+		.reg_init_amnt = ARRAY_SIZE(bd72720_ldo1_inits),
+	}, {
+		.desc = {
+			.name = "ldo2",
+			.of_match = of_match_ptr("ldo2"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_LDO2,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_ldo1234_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_ldo1234_volts),
+			.n_voltages = BD72720_NUM_LDO12346_VOLTS,
+			.enable_reg = BD72720_REG_LDO2_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_LDO2_VSEL_R,
+			.vsel_mask = BD72720_MASK_LDO12346_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_LDO2_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_LDO2_VSEL_R,
+			.run_mask = BD72720_MASK_LDO12346_VSEL,
+			.idle_reg = BD72720_REG_LDO2_VSEL_I,
+			.idle_mask = BD72720_MASK_LDO12346_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_reg = BD72720_REG_LDO2_VSEL_S,
+			.suspend_mask = BD72720_MASK_LDO12346_VSEL,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_reg = BD72720_REG_LDO2_VSEL_DI,
+			.lpsr_mask = BD72720_MASK_LDO12346_VSEL,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo3",
+			.of_match = of_match_ptr("ldo3"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_LDO3,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_ldo1234_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_ldo1234_volts),
+			.n_voltages = BD72720_NUM_LDO12346_VOLTS,
+			.enable_reg = BD72720_REG_LDO3_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_LDO3_VSEL_R,
+			.vsel_mask = BD72720_MASK_LDO12346_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_LDO3_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_LDO3_VSEL_R,
+			.run_mask = BD72720_MASK_LDO12346_VSEL,
+			.idle_reg = BD72720_REG_LDO3_VSEL_I,
+			.idle_mask = BD72720_MASK_LDO12346_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_reg = BD72720_REG_LDO3_VSEL_S,
+			.suspend_mask = BD72720_MASK_LDO12346_VSEL,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_reg = BD72720_REG_LDO3_VSEL_DI,
+			.lpsr_mask = BD72720_MASK_LDO12346_VSEL,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo4",
+			.of_match = of_match_ptr("ldo4"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_LDO4,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_ldo1234_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_ldo1234_volts),
+			.n_voltages = BD72720_NUM_LDO12346_VOLTS,
+			.enable_reg = BD72720_REG_LDO4_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_LDO4_VSEL_R,
+			.vsel_mask = BD72720_MASK_LDO12346_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_LDO4_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_LDO4_VSEL_R,
+			.run_mask = BD72720_MASK_LDO12346_VSEL,
+			.idle_reg = BD72720_REG_LDO4_VSEL_I,
+			.idle_mask = BD72720_MASK_LDO12346_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_reg = BD72720_REG_LDO4_VSEL_S,
+			.suspend_mask = BD72720_MASK_LDO12346_VSEL,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_reg = BD72720_REG_LDO4_VSEL_DI,
+			.lpsr_mask = BD72720_MASK_LDO12346_VSEL,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo5",
+			.of_match = of_match_ptr("ldo5"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_LDO5,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_ldo57891011_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+			.n_voltages = BD72720_NUM_LDO_VOLTS,
+			.enable_reg = BD72720_REG_LDO5_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_LDO5_VSEL,
+			.vsel_mask = BD72720_MASK_LDO_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_LDO5_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_LDO5_VSEL,
+			.run_mask = BD72720_MASK_LDO_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo6",
+			.of_match = of_match_ptr("ldo6"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_LDO6,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_ldo6_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_ldo6_volts),
+			.n_voltages = BD72720_NUM_LDO12346_VOLTS,
+			.enable_reg = BD72720_REG_LDO6_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_LDO6_VSEL,
+			.vsel_mask = BD72720_MASK_LDO12346_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_LDO6_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_LDO6_VSEL,
+			.run_mask = BD72720_MASK_LDO12346_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo7",
+			.of_match = of_match_ptr("ldo7"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_LDO7,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_ldo57891011_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+			.n_voltages = BD72720_NUM_LDO_VOLTS,
+			.enable_reg = BD72720_REG_LDO7_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_LDO7_VSEL,
+			.vsel_mask = BD72720_MASK_LDO_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_LDO7_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_LDO7_VSEL,
+			.run_mask = BD72720_MASK_LDO_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo8",
+			.of_match = of_match_ptr("ldo8"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_LDO8,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_ldo57891011_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+			.n_voltages = BD72720_NUM_LDO_VOLTS,
+			.enable_reg = BD72720_REG_LDO8_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_LDO8_VSEL,
+			.vsel_mask = BD72720_MASK_LDO_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_LDO8_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_LDO8_VSEL,
+			.run_mask = BD72720_MASK_LDO_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo9",
+			.of_match = of_match_ptr("ldo9"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_LDO9,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_ldo57891011_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+			.n_voltages = BD72720_NUM_LDO_VOLTS,
+			.enable_reg = BD72720_REG_LDO9_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_LDO9_VSEL,
+			.vsel_mask = BD72720_MASK_LDO_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_LDO9_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_LDO9_VSEL,
+			.run_mask = BD72720_MASK_LDO_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo10",
+			.of_match = of_match_ptr("ldo10"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_LDO10,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_ldo57891011_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+			.n_voltages = BD72720_NUM_LDO_VOLTS,
+			.enable_reg = BD72720_REG_LDO10_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_LDO10_VSEL,
+			.vsel_mask = BD72720_MASK_LDO_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_LDO10_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_LDO10_VSEL,
+			.run_mask = BD72720_MASK_LDO_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	}, {
+		.desc = {
+			.name = "ldo11",
+			.of_match = of_match_ptr("ldo11"),
+			.regulators_node = of_match_ptr("regulators"),
+			.id = BD72720_LDO11,
+			.type = REGULATOR_VOLTAGE,
+			.ops = &bd72720_regulator_ops,
+			.linear_ranges = bd72720_ldo57891011_volts,
+			.n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+			.n_voltages = BD72720_NUM_LDO_VOLTS,
+			.enable_reg = BD72720_REG_LDO11_ON,
+			.enable_mask = BD72720_MASK_RUN_B_EN,
+			.vsel_reg = BD72720_REG_LDO11_VSEL,
+			.vsel_mask = BD72720_MASK_LDO_VSEL,
+
+			.ramp_delay_table = bd72720_ramp_delay,
+			.n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+			.ramp_reg = BD72720_REG_LDO11_MODE,
+			.ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+			.owner = THIS_MODULE,
+			.of_parse_cb = buck_set_hw_dvs_levels,
+		},
+		.dvs = {
+			.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+				     ROHM_DVS_LEVEL_SUSPEND |
+				     ROHM_DVS_LEVEL_LPSR,
+			.run_reg = BD72720_REG_LDO11_VSEL,
+			.run_mask = BD72720_MASK_LDO_VSEL,
+			.idle_on_mask = BD72720_MASK_IDLE_EN,
+			.suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+			.lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+		},
+	},
+};
+
+static int bd72720_buck10_ldon_head_mode(struct device *dev,
+					 struct device_node *npreg,
+					 struct regmap *regmap,
+					 struct regulator_desc *buck10_desc)
+{
+	struct device_node *np __free(device_node) =
+		of_get_child_by_name(npreg, "buck10");
+	uint32_t ldon_head;
+	int ldon_val;
+	int ret;
+
+	if (!np) {
+		dev_err(dev, "failed to find buck10 regulator node\n");
+		return -ENODEV;
+	}
+
+	ret = of_property_read_u32(np, "rohm,ldon-head-microvolt", &ldon_head);
+	if (ret == -EINVAL)
+		return 0;
+	if (ret)
+		return ret;
+
+	/*
+	 * LDON_HEAD mode means the BUCK10 is used to supply LDOs 1-4 and
+	 * the BUCK 10 voltage is automatically set to follow LDO 1-4
+	 * settings. Thus the BUCK10 should not allow voltage [g/s]etting.
+	 */
+	buck10_desc->ops = &bd72720_buck10_ldon_head_op;
+
+	ldon_val = ldon_head / 50000 + 1;
+	if (ldon_head > 300000) {
+		dev_warn(dev, "Unsupported LDON_HEAD, clamping to 300 mV\n");
+		ldon_val = 7;
+	}
+
+	return regmap_update_bits(regmap, BD72720_REG_LDO1_MODE2,
+				  BD72720_MASK_LDON_HEAD, ldon_val);
+}
+
+static int bd72720_dt_parse(struct device *dev,
+			    struct regulator_desc *buck10_desc,
+			    struct regmap *regmap)
+{
+	struct device_node *nproot __free(device_node) =
+		of_get_child_by_name(dev->parent->of_node, "regulators");
+
+	if (!nproot) {
+		dev_err(dev, "failed to find regulators node\n");
+		return -ENODEV;
+	}
+
+	return bd72720_buck10_ldon_head_mode(dev, nproot, regmap, buck10_desc);
+}
+
 static int bd71828_probe(struct platform_device *pdev)
 {
-	int i, j, ret;
+	int i, j, ret, num_regulators;
 	struct regulator_config config = {
 		.dev = pdev->dev.parent,
 	};
+	enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data;
+	struct bd71828_regulator_data *rdata;
 
 	config.regmap = dev_get_regmap(pdev->dev.parent, NULL);
 	if (!config.regmap)
 		return -ENODEV;
 
-	for (i = 0; i < ARRAY_SIZE(bd71828_rdata); i++) {
+	switch (chip) {
+	case ROHM_CHIP_TYPE_BD72720:
+		rdata = devm_kmemdup(&pdev->dev, bd72720_rdata,
+				     sizeof(bd72720_rdata), GFP_KERNEL);
+		if (!rdata)
+			return -ENOMEM;
+
+		ret = bd72720_dt_parse(&pdev->dev, &rdata[BD72720_BUCK10_DESC_INDEX].desc,
+				       config.regmap);
+		if (ret)
+			return ret;
+
+		num_regulators = ARRAY_SIZE(bd72720_rdata);
+		break;
+
+	case ROHM_CHIP_TYPE_BD71828:
+		rdata = devm_kmemdup(&pdev->dev, bd71828_rdata,
+				     sizeof(bd71828_rdata), GFP_KERNEL);
+		if (!rdata)
+			return -ENOMEM;
+
+		num_regulators = ARRAY_SIZE(bd71828_rdata);
+
+		break;
+	default:
+		return dev_err_probe(&pdev->dev, -EINVAL,
+				     "Unsupported device\n");
+	}
+
+	for (i = 0; i < num_regulators; i++) {
 		struct regulator_dev *rdev;
-		const struct bd71828_regulator_data *rd;
+		struct bd71828_regulator_data *rd;
+
+		rd = &rdata[i];
 
-		rd = &bd71828_rdata[i];
+		config.driver_data = rd;
 		rdev = devm_regulator_register(&pdev->dev,
 					       &rd->desc, &config);
 		if (IS_ERR(rdev))
@@ -714,12 +1690,20 @@ static int bd71828_probe(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct platform_device_id bd71828_pmic_id[] = {
+	{ "bd71828-pmic", ROHM_CHIP_TYPE_BD71828 },
+	{ "bd72720-pmic", ROHM_CHIP_TYPE_BD72720 },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, bd71828_pmic_id);
+
 static struct platform_driver bd71828_regulator = {
 	.driver = {
 		.name = "bd71828-pmic",
 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
 	},
 	.probe = bd71828_probe,
+	.id_table = bd71828_pmic_id,
 };
 
 module_platform_driver(bd71828_regulator);
@@ -727,4 +1711,3 @@ module_platform_driver(bd71828_regulator);
 MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
 MODULE_DESCRIPTION("BD71828 voltage regulator driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bd71828-pmic");
-- 
2.51.1


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply related

* [PATCH v4 09/16] regulator: bd71828: rename IC specific entities
From: Matti Vaittinen @ 2025-11-13  8:54 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen
  Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sebastian Reichel, Liam Girdwood, Mark Brown,
	Michael Turquette, Stephen Boyd, Matti Vaittinen, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, linux-leds, devicetree,
	linux-kernel, linux-pm, linux-clk, linux-gpio, linux-rtc,
	Andreas Kemnade
In-Reply-To: <cover.1763022807.git.mazziesaccount@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 5360 bytes --]

From: Matti Vaittinen <mazziesaccount@gmail.com>

The new ROHM BD72720 PMIC has similarities with the BD71828. It makes
sense to support the regulator control for both PMICs using the same
driver. It is often more clear to have the IC specific functions and
globals named starting with the chip-name. So, as a preparatory step,
prefix the BD71828 specific functions and globals with the bd71828.

It would be tempting to try also removing the chip ID from those
functions which will be common for both PMICs. I have bad experiences on
this as it tends to lead to problems when yet another IC is being
supported with the same driver, and we will have some functions used for
all, some for two of the three, and some for just one. At this point
I used to start inventing wildcards like BD718XX or BD7272X. This
approach is pretty much always failing as we tend to eventually have
something like BD73900 - where all the wildcard stuff will break down.

So, my approach these days is to:
 - keep the original chip-id prefix for anything that had it already
   (and avoid the churn).
 - use same prefix for all things that are used by multiple ICs -
   typically the chip-ID of the first chip. This typically matches also
   the driver and file names.
 - use specific chip-ID as a prefix for anything which is specific to
   just one chip.

As a preparatory step to adding the BD72720, add bd71828 prefix to all
commonly usable functions and globals.

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Acked-by: Mark Brown <broonie@kernel.org>

---
Revision history:
 RFCv1 =>:
 - No changes
No functional changes intended.
---
 drivers/regulator/bd71828-regulator.c | 32 +++++++++++++--------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c
index dd871ffe979c..3d18dbfdb84e 100644
--- a/drivers/regulator/bd71828-regulator.c
+++ b/drivers/regulator/bd71828-regulator.c
@@ -28,7 +28,7 @@ struct bd71828_regulator_data {
 	int reg_init_amnt;
 };
 
-static const struct reg_init buck1_inits[] = {
+static const struct reg_init bd71828_buck1_inits[] = {
 	/*
 	 * DVS Buck voltages can be changed by register values or via GPIO.
 	 * Use register accesses by default.
@@ -40,7 +40,7 @@ static const struct reg_init buck1_inits[] = {
 	},
 };
 
-static const struct reg_init buck2_inits[] = {
+static const struct reg_init bd71828_buck2_inits[] = {
 	{
 		.reg = BD71828_REG_PS_CTRL_1,
 		.mask = BD71828_MASK_DVS_BUCK2_CTRL,
@@ -48,7 +48,7 @@ static const struct reg_init buck2_inits[] = {
 	},
 };
 
-static const struct reg_init buck6_inits[] = {
+static const struct reg_init bd71828_buck6_inits[] = {
 	{
 		.reg = BD71828_REG_PS_CTRL_1,
 		.mask = BD71828_MASK_DVS_BUCK6_CTRL,
@@ -56,7 +56,7 @@ static const struct reg_init buck6_inits[] = {
 	},
 };
 
-static const struct reg_init buck7_inits[] = {
+static const struct reg_init bd71828_buck7_inits[] = {
 	{
 		.reg = BD71828_REG_PS_CTRL_1,
 		.mask = BD71828_MASK_DVS_BUCK7_CTRL,
@@ -102,9 +102,9 @@ static int buck_set_hw_dvs_levels(struct device_node *np,
 	return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap);
 }
 
-static int ldo6_parse_dt(struct device_node *np,
-			 const struct regulator_desc *desc,
-			 struct regulator_config *cfg)
+static int bd71828_ldo6_parse_dt(struct device_node *np,
+				 const struct regulator_desc *desc,
+				 struct regulator_config *cfg)
 {
 	int ret, i;
 	uint32_t uv = 0;
@@ -212,8 +212,8 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
 			 */
 			.lpsr_on_mask = BD71828_MASK_LPSR_EN,
 		},
-		.reg_inits = buck1_inits,
-		.reg_init_amnt = ARRAY_SIZE(buck1_inits),
+		.reg_inits = bd71828_buck1_inits,
+		.reg_init_amnt = ARRAY_SIZE(bd71828_buck1_inits),
 	},
 	{
 		.desc = {
@@ -253,8 +253,8 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
 			.lpsr_reg = BD71828_REG_BUCK2_SUSP_VOLT,
 			.lpsr_mask = BD71828_MASK_BUCK1267_VOLT,
 		},
-		.reg_inits = buck2_inits,
-		.reg_init_amnt = ARRAY_SIZE(buck2_inits),
+		.reg_inits = bd71828_buck2_inits,
+		.reg_init_amnt = ARRAY_SIZE(bd71828_buck2_inits),
 	},
 	{
 		.desc = {
@@ -399,8 +399,8 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
 			.lpsr_reg = BD71828_REG_BUCK6_SUSP_VOLT,
 			.lpsr_mask = BD71828_MASK_BUCK1267_VOLT,
 		},
-		.reg_inits = buck6_inits,
-		.reg_init_amnt = ARRAY_SIZE(buck6_inits),
+		.reg_inits = bd71828_buck6_inits,
+		.reg_init_amnt = ARRAY_SIZE(bd71828_buck6_inits),
 	},
 	{
 		.desc = {
@@ -440,8 +440,8 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
 			.lpsr_reg = BD71828_REG_BUCK7_SUSP_VOLT,
 			.lpsr_mask = BD71828_MASK_BUCK1267_VOLT,
 		},
-		.reg_inits = buck7_inits,
-		.reg_init_amnt = ARRAY_SIZE(buck7_inits),
+		.reg_inits = bd71828_buck7_inits,
+		.reg_init_amnt = ARRAY_SIZE(bd71828_buck7_inits),
 	},
 	{
 		.desc = {
@@ -633,7 +633,7 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
 			 * LDO6 only supports enable/disable for all states.
 			 * Voltage for LDO6 is fixed.
 			 */
-			.of_parse_cb = ldo6_parse_dt,
+			.of_parse_cb = bd71828_ldo6_parse_dt,
 		},
 	}, {
 		.desc = {
-- 
2.51.1


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox