Devicetree
 help / color / mirror / Atom feed
* [PATCH v10 4/6] pinctrl: add NXP MC33978/MC34978 pinctrl driver
From: Oleksij Rempel @ 2026-04-01 16:25 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Peter Rosin, Linus Walleij
  Cc: David Jander, Oleksij Rempel, kernel, linux-kernel, devicetree,
	linux-hwmon, linux-gpio
In-Reply-To: <20260401162510.1706073-1-o.rempel@pengutronix.de>

From: David Jander <david@protonic.nl>

Add pin control and GPIO driver for the NXP MC33978/MC34978 Multiple
Switch Detection Interface (MSDI) devices.

This driver exposes the 22 mechanical switch detection inputs (14
Switch-to-Ground, 8 Programmable) as standard GPIOs.

Key features implemented:
- GPIO read/write: Translates physical switch states (open/closed)
  to logical GPIO levels based on the configured switch topology
  (Switch-to-Ground vs. Switch-to-Battery).
- Emulated Output: Allows setting pins "high" or "low" by manipulating
  the tri-state registers and hardware pull topologies.
- Interrupt routing: Proxies GPIO interrupt requests to the irq_domain
  managed by the parent MFD core driver via a hierarchical irq_chip.

Signed-off-by: David Jander <david@protonic.nl>
Co-developed-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Linus Walleij <linusw@kernel.org>
---
changes v10:
- Replace state macros with enum mc33978_bias_state and enum
  mc33978_drive_mode for better type safety.
- Add cached_drive array to isolate output drive topology from the input
  bias configuration (cached_bias).
- Decouple set_pull and set_drive functions to fix state leakage when
  switching between input and output modes.
- Add explicit comments explaining how hardware wetting currents are
  used to emulate standard pinctrl drive/pull modes.
- Initialize both bias and drive state caches directly from hardware
  registers during driver probe.
changes v9:
- Resolve probe fwnode directly from parent (`dev_fwnode(dev->parent)`) and
  fail early with `-ENODEV` if the parent firmware node is missing.
- Set child device node from this validated parent fwnode.
- Replace mutex_init() with devm_mutex_init()
- Add gpiochip_disable_irq/enable_irq calls in mask/unmask callbacks
  for proper gpiolib IRQ state tracking with IRQCHIP_IMMUTABLE
- Set DOMAIN_BUS_WIRED token for GPIO IRQ domain to distinguish from
  parent MFD domain sharing same fwnode
- Add explanatory comment about fwnode sharing and bus token isolation
  to prevent domain shadowing concerns
- select GPIOLIB_IRQCHIP and IRQ_DOMAIN_HIERARCHY
changes v8:
- Fix comment documentation to state the driver implements a hierarchical
  irq_chip instead of proxying .to_irq().
- Add missing <linux/irqdomain.h> include.
- Add .irq_set_wake = irq_chip_set_wake_parent to the gpio_irq_chip to
  properly proxy wake-up configuration to the parent domain.
- Replace irq_find_host() with irq_find_matching_fwnode() during probe
  to support parent domain lookup on non-OF platforms.
changes v7:
- Refactor I/O state reading and tri-state updates for SG/SB topologies
- Fix open-drain and open-source pinconf emulation
- Make direction_input a no-op to prevent overriding pinctrl bias
- Add defensive wrappers for IRQ proxying to prevent NULL pointer panics
- Add missing mutex guards to pinconf and get operations
- Convert generic internal variables to u32 and add lockdep assertions
changes v6:
- no changes
changes v5:
- no changes
changes v4:
- add Reviewed-by: Linus Walleij ...
- Replace the of_device_id match table with a platform_device_id table
- Add device_set_node(dev, dev_fwnode(dev->parent)) during probe
- Remove the check for a missing dev->of_node
changes v3:
- replace manual mutex_lock()/mutex_unlock() paths with guard(mutex)
- Unify error checking style by replacing if (ret < 0) with if (ret)
- Migrate from a custom .to_irq callback to a hierarchical gpio_irq_chip
- Implement .irq_bus_lock and .irq_bus_sync_unlock proxies to properly
  cascade SPI bus lock operations to the parent MFD domain
- Set girq->handler to handle_simple_irq
changes v2:
- Translate all remaining German comments to English.
- Remove unnecessary #ifdef CONFIG_OF wrappers around dt_node_to_map.
- Add detailed comments to mc33978_get() and mc33978_get_multiple() explaining
  the hardware comparator logic (1 = closed, 0 = open) and justifying the
  bitwise inversion required to report actual physical voltage levels.
- Add comments to the .set() and .set_config() callbacks explaining why
  gpiolib's standard open-drain emulation (switching to input mode) fails on
  this hardware due to active wetting currents, and why tri-state isolation is
  mandatory.
- Add a comment to mc33978_gpio_to_irq() explaining why it must act as a
  proxy to the parent MFD's irq_domain (shared physical INT_B line with hwmon).
- Drop dummy pin group callbacks (get_groups_count, etc.). This relies on a
  preparatory patch in this series making these callbacks optional in the core.
- Fix debugfs 'pinconf-pins' read errors by correctly returning -ENOTSUPP
  instead of -EOPNOTSUPP for unsupported generic configurations.
- Fix empty 'gpio-ranges' and missing debugfs labels by explicitly calling
  gpiochip_add_pin_range() during probe.
- Eliminate "magic" bitwise math in the wetting current configuration by
  introducing a static lookup array (mc33978_wet_mA).
- Resolve checkpatch.pl strict warnings regarding macro argument reuse by
  converting MC33978_SPSG, MC33978_PINSHIFT, MC33978_WREG, and MC33978_WSHIFT
  to static inline functions.
- Remove artifacts from previous interrupt handling implementations.
- Address minor formatting and whitespace nits.
---
 drivers/pinctrl/Kconfig           |   16 +
 drivers/pinctrl/Makefile          |    1 +
 drivers/pinctrl/pinctrl-mc33978.c | 1008 +++++++++++++++++++++++++++++
 3 files changed, 1025 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-mc33978.c

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index afecd9407f53..64f9c5b1aacb 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -388,6 +388,22 @@ config PINCTRL_MAX77620
 	  function in alternate mode. This driver also configure push-pull,
 	  open drain, FPS slots etc.
 
+config PINCTRL_MC33978
+	tristate "MC33978/MC34978 industrial input controller support"
+	depends on MFD_MC33978
+	select GPIOLIB
+	select GPIOLIB_IRQCHIP
+	select IRQ_DOMAIN_HIERARCHY
+	select GENERIC_PINCONF
+	help
+	  Say Y here to enable support for NXP MC33978/MC34978 Multiple
+	  Switch Detection Interface (MSDI) devices. This driver provides
+	  pinctrl and GPIO interfaces for the 22 mechanical switch inputs
+	  (14 Switch-to-Ground, 8 Programmable).
+
+	  It allows reading switch states, configuring hardware pull
+	  topologies, and handling interrupts for state changes.
+
 config PINCTRL_MCP23S08_I2C
 	tristate
 	select REGMAP_I2C
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f7d5d5f76d0c..afb58fb5a197 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_PINCTRL_XWAY)	+= pinctrl-xway.o
 obj-$(CONFIG_PINCTRL_LPC18XX)	+= pinctrl-lpc18xx.o
 obj-$(CONFIG_PINCTRL_MAX7360)	+= pinctrl-max7360.o
 obj-$(CONFIG_PINCTRL_MAX77620)	+= pinctrl-max77620.o
+obj-$(CONFIG_PINCTRL_MC33978)	+= pinctrl-mc33978.o
 obj-$(CONFIG_PINCTRL_MCP23S08_I2C)	+= pinctrl-mcp23s08_i2c.o
 obj-$(CONFIG_PINCTRL_MCP23S08_SPI)	+= pinctrl-mcp23s08_spi.o
 obj-$(CONFIG_PINCTRL_MCP23S08)	+= pinctrl-mcp23s08.o
diff --git a/drivers/pinctrl/pinctrl-mc33978.c b/drivers/pinctrl/pinctrl-mc33978.c
new file mode 100644
index 000000000000..9a002feebc48
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-mc33978.c
@@ -0,0 +1,1008 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 David Jander <david@protonic.nl>, Protonic Holland
+ * Copyright (C) 2026 Oleksij Rempel <kernel@pengutronix.de>, Pengutronix
+ *
+ * MC33978/MC34978 Multiple Switch Detection Interface - Pinctrl/GPIO Driver
+ *
+ * Provides GPIO and pinctrl interfaces for the 22 switch detection inputs.
+ * Handles digital input reading and wetting current configuration. Analog AMUX
+ * functionality is handled by a separate mux driver.
+ *
+ * GPIO Mapping:
+ * - GPIO 0-13:  SG0-SG13 (Switch-to-Ground inputs)
+ * - GPIO 14-21: SP0-SP7 (Programmable: Switch-to-Ground or Switch-to-Battery)
+ * This is dictated by the READ_IN register where bits [21:14] = SP[7:0]
+ * and bits [13:0] = SG[13:0].
+ *
+ * Register Organization:
+ * Configuration registers are generally paired. The _SP register at offset N
+ * controls SP0-SP7, and the _SG register at offset N+2 controls SG0-SG13.
+ *
+ * Wetting Currents vs. Pull Resistors:
+ * The hardware physically lacks traditional passive pull-up or pull-down
+ * resistors. Instead, it uses active, controllable current regulators
+ * (wetting currents) to detect switch states and clean mechanical contacts.
+ * - Because these are active current sources, specifying an ohmic value for
+ * pull-up/down biases is physically invalid. The driver ignores ohm arguments.
+ * - 8 selectable current values: 2, 6, 8, 10, 12, 14, 16, 20 mA.
+ * - Exposed via the pinconf PIN_CONFIG_DRIVE_STRENGTH parameter (in mA).
+ *
+ * Emulated Outputs:
+ * The hardware lacks traditional push-pull output drivers; it is strictly an
+ * input device. "Outputs" are simulated by toggling the wetting currents and
+ * physically isolating the pins via hardware tri-state registers. Consequently,
+ * consumers should flag outputs with GPIO_OPEN_DRAIN or GPIO_OPEN_SOURCE in
+ * the Device Tree to match the emulated output model.
+ *
+ * Input Detection Mechanics:
+ * This input mechanism relies on the active current regulators rather than
+ * passive hard resistors. For a Switch-to-Ground (SG) pin, the chip sources
+ * a constant current. When the switch is open, the pin voltage floats up to
+ * the battery voltage. When the switch closes, it creates a path to ground;
+ * because the current is strictly regulated, the pin voltage drops sharply
+ * below the internal 4.0V comparator threshold.
+ * The hardware evaluates this and reports an abstract "contact status"
+ * (1 = closed, 0 = open). For SG pins, a closed switch (~0V) reports as '1'.
+ * To align with gpiolib expectations where ~0V equals a physical logical '0',
+ * this driver explicitly inverts the hardware status for all SG-configured
+ * pins before reporting them.
+ *
+ * Interrupts:
+ * The physical INT_B line and threaded IRQ domain are managed centrally by
+ * the parent MFD core. This driver implements a hierarchical irq_chip
+ * to proxy masking/unmasking and configuration to the parent domain.
+ *
+ * Written by David Jander <david@protonic.nl>
+ *
+ * Datasheet:
+ * https://www.nxp.com/docs/en/data-sheet/MC33978.pdf
+ */
+
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/mc33978.h>
+
+#define MC33978_NGPIO		22
+
+/*
+ * Input numbering is dictated by bit-order of the input register:
+ * Inputs 0-13 -> SG0-SG13
+ * Inputs 14-21 -> SP0-SP7
+ */
+#define MC33978_NUM_SG		14
+#define MC33978_SP_MASK		GENMASK(MC33978_NGPIO - 1, MC33978_NUM_SG)
+#define MC33978_SG_MASK		GENMASK(MC33978_NUM_SG - 1, 0)
+#define MC33978_SG_SHIFT	0
+#define MC33978_SP_SHIFT	MC33978_NUM_SG
+
+enum mc33978_bias_state {
+	MC33978_TRISTATE = 0,
+	MC33978_PU,
+	MC33978_PD,
+};
+
+enum mc33978_drive_mode {
+	MC33978_OPEN_SOURCE = 0,
+	MC33978_OPEN_DRAIN,
+};
+
+struct mc33978_pinctrl {
+	struct device *dev;
+	struct regmap *regmap;
+
+	struct irq_domain *domain;
+
+	struct gpio_chip chip;
+	struct pinctrl_dev *pctldev;
+	struct pinctrl_desc pinctrl_desc;
+
+	/*
+	 * Protects multi-register hardware sequences in .set() and atomic
+	 * READ_IN + CONFIG reads in .get()
+	 */
+	struct mutex lock;
+
+	enum mc33978_bias_state cached_bias[MC33978_NGPIO];
+	enum mc33978_drive_mode cached_drive[MC33978_NGPIO];
+};
+
+static const struct pinctrl_pin_desc mc33978_pins[] = {
+	PINCTRL_PIN(0, "sg0"),
+	PINCTRL_PIN(1, "sg1"),
+	PINCTRL_PIN(2, "sg2"),
+	PINCTRL_PIN(3, "sg3"),
+	PINCTRL_PIN(4, "sg4"),
+	PINCTRL_PIN(5, "sg5"),
+	PINCTRL_PIN(6, "sg6"),
+	PINCTRL_PIN(7, "sg7"),
+	PINCTRL_PIN(8, "sg8"),
+	PINCTRL_PIN(9, "sg9"),
+	PINCTRL_PIN(10, "sg10"),
+	PINCTRL_PIN(11, "sg11"),
+	PINCTRL_PIN(12, "sg12"),
+	PINCTRL_PIN(13, "sg13"),
+	PINCTRL_PIN(14, "sp0"),
+	PINCTRL_PIN(15, "sp1"),
+	PINCTRL_PIN(16, "sp2"),
+	PINCTRL_PIN(17, "sp3"),
+	PINCTRL_PIN(18, "sp4"),
+	PINCTRL_PIN(19, "sp5"),
+	PINCTRL_PIN(20, "sp6"),
+	PINCTRL_PIN(21, "sp7"),
+};
+
+static inline bool mc33978_is_sp(unsigned int pin)
+{
+	return pin >= MC33978_NUM_SG;
+}
+
+/* Choose register offset for _SG/_SP registers. reg is always the _SP addr. */
+static inline u8 mc33978_spsg(u8 reg, unsigned int pin)
+{
+	return mc33978_is_sp(pin) ? reg : reg + 2;
+}
+
+/* Get the bit index into the corresponding register */
+static inline unsigned int mc33978_pinshift(unsigned int pin)
+{
+	return mc33978_is_sp(pin) ? pin - MC33978_NUM_SG : pin;
+}
+
+#define MC33978_PINMASK(pin)	BIT(mc33978_pinshift(pin))
+
+/*
+ * Wetting current registers: 3 in total, each pin uses a 3-bit field,
+ * 8 pins per register, except for the last one.
+ */
+static inline u8 mc33978_wreg(u8 reg, unsigned int pin)
+{
+	return reg + (mc33978_is_sp(pin) ? 0 : 2 + 2 * (pin / 8));
+}
+
+static inline unsigned int mc33978_wshift(unsigned int pin)
+{
+	return mc33978_is_sp(pin) ? 3 * (pin - MC33978_NUM_SG) : 3 * (pin % 8);
+}
+
+#define MC33978_WMASK(pin)	(7 << mc33978_wshift(pin))
+
+static int mc33978_read(struct mc33978_pinctrl *mpc, u8 reg, u32 *val)
+{
+	int ret;
+
+	ret = regmap_read(mpc->regmap, reg, val);
+	if (ret)
+		dev_err_ratelimited(mpc->dev, "Regmap read error %d at reg: %02x.\n",
+				    ret, reg);
+	return ret;
+}
+
+static int mc33978_update_bits(struct mc33978_pinctrl *mpc, u8 reg, u32 mask,
+			       u32 val)
+{
+	int ret;
+
+	ret = regmap_update_bits(mpc->regmap, reg, mask, val);
+	if (ret)
+		dev_err_ratelimited(mpc->dev, "Regmap update bits error %d at reg: %02x.\n",
+				    ret, reg);
+	return ret;
+}
+
+static const struct pinctrl_ops mc33978_pinctrl_ops = {
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+	.dt_free_map = pinconf_generic_dt_free_map,
+};
+
+/*
+ * Output Drive Emulation:
+ * The MC33978 is an input device and physically lacks output drivers.
+ * We emulate output drive modes by mapping them to the hardware's wetting
+ * current topologies:
+ * - Open-Source : Maps to Switch-to-Ground (Pull-Up) topology.
+ * - Open-Drain  : Maps to Switch-to-Battery (Pull-Down) topology.
+ *
+ * SG pins (0-13) are physically hardwired to Open-Source.
+ * SP pins (14-21) are configurable via the CONFIG register.
+ */
+static int mc33978_get_drive(struct mc33978_pinctrl *mpc, unsigned int pin,
+			     enum mc33978_drive_mode *mode)
+{
+	u32 data;
+	int ret;
+
+	lockdep_assert_held(&mpc->lock);
+
+	if (!mc33978_is_sp(pin)) {
+		*mode = MC33978_OPEN_SOURCE;
+		return 0;
+	}
+
+	ret = mc33978_read(mpc, MC33978_REG_CONFIG, &data);
+	if (ret)
+		return ret;
+
+	if (data & MC33978_PINMASK(pin))
+		*mode = MC33978_OPEN_DRAIN;
+	else
+		*mode = MC33978_OPEN_SOURCE;
+
+	return 0;
+}
+
+static int mc33978_set_drive(struct mc33978_pinctrl *mpc, unsigned int pin,
+			     enum mc33978_drive_mode mode)
+{
+	u32 mask = MC33978_PINMASK(pin);
+	int ret;
+
+	lockdep_assert_held(&mpc->lock);
+
+	if (!mc33978_is_sp(pin)) {
+		if (mode != MC33978_OPEN_SOURCE)
+			return -EINVAL;
+
+		mpc->cached_drive[pin] = MC33978_OPEN_SOURCE;
+		return 0;
+	}
+
+	ret = mc33978_update_bits(mpc, MC33978_REG_CONFIG, mask,
+				  (mode == MC33978_OPEN_DRAIN) ? mask : 0);
+	if (!ret)
+		mpc->cached_drive[pin] = mode;
+
+	return ret;
+}
+
+/*
+ * Input Bias (Pull) Configuration:
+ * The MC33978 physically lacks passive pull-up/down resistors. Instead,
+ * input bias is achieved using active wetting current sources. We map the
+ * standard pinctrl bias terms to these hardware topologies:
+ * - Pull-Up   : Maps to Switch-to-Ground (SG) topology.
+ * - Pull-Down : Maps to Switch-to-Battery (SB) topology.
+ * - Tri-state : Disables the wetting current (High-Z).
+ *
+ * SG pins (0-13) physically only support Pull-Up.
+ * SP pins (14-21) are configurable via the CONFIG register.
+ * The TRI_SP/TRI_SG registers connect or disconnect these currents.
+ */
+static int mc33978_get_pull(struct mc33978_pinctrl *mpc, unsigned int pin,
+			    enum mc33978_bias_state *val)
+{
+	u32 data;
+	int ret;
+
+	lockdep_assert_held(&mpc->lock);
+
+	ret = mc33978_read(mpc, mc33978_spsg(MC33978_REG_TRI_SP, pin), &data);
+	if (ret)
+		return ret;
+
+	/* Is the pin tri-stated? */
+	if (data & MC33978_PINMASK(pin)) {
+		*val = MC33978_TRISTATE;
+		return 0;
+	}
+
+	/* Pins 0..13 only support pull-up */
+	if (!mc33978_is_sp(pin)) {
+		*val = MC33978_PU;
+		return 0;
+	}
+
+	/* Check pin pull direction for pins 14..21 */
+	ret = mc33978_read(mpc, MC33978_REG_CONFIG, &data);
+	if (ret)
+		return ret;
+
+	if (data & MC33978_PINMASK(pin))
+		*val = MC33978_PD;
+	else
+		*val = MC33978_PU;
+
+	return 0;
+}
+
+static int mc33978_set_pull(struct mc33978_pinctrl *mpc, unsigned int pin,
+			    enum mc33978_bias_state val)
+{
+	u32 mask = MC33978_PINMASK(pin);
+	int ret;
+
+	lockdep_assert_held(&mpc->lock);
+
+	/* SG pins physically lack pull-down current sources */
+	if (val == MC33978_PD && !mc33978_is_sp(pin))
+		return -EINVAL;
+
+	/* Configure direction (Exclusively for SP pins) */
+	if (mc33978_is_sp(pin) && val != MC33978_TRISTATE) {
+		ret = mc33978_update_bits(mpc, MC33978_REG_CONFIG, mask,
+					  (val == MC33978_PD) ? mask : 0);
+		if (ret)
+			return ret;
+	}
+
+	/* Enable current source or set to tri-state  */
+	ret = mc33978_update_bits(mpc, mc33978_spsg(MC33978_REG_TRI_SP, pin),
+				   mask,
+				   (val == MC33978_TRISTATE) ? mask : 0);
+	if (!ret)
+		mpc->cached_bias[pin] = val;
+
+	return ret;
+}
+
+static const unsigned int mc33978_wet_mA[] = { 2, 6, 8, 10, 12, 14, 16, 20 };
+
+static int mc33978_set_ds(struct mc33978_pinctrl *mpc, unsigned int pin,
+			  u32 val)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mc33978_wet_mA); i++) {
+		if (val == mc33978_wet_mA[i]) {
+			return mc33978_update_bits(mpc,
+					mc33978_wreg(MC33978_REG_WET_SP, pin),
+					MC33978_WMASK(pin),
+					i << mc33978_wshift(pin));
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int mc33978_get_ds(struct mc33978_pinctrl *mpc, unsigned int pin,
+			  u32 *val)
+{
+	u32 data;
+	int ret;
+
+	ret = mc33978_read(mpc, mc33978_wreg(MC33978_REG_WET_SP, pin), &data);
+	if (ret)
+		return ret;
+
+	data &= MC33978_WMASK(pin);
+	data >>= mc33978_wshift(pin);
+
+	if (data >= ARRAY_SIZE(mc33978_wet_mA))
+		return -EINVAL;
+
+	*val = mc33978_wet_mA[data];
+
+	return 0;
+}
+
+static int mc33978_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
+			       unsigned long *config)
+{
+	struct mc33978_pinctrl *mpc = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	enum mc33978_bias_state bias;
+	enum mc33978_drive_mode drive;
+	u32 arg, data;
+	int ret;
+
+	guard(mutex)(&mpc->lock);
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_PULL_UP:
+		ret = mc33978_get_pull(mpc, pin, &bias);
+		if (ret)
+			return ret;
+		if (bias != MC33978_PU)
+			return -EINVAL;
+		arg = 0;
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		ret = mc33978_get_pull(mpc, pin, &bias);
+		if (ret)
+			return ret;
+		if (bias != MC33978_PD)
+			return -EINVAL;
+		arg = 0;
+		break;
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		ret = mc33978_get_drive(mpc, pin, &drive);
+		if (ret)
+			return ret;
+		if (drive != MC33978_OPEN_DRAIN)
+			return -EINVAL;
+		arg = 1;
+		break;
+	case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+		ret = mc33978_get_drive(mpc, pin, &drive);
+		if (ret)
+			return ret;
+		if (drive != MC33978_OPEN_SOURCE)
+			return -EINVAL;
+		arg = 1;
+		break;
+	case PIN_CONFIG_BIAS_DISABLE:
+	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+		ret = mc33978_get_pull(mpc, pin, &bias);
+		if (ret)
+			return ret;
+		if (bias != MC33978_TRISTATE)
+			return -EINVAL;
+		arg = 1;
+		break;
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		ret = mc33978_get_ds(mpc, pin, &data);
+		if (ret)
+			return ret;
+		arg = data;
+		break;
+	default:
+		/*
+		 * Ignore checkpatch warning: the pinctrl core specifically
+		 * expects -ENOTSUPP to silently skip unsupported generic
+		 * parameters. Using -EOPNOTSUPP causes debugfs read failures.
+		 */
+		return -ENOTSUPP;
+	}
+
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
+/*
+ * Hardware constraint regarding PIN_CONFIG_BIAS_PULL_UP/DOWN:
+ * The MC33978 utilizes active constant current sources (wetting currents)
+ * rather than passive pull-resistors. Since the equivalent ohmic resistance
+ * scales dynamically with the fluctuating board voltage (VBATP), computing
+ * a static ohm value is physically invalid.
+ * The driver intentionally ignores resistance arguments during configuration
+ * and continuously reports 0 ohms to the pinctrl framework.
+ */
+static int mc33978_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
+			       unsigned long *configs, unsigned int num_configs)
+{
+	struct mc33978_pinctrl *mpc = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param;
+	int ret = 0;
+	u32 arg;
+	int i;
+
+	guard(mutex)(&mpc->lock);
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		/*
+		 * The hardware physically lacks push-pull output drivers.
+		 * By explicitly handling OPEN_DRAIN and OPEN_SOURCE here, we
+		 * signal to gpiolib that we support these modes natively.
+		 *
+		 * This prevents gpiolib from falling back to its software
+		 * emulation, which attempts to achieve High-Z by switching the
+		 * pin to input mode. Because this driver's .direction_input()
+		 * explicitly restores the cached bias, gpiolib's emulation
+		 * would inadvertently turn the wetting current back on instead
+		 * of achieving a High-Z state.
+		 */
+		switch (param) {
+		case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+			ret = mc33978_set_drive(mpc, pin, MC33978_OPEN_SOURCE);
+			break;
+		case PIN_CONFIG_BIAS_PULL_UP:
+			ret = mc33978_set_pull(mpc, pin, MC33978_PU);
+			break;
+		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+			ret = mc33978_set_drive(mpc, pin, MC33978_OPEN_DRAIN);
+			break;
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			ret = mc33978_set_pull(mpc, pin, MC33978_PD);
+			break;
+		/*
+		 * The MC33978 uses active wetting currents rather than passive
+		 * pull-resistors. Disabling the bias (pull-up/down) is
+		 * physically equivalent to putting the pin into a
+		 * high-impedance state. Both actions are achieved by isolating
+		 * the pin via the hardware tri-state registers.
+		 */
+		case PIN_CONFIG_BIAS_DISABLE:
+		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+			ret = mc33978_set_pull(mpc, pin, MC33978_TRISTATE);
+			break;
+		case PIN_CONFIG_DRIVE_STRENGTH_UA:
+			arg /= 1000;
+			fallthrough;
+		case PIN_CONFIG_DRIVE_STRENGTH:
+			ret = mc33978_set_ds(mpc, pin, arg);
+			break;
+		default:
+			/*
+			 * Required by the pinctrl core to safely fall back or
+			 * skip unsupported configs. Do not use -EOPNOTSUPP.
+			 */
+			return -ENOTSUPP;
+		}
+
+		if (ret) {
+			dev_err(mpc->dev, "Failed to set config param %04x for pin %u: %d\n",
+				param, pin, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static const struct pinconf_ops mc33978_pinconf_ops = {
+	.pin_config_get = mc33978_pinconf_get,
+	.pin_config_set = mc33978_pinconf_set,
+	.is_generic = true,
+};
+
+static int mc33978_direction_input(struct gpio_chip *chip, unsigned int offset)
+{
+	struct mc33978_pinctrl *mpc = gpiochip_get_data(chip);
+
+	guard(mutex)(&mpc->lock);
+
+	/*
+	 * The MC33978 lacks physical output drivers; we emulate outputs by
+	 * toggling the hardware tri-state registers to connect or disconnect
+	 * the wetting currents.
+	 *
+	 * A valid input on this chip can be either actively wetted (PU/PD)
+	 * or High-Z (tri-stated). Because our output emulation directly
+	 * hijacks the tri-state register, switching back to input mode requires
+	 * us to explicitly restore the original wetting current topology from
+	 * the cache.
+	 */
+	return mc33978_set_pull(mpc, offset, mpc->cached_bias[offset]);
+}
+
+/*
+ * The hardware evaluates pin voltage against a threshold (default 4.0V)
+ * and reports an abstract contact status (1 = closed, 0 = open):
+ *
+ * SG (Switch-to-Ground) topology (pull-up current source):
+ * - Voltage > Threshold: Switch Open   (HW reports 0) -> Physical High
+ * - Voltage < Threshold: Switch Closed (HW reports 1) -> Physical Low
+ *
+ * SB (Switch-to-Battery) topology (pull-down current source):
+ * - Voltage > Threshold: Switch Closed (HW reports 1) -> Physical High
+ * - Voltage < Threshold: Switch Open   (HW reports 0) -> Physical Low
+ *
+ * We translate this contact status back into physical voltage levels by
+ * inverting the hardware status for all pins operating in SG topology.
+ */
+static int mc33978_read_in_state(struct mc33978_pinctrl *mpc,
+				 unsigned long mask, unsigned long *state)
+{
+	u32 status, inv_mask;
+	u32 config_reg = 0;
+	int ret;
+
+	ret = mc33978_read(mpc, MC33978_REG_READ_IN, &status);
+	if (ret)
+		return ret;
+
+	/* Read CONFIG register only if the requested mask involves SP pins */
+	if (mask & MC33978_SP_MASK) {
+		ret = mc33978_read(mpc, MC33978_REG_CONFIG, &config_reg);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * Create an inversion mask for all pins currently operating in
+	 * Switch-to-Ground (SG) topology. SG pins always have pull-ups.
+	 * For SP pins, CONFIG bit value 0 = Switch-to-Ground (PU),
+	 * CONFIG bit value 1 = Switch-to-Battery (PD).
+	 */
+	inv_mask = MC33978_SG_MASK |
+		   (~(config_reg << MC33978_NUM_SG) & MC33978_SP_MASK);
+
+	*state = (status ^ inv_mask) & mask;
+
+	return 0;
+}
+
+static int mc33978_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct mc33978_pinctrl *mpc = gpiochip_get_data(chip);
+	unsigned long state;
+	int ret;
+
+	guard(mutex)(&mpc->lock);
+
+	ret = mc33978_read_in_state(mpc, BIT(offset), &state);
+	if (ret)
+		return ret;
+
+	return !!(state & BIT(offset));
+}
+
+static int mc33978_get_multiple(struct gpio_chip *chip,
+				unsigned long *mask, unsigned long *bits)
+{
+	struct mc33978_pinctrl *mpc = gpiochip_get_data(chip);
+	unsigned long state;
+	int ret;
+
+	guard(mutex)(&mpc->lock);
+
+	ret = mc33978_read_in_state(mpc, *mask, &state);
+	if (ret)
+		return ret;
+
+	*bits = (*bits & ~*mask) | state;
+
+	return 0;
+}
+
+/*
+ * Emulate output states by routing or isolating active wetting currents.
+ * To turn the line ON, we disable the hardware tri-state (write 0).
+ * To turn the line OFF (High-Z), we enable tri-state (write 1).
+ *
+ * For Open-Source (Pull-Up): value=1 turns it ON, value=0 is High-Z.
+ * For Open-Drain (Pull-Down): value=0 turns it ON, value=1 is High-Z.
+ * We dynamically read the CONFIG register to determine the topology
+ * and invert the bits accordingly for Open-Drain pins.
+ *
+ * Note: The hardware physically lacks push-pull drivers. Toggling outputs
+ * via tri-state isolation may cause transient spikes.
+ */
+static int mc33978_update_tri_state(struct mc33978_pinctrl *mpc, u32 mask,
+				    u32 bits)
+{
+	u32 sgmask = (mask & MC33978_SG_MASK) >> MC33978_SG_SHIFT;
+	u32 sgbits = (bits & MC33978_SG_MASK) >> MC33978_SG_SHIFT;
+	u32 spmask = (mask & MC33978_SP_MASK) >> MC33978_SP_SHIFT;
+	u32 spbits = (bits & MC33978_SP_MASK) >> MC33978_SP_SHIFT;
+	u32 config_reg = 0;
+	int ret = 0;
+
+	if (spmask) {
+		/* Read topology: 1 = PD (Open-Drain), 0 = PU (Open-Source) */
+		ret = mc33978_read(mpc, MC33978_REG_CONFIG, &config_reg);
+		if (ret)
+			return ret;
+
+		/*
+		 * Invert bits for Open-Drain (PD) pins.
+		 * The Open-Drain API contract expects value=1 to be High-Z.
+		 */
+		spbits ^= (config_reg & spmask);
+
+		ret = mc33978_update_bits(mpc, MC33978_REG_TRI_SP, spmask,
+					  ~spbits);
+		if (ret)
+			return ret;
+	}
+
+	/* SG pins are always Pull-Up (Open-Source), no inversion needed */
+	if (sgmask)
+		ret = mc33978_update_bits(mpc, MC33978_REG_TRI_SG, sgmask,
+					  ~sgbits);
+
+	return ret;
+}
+
+static int mc33978_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+	struct mc33978_pinctrl *mpc = gpiochip_get_data(chip);
+	u32 mask = BIT(offset);
+	u32 bits = value ? mask : 0;
+
+	guard(mutex)(&mpc->lock);
+
+	return mc33978_update_tri_state(mpc, mask, bits);
+}
+
+static int mc33978_set_multiple(struct gpio_chip *chip,
+				unsigned long *mask, unsigned long *bits)
+{
+	struct mc33978_pinctrl *mpc = gpiochip_get_data(chip);
+
+	guard(mutex)(&mpc->lock);
+
+	return mc33978_update_tri_state(mpc, *mask, *bits);
+}
+
+static int mc33978_direction_output(struct gpio_chip *chip, unsigned int offset,
+				    int value)
+{
+	struct mc33978_pinctrl *mpc = gpiochip_get_data(chip);
+	u32 mask = BIT(offset);
+	u32 bits = value ? mask : 0;
+	int ret;
+
+	guard(mutex)(&mpc->lock);
+
+	ret = mc33978_set_drive(mpc, offset, mpc->cached_drive[offset]);
+	if (ret)
+		return ret;
+
+	return mc33978_update_tri_state(mpc, mask, bits);
+}
+
+static int mc33978_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
+					      unsigned int child,
+					      unsigned int child_type,
+					      unsigned int *parent,
+					      unsigned int *parent_type)
+{
+	*parent_type = child_type;
+	*parent = child;
+
+	return 0;
+}
+
+/*
+ * Defensive wrappers for hierarchical IRQ proxying.
+ *
+ * gpiolib's hierarchical allocation exposes a lifecycle gap: the child
+ * descriptor is registered before irq_domain_alloc_irqs_parent() fully
+ * instantiates the parent chip.
+ *
+ * During consumer probe (e.g., gpiod_to_irq()), irq_create_fwspec_mapping()
+ * allocates the hierarchy. As part of this, irq_domain_set_info() initializes
+ * the top-level irq_desc and calls __irq_set_handler(). If the irq_desc
+ * requires locking, __irq_get_desc_lock() will invoke the child's
+ * .irq_bus_lock before the parent allocation is complete.
+ *
+ * Upstream generic helpers (e.g., irq_chip_mask_parent) blindly dereference
+ * data->parent_data->chip, causing an immediate NULL pointer panic during
+ * this gap. These wrappers check for a valid parent chip to safely drop
+ * premature locking or masking events while the legacy subsystem hierarchy
+ * is still assembling itself.
+ */
+static void mc33978_gpio_irq_mask(struct irq_data *data)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+	struct irq_data *parent = data->parent_data;
+
+	if (parent && parent->chip && parent->chip->irq_mask)
+		parent->chip->irq_mask(parent);
+	gpiochip_disable_irq(gc, data->hwirq);
+}
+
+static void mc33978_gpio_irq_unmask(struct irq_data *data)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+	struct irq_data *parent = data->parent_data;
+
+	gpiochip_enable_irq(gc, data->hwirq);
+	if (parent && parent->chip && parent->chip->irq_unmask)
+		parent->chip->irq_unmask(parent);
+}
+
+static int mc33978_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+	struct irq_data *parent = data->parent_data;
+
+	if (parent && parent->chip && parent->chip->irq_set_type)
+		return parent->chip->irq_set_type(parent, type);
+
+	return -EINVAL;
+}
+
+static void mc33978_gpio_irq_bus_lock(struct irq_data *data)
+{
+	struct irq_data *parent = data->parent_data;
+
+	if (parent && parent->chip && parent->chip->irq_bus_lock)
+		parent->chip->irq_bus_lock(parent);
+}
+
+static void mc33978_gpio_irq_bus_sync_unlock(struct irq_data *data)
+{
+	struct irq_data *parent = data->parent_data;
+
+	if (parent && parent->chip && parent->chip->irq_bus_sync_unlock)
+		parent->chip->irq_bus_sync_unlock(parent);
+}
+
+static const struct irq_chip mc33978_gpio_irqchip = {
+	.name = "mc33978-gpio",
+	.irq_mask = mc33978_gpio_irq_mask,
+	.irq_unmask = mc33978_gpio_irq_unmask,
+	.irq_set_type = mc33978_gpio_irq_set_type,
+	.irq_bus_lock = mc33978_gpio_irq_bus_lock,
+	.irq_bus_sync_unlock = mc33978_gpio_irq_bus_sync_unlock,
+	.irq_set_wake = irq_chip_set_wake_parent,
+	.flags = IRQCHIP_IMMUTABLE,
+	GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static void mc33978_init_gpio_chip(struct mc33978_pinctrl *mpc,
+				   struct device *dev)
+{
+	struct gpio_irq_chip *girq;
+
+	mpc->chip.label = dev_name(dev);
+	mpc->chip.direction_input = mc33978_direction_input;
+	mpc->chip.get = mc33978_get;
+	mpc->chip.get_multiple = mc33978_get_multiple;
+	mpc->chip.direction_output = mc33978_direction_output;
+	mpc->chip.set = mc33978_set;
+	mpc->chip.set_multiple = mc33978_set_multiple;
+	mpc->chip.set_config = gpiochip_generic_config;
+
+	mpc->chip.base = -1;
+	mpc->chip.ngpio = MC33978_NGPIO;
+	mpc->chip.can_sleep = true;
+	mpc->chip.parent = dev;
+	mpc->chip.owner = THIS_MODULE;
+
+	girq = &mpc->chip.irq;
+	gpio_irq_chip_set_chip(girq, &mc33978_gpio_irqchip);
+	/*
+	 * Share parent's DT fwnode. This does NOT cause IRQ domain shadowing
+	 * because the parent MFD domain uses DOMAIN_BUS_NEXUS while this GPIO
+	 * domain will use DOMAIN_BUS_WIRED (set after gpiochip registration).
+	 * Domain lookups match on both fwnode AND bus_token, ensuring proper
+	 * domain isolation. See crystalcove GPIO driver for similar pattern.
+	 */
+	girq->fwnode = dev_fwnode(dev);
+	girq->parent_domain = mpc->domain;
+	girq->child_to_parent_hwirq = mc33978_gpio_child_to_parent_hwirq;
+	girq->handler = handle_simple_irq;
+	girq->default_type = IRQ_TYPE_NONE;
+}
+
+static void mc33978_init_pinctrl_desc(struct mc33978_pinctrl *mpc,
+				      struct device *dev)
+{
+	mpc->pinctrl_desc.name = dev_name(dev);
+
+	mpc->pinctrl_desc.pctlops = &mc33978_pinctrl_ops;
+	mpc->pinctrl_desc.confops = &mc33978_pinconf_ops;
+	mpc->pinctrl_desc.pins = mc33978_pins;
+	mpc->pinctrl_desc.npins = MC33978_NGPIO;
+	mpc->pinctrl_desc.owner = THIS_MODULE;
+}
+
+static int mc33978_init_cached_bias(struct mc33978_pinctrl *mpc)
+{
+	int i;
+
+	guard(mutex)(&mpc->lock);
+
+	for (i = 0; i < MC33978_NGPIO; i++) {
+		enum mc33978_bias_state val;
+		int ret;
+
+		ret = mc33978_get_pull(mpc, i, &val);
+		if (ret)
+			return ret;
+
+		mpc->cached_bias[i] = val;
+	}
+
+	return 0;
+}
+
+static int mc33978_init_cached_drive(struct mc33978_pinctrl *mpc)
+{
+	int i;
+
+	guard(mutex)(&mpc->lock);
+
+	for (i = 0; i < MC33978_NGPIO; i++) {
+		enum mc33978_drive_mode mode;
+		int ret;
+
+		ret = mc33978_get_drive(mpc, i, &mode);
+		if (ret)
+			return ret;
+
+		mpc->cached_drive[i] = mode;
+	}
+
+	return 0;
+}
+
+static int mc33978_pinctrl_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct fwnode_handle *fwnode;
+	struct mc33978_pinctrl *mpc;
+	int ret;
+
+	fwnode = dev_fwnode(dev->parent);
+	if (!fwnode)
+		return dev_err_probe(dev, -ENODEV,
+				     "Missing parent firmware node\n");
+
+	device_set_node(dev, fwnode);
+
+	mpc = devm_kzalloc(dev, sizeof(*mpc), GFP_KERNEL);
+	if (!mpc)
+		return -ENOMEM;
+
+	mpc->dev = dev;
+
+	mpc->regmap = dev_get_regmap(dev->parent, NULL);
+	if (!mpc->regmap)
+		return dev_err_probe(dev, -ENODEV, "Failed to get parent regmap\n");
+
+	/* Find parent MFD IRQ domain (uses DOMAIN_BUS_NEXUS token) */
+	mpc->domain = irq_find_matching_fwnode(fwnode, DOMAIN_BUS_NEXUS);
+	if (!mpc->domain)
+		return dev_err_probe(dev, -ENODEV, "Failed to find parent IRQ domain\n");
+
+	ret = devm_mutex_init(dev, &mpc->lock);
+	if (ret)
+		return ret;
+
+	ret = mc33978_init_cached_bias(mpc);
+	if (ret)
+		return ret;
+
+	ret = mc33978_init_cached_drive(mpc);
+	if (ret)
+		return ret;
+
+	mc33978_init_gpio_chip(mpc, dev);
+	mc33978_init_pinctrl_desc(mpc, dev);
+
+	mpc->pctldev = devm_pinctrl_register(dev, &mpc->pinctrl_desc, mpc);
+	if (IS_ERR(mpc->pctldev))
+		return dev_err_probe(dev, PTR_ERR(mpc->pctldev),
+				     "can't register pinctrl\n");
+
+	ret = devm_gpiochip_add_data(dev, &mpc->chip, mpc);
+	if (ret)
+		return dev_err_probe(dev, ret, "can't add GPIO chip\n");
+
+	/*
+	 * Distinguish GPIO IRQ domain from parent MFD domain sharing the same
+	 * fwnode. Matches the pattern used by other GPIO drivers (e.g.,
+	 * crystalcove). DOMAIN_BUS_WIRED indicates this domain represents
+	 * actual GPIO pin interrupts (wired lines).
+	 */
+	irq_domain_update_bus_token(mpc->chip.irq.domain, DOMAIN_BUS_WIRED);
+
+	ret = gpiochip_add_pin_range(&mpc->chip, dev_name(dev), 0, 0,
+				     MC33978_NGPIO);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to add pin range\n");
+
+	platform_set_drvdata(pdev, mpc);
+
+	return 0;
+}
+
+static const struct platform_device_id mc33978_pinctrl_id[] = {
+	{ "mc33978-pinctrl", },
+	{ "mc34978-pinctrl", },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, mc33978_pinctrl_id);
+
+static struct platform_driver mc33978_pinctrl_driver = {
+	.driver = {
+		.name = "mc33978-pinctrl",
+	},
+	.probe = mc33978_pinctrl_probe,
+	.id_table = mc33978_pinctrl_id,
+};
+module_platform_driver(mc33978_pinctrl_driver);
+
+MODULE_AUTHOR("David Jander <david@protonic.nl>");
+MODULE_DESCRIPTION("NXP MC33978/MC34978 pinctrl driver");
+MODULE_LICENSE("GPL");
-- 
2.47.3


^ permalink raw reply related

* [PATCH v10 6/6] mux: add NXP MC33978/MC34978 AMUX driver
From: Oleksij Rempel @ 2026-04-01 16:25 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Peter Rosin, Linus Walleij
  Cc: Oleksij Rempel, kernel, linux-kernel, devicetree, linux-hwmon,
	linux-gpio, David Jander
In-Reply-To: <20260401162510.1706073-1-o.rempel@pengutronix.de>

Add a mux-control driver for the 24-to-1 analog multiplexer (AMUX)
embedded in the NXP MC33978/MC34978 Multiple Switch Detection
Interface (MSDI) devices.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v10:
- no chnages
changes v9:
- rename mc33978-mux to mux-mc33978 in the Kconfig help
- fail if fwnode is NULL
changes v8:
- no chnages
changes v7:
- Simplify the return path and local variable assignment in
  mc33978_mux_set().
- Change idle_state to a signed integer to properly handle negative MUX
  subsystem constants.
- Default to MUX_IDLE_AS_IS when the "idle-state" device tree property
  is missing.
- Explicitly reject MUX_IDLE_DISCONNECT since the hardware does not
  support disconnecting the multiplexer.
changes v6:
- parse optional idle-state property
- validate idle-state against available AMUX channels
- lower-case probe error messages
changes v5:
- no changes
changes v4:
- no changes
changes v3:
- no changes
changes v2:
- Add missing <linux/err.h> include.
- Add platform_device_id table
---
 drivers/mux/Kconfig       |  14 ++++
 drivers/mux/Makefile      |   2 +
 drivers/mux/mc33978-mux.c | 141 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 157 insertions(+)
 create mode 100644 drivers/mux/mc33978-mux.c

diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
index c68132e38138..ffe92a714096 100644
--- a/drivers/mux/Kconfig
+++ b/drivers/mux/Kconfig
@@ -45,6 +45,20 @@ config MUX_GPIO
 	  To compile the driver as a module, choose M here: the module will
 	  be called mux-gpio.
 
+config MUX_MC33978
+	tristate "NXP MC33978/MC34978 Analog Multiplexer"
+	depends on MFD_MC33978
+	help
+	  MC33978/MC34978 24-to-1 analog multiplexer (AMUX) driver.
+
+	  This driver provides mux-control for the analog multiplexer,
+	  which can route switch voltages, temperature, and battery voltage
+	  to an external ADC. Typically used with IIO ADC drivers to measure
+	  analog values from the 22 switch inputs plus temperature and VBATP.
+
+	  To compile the driver as a module, choose M here: the module will
+	  be called mux-mc33978.
+
 config MUX_MMIO
 	tristate "MMIO/Regmap register bitfield-controlled Multiplexer"
 	depends on OF
diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
index 6e9fa47daf56..339c44b4d4f4 100644
--- a/drivers/mux/Makefile
+++ b/drivers/mux/Makefile
@@ -7,10 +7,12 @@ mux-core-objs			:= core.o
 mux-adg792a-objs		:= adg792a.o
 mux-adgs1408-objs		:= adgs1408.o
 mux-gpio-objs			:= gpio.o
+mux-mc33978-objs		:= mc33978-mux.o
 mux-mmio-objs			:= mmio.o
 
 obj-$(CONFIG_MULTIPLEXER)	+= mux-core.o
 obj-$(CONFIG_MUX_ADG792A)	+= mux-adg792a.o
 obj-$(CONFIG_MUX_ADGS1408)	+= mux-adgs1408.o
 obj-$(CONFIG_MUX_GPIO)		+= mux-gpio.o
+obj-$(CONFIG_MUX_MC33978)	+= mux-mc33978.o
 obj-$(CONFIG_MUX_MMIO)		+= mux-mmio.o
diff --git a/drivers/mux/mc33978-mux.c b/drivers/mux/mc33978-mux.c
new file mode 100644
index 000000000000..b44c862f0dbe
--- /dev/null
+++ b/drivers/mux/mc33978-mux.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2026 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
+/*
+ * MC33978/MC34978 Analog Multiplexer (AMUX) Driver
+ *
+ * This driver provides mux-control for the 24-to-1 analog multiplexer.
+ * The AMUX routes one of the following signals to the external AMUX pin:
+ * - Channels 0-13: SG0-SG13 switch voltages
+ * - Channels 14-21: SP0-SP7 switch voltages
+ * - Channel 22: Internal temperature diode
+ * - Channel 23: Battery voltage (VBATP)
+ *
+ * Consumer drivers (typically IIO ADC drivers) use the mux-control
+ * subsystem to select which signal to measure.
+ *
+ * Architecture:
+ * The MC33978 does not have an internal ADC. Instead, it routes analog
+ * signals to an external AMUX pin that must be connected to an external
+ * ADC (such as the SoC's internal ADC). The IIO subsystem is responsible
+ * for coordinating the mux selection and ADC sampling.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mux/driver.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/mc33978.h>
+
+/* AMUX_CTRL register field definitions */
+#define MC33978_AMUX_CTRL_MASK	GENMASK(5, 0)	/* 6-bit channel select */
+
+struct mc33978_mux_priv {
+	struct device *dev;
+	struct regmap *map;
+};
+
+static int mc33978_mux_set(struct mux_control *mux, int state)
+{
+	struct mux_chip *mux_chip = mux->chip;
+	struct mc33978_mux_priv *priv = mux_chip_priv(mux_chip);
+	int ret;
+
+	if (state < 0 || state >= MC33978_NUM_AMUX_CH)
+		return -EINVAL;
+
+	ret = regmap_update_bits(priv->map, MC33978_REG_AMUX_CTRL,
+				 MC33978_AMUX_CTRL_MASK, state);
+	if (ret)
+		dev_err(priv->dev, "failed to set AMUX channel %d: %d\n",
+			state, ret);
+
+	return ret;
+}
+
+static const struct mux_control_ops mc33978_mux_ops = {
+	.set = mc33978_mux_set,
+};
+
+static int mc33978_mux_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mc33978_mux_priv *priv;
+	struct fwnode_handle *fwnode;
+	struct mux_chip *mux_chip;
+	struct mux_control *mux;
+	s32 idle_state;
+	int ret;
+
+	mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*priv));
+	if (IS_ERR(mux_chip))
+		return dev_err_probe(dev, PTR_ERR(mux_chip), "failed to allocate mux chip\n");
+
+	fwnode = dev_fwnode(dev->parent);
+	if (!fwnode)
+		return dev_err_probe(dev, -ENODEV, "missing parent firmware node\n");
+
+	/* Borrow the parent's firmware node so consumers can find this mux chip */
+	device_set_node(&mux_chip->dev, fwnode);
+
+	priv = mux_chip_priv(mux_chip);
+	priv->dev = dev;
+
+	priv->map = dev_get_regmap(dev->parent, NULL);
+	if (!priv->map)
+		return dev_err_probe(dev, -ENODEV, "failed to get parent regmap\n");
+
+	mux_chip->ops = &mc33978_mux_ops;
+
+	mux = &mux_chip->mux[0];
+	mux->states = MC33978_NUM_AMUX_CH;
+
+	ret = device_property_read_u32(&mux_chip->dev, "idle-state",
+				       (u32 *)&idle_state);
+	if (ret < 0 && ret != -EINVAL) {
+		return dev_err_probe(dev, ret, "failed to parse idle-state\n");
+	} else if (ret == -EINVAL) {
+		mux->idle_state = MUX_IDLE_AS_IS;
+	} else {
+		if (idle_state == MUX_IDLE_DISCONNECT)
+			return dev_err_probe(dev, -EINVAL,
+					     "idle-disconnect not supported by hardware\n");
+		if (idle_state != MUX_IDLE_AS_IS &&
+		    (idle_state < 0 || idle_state >= MC33978_NUM_AMUX_CH))
+			return dev_err_probe(dev, -EINVAL, "invalid idle-state %d\n",
+					     idle_state);
+		mux->idle_state = idle_state;
+	}
+
+	ret = devm_mux_chip_register(dev, mux_chip);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register mux chip\n");
+
+	platform_set_drvdata(pdev, mux_chip);
+
+	return 0;
+}
+
+static const struct platform_device_id mc33978_mux_id[] = {
+	{ "mc33978-mux", },
+	{ "mc34978-mux", },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, mc33978_mux_id);
+
+static struct platform_driver mc33978_mux_driver = {
+	.driver = {
+		.name = "mc33978-mux",
+	},
+	.probe = mc33978_mux_probe,
+	.id_table = mc33978_mux_id,
+};
+module_platform_driver(mc33978_mux_driver);
+
+MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>");
+MODULE_DESCRIPTION("NXP MC33978/MC34978 Analog Multiplexer Driver");
+MODULE_LICENSE("GPL");
-- 
2.47.3


^ permalink raw reply related

* [PATCH v10 1/6] dt-bindings: pinctrl: add NXP MC33978/MC34978 MSDI
From: Oleksij Rempel @ 2026-04-01 16:25 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Peter Rosin, Linus Walleij
  Cc: Oleksij Rempel, kernel, linux-kernel, devicetree, linux-hwmon,
	linux-gpio, David Jander
In-Reply-To: <20260401162510.1706073-1-o.rempel@pengutronix.de>

Add device tree binding documentation for the NXP MC33978 and MC34978
Multiple Switch Detection Interface (MSDI) devices.

The MC33978 and MC34978 differ primarily in their operating temperature
ranges. While not software-detectable, providing specific compatible
strings allows the hwmon subsystem to correctly interpret thermal
thresholds and hardware faults.

These ICs monitor up to 22 mechanical switch contacts in automotive and
industrial environments. They provide configurable wetting currents to
break through contact oxidation and feature extensive hardware
protection against thermal overload and voltage transients (load
dumps/brown-outs).

The device interfaces via SPI. While it provides multiple functions, its
primary hardware purpose is pin/switch control. To accurately represent
the hardware as a single physical integrated circuit without unnecessary
DT overhead, all functions are flattened into a single pinctrl node:
- pinctrl: Exposing the 22 switch inputs (SG/SP pins) as a GPIO controller
  and managing their pin configurations.
- hwmon: Exposing critical hardware faults (OT, OV, UV) and static
  voltage/temperature thresholds.
- mux: Controlling the 24-to-1 analog multiplexer to route pin voltages,
  internal temperature, or battery voltage to an external SoC ADC.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Linus Walleij <linusw@kernel.org>
---
changes v10:
- no changes
changes v9:
- no changes
changes v8:
- Update IRQ_TYPE_* macros include path reference in documentation from
  interrupt-controller.h to dt-bindings/interrupt-controller/irq.h.
- Add bias-disable, drive-open-drain, drive-open-source, and drive-strength
  to the list of supported pin configuration properties.
changes v7:
- no changes
changes v6:
- add Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
- add Reviewed-by: Linus Walleij <linusw@kernel.org>
changes v5:
- Commit Message: Added justification for distinct compatible strings
  based on temperature ranges.
- Restricted pins property to an explicit enum of valid hardware pins
changes v4:
- Drop the standalone mfd/nxp,mc33978.yaml schema entirely.
- Move the unified device binding to bindings/pinctrl/nxp,mc33978.yaml,
- Remove the dedicated child node compatible strings (nxp,mc33978-pinctrl).
- Flatten the pinctrl/gpio properties directly into the main SPI device
  node.
changes v3:
- Drop regular expression pattern from pinctrl child node and define
  it as a standard property
- Reorder required properties list in MFD binding
- Remove stray blank line from the MFD binding devicetree example
- Replace unevaluatedProperties with additionalProperties in the pinctrl
  binding
changes v2:
- Squashed MFD, pinctrl, hwmon, and mux bindings into a single patch
- Removed the empty hwmon child node
- Folded the mux-controller node into the parent MFD node
- Added vbatp-supply and vddq-supply to the required properties block
- Changed the example node name from mc33978@0 to gpio@0
- Removed unnecessary literal block scalars (|) from descriptions
- Documented SG, SP, and SB pin acronyms in the pinctrl description
- Added consumer polarity guidance (GPIO_ACTIVE_LOW/HIGH) for SG/SB
  inputs, with a note on output circuit dependency
- Updated commit message
---
 .../bindings/pinctrl/nxp,mc33978.yaml         | 158 ++++++++++++++++++
 1 file changed, 158 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/nxp,mc33978.yaml

diff --git a/Documentation/devicetree/bindings/pinctrl/nxp,mc33978.yaml b/Documentation/devicetree/bindings/pinctrl/nxp,mc33978.yaml
new file mode 100644
index 000000000000..2a3c565c3c03
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/nxp,mc33978.yaml
@@ -0,0 +1,158 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/nxp,mc33978.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP MC33978/MC34978 Multiple Switch Detection Interface
+
+maintainers:
+  - David Jander <david@protonic.nl>
+  - Oleksij Rempel <o.rempel@pengutronix.de>
+
+description: |
+  The MC33978 and MC34978 are Multiple Switch Detection Interface (MSDI)
+  devices with 22 switch inputs, integrated fault detection, and analog
+  multiplexer (AMUX) for voltage/temperature monitoring.
+
+  Pin numbering:
+  - Pins 0-13: SG0-SG13 (Switch-to-Ground inputs). These pins monitor
+    contacts closed to ground and typically require GPIO_ACTIVE_LOW
+    flags when used as digital inputs.
+  - Pins 14-21: SP0-SP7 (Programmable inputs). These can be configured
+    as SG (Switch-to-Ground) or SB (Switch-to-Battery) inputs. SB
+    inputs monitor contacts closed to the battery voltage and typically
+    require GPIO_ACTIVE_HIGH flags when used as digital inputs.
+
+  Output Emulation:
+  The hardware lacks standard push-pull output drivers. Outputs are emulated
+  by toggling the programmable wetting current sources (acting as pull-ups
+  or pull-downs) and the hardware tri-state registers. Because of this
+  physical constraint:
+  - Consumers using pins as outputs MUST flag them with GPIO_OPEN_DRAIN or
+    GPIO_OPEN_SOURCE in the device tree.
+  - Push-pull configurations are physically unsupported.
+  - The active polarity depends entirely on the external circuit (e.g., how
+    an LED is wired) and must be flagged accordingly by the consumer.
+
+allOf:
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+  compatible:
+    enum:
+      - nxp,mc33978
+      - nxp,mc34978
+
+  reg:
+    maxItems: 1
+    description: SPI chip select number
+
+  spi-max-frequency:
+    maximum: 8000000
+    description: Maximum SPI clock frequency (up to 8 MHz)
+
+  interrupts:
+    maxItems: 1
+    description:
+      INT_B pin interrupt. Active-low, indicates pin state changes or
+      fault conditions.
+
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 2
+    description:
+      First cell is the IRQ number (0-21 for pins, 22 for faults).
+      Second cell is the trigger type (IRQ_TYPE_* from dt-bindings/interrupt-controller/irq.h).
+
+  '#mux-control-cells':
+    const: 0
+    description:
+      Present if the device AMUX selector is used as a mux provider.
+      Consumers (e.g. io-channel-mux) must provide settle-time-us for the
+      external ADC sampling path.
+
+  vddq-supply:
+    description: Digital supply voltage
+
+  vbatp-supply:
+    description: Battery/power supply
+
+  gpio-controller: true
+
+  '#gpio-cells':
+    const: 2
+
+  ngpios:
+    const: 22
+
+patternProperties:
+  '^.*-grp$':
+    type: object
+    $ref: /schemas/pinctrl/pincfg-node.yaml#
+    additionalProperties: false
+    description: Pin configuration subnodes.
+    properties:
+      pins:
+        items:
+          enum: [sg0, sg1, sg2, sg3, sg4, sg5, sg6, sg7, sg8, sg9,
+                 sg10, sg11, sg12, sg13, sp0, sp1, sp2, sp3,
+                 sp4, sp5, sp6, sp7]
+
+      bias-pull-up: true
+      bias-pull-down: true
+      bias-high-impedance: true
+      bias-disable: true
+      drive-open-drain: true
+      drive-open-source: true
+      drive-strength:
+        enum: [2, 6, 8, 10, 12, 14, 16, 20]
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-controller
+  - '#interrupt-cells'
+  - vddq-supply
+  - vbatp-supply
+  - gpio-controller
+  - '#gpio-cells'
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/gpio/gpio.h>
+
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        msdi: gpio@0 {
+            compatible = "nxp,mc33978";
+            reg = <0>;
+            spi-max-frequency = <4000000>;
+
+            interrupt-parent = <&gpiog>;
+            interrupts = <9 IRQ_TYPE_LEVEL_LOW>;
+            interrupt-controller;
+            #interrupt-cells = <2>;
+
+            vddq-supply = <&reg_3v3>;
+            vbatp-supply = <&reg_12v>;
+
+            #mux-control-cells = <0>;
+
+            gpio-controller;
+            #gpio-cells = <2>;
+            ngpios = <22>;
+
+            door-grp {
+                pins = "sg0";
+                bias-high-impedance;
+            };
+        };
+    };
-- 
2.47.3


^ permalink raw reply related

* [PATCH v10 0/6] mfd: Add support for NXP MC33978/MC34978 MSDI
From: Oleksij Rempel @ 2026-04-01 16:25 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Peter Rosin, Linus Walleij
  Cc: Oleksij Rempel, kernel, linux-kernel, devicetree, linux-hwmon,
	linux-gpio, David Jander

changes v7:
- drop gpiolib irq fix and make pinctrl more robust against NULL point
  dereference.

This series adds support for the NXP MC33978/MC34978 Multiple Switch Detection
Interface (MSDI) via the MFD framework.

Architecture overview:
* mfd: Core driver handling 2-frame pipelined SPI, regulator sequencing, and
  linear irq_domain. Harvests status bits from SPI MISO MSB.
* pinctrl: Exposes 22 physical switch inputs as standard GPIOs. Proxies IRQs to
  the MFD domain.
* hwmon: Exposes thermal limits, VBATP/VDDQ voltage boundaries, and dynamic
  fault alarms.
* mux: Controls the 24-to-1 AMUX routing analog signals (switch voltages,
  temperature, VBATP) to an external ADC.

Initial pinctrl implementation by David Jander, reworked into this MFD
architecture.

Best regards,
Oleksij

David Jander (1):
  pinctrl: add NXP MC33978/MC34978 pinctrl driver

Oleksij Rempel (5):
  dt-bindings: pinctrl: add NXP MC33978/MC34978 MSDI
  mfd: add NXP MC33978/MC34978 core driver
  pinctrl: core: Make pin group callbacks optional for pin-only drivers
  hwmon: add NXP MC33978/MC34978 driver
  mux: add NXP MC33978/MC34978 AMUX driver

 .../bindings/pinctrl/nxp,mc33978.yaml         |  158 +++
 drivers/hwmon/Kconfig                         |   10 +
 drivers/hwmon/Makefile                        |    1 +
 drivers/hwmon/mc33978-hwmon.c                 |  549 +++++++++
 drivers/mfd/Kconfig                           |   15 +
 drivers/mfd/Makefile                          |    2 +
 drivers/mfd/mc33978.c                         | 1079 +++++++++++++++++
 drivers/mux/Kconfig                           |   14 +
 drivers/mux/Makefile                          |    2 +
 drivers/mux/mc33978-mux.c                     |  141 +++
 drivers/pinctrl/Kconfig                       |   16 +
 drivers/pinctrl/Makefile                      |    1 +
 drivers/pinctrl/core.c                        |   41 +-
 drivers/pinctrl/pinconf.c                     |    9 +-
 drivers/pinctrl/pinctrl-mc33978.c             | 1008 +++++++++++++++
 include/linux/mfd/mc33978.h                   |   95 ++
 16 files changed, 3134 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/nxp,mc33978.yaml
 create mode 100644 drivers/hwmon/mc33978-hwmon.c
 create mode 100644 drivers/mfd/mc33978.c
 create mode 100644 drivers/mux/mc33978-mux.c
 create mode 100644 drivers/pinctrl/pinctrl-mc33978.c
 create mode 100644 include/linux/mfd/mc33978.h

--
2.47.3


^ permalink raw reply

* [PATCH v10 3/6] pinctrl: core: Make pin group callbacks optional for pin-only drivers
From: Oleksij Rempel @ 2026-04-01 16:25 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Peter Rosin, Linus Walleij
  Cc: Oleksij Rempel, kernel, linux-kernel, devicetree, linux-hwmon,
	linux-gpio, David Jander
In-Reply-To: <20260401162510.1706073-1-o.rempel@pengutronix.de>

Currently, the pinctrl core strictly requires all drivers to implement
.get_groups_count and .get_group_name callbacks in their pinctrl_ops.

However, for simple pinctrl drivers that act purely as GPIO controllers
and pin-specific configuration proxies, without any concept of muxing or
pin groups, this strict requirement forces the implementation of dummy
callbacks just to satisfy pinctrl_check_ops().

Relax this requirement for pin-only drivers by making the group callbacks
optional when no muxing or group pin configuration support is provided.
Update the core and debugfs helpers to check for the existence of these
callbacks before invoking them.

Drivers that provide muxing or group pin configuration operations still
must implement group enumeration and naming callbacks, and are rejected
at registration time if they do not.

Suggested-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Linus Walleij <linusw@kernel.org>
---
changes v10:
- no changes
changes v9:
- no changes
changes v8:
- no changes
changes v7:
- no changes
changes v6:
- Reject drivers in pinctrl_check_ops() that use pmxops or group confops
  without providing group callbacks.
- Add <linux/pinctrl/pinconf.h> to core.c.
- Revert the unnecessary NULL check in pinconf_show_setting(), since
  group settings are now strictly gated.
- Keep debugfs group listings tolerant of drivers without group callbacks.
changes v5:
- no changes
changes v4:
- add Reviewed-by: Linus Walleij ...
changes v3:
- no changes
---
 drivers/pinctrl/core.c    | 41 ++++++++++++++++++++++++++++++++++-----
 drivers/pinctrl/pinconf.c |  9 +++++++--
 2 files changed, 43 insertions(+), 7 deletions(-)

diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index b5e97689589f..19a9a370d7b9 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -30,6 +30,7 @@
 #include <linux/pinctrl/consumer.h>
 #include <linux/pinctrl/devinfo.h>
 #include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
 #include <linux/pinctrl/pinctrl.h>
 
 #include "core.h"
@@ -621,8 +622,13 @@ static int pinctrl_generic_group_name_to_selector(struct pinctrl_dev *pctldev,
 						  const char *function)
 {
 	const struct pinctrl_ops *ops = pctldev->desc->pctlops;
-	int ngroups = ops->get_groups_count(pctldev);
 	int selector = 0;
+	int ngroups;
+
+	if (!ops->get_groups_count || !ops->get_group_name)
+		return -EINVAL;
+
+	ngroups = ops->get_groups_count(pctldev);
 
 	/* See if this pctldev has this group */
 	while (selector < ngroups) {
@@ -737,8 +743,15 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev,
 			       const char *pin_group)
 {
 	const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
-	unsigned int ngroups = pctlops->get_groups_count(pctldev);
 	unsigned int group_selector = 0;
+	unsigned int ngroups;
+
+	if (!pctlops->get_groups_count || !pctlops->get_group_name) {
+		dev_err(pctldev->dev, "does not support pin groups\n");
+		return -EINVAL;
+	}
+
+	ngroups = pctlops->get_groups_count(pctldev);
 
 	while (group_selector < ngroups) {
 		const char *gname = pctlops->get_group_name(pctldev,
@@ -1769,6 +1782,11 @@ static int pinctrl_groups_show(struct seq_file *s, void *what)
 
 	mutex_lock(&pctldev->mutex);
 
+	if (!ops->get_groups_count || !ops->get_group_name) {
+		mutex_unlock(&pctldev->mutex);
+		return 0;
+	}
+
 	ngroups = ops->get_groups_count(pctldev);
 
 	seq_puts(s, "registered pin groups:\n");
@@ -2049,12 +2067,25 @@ static void pinctrl_remove_device_debugfs(struct pinctrl_dev *pctldev)
 static int pinctrl_check_ops(struct pinctrl_dev *pctldev)
 {
 	const struct pinctrl_ops *ops = pctldev->desc->pctlops;
+	const struct pinconf_ops *confops = pctldev->desc->confops;
+	bool needs_groups = false;
 
-	if (!ops ||
-	    !ops->get_groups_count ||
-	    !ops->get_group_name)
+	if (!ops)
 		return -EINVAL;
 
+	if (pctldev->desc->pmxops)
+		needs_groups = true;
+
+	if (confops && (confops->pin_config_group_get ||
+			confops->pin_config_group_set))
+		needs_groups = true;
+
+	if (needs_groups && (!ops->get_groups_count || !ops->get_group_name)) {
+		dev_err(pctldev->dev,
+			"driver needs group callbacks for mux or group config\n");
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c
index dca963633b5d..81686844dfa5 100644
--- a/drivers/pinctrl/pinconf.c
+++ b/drivers/pinctrl/pinconf.c
@@ -275,7 +275,7 @@ void pinconf_show_setting(struct seq_file *s,
 	case PIN_MAP_TYPE_CONFIGS_GROUP:
 		seq_printf(s, "group %s (%d)",
 			   pctlops->get_group_name(pctldev,
-					setting->data.configs.group_or_pin),
+						   setting->data.configs.group_or_pin),
 			   setting->data.configs.group_or_pin);
 		break;
 	default:
@@ -348,8 +348,13 @@ static int pinconf_groups_show(struct seq_file *s, void *what)
 {
 	struct pinctrl_dev *pctldev = s->private;
 	const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
-	unsigned int ngroups = pctlops->get_groups_count(pctldev);
 	unsigned int selector = 0;
+	unsigned int ngroups;
+
+	if (!pctlops->get_groups_count || !pctlops->get_group_name)
+		return 0;
+
+	ngroups = pctlops->get_groups_count(pctldev);
 
 	seq_puts(s, "Pin config settings per pin group\n");
 	seq_puts(s, "Format: group (name): configs\n");
-- 
2.47.3


^ permalink raw reply related

* Re: [PATCH v2 6/7] MAINTAINERS: add exynos850-pmu.c to Exynos850 entry
From: Sam Protsenko @ 2026-04-01 16:16 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Alexey Klimov, linux-samsung-soc, Peter Griffin,
	André Draszik, Conor Dooley, Alim Akhtar, Tudor Ambarus,
	Rob Herring, Krzysztof Kozlowski, linux-arm-kernel, devicetree,
	linux-kernel
In-Reply-To: <f73173b4-f44a-4d01-abec-3366b6561332@kernel.org>

Hi Krzysztof,

On Wed, Apr 1, 2026 at 9:28 AM Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
> On 01/04/2026 06:51, Alexey Klimov wrote:
> > Update Exynos850 entry to include new file
> > drivers/soc/samsung/exynos850-pmu.c. Add myself as M
> > there.
> >
> > Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
> > ---
> >  MAINTAINERS | 2 ++
> >  1 file changed, 2 insertions(+)
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index e14e6f874e05..4b28e92b4d9b 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -23601,6 +23601,7 @@ F:    include/dt-bindings/clock/samsung,exynos2200-cmu.h
> >
> >  SAMSUNG EXYNOS850 SoC SUPPORT
> >  M:   Sam Protsenko <semen.protsenko@linaro.org>
> > +M:   Alexey Klimov <alexey.klimov@linaro.org>
>
> I am surprised to see this because I did not find many reviews from your
> side before.
>

It was me who advised Alexey to add himself on the list, as he's doing
a lot of Exynos850 related work nowadays, and we expect him to
continue doing that. If you think it's too early, let's get back to
this once there are more patches submitted under his name.

Thanks!

> Please first engage in reviewing of this platform, before assigning
> yourself as a maintainer.
>
> Best regards,
> Krzysztof

^ permalink raw reply

* Re: [PATCH v4 3/4] Input: aw86938 - add driver for Awinic AW86938
From: Dmitry Torokhov @ 2026-04-01 16:11 UTC (permalink / raw)
  To: Luca Weiss
  Cc: Griffin Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio, linux-input,
	devicetree, linux-kernel, linux-arm-msm
In-Reply-To: <DHHWDE7QEOTO.1AQ85UBLO8IQG@fairphone.com>

On Wed, Apr 01, 2026 at 04:44:47PM +0200, Luca Weiss wrote:
> Hi Dmitry,
> 
> On Wed Mar 4, 2026 at 5:56 AM CET, Dmitry Torokhov wrote:
> > On Mon, Mar 02, 2026 at 11:50:27AM +0100, Griffin Kroah-Hartman wrote:
> >> Add support for the I2C-connected Awinic AW86938 LRA haptic driver.
> >> 
> >> The AW86938 has a similar but slightly different register layout. In
> >> particular, the boost mode register values.
> >> The AW86938 also has some extra features that aren't implemented
> >> in this driver yet.
> >> 
> >> Signed-off-by: Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
> >
> > Applied, thank you.
> 
> I'm curious, where did you apply these patches? linux-next doesn't have
> it and I don't see it in your kernel.org repo either.
> https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/
> 
> Did this slip through the cracks or will these patches still appear
> there?

My bad, I think there was a conflict with Dan Carpenter's patch and as a
result the series got stuck in my internal queue. My apologies.

Should be out in 'next' branch now.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 2/4] ASoC: codecs: Add TAS675x quad-channel audio amplifier driver
From: Mark Brown @ 2026-04-01 16:07 UTC (permalink / raw)
  To: Sen Wang
  Cc: linux-sound, lgirdwood, robh, krzk+dt, conor+dt, devicetree,
	perex, tiwai, shenghao-ding, kevin-lu, baojun.xu, niranjan.hy,
	l-badrinarayanan, devarsht, v-singh1, linux-kernel
In-Reply-To: <20260401024210.28542-3-sen@ti.com>

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

On Tue, Mar 31, 2026 at 09:42:07PM -0500, Sen Wang wrote:

> The TAS675x (TAS6754, TAS67524) are quad-channel, digital-input
> Class-D amplifiers with an integrated DSP, controlled over I2C.
> They support I2S and TDM serial audio interfaces.

> +static int tas675x_dapm_sleep_event(struct snd_soc_dapm_widget *widget,
> +				    struct snd_kcontrol *kcontrol, int event)
> +{
> +	struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
> +	int ret = 0;
> +
> +	switch (event) {
> +	case SND_SOC_DAPM_POST_PMU:
> +		ret = pm_runtime_resume_and_get(component->dev);
> +		break;
> +	case SND_SOC_DAPM_PRE_PMD:
> +		pm_runtime_mark_last_busy(component->dev);
> +		pm_runtime_put_autosuspend(component->dev);
> +		break;
> +	}
> +	return ret < 0 ? ret : 0;
> +}

I'm confused what's going on here, there's runtime PM management in the
ASoC core - isn't this just duplicating what's there?

> +static int tas675x_runtime_resume(struct device *dev)
> +{

> +       if (!to_i2c_client(dev)->irq)
> +               schedule_delayed_work(&tas->fault_check_work,
> +                                     msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS));

This is the only place where we start the fault checking but runtime PM
can be disabled in configuration and we also start runtime PM in the
active state so potentially might never suspend and resume.  probe()
should kick off the work as well.

> +static int tas675x_init_device(struct tas675x_priv *tas)
> +{
> +	struct regmap *regmap = tas->regmap;
> +	unsigned int val;
> +	int ret, i;
> +
> +	/* Clear POR fault flag to prevent IRQ storm */
> +	regmap_read(regmap, TAS675X_POWER_FAULT_LATCHED_REG, &val);


> +static int tas675x_i2c_probe(struct i2c_client *client)
> +{

> +	if (client->irq) {
> +		ret = devm_request_threaded_irq(tas->dev, client->irq, NULL,
> +						tas675x_irq_handler,
> +						IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
> +						"tas675x-fault", tas);
> +		if (ret)
> +			return dev_err_probe(tas->dev, ret, "Failed to request IRQ\n");
> +	}

We request the interrupt here...

> +	INIT_DELAYED_WORK(&tas->fault_check_work, tas675x_fault_check_work);

> +	ret = tas675x_power_on(tas);
> +	if (ret)
> +		return ret;

...before we do _power_on() which is what calls _hw_init() and clears any
faults that were latched in the registers.  This means that if there is
something there we'll handle it through the normal interrupt handling
flow and the rest won't buy is much, probably better to reorder the init
vs the interrupt request.

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

^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: gpu: mali-valhall-csf: Document i.MX952 support
From: Krzysztof Kozlowski @ 2026-04-01 16:07 UTC (permalink / raw)
  To: Guangliu Ding, Liviu Dudau
  Cc: Daniel Baluta (OSS), Daniel Almeida, Alice Ryhl, Boris Brezillon,
	Steven Price, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam,
	dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	linux-arm-kernel@lists.infradead.org, Jiyu Yang
In-Reply-To: <AM0PR04MB47075AF2C5863FC665285CA9F350A@AM0PR04MB4707.eurprd04.prod.outlook.com>

On 01/04/2026 13:27, Guangliu Ding wrote:
>>>>
>>>> We discuss only the lack of compatibility in terms of DT, how DT sees
>>>> compatible devices.
>>>>
>>>> And lack of driver code is clear indication that devices are
>>>> compatible in terms how DT understands it. Feel encouraged to bring
>>>> actual arguments in commit msgs in the future.
>>>>
>>>> Best regards,
>>>> Krzysztof
>>>
>>> So the best approach is only reserve "arm,mali-valhall-csf" for now,
>>> since currently there is no need for an additional compatible entry from a DT
>> compatibility perspective.
>>> We can introduce "nxp,imx952-mali" in future commits if hardware or
>>> driver differences actually require it, and include more detailed justification
>> in the commit message. Right?
>>
>> So does that mean you decided not to read writing bindings document?
> 
> Actually, I followed the compatible string of gpu node in imx952.dtsi during
> code work since they share the same GPU IP.
>          gpu: gpu@4d900000 {
>              compatible = "nxp,imx95-mali", "arm,mali-valhall-csf"; > 
> 
> Is this line in writing bindings document that you want to mention about?
> Could you please share more suggestions about the patch optimization?
> 		DO add new compatibles in case there are new features or bugs.

Please read entire file. I feel like you avoid doing this and just ask
me to give you shortcut.

There is more than one point from that trivial writing bindings file
which applies here.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH v12 2/3] of: Factor arguments passed to of_map_id() into a struct
From: Krzysztof Kozlowski @ 2026-04-01 16:01 UTC (permalink / raw)
  To: Frank Li, Vijayanand Jitta
  Cc: Nipun Gupta, Nikhil Agarwal, Joerg Roedel, Will Deacon,
	Robin Murphy, Marc Zyngier, Lorenzo Pieralisi, Thomas Gleixner,
	Saravana Kannan, Richard Zhu, Lucas Stach,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Bjorn Helgaas,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Juergen Gross, Stefano Stabellini, Oleksandr Tyshchenko,
	Dmitry Baryshkov, Konrad Dybcio, Bjorn Andersson, Rob Herring,
	Conor Dooley, Krzysztof Kozlowski, Prakash Gupta, Vikash Garodia,
	linux-kernel, iommu, linux-arm-kernel, devicetree, linux-pci, imx,
	xen-devel, linux-arm-msm, Charan Teja Kalla
In-Reply-To: <acvdqQvcitLTCZbm@lizhi-Precision-Tower-5810>

On 31/03/2026 16:43, Frank Li wrote:
> On Tue, Mar 31, 2026 at 07:34:47PM +0530, Vijayanand Jitta wrote:
>> From: Charan Teja Kalla <charan.kalla@oss.qualcomm.com>
>>
>> Change of_map_id() to take a pointer to struct of_phandle_args
>> instead of passing target device node and translated IDs separately.
>> Update all callers accordingly.
>>
>> Add an explicit filter_np parameter to of_map_id() and of_map_msi_id()
>> to separate the filter input from the output. Previously, the target
>> parameter served dual purpose: as an input filter (if non-NULL, only
>> match entries targeting that node) and as an output (receiving the
>> matched node with a reference held). Now filter_np is the explicit
>> input filter and arg->np is the pure output.
>>
>> Previously, of_map_id() would call of_node_put() on the matched node
>> when a filter was provided, making reference ownership inconsistent.
>> Remove this internal of_node_put() call so that of_map_id() now always
>> transfers ownership of the matched node reference to the caller via
>> arg->np. Callers are now consistently responsible for releasing this
>> reference with of_node_put(arg->np) when done.
>>
>> Suggested-by: Rob Herring (Arm) <robh@kernel.org>
>> Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
>> Signed-off-by: Charan Teja Kalla <charan.kalla@oss.qualcomm.com>
>> Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com>
>> ---
>>  drivers/cdx/cdx_msi.c                    |  7 ++--
>>  drivers/iommu/of_iommu.c                 |  4 +-
>>  drivers/irqchip/irq-gic-its-msi-parent.c | 11 ++++--
>>  drivers/of/base.c                        | 68 +++++++++++++++++---------------
>>  drivers/of/irq.c                         | 10 ++++-
>>  drivers/pci/controller/dwc/pci-imx6.c    | 32 +++++++--------
>>
>> for imx part.
>>
>> Reviewed-by: Frank Li <Frank.Li@nxp.com>

So that's an Ack. Leaving a Rb tag for a tiny tiny piece of big patch
will give impression that everything is reviewed on v13. And the patch
was not reviewed by you.

If you cannot certify the reviewers statement of oversight then use acked

"For instance, maintainers may use it to signify that they are OK with a
patch landing, but they may not have reviewed it as thoroughly as if a
Reviewed-by: was provided."

"For example, if a patch affects multiple subsystems and has an
Acked-by: from one subsystem maintainer then this usually indicates
acknowledgement of *just the part which affects that maintainer's code.*"


Best regards,
Krzysztof

^ permalink raw reply

* RE: Re: Re: Re: [PATCH 1/2] dt-bindings: gpu: mali-valhall-csf: Document i.MX952 support
From: Guangliu Ding @ 2026-04-01 15:59 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: Daniel Baluta (OSS), Daniel Almeida, Alice Ryhl, Boris Brezillon,
	Steven Price, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam,
	dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	linux-arm-kernel@lists.infradead.org, Jiyu Yang
In-Reply-To: <ac05OpEMjKMd8CXy@e142607>

Hi Liviu

> On Wed, Apr 01, 2026 at 10:31:01AM +0000, Guangliu Ding wrote:
> > Hi Liviu
> >
> > > On Wed, Apr 01, 2026 at 09:43:12AM +0000, Guangliu Ding wrote:
> > > > Hi Daniel
> > > >
> > > > > On 4/1/26 11:48, Guangliu Ding wrote:
> > > > > > [You don't often get email from guangliu.ding@nxp.com. Learn
> > > > > > why this is important at
> > > > > > https://aka.ms/LearnAboutSenderIdentification
> > > > > > ]
> > > > > >
> > > > > > Hi Liviu
> > > > > >
> > > > > > Thanks for your review. Please refer to my comments below:
> > > > > >
> > > > > >> On Tue, Mar 31, 2026 at 06:12:38PM +0800, Guangliu Ding wrote:
> > > > > >>> Add compatible string of Mali G310 GPU on i.MX952 board.
> > > > > >>>
> > > > > >>> Signed-off-by: Guangliu Ding <guangliu.ding@nxp.com>
> > > > > >>> Reviewed-by: Jiyu Yang <jiyu.yang@nxp.com>
> > > > > >>> ---
> > > > > >>>
> > > > > >>> Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.y
> > > > > >>> aml
> > > > > >>> | 1
> > > > > >>> +
> > > > > >>>  1 file changed, 1 insertion(+)
> > > > > >>>
> > > > > >>> diff --git
> > > > > >>> a/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf
> > > > > >>> .yam
> > > > > >>> l
> > > > > >> b/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.
> > > > > >> yaml
> > > > > >>> index 8eccd4338a2b..6a10843a26e2 100644
> > > > > >>> ---
> > > > > >>> a/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf
> > > > > >>> .yam
> > > > > >>> l
> > > > > >>> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-valhall
> > > > > >>> +++ -csf
> > > > > >>> +++ .yam
> > > > > >>> +++ l
> > > > > >>> @@ -20,6 +20,7 @@ properties:
> > > > > >>>            - enum:
> > > > > >>>                - mediatek,mt8196-mali
> > > > > >>>                - nxp,imx95-mali            # G310
> > > > > >>> +              - nxp,imx952-mali           # G310
> > > > > >> Can you explain why this is needed? Can it not be covered by
> > > > > >> the existing compatible?
> > > > > > There are functional differences in GPU module (GPUMIX)
> > > > > > between
> > > > > > i.MX95 and i.MX952. So they cannot be fully covered by a
> > > > > > single existing
> > > compatible.
> > > > > > On i.MX952, The GPU clock is controlled by hardware GPU auto
> > > > > > clock-gating mechanism, while the GPU clock is managed
> > > > > > explicitly by the
> > > > > driver on i.MX95.
> > > > > > Because of these behavioral differences, separate compatible
> > > > > > strings "nxp,imx95-mali" and "nxp,imx952-mali" are needed to
> > > > > > allow the driver to handle the two variants independently and
> > > > > > to keep room for future
> > > > > divergence.
> > > > >
> > > > >
> > > > > This information should be added in the commit message
> > > > > explaining why
> > > > >
> > > > > the change is needed.
> > > > >
> > > > >
> > > > > But then where is the driver code taking care of these diferences?
> > > > >
> > > >
> > > > Yes. Currently the driver does not require "nxp,imx952-mali" string.
> > > > However, when GPU ipa_counters are enabled to calculate the GPU
> > > > busy_time/idle_time for GPU DVFS feature, they will conflict with
> > > > the hardware GPU auto clock‑gating mechanism, causing GPU clock to
> > > > remain
> > > always on.
> > > > In such cases, ipa_counters need to be disabled so that the GPU
> > > > auto clock‑gating mechanism can operate normally, using
> "nxp,imx952-mali"
> > > string.
> > >
> > > OK, I understand that you're following guidance from some other
> > > senior people on how to upstream patches so you've tried to create
> > > the smallest patchset to ensure that it gets reviewed and accepted,
> > > but in this case we need to see the other patches as well to decide
> > > if your approach is the right one and we do need a separate compatible
> string.
> > >
> > > If enabling GPU ipa_counters causes the clocks to get stuck active,
> > > that feels like a hardware bug, so figuring out how to handle that
> > > is more important than adding a compatible string.
> > >
> > > Either add the patch(es) that use the compatible to this series in
> > > v2, or put a comment in the commit message on where we can see the
> driver changes.
> > >
> >
> > According to discussions with the GPU vendor, this is a hardware
> > limitation of Mali-G310 rather than a hardware bug, and it has been
> > addressed in newer Mali GPU families.
> 
> I represent the said GPU vendor and I think I know what you're talking about,
> but you're taking the wrong approach. All G310s have a problem where in
> order to enable access to the ipa_counters the automatic clock gating gets
> disabled. So the solution that needs to be implemented when we add support
> for IPA_COUNTERs will apply to all GPUs, not just MX952.

Yes. We have bring-up G310 (V2) GPU on both i.MX95 and i.MX952. And auto clock 
gating mechanism is firstly introduced in i.MX952 (not supported on i.MX95).
According to your update, solution needs to be implemented to all GPUs which support 
auto clock gating mechanism after IPA_COUNTERs are supported in the driver, right?
What's your suggestions for 952 gpu dtb node?
> 
> >
> > In addition, ipa_counters are not enabled in the current Panthor
> > driver. We observed this issue with the private Mali DDK where ipa_counters
> were enabled.
> > Therefore, keeping the compatible string is necessary to allow for future
> divergence.
> 
> This is not an MX952 issue only. You might be aware of it on your SoC, but it's
> not just for it.
> 
> Best regards,
> Liviu
> 
> --
> ====================
> | I would like to |
> | fix the world,  |
> | but they're not |
> | giving me the   |
>  \ source code!  /
>   ---------------
>     ¯\_(ツ)_/¯

^ permalink raw reply

* Re: [PATCH v2 2/2] hwmon:(pmbus/xdp720) Add support for efuse xdp720
From: Guenter Roeck @ 2026-04-01 15:56 UTC (permalink / raw)
  To: ASHISH YADAV, Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-hwmon, devicetree, linux-kernel, Ashish Yadav
In-Reply-To: <20260401104550.115715-3-Ashish.Yadav@infineon.com>

Hi,

On 4/1/26 03:45, ASHISH YADAV wrote:
> From: Ashish Yadav <ashish.yadav@infineon.com>
> 
> Add the pmbus driver for Infineon XDP720 Digital eFuse Controller.
> 
> Signed-off-by: Ashish Yadav <ashish.yadav@infineon.com>
> ---
> XDP720 Digital eFuse Controller provides accurate system telemetry
> (V, I, P, T) and reports analog current at the IMON pin for post-processing.
> 
> The Current and Power measurement depends on the RIMON and GIMON values.
> Please look into data sheet sections 5.4.2 and 5.4.4 for more details:
> https://www.infineon.com/assets/row/public/documents/24/49/infineon-xdp720-001-datasheet-en.pdf
> 
> The GIMON (microA/A) depends on the 10th bit of TELEMETRY_AVG PMBUS Register.
> The value of RIMON (kohm) can be provided by the user through device tree using
> infineon,rimon-micro-ohms  property.

Please have a look at

https://sashiko.dev/#/patchset/20260401104550.115715-1-Ashish.Yadav%40infineon.com

Main concern is the power measurement range, but also please use 1000000000000ULL
as suggested.

Thanks,
Guenter

> ---
>   drivers/hwmon/pmbus/Kconfig  |   9 +++
>   drivers/hwmon/pmbus/Makefile |   1 +
>   drivers/hwmon/pmbus/xdp720.c | 123 +++++++++++++++++++++++++++++++++++
>   3 files changed, 133 insertions(+)
>   create mode 100644 drivers/hwmon/pmbus/xdp720.c
> 
> diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
> index fc1273abe357..c419e3ecce90 100644
> --- a/drivers/hwmon/pmbus/Kconfig
> +++ b/drivers/hwmon/pmbus/Kconfig
> @@ -702,6 +702,15 @@ config SENSORS_XDP710
>   	  This driver can also be built as a module. If so, the module will
>   	  be called xdp710.
>   
> +config SENSORS_XDP720
> +	tristate "Infineon XDP720 family"
> +	help
> +	  If you say yes here you get hardware monitoring support for Infineon
> +	  XDP720.
> +
> +	  This driver can also be built as a module. If so, the module will
> +	  be called xdp720.
> +
>   config SENSORS_XDPE152
>   	tristate "Infineon XDPE152 family"
>   	help
> diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
> index d6c86924f887..1cac7ccae79f 100644
> --- a/drivers/hwmon/pmbus/Makefile
> +++ b/drivers/hwmon/pmbus/Makefile
> @@ -68,6 +68,7 @@ obj-$(CONFIG_SENSORS_TPS546D24)	+= tps546d24.o
>   obj-$(CONFIG_SENSORS_UCD9000)	+= ucd9000.o
>   obj-$(CONFIG_SENSORS_UCD9200)	+= ucd9200.o
>   obj-$(CONFIG_SENSORS_XDP710)	+= xdp710.o
> +obj-$(CONFIG_SENSORS_XDP720)	+= xdp720.o
>   obj-$(CONFIG_SENSORS_XDPE122)	+= xdpe12284.o
>   obj-$(CONFIG_SENSORS_XDPE152)	+= xdpe152c4.o
>   obj-$(CONFIG_SENSORS_ZL6100)	+= zl6100.o
> diff --git a/drivers/hwmon/pmbus/xdp720.c b/drivers/hwmon/pmbus/xdp720.c
> new file mode 100644
> index 000000000000..382dc3f9ce80
> --- /dev/null
> +++ b/drivers/hwmon/pmbus/xdp720.c
> @@ -0,0 +1,123 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Hardware monitoring driver for Infineon XDP720 Digital eFuse Controller
> + *
> + * Copyright (c) 2026 Infineon Technologies. All rights reserved.
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/of_device.h>
> +#include <linux/bitops.h>
> +#include <linux/math64.h>
> +#include "pmbus.h"
> +
> +/*
> + * The IMON resistor required to generate the system overcurrent protection.
> + * Arbitrary default Rimon value: 2k Ohm
> + */
> +#define XDP720_DEFAULT_RIMON 2000000000 /* 2k ohm */
> +#define XDP720_TELEMETRY_AVG 0xE9
> +
> +static struct pmbus_driver_info xdp720_info = {
> +	.pages = 1,
> +	.format[PSC_VOLTAGE_IN] = direct,
> +	.format[PSC_VOLTAGE_OUT] = direct,
> +	.format[PSC_CURRENT_OUT] = direct,
> +	.format[PSC_POWER] = direct,
> +	.format[PSC_TEMPERATURE] = direct,
> +
> +	.m[PSC_VOLTAGE_IN] = 4653,
> +	.b[PSC_VOLTAGE_IN] = 0,
> +	.R[PSC_VOLTAGE_IN] = -2,
> +	.m[PSC_VOLTAGE_OUT] = 4653,
> +	.b[PSC_VOLTAGE_OUT] = 0,
> +	.R[PSC_VOLTAGE_OUT] = -2,
> +	/*
> +	 * Current and Power measurement depends on the RIMON (kOhm) and
> +	 * GIMON(microA/A) values.
> +	 */
> +	.m[PSC_CURRENT_OUT] = 24668,
> +	.b[PSC_CURRENT_OUT] = 0,
> +	.R[PSC_CURRENT_OUT] = -4,
> +	.m[PSC_POWER] = 4486,
> +	.b[PSC_POWER] = 0,
> +	.R[PSC_POWER] = -1,
> +	.m[PSC_TEMPERATURE] = 54,
> +	.b[PSC_TEMPERATURE] = 22521,
> +	.R[PSC_TEMPERATURE] = -1,
> +
> +	.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_PIN |
> +		   PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_INPUT |
> +		   PMBUS_HAVE_STATUS_TEMP,
> +};
> +
> +static int xdp720_probe(struct i2c_client *client)
> +{
> +	struct pmbus_driver_info *info;
> +	int ret;
> +	u32 rimon;
> +	int gimon;
> +
> +	info = devm_kmemdup(&client->dev, &xdp720_info, sizeof(*info),
> +			    GFP_KERNEL);
> +	if (!info)
> +		return -ENOMEM;
> +
> +	ret = i2c_smbus_read_word_data(client, XDP720_TELEMETRY_AVG);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "Can't get TELEMETRY_AVG\n");
> +		return ret;
> +	}
> +
> +	ret >>= 10; /* 10th bit of TELEMETRY_AVG REG for GIMON Value */
> +	ret &= GENMASK(0, 0);
> +	if (ret == 1)
> +		gimon = 18200; /* output gain 18.2 microA/A */
> +	else
> +		gimon = 9100; /* output gain 9.1 microA/A */
> +
> +	if (of_property_read_u32(client->dev.of_node,
> +				 "infineon,rimon-micro-ohms", &rimon))
> +		rimon = XDP720_DEFAULT_RIMON; /* Default if not set via DT */
> +	if (rimon == 0)
> +		return -EINVAL;
> +
> +	/* Adapt the current and power scale for each instance */
> +	info->m[PSC_CURRENT_OUT] = DIV64_U64_ROUND_CLOSEST((u64)
> +		info->m[PSC_CURRENT_OUT] * rimon * gimon, 1000000000000);
> +	info->m[PSC_POWER] = DIV64_U64_ROUND_CLOSEST((u64)
> +		info->m[PSC_POWER] * rimon * gimon, 1000000000000);
> +
> +	return pmbus_do_probe(client, info);
> +}
> +
> +static const struct of_device_id xdp720_of_match[] = {
> +	{ .compatible = "infineon,xdp720" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, xdp720_of_match);
> +
> +static const struct i2c_device_id xdp720_id[] = {
> +	{ "xdp720" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, xdp720_id);
> +
> +static struct i2c_driver xdp720_driver = {
> +	.driver = {
> +		   .name = "xdp720",
> +		   .of_match_table = xdp720_of_match,
> +	},
> +	.probe = xdp720_probe,
> +	.id_table = xdp720_id,
> +};
> +
> +module_i2c_driver(xdp720_driver);
> +
> +MODULE_AUTHOR("Ashish Yadav <ashish.yadav@infineon.com>");
> +MODULE_DESCRIPTION("PMBus driver for Infineon XDP720 Digital eFuse Controller");
> +MODULE_LICENSE("GPL");
> +MODULE_IMPORT_NS("PMBUS");


^ permalink raw reply

* Re: [PATCH 7/7] clk: qcom: Add support for global clock controller on Hawi
From: Mike Tipton @ 2026-04-01 15:54 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Vivek Aknurwar, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Taniya Das, Taniya Das,
	linux-arm-msm, linux-clk, devicetree, linux-kernel
In-Reply-To: <acw4FDsIbgAUTHXo@baldur>

On Tue, Mar 31, 2026 at 04:11:22PM -0500, Bjorn Andersson wrote:
> On Mon, Mar 30, 2026 at 05:35:02PM -0700, Vivek Aknurwar wrote:
> > +++ b/drivers/clk/qcom/gcc-hawi.c
> [..]
> > +static const struct qcom_cc_desc gcc_hawi_desc = {
> > +	.config = &gcc_hawi_regmap_config,
> > +	.clks = gcc_hawi_clocks,
> > +	.num_clks = ARRAY_SIZE(gcc_hawi_clocks),
> > +	.resets = gcc_hawi_resets,
> > +	.num_resets = ARRAY_SIZE(gcc_hawi_resets),
> > +	.gdscs = gcc_hawi_gdscs,
> > +	.num_gdscs = ARRAY_SIZE(gcc_hawi_gdscs),
> > +	.driver_data = &gcc_hawi_driver_data,
> 
> Sorry for not spotting this earlier, but don't we need a ".use_rpm =
> true" here?
> 
> In line with
> https://lore.kernel.org/all/20260309-glymur-fix-gcc-cx-scaling-v2-2-d7a58a0a9ecb@oss.qualcomm.com/

Yeah, we should. Same rationale, which technically applies to many other
GCC instances that aren't setting this today.

Mike

> 
> Regards,
> Bjorn
> 
> > +};
> > +

^ permalink raw reply

* Re: [PATCH 1/2] arm64: dts: qcom: qcs6490-rb3gen2-industrial-mezzanine: Add model string
From: Krzysztof Kozlowski @ 2026-04-01 15:39 UTC (permalink / raw)
  To: Bjorn Andersson, Dmitry Baryshkov
  Cc: Umang Chheda, konradybcio, robh, krzk+dt, conor+dt,
	richardcochran, linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <acSfUEVueSR5Ikq7@baldur>

On 26/03/2026 03:59, Bjorn Andersson wrote:
>>
>>> considering above point - is it okay to have this patch ? I believe it would be good to have model string in the DTSO file added.
>>
>> No. A _mezzanine_ can not be a device model. It is by definition
>> something that you plug into the main device. Maybe creating something
>> new, a new kit.
>>
>> Moreover, the model name is not an ABI, we should not be using it for
>> device identification.
>>
> 
> I do agree.
> 
> That said, as we (others) discussed during LPC, it would be very useful
> for bug reporting purposes to know what overlays has been applied - or
> in general what "version" of the dtb we're running the system from.
> 
> I don't think "model" is what we're looking for there though.

No, it's not. Prepending compatible is one way, but that cannot be done
directly by DTSO. In my notes from LPC I have also top-level config node
(there is patch for U-Boot for that). And I finished my notes around
that time, so I don't have more ideas...

Best regards,
Krzysztof

^ permalink raw reply

* Re: Re: Re: [PATCH 1/2] dt-bindings: gpu: mali-valhall-csf: Document i.MX952 support
From: Liviu Dudau @ 2026-04-01 15:26 UTC (permalink / raw)
  To: Guangliu Ding
  Cc: Daniel Baluta (OSS), Daniel Almeida, Alice Ryhl, Boris Brezillon,
	Steven Price, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam,
	dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	linux-arm-kernel@lists.infradead.org, Jiyu Yang
In-Reply-To: <AM0PR04MB4707335CC7035232D44E6ED9F350A@AM0PR04MB4707.eurprd04.prod.outlook.com>

On Wed, Apr 01, 2026 at 10:31:01AM +0000, Guangliu Ding wrote:
> Hi Liviu
> 
> > On Wed, Apr 01, 2026 at 09:43:12AM +0000, Guangliu Ding wrote:
> > > Hi Daniel
> > >
> > > > On 4/1/26 11:48, Guangliu Ding wrote:
> > > > > [You don't often get email from guangliu.ding@nxp.com. Learn why
> > > > > this is important at https://aka.ms/LearnAboutSenderIdentification
> > > > > ]
> > > > >
> > > > > Hi Liviu
> > > > >
> > > > > Thanks for your review. Please refer to my comments below:
> > > > >
> > > > >> On Tue, Mar 31, 2026 at 06:12:38PM +0800, Guangliu Ding wrote:
> > > > >>> Add compatible string of Mali G310 GPU on i.MX952 board.
> > > > >>>
> > > > >>> Signed-off-by: Guangliu Ding <guangliu.ding@nxp.com>
> > > > >>> Reviewed-by: Jiyu Yang <jiyu.yang@nxp.com>
> > > > >>> ---
> > > > >>>  Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
> > > > >>> | 1
> > > > >>> +
> > > > >>>  1 file changed, 1 insertion(+)
> > > > >>>
> > > > >>> diff --git
> > > > >>> a/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yam
> > > > >>> l
> > > > >> b/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
> > > > >>> index 8eccd4338a2b..6a10843a26e2 100644
> > > > >>> ---
> > > > >>> a/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yam
> > > > >>> l
> > > > >>> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf
> > > > >>> +++ .yam
> > > > >>> +++ l
> > > > >>> @@ -20,6 +20,7 @@ properties:
> > > > >>>            - enum:
> > > > >>>                - mediatek,mt8196-mali
> > > > >>>                - nxp,imx95-mali            # G310
> > > > >>> +              - nxp,imx952-mali           # G310
> > > > >> Can you explain why this is needed? Can it not be covered by the
> > > > >> existing compatible?
> > > > > There are functional differences in GPU module (GPUMIX) between
> > > > > i.MX95 and i.MX952. So they cannot be fully covered by a single existing
> > compatible.
> > > > > On i.MX952, The GPU clock is controlled by hardware GPU auto
> > > > > clock-gating mechanism, while the GPU clock is managed explicitly
> > > > > by the
> > > > driver on i.MX95.
> > > > > Because of these behavioral differences, separate compatible
> > > > > strings "nxp,imx95-mali" and "nxp,imx952-mali" are needed to allow
> > > > > the driver to handle the two variants independently and to keep
> > > > > room for future
> > > > divergence.
> > > >
> > > >
> > > > This information should be added in the commit message explaining
> > > > why
> > > >
> > > > the change is needed.
> > > >
> > > >
> > > > But then where is the driver code taking care of these diferences?
> > > >
> > >
> > > Yes. Currently the driver does not require "nxp,imx952-mali" string.
> > > However, when GPU ipa_counters are enabled to calculate the GPU
> > > busy_time/idle_time for GPU DVFS feature, they will conflict with the
> > > hardware GPU auto clock‑gating mechanism, causing GPU clock to remain
> > always on.
> > > In such cases, ipa_counters need to be disabled so that the GPU auto
> > > clock‑gating mechanism can operate normally, using "nxp,imx952-mali"
> > string.
> > 
> > OK, I understand that you're following guidance from some other senior
> > people on how to upstream patches so you've tried to create the smallest
> > patchset to ensure that it gets reviewed and accepted, but in this case we need
> > to see the other patches as well to decide if your approach is the right one and
> > we do need a separate compatible string.
> > 
> > If enabling GPU ipa_counters causes the clocks to get stuck active, that feels
> > like a hardware bug, so figuring out how to handle that is more important than
> > adding a compatible string.
> > 
> > Either add the patch(es) that use the compatible to this series in v2, or put a
> > comment in the commit message on where we can see the driver changes.
> > 
> 
> According to discussions with the GPU vendor, this is a hardware limitation 
> of Mali-G310 rather than a hardware bug, and it has been addressed in newer
> Mali GPU families.

I represent the said GPU vendor and I think I know what you're talking about, but you're
taking the wrong approach. All G310s have a problem where in order to enable access to
the ipa_counters the automatic clock gating gets disabled. So the solution that needs
to be implemented when we add support for IPA_COUNTERs will apply to all GPUs, not just
MX952.

> 
> In addition, ipa_counters are not enabled in the current Panthor driver. We observed
> this issue with the private Mali DDK where ipa_counters were enabled.
> Therefore, keeping the compatible string is necessary to allow for future divergence.

This is not an MX952 issue only. You might be aware of it on your SoC, but it's not just
for it.

Best regards,
Liviu

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯

^ permalink raw reply

* Re: [PATCH v3 0/6] drm/sun4i: Support LVDS on D1s/T113 combo D-PHY
From: Kuba Szczodrzyński @ 2026-04-01 15:04 UTC (permalink / raw)
  To: Parthiban, Maxime Ripard, Samuel Holland, Chen-Yu Tsai,
	Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: David Airlie, Simona Vetter, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-riscv, linux-phy, devicetree, dri-devel,
	paulk
In-Reply-To: <1027afd7-dec6-4ff3-85eb-2d9a1646ada6@linumiz.com>

Hi,

W dniu 2026-04-01 o 10:39:42, Parthiban pisze:

> Dear Kuba,
>
> On 2/7/26 2:34 PM, Parthiban wrote:
>> On 11/16/25 2:46 PM, Kuba Szczodrzyński wrote:
>>> Some Allwinner chips (notably the D1s/T113 and the A100) have a "combo
>>> MIPI DSI D-PHY" which is required when using single-link LVDS0. The same
>>> PD0..PD9 pins are used for either DSI or LVDS.
>>>
>>> Other than having to use the combo D-PHY, LVDS output is configured in
>>> the same way as on older chips.
>>>
>>> This series enables the sun6i MIPI D-PHY to also work in LVDS mode. It
>>> is then configured by the LCD TCON, which allows connecting a
>>> single-link LVDS display panel.
> Now I also have the MIPI and LVDS working together on A133. Can I pick your
> changes and post a combined series for the display support for A133? This will
> also address D1s/T114 as well.

I guess it's been waiting for too long anyway - so yes, if you know how 
to push it further, then please do so.

Regards Kuba

> --
> Thanks,
> Parthiban
> https://linumiz.com
> https://www.linkedin.com/company/linumiz

^ permalink raw reply

* Re: [PATCH 1/2] arm64: dts: qcom: qcs6490-rb3gen2-industrial-mezzanine: Add model string
From: Umang Chheda @ 2026-04-01 15:21 UTC (permalink / raw)
  To: Bjorn Andersson, Dmitry Baryshkov
  Cc: konradybcio, robh, krzk+dt, conor+dt, richardcochran,
	linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <acSfUEVueSR5Ikq7@baldur>

Hello Bjorn, Dmitry,

On 3/26/2026 8:29 AM, Bjorn Andersson wrote:
> On Mon, Mar 23, 2026 at 09:06:54PM +0200, Dmitry Baryshkov wrote:
>> On Mon, Mar 23, 2026 at 10:33:45PM +0530, Umang Chheda wrote:
>>> Hello Dmitry,
>>>
>>> On 3/13/2026 11:23 PM, Dmitry Baryshkov wrote:
>>>> On Tue, Mar 10, 2026 at 02:09:58PM +0530, Umang Chheda wrote:
>>>>> Hello Dmitry,
>>>>>
>>>>> On 3/10/2026 2:07 AM, Dmitry Baryshkov wrote:
>>>>>> On Mon, Mar 09, 2026 at 05:49:35PM +0530, Umang Chheda wrote:
>>>>>>> The RB3 Gen2 Industrial mezzanine DT file lacks a "model" property,
>>>>>>> resulting in the hardware always being reported as the "Robotics RB3gen2".
>>>>>>> This prevents applications and user-space tooling from distinguishing
>>>>>>> the Industrial mezzanine Kit from other RB3Gen2 variants.
>>>>>>>
>>>>>>> To ensure consistent identification across all RB3 Gen2 mezzanines,
>>>>>>> add the appropriate model string.
>>>>>>>
>>>>>>> Signed-off-by: Umang Chheda <umang.chheda@oss.qualcomm.com>
>>>>>>> ---
>>>>>>>  .../boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso   | 4 ++++
>>>>>>>  1 file changed, 4 insertions(+)
>>>>>>>
>>>>>>> diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso
>>>>>>> index 619a42b5ef48..7d6e425dae89 100644
>>>>>>> --- a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso
>>>>>>> +++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso
>>>>>>> @@ -8,6 +8,10 @@
>>>>>>>  #include <dt-bindings/clock/qcom,gcc-sc7280.h>
>>>>>>>  #include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
>>>>>>>
>>>>>>> +&{/} {
>>>>>>> +	model = "Qualcomm Technologies, Inc. QCS6490 RB3gen2 Industrial Mezzanine";
>>>>>> So, what happens if I combine Industrial and Vision mezzanines?
>>>>> H/W does not support to connect both Industrial and vision mezzanine at same time - only single mezzanine can be
>>>>>
>>>>> connected at a given point in time - hence probably the above scenario would never occur.
>>>> Industrial mezzanine has all LS and HS connectors, so it is possible to
>>>> stack Vision mezz on top of the industrial one.
>>>
>>> I re-checked - from f/w side as well multiple DTSO selection is not supported - and at a single point in time only single mezz can be selected
>>
>> What does this have to do with the firmware? From the Linux build system
>> point of view we are combining the overlay into a single
>> qcs6490-rb3gen2-industrial-mezzanine.dtb file.
>>
>> Nothing prevents me from writing:
>>
>> qcs6490-rb3gen2-multi-mezzanine-dtbs := qcs6490-rb3gen2.dtb \
>> 		qcs6490-rb3gen2-industrial-mezzanine.dtbo \
>> 		qcs6490-rb3gen2-vision-mezzanine.dtbo
>> dtb-$(CONFIG_ARCH_QCOM)        += qcs6490-rb3gen2-multi-mezzanine.dtb
>>
>>> considering above point - is it okay to have this patch ? I believe it would be good to have model string in the DTSO file added.
>>
>> No. A _mezzanine_ can not be a device model. It is by definition
>> something that you plug into the main device. Maybe creating something
>> new, a new kit.
>>
>> Moreover, the model name is not an ABI, we should not be using it for
>> device identification.
>>
> 
> I do agree.
> 
> That said, as we (others) discussed during LPC, it would be very useful
> for bug reporting purposes to know what overlays has been applied - or
> in general what "version" of the dtb we're running the system from.
> 
> I don't think "model" is what we're looking for there though.

Does adding something like below in the chosen node from the DTSO file
works good instead of a model string to identify the list of overlays
applied ?

&{/chosen} {
	applied-overlays {
		industrial-mezzanine {
			status = "okay";
		};
	};
};

> 
> Regards,
> Bjorn
> 
>>> Kindly share your views
>>
>> -- 
>> With best wishes
>> Dmitry

Thanks,
Umang


^ permalink raw reply

* Re: [PATCH v20 01/10] power: reset: reboot-mode: Remove devres based allocations
From: Krzysztof Kozlowski @ 2026-04-01 15:19 UTC (permalink / raw)
  To: Shivendra Pratap, Lorenzo Pieralisi, Arnd Bergmann,
	Bjorn Andersson, Sebastian Reichel, Rob Herring,
	Souvik Chakravarty, Krzysztof Kozlowski, Andy Yan,
	Matthias Brugger, Mark Rutland, Conor Dooley, Konrad Dybcio,
	John Stultz, Moritz Fischer, Bartosz Golaszewski, Sudeep Holla
  Cc: Florian Fainelli, Dmitry Baryshkov, Mukesh Ojha, Andre Draszik,
	Kathiravan Thirumoorthy, linux-pm, linux-kernel, linux-arm-kernel,
	linux-arm-msm, devicetree, Srinivas Kandagatla
In-Reply-To: <20260304-arm-psci-system_reset2-vendor-reboots-v20-1-cf7d346b8372@oss.qualcomm.com>

On 04/03/2026 19:03, Shivendra Pratap wrote:
> Devres APIs are intended for use in drivers, where the managed lifetime
> of resources is tied directly to the driver attach/detach cycle. In
> shared subsystem code, there is no guarantee that the subsystem
> functions will only be called after a driver has been attached, nor that
> they will not be referenced after the managed resources have been
> released during driver detach.
> 
> To ensure correct lifetime handling, avoid using devres-based
> allocations in the reboot-mode and explicitly handle allocation and
> cleanup of resources.
> 
> Fixes: 4fcd504edbf7 ("power: reset: add reboot mode driver")

I don't think this is correct tag.

That commit added code which was a driver, not subsystem level things.
Using devres, as you pointed out, in platform_driver is correct.

Otherwise please specify what bug from that commit is being fixed.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [RFC PATCH 15/15] Introduce v18 dtb version
From: Luca Ceresoli @ 2026-04-01 15:19 UTC (permalink / raw)
  To: Herve Codina, David Gibson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: Ayush Singh, Geert Uytterhoeven, devicetree-compiler, devicetree,
	linux-kernel, devicetree-spec, Hui Pu, Ian Ray, Thomas Petazzoni
In-Reply-To: <20260210173349.636766-16-herve.codina@bootlin.com>

On Tue Feb 10, 2026 at 6:33 PM CET, Herve Codina wrote:
> This v18 version will add support for
>   - Structured tags.
>     Those tags value definition will allow old libfdt, dtc and other
>     tools to skip unknown tags if encountered in future dtb version.

"old" seems to imply that versions released before today will be able to
wkip unknown tags. I think this should be clarified along the lines of:

  libfdt, dtc and other tools implementing version v18 will be able to wkip
  unknown tags in dtbs generated with later versions of dtc

>   - dt_flags header field.
>     For now this flag field is set to 0. It is a placeholder for future
>     dtb version and could be used to store some dtb related information
>     such as the kind of dtb.

Is this intended for DT addons?

You may mention a realistiv use case here.

>
>   - last_comp_version_w header field.
>     This field is similar to last_comp_version but for writing.
>     It contains the lowest version of the devicetree data structure with
>     which the version used can safely perform modifications (taking into
>     account following rules related to unknown tags).
>     If this lowest version is greater than the last known supported
>     version, modification are simply forbidden and lead to a
>     FDT_ERR_BADVERSION error.
>
> For modification, when an unknown tag that can be skipped is involved
> and last_comp_version_w allows modifications, the following rules
> applies:
  ^
  apply

>   - When a property is modified, tags related to this property are
>     removed and the dtb version is downgraded.
>
>   - When a property is removed, tags related to this property are
>     obviously removed. The dtb version is kept unchanged.
>
>   - When a property or a node is added, obviously no unknown tags are
>     added and the dtb version is kept unchanged.
>
>   - When a node is removed, tags related to this node are obviously
>     removed. The dtb version is kept unchanged.
>
>   - Adding, removing or modifying a property is not considered as a node
>     modification and so, those operations have no impacts on unknown
>     tags related to the node. Those node related tags are kept
>     unchanged.
>
>   - The only modification considered as a node modification is setting
>     its name. We consider that this operation has no impact on tags
>     related to the node. Here also, those node related tags and the dtb
>     version are kept unchanged.
>
>   - Global (dtb related) unknown tags are kept unchanged regardless the
>     modification done.
>
> In all cases, if unknown tags are not involved in a modification, the
> dtb version is not downgraded when the modification is node.
                                                         ^
							 made?


>
> Compared to previous version, it is worth noting that the dtb is not
                                                               ^
				                        "dtb version"

> downgrade for all modification but only when unknown tags are removed
  ^
  downgraded for any


> due a property modification.
  ^
  "due to a ..."

I'm not sure I got what you mean by the initial "Compared to previous
version". Version(s) of what?

If I just remove those 4 words the sentence seems OK to me BTW.

> Signed-off-by: Herve Codina <herve.codina@bootlin.com>

I am unable to fully understand the implications of this patch, but the
code seems OK to me as far as I can tell.

Luca

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply

* Re: [RFC PATCH 14/15] libfdt: Handle unknown tags on dtb modifications
From: Luca Ceresoli @ 2026-04-01 15:18 UTC (permalink / raw)
  To: Herve Codina, David Gibson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: Ayush Singh, Geert Uytterhoeven, devicetree-compiler, devicetree,
	linux-kernel, devicetree-spec, Hui Pu, Ian Ray, Thomas Petazzoni
In-Reply-To: <20260210173349.636766-15-herve.codina@bootlin.com>

On Tue Feb 10, 2026 at 6:33 PM CET, Herve Codina wrote:
> The structured tag value definition introduced recently gives the
> ability to ignore unknown tags without any error.
>
> When the dtb is modified those unknown tags have to be taken into
> account.
>
> First, depending on the unknown tag location, the item associated with
> the tag is identified:
>   - An unknown tag located just after a FDT_BEGIN_NODE is related to the
>     node.
>
>   - An unknown tag located just after a FDT_PROP is related to the
>     property.
>
>   - An unknown tag out of any node (i.e located before the first
>     FDT_BEGIN_NODE or after the last FDT_END_NODE is a global tag
                                                   ^
						   missing ')'
>     related to the dtb itself.

Out of curiosity, is there a real use case for global tags after
FDT_END_NODE?

> Then, if we are allowed to write a dtb containing unknown tags, the
> following rules are used:
>   - When a property is modified, tags related to this property are
>     removed and the dtb version is downgraded.
>
>   - When a property is removed, tags related to this property are
>     obviously removed. The dtb version is kept unchanged.
>
>   - When a property or a node is added, obviously no unknown tags are
>     added and the dtb version is kept unchanged.
>
>   - When a node is removed, tags related to this node are obviously
>     removed. The dtb version is kept unchanged.
>
>   - Adding, removing or modifying a property is not considered as a node
>     modification and so, those operations have no impacts on unknown
>     tags related to the node. Those node related tags are kept unchanged.
>
>   - The only modification considered as a node modification is setting
>     its name. We consider that this operation has no impact on tags
>     related to the node. Here also, those node related tags and the
>     dtb version are kept unchanged.
>
>   - Global (dtb related) unknown tags are kept unchanged regardless the
>     modification done.
>
> Implement those rules when a dtb is modified.
>
> Signed-off-by: Herve Codina <herve.codina@bootlin.com>

With the tests updated as per comments in patch 10:
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply

* Re: [RFC PATCH 13/15] libfdt: Introduce fdt_ptr_offset_
From: Luca Ceresoli @ 2026-04-01 15:18 UTC (permalink / raw)
  To: Herve Codina, David Gibson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: Ayush Singh, Geert Uytterhoeven, devicetree-compiler, devicetree,
	linux-kernel, devicetree-spec, Hui Pu, Ian Ray, Thomas Petazzoni
In-Reply-To: <20260210173349.636766-14-herve.codina@bootlin.com>

On Tue Feb 10, 2026 at 6:33 PM CET, Herve Codina wrote:
> libfdt provides internal used helpers to convert an offset to a pointer
                  ^^^^^^^^^^^^^^^^^^^^^

Just "internal helpers"


> but nothing to do the reverse operation.
>
> Fill this lack and introduce the fdt_ptr_offset_() internal helper to

"Fill this gap"? Or just "Introduce the..." would be enough.

With those fixed:
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply

* Re: [RFC PATCH 12/15] libfdt: Handle unknown tags in fdt_get_next()
From: Luca Ceresoli @ 2026-04-01 15:17 UTC (permalink / raw)
  To: Herve Codina, David Gibson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: Ayush Singh, Geert Uytterhoeven, devicetree-compiler, devicetree,
	linux-kernel, devicetree-spec, Hui Pu, Ian Ray, Thomas Petazzoni
In-Reply-To: <20260210173349.636766-13-herve.codina@bootlin.com>

On Tue Feb 10, 2026 at 6:33 PM CET, Herve Codina wrote:
> The structured tag value definition introduced recently gives the
> ability to ignore unknown tags without any error when they are read.
>
> libfdt uses fdt_get_next() to get a tag.

I think you mean fdt_next_tag(), here and elsewhere in the commit message.

>
> Filtering out tags that should be ignored in fdt_get_next() allows to
> have the filtering done globally and allows, in future release, to have
                                                         ^
							 releases

> a central place to add new known tags that should not be filtered out.
>
> An already known tag exists with the meaning of "just ignore". This tag
> is FDT_NOP. fdt_get_next() callers already handle the FDT_NOP tag.
>
> Avoid unneeded modification at callers side and use a fake FDT_NOP tag
> when an unknown tag that should be ignored is encountered.
>
> Add also fdt_get_next_() internal function for callers who need to know

And here fdt_next_tag_()?

> if the FDT_NOP tag returned is a real FDT_NOP or a fake FDT_NOP due to
> an unknown tag.
>
> Signed-off-by: Herve Codina <herve.codina@bootlin.com>

Looks good otherwise, so with those fixed:
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply

* [PATCH v10 5/5] reset: rzv2h-usb2phy: Add support for VBUS mux controller registration
From: Tommaso Merciai @ 2026-04-01 15:16 UTC (permalink / raw)
  To: tomm.merciai, peda, p.zabel
  Cc: linux-renesas-soc, biju.das.jz, Tommaso Merciai, Fabrizio Castro,
	Lad Prabhakar, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Arnd Bergmann, Ulf Hansson,
	Josua Mayer, devicetree, linux-kernel
In-Reply-To: <cover.1775047175.git.tommaso.merciai.xr@bp.renesas.com>

The RZ/V2H USB2 PHY requires control of the VBUS selection line
(VBENCTL) through a mux controller described in the device tree as
"mux-controller". This change adds support for registering the
rzv2h-usb-vbenctl auxiliary driver during probe.

This enables proper management of USB2.0 VBUS source selection on
platforms using the RZ/V2H SoC.

Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
---
v9->v10:
 - No changes.

v8->v9:
 - Drop linux/reset/reset_rzv2h_usb2phy.h dependecy as the driver
   is now based on regmap and does not need the reset driver's
   private header, update driver accordingly.
 - Collected PZabel tag.

v7->v8:
 - No changes

v6->v7:
 - No changes

v5->v6:
 - No changes

v4->v5:
 - Update mux_name to "vbenctl" to match the driver name.
 - Updated commit message.

v3->v4:
 - No changes.

v2->v3:
 - Use __devm_auxiliary_device_create() to create the auxiliary device.

v1->v2:
 - New patch

 drivers/reset/Kconfig               |  1 +
 drivers/reset/reset-rzv2h-usb2phy.c | 35 +++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index c539ca88518f..62b2c3919613 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -257,6 +257,7 @@ config RESET_RZG2L_USBPHY_CTRL
 config RESET_RZV2H_USB2PHY
 	tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver"
 	depends on ARCH_RENESAS || COMPILE_TEST
+	select AUXILIARY_BUS
 	select REGMAP_MMIO
 	help
 	  Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC
diff --git a/drivers/reset/reset-rzv2h-usb2phy.c b/drivers/reset/reset-rzv2h-usb2phy.c
index c79bf72602e8..d96042e28cd5 100644
--- a/drivers/reset/reset-rzv2h-usb2phy.c
+++ b/drivers/reset/reset-rzv2h-usb2phy.c
@@ -5,7 +5,9 @@
  * Copyright (C) 2025 Renesas Electronics Corporation
  */
 
+#include <linux/auxiliary_bus.h>
 #include <linux/delay.h>
+#include <linux/idr.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -15,6 +17,8 @@
 #include <linux/reset.h>
 #include <linux/reset-controller.h>
 
+static DEFINE_IDA(auxiliary_ids);
+
 struct rzv2h_usb2phy_reset_of_data {
 	const struct reg_sequence *init_seq;
 	unsigned int init_nseq;
@@ -84,6 +88,33 @@ static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev,
 	return 0;
 }
 
+static void rzv2h_usb2phy_reset_ida_free(void *data)
+{
+	struct auxiliary_device *adev = data;
+
+	ida_free(&auxiliary_ids, adev->id);
+}
+
+static int rzv2h_usb2phy_reset_mux_register(struct device *dev,
+					    const char *mux_name)
+{
+	struct auxiliary_device *adev;
+	int id;
+
+	id = ida_alloc(&auxiliary_ids, GFP_KERNEL);
+	if (id < 0)
+		return id;
+
+	adev = __devm_auxiliary_device_create(dev, dev->driver->name,
+					      mux_name, NULL, id);
+	if (!adev) {
+		ida_free(&auxiliary_ids, id);
+		return -ENOMEM;
+	}
+
+	return devm_add_action_or_reset(dev, rzv2h_usb2phy_reset_ida_free, adev);
+}
+
 static const struct regmap_config rzv2h_usb2phy_reset_regconf = {
 	.reg_bits = 32,
 	.val_bits = 32,
@@ -153,6 +184,10 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev)
 	if (error)
 		return dev_err_probe(dev, error, "could not register reset controller\n");
 
+	error = rzv2h_usb2phy_reset_mux_register(dev, "vbenctl");
+	if (error)
+		return dev_err_probe(dev, error, "could not register aux mux\n");
+
 	return 0;
 }
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v10 4/5] reset: rzv2h-usb2phy: Convert to regmap API
From: Tommaso Merciai @ 2026-04-01 15:16 UTC (permalink / raw)
  To: tomm.merciai, peda, p.zabel
  Cc: linux-renesas-soc, biju.das.jz, Tommaso Merciai, Fabrizio Castro,
	Lad Prabhakar, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Arnd Bergmann, Ulf Hansson,
	Josua Mayer, devicetree, linux-kernel
In-Reply-To: <cover.1775047175.git.tommaso.merciai.xr@bp.renesas.com>

Replace raw MMIO accesses (readl/writel) with regmap_read() and
regmap_multi_reg_write() via devm_regmap_init_mmio().
Drop the manual spinlock as regmap provides internal locking.

Replace the custom rzv2h_usb2phy_regval struct with the standard
reg_sequence, and encode assert/deassert sequences as reg_sequence
arrays rather than individual scalar fields in the of_data
descriptor.

Use the reg_sequence .delay_us field to encode the 11 µs post-assert
delay, replacing the explicit usleep_range(11, 20) call in
rzv2h_usbphy_reset_assert().

Select REGMAP_MMIO in Kconfig.

Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
---
v9->v10:
 - Use struct reg_sequence and regmap_multi_reg_write() to handle
   initialization, assert and deassert sequences and drop custom struct
   rzv2h_usb2phy_regval.

v8->v9:
 - New patch

 drivers/reset/Kconfig               |   1 +
 drivers/reset/reset-rzv2h-usb2phy.c | 108 ++++++++++++++--------------
 2 files changed, 55 insertions(+), 54 deletions(-)

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 5165006be693..c539ca88518f 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -257,6 +257,7 @@ config RESET_RZG2L_USBPHY_CTRL
 config RESET_RZV2H_USB2PHY
 	tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver"
 	depends on ARCH_RENESAS || COMPILE_TEST
+	select REGMAP_MMIO
 	help
 	  Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC
 	  (and similar SoCs).
diff --git a/drivers/reset/reset-rzv2h-usb2phy.c b/drivers/reset/reset-rzv2h-usb2phy.c
index 5bdd39274612..c79bf72602e8 100644
--- a/drivers/reset/reset-rzv2h-usb2phy.c
+++ b/drivers/reset/reset-rzv2h-usb2phy.c
@@ -5,42 +5,35 @@
  * Copyright (C) 2025 Renesas Electronics Corporation
  */
 
-#include <linux/cleanup.h>
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/regmap.h>
 #include <linux/reset.h>
 #include <linux/reset-controller.h>
 
-struct rzv2h_usb2phy_regval {
-	u16 reg;
-	u16 val;
-};
-
 struct rzv2h_usb2phy_reset_of_data {
-	const struct rzv2h_usb2phy_regval *init_vals;
-	unsigned int init_val_count;
+	const struct reg_sequence *init_seq;
+	unsigned int init_nseq;
+
+	const struct reg_sequence *assert_seq;
+	unsigned int assert_nseq;
+
+	const struct reg_sequence *deassert_seq;
+	unsigned int deassert_nseq;
 
 	u16 reset_reg;
-	u16 reset_assert_val;
-	u16 reset_deassert_val;
 	u16 reset_status_bits;
-	u16 reset_release_val;
-
-	u16 reset2_reg;
-	u16 reset2_acquire_val;
-	u16 reset2_release_val;
 };
 
 struct rzv2h_usb2phy_reset_priv {
 	const struct rzv2h_usb2phy_reset_of_data *data;
-	void __iomem *base;
+	struct regmap *regmap;
 	struct device *dev;
 	struct reset_controller_dev rcdev;
-	spinlock_t lock; /* protects register accesses */
 };
 
 static inline struct rzv2h_usb2phy_reset_priv
@@ -53,31 +46,18 @@ static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev,
 				     unsigned long id)
 {
 	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
-	const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
 
-	scoped_guard(spinlock, &priv->lock) {
-		writel(data->reset2_acquire_val, priv->base + data->reset2_reg);
-		writel(data->reset_assert_val, priv->base + data->reset_reg);
-	}
-
-	usleep_range(11, 20);
-
-	return 0;
+	return regmap_multi_reg_write(priv->regmap, priv->data->assert_seq,
+				      priv->data->assert_nseq);
 }
 
 static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev,
 				       unsigned long id)
 {
 	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
-	const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
 
-	scoped_guard(spinlock, &priv->lock) {
-		writel(data->reset_deassert_val, priv->base + data->reset_reg);
-		writel(data->reset2_release_val, priv->base + data->reset2_reg);
-		writel(data->reset_release_val, priv->base + data->reset_reg);
-	}
-
-	return 0;
+	return regmap_multi_reg_write(priv->regmap, priv->data->deassert_seq,
+				      priv->data->deassert_nseq);
 }
 
 static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev,
@@ -86,7 +66,7 @@ static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev,
 	struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
 	u32 reg;
 
-	reg = readl(priv->base + priv->data->reset_reg);
+	regmap_read(priv->regmap, priv->data->reset_reg, &reg);
 
 	return (reg & priv->data->reset_status_bits) == priv->data->reset_status_bits;
 }
@@ -104,6 +84,13 @@ static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev,
 	return 0;
 }
 
+static const struct regmap_config rzv2h_usb2phy_reset_regconf = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.can_sleep = true,
+};
+
 static void rzv2h_usb2phy_reset_pm_runtime_put(void *data)
 {
 	pm_runtime_put(data);
@@ -115,6 +102,7 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev)
 	struct rzv2h_usb2phy_reset_priv *priv;
 	struct device *dev = &pdev->dev;
 	struct reset_control *rstc;
+	void __iomem *base;
 	int error;
 
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -124,17 +112,19 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev)
 	data = of_device_get_match_data(dev);
 	priv->data = data;
 	priv->dev = dev;
-	priv->base = devm_platform_ioremap_resource(pdev, 0);
-	if (IS_ERR(priv->base))
-		return PTR_ERR(priv->base);
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	priv->regmap = devm_regmap_init_mmio(dev, base, &rzv2h_usb2phy_reset_regconf);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
 
 	rstc = devm_reset_control_get_shared_deasserted(dev, NULL);
 	if (IS_ERR(rstc))
 		return dev_err_probe(dev, PTR_ERR(rstc),
 				     "failed to get deasserted reset\n");
 
-	spin_lock_init(&priv->lock);
-
 	error = devm_pm_runtime_enable(dev);
 	if (error)
 		return dev_err_probe(dev, error, "Failed to enable pm_runtime\n");
@@ -148,8 +138,9 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev)
 	if (error)
 		return dev_err_probe(dev, error, "unable to register cleanup action\n");
 
-	for (unsigned int i = 0; i < data->init_val_count; i++)
-		writel(data->init_vals[i].val, priv->base + data->init_vals[i].reg);
+	error = regmap_multi_reg_write(priv->regmap, data->init_seq, data->init_nseq);
+	if (error)
+		return dev_err_probe(dev, error, "failed to initialize PHY registers\n");
 
 	priv->rcdev.ops = &rzv2h_usbphy_reset_ops;
 	priv->rcdev.of_reset_n_cells = 0;
@@ -169,23 +160,32 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev)
  * initialization values required to prepare the PHY to receive
  * assert and deassert requests.
  */
-static const struct rzv2h_usb2phy_regval rzv2h_init_vals[] = {
-	{ .reg = 0xc10, .val = 0x67c },
-	{ .reg = 0xc14, .val = 0x1f },
-	{ .reg = 0x600, .val = 0x909 },
+static const struct reg_sequence rzv2h_init_seq[] = {
+	{ .reg = 0xc10, .def = 0x67c },
+	{ .reg = 0xc14, .def = 0x01f },
+	{ .reg = 0x600, .def = 0x909 },
+};
+
+static const struct reg_sequence rzv2h_assert_seq[] = {
+	{ .reg = 0xb04, .def = 0x303 },
+	{ .reg = 0x000, .def = 0x206, .delay_us = 11 },
+};
+
+static const struct reg_sequence rzv2h_deassert_seq[] = {
+	{ .reg = 0x000, .def = 0x200 },
+	{ .reg = 0xb04, .def = 0x003 },
+	{ .reg = 0x000, .def = 0x000 },
 };
 
 static const struct rzv2h_usb2phy_reset_of_data rzv2h_reset_of_data = {
-	.init_vals = rzv2h_init_vals,
-	.init_val_count = ARRAY_SIZE(rzv2h_init_vals),
+	.init_seq = rzv2h_init_seq,
+	.init_nseq = ARRAY_SIZE(rzv2h_init_seq),
+	.assert_seq = rzv2h_assert_seq,
+	.assert_nseq = ARRAY_SIZE(rzv2h_assert_seq),
+	.deassert_seq = rzv2h_deassert_seq,
+	.deassert_nseq = ARRAY_SIZE(rzv2h_deassert_seq),
 	.reset_reg = 0,
-	.reset_assert_val = 0x206,
 	.reset_status_bits = BIT(2),
-	.reset_deassert_val = 0x200,
-	.reset_release_val = 0x0,
-	.reset2_reg = 0xb04,
-	.reset2_acquire_val = 0x303,
-	.reset2_release_val = 0x3,
 };
 
 static const struct of_device_id rzv2h_usb2phy_reset_of_match[] = {
-- 
2.43.0


^ permalink raw reply related

* [PATCH v10 3/5] dt-bindings: reset: renesas,rzv2h-usb2phy: Document RZ/G3E USB2PHY reset
From: Tommaso Merciai @ 2026-04-01 15:16 UTC (permalink / raw)
  To: tomm.merciai, peda, p.zabel
  Cc: linux-renesas-soc, biju.das.jz, Tommaso Merciai, Fabrizio Castro,
	Lad Prabhakar, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Arnd Bergmann, Ulf Hansson,
	Josua Mayer, devicetree, linux-kernel, Conor Dooley
In-Reply-To: <cover.1775047175.git.tommaso.merciai.xr@bp.renesas.com>

Document USB2PHY reset controller bindings for RZ/G3E ("R9A09G047") SoC.

The RZ/G3E USB2PHY reset controller is functionally identical to the one
found on the RZ/V2H(P), so no driver changes are needed. The existing
"renesas,r9a09g057-usb2phy-reset" will be used as a fallback compatible
for this IP.

Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
---
v9->v10:
 - No changes

v8->v9:
 - No changes

v7->v8:
 - No changes

v6->v7:
 - No changes

v5->v6:
 - Fixed commit msg

v4->v5:
 - No changes

v3->v4:
 - No changes

v2->v3:
 - No changes

v1->v2:
 - Collected CDooley tag

 .../bindings/reset/renesas,rzv2h-usb2phy-reset.yaml           | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml
index 7ed0980b9ee1..66650ef8f772 100644
--- a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml
+++ b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml
@@ -17,7 +17,9 @@ properties:
   compatible:
     oneOf:
       - items:
-          - const: renesas,r9a09g056-usb2phy-reset # RZ/V2N
+          - enum:
+              - renesas,r9a09g047-usb2phy-reset # RZ/G3E
+              - renesas,r9a09g056-usb2phy-reset # RZ/V2N
           - const: renesas,r9a09g057-usb2phy-reset
 
       - const: renesas,r9a09g057-usb2phy-reset # RZ/V2H(P)
-- 
2.43.0


^ 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