From 0747740a123b844a87f7b86977845ccaca15362b Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:19 +0300 Subject: [PATCH 01/74] dt-bindings: leds: Document TI LM3533 LED controller Document the LM3533 - a complete power source for backlight, keypad and indicator LEDs in smartphone handsets. The high-voltage inductive boost converter provides the power for two series LED strings display backlight and keypad functions. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Jonathan Cameron #for light sensor Reviewed-by: Daniel Thompson (RISCstar) #for backlight Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260731113632.158440-2-clamor95@gmail.com Signed-off-by: Lee Jones --- .../leds/backlight/ti,lm3533-backlight.yaml | 69 +++++++ .../bindings/leds/ti,lm3533-leds.yaml | 67 +++++++ .../devicetree/bindings/leds/ti,lm3533.yaml | 169 ++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/backlight/ti,lm3533-backlight.yaml create mode 100644 Documentation/devicetree/bindings/leds/ti,lm3533-leds.yaml create mode 100644 Documentation/devicetree/bindings/leds/ti,lm3533.yaml diff --git a/Documentation/devicetree/bindings/leds/backlight/ti,lm3533-backlight.yaml b/Documentation/devicetree/bindings/leds/backlight/ti,lm3533-backlight.yaml new file mode 100644 index 000000000000..a25846e9b375 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/backlight/ti,lm3533-backlight.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/backlight/ti,lm3533-backlight.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI LM3533 high voltage series LED strings + +description: + This is part of the TI LM3533 MFD device. It represents two high voltage series + LED strings for display backlight controlled by the TI LM3533. + +maintainers: + - Svyatoslav Ryhel + +allOf: + - $ref: /schemas/leds/backlight/common.yaml# + +properties: + compatible: + const: ti,lm3533-backlight + + reg: + description: Control bank selection (0 = bank A, 1 = bank B). + maximum: 1 + + led-max-microamp: + description: maximum current in uA with a 800 uA step. + minimum: 5000 + maximum: 29800 + default: 5000 + + led-sources: + description: | + HVLED strings associated with this control bank: + 0 - HVLED1 + 1 - HVLED2 + minItems: 1 + maxItems: 2 + items: + maximum: 1 + + ti,pwm-config-mask: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Control Bank PWM Configuration Register mask that allows to configure + PWM input in Zones 0-4 + BIT(0) - PWM Input is enabled + BIT(1) - PWM Input is enabled in Zone 0 + BIT(2) - PWM Input is enabled in Zone 1 + BIT(3) - PWM Input is enabled in Zone 2 + BIT(4) - PWM Input is enabled in Zone 3 + BIT(5) - PWM Input is enabled in Zone 4 + default: 0 + + ti,linear-mapping-mode: + description: + Enable linear mapping mode. If disabled, then it will use exponential + mapping mode in which the ramp up/down appears to have a more uniform + transition to the human eye. + type: boolean + +required: + - compatible + - reg + +unevaluatedProperties: false + +# see ti,lm3533.yaml for an example diff --git a/Documentation/devicetree/bindings/leds/ti,lm3533-leds.yaml b/Documentation/devicetree/bindings/leds/ti,lm3533-leds.yaml new file mode 100644 index 000000000000..d8dbefef3323 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/ti,lm3533-leds.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/ti,lm3533-leds.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI LM3533 low voltage control banks for individual LEDs + +description: + This is part of the TI LM3533 MFD device. It represents four low voltage + control banks for individual LEDs provided by the TI LM3533. + +maintainers: + - Svyatoslav Ryhel + +allOf: + - $ref: /schemas/leds/common.yaml# + +properties: + compatible: + const: ti,lm3533-leds + + reg: + description: + Control bank selection (2 = bank C, 3 = bank D, 4 = bank E, 5 = bank F). + minimum: 2 + maximum: 5 + + led-max-microamp: + description: maximum current in uA with a 800 uA step. + minimum: 5000 + maximum: 29800 + default: 5000 + + led-sources: + description: | + LVLED associated with this control bank. May be more than 1 source per bank. + 0 - LVLED1 + 1 - LVLED2 + 2 - LVLED3 + 3 - LVLED4 + 4 - LVLED5 + minItems: 1 + maxItems: 5 + items: + maximum: 4 + + ti,pwm-config-mask: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Control Bank PWM Configuration Register mask that allows to configure + PWM input in Zones 0-4 + BIT(0) - PWM Input is enabled + BIT(1) - PWM Input is enabled in Zone 0 + BIT(2) - PWM Input is enabled in Zone 1 + BIT(3) - PWM Input is enabled in Zone 2 + BIT(4) - PWM Input is enabled in Zone 3 + BIT(5) - PWM Input is enabled in Zone 4 + default: 0 + +required: + - compatible + - reg + +unevaluatedProperties: false + +# see ti,lm3533.yaml for an example diff --git a/Documentation/devicetree/bindings/leds/ti,lm3533.yaml b/Documentation/devicetree/bindings/leds/ti,lm3533.yaml new file mode 100644 index 000000000000..bdc05a9f3d66 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/ti,lm3533.yaml @@ -0,0 +1,169 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/ti,lm3533.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI LM3533 Complete Lighting Power Solution + +description: > + The LM3533 is a complete power source for backlight, keypad, and indicator LEDs + in smartphone handsets. The high-voltage inductive boost converter provides the + power for two high voltage series LED strings for display backlight and four low + voltage control banks for individual LEDs. Additionally, LM3533 features an + interface for an external light sensor. + + https://www.ti.com/product/LM3533 + +maintainers: + - Svyatoslav Ryhel + +properties: + compatible: + const: ti,lm3533 + + reg: + maxItems: 1 + + enable-gpios: + description: GPIO connected to the HWEN pin. + maxItems: 1 + + vin-supply: + description: Supply connected to the IN line (2.7 V to 5.5 V). + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + ti,boost-ovp-microvolt: + description: boost OVP select (16V, 24V, 32V, 40V) + enum: [ 16000000, 24000000, 32000000, 40000000 ] + default: 16000000 + + ti,boost-freq-hz: + description: boost frequency select (500KHz or 1MHz) + enum: [ 500000, 1000000 ] + default: 500000 + + light-sensor: + type: object + additionalProperties: false + + properties: + compatible: + const: ti,lm3533-als + + interrupts: + maxItems: 1 + + ti,resistor-ohms: + description: + Internal configuration resistor value when light sensor is in + Analog Sensor mode and PWM mode is disabled. The expectation is + the input is a current from the external analog light sensor and + this is used to convert it to a voltage within the target range. + minimum: 1575 + maximum: 200000 + + ti,pwm-mode: + type: boolean + description: + Switch for mode in which light sensor interface is running. If + this property is set then the light sensor interface is running + in PWM mode, internal resistor value is set to high-impedance (0) + and ti,resistor-ohms property is ignored. + + required: + - compatible + + oneOf: + - required: + - ti,resistor-ohms + - required: + - ti,pwm-mode + +patternProperties: + "^backlight@[01]$": + $ref: /schemas/leds/backlight/ti,lm3533-backlight.yaml# + + "^led@[2-5]$": + $ref: /schemas/leds/ti,lm3533-leds.yaml# + +required: + - compatible + - reg + - '#address-cells' + - '#size-cells' + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + led-controller@36 { + compatible = "ti,lm3533"; + reg = <0x36>; + + enable-gpios = <&gpio 110 GPIO_ACTIVE_HIGH>; + vin-supply = <&vdd_3v3_bat>; + + ti,boost-ovp-microvolt = <24000000>; + ti,boost-freq-hz = <500000>; + + #address-cells = <1>; + #size-cells = <0>; + + backlight@0 { + compatible = "ti,lm3533-backlight"; + reg = <0>; + + default-brightness = <113>; + + led-max-microamp = <23400>; + led-sources = <0 1>; + }; + + led@2 { + compatible = "ti,lm3533-leds"; + reg = <2>; + + led-max-microamp = <23400>; + led-sources = <0 1>; + }; + + led@4 { + compatible = "ti,lm3533-leds"; + reg = <4>; + + led-max-microamp = <23400>; + led-sources = <2>; + }; + + led@5 { + compatible = "ti,lm3533-leds"; + reg = <5>; + + led-max-microamp = <23400>; + led-sources = <3 4>; + }; + + light-sensor { + compatible = "ti,lm3533-als"; + + interrupt-parent = <&gpio>; + interrupts = <80 IRQ_TYPE_LEVEL_LOW>; + + ti,pwm-mode; + }; + }; + }; +... From f022c47f4843462cd02f1163f624631f7f4e409e Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:20 +0300 Subject: [PATCH 02/74] mfd: lm3533: Remove driver specific regmap wrappers Remove driver-specific regmap wrappers in favor of using regmap helpers directly. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Andy Shevchenko Acked-by: Jonathan Cameron Link: https://patch.msgid.link/20260731113632.158440-3-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/iio/light/lm3533-als.c | 79 +++++++++++++---------------- drivers/leds/leds-lm3533.c | 62 ++++++++-------------- drivers/mfd/lm3533-core.c | 73 ++++---------------------- drivers/mfd/lm3533-ctrlbank.c | 28 +++++----- drivers/video/backlight/lm3533_bl.c | 37 +++++--------- include/linux/mfd/lm3533.h | 15 ++---- 6 files changed, 100 insertions(+), 194 deletions(-) diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index 99f0b903018c..44b104c2d77f 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -64,7 +65,7 @@ static int lm3533_als_get_adc(struct iio_dev *indio_dev, bool average, { struct lm3533_als *als = iio_priv(indio_dev); u8 reg; - u8 val; + u32 val; int ret; if (average) @@ -72,7 +73,7 @@ static int lm3533_als_get_adc(struct iio_dev *indio_dev, bool average, else reg = LM3533_REG_ALS_READ_ADC_RAW; - ret = lm3533_read(als->lm3533, reg, &val); + ret = regmap_read(als->lm3533->regmap, reg, &val); if (ret) { dev_err(&indio_dev->dev, "failed to read adc\n"); return ret; @@ -86,10 +87,10 @@ static int lm3533_als_get_adc(struct iio_dev *indio_dev, bool average, static int _lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone) { struct lm3533_als *als = iio_priv(indio_dev); - u8 val; + u32 val; int ret; - ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val); + ret = regmap_read(als->lm3533->regmap, LM3533_REG_ALS_ZONE_INFO, &val); if (ret) { dev_err(&indio_dev->dev, "failed to read zone\n"); return ret; @@ -127,7 +128,7 @@ static inline u8 lm3533_als_get_target_reg(unsigned channel, unsigned zone) } static int lm3533_als_get_target(struct iio_dev *indio_dev, unsigned channel, - unsigned zone, u8 *val) + unsigned zone, u32 *val) { struct lm3533_als *als = iio_priv(indio_dev); u8 reg; @@ -140,7 +141,7 @@ static int lm3533_als_get_target(struct iio_dev *indio_dev, unsigned channel, return -EINVAL; reg = lm3533_als_get_target_reg(channel, zone); - ret = lm3533_read(als->lm3533, reg, val); + ret = regmap_read(als->lm3533->regmap, reg, val); if (ret) dev_err(&indio_dev->dev, "failed to get target current\n"); @@ -161,7 +162,7 @@ static int lm3533_als_set_target(struct iio_dev *indio_dev, unsigned channel, return -EINVAL; reg = lm3533_als_get_target_reg(channel, zone); - ret = lm3533_write(als->lm3533, reg, val); + ret = regmap_write(als->lm3533->regmap, reg, val); if (ret) dev_err(&indio_dev->dev, "failed to set target current\n"); @@ -171,8 +172,8 @@ static int lm3533_als_set_target(struct iio_dev *indio_dev, unsigned channel, static int lm3533_als_get_current(struct iio_dev *indio_dev, unsigned channel, int *val) { + u32 target; u8 zone; - u8 target; int ret; ret = lm3533_als_get_zone(indio_dev, &zone); @@ -271,16 +272,10 @@ static irqreturn_t lm3533_als_isr(int irq, void *dev_id) static int lm3533_als_set_int_mode(struct iio_dev *indio_dev, int enable) { struct lm3533_als *als = iio_priv(indio_dev); - u8 mask = LM3533_ALS_INT_ENABLE_MASK; - u8 val; int ret; - if (enable) - val = mask; - else - val = 0; - - ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, val, mask); + ret = regmap_assign_bits(als->lm3533->regmap, LM3533_REG_ALS_ZONE_INFO, + LM3533_ALS_INT_ENABLE_MASK, enable); if (ret) { dev_err(&indio_dev->dev, "failed to set int mode %d\n", enable); @@ -293,17 +288,16 @@ static int lm3533_als_set_int_mode(struct iio_dev *indio_dev, int enable) static int lm3533_als_get_int_mode(struct iio_dev *indio_dev, int *enable) { struct lm3533_als *als = iio_priv(indio_dev); - u8 mask = LM3533_ALS_INT_ENABLE_MASK; - u8 val; int ret; - ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val); - if (ret) { + ret = regmap_test_bits(als->lm3533->regmap, LM3533_REG_ALS_ZONE_INFO, + LM3533_ALS_INT_ENABLE_MASK); + if (ret < 0) { dev_err(&indio_dev->dev, "failed to get int mode\n"); return ret; } - *enable = !!(val & mask); + *enable = ret; return 0; } @@ -316,7 +310,7 @@ static inline u8 lm3533_als_get_threshold_reg(unsigned nr, bool raising) } static int lm3533_als_get_threshold(struct iio_dev *indio_dev, unsigned nr, - bool raising, u8 *val) + bool raising, u32 *val) { struct lm3533_als *als = iio_priv(indio_dev); u8 reg; @@ -326,7 +320,7 @@ static int lm3533_als_get_threshold(struct iio_dev *indio_dev, unsigned nr, return -EINVAL; reg = lm3533_als_get_threshold_reg(nr, raising); - ret = lm3533_read(als->lm3533, reg, val); + ret = regmap_read(als->lm3533->regmap, reg, val); if (ret) dev_err(&indio_dev->dev, "failed to get threshold\n"); @@ -337,8 +331,8 @@ static int lm3533_als_set_threshold(struct iio_dev *indio_dev, unsigned nr, bool raising, u8 val) { struct lm3533_als *als = iio_priv(indio_dev); - u8 val2; u8 reg, reg2; + u32 val2; int ret; if (nr > LM3533_ALS_THRESH_MAX) @@ -348,7 +342,7 @@ static int lm3533_als_set_threshold(struct iio_dev *indio_dev, unsigned nr, reg2 = lm3533_als_get_threshold_reg(nr, !raising); mutex_lock(&als->thresh_mutex); - ret = lm3533_read(als->lm3533, reg2, &val2); + ret = regmap_read(als->lm3533->regmap, reg2, &val2); if (ret) { dev_err(&indio_dev->dev, "failed to get threshold\n"); goto out; @@ -363,7 +357,7 @@ static int lm3533_als_set_threshold(struct iio_dev *indio_dev, unsigned nr, goto out; } - ret = lm3533_write(als->lm3533, reg, val); + ret = regmap_write(als->lm3533->regmap, reg, val); if (ret) { dev_err(&indio_dev->dev, "failed to set threshold\n"); goto out; @@ -375,11 +369,11 @@ static int lm3533_als_set_threshold(struct iio_dev *indio_dev, unsigned nr, } static int lm3533_als_get_hysteresis(struct iio_dev *indio_dev, unsigned nr, - u8 *val) + u32 *val) { struct lm3533_als *als = iio_priv(indio_dev); - u8 falling; - u8 raising; + u32 falling; + u32 raising; int ret; if (nr > LM3533_ALS_THRESH_MAX) @@ -503,7 +497,7 @@ static ssize_t show_als_attr(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct lm3533_als_attribute *als_attr = to_lm3533_als_attr(attr); - u8 val; + u32 val; int ret; switch (als_attr->type) { @@ -716,16 +710,10 @@ static const struct attribute_group lm3533_als_attribute_group = { static int lm3533_als_set_input_mode(struct lm3533_als *als, bool pwm_mode) { - u8 mask = LM3533_ALS_INPUT_MODE_MASK; - u8 val; int ret; - if (pwm_mode) - val = mask; /* pwm input */ - else - val = 0; /* analog input */ - - ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, val, mask); + ret = regmap_assign_bits(als->lm3533->regmap, LM3533_REG_ALS_CONF, + LM3533_ALS_INPUT_MODE_MASK, pwm_mode); if (ret) { dev_err(&als->pdev->dev, "failed to set input mode %d\n", pwm_mode); @@ -744,7 +732,8 @@ static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val) return -EINVAL; } - ret = lm3533_write(als->lm3533, LM3533_REG_ALS_RESISTOR_SELECT, val); + ret = regmap_write(als->lm3533->regmap, LM3533_REG_ALS_RESISTOR_SELECT, + val); if (ret) { dev_err(&als->pdev->dev, "failed to set resistor\n"); return ret; @@ -774,11 +763,11 @@ static int lm3533_als_setup(struct lm3533_als *als, static int lm3533_als_setup_irq(struct lm3533_als *als, void *dev) { - u8 mask = LM3533_ALS_INT_ENABLE_MASK; int ret; /* Make sure interrupts are disabled. */ - ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, 0, mask); + ret = regmap_clear_bits(als->lm3533->regmap, LM3533_REG_ALS_ZONE_INFO, + LM3533_ALS_INT_ENABLE_MASK); if (ret) { dev_err(&als->pdev->dev, "failed to disable interrupts\n"); return ret; @@ -798,10 +787,10 @@ static int lm3533_als_setup_irq(struct lm3533_als *als, void *dev) static int lm3533_als_enable(struct lm3533_als *als) { - u8 mask = LM3533_ALS_ENABLE_MASK; int ret; - ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, mask, mask); + ret = regmap_set_bits(als->lm3533->regmap, LM3533_REG_ALS_CONF, + LM3533_ALS_ENABLE_MASK); if (ret) dev_err(&als->pdev->dev, "failed to enable ALS\n"); @@ -810,10 +799,10 @@ static int lm3533_als_enable(struct lm3533_als *als) static int lm3533_als_disable(struct lm3533_als *als) { - u8 mask = LM3533_ALS_ENABLE_MASK; int ret; - ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, 0, mask); + ret = regmap_clear_bits(als->lm3533->regmap, LM3533_REG_ALS_CONF, + LM3533_ALS_ENABLE_MASK); if (ret) dev_err(&als->pdev->dev, "failed to disable ALS\n"); diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index 45795f2a1042..335c758071f3 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -80,7 +81,6 @@ static inline u8 lm3533_led_get_pattern_reg(struct lm3533_led *led, static int lm3533_led_pattern_enable(struct lm3533_led *led, int enable) { u8 mask; - u8 val; int pattern; int state; int ret = 0; @@ -96,12 +96,8 @@ static int lm3533_led_pattern_enable(struct lm3533_led *led, int enable) pattern = lm3533_led_get_pattern(led); mask = 1 << (2 * pattern); - if (enable) - val = mask; - else - val = 0; - - ret = lm3533_update(led->lm3533, LM3533_REG_PATTERN_ENABLE, val, mask); + ret = regmap_assign_bits(led->lm3533->regmap, + LM3533_REG_PATTERN_ENABLE, mask, enable); if (ret) { dev_err(led->cdev.dev, "failed to enable pattern %d (%d)\n", pattern, enable); @@ -131,7 +127,7 @@ static int lm3533_led_set(struct led_classdev *cdev, static enum led_brightness lm3533_led_get(struct led_classdev *cdev) { struct lm3533_led *led = to_lm3533_led(cdev); - u8 val; + u32 val; int ret; ret = lm3533_ctrlbank_get_brightness(&led->cb, &val); @@ -259,7 +255,7 @@ static u8 lm3533_led_delay_set(struct lm3533_led *led, u8 base, dev_dbg(led->cdev.dev, "%s - %lu: %u (0x%02x)\n", __func__, *delay, t, val); reg = lm3533_led_get_pattern_reg(led, base); - ret = lm3533_write(led->lm3533, reg, val); + ret = regmap_write(led->lm3533->regmap, reg, val); if (ret) dev_err(led->cdev.dev, "failed to set delay (%02x)\n", reg); @@ -337,10 +333,10 @@ static ssize_t show_risefalltime(struct device *dev, struct lm3533_led *led = to_lm3533_led(led_cdev); ssize_t ret; u8 reg; - u8 val; + u32 val; reg = lm3533_led_get_pattern_reg(led, base); - ret = lm3533_read(led->lm3533, reg, &val); + ret = regmap_read(led->lm3533->regmap, reg, &val); if (ret) return ret; @@ -375,7 +371,7 @@ static ssize_t store_risefalltime(struct device *dev, return -EINVAL; reg = lm3533_led_get_pattern_reg(led, base); - ret = lm3533_write(led->lm3533, reg, val); + ret = regmap_write(led->lm3533->regmap, reg, val); if (ret) return ret; @@ -405,11 +401,11 @@ static ssize_t show_als_channel(struct device *dev, struct lm3533_led *led = to_lm3533_led(led_cdev); unsigned channel; u8 reg; - u8 val; + u32 val; int ret; reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); - ret = lm3533_read(led->lm3533, reg, &val); + ret = regmap_read(led->lm3533->regmap, reg, &val); if (ret) return ret; @@ -441,7 +437,7 @@ static ssize_t store_als_channel(struct device *dev, mask = LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK; val = channel - 1; - ret = lm3533_update(led->lm3533, reg, val, mask); + ret = regmap_update_bits(led->lm3533->regmap, reg, mask, val); if (ret) return ret; @@ -455,11 +451,11 @@ static ssize_t show_als_en(struct device *dev, struct lm3533_led *led = to_lm3533_led(led_cdev); bool enable; u8 reg; - u8 val; + u32 val; int ret; reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); - ret = lm3533_read(led->lm3533, reg, &val); + ret = regmap_read(led->lm3533->regmap, reg, &val); if (ret) return ret; @@ -476,22 +472,15 @@ static ssize_t store_als_en(struct device *dev, struct lm3533_led *led = to_lm3533_led(led_cdev); unsigned enable; u8 reg; - u8 mask; - u8 val; int ret; if (kstrtouint(buf, 0, &enable)) return -EINVAL; reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); - mask = LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK; - if (enable) - val = mask; - else - val = 0; - - ret = lm3533_update(led->lm3533, reg, val, mask); + ret = regmap_assign_bits(led->lm3533->regmap, reg, + LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK, enable); if (ret) return ret; @@ -504,12 +493,12 @@ static ssize_t show_linear(struct device *dev, struct led_classdev *led_cdev = dev_get_drvdata(dev); struct lm3533_led *led = to_lm3533_led(led_cdev); u8 reg; - u8 val; + u32 val; int linear; int ret; reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); - ret = lm3533_read(led->lm3533, reg, &val); + ret = regmap_read(led->lm3533->regmap, reg, &val); if (ret) return ret; @@ -529,22 +518,15 @@ static ssize_t store_linear(struct device *dev, struct lm3533_led *led = to_lm3533_led(led_cdev); unsigned long linear; u8 reg; - u8 mask; - u8 val; int ret; if (kstrtoul(buf, 0, &linear)) return -EINVAL; reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); - mask = LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK; - if (linear) - val = mask; - else - val = 0; - - ret = lm3533_update(led->lm3533, reg, val, mask); + ret = regmap_assign_bits(led->lm3533->regmap, reg, + LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK, linear); if (ret) return ret; @@ -557,7 +539,7 @@ static ssize_t show_pwm(struct device *dev, { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct lm3533_led *led = to_lm3533_led(led_cdev); - u8 val; + u32 val; int ret; ret = lm3533_ctrlbank_get_pwm(&led->cb, &val); @@ -573,10 +555,10 @@ static ssize_t store_pwm(struct device *dev, { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct lm3533_led *led = to_lm3533_led(led_cdev); - u8 val; + u32 val; int ret; - if (kstrtou8(buf, 0, &val)) + if (kstrtou32(buf, 0, &val)) return -EINVAL; ret = lm3533_ctrlbank_set_pwm(&led->cb, val); diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c index 0a2409d00b2e..f9bdc0a1d9be 100644 --- a/drivers/mfd/lm3533-core.c +++ b/drivers/mfd/lm3533-core.c @@ -80,66 +80,14 @@ static struct mfd_cell lm3533_led_devs[] = { }, }; -int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val) -{ - int tmp; - int ret; - - ret = regmap_read(lm3533->regmap, reg, &tmp); - if (ret < 0) { - dev_err(lm3533->dev, "failed to read register %02x: %d\n", - reg, ret); - return ret; - } - - *val = tmp; - - dev_dbg(lm3533->dev, "read [%02x]: %02x\n", reg, *val); - - return ret; -} -EXPORT_SYMBOL_GPL(lm3533_read); - -int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val) -{ - int ret; - - dev_dbg(lm3533->dev, "write [%02x]: %02x\n", reg, val); - - ret = regmap_write(lm3533->regmap, reg, val); - if (ret < 0) { - dev_err(lm3533->dev, "failed to write register %02x: %d\n", - reg, ret); - } - - return ret; -} -EXPORT_SYMBOL_GPL(lm3533_write); - -int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask) -{ - int ret; - - dev_dbg(lm3533->dev, "update [%02x]: %02x/%02x\n", reg, val, mask); - - ret = regmap_update_bits(lm3533->regmap, reg, mask, val); - if (ret < 0) { - dev_err(lm3533->dev, "failed to update register %02x: %d\n", - reg, ret); - } - - return ret; -} -EXPORT_SYMBOL_GPL(lm3533_update); - static int lm3533_set_boost_freq(struct lm3533 *lm3533, enum lm3533_boost_freq freq) { int ret; - ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM, - freq << LM3533_BOOST_FREQ_SHIFT, - LM3533_BOOST_FREQ_MASK); + ret = regmap_update_bits(lm3533->regmap, LM3533_REG_BOOST_PWM, + LM3533_BOOST_FREQ_MASK, + freq << LM3533_BOOST_FREQ_SHIFT); if (ret) dev_err(lm3533->dev, "failed to set boost frequency\n"); @@ -152,9 +100,9 @@ static int lm3533_set_boost_ovp(struct lm3533 *lm3533, { int ret; - ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM, - ovp << LM3533_BOOST_OVP_SHIFT, - LM3533_BOOST_OVP_MASK); + ret = regmap_update_bits(lm3533->regmap, LM3533_REG_BOOST_PWM, + LM3533_BOOST_OVP_MASK, + ovp << LM3533_BOOST_OVP_SHIFT); if (ret) dev_err(lm3533->dev, "failed to set boost ovp\n"); @@ -181,7 +129,8 @@ static int lm3533_set_hvled_config(struct lm3533 *lm3533, u8 hvled, u8 bl) mask = LM3533_BL_ID_MASK << shift; val = bl << shift; - ret = lm3533_update(lm3533, LM3533_REG_OUTPUT_CONF1, val, mask); + ret = regmap_update_bits(lm3533->regmap, LM3533_REG_OUTPUT_CONF1, + mask, val); if (ret) dev_err(lm3533->dev, "failed to set hvled config\n"); @@ -216,7 +165,7 @@ static int lm3533_set_lvled_config(struct lm3533 *lm3533, u8 lvled, u8 led) mask = LM3533_LED_ID_MASK << shift; val = led << shift; - ret = lm3533_update(lm3533, reg, val, mask); + ret = regmap_update_bits(lm3533->regmap, reg, mask, val); if (ret) dev_err(lm3533->dev, "failed to set lvled config\n"); @@ -258,7 +207,7 @@ static ssize_t show_output(struct device *dev, struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr); int id = lattr->u.output.id; u8 reg; - u8 val; + u32 val; u8 mask; int shift; int ret; @@ -278,7 +227,7 @@ static ssize_t show_output(struct device *dev, mask = LM3533_LED_ID_MASK << shift; } - ret = lm3533_read(lm3533, reg, &val); + ret = regmap_read(lm3533->regmap, reg, &val); if (ret) return ret; diff --git a/drivers/mfd/lm3533-ctrlbank.c b/drivers/mfd/lm3533-ctrlbank.c index 2537dfade51c..c2b5fd38b81d 100644 --- a/drivers/mfd/lm3533-ctrlbank.c +++ b/drivers/mfd/lm3533-ctrlbank.c @@ -9,6 +9,7 @@ #include #include +#include #include @@ -38,8 +39,8 @@ int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb) dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id); mask = 1 << cb->id; - ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE, - mask, mask); + ret = regmap_set_bits(cb->lm3533->regmap, LM3533_REG_CTRLBANK_ENABLE, + mask); if (ret) dev_err(cb->dev, "failed to enable ctrlbank %d\n", cb->id); @@ -55,7 +56,8 @@ int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb) dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id); mask = 1 << cb->id; - ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE, 0, mask); + ret = regmap_clear_bits(cb->lm3533->regmap, LM3533_REG_CTRLBANK_ENABLE, + mask); if (ret) dev_err(cb->dev, "failed to disable ctrlbank %d\n", cb->id); @@ -68,7 +70,7 @@ EXPORT_SYMBOL_GPL(lm3533_ctrlbank_disable); * * imax 5000 - 29800 uA (800 uA step) */ -int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u16 imax) +int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u32 imax) { u8 reg; u8 val; @@ -80,7 +82,7 @@ int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u16 imax) val = (imax - LM3533_MAX_CURRENT_MIN) / LM3533_MAX_CURRENT_STEP; reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_MAX_CURRENT_BASE); - ret = lm3533_write(cb->lm3533, reg, val); + ret = regmap_write(cb->lm3533->regmap, reg, val); if (ret) dev_err(cb->dev, "failed to set max current\n"); @@ -88,13 +90,13 @@ int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u16 imax) } EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_max_current); -int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u8 val) +int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u32 val) { u8 reg; int ret; reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_BRIGHTNESS_BASE); - ret = lm3533_write(cb->lm3533, reg, val); + ret = regmap_write(cb->lm3533->regmap, reg, val); if (ret) dev_err(cb->dev, "failed to set brightness\n"); @@ -102,13 +104,13 @@ int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u8 val) } EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_brightness); -int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u8 *val) +int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u32 *val) { u8 reg; int ret; reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_BRIGHTNESS_BASE); - ret = lm3533_read(cb->lm3533, reg, val); + ret = regmap_read(cb->lm3533->regmap, reg, val); if (ret) dev_err(cb->dev, "failed to get brightness\n"); @@ -126,7 +128,7 @@ EXPORT_SYMBOL_GPL(lm3533_ctrlbank_get_brightness); * bit 1 - PWM-input enabled in Zone 0 * bit 0 - PWM-input enabled */ -int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u8 val) +int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u32 val) { u8 reg; int ret; @@ -135,7 +137,7 @@ int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u8 val) return -EINVAL; reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_PWM_BASE); - ret = lm3533_write(cb->lm3533, reg, val); + ret = regmap_write(cb->lm3533->regmap, reg, val); if (ret) dev_err(cb->dev, "failed to set PWM mask\n"); @@ -143,13 +145,13 @@ int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u8 val) } EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_pwm); -int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u8 *val) +int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u32 *val) { u8 reg; int ret; reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_PWM_BASE); - ret = lm3533_read(cb->lm3533, reg, val); + ret = regmap_read(cb->lm3533->regmap, reg, val); if (ret) dev_err(cb->dev, "failed to get PWM mask\n"); diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c index babfd3ceec86..cd21c64b6485 100644 --- a/drivers/video/backlight/lm3533_bl.c +++ b/drivers/video/backlight/lm3533_bl.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -45,7 +46,7 @@ static int lm3533_bl_update_status(struct backlight_device *bd) static int lm3533_bl_get_brightness(struct backlight_device *bd) { struct lm3533_bl *bl = bl_get_data(bd); - u8 val; + u32 val; int ret; ret = lm3533_ctrlbank_get_brightness(&bl->cb, &val); @@ -82,12 +83,12 @@ static ssize_t show_als_en(struct device *dev, { struct lm3533_bl *bl = dev_get_drvdata(dev); int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); - u8 val; + u32 val; u8 mask; bool enable; int ret; - ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val); + ret = regmap_read(bl->lm3533->regmap, LM3533_REG_CTRLBANK_AB_BCONF, &val); if (ret) return ret; @@ -104,7 +105,6 @@ static ssize_t store_als_en(struct device *dev, struct lm3533_bl *bl = dev_get_drvdata(dev); int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); int enable; - u8 val; u8 mask; int ret; @@ -113,13 +113,8 @@ static ssize_t store_als_en(struct device *dev, mask = 1 << (2 * ctrlbank); - if (enable) - val = mask; - else - val = 0; - - ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val, - mask); + ret = regmap_assign_bits(bl->lm3533->regmap, LM3533_REG_CTRLBANK_AB_BCONF, + mask, enable); if (ret) return ret; @@ -130,12 +125,12 @@ static ssize_t show_linear(struct device *dev, struct device_attribute *attr, char *buf) { struct lm3533_bl *bl = dev_get_drvdata(dev); - u8 val; + u32 val; u8 mask; int linear; int ret; - ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val); + ret = regmap_read(bl->lm3533->regmap, LM3533_REG_CTRLBANK_AB_BCONF, &val); if (ret) return ret; @@ -156,7 +151,6 @@ static ssize_t store_linear(struct device *dev, struct lm3533_bl *bl = dev_get_drvdata(dev); unsigned long linear; u8 mask; - u8 val; int ret; if (kstrtoul(buf, 0, &linear)) @@ -164,13 +158,8 @@ static ssize_t store_linear(struct device *dev, mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1); - if (linear) - val = mask; - else - val = 0; - - ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val, - mask); + ret = regmap_assign_bits(bl->lm3533->regmap, LM3533_REG_CTRLBANK_AB_BCONF, + mask, linear); if (ret) return ret; @@ -182,7 +171,7 @@ static ssize_t show_pwm(struct device *dev, char *buf) { struct lm3533_bl *bl = dev_get_drvdata(dev); - u8 val; + u32 val; int ret; ret = lm3533_ctrlbank_get_pwm(&bl->cb, &val); @@ -197,10 +186,10 @@ static ssize_t store_pwm(struct device *dev, const char *buf, size_t len) { struct lm3533_bl *bl = dev_get_drvdata(dev); - u8 val; + u32 val; int ret; - if (kstrtou8(buf, 0, &val)) + if (kstrtou32(buf, 0, &val)) return -EINVAL; ret = lm3533_ctrlbank_set_pwm(&bl->cb, val); diff --git a/include/linux/mfd/lm3533.h b/include/linux/mfd/lm3533.h index 69059a7a2ce5..3463b164d0c1 100644 --- a/include/linux/mfd/lm3533.h +++ b/include/linux/mfd/lm3533.h @@ -85,15 +85,10 @@ struct lm3533_platform_data { extern int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb); extern int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb); -extern int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u8 val); -extern int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u8 *val); -extern int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, - u16 imax); -extern int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u8 val); -extern int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u8 *val); - -extern int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val); -extern int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val); -extern int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask); +extern int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u32 val); +extern int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u32 *val); +extern int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u32 imax); +extern int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u32 val); +extern int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u32 *val); #endif /* __LINUX_MFD_LM3533_H */ From 06ea59b4135ed6c999bc33daca1abf51071f5f53 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:21 +0300 Subject: [PATCH 03/74] mfd: lm3533: Remove extern from shared functions in the header Using 'extern' is not required, drop them. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260731113632.158440-4-clamor95@gmail.com Signed-off-by: Lee Jones --- include/linux/mfd/lm3533.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/linux/mfd/lm3533.h b/include/linux/mfd/lm3533.h index 3463b164d0c1..c4c38876c09b 100644 --- a/include/linux/mfd/lm3533.h +++ b/include/linux/mfd/lm3533.h @@ -82,13 +82,13 @@ struct lm3533_platform_data { int num_leds; }; -extern int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb); -extern int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb); +int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb); +int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb); -extern int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u32 val); -extern int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u32 *val); -extern int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u32 imax); -extern int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u32 val); -extern int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u32 *val); +int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u32 val); +int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u32 *val); +int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u32 imax); +int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u32 val); +int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u32 *val); #endif /* __LINUX_MFD_LM3533_H */ From f6420210fbaf563de34c769f4a408790523bc36c Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:22 +0300 Subject: [PATCH 04/74] mfd: lm3533: Pass only regmap and light sensor presence to child devices Instead of passing the entire lm3533 core data structure, only pass the regmap and the light sensor presence flag to child devices. Signed-off-by: Svyatoslav Ryhel Acked-by: Jonathan Cameron #for-iio Link: https://patch.msgid.link/20260731113632.158440-5-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/iio/light/lm3533-als.c | 32 ++++++++++++++--------------- drivers/leds/leds-lm3533.c | 32 ++++++++++++++++------------- drivers/mfd/lm3533-ctrlbank.c | 14 ++++++------- drivers/video/backlight/lm3533_bl.c | 19 +++++++++-------- include/linux/mfd/lm3533.h | 2 +- 5 files changed, 53 insertions(+), 46 deletions(-) diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index 44b104c2d77f..a9af8e2b965f 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -49,7 +49,7 @@ struct lm3533_als { - struct lm3533 *lm3533; + struct regmap *regmap; struct platform_device *pdev; unsigned long flags; @@ -73,7 +73,7 @@ static int lm3533_als_get_adc(struct iio_dev *indio_dev, bool average, else reg = LM3533_REG_ALS_READ_ADC_RAW; - ret = regmap_read(als->lm3533->regmap, reg, &val); + ret = regmap_read(als->regmap, reg, &val); if (ret) { dev_err(&indio_dev->dev, "failed to read adc\n"); return ret; @@ -90,7 +90,7 @@ static int _lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone) u32 val; int ret; - ret = regmap_read(als->lm3533->regmap, LM3533_REG_ALS_ZONE_INFO, &val); + ret = regmap_read(als->regmap, LM3533_REG_ALS_ZONE_INFO, &val); if (ret) { dev_err(&indio_dev->dev, "failed to read zone\n"); return ret; @@ -141,7 +141,7 @@ static int lm3533_als_get_target(struct iio_dev *indio_dev, unsigned channel, return -EINVAL; reg = lm3533_als_get_target_reg(channel, zone); - ret = regmap_read(als->lm3533->regmap, reg, val); + ret = regmap_read(als->regmap, reg, val); if (ret) dev_err(&indio_dev->dev, "failed to get target current\n"); @@ -162,7 +162,7 @@ static int lm3533_als_set_target(struct iio_dev *indio_dev, unsigned channel, return -EINVAL; reg = lm3533_als_get_target_reg(channel, zone); - ret = regmap_write(als->lm3533->regmap, reg, val); + ret = regmap_write(als->regmap, reg, val); if (ret) dev_err(&indio_dev->dev, "failed to set target current\n"); @@ -274,7 +274,7 @@ static int lm3533_als_set_int_mode(struct iio_dev *indio_dev, int enable) struct lm3533_als *als = iio_priv(indio_dev); int ret; - ret = regmap_assign_bits(als->lm3533->regmap, LM3533_REG_ALS_ZONE_INFO, + ret = regmap_assign_bits(als->regmap, LM3533_REG_ALS_ZONE_INFO, LM3533_ALS_INT_ENABLE_MASK, enable); if (ret) { dev_err(&indio_dev->dev, "failed to set int mode %d\n", @@ -290,7 +290,7 @@ static int lm3533_als_get_int_mode(struct iio_dev *indio_dev, int *enable) struct lm3533_als *als = iio_priv(indio_dev); int ret; - ret = regmap_test_bits(als->lm3533->regmap, LM3533_REG_ALS_ZONE_INFO, + ret = regmap_test_bits(als->regmap, LM3533_REG_ALS_ZONE_INFO, LM3533_ALS_INT_ENABLE_MASK); if (ret < 0) { dev_err(&indio_dev->dev, "failed to get int mode\n"); @@ -320,7 +320,7 @@ static int lm3533_als_get_threshold(struct iio_dev *indio_dev, unsigned nr, return -EINVAL; reg = lm3533_als_get_threshold_reg(nr, raising); - ret = regmap_read(als->lm3533->regmap, reg, val); + ret = regmap_read(als->regmap, reg, val); if (ret) dev_err(&indio_dev->dev, "failed to get threshold\n"); @@ -342,7 +342,7 @@ static int lm3533_als_set_threshold(struct iio_dev *indio_dev, unsigned nr, reg2 = lm3533_als_get_threshold_reg(nr, !raising); mutex_lock(&als->thresh_mutex); - ret = regmap_read(als->lm3533->regmap, reg2, &val2); + ret = regmap_read(als->regmap, reg2, &val2); if (ret) { dev_err(&indio_dev->dev, "failed to get threshold\n"); goto out; @@ -357,7 +357,7 @@ static int lm3533_als_set_threshold(struct iio_dev *indio_dev, unsigned nr, goto out; } - ret = regmap_write(als->lm3533->regmap, reg, val); + ret = regmap_write(als->regmap, reg, val); if (ret) { dev_err(&indio_dev->dev, "failed to set threshold\n"); goto out; @@ -712,7 +712,7 @@ static int lm3533_als_set_input_mode(struct lm3533_als *als, bool pwm_mode) { int ret; - ret = regmap_assign_bits(als->lm3533->regmap, LM3533_REG_ALS_CONF, + ret = regmap_assign_bits(als->regmap, LM3533_REG_ALS_CONF, LM3533_ALS_INPUT_MODE_MASK, pwm_mode); if (ret) { dev_err(&als->pdev->dev, "failed to set input mode %d\n", @@ -732,7 +732,7 @@ static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val) return -EINVAL; } - ret = regmap_write(als->lm3533->regmap, LM3533_REG_ALS_RESISTOR_SELECT, + ret = regmap_write(als->regmap, LM3533_REG_ALS_RESISTOR_SELECT, val); if (ret) { dev_err(&als->pdev->dev, "failed to set resistor\n"); @@ -766,7 +766,7 @@ static int lm3533_als_setup_irq(struct lm3533_als *als, void *dev) int ret; /* Make sure interrupts are disabled. */ - ret = regmap_clear_bits(als->lm3533->regmap, LM3533_REG_ALS_ZONE_INFO, + ret = regmap_clear_bits(als->regmap, LM3533_REG_ALS_ZONE_INFO, LM3533_ALS_INT_ENABLE_MASK); if (ret) { dev_err(&als->pdev->dev, "failed to disable interrupts\n"); @@ -789,7 +789,7 @@ static int lm3533_als_enable(struct lm3533_als *als) { int ret; - ret = regmap_set_bits(als->lm3533->regmap, LM3533_REG_ALS_CONF, + ret = regmap_set_bits(als->regmap, LM3533_REG_ALS_CONF, LM3533_ALS_ENABLE_MASK); if (ret) dev_err(&als->pdev->dev, "failed to enable ALS\n"); @@ -801,7 +801,7 @@ static int lm3533_als_disable(struct lm3533_als *als) { int ret; - ret = regmap_clear_bits(als->lm3533->regmap, LM3533_REG_ALS_CONF, + ret = regmap_clear_bits(als->regmap, LM3533_REG_ALS_CONF, LM3533_ALS_ENABLE_MASK); if (ret) dev_err(&als->pdev->dev, "failed to disable ALS\n"); @@ -845,7 +845,7 @@ static int lm3533_als_probe(struct platform_device *pdev) indio_dev->modes = INDIO_DIRECT_MODE; als = iio_priv(indio_dev); - als->lm3533 = lm3533; + als->regmap = lm3533->regmap; als->pdev = pdev; als->irq = lm3533->irq; atomic_set(&als->zone, 0); diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index 335c758071f3..0cb0585eb960 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -42,13 +42,15 @@ struct lm3533_led { - struct lm3533 *lm3533; + struct regmap *regmap; struct lm3533_ctrlbank cb; struct led_classdev cdev; int id; struct mutex mutex; unsigned long flags; + + bool have_als; }; @@ -96,7 +98,7 @@ static int lm3533_led_pattern_enable(struct lm3533_led *led, int enable) pattern = lm3533_led_get_pattern(led); mask = 1 << (2 * pattern); - ret = regmap_assign_bits(led->lm3533->regmap, + ret = regmap_assign_bits(led->regmap, LM3533_REG_PATTERN_ENABLE, mask, enable); if (ret) { dev_err(led->cdev.dev, "failed to enable pattern %d (%d)\n", @@ -255,7 +257,7 @@ static u8 lm3533_led_delay_set(struct lm3533_led *led, u8 base, dev_dbg(led->cdev.dev, "%s - %lu: %u (0x%02x)\n", __func__, *delay, t, val); reg = lm3533_led_get_pattern_reg(led, base); - ret = regmap_write(led->lm3533->regmap, reg, val); + ret = regmap_write(led->regmap, reg, val); if (ret) dev_err(led->cdev.dev, "failed to set delay (%02x)\n", reg); @@ -336,7 +338,7 @@ static ssize_t show_risefalltime(struct device *dev, u32 val; reg = lm3533_led_get_pattern_reg(led, base); - ret = regmap_read(led->lm3533->regmap, reg, &val); + ret = regmap_read(led->regmap, reg, &val); if (ret) return ret; @@ -371,7 +373,7 @@ static ssize_t store_risefalltime(struct device *dev, return -EINVAL; reg = lm3533_led_get_pattern_reg(led, base); - ret = regmap_write(led->lm3533->regmap, reg, val); + ret = regmap_write(led->regmap, reg, val); if (ret) return ret; @@ -405,7 +407,7 @@ static ssize_t show_als_channel(struct device *dev, int ret; reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); - ret = regmap_read(led->lm3533->regmap, reg, &val); + ret = regmap_read(led->regmap, reg, &val); if (ret) return ret; @@ -437,7 +439,7 @@ static ssize_t store_als_channel(struct device *dev, mask = LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK; val = channel - 1; - ret = regmap_update_bits(led->lm3533->regmap, reg, mask, val); + ret = regmap_update_bits(led->regmap, reg, mask, val); if (ret) return ret; @@ -455,7 +457,7 @@ static ssize_t show_als_en(struct device *dev, int ret; reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); - ret = regmap_read(led->lm3533->regmap, reg, &val); + ret = regmap_read(led->regmap, reg, &val); if (ret) return ret; @@ -479,7 +481,7 @@ static ssize_t store_als_en(struct device *dev, reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); - ret = regmap_assign_bits(led->lm3533->regmap, reg, + ret = regmap_assign_bits(led->regmap, reg, LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK, enable); if (ret) return ret; @@ -498,7 +500,7 @@ static ssize_t show_linear(struct device *dev, int ret; reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); - ret = regmap_read(led->lm3533->regmap, reg, &val); + ret = regmap_read(led->regmap, reg, &val); if (ret) return ret; @@ -525,7 +527,7 @@ static ssize_t store_linear(struct device *dev, reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); - ret = regmap_assign_bits(led->lm3533->regmap, reg, + ret = regmap_assign_bits(led->regmap, reg, LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK, linear); if (ret) return ret; @@ -597,7 +599,7 @@ static umode_t lm3533_led_attr_is_visible(struct kobject *kobj, if (attr == &dev_attr_als_channel.attr || attr == &dev_attr_als_en.attr) { - if (!led->lm3533->have_als) + if (!led->have_als) mode = 0; } @@ -654,7 +656,9 @@ static int lm3533_led_probe(struct platform_device *pdev) if (!led) return -ENOMEM; - led->lm3533 = lm3533; + led->regmap = lm3533->regmap; + led->have_als = lm3533->have_als; + led->cdev.name = pdata->name; led->cdev.default_trigger = pdata->default_trigger; led->cdev.brightness_set_blocking = lm3533_led_set; @@ -670,7 +674,7 @@ static int lm3533_led_probe(struct platform_device *pdev) * registration so use parent device (for error reporting) until * registered. */ - led->cb.lm3533 = lm3533; + led->cb.regmap = lm3533->regmap; led->cb.id = lm3533_led_get_ctrlbank_id(led); led->cb.dev = lm3533->dev; diff --git a/drivers/mfd/lm3533-ctrlbank.c b/drivers/mfd/lm3533-ctrlbank.c index c2b5fd38b81d..91e13cfa3cf0 100644 --- a/drivers/mfd/lm3533-ctrlbank.c +++ b/drivers/mfd/lm3533-ctrlbank.c @@ -39,7 +39,7 @@ int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb) dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id); mask = 1 << cb->id; - ret = regmap_set_bits(cb->lm3533->regmap, LM3533_REG_CTRLBANK_ENABLE, + ret = regmap_set_bits(cb->regmap, LM3533_REG_CTRLBANK_ENABLE, mask); if (ret) dev_err(cb->dev, "failed to enable ctrlbank %d\n", cb->id); @@ -56,7 +56,7 @@ int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb) dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id); mask = 1 << cb->id; - ret = regmap_clear_bits(cb->lm3533->regmap, LM3533_REG_CTRLBANK_ENABLE, + ret = regmap_clear_bits(cb->regmap, LM3533_REG_CTRLBANK_ENABLE, mask); if (ret) dev_err(cb->dev, "failed to disable ctrlbank %d\n", cb->id); @@ -82,7 +82,7 @@ int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u32 imax) val = (imax - LM3533_MAX_CURRENT_MIN) / LM3533_MAX_CURRENT_STEP; reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_MAX_CURRENT_BASE); - ret = regmap_write(cb->lm3533->regmap, reg, val); + ret = regmap_write(cb->regmap, reg, val); if (ret) dev_err(cb->dev, "failed to set max current\n"); @@ -96,7 +96,7 @@ int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u32 val) int ret; reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_BRIGHTNESS_BASE); - ret = regmap_write(cb->lm3533->regmap, reg, val); + ret = regmap_write(cb->regmap, reg, val); if (ret) dev_err(cb->dev, "failed to set brightness\n"); @@ -110,7 +110,7 @@ int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u32 *val) int ret; reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_BRIGHTNESS_BASE); - ret = regmap_read(cb->lm3533->regmap, reg, val); + ret = regmap_read(cb->regmap, reg, val); if (ret) dev_err(cb->dev, "failed to get brightness\n"); @@ -137,7 +137,7 @@ int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u32 val) return -EINVAL; reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_PWM_BASE); - ret = regmap_write(cb->lm3533->regmap, reg, val); + ret = regmap_write(cb->regmap, reg, val); if (ret) dev_err(cb->dev, "failed to set PWM mask\n"); @@ -151,7 +151,7 @@ int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u32 *val) int ret; reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_PWM_BASE); - ret = regmap_read(cb->lm3533->regmap, reg, val); + ret = regmap_read(cb->regmap, reg, val); if (ret) dev_err(cb->dev, "failed to get PWM mask\n"); diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c index cd21c64b6485..4d6f68033480 100644 --- a/drivers/video/backlight/lm3533_bl.c +++ b/drivers/video/backlight/lm3533_bl.c @@ -24,10 +24,12 @@ struct lm3533_bl { - struct lm3533 *lm3533; + struct regmap *regmap; struct lm3533_ctrlbank cb; struct backlight_device *bd; int id; + + bool have_als; }; @@ -88,7 +90,7 @@ static ssize_t show_als_en(struct device *dev, bool enable; int ret; - ret = regmap_read(bl->lm3533->regmap, LM3533_REG_CTRLBANK_AB_BCONF, &val); + ret = regmap_read(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF, &val); if (ret) return ret; @@ -113,7 +115,7 @@ static ssize_t store_als_en(struct device *dev, mask = 1 << (2 * ctrlbank); - ret = regmap_assign_bits(bl->lm3533->regmap, LM3533_REG_CTRLBANK_AB_BCONF, + ret = regmap_assign_bits(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF, mask, enable); if (ret) return ret; @@ -130,7 +132,7 @@ static ssize_t show_linear(struct device *dev, int linear; int ret; - ret = regmap_read(bl->lm3533->regmap, LM3533_REG_CTRLBANK_AB_BCONF, &val); + ret = regmap_read(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF, &val); if (ret) return ret; @@ -158,7 +160,7 @@ static ssize_t store_linear(struct device *dev, mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1); - ret = regmap_assign_bits(bl->lm3533->regmap, LM3533_REG_CTRLBANK_AB_BCONF, + ret = regmap_assign_bits(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF, mask, linear); if (ret) return ret; @@ -223,7 +225,7 @@ static umode_t lm3533_bl_attr_is_visible(struct kobject *kobj, if (attr == &dev_attr_als_channel.attr || attr == &dev_attr_als_en.attr) { - if (!bl->lm3533->have_als) + if (!bl->have_als) mode = 0; } @@ -277,10 +279,11 @@ static int lm3533_bl_probe(struct platform_device *pdev) if (!bl) return -ENOMEM; - bl->lm3533 = lm3533; + bl->regmap = lm3533->regmap; + bl->have_als = lm3533->have_als; bl->id = pdev->id; - bl->cb.lm3533 = lm3533; + bl->cb.regmap = lm3533->regmap; bl->cb.id = lm3533_bl_get_ctrlbank_id(bl); bl->cb.dev = NULL; /* until registered */ diff --git a/include/linux/mfd/lm3533.h b/include/linux/mfd/lm3533.h index c4c38876c09b..74cfb52c3bd1 100644 --- a/include/linux/mfd/lm3533.h +++ b/include/linux/mfd/lm3533.h @@ -33,7 +33,7 @@ struct lm3533 { }; struct lm3533_ctrlbank { - struct lm3533 *lm3533; + struct regmap *regmap; struct device *dev; int id; }; From 948c42932e0b696de11a2aee69a81d8a6f83dd12 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:23 +0300 Subject: [PATCH 05/74] iio: light: lm3533-als: Remove redundant pdata helpers The lm3533_als_set_input_mode() and lm3533_als_set_resistor() functions are used only in lm3533_als_setup(). Incorporate their code into lm3533_als_setup() directly to simplify driver readability. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/20260731113632.158440-6-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/iio/light/lm3533-als.c | 58 ++++++++++------------------------ 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index a9af8e2b965f..55c35018704c 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -708,55 +708,31 @@ static const struct attribute_group lm3533_als_attribute_group = { .attrs = lm3533_als_attributes }; -static int lm3533_als_set_input_mode(struct lm3533_als *als, bool pwm_mode) -{ - int ret; - - ret = regmap_assign_bits(als->regmap, LM3533_REG_ALS_CONF, - LM3533_ALS_INPUT_MODE_MASK, pwm_mode); - if (ret) { - dev_err(&als->pdev->dev, "failed to set input mode %d\n", - pwm_mode); - return ret; - } - - return 0; -} - -static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val) -{ - int ret; - - if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX) { - dev_err(&als->pdev->dev, "invalid resistor value\n"); - return -EINVAL; - } - - ret = regmap_write(als->regmap, LM3533_REG_ALS_RESISTOR_SELECT, - val); - if (ret) { - dev_err(&als->pdev->dev, "failed to set resistor\n"); - return ret; - } - - return 0; -} - static int lm3533_als_setup(struct lm3533_als *als, const struct lm3533_als_platform_data *pdata) { + struct device *dev = &als->pdev->dev; int ret; - ret = lm3533_als_set_input_mode(als, pdata->pwm_mode); + ret = regmap_assign_bits(als->regmap, LM3533_REG_ALS_CONF, + LM3533_ALS_INPUT_MODE_MASK, pdata->pwm_mode); if (ret) - return ret; + return dev_err_probe(dev, ret, "failed to set input mode %d\n", + pdata->pwm_mode); /* ALS input is always high impedance in PWM-mode. */ - if (!pdata->pwm_mode) { - ret = lm3533_als_set_resistor(als, pdata->r_select); - if (ret) - return ret; - } + if (pdata->pwm_mode) + return 0; + + if (pdata->r_select < LM3533_ALS_RESISTOR_MIN || + pdata->r_select > LM3533_ALS_RESISTOR_MAX) + return dev_err_probe(dev, -EINVAL, + "invalid resistor value\n"); + + ret = regmap_write(als->regmap, LM3533_REG_ALS_RESISTOR_SELECT, + pdata->r_select); + if (ret) + return dev_err_probe(dev, ret, "failed to set resistor\n"); return 0; } From cdc9de135bc4bac7b7a344e76bef8e7e6cfb3e16 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:24 +0300 Subject: [PATCH 06/74] mfd: lm3533-core: Remove redundant pdata helpers The lm3533_set_boost_freq() and lm3533_set_boost_ovp() functions are used only in lm3533_device_setup(), which in turn is only called by lm3533_device_init(). Incorporate their code directly into lm3533_device_init() to simplify driver readability. Signed-off-by: Svyatoslav Ryhel Link: https://patch.msgid.link/20260731113632.158440-7-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/lm3533-core.c | 61 ++++++++++----------------------------- 1 file changed, 15 insertions(+), 46 deletions(-) diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c index f9bdc0a1d9be..5baf934fac82 100644 --- a/drivers/mfd/lm3533-core.c +++ b/drivers/mfd/lm3533-core.c @@ -7,6 +7,7 @@ * Author: Johan Hovold */ +#include #include #include #include @@ -23,10 +24,7 @@ #define LM3533_BOOST_OVP_MASK 0x06 -#define LM3533_BOOST_OVP_SHIFT 1 - #define LM3533_BOOST_FREQ_MASK 0x01 -#define LM3533_BOOST_FREQ_SHIFT 0 #define LM3533_BL_ID_MASK 1 #define LM3533_LED_ID_MASK 3 @@ -80,35 +78,6 @@ static struct mfd_cell lm3533_led_devs[] = { }, }; -static int lm3533_set_boost_freq(struct lm3533 *lm3533, - enum lm3533_boost_freq freq) -{ - int ret; - - ret = regmap_update_bits(lm3533->regmap, LM3533_REG_BOOST_PWM, - LM3533_BOOST_FREQ_MASK, - freq << LM3533_BOOST_FREQ_SHIFT); - if (ret) - dev_err(lm3533->dev, "failed to set boost frequency\n"); - - return ret; -} - - -static int lm3533_set_boost_ovp(struct lm3533 *lm3533, - enum lm3533_boost_ovp ovp) -{ - int ret; - - ret = regmap_update_bits(lm3533->regmap, LM3533_REG_BOOST_PWM, - LM3533_BOOST_OVP_MASK, - ovp << LM3533_BOOST_OVP_SHIFT); - if (ret) - dev_err(lm3533->dev, "failed to set boost ovp\n"); - - return ret; -} - /* * HVLED output config -- output hvled controlled by backlight bl */ @@ -406,18 +375,6 @@ static int lm3533_device_led_init(struct lm3533 *lm3533) return 0; } -static int lm3533_device_setup(struct lm3533 *lm3533, - struct lm3533_platform_data *pdata) -{ - int ret; - - ret = lm3533_set_boost_freq(lm3533, pdata->boost_freq); - if (ret) - return ret; - - return lm3533_set_boost_ovp(lm3533, pdata->boost_ovp); -} - static int lm3533_device_init(struct lm3533 *lm3533) { struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev); @@ -437,9 +394,21 @@ static int lm3533_device_init(struct lm3533 *lm3533) lm3533_enable(lm3533); - ret = lm3533_device_setup(lm3533, pdata); - if (ret) + ret = regmap_update_bits(lm3533->regmap, LM3533_REG_BOOST_PWM, + LM3533_BOOST_FREQ_MASK, + FIELD_PREP(LM3533_BOOST_FREQ_MASK, pdata->boost_freq)); + if (ret) { + dev_err(lm3533->dev, "failed to set boost frequency\n"); goto err_disable; + } + + ret = regmap_update_bits(lm3533->regmap, LM3533_REG_BOOST_PWM, + LM3533_BOOST_OVP_MASK, + FIELD_PREP(LM3533_BOOST_OVP_MASK, pdata->boost_ovp)); + if (ret) { + dev_err(lm3533->dev, "failed to set boost ovp\n"); + goto err_disable; + } lm3533_device_als_init(lm3533); lm3533_device_bl_init(lm3533); From d172f3814461c91db5af71fb628601af8389a3ee Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:25 +0300 Subject: [PATCH 07/74] mfd: lm3533: Use dev_groups in struct device_driver Instead of creating and removing the device sysfs attributes directly during probe and remove of the driver, respectively, use dev_groups in struct device_driver to point to the attribute definitions and let the core take care of creating and removing them. No intentional functional impact. Suggested-by: Andy Shevchenko Signed-off-by: Svyatoslav Ryhel Reviewed-by: Johan Hovold Link: https://patch.msgid.link/20260731113632.158440-8-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/lm3533-core.c | 16 ++++++---------- drivers/video/backlight/lm3533_bl.c | 22 ++++++++-------------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c index 5baf934fac82..53b87958c2c6 100644 --- a/drivers/mfd/lm3533-core.c +++ b/drivers/mfd/lm3533-core.c @@ -294,6 +294,11 @@ static struct attribute_group lm3533_attribute_group = { .attrs = lm3533_attributes }; +static const struct attribute_group *lm3533_attribute_groups[] = { + &lm3533_attribute_group, + NULL, +}; + static int lm3533_device_als_init(struct lm3533 *lm3533) { struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev); @@ -414,16 +419,8 @@ static int lm3533_device_init(struct lm3533 *lm3533) lm3533_device_bl_init(lm3533); lm3533_device_led_init(lm3533); - ret = sysfs_create_group(&lm3533->dev->kobj, &lm3533_attribute_group); - if (ret < 0) { - dev_err(lm3533->dev, "failed to create sysfs attributes\n"); - goto err_unregister; - } - return 0; -err_unregister: - mfd_remove_devices(lm3533->dev); err_disable: lm3533_disable(lm3533); @@ -434,8 +431,6 @@ static void lm3533_device_exit(struct lm3533 *lm3533) { dev_dbg(lm3533->dev, "%s\n", __func__); - sysfs_remove_group(&lm3533->dev->kobj, &lm3533_attribute_group); - mfd_remove_devices(lm3533->dev); lm3533_disable(lm3533); } @@ -530,6 +525,7 @@ MODULE_DEVICE_TABLE(i2c, lm3533_i2c_ids); static struct i2c_driver lm3533_i2c_driver = { .driver = { .name = "lm3533", + .dev_groups = lm3533_attribute_groups, }, .id_table = lm3533_i2c_ids, .probe = lm3533_i2c_probe, diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c index 4d6f68033480..9ef171d3aaea 100644 --- a/drivers/video/backlight/lm3533_bl.c +++ b/drivers/video/backlight/lm3533_bl.c @@ -237,6 +237,11 @@ static struct attribute_group lm3533_bl_attribute_group = { .attrs = lm3533_bl_attributes }; +static const struct attribute_group *lm3533_bl_attribute_groups[] = { + &lm3533_bl_attribute_group, + NULL, +}; + static int lm3533_bl_setup(struct lm3533_bl *bl, struct lm3533_bl_platform_data *pdata) { @@ -304,28 +309,17 @@ static int lm3533_bl_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bl); - ret = sysfs_create_group(&bd->dev.kobj, &lm3533_bl_attribute_group); - if (ret < 0) { - dev_err(&pdev->dev, "failed to create sysfs attributes\n"); - return ret; - } - backlight_update_status(bd); ret = lm3533_bl_setup(bl, pdata); if (ret) - goto err_sysfs_remove; + return ret; ret = lm3533_ctrlbank_enable(&bl->cb); if (ret) - goto err_sysfs_remove; + return ret; return 0; - -err_sysfs_remove: - sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group); - - return ret; } static void lm3533_bl_remove(struct platform_device *pdev) @@ -339,7 +333,6 @@ static void lm3533_bl_remove(struct platform_device *pdev) bd->props.brightness = 0; lm3533_ctrlbank_disable(&bl->cb); - sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group); } #ifdef CONFIG_PM_SLEEP @@ -377,6 +370,7 @@ static struct platform_driver lm3533_bl_driver = { .driver = { .name = "lm3533-backlight", .pm = &lm3533_bl_pm_ops, + .dev_groups = lm3533_bl_attribute_groups, }, .probe = lm3533_bl_probe, .remove = lm3533_bl_remove, From 5669316d7055325d981b5a37f574299c848af581 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:26 +0300 Subject: [PATCH 08/74] mfd: lm3533: Convert to use OF bindings Since there are no users of this driver via platform data, remove the platform data support and switch to using Device Tree bindings. Previously, all cell devices were linked to the parent device. This could cause problems further down the line because the LM3533 has multiple cells of the same type. Using a single phandle to the parent could cause confusion when attempting to reference or call the correct child node. Since this commit adds a dedicated node for every cell, remove linking of the cell to the parent node. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Daniel Thompson (RISCstar) #for backlight Acked-by: Jonathan Cameron #for-iio Link: https://patch.msgid.link/20260731113632.158440-9-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/iio/light/lm3533-als.c | 66 +++++--- drivers/leds/leds-lm3533.c | 47 ++++-- drivers/mfd/lm3533-core.c | 237 ++++++++++++---------------- drivers/mfd/lm3533-ctrlbank.c | 5 - drivers/video/backlight/lm3533_bl.c | 54 +++++-- include/linux/mfd/lm3533.h | 52 +----- 6 files changed, 217 insertions(+), 244 deletions(-) diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index 55c35018704c..4870e8050088 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -17,15 +17,17 @@ #include #include #include +#include #include #include #include +#include #include -#define LM3533_ALS_RESISTOR_MIN 1 -#define LM3533_ALS_RESISTOR_MAX 127 +#define LM3533_ALS_RESISTOR_MIN_OHMS 1575 +#define LM3533_ALS_RESISTOR_MAX_OHMS 200000 #define LM3533_ALS_CHANNEL_CURRENT_MAX 2 #define LM3533_ALS_THRESH_MAX 3 #define LM3533_ALS_ZONE_MAX 4 @@ -57,6 +59,9 @@ struct lm3533_als { atomic_t zone; struct mutex thresh_mutex; + + u32 r_select; + bool pwm_mode; }; @@ -403,7 +408,7 @@ static ssize_t show_thresh_either_en(struct device *dev, int enable; int ret; - if (als->irq) { + if (als->irq > 0) { ret = lm3533_als_get_int_mode(indio_dev, &enable); if (ret) return ret; @@ -425,7 +430,7 @@ static ssize_t store_thresh_either_en(struct device *dev, u8 zone; int ret; - if (!als->irq) + if (als->irq <= 0) return -EBUSY; if (kstrtoul(buf, 0, &enable)) @@ -708,29 +713,38 @@ static const struct attribute_group lm3533_als_attribute_group = { .attrs = lm3533_als_attributes }; -static int lm3533_als_setup(struct lm3533_als *als, - const struct lm3533_als_platform_data *pdata) +static int lm3533_als_setup(struct lm3533_als *als) { struct device *dev = &als->pdev->dev; int ret; + als->pwm_mode = device_property_read_bool(dev, "ti,pwm-mode"); + ret = regmap_assign_bits(als->regmap, LM3533_REG_ALS_CONF, - LM3533_ALS_INPUT_MODE_MASK, pdata->pwm_mode); + LM3533_ALS_INPUT_MODE_MASK, als->pwm_mode); if (ret) return dev_err_probe(dev, ret, "failed to set input mode %d\n", - pdata->pwm_mode); + als->pwm_mode); /* ALS input is always high impedance in PWM-mode. */ - if (pdata->pwm_mode) + if (als->pwm_mode) return 0; - if (pdata->r_select < LM3533_ALS_RESISTOR_MIN || - pdata->r_select > LM3533_ALS_RESISTOR_MAX) + ret = device_property_read_u32(dev, "ti,resistor-ohms", + &als->r_select); + if (ret) + return dev_err_probe(dev, ret, + "failed to ger resistor value\n"); + + if (als->r_select < LM3533_ALS_RESISTOR_MIN_OHMS || + als->r_select > LM3533_ALS_RESISTOR_MAX_OHMS) return dev_err_probe(dev, -EINVAL, "invalid resistor value\n"); + als->r_select = DIV_ROUND_UP(2 * MICRO, 10 * als->r_select); + ret = regmap_write(als->regmap, LM3533_REG_ALS_RESISTOR_SELECT, - pdata->r_select); + als->r_select); if (ret) return dev_err_probe(dev, ret, "failed to set resistor\n"); @@ -793,7 +807,6 @@ static const struct iio_info lm3533_als_info = { static int lm3533_als_probe(struct platform_device *pdev) { - const struct lm3533_als_platform_data *pdata; struct lm3533 *lm3533; struct lm3533_als *als; struct iio_dev *indio_dev; @@ -803,12 +816,6 @@ static int lm3533_als_probe(struct platform_device *pdev) if (!lm3533) return -EINVAL; - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(&pdev->dev, "no platform data\n"); - return -EINVAL; - } - indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*als)); if (!indio_dev) return -ENOMEM; @@ -817,25 +824,27 @@ static int lm3533_als_probe(struct platform_device *pdev) indio_dev->channels = lm3533_als_channels; indio_dev->num_channels = ARRAY_SIZE(lm3533_als_channels); indio_dev->name = dev_name(&pdev->dev); - iio_device_set_parent(indio_dev, pdev->dev.parent); indio_dev->modes = INDIO_DIRECT_MODE; als = iio_priv(indio_dev); als->regmap = lm3533->regmap; als->pdev = pdev; - als->irq = lm3533->irq; + als->irq = platform_get_irq_optional(pdev, 0); + if (als->irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + atomic_set(&als->zone, 0); mutex_init(&als->thresh_mutex); platform_set_drvdata(pdev, indio_dev); - if (als->irq) { + if (als->irq > 0) { ret = lm3533_als_setup_irq(als, indio_dev); if (ret) return ret; } - ret = lm3533_als_setup(als, pdata); + ret = lm3533_als_setup(als); if (ret) goto err_free_irq; @@ -854,7 +863,7 @@ static int lm3533_als_probe(struct platform_device *pdev) err_disable: lm3533_als_disable(als); err_free_irq: - if (als->irq) + if (als->irq > 0) free_irq(als->irq, indio_dev); return ret; @@ -868,13 +877,20 @@ static void lm3533_als_remove(struct platform_device *pdev) lm3533_als_set_int_mode(indio_dev, false); iio_device_unregister(indio_dev); lm3533_als_disable(als); - if (als->irq) + if (als->irq > 0) free_irq(als->irq, indio_dev); } +static const struct of_device_id lm3533_als_match_table[] = { + { .compatible = "ti,lm3533-als" }, + { } +}; +MODULE_DEVICE_TABLE(of, lm3533_als_match_table); + static struct platform_driver lm3533_als_driver = { .driver = { .name = "lm3533-als", + .of_match_table = lm3533_als_match_table, }, .probe = lm3533_als_probe, .remove = lm3533_als_remove, diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index 0cb0585eb960..7259097bd6a7 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,9 @@ struct lm3533_led { struct mutex mutex; unsigned long flags; + u32 max_current; + u32 pwm; + bool have_als; }; @@ -616,22 +620,20 @@ static const struct attribute_group *lm3533_led_attribute_groups[] = { NULL }; -static int lm3533_led_setup(struct lm3533_led *led, - struct lm3533_led_platform_data *pdata) +static int lm3533_led_setup(struct lm3533_led *led) { int ret; - ret = lm3533_ctrlbank_set_max_current(&led->cb, pdata->max_current); + ret = lm3533_ctrlbank_set_max_current(&led->cb, led->max_current); if (ret) return ret; - return lm3533_ctrlbank_set_pwm(&led->cb, pdata->pwm); + return lm3533_ctrlbank_set_pwm(&led->cb, led->pwm); } static int lm3533_led_probe(struct platform_device *pdev) { struct lm3533 *lm3533; - struct lm3533_led_platform_data *pdata; struct lm3533_led *led; int ret; @@ -641,12 +643,6 @@ static int lm3533_led_probe(struct platform_device *pdev) if (!lm3533) return -EINVAL; - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(&pdev->dev, "no platform data\n"); - return -EINVAL; - } - if (pdev->id < 0 || pdev->id >= LM3533_LVCTRLBANK_COUNT) { dev_err(&pdev->dev, "illegal LED id %d\n", pdev->id); return -EINVAL; @@ -659,8 +655,6 @@ static int lm3533_led_probe(struct platform_device *pdev) led->regmap = lm3533->regmap; led->have_als = lm3533->have_als; - led->cdev.name = pdata->name; - led->cdev.default_trigger = pdata->default_trigger; led->cdev.brightness_set_blocking = lm3533_led_set; led->cdev.brightness_get = lm3533_led_get; led->cdev.blink_set = lm3533_led_blink_set; @@ -668,6 +662,15 @@ static int lm3533_led_probe(struct platform_device *pdev) led->cdev.groups = lm3533_led_attribute_groups; led->id = pdev->id; + led->cdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-%d", + pdev->name, led->id); + if (!led->cdev.name) + return -ENOMEM; + + led->cdev.default_trigger = "none"; + device_property_read_string(&pdev->dev, "linux,default-trigger", + &led->cdev.default_trigger); + mutex_init(&led->mutex); /* The class framework makes a callback to get brightness during @@ -680,7 +683,7 @@ static int lm3533_led_probe(struct platform_device *pdev) platform_set_drvdata(pdev, led); - ret = led_classdev_register(pdev->dev.parent, &led->cdev); + ret = led_classdev_register(&pdev->dev, &led->cdev); if (ret) { dev_err(&pdev->dev, "failed to register LED %d\n", pdev->id); return ret; @@ -688,7 +691,14 @@ static int lm3533_led_probe(struct platform_device *pdev) led->cb.dev = led->cdev.dev; - ret = lm3533_led_setup(led, pdata); + device_property_read_u32(&pdev->dev, "led-max-microamp", + &led->max_current); + led->max_current = clamp(led->max_current, LM3533_MAX_CURRENT_MIN, + LM3533_MAX_CURRENT_MAX); + + device_property_read_u32(&pdev->dev, "ti,pwm-config-mask", &led->pwm); + + ret = lm3533_led_setup(led); if (ret) goto err_deregister; @@ -725,9 +735,16 @@ static void lm3533_led_shutdown(struct platform_device *pdev) lm3533_led_set(&led->cdev, LED_OFF); /* disable blink */ } +static const struct of_device_id lm3533_led_match_table[] = { + { .compatible = "ti,lm3533-leds" }, + { } +}; +MODULE_DEVICE_TABLE(of, lm3533_led_match_table); + static struct platform_driver lm3533_led_driver = { .driver = { .name = "lm3533-leds", + .of_match_table = lm3533_led_match_table, }, .probe = lm3533_led_probe, .remove = lm3533_led_remove, diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c index 53b87958c2c6..c7c7e71719ae 100644 --- a/drivers/mfd/lm3533-core.c +++ b/drivers/mfd/lm3533-core.c @@ -15,16 +15,23 @@ #include #include #include +#include #include #include #include #include +#include #include #define LM3533_BOOST_OVP_MASK 0x06 +#define LM3533_BOOST_OVP_MIN (16 * MICRO) +#define LM3533_BOOST_OVP_MAX (40 * MICRO) + #define LM3533_BOOST_FREQ_MASK 0x01 +#define LM3533_BOOST_FREQ_MIN (500 * HZ_PER_KHZ) +#define LM3533_BOOST_FREQ_MAX (1000 * HZ_PER_KHZ) #define LM3533_BL_ID_MASK 1 #define LM3533_LED_ID_MASK 3 @@ -33,6 +40,7 @@ #define LM3533_HVLED_ID_MAX 2 #define LM3533_LVLED_ID_MAX 5 +#define LM3533_CELLS_MAX 7 #define LM3533_REG_OUTPUT_CONF1 0x10 #define LM3533_REG_OUTPUT_CONF2 0x11 @@ -40,44 +48,6 @@ #define LM3533_REG_MAX 0xb2 - -static struct mfd_cell lm3533_als_devs[] = { - { - .name = "lm3533-als", - .id = -1, - }, -}; - -static struct mfd_cell lm3533_bl_devs[] = { - { - .name = "lm3533-backlight", - .id = 0, - }, - { - .name = "lm3533-backlight", - .id = 1, - }, -}; - -static struct mfd_cell lm3533_led_devs[] = { - { - .name = "lm3533-leds", - .id = 0, - }, - { - .name = "lm3533-leds", - .id = 1, - }, - { - .name = "lm3533-leds", - .id = 2, - }, - { - .name = "lm3533-leds", - .id = 3, - }, -}; - /* * HVLED output config -- output hvled controlled by backlight bl */ @@ -299,125 +269,92 @@ static const struct attribute_group *lm3533_attribute_groups[] = { NULL, }; -static int lm3533_device_als_init(struct lm3533 *lm3533) -{ - struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev); - int ret; - - if (!pdata->als) - return 0; - - lm3533_als_devs[0].platform_data = pdata->als; - lm3533_als_devs[0].pdata_size = sizeof(*pdata->als); - - ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL, - 0, NULL); - if (ret) { - dev_err(lm3533->dev, "failed to add ALS device\n"); - return ret; - } - - lm3533->have_als = 1; - - return 0; -} - -static int lm3533_device_bl_init(struct lm3533 *lm3533) -{ - struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev); - int i; - int ret; - - if (!pdata->backlights || pdata->num_backlights == 0) - return 0; - - if (pdata->num_backlights > ARRAY_SIZE(lm3533_bl_devs)) - pdata->num_backlights = ARRAY_SIZE(lm3533_bl_devs); - - for (i = 0; i < pdata->num_backlights; ++i) { - lm3533_bl_devs[i].platform_data = &pdata->backlights[i]; - lm3533_bl_devs[i].pdata_size = sizeof(pdata->backlights[i]); - } - - ret = mfd_add_devices(lm3533->dev, 0, lm3533_bl_devs, - pdata->num_backlights, NULL, 0, NULL); - if (ret) { - dev_err(lm3533->dev, "failed to add backlight devices\n"); - return ret; - } - - lm3533->have_backlights = 1; - - return 0; -} - -static int lm3533_device_led_init(struct lm3533 *lm3533) -{ - struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev); - int i; - int ret; - - if (!pdata->leds || pdata->num_leds == 0) - return 0; - - if (pdata->num_leds > ARRAY_SIZE(lm3533_led_devs)) - pdata->num_leds = ARRAY_SIZE(lm3533_led_devs); - - for (i = 0; i < pdata->num_leds; ++i) { - lm3533_led_devs[i].platform_data = &pdata->leds[i]; - lm3533_led_devs[i].pdata_size = sizeof(pdata->leds[i]); - } - - ret = mfd_add_devices(lm3533->dev, 0, lm3533_led_devs, - pdata->num_leds, NULL, 0, NULL); - if (ret) { - dev_err(lm3533->dev, "failed to add LED devices\n"); - return ret; - } - - lm3533->have_leds = 1; - - return 0; -} - static int lm3533_device_init(struct lm3533 *lm3533) { - struct lm3533_platform_data *pdata = dev_get_platdata(lm3533->dev); + struct device *dev = lm3533->dev; + struct mfd_cell *lm3533_devices; + u32 reg, nchilds; + u32 count = 0; int ret; - dev_dbg(lm3533->dev, "%s\n", __func__); + nchilds = device_get_child_node_count(dev); + if (!nchilds || nchilds > LM3533_CELLS_MAX) + return dev_err_probe(dev, -ENODEV, + "num of child nodes is not supported\n"); - if (!pdata) { - dev_err(lm3533->dev, "no platform data\n"); - return -EINVAL; + lm3533_devices = devm_kcalloc(dev, nchilds, sizeof(*lm3533_devices), + GFP_KERNEL); + if (!lm3533_devices) + return -ENOMEM; + + device_for_each_child_node_scoped(dev, child) { + if (count >= nchilds) + break; + + if (fwnode_device_is_compatible(child, "ti,lm3533-als")) { + lm3533_devices[count].name = "lm3533-als"; + lm3533_devices[count].of_compatible = "ti,lm3533-als"; + lm3533_devices[count].id = PLATFORM_DEVID_NONE; + + lm3533->have_als = true; + count++; + } else if (fwnode_device_is_compatible(child, "ti,lm3533-backlight")) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret || reg >= LM3533_HVLED_ID_MAX) { + dev_err(dev, "invalid backlight node %pfw\n", child); + continue; + } + + lm3533_devices[count].name = "lm3533-backlight"; + lm3533_devices[count].of_compatible = "ti,lm3533-backlight"; + lm3533_devices[count].id = reg; + lm3533_devices[count].of_reg = reg; + lm3533_devices[count].use_of_reg = true; + + lm3533->have_backlights = true; + count++; + } else if (fwnode_device_is_compatible(child, "ti,lm3533-leds")) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret || reg < LM3533_HVLED_ID_MAX || + reg > LM3533_LVLED_ID_MAX) { + dev_err(dev, "invalid LED node %pfw\n", child); + continue; + } + + lm3533_devices[count].name = "lm3533-leds"; + lm3533_devices[count].of_compatible = "ti,lm3533-leds"; + lm3533_devices[count].id = reg - LM3533_HVLED_ID_MAX; + lm3533_devices[count].of_reg = reg; + lm3533_devices[count].use_of_reg = true; + + lm3533->have_leds = true; + count++; + } } - lm3533->hwen = devm_gpiod_get(lm3533->dev, NULL, GPIOD_OUT_LOW); - if (IS_ERR(lm3533->hwen)) - return dev_err_probe(lm3533->dev, PTR_ERR(lm3533->hwen), "failed to request HWEN GPIO\n"); - gpiod_set_consumer_name(lm3533->hwen, "lm3533-hwen"); - lm3533_enable(lm3533); ret = regmap_update_bits(lm3533->regmap, LM3533_REG_BOOST_PWM, LM3533_BOOST_FREQ_MASK, - FIELD_PREP(LM3533_BOOST_FREQ_MASK, pdata->boost_freq)); + FIELD_PREP(LM3533_BOOST_FREQ_MASK, lm3533->boost_freq)); if (ret) { - dev_err(lm3533->dev, "failed to set boost frequency\n"); + dev_err(dev, "failed to set boost frequency\n"); goto err_disable; } ret = regmap_update_bits(lm3533->regmap, LM3533_REG_BOOST_PWM, LM3533_BOOST_OVP_MASK, - FIELD_PREP(LM3533_BOOST_OVP_MASK, pdata->boost_ovp)); + FIELD_PREP(LM3533_BOOST_OVP_MASK, lm3533->boost_ovp)); if (ret) { - dev_err(lm3533->dev, "failed to set boost ovp\n"); + dev_err(dev, "failed to set boost ovp\n"); goto err_disable; } - lm3533_device_als_init(lm3533); - lm3533_device_bl_init(lm3533); - lm3533_device_led_init(lm3533); + ret = mfd_add_devices(dev, 0, lm3533_devices, count, NULL, 0, NULL); + if (ret) { + dev_err(dev, "failed to add MFD devices: %d\n", ret); + goto err_disable; + } return 0; @@ -502,7 +439,26 @@ static int lm3533_i2c_probe(struct i2c_client *i2c) return PTR_ERR(lm3533->regmap); lm3533->dev = &i2c->dev; - lm3533->irq = i2c->irq; + + lm3533->hwen = devm_gpiod_get_optional(lm3533->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(lm3533->hwen)) + return dev_err_probe(lm3533->dev, PTR_ERR(lm3533->hwen), + "failed to get HWEN GPIO\n"); + + device_property_read_u32(lm3533->dev, "ti,boost-ovp-microvolt", + &lm3533->boost_ovp); + + lm3533->boost_ovp = clamp(lm3533->boost_ovp, LM3533_BOOST_OVP_MIN, + LM3533_BOOST_OVP_MAX); + lm3533->boost_ovp = lm3533->boost_ovp / (8 * MICRO) - 2; + + device_property_read_u32(lm3533->dev, "ti,boost-freq-hz", + &lm3533->boost_freq); + + lm3533->boost_freq = clamp(lm3533->boost_freq, LM3533_BOOST_FREQ_MIN, + LM3533_BOOST_FREQ_MAX); + lm3533->boost_freq = lm3533->boost_freq / (500 * KILO) - 1; return lm3533_device_init(lm3533); } @@ -516,6 +472,12 @@ static void lm3533_i2c_remove(struct i2c_client *i2c) lm3533_device_exit(lm3533); } +static const struct of_device_id lm3533_match_table[] = { + { .compatible = "ti,lm3533" }, + { } +}; +MODULE_DEVICE_TABLE(of, lm3533_match_table); + static const struct i2c_device_id lm3533_i2c_ids[] = { { "lm3533" }, { } @@ -526,6 +488,7 @@ static struct i2c_driver lm3533_i2c_driver = { .driver = { .name = "lm3533", .dev_groups = lm3533_attribute_groups, + .of_match_table = lm3533_match_table, }, .id_table = lm3533_i2c_ids, .probe = lm3533_i2c_probe, diff --git a/drivers/mfd/lm3533-ctrlbank.c b/drivers/mfd/lm3533-ctrlbank.c index 91e13cfa3cf0..3aab8ece4e8c 100644 --- a/drivers/mfd/lm3533-ctrlbank.c +++ b/drivers/mfd/lm3533-ctrlbank.c @@ -13,11 +13,6 @@ #include - -#define LM3533_MAX_CURRENT_MIN 5000 -#define LM3533_MAX_CURRENT_MAX 29800 -#define LM3533_MAX_CURRENT_STEP 800 - #define LM3533_PWM_MAX 0x3f #define LM3533_REG_PWM_BASE 0x14 diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c index 9ef171d3aaea..5ce237cb4219 100644 --- a/drivers/video/backlight/lm3533_bl.c +++ b/drivers/video/backlight/lm3533_bl.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,9 @@ struct lm3533_bl { struct backlight_device *bd; int id; + u32 max_current; + u32 pwm; + bool have_als; }; @@ -242,25 +246,25 @@ static const struct attribute_group *lm3533_bl_attribute_groups[] = { NULL, }; -static int lm3533_bl_setup(struct lm3533_bl *bl, - struct lm3533_bl_platform_data *pdata) +static int lm3533_bl_setup(struct lm3533_bl *bl) { int ret; - ret = lm3533_ctrlbank_set_max_current(&bl->cb, pdata->max_current); + ret = lm3533_ctrlbank_set_max_current(&bl->cb, bl->max_current); if (ret) return ret; - return lm3533_ctrlbank_set_pwm(&bl->cb, pdata->pwm); + return lm3533_ctrlbank_set_pwm(&bl->cb, bl->pwm); } static int lm3533_bl_probe(struct platform_device *pdev) { struct lm3533 *lm3533; - struct lm3533_bl_platform_data *pdata; struct lm3533_bl *bl; struct backlight_device *bd; struct backlight_properties props; + char *name = NULL; + u32 default_brightness = LM3533_BL_MAX_BRIGHTNESS; int ret; dev_dbg(&pdev->dev, "%s\n", __func__); @@ -269,12 +273,6 @@ static int lm3533_bl_probe(struct platform_device *pdev) if (!lm3533) return -EINVAL; - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(&pdev->dev, "no platform data\n"); - return -EINVAL; - } - if (pdev->id < 0 || pdev->id >= LM3533_HVCTRLBANK_COUNT) { dev_err(&pdev->dev, "illegal backlight id %d\n", pdev->id); return -EINVAL; @@ -292,13 +290,21 @@ static int lm3533_bl_probe(struct platform_device *pdev) bl->cb.id = lm3533_bl_get_ctrlbank_id(bl); bl->cb.dev = NULL; /* until registered */ + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-%d", + pdev->name, pdev->id); + if (!name) + return -ENOMEM; + + device_property_read_u32(&pdev->dev, "default-brightness", + &default_brightness); + memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_RAW; props.max_brightness = LM3533_BL_MAX_BRIGHTNESS; - props.brightness = pdata->default_brightness; - bd = devm_backlight_device_register(&pdev->dev, pdata->name, - pdev->dev.parent, bl, &lm3533_bl_ops, - &props); + props.brightness = default_brightness; + + bd = devm_backlight_device_register(&pdev->dev, name, &pdev->dev, + bl, &lm3533_bl_ops, &props); if (IS_ERR(bd)) { dev_err(&pdev->dev, "failed to register backlight device\n"); return PTR_ERR(bd); @@ -309,12 +315,19 @@ static int lm3533_bl_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bl); - backlight_update_status(bd); + device_property_read_u32(&pdev->dev, "led-max-microamp", + &bl->max_current); + bl->max_current = clamp(bl->max_current, LM3533_MAX_CURRENT_MIN, + LM3533_MAX_CURRENT_MAX); - ret = lm3533_bl_setup(bl, pdata); + device_property_read_u32(&pdev->dev, "ti,pwm-config-mask", &bl->pwm); + + ret = lm3533_bl_setup(bl); if (ret) return ret; + backlight_update_status(bd); + ret = lm3533_ctrlbank_enable(&bl->cb); if (ret) return ret; @@ -366,11 +379,18 @@ static void lm3533_bl_shutdown(struct platform_device *pdev) lm3533_ctrlbank_disable(&bl->cb); } +static const struct of_device_id lm3533_bl_match_table[] = { + { .compatible = "ti,lm3533-backlight" }, + { } +}; +MODULE_DEVICE_TABLE(of, lm3533_bl_match_table); + static struct platform_driver lm3533_bl_driver = { .driver = { .name = "lm3533-backlight", .pm = &lm3533_bl_pm_ops, .dev_groups = lm3533_bl_attribute_groups, + .of_match_table = lm3533_bl_match_table, }, .probe = lm3533_bl_probe, .remove = lm3533_bl_remove, diff --git a/include/linux/mfd/lm3533.h b/include/linux/mfd/lm3533.h index 74cfb52c3bd1..5710e88da45d 100644 --- a/include/linux/mfd/lm3533.h +++ b/include/linux/mfd/lm3533.h @@ -15,6 +15,10 @@ #define LM3533_ATTR_RW(_name) \ DEVICE_ATTR(_name, S_IRUGO | S_IWUSR , show_##_name, store_##_name) +#define LM3533_MAX_CURRENT_MIN 5000 +#define LM3533_MAX_CURRENT_MAX 29800 +#define LM3533_MAX_CURRENT_STEP 800 + struct device; struct gpio_desc; struct regmap; @@ -25,7 +29,9 @@ struct lm3533 { struct regmap *regmap; struct gpio_desc *hwen; - int irq; + + u32 boost_ovp; + u32 boost_freq; unsigned have_als:1; unsigned have_backlights:1; @@ -38,50 +44,6 @@ struct lm3533_ctrlbank { int id; }; -struct lm3533_als_platform_data { - unsigned pwm_mode:1; /* PWM input mode (default analog) */ - u8 r_select; /* 1 - 127 (ignored in PWM-mode) */ -}; - -struct lm3533_bl_platform_data { - char *name; - u16 max_current; /* 5000 - 29800 uA (800 uA step) */ - u8 default_brightness; /* 0 - 255 */ - u8 pwm; /* 0 - 0x3f */ -}; - -struct lm3533_led_platform_data { - char *name; - const char *default_trigger; - u16 max_current; /* 5000 - 29800 uA (800 uA step) */ - u8 pwm; /* 0 - 0x3f */ -}; - -enum lm3533_boost_freq { - LM3533_BOOST_FREQ_500KHZ, - LM3533_BOOST_FREQ_1000KHZ, -}; - -enum lm3533_boost_ovp { - LM3533_BOOST_OVP_16V, - LM3533_BOOST_OVP_24V, - LM3533_BOOST_OVP_32V, - LM3533_BOOST_OVP_40V, -}; - -struct lm3533_platform_data { - enum lm3533_boost_ovp boost_ovp; - enum lm3533_boost_freq boost_freq; - - struct lm3533_als_platform_data *als; - - struct lm3533_bl_platform_data *backlights; - int num_backlights; - - struct lm3533_led_platform_data *leds; - int num_leds; -}; - int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb); int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb); From 4a2300981a68c8f6ef5c667d6f81667c4036b269 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:27 +0300 Subject: [PATCH 09/74] mfd: lm3533: Add support for VIN power supply Add support for 2.7V-5.5V VIN power supply. Signed-off-by: Svyatoslav Ryhel Link: https://patch.msgid.link/20260731113632.158440-10-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/lm3533-core.c | 23 +++++++++++++++++++++-- include/linux/mfd/lm3533.h | 2 ++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c index c7c7e71719ae..2a63df034171 100644 --- a/drivers/mfd/lm3533-core.c +++ b/drivers/mfd/lm3533-core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -111,14 +112,25 @@ static int lm3533_set_lvled_config(struct lm3533 *lm3533, u8 lvled, u8 led) return ret; } -static void lm3533_enable(struct lm3533 *lm3533) +static int lm3533_enable(struct lm3533 *lm3533) { + int ret; + + ret = regulator_enable(lm3533->vin_supply); + if (ret) { + dev_err(lm3533->dev, "failed to enable vin power supply\n"); + return ret; + } + gpiod_set_value(lm3533->hwen, 1); + + return 0; } static void lm3533_disable(struct lm3533 *lm3533) { gpiod_set_value(lm3533->hwen, 0); + regulator_disable(lm3533->vin_supply); } enum lm3533_attribute_type { @@ -332,7 +344,9 @@ static int lm3533_device_init(struct lm3533 *lm3533) } } - lm3533_enable(lm3533); + ret = lm3533_enable(lm3533); + if (ret) + return ret; ret = regmap_update_bits(lm3533->regmap, LM3533_REG_BOOST_PWM, LM3533_BOOST_FREQ_MASK, @@ -446,6 +460,11 @@ static int lm3533_i2c_probe(struct i2c_client *i2c) return dev_err_probe(lm3533->dev, PTR_ERR(lm3533->hwen), "failed to get HWEN GPIO\n"); + lm3533->vin_supply = devm_regulator_get(lm3533->dev, "vin"); + if (IS_ERR(lm3533->vin_supply)) + return dev_err_probe(lm3533->dev, PTR_ERR(lm3533->vin_supply), + "failed to get vin-supply\n"); + device_property_read_u32(lm3533->dev, "ti,boost-ovp-microvolt", &lm3533->boost_ovp); diff --git a/include/linux/mfd/lm3533.h b/include/linux/mfd/lm3533.h index 5710e88da45d..8f72dd41e8f0 100644 --- a/include/linux/mfd/lm3533.h +++ b/include/linux/mfd/lm3533.h @@ -22,6 +22,7 @@ struct device; struct gpio_desc; struct regmap; +struct regulator; struct lm3533 { struct device *dev; @@ -29,6 +30,7 @@ struct lm3533 { struct regmap *regmap; struct gpio_desc *hwen; + struct regulator *vin_supply; u32 boost_ovp; u32 boost_freq; From 95a968ec7c31d70d4e38840409c3983ca7cac937 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:28 +0300 Subject: [PATCH 10/74] mfd: lm3533: Set DMA mask Missing coherent_dma_mask assigning triggers the following warning in dmesg: [ 3.287872] platform lm3533-backlight.0: DMA mask not set Since this warning might be elevated to an error in the future, set coherent_dma_mask to zero because both the core and cells do not utilize DMA. Signed-off-by: Svyatoslav Ryhel Link: https://patch.msgid.link/20260731113632.158440-11-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/lm3533-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c index 2a63df034171..9233da7ce099 100644 --- a/drivers/mfd/lm3533-core.c +++ b/drivers/mfd/lm3533-core.c @@ -479,6 +479,10 @@ static int lm3533_i2c_probe(struct i2c_client *i2c) LM3533_BOOST_FREQ_MAX); lm3533->boost_freq = lm3533->boost_freq / (500 * KILO) - 1; + /* LM3533 and child devices do not use DMA */ + i2c->dev.coherent_dma_mask = 0; + i2c->dev.dma_mask = &i2c->dev.coherent_dma_mask; + return lm3533_device_init(lm3533); } From 92992a77a4c471064cec8398310c3f9486340c16 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:29 +0300 Subject: [PATCH 11/74] video: backlight: lm3533_bl: Improve logic of sysfs functions Simplify the sysfs logic of properties by switching to macros and proper regmap helpers. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20260731113632.158440-12-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/video/backlight/lm3533_bl.c | 62 ++++++++++------------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c index 5ce237cb4219..2e31a79628d6 100644 --- a/drivers/video/backlight/lm3533_bl.c +++ b/drivers/video/backlight/lm3533_bl.c @@ -22,6 +22,8 @@ #define LM3533_BL_MAX_BRIGHTNESS 255 #define LM3533_REG_CTRLBANK_AB_BCONF 0x1a +#define CTRLBANK_AB_BCONF_ALS(n) BIT(2 * (n)) +#define CTRLBANK_AB_BCONF_MODE(n) BIT(2 * (n) + 1) struct lm3533_bl { @@ -84,88 +86,68 @@ static ssize_t show_als_channel(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%u\n", channel); } -static ssize_t show_als_en(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t show_als_en(struct device *dev, struct device_attribute *attr, + char *buf) { struct lm3533_bl *bl = dev_get_drvdata(dev); int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); - u32 val; - u8 mask; - bool enable; int ret; - ret = regmap_read(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF, &val); - if (ret) + ret = regmap_test_bits(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF, + CTRLBANK_AB_BCONF_ALS(ctrlbank)); + if (ret < 0) return ret; - mask = 1 << (2 * ctrlbank); - enable = val & mask; - - return scnprintf(buf, PAGE_SIZE, "%d\n", enable); + return scnprintf(buf, PAGE_SIZE, "%d\n", ret); } -static ssize_t store_als_en(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) +static ssize_t store_als_en(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) { struct lm3533_bl *bl = dev_get_drvdata(dev); int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); int enable; - u8 mask; int ret; if (kstrtoint(buf, 0, &enable)) return -EINVAL; - mask = 1 << (2 * ctrlbank); - ret = regmap_assign_bits(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF, - mask, enable); + CTRLBANK_AB_BCONF_ALS(ctrlbank), enable); if (ret) return ret; return len; } -static ssize_t show_linear(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t show_linear(struct device *dev, struct device_attribute *attr, + char *buf) { struct lm3533_bl *bl = dev_get_drvdata(dev); - u32 val; - u8 mask; - int linear; + int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); int ret; - ret = regmap_read(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF, &val); - if (ret) + ret = regmap_test_bits(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF, + CTRLBANK_AB_BCONF_MODE(ctrlbank)); + if (ret < 0) return ret; - mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1); - - if (val & mask) - linear = 1; - else - linear = 0; - - return scnprintf(buf, PAGE_SIZE, "%x\n", linear); + return scnprintf(buf, PAGE_SIZE, "%x\n", ret); } -static ssize_t store_linear(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) +static ssize_t store_linear(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) { struct lm3533_bl *bl = dev_get_drvdata(dev); + int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); unsigned long linear; - u8 mask; int ret; if (kstrtoul(buf, 0, &linear)) return -EINVAL; - mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1); - ret = regmap_assign_bits(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF, - mask, linear); + CTRLBANK_AB_BCONF_MODE(ctrlbank), linear); if (ret) return ret; From 879dd08125dbec68ea0d5b624aa8c42175232637 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:30 +0300 Subject: [PATCH 12/74] video: backlight: lm3533_bl: Set initial mapping mode from DT Add support to obtain the initial mapping mode from DT instead of leaving it unconfigured. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20260731113632.158440-13-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/video/backlight/lm3533_bl.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c index 2e31a79628d6..666213cab667 100644 --- a/drivers/video/backlight/lm3533_bl.c +++ b/drivers/video/backlight/lm3533_bl.c @@ -36,6 +36,7 @@ struct lm3533_bl { u32 pwm; bool have_als; + bool linear; }; @@ -230,8 +231,14 @@ static const struct attribute_group *lm3533_bl_attribute_groups[] = { static int lm3533_bl_setup(struct lm3533_bl *bl) { + int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); int ret; + ret = regmap_assign_bits(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF, + CTRLBANK_AB_BCONF_MODE(ctrlbank), bl->linear); + if (ret) + return ret; + ret = lm3533_ctrlbank_set_max_current(&bl->cb, bl->max_current); if (ret) return ret; @@ -285,6 +292,9 @@ static int lm3533_bl_probe(struct platform_device *pdev) props.max_brightness = LM3533_BL_MAX_BRIGHTNESS; props.brightness = default_brightness; + bl->linear = device_property_read_bool(&pdev->dev, + "ti,linear-mapping-mode"); + bd = devm_backlight_device_register(&pdev->dev, name, &pdev->dev, bl, &lm3533_bl_ops, &props); if (IS_ERR(bd)) { From 0077e5ed836a01a0c82f23b91afdd38e42ace8b8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 31 Jul 2026 14:36:31 +0300 Subject: [PATCH 13/74] video: backlight: lm3533_bl: Implement backlight_scale property Since the device supports linear and non-linear modes, implement the backlight_scale property to describe this state. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20260731113632.158440-14-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/video/backlight/lm3533_bl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c index 666213cab667..adcd6ac7cf22 100644 --- a/drivers/video/backlight/lm3533_bl.c +++ b/drivers/video/backlight/lm3533_bl.c @@ -141,6 +141,7 @@ static ssize_t store_linear(struct device *dev, struct device_attribute *attr, { struct lm3533_bl *bl = dev_get_drvdata(dev); int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); + struct backlight_device *bd = bl->bd; unsigned long linear; int ret; @@ -152,6 +153,9 @@ static ssize_t store_linear(struct device *dev, struct device_attribute *attr, if (ret) return ret; + bd->props.scale = linear ? BACKLIGHT_SCALE_LINEAR : + BACKLIGHT_SCALE_NON_LINEAR; + return len; } @@ -294,6 +298,8 @@ static int lm3533_bl_probe(struct platform_device *pdev) bl->linear = device_property_read_bool(&pdev->dev, "ti,linear-mapping-mode"); + props.scale = bl->linear ? BACKLIGHT_SCALE_LINEAR : + BACKLIGHT_SCALE_NON_LINEAR; bd = devm_backlight_device_register(&pdev->dev, name, &pdev->dev, bl, &lm3533_bl_ops, &props); From 6277c3cd74f54c26bf4d5e88182a378ee7190d90 Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Thu, 17 Sep 2026 09:59:00 +0200 Subject: [PATCH 14/74] mfd: khadas-mcu: Add per-variant configuration infrastructure and VIM4 support Some values change per variant, add the structure to manage it. Add VIM4 specific registers done for a subsequent patch, which will use a new compatible especially to get the right regmap config. Add an enum khadas_mcu_type used as value to match. Also fix a kernel-doc warning for the khadas_mcu struct. Reviewed-by: Neil Armstrong Signed-off-by: Ronald Claveau Link: https://patch.msgid.link/20260917-add-mcu-fan-khadas-vim4-v12-1-497cd543a148@aliel.fr Signed-off-by: Lee Jones --- include/linux/mfd/khadas-mcu.h | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/include/linux/mfd/khadas-mcu.h b/include/linux/mfd/khadas-mcu.h index a99ba2ed0e4e..acd3291061b4 100644 --- a/include/linux/mfd/khadas-mcu.h +++ b/include/linux/mfd/khadas-mcu.h @@ -70,6 +70,13 @@ #define KHADAS_MCU_WOL_INIT_START_REG 0x87 /* WO */ #define KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG 0x88 /* WO */ +/* VIM4 specific registers */ +#define KHADAS_MCU_VIM4_REST_CONF_REG 0x2c /* WO - reset EEPROM */ +#define KHADAS_MCU_VIM4_LED_ON_RAM_REG 0x89 /* WO - LED volatile */ +#define KHADAS_MCU_VIM4_FAN_CTRL_REG 0x8a /* WO */ +#define KHADAS_MCU_VIM4_WDT_EN_REG 0x8b /* WO */ +#define KHADAS_MCU_VIM4_SYS_RST_REG 0x91 /* WO */ + enum { KHADAS_BOARD_VIM1 = 0x1, KHADAS_BOARD_VIM2, @@ -80,7 +87,7 @@ enum { /** * struct khadas_mcu - Khadas MCU structure - * @device: device reference used for logs + * @dev: device reference used for logs * @regmap: register map */ struct khadas_mcu { @@ -88,4 +95,14 @@ struct khadas_mcu { struct regmap *regmap; }; +/** + * enum khadas_mcu_type - Khadas MCU hardware variant + * @KHADAS_MCU_GENERIC: VIM1, VIM2, VIM3, Edge, Edge-V (shared register map) + * @KHADAS_MCU_VIM4: VIM4 (extended register map, distinct fan/LED/WDT regs) + */ +enum khadas_mcu_type { + KHADAS_MCU_GENERIC = 1, + KHADAS_MCU_VIM4, +}; + #endif /* MFD_KHADAS_MCU_H */ From f9f61c21b942ac387acd7fb3ef1f950cba59755f Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Thu, 17 Sep 2026 09:59:01 +0200 Subject: [PATCH 15/74] mfd: khadas-mcu: Use MFD_CELL_* macros for cell declarations Convert khadas_mcu_fan_cells[] and khadas_mcu_cells[] to use the MFD_CELL_NAME() helper macro instead of open-coding the struct mfd_cell initialisers. While at it, make both arrays const since they are never modified after initialisation. This is a pure cleanup with no functional change, done in preparation for a subsequent patch which will need to attach platform_data to the fan cell. Signed-off-by: Ronald Claveau Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20260917-add-mcu-fan-khadas-vim4-v12-2-497cd543a148@aliel.fr Signed-off-by: Lee Jones --- drivers/mfd/khadas-mcu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/khadas-mcu.c b/drivers/mfd/khadas-mcu.c index ba981a788692..0e74efe14f36 100644 --- a/drivers/mfd/khadas-mcu.c +++ b/drivers/mfd/khadas-mcu.c @@ -75,13 +75,13 @@ static const struct regmap_config khadas_mcu_regmap_config = { .cache_type = REGCACHE_MAPLE, }; -static struct mfd_cell khadas_mcu_fan_cells[] = { +static const struct mfd_cell khadas_mcu_fan_cells[] = { /* VIM1/2 Rev13+ and VIM3 only */ - { .name = "khadas-mcu-fan-ctrl", }, + MFD_CELL_NAME("khadas-mcu-fan-ctrl"), }; -static struct mfd_cell khadas_mcu_cells[] = { - { .name = "khadas-mcu-user-mem", }, +static const struct mfd_cell khadas_mcu_cells[] = { + MFD_CELL_NAME("khadas-mcu-user-mem"), }; static int khadas_mcu_probe(struct i2c_client *client) From c94c97f8c97e684d44ef5483d7f2ad6b3dfa55ac Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Thu, 17 Sep 2026 09:59:02 +0200 Subject: [PATCH 16/74] mfd: khadas-mcu: Add support for VIM4 MCU variant Refactor probe() to use per-variant values instead of hardcoded globals. Add dedicated regmap configuration for the VIM4 MCU, with its own volatile/writeable registers. Add a new compatible string "khadas,vim4-mcu". Reviewed-by: Neil Armstrong Signed-off-by: Ronald Claveau Link: https://patch.msgid.link/20260917-add-mcu-fan-khadas-vim4-v12-3-497cd543a148@aliel.fr Signed-off-by: Lee Jones --- drivers/mfd/khadas-mcu.c | 96 ++++++++++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 13 deletions(-) diff --git a/drivers/mfd/khadas-mcu.c b/drivers/mfd/khadas-mcu.c index 0e74efe14f36..4b37e74e2b04 100644 --- a/drivers/mfd/khadas-mcu.c +++ b/drivers/mfd/khadas-mcu.c @@ -84,38 +84,107 @@ static const struct mfd_cell khadas_mcu_cells[] = { MFD_CELL_NAME("khadas-mcu-user-mem"), }; +static bool khadas_mcu_vim4_reg_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case KHADAS_MCU_PWR_OFF_CMD_REG: + case KHADAS_MCU_VIM4_REST_CONF_REG: + case KHADAS_MCU_WOL_INIT_START_REG: + case KHADAS_MCU_VIM4_LED_ON_RAM_REG: + case KHADAS_MCU_VIM4_FAN_CTRL_REG: + case KHADAS_MCU_VIM4_WDT_EN_REG: + case KHADAS_MCU_VIM4_SYS_RST_REG: + return true; + default: + return false; + } +} + +static bool khadas_mcu_vim4_reg_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case KHADAS_MCU_VERSION_0_REG: + case KHADAS_MCU_VERSION_1_REG: + case KHADAS_MCU_SHUTDOWN_NORMAL_STATUS_REG: + return false; + default: + return true; + } +} + +static const struct regmap_config khadas_mcu_vim4_regmap_config = { + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .max_register = KHADAS_MCU_VIM4_SYS_RST_REG, + .volatile_reg = khadas_mcu_vim4_reg_volatile, + .writeable_reg = khadas_mcu_vim4_reg_writeable, + .cache_type = REGCACHE_MAPLE, +}; + +static const struct mfd_cell khadas_mcu_vim4_fan_cells[] = { + MFD_CELL_NAME("khadas-mcu-vim4-fan"), +}; + static int khadas_mcu_probe(struct i2c_client *client) { + const struct mfd_cell *cells, *fan_cells; + const struct regmap_config *regmap_cfg; struct device *dev = &client->dev; + int ncells, nfan_cells, ret; struct khadas_mcu *ddata; - int ret; + const void *mcu_variant; ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) return -ENOMEM; + mcu_variant = i2c_get_match_data(client); + if (!mcu_variant) + return -ENODEV; + + switch ((uintptr_t)mcu_variant) { + case KHADAS_MCU_GENERIC: + regmap_cfg = &khadas_mcu_regmap_config; + cells = khadas_mcu_cells; + ncells = ARRAY_SIZE(khadas_mcu_cells); + fan_cells = khadas_mcu_fan_cells; + nfan_cells = ARRAY_SIZE(khadas_mcu_fan_cells); + break; + case KHADAS_MCU_VIM4: + regmap_cfg = &khadas_mcu_vim4_regmap_config; + cells = NULL; + ncells = 0; + fan_cells = khadas_mcu_vim4_fan_cells; + nfan_cells = ARRAY_SIZE(khadas_mcu_vim4_fan_cells); + break; + default: + return -ENODEV; + } + i2c_set_clientdata(client, ddata); ddata->dev = dev; - ddata->regmap = devm_regmap_init_i2c(client, &khadas_mcu_regmap_config); + ddata->regmap = devm_regmap_init_i2c(client, regmap_cfg); if (IS_ERR(ddata->regmap)) { ret = PTR_ERR(ddata->regmap); - dev_err(dev, "Failed to allocate register map: %d\n", ret); - return ret; + return dev_err_probe(dev, ret, "Failed to allocate register map\n"); } - ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, - khadas_mcu_cells, - ARRAY_SIZE(khadas_mcu_cells), - NULL, 0, NULL); - if (ret) - return ret; + if (cells && ncells) { + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + cells, + ncells, + NULL, 0, NULL); + if (ret) + return ret; + } if (of_property_present(dev->of_node, "#cooling-cells")) return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, - khadas_mcu_fan_cells, - ARRAY_SIZE(khadas_mcu_fan_cells), + fan_cells, + nfan_cells, NULL, 0, NULL); return 0; @@ -123,7 +192,8 @@ static int khadas_mcu_probe(struct i2c_client *client) #ifdef CONFIG_OF static const struct of_device_id khadas_mcu_of_match[] = { - { .compatible = "khadas,mcu", }, + { .compatible = "khadas,mcu", .data = (void *)KHADAS_MCU_GENERIC }, + { .compatible = "khadas,vim4-mcu", .data = (void *)KHADAS_MCU_VIM4 }, {}, }; MODULE_DEVICE_TABLE(of, khadas_mcu_of_match); From dad6f7897a93b4e1614664dbbcae0f61e57d1ac0 Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Thu, 17 Sep 2026 09:59:03 +0200 Subject: [PATCH 17/74] thermal: khadas-mcu-fan: Add fan config from platform data Add regulator support Introduce platform name, control register and duty-cycle levels per variant. VIM4 uses a different register and level set (0-100) declared as a list (0, 30, 40, 55, 75, 100) compared to previous boards (0-3). MAX_LEVEL is kept and bumped from 3 to 5 to make the speed change more gradual with the newer MCUs. The cooling device's state is stored as an index into levels[], and khadas_mcu_fan_set_level() writes levels[state] to fan_reg, rather than writing the raw state value as before. Optionally acquire and enable a "fan" regulator supply at probe time and on resume, so boards that gate fan power through a regulator are handled. The regulator is released via a devm_add_action_or_reset() callback. Signed-off-by: Ronald Claveau Reviewed-by: Neil Armstrong Acked-by: Daniel Lezcano Link: https://patch.msgid.link/20260917-add-mcu-fan-khadas-vim4-v12-4-497cd543a148@aliel.fr Signed-off-by: Lee Jones --- drivers/thermal/khadas_mcu_fan.c | 108 ++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 22 deletions(-) diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c index 21b3d0a71bd0..1da639d05497 100644 --- a/drivers/thermal/khadas_mcu_fan.c +++ b/drivers/thermal/khadas_mcu_fan.c @@ -13,34 +13,33 @@ #include #include #include +#include +#include -#define MAX_LEVEL 3 +#define MAX_LEVEL 5 struct khadas_mcu_fan_ctx { struct khadas_mcu *mcu; + unsigned int fan_reg; unsigned int level; + const unsigned int *levels; + unsigned int nlevels; struct thermal_cooling_device *cdev; + struct regulator *power; }; static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, unsigned int level) { - int ret; - - ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG, - level); - if (ret) - return ret; - - ctx->level = level; - - return 0; + return regmap_write(ctx->mcu->regmap, ctx->fan_reg, level); } static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { - *state = MAX_LEVEL; + struct khadas_mcu_fan_ctx *ctx = cdev->devdata; + + *state = min_t(unsigned int, MAX_LEVEL, ctx->nlevels - 1); return 0; } @@ -60,14 +59,21 @@ khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { struct khadas_mcu_fan_ctx *ctx = cdev->devdata; + int ret; - if (state > MAX_LEVEL) + if (state > MAX_LEVEL || state >= ctx->nlevels) return -EINVAL; if (state == ctx->level) return 0; - return khadas_mcu_fan_set_level(ctx, state); + ret = khadas_mcu_fan_set_level(ctx, ctx->levels[state]); + if (ret) + return ret; + + ctx->level = state; + + return 0; } static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = { @@ -76,22 +82,71 @@ static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = { .set_cur_state = khadas_mcu_fan_set_cur_state, }; +static void khadas_mcu_fan_regulator_disable(void *data) +{ + struct regulator *power = data; + + regulator_disable(power); +} + +static const unsigned int khadas_mcu_fan_levels[] = { 0, 1, 2, 3 }; + +static const unsigned int khadas_mcu_vim4_fan_levels[] = { 0, 30, 40, 55, 75, 100 }; + static int khadas_mcu_fan_probe(struct platform_device *pdev) { + const struct platform_device_id *id = platform_get_device_id(pdev); struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent); struct thermal_cooling_device *cdev; struct device *dev = &pdev->dev; struct khadas_mcu_fan_ctx *ctx; int ret; + + if (!dev->of_node) + dev->of_node = of_node_get(dev->parent->of_node); + + if (!id) + return -EINVAL; + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; + ctx->mcu = mcu; + switch (id->driver_data) { + case KHADAS_MCU_GENERIC: + ctx->fan_reg = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG; + ctx->levels = khadas_mcu_fan_levels; + ctx->nlevels = ARRAY_SIZE(khadas_mcu_fan_levels); + break; + case KHADAS_MCU_VIM4: + ctx->fan_reg = KHADAS_MCU_VIM4_FAN_CTRL_REG; + ctx->levels = khadas_mcu_vim4_fan_levels; + ctx->nlevels = ARRAY_SIZE(khadas_mcu_vim4_fan_levels); + break; + default: + return -ENODEV; + } + + ctx->power = devm_regulator_get(dev, "fan"); + if (IS_ERR(ctx->power)) + return PTR_ERR(ctx->power); + + ret = regulator_enable(ctx->power); + if (ret) { + dev_err(dev, "Failed to enable fan power supply: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, khadas_mcu_fan_regulator_disable, ctx->power); + if (ret) + return ret; + platform_set_drvdata(pdev, ctx); - cdev = devm_thermal_of_child_cooling_device_register(dev->parent, - dev->parent->of_node, + cdev = devm_thermal_of_child_cooling_device_register(dev, + dev->of_node, "khadas-mcu-fan", ctx, &khadas_mcu_fan_cooling_ops); if (IS_ERR(cdev)) { @@ -109,21 +164,24 @@ static void khadas_mcu_fan_shutdown(struct platform_device *pdev) { struct khadas_mcu_fan_ctx *ctx = platform_get_drvdata(pdev); - khadas_mcu_fan_set_level(ctx, 0); + khadas_mcu_fan_set_level(ctx, ctx->levels[0]); } #ifdef CONFIG_PM_SLEEP static int khadas_mcu_fan_suspend(struct device *dev) { struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev); - unsigned int level_save = ctx->level; int ret; - ret = khadas_mcu_fan_set_level(ctx, 0); + ret = khadas_mcu_fan_set_level(ctx, ctx->levels[0]); if (ret) return ret; - ctx->level = level_save; + ret = regulator_disable(ctx->power); + if (ret) { + khadas_mcu_fan_set_level(ctx, ctx->levels[ctx->level]); + return ret; + } return 0; } @@ -131,8 +189,13 @@ static int khadas_mcu_fan_suspend(struct device *dev) static int khadas_mcu_fan_resume(struct device *dev) { struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev); + int ret; - return khadas_mcu_fan_set_level(ctx, ctx->level); + ret = regulator_enable(ctx->power); + if (ret) + return ret; + + return khadas_mcu_fan_set_level(ctx, ctx->levels[ctx->level]); } #endif @@ -140,7 +203,8 @@ static SIMPLE_DEV_PM_OPS(khadas_mcu_fan_pm, khadas_mcu_fan_suspend, khadas_mcu_fan_resume); static const struct platform_device_id khadas_mcu_fan_id_table[] = { - { .name = "khadas-mcu-fan-ctrl", }, + { .name = "khadas-mcu-fan-ctrl", .driver_data = KHADAS_MCU_GENERIC }, + { .name = "khadas-mcu-vim4-fan", .driver_data = KHADAS_MCU_VIM4 }, {}, }; MODULE_DEVICE_TABLE(platform, khadas_mcu_fan_id_table); From 43be6abd6775820fdac3729962f7e7b841104fda Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 23 Sep 2026 19:46:55 +0200 Subject: [PATCH 18/74] mfd: dt-bindings: ab8500: Describe codec graph and microphone wiring The AB8500 and AB8505 codec block exposes two digital audio interfaces. Define the codec as a one-cell sound DAI provider and allow it to expose both interfaces as audio graph ports. The codec binding conversion also omitted the analog microphone type and bias-routing properties supported by the driver and documented by the old text binding. Restore them so boards can describe the codec wiring without schema warnings. Assisted-by: LLM Signed-off-by: Linus Walleij Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260923-ux500-simple-sound-v3-1-56a771c3ea28@kernel.org Signed-off-by: Lee Jones --- .../bindings/mfd/stericsson,ab8500.yaml | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml b/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml index 0fdfbfdfe88a..873e08266a2b 100644 --- a/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml +++ b/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml @@ -312,6 +312,33 @@ properties: items: - const: audioclk + '#sound-dai-cells': + description: Selects one of the two digital audio interfaces + const: 1 + + ports: + $ref: /schemas/sound/audio-graph-port.yaml#/definitions/ports + + stericsson,amic1-type-single-ended: + description: Configure analog microphone 1 as single-ended + type: boolean + + stericsson,amic2-type-single-ended: + description: Configure analog microphone 2 as single-ended + type: boolean + + stericsson,amic1a-bias-vamic2: + description: Supply analog microphone 1A from VAMIC2 instead of VAMIC1 + type: boolean + + stericsson,amic1b-bias-vamic2: + description: Supply analog microphone 1B from VAMIC2 instead of VAMIC1 + type: boolean + + stericsson,amic2-bias-vamic1: + description: Supply analog microphone 2 from VAMIC1 instead of VAMIC2 + type: boolean + stericsson,earpeice-cmv: description: Earpiece voltage $ref: /schemas/types.yaml#/definitions/uint32 @@ -319,6 +346,7 @@ properties: required: - compatible + - '#sound-dai-cells' additionalProperties: false From fd2258999ecdc7164ef0cda2d4102e4558a5660f Mon Sep 17 00:00:00 2001 From: Hongyan Xu Date: Fri, 14 Aug 2026 23:30:19 +0800 Subject: [PATCH 19/74] mfd: da903x: Cancel IRQ work during teardown The IRQ handler disables the IRQ and schedules irq_work. Releasing the IRQ does not drain that work, which can continue to use the devm-allocated chip and notifier state. Manage both IRQ-work cancellation and subdevice removal with devres. Register the subdevice-removal action before requesting the IRQ, then add the IRQ-work action after the request. This avoids leaving a live IRQ without work cancellation if action registration fails. Devres cleanup then disables the IRQ and drains the work before releasing the IRQ and removing child devices. Register both actions before creating the children so partial probe failures use the same ordering. This issue was found by the author's in-house static analysis tool. The patch was reviewed by the author against the latest mainline tree. Fixes: 26b8f5e1e2d1 ("mfd: add base support for Dialog DA9030/DA9034 PMICs") Cc: stable@vger.kernel.org Suggested-by: Lee Jones Assisted-by: Codex:GPT-5 Signed-off-by: Hongyan Xu Link: https://patch.msgid.link/20260814153019.1114-1-getshell@seu.edu.cn Signed-off-by: Lee Jones --- drivers/mfd/da903x.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c index e86b39de3303..e4ad48e1f139 100644 --- a/drivers/mfd/da903x.c +++ b/drivers/mfd/da903x.c @@ -421,6 +421,14 @@ static irqreturn_t da903x_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static void da903x_cancel_irq_work(void *data) +{ + struct da903x_chip *chip = data; + + disable_irq(chip->client->irq); + cancel_work_sync(&chip->irq_work); +} + static const struct da903x_chip_ops da903x_ops[] = { [0] = { .init_chip = da9030_init_chip, @@ -456,6 +464,13 @@ static int da903x_remove_subdevs(struct da903x_chip *chip) return device_for_each_child(chip->dev, NULL, __remove_subdev); } +static void da903x_remove_subdevs_action(void *data) +{ + struct da903x_chip *chip = data; + + da903x_remove_subdevs(chip); +} + static int da903x_add_subdevs(struct da903x_chip *chip, struct da903x_platform_data *pdata) { @@ -484,7 +499,6 @@ static int da903x_add_subdevs(struct da903x_chip *chip, return 0; failed: - da903x_remove_subdevs(chip); return ret; } @@ -520,6 +534,11 @@ static int da903x_probe(struct i2c_client *client) chip->ops->mask_events(chip, chip->events_mask); chip->ops->read_events(chip, &tmp); + ret = devm_add_action_or_reset(&client->dev, + da903x_remove_subdevs_action, chip); + if (ret) + return ret; + ret = devm_request_irq(&client->dev, client->irq, da903x_irq_handler, IRQF_TRIGGER_FALLING, "da903x", chip); @@ -529,22 +548,19 @@ static int da903x_probe(struct i2c_client *client) return ret; } + ret = devm_add_action_or_reset(&client->dev, da903x_cancel_irq_work, + chip); + if (ret) + return ret; + return da903x_add_subdevs(chip, pdata); } -static void da903x_remove(struct i2c_client *client) -{ - struct da903x_chip *chip = i2c_get_clientdata(client); - - da903x_remove_subdevs(chip); -} - static struct i2c_driver da903x_driver = { .driver = { .name = "da903x", }, .probe = da903x_probe, - .remove = da903x_remove, .id_table = da903x_id_table, }; From 021accc07b1eb304c38a0835ea9dcebb83e4577c Mon Sep 17 00:00:00 2001 From: Hiroki Nakajima <3na7nanana@gmail.com> Date: Mon, 24 Aug 2026 02:42:48 +0900 Subject: [PATCH 20/74] mfd: da9062: Use DEFINE_RES_IRQ_NAMED() Use the dedicated DEFINE_RES_IRQ_NAMED() helper for the DA9062 IRQ resources instead of open-coding it with DEFINE_RES_NAMED(). This makes the intended resource type explicit and keeps the definitions consistent with the DA9061 resources. This is a mechanical cleanup with no functional change. With an x86_64 allmodconfig and W=1, drivers/mfd/da9062-core.o builds successfully and is byte-identical before and after this change. Assisted-by: Codex:gpt-5 coccinelle Signed-off-by: Hiroki Nakajima <3na7nanana@gmail.com> Link: https://patch.msgid.link/20260823174248.833535-1-3na7nanana@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/da9062-core.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c index 637c5f47a4b0..ad198183aff7 100644 --- a/drivers/mfd/da9062-core.c +++ b/drivers/mfd/da9062-core.c @@ -203,36 +203,36 @@ static const struct mfd_cell da9061_devs_noirq[] = { }; static const struct resource da9062_core_resources[] = { - DEFINE_RES_NAMED(DA9062_IRQ_VDD_WARN, 1, "VDD_WARN", IORESOURCE_IRQ), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_VDD_WARN, "VDD_WARN"), }; static const struct resource da9062_regulators_resources[] = { - DEFINE_RES_NAMED(DA9062_IRQ_LDO_LIM, 1, "LDO_LIM", IORESOURCE_IRQ), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_LDO_LIM, "LDO_LIM"), }; static const struct resource da9062_thermal_resources[] = { - DEFINE_RES_NAMED(DA9062_IRQ_TEMP, 1, "THERMAL", IORESOURCE_IRQ), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_TEMP, "THERMAL"), }; static const struct resource da9062_wdt_resources[] = { - DEFINE_RES_NAMED(DA9062_IRQ_WDG_WARN, 1, "WD_WARN", IORESOURCE_IRQ), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_WDG_WARN, "WD_WARN"), }; static const struct resource da9062_rtc_resources[] = { - DEFINE_RES_NAMED(DA9062_IRQ_ALARM, 1, "ALARM", IORESOURCE_IRQ), - DEFINE_RES_NAMED(DA9062_IRQ_TICK, 1, "TICK", IORESOURCE_IRQ), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_ALARM, "ALARM"), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_TICK, "TICK"), }; static const struct resource da9062_onkey_resources[] = { - DEFINE_RES_NAMED(DA9062_IRQ_ONKEY, 1, "ONKEY", IORESOURCE_IRQ), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_ONKEY, "ONKEY"), }; static const struct resource da9062_gpio_resources[] = { - DEFINE_RES_NAMED(DA9062_IRQ_GPI0, 1, "GPI0", IORESOURCE_IRQ), - DEFINE_RES_NAMED(DA9062_IRQ_GPI1, 1, "GPI1", IORESOURCE_IRQ), - DEFINE_RES_NAMED(DA9062_IRQ_GPI2, 1, "GPI2", IORESOURCE_IRQ), - DEFINE_RES_NAMED(DA9062_IRQ_GPI3, 1, "GPI3", IORESOURCE_IRQ), - DEFINE_RES_NAMED(DA9062_IRQ_GPI4, 1, "GPI4", IORESOURCE_IRQ), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_GPI0, "GPI0"), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_GPI1, "GPI1"), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_GPI2, "GPI2"), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_GPI3, "GPI3"), + DEFINE_RES_IRQ_NAMED(DA9062_IRQ_GPI4, "GPI4"), }; static const struct mfd_cell da9062_devs_irq[] = { From 2fdae2d590a0a5ad4cd60bf8e7a6ecba7265f268 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Thu, 27 Aug 2026 16:27:41 +0200 Subject: [PATCH 21/74] mfd: Fix MAX77705 dependency on regmap The max77705 uses devm_regmap_add_irq_chip(), so add REGMAP_IRQ and REGMAP_I2C to what the core driver selects in order to provide these APIs. This fixes a build error: drivers/mfd/max77705.o: in function `max77705_i2c_probe': max77705.c:(.text+0x250): undefined reference to `devm_regmap_add_irq_chip' Cc: stable@vger.kernel.org Fixes: c8d50f029748 ("mfd: Add new driver for MAX77705 PMIC") Signed-off-by: David Heidelberg Link: https://patch.msgid.link/20260827-deps-reg-max77705-v1-1-c83a74d3d428@ixit.cz Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 439dbf5ee110..0a2778052519 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1006,6 +1006,8 @@ config MFD_MAX77705 depends on I2C select MFD_CORE select MFD_SIMPLE_MFD_I2C + select REGMAP_I2C + select REGMAP_IRQ help Say yes here to add support for Maxim Integrated MAX77705 PMIC. This is a Power Management IC with Charger, safe LDOs, Flash, Haptic From 4119908f3376d22c71fe9ac7199ef33bf4c97b68 Mon Sep 17 00:00:00 2001 From: Hemanth Selam Date: Mon, 7 Sep 2026 10:23:31 +0530 Subject: [PATCH 22/74] mfd: wm831x: Fix typo "convertor" in comment Correct "convertor" to "converter", reported by scripts/checkpatch.pl using the misspelling list in scripts/spelling.txt. Only touches a comment, no code changes. Since v1 the subject carries the driver name and a capitalised description, to match the MFD subsystem convention. Assisted-by: Cursor:claude-opus-5 Signed-off-by: Hemanth Selam Link: https://patch.msgid.link/20260907045331.16932-4-hemanth.selam@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/wm831x-auxadc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c index 744e1d161c5a..6280d4f38981 100644 --- a/drivers/mfd/wm831x-auxadc.c +++ b/drivers/mfd/wm831x-auxadc.c @@ -132,7 +132,7 @@ static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data) 1 << input, 0); wm831x->auxadc_active &= ~(1 << input); - /* Turn off the entire convertor if idle */ + /* Turn off the entire converter if idle */ if (!wm831x->auxadc_active) wm831x_reg_write(wm831x, WM831X_AUXADC_CONTROL, 0); From a56241bdb71ed4b28d63e04771e810d53e936401 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 1 Sep 2026 22:02:13 +0200 Subject: [PATCH 23/74] dt-bindings: mfd: ab8500: Add regulators The buck converters were not initially added to the device tree or drivers as they are mostly always-on, but their outputs supply devices which need to reference the correct regulator. Describe the six AB8500 buck converters when the regulator node uses "stericsson,ab8500-regulator". Describe the AB8505 converters under their native VSMPSA, VSMPSB, VSAFE, VARM, VSMPSC and VSMPSM names when the node uses "stericsson,ab8505-regulator". Keeping the two namespaces conditional on the regulator compatible avoids representing the distinct AB8505 VSAFE and VSMPSC rails with the AB8500 SMPS3 and VAPE node names. Assisted-by: LLM Acked-by: Conor Dooley Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260901-ux500-dts-snowball-regulator-v2-8-fe88b01829bf@kernel.org Signed-off-by: Lee Jones --- .../bindings/mfd/stericsson,ab8500.yaml | 122 ++++++++++++++++-- 1 file changed, 114 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml b/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml index 873e08266a2b..d819954ee731 100644 --- a/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml +++ b/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml @@ -352,6 +352,8 @@ properties: regulator: description: Node describing the AB8500 internal regulators. + LDO means low-dropout regulator. SMPS means switched-mode power + supply; the SMPS regulators described here are buck converters. type: object properties: @@ -467,16 +469,117 @@ properties: $ref: /schemas/regulator/regulator.yaml# unevaluatedProperties: false + allOf: + - if: + properties: + compatible: + const: stericsson,ab8500-regulator + then: + properties: + ab8500_buck_smps1: + description: Analog buck converter for the VSMPS1 1.2V I/O + and memory supply. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + ab8500_buck_smps2: + description: Analog buck converter for the VSMPS2 1.8V I/O + and memory supply. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + ab8500_buck_smps3: + description: Analog buck converter for VSMPS3, which supplies + the VSAFE power domain. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + ab8500_buck_arm: + description: Analog buck converter for the VARM processor + supply. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + ab8500_buck_ape: + description: Analog buck converter for the VAPE application + processor supply. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + ab8500_buck_mod: + description: Analog buck converter for the VMOD modem supply. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + - if: + properties: + compatible: + const: stericsson,ab8505-regulator + then: + properties: + ab8505_buck_smpsa: + description: Analog buck converter for the VSMPSA 1.2V I/O + and memory supply. The selector range depends on OTP + configuration. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + ab8505_buck_smpsb: + description: Analog buck converter for the VSMPSB 1.8V I/O + and memory supply. The selector range depends on OTP + configuration. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + ab8505_buck_safe: + description: Analog buck converter for the VSAFE permanent + logic and DDR supply. The selector range depends on OTP + configuration. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + ab8505_buck_arm: + description: Analog buck converter for the VARM processor + supply, with a 0.6V to 1.39375V selector range. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + ab8505_buck_smpsc: + description: Analog buck converter for the VSMPSC multimedia + and graphics supply. The selector range depends on OTP + configuration. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + ab8505_buck_smpsm: + description: Analog buck converter for the VSMPSM modem + supply. The selector range depends on OTP configuration. + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + required: - compatible - additionalProperties: false + unevaluatedProperties: false regulator-external: - description: Node describing the AB8500 external regulators. This - concerns the autonomous regulators VSMPS1, VSMPS2 and VSMPS3 - that are normally controlled by external electronics but also - sometimes need to be explicitly controlled by software. + description: Node describing AB8500 control interfaces for external + regulators. AB8500 controls these external devices through the + ExtSupply1Ena, ExtSupply2Ena and ExtSupply3Ena signals and associated + low-power-mode signals. They are distinct from the VSMPS1, VSMPS2 and + VSMPS3 buck converters integrated into AB8500. type: object properties: @@ -484,19 +587,22 @@ properties: const: stericsson,ab8500-ext-regulator ab8500_ext1: - description: The voltage for the VSMPS1 external regulator + description: External regulator controlled through ExtSupply1Ena and + ExtSupply12LPn type: object $ref: /schemas/regulator/regulator.yaml# unevaluatedProperties: false ab8500_ext2: - description: The voltage for the VSMPS2 external regulator + description: External regulator controlled through ExtSupply2Ena and + ExtSupply12LPn type: object $ref: /schemas/regulator/regulator.yaml# unevaluatedProperties: false ab8500_ext3: - description: The voltage for the VSMPS3 external regulator + description: External regulator controlled through ExtSupply3Ena and + ExtSupply3LPn type: object $ref: /schemas/regulator/regulator.yaml# unevaluatedProperties: false From 2754d5243bfc2a57a885cf6feb18074f0434c121 Mon Sep 17 00:00:00 2001 From: Triet Hoang Date: Sat, 29 Aug 2026 12:02:28 +0700 Subject: [PATCH 24/74] mfd: Convert to DEFINE_SIMPLE_DEV_PM_OPS() Convert the deprecated SIMPLE_DEV_PM_OPS() to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr(). This allows us to drop the __maybe_unused annotations from PM callbacks. This is a straightforward cleanup with no functional change intended. Signed-off-by: Triet Hoang Link: https://patch.msgid.link/20260829050228.42638-1-triet.hoang.dev@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/iqs62x.c | 8 ++++---- drivers/mfd/max77843.c | 8 ++++---- drivers/mfd/mt6360-core.c | 8 ++++---- drivers/mfd/rk8xx-i2c.c | 4 ++-- drivers/mfd/rn5t618.c | 8 ++++---- drivers/mfd/ti_am335x_tscadc.c | 8 ++++---- drivers/mfd/tps6586x.c | 8 ++++---- drivers/mfd/twl-core.c | 8 ++++---- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c index 412ae7777f72..ef747a7b3845 100644 --- a/drivers/mfd/iqs62x.c +++ b/drivers/mfd/iqs62x.c @@ -1015,7 +1015,7 @@ static void iqs62x_remove(struct i2c_client *client) wait_for_completion(&iqs62x->fw_done); } -static int __maybe_unused iqs62x_suspend(struct device *dev) +static int iqs62x_suspend(struct device *dev) { struct iqs62x_core *iqs62x = dev_get_drvdata(dev); int ret; @@ -1036,7 +1036,7 @@ static int __maybe_unused iqs62x_suspend(struct device *dev) IQS62X_PWR_SETTINGS_PWR_MODE_HALT); } -static int __maybe_unused iqs62x_resume(struct device *dev) +static int iqs62x_resume(struct device *dev) { struct iqs62x_core *iqs62x = dev_get_drvdata(dev); int ret; @@ -1051,7 +1051,7 @@ static int __maybe_unused iqs62x_resume(struct device *dev) IQS62X_PWR_SETTINGS_DIS_AUTO, 0); } -static SIMPLE_DEV_PM_OPS(iqs62x_pm, iqs62x_suspend, iqs62x_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(iqs62x_pm, iqs62x_suspend, iqs62x_resume); static const struct of_device_id iqs62x_of_match[] = { { .compatible = "azoteq,iqs620a" }, @@ -1067,7 +1067,7 @@ static struct i2c_driver iqs62x_i2c_driver = { .driver = { .name = "iqs62x", .of_match_table = iqs62x_of_match, - .pm = &iqs62x_pm, + .pm = pm_sleep_ptr(&iqs62x_pm), }, .probe = iqs62x_probe, .remove = iqs62x_remove, diff --git a/drivers/mfd/max77843.c b/drivers/mfd/max77843.c index 2a48577b1a79..9ae6715642ae 100644 --- a/drivers/mfd/max77843.c +++ b/drivers/mfd/max77843.c @@ -173,7 +173,7 @@ static const struct i2c_device_id max77843_id[] = { { }, }; -static int __maybe_unused max77843_suspend(struct device *dev) +static int max77843_suspend(struct device *dev) { struct i2c_client *i2c = to_i2c_client(dev); struct max77693_dev *max77843 = i2c_get_clientdata(i2c); @@ -185,7 +185,7 @@ static int __maybe_unused max77843_suspend(struct device *dev) return 0; } -static int __maybe_unused max77843_resume(struct device *dev) +static int max77843_resume(struct device *dev) { struct i2c_client *i2c = to_i2c_client(dev); struct max77693_dev *max77843 = i2c_get_clientdata(i2c); @@ -197,12 +197,12 @@ static int __maybe_unused max77843_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(max77843_pm, max77843_suspend, max77843_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(max77843_pm, max77843_suspend, max77843_resume); static struct i2c_driver max77843_i2c_driver = { .driver = { .name = "max77843", - .pm = &max77843_pm, + .pm = pm_sleep_ptr(&max77843_pm), .of_match_table = max77843_dt_match, .suppress_bind_attrs = true, }, diff --git a/drivers/mfd/mt6360-core.c b/drivers/mfd/mt6360-core.c index b9b1036c8ff4..8a97c30cad3f 100644 --- a/drivers/mfd/mt6360-core.c +++ b/drivers/mfd/mt6360-core.c @@ -583,7 +583,7 @@ static int mt6360_probe(struct i2c_client *client) return 0; } -static int __maybe_unused mt6360_suspend(struct device *dev) +static int mt6360_suspend(struct device *dev) { struct i2c_client *i2c = to_i2c_client(dev); @@ -593,7 +593,7 @@ static int __maybe_unused mt6360_suspend(struct device *dev) return 0; } -static int __maybe_unused mt6360_resume(struct device *dev) +static int mt6360_resume(struct device *dev) { struct i2c_client *i2c = to_i2c_client(dev); @@ -604,7 +604,7 @@ static int __maybe_unused mt6360_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(mt6360_pm_ops, mt6360_suspend, mt6360_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(mt6360_pm_ops, mt6360_suspend, mt6360_resume); static const struct of_device_id __maybe_unused mt6360_of_id[] = { { .compatible = "mediatek,mt6360", }, @@ -615,7 +615,7 @@ MODULE_DEVICE_TABLE(of, mt6360_of_id); static struct i2c_driver mt6360_driver = { .driver = { .name = "mt6360", - .pm = &mt6360_pm_ops, + .pm = pm_sleep_ptr(&mt6360_pm_ops), .of_match_table = of_match_ptr(mt6360_of_id), }, .probe = mt6360_probe, diff --git a/drivers/mfd/rk8xx-i2c.c b/drivers/mfd/rk8xx-i2c.c index 2951b2911a37..161b25fbe146 100644 --- a/drivers/mfd/rk8xx-i2c.c +++ b/drivers/mfd/rk8xx-i2c.c @@ -251,7 +251,7 @@ static void rk8xx_i2c_shutdown(struct i2c_client *client) rk8xx_shutdown(&client->dev); } -static SIMPLE_DEV_PM_OPS(rk8xx_i2c_pm_ops, rk8xx_suspend, rk8xx_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(rk8xx_i2c_pm_ops, rk8xx_suspend, rk8xx_resume); static const struct of_device_id rk8xx_i2c_of_match[] = { { .compatible = "rockchip,rk801", .data = &rk801_data }, @@ -270,7 +270,7 @@ static struct i2c_driver rk8xx_i2c_driver = { .driver = { .name = "rk8xx-i2c", .of_match_table = rk8xx_i2c_of_match, - .pm = &rk8xx_i2c_pm_ops, + .pm = pm_sleep_ptr(&rk8xx_i2c_pm_ops), }, .probe = rk8xx_i2c_probe, .shutdown = rk8xx_i2c_shutdown, diff --git a/drivers/mfd/rn5t618.c b/drivers/mfd/rn5t618.c index 23ca00d2c624..8c68dd16bb9e 100644 --- a/drivers/mfd/rn5t618.c +++ b/drivers/mfd/rn5t618.c @@ -243,7 +243,7 @@ static void rn5t618_i2c_remove(struct i2c_client *i2c) unregister_restart_handler(&rn5t618_restart_handler); } -static int __maybe_unused rn5t618_i2c_suspend(struct device *dev) +static int rn5t618_i2c_suspend(struct device *dev) { struct rn5t618 *priv = dev_get_drvdata(dev); @@ -253,7 +253,7 @@ static int __maybe_unused rn5t618_i2c_suspend(struct device *dev) return 0; } -static int __maybe_unused rn5t618_i2c_resume(struct device *dev) +static int rn5t618_i2c_resume(struct device *dev) { struct rn5t618 *priv = dev_get_drvdata(dev); @@ -263,7 +263,7 @@ static int __maybe_unused rn5t618_i2c_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(rn5t618_i2c_dev_pm_ops, +static DEFINE_SIMPLE_DEV_PM_OPS(rn5t618_i2c_dev_pm_ops, rn5t618_i2c_suspend, rn5t618_i2c_resume); @@ -271,7 +271,7 @@ static struct i2c_driver rn5t618_i2c_driver = { .driver = { .name = "rn5t618", .of_match_table = rn5t618_of_match, - .pm = &rn5t618_i2c_dev_pm_ops, + .pm = pm_sleep_ptr(&rn5t618_i2c_dev_pm_ops), }, .probe = rn5t618_i2c_probe, .remove = rn5t618_i2c_remove, diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 068c25401c6c..81f07a1d780c 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -313,7 +313,7 @@ static int __maybe_unused ti_tscadc_can_wakeup(struct device *dev, void *data) return device_may_wakeup(dev); } -static int __maybe_unused tscadc_suspend(struct device *dev) +static int tscadc_suspend(struct device *dev) { struct ti_tscadc_dev *tscadc = dev_get_drvdata(dev); @@ -331,7 +331,7 @@ static int __maybe_unused tscadc_suspend(struct device *dev) return 0; } -static int __maybe_unused tscadc_resume(struct device *dev) +static int tscadc_resume(struct device *dev) { struct ti_tscadc_dev *tscadc = dev_get_drvdata(dev); @@ -345,7 +345,7 @@ static int __maybe_unused tscadc_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(tscadc_pm_ops, tscadc_suspend, tscadc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tscadc_pm_ops, tscadc_suspend, tscadc_resume); static const struct ti_tscadc_data tscdata = { .adc_feature_name = "TI-am335x-adc", @@ -373,7 +373,7 @@ MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids); static struct platform_driver ti_tscadc_driver = { .driver = { .name = "ti_am3359-tscadc", - .pm = &tscadc_pm_ops, + .pm = pm_sleep_ptr(&tscadc_pm_ops), .of_match_table = ti_tscadc_dt_ids, }, .probe = ti_tscadc_probe, diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 3cfd2f02b62f..600186b1e8cb 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -618,7 +618,7 @@ static void tps6586x_i2c_remove(struct i2c_client *client) free_irq(client->irq, tps6586x); } -static int __maybe_unused tps6586x_i2c_suspend(struct device *dev) +static int tps6586x_i2c_suspend(struct device *dev) { struct tps6586x *tps6586x = dev_get_drvdata(dev); @@ -628,7 +628,7 @@ static int __maybe_unused tps6586x_i2c_suspend(struct device *dev) return 0; } -static int __maybe_unused tps6586x_i2c_resume(struct device *dev) +static int tps6586x_i2c_resume(struct device *dev) { struct tps6586x *tps6586x = dev_get_drvdata(dev); @@ -638,7 +638,7 @@ static int __maybe_unused tps6586x_i2c_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(tps6586x_pm_ops, tps6586x_i2c_suspend, +static DEFINE_SIMPLE_DEV_PM_OPS(tps6586x_pm_ops, tps6586x_i2c_suspend, tps6586x_i2c_resume); static const struct i2c_device_id tps6586x_id_table[] = { @@ -651,7 +651,7 @@ static struct i2c_driver tps6586x_driver = { .driver = { .name = "tps6586x", .of_match_table = of_match_ptr(tps6586x_of_match), - .pm = &tps6586x_pm_ops, + .pm = pm_sleep_ptr(&tps6586x_pm_ops), }, .probe = tps6586x_i2c_probe, .remove = tps6586x_i2c_remove, diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index c024a28b057e..b7d749208131 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -903,7 +903,7 @@ twl_probe(struct i2c_client *client) return status; } -static int __maybe_unused twl_suspend(struct device *dev) +static int twl_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -913,7 +913,7 @@ static int __maybe_unused twl_suspend(struct device *dev) return 0; } -static int __maybe_unused twl_resume(struct device *dev) +static int twl_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -923,7 +923,7 @@ static int __maybe_unused twl_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(twl_dev_pm_ops, twl_suspend, twl_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(twl_dev_pm_ops, twl_suspend, twl_resume); static const struct i2c_device_id twl_ids[] = { { "twl4030", TWL4030_VAUX2 }, /* "Triton 2" */ @@ -942,7 +942,7 @@ static const struct i2c_device_id twl_ids[] = { /* One Client Driver , 4 Clients */ static struct i2c_driver twl_driver = { .driver.name = DRIVER_NAME, - .driver.pm = &twl_dev_pm_ops, + .driver.pm = pm_sleep_ptr(&twl_dev_pm_ops), .id_table = twl_ids, .probe = twl_probe, .remove = twl_remove, From 2758c011b5412a86dab88c592eed519fbea073c3 Mon Sep 17 00:00:00 2001 From: Eduard Bostina Date: Sat, 15 Aug 2026 08:31:45 +0000 Subject: [PATCH 25/74] dt-bindings: mfd: Convert TPS61050 to DT schema Convert the Texas Instruments TPS61050/TPS61052 boost converters bindings to DT schema. Signed-off-by: Eduard Bostina Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260815083145.2145984-1-egbostina@gmail.com Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/ti,tps61050.yaml | 92 +++++++++++++++++++ .../devicetree/bindings/mfd/tps6105x.txt | 62 ------------- 2 files changed, 92 insertions(+), 62 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/ti,tps61050.yaml delete mode 100644 Documentation/devicetree/bindings/mfd/tps6105x.txt diff --git a/Documentation/devicetree/bindings/mfd/ti,tps61050.yaml b/Documentation/devicetree/bindings/mfd/ti,tps61050.yaml new file mode 100644 index 000000000000..974ef78d6e26 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/ti,tps61050.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/ti,tps61050.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TPS61050/TPS61052 Boost Converter + +maintainers: + - Eduard Bostina + +description: + The TP61050/TPS61052 is a high-power "white LED driver". The device + provides LED, GPIO and regulator functionalities. + +properties: + compatible: + enum: + - ti,tps61050 + - ti,tps61052 + + reg: + maxItems: 1 + + regulator: + type: object + $ref: /schemas/regulator/regulator.yaml# + description: Puts the chip in regulator mode. + unevaluatedProperties: false + + led: + type: object + $ref: /schemas/leds/common.yaml# + description: Puts the chip in LED mode. + unevaluatedProperties: false + +required: + - compatible + - reg + +allOf: + - not: + required: + - regulator + - led + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + tps61052@33 { + compatible = "ti,tps61052"; + reg = <0x33>; + }; + }; + + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + tps61052@33 { + compatible = "ti,tps61052"; + reg = <0x33>; + + regulator { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + }; + }; + + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + tps61052@33 { + compatible = "ti,tps61052"; + reg = <0x33>; + + led { + color = ; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/tps6105x.txt b/Documentation/devicetree/bindings/mfd/tps6105x.txt deleted file mode 100644 index dc448a9d5b4d..000000000000 --- a/Documentation/devicetree/bindings/mfd/tps6105x.txt +++ /dev/null @@ -1,62 +0,0 @@ -* Device tree bindings for TI TPS61050/61052 Boost Converters - -The TP61050/TPS61052 is a high-power "white LED driver". The -device provides LED, GPIO and regulator functionalities. - -Required properties: -- compatible: "ti,tps61050" or "ti,tps61052" -- reg: Specifies the I2C slave address - -Optional sub-node: - -This subnode selects the chip's operational mode. -There can be at most one single available subnode. - -- regulator: presence of this sub-node puts the chip in regulator mode. - see ../regulator/regulator.yaml - -- led: presence of this sub-node puts the chip in led mode. - Optional properties: - - function : see ../leds/common.txt - - color : see ../leds/common.txt - - label : see ../leds/common.txt - (deprecated) - -Example (GPIO operation only): - -i2c0 { - tps61052@33 { - compatible = "ti,tps61052"; - reg = <0x33>; - }; -}; - -Example (GPIO + regulator operation): - -i2c0 { - tps61052@33 { - compatible = "ti,tps61052"; - reg = <0x33>; - - regulator { - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - regulator-always-on; - }; - }; -}; - -Example (GPIO + led operation): - -#include - -i2c0 { - tps61052@33 { - compatible = "ti,tps61052"; - reg = <0x33>; - - led { - color = ; - }; - }; -}; From 7c2c57ec8c5e31e9a4ddb0d5aaa4d0cebdb7fd80 Mon Sep 17 00:00:00 2001 From: Dhruvin Rajpura Date: Sun, 30 Aug 2026 11:47:19 +0800 Subject: [PATCH 26/74] dt-bindings: mfd: qcom,spmi-pmic: Document PMAU0102 Add compatible for Qualcomm PMAU0102 PMIC that is used on Nord platform. Signed-off-by: Dhruvin Rajpura Signed-off-by: Shawn Guo Reviewed-by: Bartosz Golaszewski Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260830034719.140213-1-shengchao.guo@oss.qualcomm.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml index 5e0ed30a9a46..f6534e9e9912 100644 --- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml @@ -74,6 +74,7 @@ properties: - qcom,pm8994 - qcom,pm8998 - qcom,pma8084 + - qcom,pmau0102 - qcom,pmc8180 - qcom,pmc8180c - qcom,pmc8380 From fdb122928afb50039af49ede49d213a01271f069 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 1 Sep 2026 16:14:17 +0100 Subject: [PATCH 27/74] mfd: cs42l43: Add support for new cs42l44 variant The cs42l44 is a cost optimised variant of cs42l43b. Add basic support for this new device. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260901151417.2546618-1-ckeepax@opensource.cirrus.com Signed-off-by: Lee Jones --- drivers/mfd/cs42l43-i2c.c | 2 ++ drivers/mfd/cs42l43-sdw.c | 1 + drivers/mfd/cs42l43.c | 18 ++++++++++++------ include/linux/mfd/cs42l43-regs.h | 1 + 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/mfd/cs42l43-i2c.c b/drivers/mfd/cs42l43-i2c.c index cbe05c3ea910..dcc167f78d8a 100644 --- a/drivers/mfd/cs42l43-i2c.c +++ b/drivers/mfd/cs42l43-i2c.c @@ -58,6 +58,7 @@ static int cs42l43_i2c_probe(struct i2c_client *i2c) static const struct of_device_id cs42l43_of_match[] = { { .compatible = "cirrus,cs42l43", .data = (void *)CS42L43_DEVID_VAL }, { .compatible = "cirrus,cs42l43b", .data = (void *)CS42L43B_DEVID_VAL }, + { .compatible = "cirrus,cs42l44", .data = (void *)CS42L43B_DEVID_VAL }, {} }; MODULE_DEVICE_TABLE(of, cs42l43_of_match); @@ -67,6 +68,7 @@ MODULE_DEVICE_TABLE(of, cs42l43_of_match); static const struct acpi_device_id cs42l43_acpi_match[] = { { "CSC4243", CS42L43_DEVID_VAL }, { "CSC2A3B", CS42L43B_DEVID_VAL }, + { "CSC4244", CS42L43B_DEVID_VAL }, {} }; MODULE_DEVICE_TABLE(acpi, cs42l43_acpi_match); diff --git a/drivers/mfd/cs42l43-sdw.c b/drivers/mfd/cs42l43-sdw.c index 0a6999453f32..2d4a192dcc01 100644 --- a/drivers/mfd/cs42l43-sdw.c +++ b/drivers/mfd/cs42l43-sdw.c @@ -183,6 +183,7 @@ static int cs42l43_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id * static const struct sdw_device_id cs42l43_sdw_id[] = { SDW_SLAVE_ENTRY(0x01FA, 0x4243, (void *)CS42L43_DEVID_VAL), SDW_SLAVE_ENTRY(0x01FA, 0x2A3B, (void *)CS42L43B_DEVID_VAL), + SDW_SLAVE_ENTRY(0x01FA, 0x4244, (void *)CS42L43B_DEVID_VAL), { } }; MODULE_DEVICE_TABLE(sdw, cs42l43_sdw_id); diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c index 4212ebcca60b..592677e1db41 100644 --- a/drivers/mfd/cs42l43.c +++ b/drivers/mfd/cs42l43.c @@ -959,6 +959,7 @@ static void cs42l43_boot_work(struct work_struct *work) { struct cs42l43 *cs42l43 = container_of(work, struct cs42l43, boot_work); unsigned int devid, revid, otp; + bool valid_id; int ret; ret = cs42l43_wait_for_attach(cs42l43); @@ -973,19 +974,24 @@ static void cs42l43_boot_work(struct work_struct *work) switch (devid) { case CS42L43_DEVID_VAL: + valid_id = (cs42l43->variant_id == CS42L43_DEVID_VAL); + break; case CS42L43B_DEVID_VAL: - if (devid != cs42l43->variant_id) { - dev_err(cs42l43->dev, - "Device ID (0x%06x) does not match variant ID (0x%06lx)\n", - devid, cs42l43->variant_id); - goto err; - } + case CS42L44_DEVID_VAL: + valid_id = (cs42l43->variant_id == CS42L43B_DEVID_VAL); break; default: dev_err(cs42l43->dev, "Unrecognised devid: 0x%06x\n", devid); goto err; } + if (!valid_id) { + dev_err(cs42l43->dev, + "Device ID (0x%06x) does not match variant ID (0x%06lx)\n", + devid, cs42l43->variant_id); + goto err; + } + ret = regmap_read(cs42l43->regmap, CS42L43_REVID, &revid); if (ret) { dev_err(cs42l43->dev, "Failed to read rev: %d\n", ret); diff --git a/include/linux/mfd/cs42l43-regs.h b/include/linux/mfd/cs42l43-regs.h index 68831f113589..4c00ceae8b46 100644 --- a/include/linux/mfd/cs42l43-regs.h +++ b/include/linux/mfd/cs42l43-regs.h @@ -1183,6 +1183,7 @@ /* CS42L43B VARIANT REGISTERS */ #define CS42L43B_DEVID_VAL 0x0042A43B +#define CS42L44_DEVID_VAL 0x00042A44 #define CS42L43B_DECIM_VOL_CTRL_CH1_CH2 0x00008280 #define CS42L43B_DECIM_VOL_CTRL_CH3_CH4 0x00008284 From b296be1dc008fe3a536341afae01f66bb302e67f Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Thu, 3 Sep 2026 16:42:05 +0200 Subject: [PATCH 28/74] mfd: wcd934x: set DMA mask on parent to silence "DMA mask not set" The wcd934x MFD parent (a slim_device) never initializes its dma_mask/coherent_dma_mask. When the DT-aware children pass through of_dma_configure() this triggers the "DMA mask not set" warning for each child. Cc: stable@vger.kernel.org Fixes: f959dcd6ddfd ("dma-direct: Fix potential NULL pointer dereference") Assisted-by: tencent:hy3 Signed-off-by: David Heidelberg Link: https://patch.msgid.link/20260903-wcd-dma-v2-1-189cff560a28@ixit.cz Signed-off-by: Lee Jones --- drivers/mfd/wcd934x.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mfd/wcd934x.c b/drivers/mfd/wcd934x.c index 3c3080e8c8cf..7bea64fc0956 100644 --- a/drivers/mfd/wcd934x.c +++ b/drivers/mfd/wcd934x.c @@ -2,6 +2,7 @@ // Copyright (c) 2019, Linaro Limited #include +#include #include #include #include @@ -217,6 +218,11 @@ static int wcd934x_slim_probe(struct slim_device *sdev) struct gpio_desc *reset_gpio; int ret; + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set DMA mask\n"); + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) return -ENOMEM; From dcf0e640b553d33512fbcaedb909c32ba9636a64 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Thu, 3 Sep 2026 17:21:42 +0200 Subject: [PATCH 29/74] mfd: wcd934x: Fix NULL pointer dereference on IRQ probe deferral ddata->dev is only assigned at the end of wcd934x_slim_probe(), but the of_irq_get() error path has passed it to the error print since the driver was added, while it is still NULL. This used to be harmless with dev_err(), which copes with a NULL device and was skipped on -EPROBE_DEFER. Since the switch to dev_err_probe(), a deferred IRQ reaches device_set_deferred_probe_reason() -> dev_driver_string(), which dereferences the NULL pointer and crashes during probe. Use dev, which is already valid at this point. Cc: stable@vger.kernel.org Fixes: 0f1b1b899521 ("mfd: wcd934x: Simplify with dev_err_probe()") Reported-by: Sashiko Link: https://lore.kernel.org/mfd/20260903145651.AC1311F00ACA@smtp.kernel.org/ Signed-off-by: David Heidelberg Link: https://patch.msgid.link/20260903-wcd-dma-v3-1-d3920c6563c4@ixit.cz Signed-off-by: Lee Jones --- drivers/mfd/wcd934x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/wcd934x.c b/drivers/mfd/wcd934x.c index 7bea64fc0956..d57796933d87 100644 --- a/drivers/mfd/wcd934x.c +++ b/drivers/mfd/wcd934x.c @@ -229,7 +229,7 @@ static int wcd934x_slim_probe(struct slim_device *sdev) ddata->irq = of_irq_get(np, 0); if (ddata->irq < 0) - return dev_err_probe(ddata->dev, ddata->irq, + return dev_err_probe(dev, ddata->irq, "Failed to get IRQ\n"); ddata->extclk = devm_clk_get(dev, "extclk"); From 5ae1cf7abd4c76414f9f44a900a6e7d0483a665b Mon Sep 17 00:00:00 2001 From: Hemanth Selam Date: Mon, 7 Sep 2026 10:25:26 +0530 Subject: [PATCH 30/74] mfd: ab8500: Fix typo "interupts" in comment Correct "interupts" to "interrupts", reported by scripts/checkpatch.pl using the misspelling list in scripts/spelling.txt. Only touches a comment, no code changes. Since v1 the subject carries the driver name and a capitalised description, to match the MFD subsystem convention. Assisted-by: Cursor:claude-opus-5 Reviewed-by: Linus Walleij Signed-off-by: Hemanth Selam Link: https://patch.msgid.link/20260907045526.18873-1-hemanth.selam@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/ab8500-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 86fa99022cb3..ceea1ae34dd8 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -125,7 +125,7 @@ static u8 turn_on_stat_set; /* * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt - * numbers are indexed into this array with (num / 8). The interupts are + * numbers are indexed into this array with (num / 8). The interrupts are * defined in linux/mfd/ab8500.h * * This is one off from the register names, i.e. AB8500_IT_MASK1_REG is at From 86bcfdc6f1e1f8943fbe12256b3c89a66ebbad03 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 4 Sep 2026 14:12:43 +0100 Subject: [PATCH 31/74] mfd: cs42l43: Move cancel_work_sync() into remove The cs42l43 driver kicks off a work item waiting for the device to appear on the SoundWire bus, once the device appears the MFD children are added and the clean up is then done through devres. As the work is synchronised in from the devres clean up this could lead to new devres items being added after the clean up has started, if the driver is removed before the work has fully completed. The work should be synchronised before any devres clean up is started. Fixes: 0f35dc4bd50d ("mfd: cs42l43: Use devres for remove as well") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260904131243.755201-1-ckeepax@opensource.cirrus.com Signed-off-by: Lee Jones --- drivers/mfd/cs42l43-i2c.c | 8 ++++++++ drivers/mfd/cs42l43-sdw.c | 8 ++++++++ drivers/mfd/cs42l43.c | 12 ++++++++---- drivers/mfd/cs42l43.h | 1 + 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/cs42l43-i2c.c b/drivers/mfd/cs42l43-i2c.c index dcc167f78d8a..2f1862199618 100644 --- a/drivers/mfd/cs42l43-i2c.c +++ b/drivers/mfd/cs42l43-i2c.c @@ -54,6 +54,13 @@ static int cs42l43_i2c_probe(struct i2c_client *i2c) return cs42l43_dev_probe(cs42l43); } +static void cs42l43_i2c_remove(struct i2c_client *i2c) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(&i2c->dev); + + cs42l43_dev_remove(cs42l43); +} + #if IS_ENABLED(CONFIG_OF) static const struct of_device_id cs42l43_of_match[] = { { .compatible = "cirrus,cs42l43", .data = (void *)CS42L43_DEVID_VAL }, @@ -83,6 +90,7 @@ static struct i2c_driver cs42l43_i2c_driver = { }, .probe = cs42l43_i2c_probe, + .remove = cs42l43_i2c_remove, }; module_i2c_driver(cs42l43_i2c_driver); diff --git a/drivers/mfd/cs42l43-sdw.c b/drivers/mfd/cs42l43-sdw.c index 2d4a192dcc01..22e4c1fe9298 100644 --- a/drivers/mfd/cs42l43-sdw.c +++ b/drivers/mfd/cs42l43-sdw.c @@ -180,6 +180,13 @@ static int cs42l43_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id * return cs42l43_dev_probe(cs42l43); } +static void cs42l43_sdw_remove(struct sdw_slave *sdw) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev); + + cs42l43_dev_remove(cs42l43); +} + static const struct sdw_device_id cs42l43_sdw_id[] = { SDW_SLAVE_ENTRY(0x01FA, 0x4243, (void *)CS42L43_DEVID_VAL), SDW_SLAVE_ENTRY(0x01FA, 0x2A3B, (void *)CS42L43B_DEVID_VAL), @@ -195,6 +202,7 @@ static struct sdw_driver cs42l43_sdw_driver = { }, .probe = cs42l43_sdw_probe, + .remove = cs42l43_sdw_remove, .id_table = cs42l43_sdw_id, .ops = &cs42l43_sdw_ops, }; diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c index 592677e1db41..a83b62064dae 100644 --- a/drivers/mfd/cs42l43.c +++ b/drivers/mfd/cs42l43.c @@ -1105,15 +1105,19 @@ static int cs42l43_power_down(struct cs42l43 *cs42l43) return 0; } -static void cs42l43_dev_remove(void *data) +static void cs42l43_dev_power_down(void *data) { struct cs42l43 *cs42l43 = data; - cancel_work_sync(&cs42l43->boot_work); - cs42l43_power_down(cs42l43); } +void cs42l43_dev_remove(struct cs42l43 *cs42l43) +{ + flush_work(&cs42l43->boot_work); +} +EXPORT_SYMBOL_NS_GPL(cs42l43_dev_remove, "MFD_CS42L43"); + int cs42l43_dev_probe(struct cs42l43 *cs42l43) { int i, ret; @@ -1159,7 +1163,7 @@ int cs42l43_dev_probe(struct cs42l43 *cs42l43) if (ret) return ret; - ret = devm_add_action_or_reset(cs42l43->dev, cs42l43_dev_remove, cs42l43); + ret = devm_add_action_or_reset(cs42l43->dev, cs42l43_dev_power_down, cs42l43); if (ret) return ret; diff --git a/drivers/mfd/cs42l43.h b/drivers/mfd/cs42l43.h index a0068f6572e2..862bc6fb68cb 100644 --- a/drivers/mfd/cs42l43.h +++ b/drivers/mfd/cs42l43.h @@ -25,5 +25,6 @@ bool cs42l43_precious_register(struct device *dev, unsigned int reg); bool cs42l43_volatile_register(struct device *dev, unsigned int reg); int cs42l43_dev_probe(struct cs42l43 *cs42l43); +void cs42l43_dev_remove(struct cs42l43 *cs42l43); #endif /* CS42L43_CORE_INT_H */ From 9b344554351ffefc4430c4ce150fb0aaa9c4d127 Mon Sep 17 00:00:00 2001 From: Eduard Bostina Date: Sat, 5 Sep 2026 21:52:34 +0000 Subject: [PATCH 32/74] dt-bindings: mfd: Convert TI TWL6040 to DT schema Convert the Texas Instruments TWL6040 audio codecs family bindings to DT schema. During the conversion, the following updates were made: - Changed 'twl6040,audpwron-gpio' to 'ti,audpwron-gpio', which was misnamed in the old txt binding. - Made 'gpio-controller', '#gpio-cells', and 'ti,audpwron-gpio' optional because modern platforms do not require them. - Renamed the vibra 'vddvibl_uV'/'vddvibr_uV' properties to 'ti,vddvibl-uV'/'ti,vddvibr-uV', the names actually read by the twl6040-vibra driver. - Required 'vddvibl-supply' and 'vddvibr-supply' when the 'vibra' child node is present, since the twl6040-vibra driver unconditionally requests both. Signed-off-by: Eduard Bostina Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260905215234.454266-1-egbostina@gmail.com Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/ti,twl6040.yaml | 156 ++++++++++++++++++ .../devicetree/bindings/mfd/twl6040.txt | 67 -------- 2 files changed, 156 insertions(+), 67 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/ti,twl6040.yaml delete mode 100644 Documentation/devicetree/bindings/mfd/twl6040.txt diff --git a/Documentation/devicetree/bindings/mfd/ti,twl6040.yaml b/Documentation/devicetree/bindings/mfd/ti,twl6040.yaml new file mode 100644 index 000000000000..5efcd79b527c --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/ti,twl6040.yaml @@ -0,0 +1,156 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/ti,twl6040.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TWL6040 family audio codec + +maintainers: + - Eduard Bostina + +description: + The TWL6040s are 8-channel high quality low-power audio codecs providing + audio, vibra and GPO functionality on OMAP4+ platforms. They are connected + to the host processor via i2c for commands, McPDM for audio data and + commands. + +properties: + compatible: + enum: + - ti,twl6040 + - ti,twl6041 + + reg: + const: 0x4b + + interrupts: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 1 + + "#clock-cells": + const: 0 + + ti,audpwron-gpio: + maxItems: 1 + description: Power on GPIO line for the twl6040 + + vio-supply: + description: Regulator for the twl6040 VIO supply + + v2v1-supply: + description: Regulator for the twl6040 V2V1 supply + + enable-active-high: + type: boolean + description: To power on the twl6040 during boot. + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + maxItems: 2 + items: + enum: + - clk32k + - mclk + + vddvibl-supply: + description: Regulator for the left vibra motor + + vddvibr-supply: + description: Regulator for the right vibra motor + + vibra: + type: object + additionalProperties: false + properties: + ti,vibldrv-res: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Resistance parameter for left driver + + ti,vibrdrv-res: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Resistance parameter for right driver + + ti,viblmotor-res: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Resistance parameter for left motor + + ti,vibrmotor-res: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Resistance parameter for right motor + + ti,vddvibl-uV: + $ref: /schemas/types.yaml#/definitions/uint32 + description: vddvibl default voltage if it needs to be changed + + ti,vddvibr-uV: + $ref: /schemas/types.yaml#/definitions/uint32 + description: vddvibr default voltage if it needs to be changed + + required: + - ti,vibldrv-res + - ti,vibrdrv-res + - ti,viblmotor-res + - ti,vibrmotor-res + +required: + - compatible + - reg + - interrupts + - "#clock-cells" + - vio-supply + - v2v1-supply + +allOf: + - if: + required: + - vibra + then: + required: + - vddvibl-supply + - vddvibr-supply + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + audio-codec@4b { + compatible = "ti,twl6040"; + reg = <0x4b>; + interrupts = <0 119 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&gic>; + gpio-controller; + #gpio-cells = <1>; + #clock-cells = <0>; + ti,audpwron-gpio = <&gpio4 31 0>; + + vio-supply = <&v1v8>; + v2v1-supply = <&v2v1>; + enable-active-high; + + /* regulators for vibra motor */ + vddvibl-supply = <&vbat>; + vddvibr-supply = <&vbat>; + + vibra { + ti,vibldrv-res = <8>; + ti,vibrdrv-res = <3>; + ti,viblmotor-res = <10>; + ti,vibrmotor-res = <10>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/twl6040.txt b/Documentation/devicetree/bindings/mfd/twl6040.txt deleted file mode 100644 index dfd8683ede0c..000000000000 --- a/Documentation/devicetree/bindings/mfd/twl6040.txt +++ /dev/null @@ -1,67 +0,0 @@ -Texas Instruments TWL6040 family - -The TWL6040s are 8-channel high quality low-power audio codecs providing audio, -vibra and GPO functionality on OMAP4+ platforms. -They are connected to the host processor via i2c for commands, McPDM for audio -data and commands. - -Required properties: -- compatible : "ti,twl6040" for twl6040, "ti,twl6041" for twl6041 -- reg: must be 0x4b for i2c address -- interrupts: twl6040 has one interrupt line connecteded to the main SoC -- gpio-controller: -- #gpio-cells = <1>: twl6040 provides GPO lines. -- #clock-cells = <0>; twl6040 is a provider of pdmclk which is used by McPDM -- twl6040,audpwron-gpio: Power on GPIO line for the twl6040 - -- vio-supply: Regulator for the twl6040 VIO supply -- v2v1-supply: Regulator for the twl6040 V2V1 supply - -Optional properties, nodes: -- enable-active-high: To power on the twl6040 during boot. -- clocks: phandle to the clk32k and/or to mclk clock provider -- clock-names: Must be "clk32k" for the 32K clock and "mclk" for the MCLK. - -Vibra functionality -Required properties: -- vddvibl-supply: Regulator for the left vibra motor -- vddvibr-supply: Regulator for the right vibra motor -- vibra { }: Configuration section for vibra parameters containing the following - properties: -- ti,vibldrv-res: Resistance parameter for left driver -- ti,vibrdrv-res: Resistance parameter for right driver -- ti,viblmotor-res: Resistance parameter for left motor -- ti,viblmotor-res: Resistance parameter for right motor - -Optional properties within vibra { } section: -- vddvibl_uV: If the vddvibl default voltage need to be changed -- vddvibr_uV: If the vddvibr default voltage need to be changed - -Example: -&i2c1 { - twl6040: twl@4b { - compatible = "ti,twl6040"; - - interrupts = <0 119 4>; - interrupt-parent = <&gic>; - twl6040,audpwron-gpio = <&gpio4 31 0>; - - vio-supply = <&v1v8>; - v2v1-supply = <&v2v1>; - enable-active-high; - - /* regulators for vibra motor */ - vddvibl-supply = <&vbat>; - vddvibr-supply = <&vbat>; - - vibra { - /* Vibra driver, motor resistance parameters */ - ti,vibldrv-res = <8>; - ti,vibrdrv-res = <3>; - ti,viblmotor-res = <10>; - ti,vibrmotor-res = <10>; - }; - }; -}; - -/include/ "twl6040.dtsi" From 128115b520c6b61afbffd01809cfc3e8cbb78516 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Tue, 8 Sep 2026 12:20:39 -0700 Subject: [PATCH 33/74] mfd: rk8xx: Register poweroff handler in atomic phase This fixes a regression from v6.1 to v6.6 where Google Veyron devices (rk3288) would not power off after being shut down. Prior to commit 4fec8a5a85c4 ("mfd: rk808: Convert to device managed resources"), pm_power_off was called by legacy_pm_power_off() which itself was registered using SYS_OFF_MODE_POWER_OFF. But in that commit the registration of rk808_power_off() moved to SYS_OFF_MODE_POWER_OFF_PREPARE. At the very least, the code should be consistent between SYS_OFF_MODE_POWER_OFF and SYS_OFF_MODE_RESTART; both should be *_PREPARE or both not. It appears interrupt delivery is already shut down (but not disabled) when SYS_OFF_MODE_POWER_OFF_PREPARE is triggered, so regmap_update_bits() in rk808_power_off() does not complete successfully. Registering rk808_power_off() as SYS_OFF_MODE_POWER_OFF runs with interrupts disabled which causes polling I/O to be used instead, and the device to power down. Fixes: 4fec8a5a85c4 ("mfd: rk808: Convert to device managed resources") Signed-off-by: Eric Anderson Assisted-by: Gemini:gemini-3.5-flash Link: https://patch.msgid.link/20260908-veyron-shutdown-v1-1-066ecb80f5f2@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/rk8xx-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/rk8xx-core.c b/drivers/mfd/rk8xx-core.c index 3dcf6abfda74..21182df45d1b 100644 --- a/drivers/mfd/rk8xx-core.c +++ b/drivers/mfd/rk8xx-core.c @@ -875,7 +875,7 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap if (device_property_read_bool(dev, "system-power-controller") || device_property_read_bool(dev, "rockchip,system-power-controller")) { ret = devm_register_sys_off_handler(dev, - SYS_OFF_MODE_POWER_OFF_PREPARE, SYS_OFF_PRIO_HIGH, + SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_HIGH, &rk808_power_off, rk808); if (ret) return dev_err_probe(dev, ret, From 14a7cf1a3730c7c2393ef5a226e90baf61aa8a9f Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sat, 12 Sep 2026 15:12:23 -0400 Subject: [PATCH 34/74] mfd: intel_quark_i2c_gpio: Manage the fixed-rate clock The I2C clock is allocated by clk_register_fixed_rate(), but cleanup uses clk_unregister(), leaving the fixed-rate provider allocation behind. Manage the provider with devm_clk_hw_register_fixed_rate() and its lookup with devm_clk_hw_register_clkdev(). Managed resources release the lookup before the provider, after the MFD children have been removed. Remove the manual clock cleanup and the private structure that only stored the clock and lookup pointers. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 60ae5b9f5cdd ("mfd: intel_quark_i2c_gpio: Add Intel Quark X1000 I2C-GPIO MFD Driver") Assisted-by: OpenAI:GPT-5.6 Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260912191223.46136-1-mhun512@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/intel_quark_i2c_gpio.c | 52 ++++-------------------------- 1 file changed, 6 insertions(+), 46 deletions(-) diff --git a/drivers/mfd/intel_quark_i2c_gpio.c b/drivers/mfd/intel_quark_i2c_gpio.c index 9b9c76bd067b..443a1a0cf777 100644 --- a/drivers/mfd/intel_quark_i2c_gpio.c +++ b/drivers/mfd/intel_quark_i2c_gpio.c @@ -35,11 +35,6 @@ /* The Quark I2C controller source clock */ #define INTEL_QUARK_I2C_CLK_HZ 33000000 -struct intel_quark_mfd { - struct clk *i2c_clk; - struct clk_lookup *i2c_clk_lookup; -}; - static const struct property_entry intel_quark_i2c_controller_standard_properties[] = { PROPERTY_ENTRY_U32("clock-frequency", I2C_MAX_STANDARD_MODE_FREQ), { } @@ -160,37 +155,12 @@ MODULE_DEVICE_TABLE(pci, intel_quark_mfd_ids); static int intel_quark_register_i2c_clk(struct device *dev) { - struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev); - struct clk *i2c_clk; + struct clk_hw *hw; - i2c_clk = clk_register_fixed_rate(dev, - INTEL_QUARK_I2C_CONTROLLER_CLK, NULL, - 0, INTEL_QUARK_I2C_CLK_HZ); - if (IS_ERR(i2c_clk)) - return PTR_ERR(i2c_clk); - - quark_mfd->i2c_clk = i2c_clk; - quark_mfd->i2c_clk_lookup = clkdev_create(i2c_clk, NULL, - INTEL_QUARK_I2C_CONTROLLER_CLK); - - if (!quark_mfd->i2c_clk_lookup) { - clk_unregister(quark_mfd->i2c_clk); - dev_err(dev, "Fixed clk register failed\n"); - return -ENOMEM; - } - - return 0; -} - -static void intel_quark_unregister_i2c_clk(struct device *dev) -{ - struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev); - - if (!quark_mfd->i2c_clk_lookup) - return; - - clkdev_drop(quark_mfd->i2c_clk_lookup); - clk_unregister(quark_mfd->i2c_clk); + hw = devm_clk_hw_register_fixed_rate(dev, INTEL_QUARK_I2C_CONTROLLER_CLK, + NULL, 0, INTEL_QUARK_I2C_CLK_HZ); + return devm_clk_hw_register_clkdev(dev, hw, NULL, + INTEL_QUARK_I2C_CONTROLLER_CLK); } static int intel_quark_i2c_setup(struct pci_dev *pdev) @@ -238,19 +208,12 @@ static int intel_quark_gpio_setup(struct pci_dev *pdev) static int intel_quark_mfd_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct intel_quark_mfd *quark_mfd; int ret; ret = pcim_enable_device(pdev); if (ret) return ret; - quark_mfd = devm_kzalloc(&pdev->dev, sizeof(*quark_mfd), GFP_KERNEL); - if (!quark_mfd) - return -ENOMEM; - - dev_set_drvdata(&pdev->dev, quark_mfd); - ret = intel_quark_register_i2c_clk(&pdev->dev); if (ret) return ret; @@ -260,7 +223,7 @@ static int intel_quark_mfd_probe(struct pci_dev *pdev, /* This driver only requires 1 IRQ vector */ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); if (ret < 0) - goto err_unregister_i2c_clk; + return ret; ret = intel_quark_i2c_setup(pdev); if (ret) @@ -282,8 +245,6 @@ static int intel_quark_mfd_probe(struct pci_dev *pdev, software_node_unregister_node_group(intel_quark_gpio_node_group); err_free_irq_vectors: pci_free_irq_vectors(pdev); -err_unregister_i2c_clk: - intel_quark_unregister_i2c_clk(&pdev->dev); return ret; } @@ -292,7 +253,6 @@ static void intel_quark_mfd_remove(struct pci_dev *pdev) mfd_remove_devices(&pdev->dev); software_node_unregister_node_group(intel_quark_gpio_node_group); pci_free_irq_vectors(pdev); - intel_quark_unregister_i2c_clk(&pdev->dev); } static struct pci_driver intel_quark_mfd_driver = { From 3c5c55d979ddcb0765ce531bc7ba173c87c8712a Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sun, 13 Sep 2026 16:50:12 -0400 Subject: [PATCH 35/74] mfd: twl-core: Unregister the auxiliary platform device on unbind twl_probe() registers a standalone platform device named "twl", but keeps its pointer only in a local variable. Probe errors unregister the device, while successful probe leaves no way for twl_remove() to release it. The platform device therefore remains registered after the I2C driver unbinds. Register a managed action on the I2C device immediately after adding the platform device. This unregisters it on probe failure and driver unbind, and also handles failure to register the action itself. Remove the manual error-path unregister to avoid releasing the device twice. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: defa6be1c821 ("mfd: Fix compile for twl-core.c by removing cpu_is_omap usage") Cc: stable@vger.kernel.org Assisted-by: OpenAI:GPT-5.6 Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260913205013.54777-1-mhun512@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/twl-core.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index b7d749208131..7fbce76efd30 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -719,6 +719,11 @@ static const struct mfd_cell twl6032_cells[] = { { .name = "twl6032-clk" }, }; +static void twl_unregister_device(void *data) +{ + platform_device_unregister(data); +} + /* NOTE: This driver only handles a single twl4030/tps659x0 chip */ static int twl_probe(struct i2c_client *client) @@ -754,17 +759,22 @@ twl_probe(struct i2c_client *client) return status; } + status = devm_add_action_or_reset(&client->dev, twl_unregister_device, + pdev); + if (status) + return status; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_dbg(&client->dev, "can't talk I2C?\n"); status = -EIO; - goto free; + goto out; } twl_priv = devm_kzalloc(&client->dev, sizeof(struct twl_private), GFP_KERNEL); if (!twl_priv) { status = -ENOMEM; - goto free; + goto out; } if ((id->driver_data) & TWL6030_CLASS) { @@ -784,7 +794,7 @@ twl_probe(struct i2c_client *client) GFP_KERNEL); if (!twl_priv->twl_modules) { status = -ENOMEM; - goto free; + goto out; } for (i = 0; i < num_slaves; i++) { @@ -880,7 +890,7 @@ twl_probe(struct i2c_client *client) status = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, cells, num_cells, NULL, 0, NULL); if (status < 0) - goto free; + goto out; if (of_device_is_system_power_controller(node)) { if (!pm_power_off) @@ -896,10 +906,7 @@ twl_probe(struct i2c_client *client) fail: if (status < 0) twl_remove(client); -free: - if (status < 0) - platform_device_unregister(pdev); - +out: return status; } From b761214ac8d48f20be2e76d415762bce05474f55 Mon Sep 17 00:00:00 2001 From: Maria Lisina Date: Wed, 16 Sep 2026 21:23:47 +0500 Subject: [PATCH 36/74] mfd: intel-lpss: Remove redundant DebugFS error check The DebugFS API is designed to handle errors gracefully. Any explicit checking on return values is considered an anti-pattern. This patch removes unnecessary error checking and converts intel_lpss_debugfs_add() into a void function. Signed-off-by: Maria Lisina Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260916-intel-lpss-debugfs-v7-1-eb51d41f55e0@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index 63d6694f7145..b6958e99cf54 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -142,13 +142,11 @@ static void intel_lpss_cache_ltr(struct intel_lpss *lpss) lpss->idle_ltr = readl(lpss->priv + LPSS_PRIV_IDLELTR); } -static int intel_lpss_debugfs_add(struct intel_lpss *lpss) +static void intel_lpss_debugfs_add(struct intel_lpss *lpss) { struct dentry *dir; dir = debugfs_create_dir(dev_name(lpss->dev), intel_lpss_debugfs); - if (IS_ERR(dir)) - return PTR_ERR(dir); /* Cache the values into lpss structure */ intel_lpss_cache_ltr(lpss); @@ -158,7 +156,6 @@ static int intel_lpss_debugfs_add(struct intel_lpss *lpss) debugfs_create_x32("idle_ltr", S_IRUGO, dir, &lpss->idle_ltr); lpss->debugfs = dir; - return 0; } static void intel_lpss_debugfs_remove(struct intel_lpss *lpss) @@ -432,10 +429,7 @@ int intel_lpss_probe(struct device *dev, goto err_clk_register; intel_lpss_ltr_expose(lpss); - - ret = intel_lpss_debugfs_add(lpss); - if (ret) - dev_warn(dev, "Failed to create debugfs entries\n"); + intel_lpss_debugfs_add(lpss); if (intel_lpss_has_idma(lpss)) { ret = mfd_add_devices(dev, lpss->devid, &intel_lpss_idma64_cell, From a5712fe170af6b5eab3e148c1bd794d539c90150 Mon Sep 17 00:00:00 2001 From: Maria Lisina Date: Wed, 16 Sep 2026 21:23:48 +0500 Subject: [PATCH 37/74] mfd: intel-lpss: Use plain octal values for file permissions S_IRUGO macro is obsoleted and not recommended to use, therefore replace it with plain octal values. Signed-off-by: Maria Lisina Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260916-intel-lpss-debugfs-v7-2-eb51d41f55e0@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index b6958e99cf54..190de88d146a 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -151,9 +151,9 @@ static void intel_lpss_debugfs_add(struct intel_lpss *lpss) /* Cache the values into lpss structure */ intel_lpss_cache_ltr(lpss); - debugfs_create_x32("capabilities", S_IRUGO, dir, &lpss->caps); - debugfs_create_x32("active_ltr", S_IRUGO, dir, &lpss->active_ltr); - debugfs_create_x32("idle_ltr", S_IRUGO, dir, &lpss->idle_ltr); + debugfs_create_x32("capabilities", 0444, dir, &lpss->caps); + debugfs_create_x32("active_ltr", 0444, dir, &lpss->active_ltr); + debugfs_create_x32("idle_ltr", 0444, dir, &lpss->idle_ltr); lpss->debugfs = dir; } From 7cd5da70ff2b555f42cb938bcb0f4e8d081908f7 Mon Sep 17 00:00:00 2001 From: Maria Lisina Date: Wed, 16 Sep 2026 21:23:49 +0500 Subject: [PATCH 38/74] mfd: intel-lpss: Remove extra DebugFS dentry There is no point to keep the variable dir just to assing lpss->debugfs to it later, just use lpss->debugfs directly. Signed-off-by: Maria Lisina Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260916-intel-lpss-debugfs-v7-3-eb51d41f55e0@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index 190de88d146a..170cc004a944 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -144,18 +144,14 @@ static void intel_lpss_cache_ltr(struct intel_lpss *lpss) static void intel_lpss_debugfs_add(struct intel_lpss *lpss) { - struct dentry *dir; - - dir = debugfs_create_dir(dev_name(lpss->dev), intel_lpss_debugfs); + lpss->debugfs = debugfs_create_dir(dev_name(lpss->dev), intel_lpss_debugfs); /* Cache the values into lpss structure */ intel_lpss_cache_ltr(lpss); - debugfs_create_x32("capabilities", 0444, dir, &lpss->caps); - debugfs_create_x32("active_ltr", 0444, dir, &lpss->active_ltr); - debugfs_create_x32("idle_ltr", 0444, dir, &lpss->idle_ltr); - - lpss->debugfs = dir; + debugfs_create_x32("capabilities", 0444, lpss->debugfs, &lpss->caps); + debugfs_create_x32("active_ltr", 0444, lpss->debugfs, &lpss->active_ltr); + debugfs_create_x32("idle_ltr", 0444, lpss->debugfs, &lpss->idle_ltr); } static void intel_lpss_debugfs_remove(struct intel_lpss *lpss) From 61b7037e6353de9f243a8d6baa1094250d3e6380 Mon Sep 17 00:00:00 2001 From: Maria Lisina Date: Wed, 16 Sep 2026 21:23:50 +0500 Subject: [PATCH 39/74] mfd: intel-lpss: Switch to debugfs_remove() debugfs_remove_recursive() is deprecated and has become an alias to debugfs_remove(), replace it with the direct debugfs_remove() call instead. Signed-off-by: Maria Lisina Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260916-intel-lpss-debugfs-v7-4-eb51d41f55e0@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index 170cc004a944..29f30ef0da99 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -156,7 +156,7 @@ static void intel_lpss_debugfs_add(struct intel_lpss *lpss) static void intel_lpss_debugfs_remove(struct intel_lpss *lpss) { - debugfs_remove_recursive(lpss->debugfs); + debugfs_remove(lpss->debugfs); } static void intel_lpss_ltr_set(struct device *dev, s32 val) From 0f2121ff242d8fb5f20064d15c7d777f7cd229dd Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Tue, 1 Sep 2026 05:08:13 +0530 Subject: [PATCH 40/74] dt-bindings: mfd: ti,keystone-devctrl: Convert to DT schema Convert the Texas Instruments Keystone Device State Control binding from legacy text format to DT schema. During the conversion, the following updates were made: - Add "simple-mfd" to compatibles matching existing dts usage. - Add required bus properties #address-cells, #size-cells, and ranges which were missing in text binding. - Document child nodes gpio, interrupt-controller, and reset-controller. Signed-off-by: Bhargav Joshi Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260901-ti-keystone-devctrl-v4-2-2922f2a44792@gmail.com Signed-off-by: Lee Jones --- .../bindings/mfd/ti,keystone-devctrl.yaml | 90 +++++++++++++++++++ .../bindings/mfd/ti-keystone-devctrl.txt | 19 ---- 2 files changed, 90 insertions(+), 19 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/ti,keystone-devctrl.yaml delete mode 100644 Documentation/devicetree/bindings/mfd/ti-keystone-devctrl.txt diff --git a/Documentation/devicetree/bindings/mfd/ti,keystone-devctrl.yaml b/Documentation/devicetree/bindings/mfd/ti,keystone-devctrl.yaml new file mode 100644 index 000000000000..564dfc8a731f --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/ti,keystone-devctrl.yaml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/ti,keystone-devctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments Keystone Device State Control + +maintainers: + - Ivan Khoronzhuk + +description: + The Keystone II devices have a set of registers that are used to control the + status of its peripherals. This node is intended to allow access to this + functionality. + +properties: + compatible: + items: + - const: ti,keystone-devctrl + - const: syscon + - const: simple-mfd + + reg: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + +patternProperties: + "^gpio@[0-9a-f]+$": + type: object + description: Keystone DSP GPIO controller + required: + - compatible + + "^interrupt-controller@[0-9a-f]+$": + type: object + description: Keystone DSP to ARM interrupt controller + required: + - compatible + + "^reset-controller@[0-9a-f]+$": + type: object + description: Keystone reset controller + required: + - compatible + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + #include + + device-state-control@2620000 { + compatible = "ti,keystone-devctrl", "syscon", "simple-mfd"; + reg = <0x02620000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x02620000 0x1000>; + + interrupt-controller@2a0 { + compatible = "ti,keystone-irq"; + reg = <0x2a0 0x4>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + ti,syscon-dev = <&devctrl 0x2a0>; + }; + + reset-controller@328 { + compatible = "ti,keystone-reset"; + reg = <0x328 0x10>; + ti,syscon-pll = <&pllctrl 0xe4>; + ti,syscon-dev = <&devctrl 0x328>; + ti,wdt-list = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/ti-keystone-devctrl.txt b/Documentation/devicetree/bindings/mfd/ti-keystone-devctrl.txt deleted file mode 100644 index 71a1f5963936..000000000000 --- a/Documentation/devicetree/bindings/mfd/ti-keystone-devctrl.txt +++ /dev/null @@ -1,19 +0,0 @@ -* Device tree bindings for Texas Instruments keystone device state control - -The Keystone II devices have a set of registers that are used to control -the status of its peripherals. This node is intended to allow access to -this functionality. - -Required properties: - -- compatible: "ti,keystone-devctrl", "syscon" - -- reg: contains offset/length value for device state control - registers space. - -Example: - -devctrl: device-state-control@02620000 { - compatible = "ti,keystone-devctrl", "syscon"; - reg = <0x02620000 0x1000>; -}; From a26c8e81f32979934a975f4e2278777ec9146f6a Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Sat, 19 Sep 2026 02:31:22 +0530 Subject: [PATCH 41/74] dt-bindings: mfd: ti,tps65910: Add wakeup-source and GPIO hogs Device tree validation throws warnings for the TPS65911 PMIC on Tegra boards because wakeup-source and GPIO hog nodes are not documented in the schema, even though the hardware supports them. Add wakeup-source property and allow child nodes that match the "-hog" pattern. Signed-off-by: Bhargav Joshi Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260919-ti-tps6586x-v3-1-63dd240f4684@gmail.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/ti,tps65910.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/ti,tps65910.yaml b/Documentation/devicetree/bindings/mfd/ti,tps65910.yaml index f1a76f88fc0c..caff9dc0b2bb 100644 --- a/Documentation/devicetree/bindings/mfd/ti,tps65910.yaml +++ b/Documentation/devicetree/bindings/mfd/ti,tps65910.yaml @@ -37,6 +37,8 @@ properties: interrupt-controller: true + wakeup-source: true + '#interrupt-cells': description: Specifies the IRQ number and flags const: 2 @@ -163,6 +165,12 @@ patternProperties: vcc7-supply: VRTC input. vccio-supply: VIO input. + "^.+-hog$": + type: object + + required: + - gpio-hog + required: - compatible - reg From 811997a90761f6906446b1f48080e75732b67339 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 21 Jul 2026 12:56:49 +0300 Subject: [PATCH 42/74] dt-bindings: input: cpcap-pwrbutton: Convert to DT schema Convert power button devicetree bindings for the Motorola CPCAP MFD from TXT to YAML format. This patch does not change any functionality; the bindings remain the same. Signed-off-by: Svyatoslav Ryhel Acked-by: Dmitry Torokhov Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260721095654.429346-3-clamor95@gmail.com Signed-off-by: Lee Jones --- .../bindings/input/cpcap-pwrbutton.txt | 20 ------------ .../input/motorola,cpcap-pwrbutton.yaml | 32 +++++++++++++++++++ 2 files changed, 32 insertions(+), 20 deletions(-) delete mode 100644 Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt create mode 100644 Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml diff --git a/Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt b/Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt deleted file mode 100644 index 0dd0076daf71..000000000000 --- a/Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt +++ /dev/null @@ -1,20 +0,0 @@ -Motorola CPCAP on key - -This module is part of the CPCAP. For more details about the whole -chip see Documentation/devicetree/bindings/mfd/motorola-cpcap.txt. - -This module provides a simple power button event via an Interrupt. - -Required properties: -- compatible: should be one of the following - - "motorola,cpcap-pwrbutton" -- interrupts: irq specifier for CPCAP's ON IRQ - -Example: - -&cpcap { - cpcap_pwrbutton: pwrbutton { - compatible = "motorola,cpcap-pwrbutton"; - interrupts = <23 IRQ_TYPE_NONE>; - }; -}; diff --git a/Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml b/Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml new file mode 100644 index 000000000000..77a3e5a47d1a --- /dev/null +++ b/Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/motorola,cpcap-pwrbutton.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Motorola CPCAP PMIC power key + +maintainers: + - Svyatoslav Ryhel + +description: + This module is part of the Motorola CPCAP MFD device. For more details + see Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml. The + power key is represented as a sub-node of the PMIC node on the device + tree. + +properties: + compatible: + const: motorola,cpcap-pwrbutton + + interrupts: + items: + - description: CPCAP's ON interrupt + +required: + - compatible + - interrupts + +additionalProperties: false + +... From ed7977e8eacb7efffda85be34af62631d0f03939 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 21 Jul 2026 12:56:50 +0300 Subject: [PATCH 43/74] dt-bindings: mfd: motorola-cpcap: Convert to DT schema Convert devicetree bindings for the Motorola CPCAP MFD from TXT to YAML. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260721095654.429346-4-clamor95@gmail.com Signed-off-by: Lee Jones --- .../bindings/mfd/motorola,cpcap.yaml | 403 ++++++++++++++++++ .../bindings/mfd/motorola-cpcap.txt | 78 ---- 2 files changed, 403 insertions(+), 78 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml delete mode 100644 Documentation/devicetree/bindings/mfd/motorola-cpcap.txt diff --git a/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml b/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml new file mode 100644 index 000000000000..76705ea56805 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml @@ -0,0 +1,403 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/motorola,cpcap.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Motorola CPCAP PMIC MFD + +maintainers: + - Svyatoslav Ryhel + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + items: + - const: motorola,cpcap + - const: st,6556002 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + spi-max-frequency: + maximum: 9600000 + + spi-cs-high: true + spi-cpol: true + spi-cpha: true + + adc: + $ref: /schemas/iio/adc/motorola,cpcap-adc.yaml# + + audio-codec: + type: object + additionalProperties: false + + properties: + interrupts: + items: + - description: headset detect interrupt + - description: microphone bias 2 detect interrupt + + interrupt-names: + items: + - const: hs + - const: mb2 + + "#sound-dai-cells": + const: 1 + + VAUDIO-supply: + description: + Codec power supply, usually VAUDIO regulator of CPCAP. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: port connected to the Stereo HiFi DAC + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: port connected to the Voice DAC + + required: + - port@0 + - port@1 + + required: + - interrupts + - interrupt-names + - "#sound-dai-cells" + + battery: + $ref: /schemas/power/supply/cpcap-battery.yaml# + + charger: + $ref: /schemas/power/supply/cpcap-charger.yaml# + + key-power: + $ref: /schemas/input/motorola,cpcap-pwrbutton.yaml# + + phy: + $ref: /schemas/phy/motorola,cpcap-usb-phy.yaml# + + regulator: + $ref: /schemas/regulator/motorola,cpcap-regulator.yaml# + + rtc: + $ref: /schemas/rtc/motorola,cpcap-rtc.yaml# + +patternProperties: + "^led(-[a-z]+)?$": + $ref: /schemas/leds/motorola,cpcap-leds.yaml# + +required: + - compatible + - reg + - interrupts + - interrupt-controller + - "#interrupt-cells" + - spi-max-frequency + +additionalProperties: false + +examples: + - | + #include + #include + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + cpcap: pmic@0 { + compatible = "motorola,cpcap", "st,6556002"; + reg = <0>; /* cs0 */ + + interrupt-parent = <&gpio1>; + interrupts = <7 IRQ_TYPE_EDGE_RISING>; + + interrupt-controller; + #interrupt-cells = <2>; + + spi-max-frequency = <3000000>; + spi-cs-high; + + spi-cpol; + spi-cpha; + + cpcap_adc: adc { + compatible = "motorola,cpcap-adc"; + + interrupt-parent = <&cpcap>; + interrupts = <8 IRQ_TYPE_NONE>; + interrupt-names = "adcdone"; + + #io-channel-cells = <1>; + }; + + cpcap_audio: audio-codec { + interrupt-parent = <&cpcap>; + interrupts = <9 IRQ_TYPE_NONE>, <10 IRQ_TYPE_NONE>; + interrupt-names = "hs", "mb2"; + + VAUDIO-supply = <&vdd_audio>; + + #sound-dai-cells = <1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + /* HiFi */ + port@0 { + reg = <0>; + + cpcap_audio_codec0: endpoint { + }; + }; + + /* Voice */ + port@1 { + reg = <1>; + + cpcap_audio_codec1: endpoint { + }; + }; + }; + }; + + cpcap_battery: battery { + compatible = "motorola,cpcap-battery"; + + interrupt-parent = <&cpcap>; + interrupts = <6 IRQ_TYPE_NONE>, <5 IRQ_TYPE_NONE>, + <3 IRQ_TYPE_NONE>, <20 IRQ_TYPE_NONE>, + <54 IRQ_TYPE_NONE>, <57 IRQ_TYPE_NONE>; + interrupt-names = "eol", "lowbph", "lowbpl", + "chrgcurr1", "battdetb", "cccal"; + + io-channels = <&cpcap_adc 0>, <&cpcap_adc 1>, + <&cpcap_adc 5>, <&cpcap_adc 6>; + io-channel-names = "battdetb", "battp", + "chg_isense", "batti"; + power-supplies = <&cpcap_charger>; + }; + + cpcap_charger: charger { + compatible = "motorola,mapphone-cpcap-charger"; + + interrupt-parent = <&cpcap>; + interrupts = <13 IRQ_TYPE_NONE>, <12 IRQ_TYPE_NONE>, + <29 IRQ_TYPE_NONE>, <28 IRQ_TYPE_NONE>, + <22 IRQ_TYPE_NONE>, <21 IRQ_TYPE_NONE>, + <20 IRQ_TYPE_NONE>, <19 IRQ_TYPE_NONE>, + <54 IRQ_TYPE_NONE>; + interrupt-names = "chrg_det", "rvrs_chrg", "chrg_se1b", + "se0conn", "rvrs_mode", "chrgcurr2", + "chrgcurr1", "vbusvld", "battdetb"; + + mode-gpios = <&gpio3 29 GPIO_ACTIVE_LOW>, + <&gpio3 23 GPIO_ACTIVE_LOW>; + + io-channels = <&cpcap_adc 0>, <&cpcap_adc 1>, + <&cpcap_adc 2>, <&cpcap_adc 5>, + <&cpcap_adc 6>; + io-channel-names = "battdetb", "battp", + "vbus", "chg_isense", + "batti"; + }; + + key-power { + compatible = "motorola,cpcap-pwrbutton"; + + interrupt-parent = <&cpcap>; + interrupts = <23 IRQ_TYPE_NONE>; + }; + + led-red { + compatible = "motorola,cpcap-led-red"; + vdd-supply = <&vdd_led>; + label = "status-led::red"; + }; + + led-green { + compatible = "motorola,cpcap-led-green"; + vdd-supply = <&vdd_led>; + label = "status-led::green"; + }; + + led-blue { + compatible = "motorola,cpcap-led-blue"; + vdd-supply = <&vdd_led>; + label = "status-led::blue"; + }; + + cpcap_usb2_phy: phy { + compatible = "motorola,cpcap-usb-phy"; + + pinctrl-0 = <&usb_gpio_mux_sel1>, <&usb_gpio_mux_sel2>; + pinctrl-1 = <&usb_ulpi_pins>; + pinctrl-2 = <&usb_utmi_pins>; + pinctrl-3 = <&uart3_pins>; + pinctrl-names = "default", "ulpi", "utmi", "uart"; + #phy-cells = <0>; + + interrupts-extended = + <&cpcap 15 IRQ_TYPE_NONE>, <&cpcap 14 IRQ_TYPE_NONE>, + <&cpcap 28 IRQ_TYPE_NONE>, <&cpcap 19 IRQ_TYPE_NONE>, + <&cpcap 18 IRQ_TYPE_NONE>, <&cpcap 17 IRQ_TYPE_NONE>, + <&cpcap 16 IRQ_TYPE_NONE>, <&cpcap 49 IRQ_TYPE_NONE>, + <&cpcap 48 IRQ_TYPE_NONE>; + interrupt-names = "id_ground", "id_float", "se0conn", + "vbusvld", "sessvld", "sessend", + "se1", "dm", "dp"; + + mode-gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>, + <&gpio1 0 GPIO_ACTIVE_HIGH>; + + io-channels = <&cpcap_adc 2>, <&cpcap_adc 7>; + io-channel-names = "vbus", "id"; + + vusb-supply = <&avdd_usb>; + }; + + regulator { + compatible = "motorola,cpcap-regulator"; + + regulators { + vdd_cpu: SW1 { + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <1125000>; + regulator-enable-ramp-delay = <1500>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_core: SW2 { + regulator-name = "vdd_core"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1300000>; + regulator-enable-ramp-delay = <1500>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_1v8_vio: SW3 { + regulator-name = "vdd_1v8_vio"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-enable-ramp-delay = <0>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_aon: SW4 { + regulator-name = "vdd_aon"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1300000>; + regulator-enable-ramp-delay = <1500>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_led: SW5 { + regulator-name = "vdd_led"; + regulator-min-microvolt = <5050000>; + regulator-max-microvolt = <5050000>; + regulator-enable-ramp-delay = <1500>; + regulator-boot-on; + }; + + vdd_hvio: VHVIO { + regulator-name = "vdd_hvio"; + regulator-min-microvolt = <2775000>; + regulator-max-microvolt = <2775000>; + regulator-enable-ramp-delay = <1000>; + }; + + vcore_emmc: VSDIO { + regulator-name = "vcore_emmc"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + regulator-enable-ramp-delay = <1000>; + regulator-always-on; + regulator-boot-on; + }; + + avdd_dsi_csi: VCSI { + regulator-name = "avdd_dsi_csi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-enable-ramp-delay = <1000>; + regulator-boot-on; + }; + + avdd_3v3_periph: VWLAN2 { + regulator-name = "avdd_3v3_periph"; + regulator-min-microvolt = <2775000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <1000>; + regulator-boot-on; + }; + + vddio_usd: VSIMCARD { + regulator-name = "vddio_usd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2900000>; + regulator-enable-ramp-delay = <1000>; + regulator-boot-on; + }; + + vdd_haptic: VVIB { + regulator-name = "vdd_haptic"; + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <3000000>; + regulator-enable-ramp-delay = <1000>; + }; + + avdd_usb: VUSB { + regulator-name = "avdd_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <1000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_audio: VAUDIO { + regulator-name = "vdd_audio"; + regulator-min-microvolt = <2775000>; + regulator-max-microvolt = <2775000>; + regulator-enable-ramp-delay = <1000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; + + cpcap_rtc: rtc { + compatible = "motorola,cpcap-rtc"; + + interrupt-parent = <&cpcap>; + interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt b/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt deleted file mode 100644 index 18c3fc26ca93..000000000000 --- a/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt +++ /dev/null @@ -1,78 +0,0 @@ -Motorola CPCAP PMIC device tree binding - -Required properties: -- compatible : One or both of "motorola,cpcap" or "ste,6556002" -- reg : SPI chip select -- interrupts : The interrupt line the device is connected to -- interrupt-controller : Marks the device node as an interrupt controller -- #interrupt-cells : The number of cells to describe an IRQ, should be 2 -- #address-cells : Child device offset number of cells, should be 1 -- #size-cells : Child device size number of cells, should be 0 -- spi-max-frequency : Typically set to 3000000 -- spi-cs-high : SPI chip select direction - -Optional subnodes: - -The sub-functions of CPCAP get their own node with their own compatible values, -which are described in the following files: - -- Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml -- Documentation/devicetree/bindings/power/supply/cpcap-charger.yaml -- Documentation/devicetree/bindings/regulator/cpcap-regulator.txt -- Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml -- Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt -- Documentation/devicetree/bindings/rtc/cpcap-rtc.txt -- Documentation/devicetree/bindings/leds/leds-cpcap.txt -- Documentation/devicetree/bindings/iio/adc/motorola,cpcap-adc.yaml - -The only exception is the audio codec. Instead of a compatible value its -node must be named "audio-codec". - -Required properties for the audio-codec subnode: - -- #sound-dai-cells = <1>; -- interrupts : should contain jack detection interrupts, with headset - detect interrupt matching "hs" and microphone bias 2 - detect interrupt matching "mb2" in interrupt-names. -- interrupt-names : Contains "hs", "mb2" - -The audio-codec provides two DAIs. The first one is connected to the -Stereo HiFi DAC and the second one is connected to the Voice DAC. - -Example: - -&mcspi1 { - cpcap: pmic@0 { - compatible = "motorola,cpcap", "ste,6556002"; - reg = <0>; /* cs0 */ - interrupt-parent = <&gpio1>; - interrupts = <7 IRQ_TYPE_EDGE_RISING>; - interrupt-controller; - #interrupt-cells = <2>; - #address-cells = <1>; - #size-cells = <0>; - spi-max-frequency = <3000000>; - spi-cs-high; - - audio-codec { - #sound-dai-cells = <1>; - interrupts-extended = <&cpcap 9 0>, <&cpcap 10 0>; - interrupt-names = "hs", "mb2"; - - /* HiFi */ - port@0 { - endpoint { - remote-endpoint = <&cpu_dai1>; - }; - }; - - /* Voice */ - port@1 { - endpoint { - remote-endpoint = <&cpu_dai2>; - }; - }; - }; - }; -}; - From a695b4265600c0d25d334712ca3894788cd249c8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 21 Jul 2026 12:56:51 +0300 Subject: [PATCH 44/74] dt-bindings: mfd: motorola-cpcap: Document Mapphone and Mot CPCAP Add compatibles for Mapphone and Mot CPCAP subdevice compositions. Both variations cannot use st,6556002 fallback since they may be based on different controllers. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260721095654.429346-5-clamor95@gmail.com Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/motorola,cpcap.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml b/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml index 76705ea56805..da7abe2ec912 100644 --- a/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml +++ b/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml @@ -14,9 +14,14 @@ allOf: properties: compatible: - items: - - const: motorola,cpcap - - const: st,6556002 + oneOf: + - enum: + - motorola,mapphone-cpcap + - motorola,mot-cpcap + + - items: + - const: motorola,cpcap + - const: st,6556002 reg: maxItems: 1 From 8b8035cbfbb8487087b87db78724a7b1a2ab1982 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 21 Jul 2026 12:56:52 +0300 Subject: [PATCH 45/74] mfd: motorola-cpcap: Diverge configuration per-board MFD have rigid subdevice structure which does not allow flexible dynamic subdevice linking. Address this by diverging CPCAP subdevice composition to take into account board specific configuration. Create a common and default subdevice composition, rename edit existing subdevice composition into cpcap_mapphone_devices since it targets mainly Mapphone board. Removed st,6556002 as it is no longer applicable to all cases and duplicates motorola,cpcap, which is used as the default composition. Signed-off-by: Svyatoslav Ryhel Link: https://patch.msgid.link/20260721095654.429346-6-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/motorola-cpcap.c | 125 +++++++++++++++-------------- include/linux/mfd/motorola-cpcap.h | 6 ++ 2 files changed, 70 insertions(+), 61 deletions(-) diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c index 4ed8704c985a..5feb403f2610 100644 --- a/drivers/mfd/motorola-cpcap.c +++ b/drivers/mfd/motorola-cpcap.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,7 @@ struct cpcap_ddata { struct regmap_irq_chip_data *irqdata[CPCAP_NR_IRQ_CHIPS]; const struct regmap_config *regmap_conf; struct regmap *regmap; + enum cpcap_variant variant; }; static int cpcap_sense_irq(struct regmap *regmap, int irq) @@ -194,20 +196,6 @@ static int cpcap_init_irq(struct cpcap_ddata *cpcap) return 0; } -static const struct of_device_id cpcap_of_match[] = { - { .compatible = "motorola,cpcap", }, - { .compatible = "st,6556002", }, - {}, -}; -MODULE_DEVICE_TABLE(of, cpcap_of_match); - -static const struct spi_device_id cpcap_spi_ids[] = { - { .name = "cpcap" }, - { .name = "6556002" }, - { } -}; -MODULE_DEVICE_TABLE(spi, cpcap_spi_ids); - static const struct regmap_config cpcap_regmap_config = { .reg_bits = 16, .reg_stride = 4, @@ -240,62 +228,58 @@ static int cpcap_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(cpcap_pm, cpcap_suspend, cpcap_resume); -static const struct mfd_cell cpcap_mfd_devices[] = { - { - .name = "cpcap_adc", - .of_compatible = "motorola,mapphone-cpcap-adc", - }, { - .name = "cpcap_battery", - .of_compatible = "motorola,cpcap-battery", - }, { - .name = "cpcap-charger", - .of_compatible = "motorola,mapphone-cpcap-charger", - }, { - .name = "cpcap-regulator", - .of_compatible = "motorola,mapphone-cpcap-regulator", - }, { - .name = "cpcap-rtc", - .of_compatible = "motorola,cpcap-rtc", - }, { - .name = "cpcap-pwrbutton", - .of_compatible = "motorola,cpcap-pwrbutton", - }, { - .name = "cpcap-usb-phy", - .of_compatible = "motorola,mapphone-cpcap-usb-phy", - }, { - .name = "cpcap-led", - .id = 0, - .of_compatible = "motorola,cpcap-led-red", - }, { - .name = "cpcap-led", - .id = 1, - .of_compatible = "motorola,cpcap-led-green", - }, { - .name = "cpcap-led", - .id = 2, - .of_compatible = "motorola,cpcap-led-blue", - }, { - .name = "cpcap-led", - .id = 3, - .of_compatible = "motorola,cpcap-led-adl", - }, { - .name = "cpcap-led", - .id = 4, - .of_compatible = "motorola,cpcap-led-cp", - }, { - .name = "cpcap-codec", - } +static const struct mfd_cell cpcap_common_devices[] = { + MFD_CELL_OF("cpcap_battery", NULL, NULL, 0, 0, "motorola,cpcap-battery"), + MFD_CELL_OF("cpcap-rtc", NULL, NULL, 0, 0, "motorola,cpcap-rtc"), + MFD_CELL_OF("cpcap-pwrbutton", NULL, NULL, 0, 0, "motorola,cpcap-pwrbutton"), + MFD_CELL_OF("cpcap-led", NULL, NULL, 0, 0, "motorola,cpcap-led-red"), + MFD_CELL_OF("cpcap-led", NULL, NULL, 0, 1, "motorola,cpcap-led-green"), + MFD_CELL_OF("cpcap-led", NULL, NULL, 0, 2, "motorola,cpcap-led-blue"), + MFD_CELL_OF("cpcap-led", NULL, NULL, 0, 3, "motorola,cpcap-led-adl"), + MFD_CELL_OF("cpcap-led", NULL, NULL, 0, 4, "motorola,cpcap-led-cp"), + MFD_CELL_NAME("cpcap-codec"), +}; + +static const struct mfd_cell cpcap_default_devices[] = { + MFD_CELL_OF("cpcap_adc", NULL, NULL, 0, 0, "motorola,cpcap-adc"), + MFD_CELL_OF("cpcap-regulator", NULL, NULL, 0, 0, "motorola,cpcap-regulator"), + MFD_CELL_OF("cpcap-usb-phy", NULL, NULL, 0, 0, "motorola,cpcap-usb-phy"), +}; + +static const struct mfd_cell cpcap_mapphone_devices[] = { + MFD_CELL_OF("cpcap_adc", NULL, NULL, 0, 0, "motorola,mapphone-cpcap-adc"), + MFD_CELL_OF("cpcap-charger", NULL, NULL, 0, 0, "motorola,mapphone-cpcap-charger"), + MFD_CELL_OF("cpcap-regulator", NULL, NULL, 0, 0, "motorola,mapphone-cpcap-regulator"), + MFD_CELL_OF("cpcap-usb-phy", NULL, NULL, 0, 0, "motorola,mapphone-cpcap-usb-phy"), }; static int cpcap_probe(struct spi_device *spi) { struct cpcap_ddata *cpcap; + const struct mfd_cell *cells; + unsigned int num_cells; int ret; cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL); if (!cpcap) return -ENOMEM; + cpcap->variant = (enum cpcap_variant)spi_get_device_match_data(spi); + + switch (cpcap->variant) { + case CPCAP_DEFAULT: + cells = cpcap_default_devices; + num_cells = ARRAY_SIZE(cpcap_default_devices); + break; + case CPCAP_MAPPHONE: + cells = cpcap_mapphone_devices; + num_cells = ARRAY_SIZE(cpcap_mapphone_devices); + break; + default: + return dev_err_probe(&spi->dev, -ENODEV, + "Unknown device %d\n", cpcap->variant); + } + cpcap->spi = spi; spi_set_drvdata(spi, cpcap); @@ -330,10 +314,29 @@ static int cpcap_probe(struct spi_device *spi) spi->dev.coherent_dma_mask = 0; spi->dev.dma_mask = &spi->dev.coherent_dma_mask; - return devm_mfd_add_devices(&spi->dev, 0, cpcap_mfd_devices, - ARRAY_SIZE(cpcap_mfd_devices), NULL, 0, NULL); + ret = devm_mfd_add_devices(&spi->dev, 0, cpcap_common_devices, + ARRAY_SIZE(cpcap_common_devices), NULL, 0, NULL); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to add common child devices\n"); + + return devm_mfd_add_devices(&spi->dev, 0, cells, num_cells, NULL, 0, NULL); } +static const struct of_device_id cpcap_of_match[] = { + { .compatible = "motorola,cpcap", .data = (void *)CPCAP_DEFAULT }, + { .compatible = "motorola,mapphone-cpcap", .data = (void *)CPCAP_MAPPHONE }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cpcap_of_match); + +static const struct spi_device_id cpcap_spi_ids[] = { + { .name = "cpcap", .driver_data = CPCAP_DEFAULT }, + { .name = "mapphone-cpcap", .driver_data = CPCAP_MAPPHONE }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, cpcap_spi_ids); + static struct spi_driver cpcap_driver = { .driver = { .name = "cpcap-core", diff --git a/include/linux/mfd/motorola-cpcap.h b/include/linux/mfd/motorola-cpcap.h index 981e5777deb7..1a85b06272c8 100644 --- a/include/linux/mfd/motorola-cpcap.h +++ b/include/linux/mfd/motorola-cpcap.h @@ -25,6 +25,12 @@ #define CPCAP_REVISION_2_0 0x10 #define CPCAP_REVISION_2_1 0x11 +enum cpcap_variant { + CPCAP_DEFAULT = 1, + CPCAP_MAPPHONE, + CPCAP_MAX +}; + /* CPCAP registers */ #define CPCAP_REG_INT1 0x0000 /* Interrupt 1 */ #define CPCAP_REG_INT2 0x0004 /* Interrupt 2 */ From c115afabf0cbe85297f0ebcf1f9aa64bf66e491f Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 21 Jul 2026 12:56:53 +0300 Subject: [PATCH 46/74] mfd: motorola-cpcap: Add support for Mot CPCAP composition Add a MFD subdevice composition used in Tegra20 based Mot board (Motorola Atrix 4G and Droid X2). Signed-off-by: Svyatoslav Ryhel Link: https://patch.msgid.link/20260721095654.429346-7-clamor95@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/motorola-cpcap.c | 20 +++++++++++++++++++- include/linux/mfd/motorola-cpcap.h | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c index 5feb403f2610..65d5eb18aa00 100644 --- a/drivers/mfd/motorola-cpcap.c +++ b/drivers/mfd/motorola-cpcap.c @@ -236,7 +236,6 @@ static const struct mfd_cell cpcap_common_devices[] = { MFD_CELL_OF("cpcap-led", NULL, NULL, 0, 1, "motorola,cpcap-led-green"), MFD_CELL_OF("cpcap-led", NULL, NULL, 0, 2, "motorola,cpcap-led-blue"), MFD_CELL_OF("cpcap-led", NULL, NULL, 0, 3, "motorola,cpcap-led-adl"), - MFD_CELL_OF("cpcap-led", NULL, NULL, 0, 4, "motorola,cpcap-led-cp"), MFD_CELL_NAME("cpcap-codec"), }; @@ -244,6 +243,7 @@ static const struct mfd_cell cpcap_default_devices[] = { MFD_CELL_OF("cpcap_adc", NULL, NULL, 0, 0, "motorola,cpcap-adc"), MFD_CELL_OF("cpcap-regulator", NULL, NULL, 0, 0, "motorola,cpcap-regulator"), MFD_CELL_OF("cpcap-usb-phy", NULL, NULL, 0, 0, "motorola,cpcap-usb-phy"), + MFD_CELL_OF("cpcap-led", NULL, NULL, 0, 4, "motorola,cpcap-led-cp"), }; static const struct mfd_cell cpcap_mapphone_devices[] = { @@ -251,6 +251,18 @@ static const struct mfd_cell cpcap_mapphone_devices[] = { MFD_CELL_OF("cpcap-charger", NULL, NULL, 0, 0, "motorola,mapphone-cpcap-charger"), MFD_CELL_OF("cpcap-regulator", NULL, NULL, 0, 0, "motorola,mapphone-cpcap-regulator"), MFD_CELL_OF("cpcap-usb-phy", NULL, NULL, 0, 0, "motorola,mapphone-cpcap-usb-phy"), + MFD_CELL_OF("cpcap-led", NULL, NULL, 0, 4, "motorola,cpcap-led-cp"), +}; + +/* + * The Mot board features a USB-PHY and charger similar to the ones in + * Mapphone; however, because Mot is based on Tegra20, it is incompatible + * with the existing implementation, which is tightly interconnected with + * the OMAP USB PHY. + */ +static const struct mfd_cell cpcap_mot_devices[] = { + MFD_CELL_OF("cpcap_adc", NULL, NULL, 0, 0, "motorola,mot-cpcap-adc"), + MFD_CELL_OF("cpcap-regulator", NULL, NULL, 0, 0, "motorola,mot-cpcap-regulator"), }; static int cpcap_probe(struct spi_device *spi) @@ -275,6 +287,10 @@ static int cpcap_probe(struct spi_device *spi) cells = cpcap_mapphone_devices; num_cells = ARRAY_SIZE(cpcap_mapphone_devices); break; + case CPCAP_MOT: + cells = cpcap_mot_devices; + num_cells = ARRAY_SIZE(cpcap_mot_devices); + break; default: return dev_err_probe(&spi->dev, -ENODEV, "Unknown device %d\n", cpcap->variant); @@ -326,6 +342,7 @@ static int cpcap_probe(struct spi_device *spi) static const struct of_device_id cpcap_of_match[] = { { .compatible = "motorola,cpcap", .data = (void *)CPCAP_DEFAULT }, { .compatible = "motorola,mapphone-cpcap", .data = (void *)CPCAP_MAPPHONE }, + { .compatible = "motorola,mot-cpcap", .data = (void *)CPCAP_MOT }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, cpcap_of_match); @@ -333,6 +350,7 @@ MODULE_DEVICE_TABLE(of, cpcap_of_match); static const struct spi_device_id cpcap_spi_ids[] = { { .name = "cpcap", .driver_data = CPCAP_DEFAULT }, { .name = "mapphone-cpcap", .driver_data = CPCAP_MAPPHONE }, + { .name = "mot-cpcap", .driver_data = CPCAP_MOT }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(spi, cpcap_spi_ids); diff --git a/include/linux/mfd/motorola-cpcap.h b/include/linux/mfd/motorola-cpcap.h index 1a85b06272c8..bb23363eeccd 100644 --- a/include/linux/mfd/motorola-cpcap.h +++ b/include/linux/mfd/motorola-cpcap.h @@ -28,6 +28,7 @@ enum cpcap_variant { CPCAP_DEFAULT = 1, CPCAP_MAPPHONE, + CPCAP_MOT, CPCAP_MAX }; From ef464f82f3aecad4071af71074b2ceb77ffbb609 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 22 Sep 2026 22:19:36 -0400 Subject: [PATCH 47/74] mfd: da9150: Balance IRQ wake on teardown DA9150 enables IRQ wake after registering its regmap IRQ chip, but does not disable it on a later probe failure or driver removal. This leaves the wake depth elevated after the handler is removed, and repeated bind attempts can accumulate the imbalance. Remember whether enabling IRQ wake succeeded and balance only a successful call. Remove MFD children first so their nested IRQ users are gone, then disable wake before removing the regmap IRQ chip. Preserve the existing non-fatal behavior when IRQ wake cannot be enabled. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: b8fce55c09d3 ("mfd: Add support for DA9150 combined charger & fuel-gauge device") Assisted-by: LLM Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260923021936.957065-1-mhun512@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/da9150-core.c | 8 ++++++-- include/linux/mfd/da9150/core.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/da9150-core.c b/drivers/mfd/da9150-core.c index 5c59cc869fb3..19325d72a27e 100644 --- a/drivers/mfd/da9150-core.c +++ b/drivers/mfd/da9150-core.c @@ -450,7 +450,7 @@ static int da9150_probe(struct i2c_client *client) da9150->irq_base = regmap_irq_chip_get_base(da9150->regmap_irq_data); - enable_irq_wake(da9150->irq); + da9150->irq_wake_enabled = !enable_irq_wake(da9150->irq); ret = mfd_add_devices(da9150->dev, -1, da9150_devs, ARRAY_SIZE(da9150_devs), NULL, @@ -463,6 +463,8 @@ static int da9150_probe(struct i2c_client *client) return 0; mfd_fail: + if (da9150->irq_wake_enabled) + disable_irq_wake(da9150->irq); regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data); regmap_irq_fail: i2c_unregister_device(da9150->core_qif); @@ -474,8 +476,10 @@ static void da9150_remove(struct i2c_client *client) { struct da9150 *da9150 = i2c_get_clientdata(client); - regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data); mfd_remove_devices(da9150->dev); + if (da9150->irq_wake_enabled) + disable_irq_wake(da9150->irq); + regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data); i2c_unregister_device(da9150->core_qif); } diff --git a/include/linux/mfd/da9150/core.h b/include/linux/mfd/da9150/core.h index d116d5f3ef56..369698036d1a 100644 --- a/include/linux/mfd/da9150/core.h +++ b/include/linux/mfd/da9150/core.h @@ -65,6 +65,7 @@ struct da9150 { struct regmap_irq_chip_data *regmap_irq_data; int irq; int irq_base; + bool irq_wake_enabled; }; /* Device I/O - Query Interface for FG and standard register access */ From 319633b06ff2bfc2a7a60d2cfcab2e681a343b27 Mon Sep 17 00:00:00 2001 From: Muzaffer Kadir Date: Wed, 16 Sep 2026 17:22:14 +0300 Subject: [PATCH 48/74] dt-bindings: mfd: qcom,tcsr: Add compatible for MSM8952 Document the qcom,tcsr-msm8952 compatible. Signed-off-by: Muzaffer Kadir Link: https://patch.msgid.link/20260916-msm8952-initial-support-v2-9-798c2602dde1@mainlining.org Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml index c3889f974ed3..a021e4dea486 100644 --- a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml +++ b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml @@ -60,6 +60,7 @@ properties: - qcom,tcsr-msm8916 - qcom,tcsr-msm8917 - qcom,tcsr-msm8937 + - qcom,tcsr-msm8952 - qcom,tcsr-msm8953 - qcom,tcsr-msm8960 - qcom,tcsr-msm8974 From fabc98ef4665b3b2a506b445c0ae3733caaaacea Mon Sep 17 00:00:00 2001 From: Hemanth Selam Date: Thu, 24 Sep 2026 16:00:46 +0530 Subject: [PATCH 49/74] mfd: Fix a bunch of typos in comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix misspellings in comments across the MFD drivers and headers, found with scripts/checkpatch.pl using the list in scripts/spelling.txt. Only touches comments, no code changes. Signed-off-by: Hemanth Selam Reviewed-by: Matti Vaittinen Reviewed-by: André Draszik Link: https://patch.msgid.link/20260924103046.1709181-1-hemanth.selam@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/88pm800.c | 4 ++-- drivers/mfd/kempld-core.c | 2 +- drivers/mfd/max8998.c | 2 +- drivers/mfd/rave-sp.c | 6 +++--- drivers/mfd/si476x-i2c.c | 2 +- include/linux/mfd/da9063/registers.h | 2 +- include/linux/mfd/db8500-prcmu.h | 32 ++++++++++++++-------------- include/linux/mfd/lp8788.h | 2 +- include/linux/mfd/max8998.h | 2 +- include/linux/mfd/palmas.h | 2 +- include/linux/mfd/rohm-bd71828.h | 2 +- include/linux/mfd/samsung/core.h | 2 +- include/linux/mfd/si476x-core.h | 4 ++-- include/linux/mfd/twl.h | 2 +- 14 files changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c index e9941da58b18..7f3e312b8f73 100644 --- a/drivers/mfd/88pm800.c +++ b/drivers/mfd/88pm800.c @@ -275,10 +275,10 @@ static int device_gpadc_init(struct pm80x_chip *chip, goto out; /* - * the defult of PM800 is GPADC operates at 100Ks/s rate + * the default of PM800 is GPADC operates at 100Ks/s rate * and Number of GPADC slots with active current bias prior * to GPADC sampling = 1 slot for all GPADCs set for - * Temprature mesurmants + * Temperature measurements */ mask = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 | PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3); diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index b64729918dfd..47062f876e5d 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -37,7 +37,7 @@ static void kempld_get_hardware_mutex(struct kempld_device_data *pld) static void kempld_release_hardware_mutex(struct kempld_device_data *pld) { - /* The harware mutex is released when 1 is written to the mutex bit. */ + /* The hardware mutex is released when 1 is written to the mutex bit. */ iowrite8(KEMPLD_MUTEX_KEY, pld->io_index); } diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index eb13bbaeda55..5032eab05bb4 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -146,7 +146,7 @@ static struct max8998_platform_data *max8998_i2c_parse_dt_pdata( /* * ToDo: the 'wakeup' member in the platform data is more of a linux - * specfic information. Hence, there is no binding for that yet and + * specific information. Hence, there is no binding for that yet and * not parsed here. */ return pd; diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c index 05d26d92df03..53c57bafea8f 100644 --- a/drivers/mfd/rave-sp.c +++ b/drivers/mfd/rave-sp.c @@ -331,7 +331,7 @@ static u8 rave_sp_reply_code(u8 command) return command + 0x20; case 0xE0 ... 0xEF: /* - * Events emitted by all versions of the firmare use + * Events emitted by all versions of the firmware use * least significant bit to get an ACK code */ return command | 0x01; @@ -544,7 +544,7 @@ static size_t rave_sp_receive_buf(struct serdev_device *serdev, * NOTE: The above means that only the * frame started by third SOF, sent * after this one will have a chance - * to get throught. + * to get through. */ goto reset_framer; case RAVE_SP_DLE: @@ -595,7 +595,7 @@ static size_t rave_sp_receive_buf(struct serdev_device *serdev, /* * The only way to get out of the above loop and end up here - * is throught consuming all of the supplied data, so here we + * is through consuming all of the supplied data, so here we * report that we processed it all. */ return size; diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c index 9bce720a0a08..6351aef2d3bb 100644 --- a/drivers/mfd/si476x-i2c.c +++ b/drivers/mfd/si476x-i2c.c @@ -222,7 +222,7 @@ int si476x_core_stop(struct si476x_core *core, bool soft) atomic_set(&core->is_alive, 0); if (soft) { - /* TODO: This probably shoud be a configurable option, + /* TODO: This probably should be a configurable option, * so it is possible to have the chips keep their * oscillators running */ diff --git a/include/linux/mfd/da9063/registers.h b/include/linux/mfd/da9063/registers.h index 7b8364bd08a0..376c5d330599 100644 --- a/include/linux/mfd/da9063/registers.h +++ b/include/linux/mfd/da9063/registers.h @@ -14,7 +14,7 @@ #define DA9063_I2C_PAGE_SEL_SHIFT 1 #define DA9063_EVENT_REG_NUM 4 -/* Page selection I2C or SPI always in the begining of any page. */ +/* Page selection I2C or SPI always in the beginning of any page. */ /* Page 0 : I2C access 0x000 - 0x0FF SPI access 0x000 - 0x07F */ /* Page 1 : SPI access 0x080 - 0x0FF */ /* Page 2 : I2C access 0x100 - 0x1FF SPI access 0x100 - 0x17F */ diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h index c939c9a1170a..87a28c4f1d81 100644 --- a/include/linux/mfd/db8500-prcmu.h +++ b/include/linux/mfd/db8500-prcmu.h @@ -337,31 +337,31 @@ enum sia_mmdsp_stat { * @SYSCLKOK_ERR: The SYSCLK is not available in the given time * @BOOT_ERR: Romcode has not validated the XP70 self reset in the given time * @ROMCODESAVECONTEXT: The Romcode didn.t correctly save it secure context - * @VARMHIGHSPEEDVALTO_ERR: The ARM high speed supply value transfered + * @VARMHIGHSPEEDVALTO_ERR: The ARM high speed supply value transferred * through I2C has not been correctly executed in the given time - * @VARMHIGHSPEEDACCESS_ERR: The command value of VarmHighSpeedVal transfered + * @VARMHIGHSPEEDACCESS_ERR: The command value of VarmHighSpeedVal transferred * through I2C has not been correctly executed in the given time - * @VARMLOWSPEEDVALTO_ERR:The ARM low speed supply value transfered through + * @VARMLOWSPEEDVALTO_ERR:The ARM low speed supply value transferred through * I2C has not been correctly executed in the given time - * @VARMLOWSPEEDACCESS_ERR: The command value of VarmLowSpeedVal transfered + * @VARMLOWSPEEDACCESS_ERR: The command value of VarmLowSpeedVal transferred * through I2C has not been correctly executed in the given time - * @VARMRETENTIONVALTO_ERR: The ARM retention supply value transfered through + * @VARMRETENTIONVALTO_ERR: The ARM retention supply value transferred through * I2C has not been correctly executed in the given time - * @VARMRETENTIONACCESS_ERR: The command value of VarmRetentionVal transfered + * @VARMRETENTIONACCESS_ERR: The command value of VarmRetentionVal transferred * through I2C has not been correctly executed in the given time - * @VAPEHIGHSPEEDVALTO_ERR: The APE highspeed supply value transfered through + * @VAPEHIGHSPEEDVALTO_ERR: The APE highspeed supply value transferred through * I2C has not been correctly executed in the given time - * @VSAFEHPVALTO_ERR: The SAFE high power supply value transfered through I2C + * @VSAFEHPVALTO_ERR: The SAFE high power supply value transferred through I2C * has not been correctly executed in the given time - * @VMODSEL1VALTO_ERR: The MODEM sel1 supply value transfered through I2C has + * @VMODSEL1VALTO_ERR: The MODEM sel1 supply value transferred through I2C has * not been correctly executed in the given time - * @VMODSEL2VALTO_ERR: The MODEM sel2 supply value transfered through I2C has + * @VMODSEL2VALTO_ERR: The MODEM sel2 supply value transferred through I2C has * not been correctly executed in the given time - * @VARMOFFACCESS_ERR: The command value of Varm ON/OFF transfered through + * @VARMOFFACCESS_ERR: The command value of Varm ON/OFF transferred through * I2C has not been correctly executed in the given time - * @VAPEOFFACCESS_ERR: The command value of Vape ON/OFF transfered through + * @VAPEOFFACCESS_ERR: The command value of Vape ON/OFF transferred through * I2C has not been correctly executed in the given time - * @VARMRETACCES_ERR: The command value of Varm retention ON/OFF transfered + * @VARMRETACCES_ERR: The command value of Varm retention ON/OFF transferred * through I2C has not been correctly executed in the given time * @CURAPPWRSTISNOTBOOT:Generated when Arm want to do power state transition * ApBoot to ApExecute but the power current state is not Apboot @@ -376,11 +376,11 @@ enum sia_mmdsp_stat { * been correctly executed in the given time * @ARMREGU2VALTO_ERR: The ArmRegu2 value transferred through I2C has not * been correctly executed in the given time - * @VAPEREGUVALTO_ERR: The VApeRegu value transfered through I2C has not + * @VAPEREGUVALTO_ERR: The VApeRegu value transferred through I2C has not * been correctly executed in the given time - * @VSMPS3REGUVALTO_ERR: The VSmps3Regu value transfered through I2C has not + * @VSMPS3REGUVALTO_ERR: The VSmps3Regu value transferred through I2C has not * been correctly executed in the given time - * @VMODREGUVALTO_ERR: The VModemRegu value transfered through I2C has not + * @VMODREGUVALTO_ERR: The VModemRegu value transferred through I2C has not * been correctly executed in the given time */ enum mbox_to_arm_err { diff --git a/include/linux/mfd/lp8788.h b/include/linux/mfd/lp8788.h index fd17bec2a33e..e8c50bd38a8a 100644 --- a/include/linux/mfd/lp8788.h +++ b/include/linux/mfd/lp8788.h @@ -34,7 +34,7 @@ #define LP8788_ALM_IRQ "ALARM_IRQ" enum lp8788_int_id { - /* interrup register 1 : Addr 00h */ + /* interrupt register 1 : Addr 00h */ LP8788_INT_TSDL, LP8788_INT_TSDH, LP8788_INT_UVLO, diff --git a/include/linux/mfd/max8998.h b/include/linux/mfd/max8998.h index 5473f1983e31..ad2ad24412fb 100644 --- a/include/linux/mfd/max8998.h +++ b/include/linux/mfd/max8998.h @@ -45,7 +45,7 @@ enum { /** * max8998_regulator_data - regulator data * @id: regulator id - * @initdata: regulator init data (contraints, supplies, ...) + * @initdata: regulator init data (constraints, supplies, ...) * @reg_node: DT node of regulator (unused on non-DT platforms) */ struct max8998_regulator_data { diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index dabcc0dea802..1d872363820a 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -251,7 +251,7 @@ enum tps65917_regulators { TPS65917_NUM_REGS, }; -/* External controll signal name */ +/* External control signal name */ enum { PALMAS_EXT_CONTROL_ENABLE1 = 0x1, PALMAS_EXT_CONTROL_ENABLE2 = 0x2, diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h index 39fe275a8117..ea9b1a8f495f 100644 --- a/include/linux/mfd/rohm-bd71828.h +++ b/include/linux/mfd/rohm-bd71828.h @@ -186,7 +186,7 @@ enum { #define BD71828_REG_RTC_ALM2 0x63 #define BD71828_REG_RTC_START BD71828_REG_RTC_SEC -/* Charger/Battey */ +/* Charger/Battery */ #define BD71828_REG_CHG_STATE 0x65 #define BD71828_REG_CHG_FULL 0xd2 #define BD71828_REG_CHG_EN 0x6F diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h index 6191f409de94..e489ca0d55ed 100644 --- a/include/linux/mfd/samsung/core.h +++ b/include/linux/mfd/samsung/core.h @@ -106,7 +106,7 @@ struct sec_platform_data { /** * sec_regulator_data - regulator data * @id: regulator id - * @initdata: regulator init data (contraints, supplies, ...) + * @initdata: regulator init data (constraints, supplies, ...) */ struct sec_regulator_data { int id; diff --git a/include/linux/mfd/si476x-core.h b/include/linux/mfd/si476x-core.h index d9e3a322134c..3e924ca4db00 100644 --- a/include/linux/mfd/si476x-core.h +++ b/include/linux/mfd/si476x-core.h @@ -94,10 +94,10 @@ enum si476x_power_state { * of the @rds_fifo_drainer. * @rds_drainer_status_lock: Lock used to guard access to the * @rds_drainer_is_working variable. - * @command: Wait queue for wainting on the command comapletion. + * @command: Wait queue for waiting on the command completion. * @cts: Clear To Send flag set upon receiving first status with CTS * set. - * @tuning: Wait queue used for wainting for tune/seek comand + * @tuning: Wait queue used for waiting for tune/seek command * completion. * @stc: Similar to @cts, but for the STC bit of the status value. * @power_up_parameters: Parameters used as argument for POWER_UP diff --git a/include/linux/mfd/twl.h b/include/linux/mfd/twl.h index b31e07fa4d51..238d9dcefc35 100644 --- a/include/linux/mfd/twl.h +++ b/include/linux/mfd/twl.h @@ -739,7 +739,7 @@ int twl4030_sih_setup(struct device *dev, int module, int irq_base); #define TWL4030_REG_VUSB3V1 19 /* TWL6030 SMPS/LDO's */ -/* EXTERNAL dc-to-dc buck convertor controllable via SR */ +/* EXTERNAL dc-to-dc buck converter controllable via SR */ #define TWL6030_REG_VDD1 30 #define TWL6030_REG_VDD2 31 #define TWL6030_REG_VDD3 32 From eca76cc1708ea059c5637cfee7c2e38a8a26b843 Mon Sep 17 00:00:00 2001 From: Matthias Goergens Date: Mon, 28 Sep 2026 18:59:48 +0800 Subject: [PATCH 50/74] MAINTAINERS: Name the lee/mfd for-mfd-next branch The T: entries for MULTIFUNCTION DEVICES (MFD) and SYSTEM CONFIGURATION (SYSCON) name lee/mfd without a branch. The repository's HEAD points to branch master, which mainline already contains. linux-next merges the for-mfd-next branch of this repository (Next/Trees). Name that branch in both entries. Signed-off-by: Matthias Goergens Link: https://patch.msgid.link/20260928105948.2186851-1-matthias.goergens@gmail.com Signed-off-by: Lee Jones --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index b0925fa212cc..a7a353d5c6f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18650,7 +18650,7 @@ MULTIFUNCTION DEVICES (MFD) M: Lee Jones L: mfd@lists.linux.dev S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next F: Documentation/devicetree/bindings/mfd/ F: drivers/mfd/ F: include/dt-bindings/mfd/ @@ -26592,7 +26592,7 @@ SYSTEM CONFIGURATION (SYSCON) M: Lee Jones M: Arnd Bergmann S: Supported -T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next F: drivers/mfd/syscon.c SYSTEM CONTROL & POWER/MANAGEMENT INTERFACE (SCPI/SCMI) Message Protocol drivers From 687e666eeb2039f8720e27469d4b05b24eb4650b Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Tue, 22 Sep 2026 10:34:34 -0600 Subject: [PATCH 51/74] dt-bindings: mfd: x-powers: Add AC200 The AC200 is an I2C-controlled mixed-signal companion IC containing audio, video, RTC and Fast Ethernet PHY functions. Describe the shared input clock, audio DAI, TV encoder display graph and optional bandgap calibration in one parent node. The codec and TV encoder do not need separate firmware nodes. AC_LDOIN and TV_VCC are distinct 3.3 V supply inputs. Describe them separately without requiring different external regulators. Require the audio supply when the DAI is described and the video supply when the display graph is present, while allowing PHY-only configurations. Describe the optional shared INTB connection without an internal interrupt domain. Cable detection status remains readable over I2C when INTB is not connected. Include wired and unwired examples. The Ethernet PHY is represented by a PHY package on its MDIO bus, with a phandle back to this device for access to the package control registers. It therefore does not require an MFD child node. The input clock must run at 24 or 27 MHz when the Ethernet PHY is used, matching the rates encoded by its documented clock selector. Co-developed-by: Jernej Skrabec Signed-off-by: Jernej Skrabec Signed-off-by: James Hilliard Reviewed-by: Conor Dooley Link: https://patch.msgid.link/20260922-submit-ac200-mfd-v13-1-8832afd2481c@gmail.com Signed-off-by: Lee Jones --- .../bindings/mfd/x-powers,ac200.yaml | 168 ++++++++++++++++++ MAINTAINERS | 7 + 2 files changed, 175 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/x-powers,ac200.yaml diff --git a/Documentation/devicetree/bindings/mfd/x-powers,ac200.yaml b/Documentation/devicetree/bindings/mfd/x-powers,ac200.yaml new file mode 100644 index 000000000000..75dafa7c1071 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/x-powers,ac200.yaml @@ -0,0 +1,168 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/x-powers,ac200.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: X-Powers AC200 mixed-signal IC + +maintainers: + - James Hilliard + - Jernej Skrabec + +description: + The AC200 is a mixed-signal companion IC containing audio, video, RTC and + Fast Ethernet PHY functions. Its control registers are accessed over I2C. + The audio codec and TV encoder are described by properties of this node. + The Ethernet PHY is described separately on its MDIO bus. + +allOf: + - $ref: /schemas/sound/dai-common.yaml# + +properties: + compatible: + const: x-powers,ac200 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + description: + AC200 input clock. When using the Ethernet PHY, its configured rate must + be 24 or 27 MHz, matching the rates encoded by the documented EPHY clock + selector. + + interrupts: + maxItems: 1 + description: + The shared, level-triggered, open-drain INTB output for the TV encoder, + Ethernet PHY and RTC interrupts. Omit when INTB is not connected. TV + encoder cable detection status can also be read over I2C without an + interrupt. + + '#sound-dai-cells': + const: 0 + + ac-ldoin-supply: + description: + The 3.3 V supply connected to the AC_LDOIN pin for the audio codec LDO + input. This may be the same regulator as tv-vcc-supply. + + tv-vcc-supply: + description: + The 3.3 V supply connected to the TV_VCC pin for the composite-video + DAC. This may be the same regulator as ac-ldoin-supply. + + nvmem-cells: + maxItems: 1 + description: TV encoder bandgap calibration. + + nvmem-cell-names: + items: + - const: bandgap + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: Input to the TV encoder, carrying CCIR656. + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: Output to the composite-video connector. + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - clocks + +dependencies: + '#sound-dai-cells': [ ac-ldoin-supply ] + ports: [ tv-vcc-supply ] + nvmem-cells: [ nvmem-cell-names ] + nvmem-cell-names: [ nvmem-cells ] + +unevaluatedProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + mixed-signal@10 { + compatible = "x-powers,ac200"; + reg = <0x10>; + clocks = <&pwm 5>; + interrupt-parent = <&pio>; + interrupts = <1 20 IRQ_TYPE_LEVEL_LOW>; + #sound-dai-cells = <0>; + ac-ldoin-supply = <®_aldo2>; + tv-vcc-supply = <®_aldo2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + tve_in: endpoint { + remote-endpoint = <&tcon_out_tve>; + }; + }; + + port@1 { + reg = <1>; + + tve_out: endpoint { + remote-endpoint = <&composite_in>; + }; + }; + }; + }; + }; + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + mixed-signal@10 { + compatible = "x-powers,ac200"; + reg = <0x10>; + clocks = <&pwm 5>; + /* INTB is not connected. */ + tv-vcc-supply = <®_aldo2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + endpoint { + remote-endpoint = <&tcon_out_tve>; + }; + }; + + port@1 { + reg = <1>; + + endpoint { + remote-endpoint = <&composite_in>; + }; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index a7a353d5c6f4..316adebbfd00 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -29506,6 +29506,13 @@ F: drivers/net/wwan/ F: include/linux/wwan.h F: include/uapi/linux/wwan.h +X-POWERS AC200 DRIVERS +M: Jernej Skrabec +M: James Hilliard +L: linux-sunxi@lists.linux.dev +S: Maintained +F: Documentation/devicetree/bindings/mfd/x-powers,ac200.yaml + X-POWERS AXP288 PMIC DRIVERS M: Hans de Goede S: Maintained From 1d53aa3f574b77525a39c5d9c5a32882c6f699e7 Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Tue, 22 Sep 2026 10:34:35 -0600 Subject: [PATCH 52/74] mfd: ac200: Add X-Powers AC200 support The X-Powers AC200 is a mixed-signal companion IC with a paged register map accessed over I2C. Enable and rate-lock the shared input clock, retain the vendor resume path's 40 ms wait before creating the regmap and accessing registers, and deassert the common reset. No minimum delay is documented. Set only the deassert bit instead of forcing a reset cycle, avoiding a chip-wide reset of unrelated function registers. Leave the common reset deasserted during driver removal and system shutdown; function drivers own their block resets. Supplier unbind still tears down linked consumers and releases the provider's clock references. Cache only the common page selector. Individual functions can reset independently and invalidate their other registers, so leave all functional registers volatile. Register the audio codec and TV encoder using static MFD cells and automatically assigned platform device IDs. Their firmware properties belong to the parent node. Map their supply lookups to the parent with MFD supply aliases, without assigning separate OF nodes to the cells. When INTB is connected, pass its physical IRQ to the TV encoder cell. Select a cell array without IRQ resources when INTB is absent. No regmap IRQ controller or private IRQ domain is needed. Configure the level-triggered INTB output, defaulting to level-low when the upstream IRQ has no trigger type. Mask all sources before enabling INTB. Function drivers request shared threaded IRQs before enabling their own source, check and clear their own status, and mask their source before freeing the IRQ. Disable INTB after removing the children on failure or removal, while the regmap and clock are still available. The Ethernet PHY is enumerated on its MDIO bus rather than as an MFD child. It follows the x-powers,ac200 phandle and uses this regmap for ancillary package-control access. Co-developed-by: Jernej Skrabec Signed-off-by: Jernej Skrabec Signed-off-by: James Hilliard Link: https://patch.msgid.link/20260922-submit-ac200-mfd-v13-2-8832afd2481c@gmail.com Signed-off-by: Lee Jones --- MAINTAINERS | 1 + drivers/mfd/Kconfig | 14 +++ drivers/mfd/Makefile | 1 + drivers/mfd/ac200.c | 222 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 238 insertions(+) create mode 100644 drivers/mfd/ac200.c diff --git a/MAINTAINERS b/MAINTAINERS index 316adebbfd00..d41dccf2af46 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -29512,6 +29512,7 @@ M: James Hilliard L: linux-sunxi@lists.linux.dev S: Maintained F: Documentation/devicetree/bindings/mfd/x-powers,ac200.yaml +F: drivers/mfd/ac200.c X-POWERS AXP288 PMIC DRIVERS M: Hans de Goede diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0a2778052519..87ed55c155f0 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -205,6 +205,20 @@ config MFD_AC100 This driver include only the core APIs. You have to select individual components like codecs or RTC under the corresponding menus. +config MFD_AC200 + tristate "X-Powers AC200" + depends on COMMON_CLK + depends on I2C + depends on OF + select MFD_CORE + select REGMAP_I2C + help + Support for the X-Powers AC200 mixed-signal companion IC. The AC200 + contains audio, video, RTC and Fast Ethernet PHY functions and is + co-packaged with some Allwinner H6 and H616 SoCs. This driver provides + the shared register access and instantiates the individual function + devices. + config MFD_AXP20X tristate select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 72d3944b0ad8..f8101d2a9ce9 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -150,6 +150,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o obj-$(CONFIG_MFD_AC100) += ac100.o +obj-$(CONFIG_MFD_AC200) += ac200.o obj-$(CONFIG_MFD_AXP20X) += axp20x.o obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o diff --git a/drivers/mfd/ac200.c b/drivers/mfd/ac200.c new file mode 100644 index 000000000000..3c6543a1688c --- /dev/null +++ b/drivers/mfd/ac200.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * X-Powers AC200 Core driver + * + * Copyright (C) 2019 Jernej Skrabec + * Copyright (C) 2026 James Hilliard + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define AC200_SYS_CONTROL_REG 0x0002 +#define AC200_SYS_CONTROL_CHIP_RESET_DEASSERT BIT(0) +#define AC200_SYS_IRQ_ENABLE_REG 0x0004 +#define AC200_SYS_IRQ_INTB_ENABLE BIT(15) +#define AC200_SYS_IRQ_INTB_ACTIVE_HIGH BIT(14) +#define AC200_SYS_IRQ_RTC BIT(12) +#define AC200_SYS_IRQ_EPHY BIT(8) +#define AC200_SYS_IRQ_TVE BIT(4) + +/* Interface register accessible from every register page. */ +#define AC200_TWI_REG_ADDR_H 0x00fe +#define AC200_MAX_REG 0xa1f2 + +static const struct regmap_range_cfg ac200_range_cfg[] = { + { + .range_max = AC200_MAX_REG, + .selector_reg = AC200_TWI_REG_ADDR_H, + .selector_mask = 0xff, + .window_len = 256, + }, +}; + +/* + * Each AC200 sub-block can reset independently, invalidating its register + * contents without regmap's knowledge. Cache only the common page selector; + * this avoids a selector read-modify-write for every access on the same page + * without ever returning stale functional-register values. + */ +static bool ac200_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg != AC200_TWI_REG_ADDR_H; +} + +static const struct regmap_config ac200_regmap_config = { + .name = "ac200", + .reg_bits = 8, + .reg_stride = 2, + .val_bits = 16, + .ranges = ac200_range_cfg, + .num_ranges = ARRAY_SIZE(ac200_range_cfg), + .max_register = AC200_MAX_REG, + .volatile_reg = ac200_volatile_reg, + .cache_type = REGCACHE_MAPLE, +}; + +static const char * const ac200_codec_supplies[] = { + "ac-ldoin", +}; + +static const char * const ac200_tve_supplies[] = { + "tv-vcc", +}; + +static const struct resource ac200_tve_resources[] = { + DEFINE_RES_IRQ_NAMED(0, "intb"), +}; + +#define AC200_CELL(_name, _supplies, _resources) \ + { \ + .name = (_name), \ + .parent_supplies = (_supplies), \ + .num_parent_supplies = ARRAY_SIZE(_supplies), \ + .resources = (_resources), \ + .num_resources = MFD_RES_SIZE(_resources), \ + } + +static const struct mfd_cell ac200_cells[] = { + AC200_CELL("ac200-codec", ac200_codec_supplies, NULL), + AC200_CELL("ac200-tve", ac200_tve_supplies, ac200_tve_resources), +}; + +static const struct mfd_cell ac200_noirq_cells[] = { + AC200_CELL("ac200-codec", ac200_codec_supplies, NULL), + AC200_CELL("ac200-tve", ac200_tve_supplies, NULL), +}; + +static void ac200_disable_intb(void *data) +{ + struct regmap *regmap = data; + int ret; + + ret = regmap_clear_bits(regmap, AC200_SYS_IRQ_ENABLE_REG, AC200_SYS_IRQ_INTB_ENABLE); + if (ret) + dev_err(regmap_get_device(regmap), "failed to disable INTB: %d\n", ret); +} + +static int ac200_init_irq(struct device *dev, struct regmap *regmap, int irq) +{ + unsigned int trigger; + u16 value = 0; + int ret; + + trigger = irq_get_trigger_type(irq); + switch (trigger) { + case IRQ_TYPE_LEVEL_HIGH: + value |= AC200_SYS_IRQ_INTB_ACTIVE_HIGH; + break; + case IRQ_TYPE_NONE: + case IRQ_TYPE_LEVEL_LOW: + break; + default: + return dev_err_probe(dev, -EINVAL, + "INTB is level triggered, not type %u\n", trigger); + } + + /* Mask every source before enabling the shared output. */ + ret = regmap_update_bits(regmap, AC200_SYS_IRQ_ENABLE_REG, + AC200_SYS_IRQ_INTB_ENABLE | AC200_SYS_IRQ_INTB_ACTIVE_HIGH | + AC200_SYS_IRQ_RTC | AC200_SYS_IRQ_EPHY | AC200_SYS_IRQ_TVE, value); + if (ret) + return ret; + + /* Children are removed before INTB is disabled and the regmap released. */ + ret = devm_add_action_or_reset(dev, ac200_disable_intb, regmap); + if (ret) + return ret; + + if (trigger == IRQ_TYPE_NONE) { + ret = irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); + if (ret) + return dev_err_probe(dev, ret, "failed to set INTB trigger\n"); + } + + /* + * Function drivers request INTB with IRQF_SHARED | IRQF_ONESHOT before + * enabling their own source. They check and clear their own status and + * mask their source before freeing the IRQ. No IRQ domain is needed. + */ + return regmap_set_bits(regmap, AC200_SYS_IRQ_ENABLE_REG, AC200_SYS_IRQ_INTB_ENABLE); +} + +static int ac200_probe(struct i2c_client *client) +{ + const struct mfd_cell *cells = ac200_noirq_cells; + unsigned int num_cells = ARRAY_SIZE(ac200_noirq_cells); + struct device *dev = &client->dev; + struct regmap *regmap; + struct clk *clk; + int ret; + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to enable input clock\n"); + + ret = devm_clk_rate_exclusive_get(dev, clk); + if (ret) + return dev_err_probe(dev, ret, "failed to lock clock rate\n"); + + /* + * No minimum delay is documented. Retain the vendor resume path's 40 ms + * wait after enabling the input clock and before register access. + */ + msleep(40); + + regmap = devm_regmap_init_i2c(client, &ac200_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "failed to initialize regmap\n"); + + ret = regmap_set_bits(regmap, AC200_SYS_CONTROL_REG, AC200_SYS_CONTROL_CHIP_RESET_DEASSERT); + if (ret) + return ret; + + if (client->irq > 0) { + ret = ac200_init_irq(dev, regmap, client->irq); + if (ret) + return ret; + cells = ac200_cells; + num_cells = ARRAY_SIZE(ac200_cells); + } + + /* Function drivers read their firmware properties from the parent. */ + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, num_cells, + NULL, client->irq, NULL); + if (ret) + return dev_err_probe(dev, ret, "failed to add function devices\n"); + + return 0; +} + +static const struct of_device_id ac200_of_match[] = { + { .compatible = "x-powers,ac200" }, + { } +}; +MODULE_DEVICE_TABLE(of, ac200_of_match); + +static const struct i2c_device_id ac200_i2c_ids[] = { + { .name = "ac200" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ac200_i2c_ids); + +static struct i2c_driver ac200_driver = { + .driver = { + .name = "ac200", + .of_match_table = ac200_of_match, + }, + .probe = ac200_probe, + .id_table = ac200_i2c_ids, +}; +module_i2c_driver(ac200_driver); + +MODULE_AUTHOR("Jernej Skrabec "); +MODULE_AUTHOR("James Hilliard "); +MODULE_DESCRIPTION("X-Powers AC200 Core driver"); +MODULE_LICENSE("GPL"); From 88e0bfab55e131fb2179dc3eee5893e9f638926b Mon Sep 17 00:00:00 2001 From: Chaosheng Qu Date: Wed, 30 Sep 2026 10:39:23 +0800 Subject: [PATCH 53/74] dt-bindings: mfd: Convert lp3943 to DT schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the LP3943 MFD binding from free-form text to DT schema. The LP3943 is an I2C LED driver with two sub-devices, lp3943-gpio and lp3943-pwm. The GPIO sub-device is covered by trivial-gpio.yaml, so only the PWM sub-device gets a schema of its own. The PWM schema documents ti,pwm0 and ti,pwm1, which select the output pins of the two internal PWM channels and are read by pwm-lp3943.c with of_property_read_u32_array(). Assisted-by: LLM Signed-off-by: Chaosheng Qu Reviewed-by: Rob Herring (Arm) Acked-by: Uwe Kleine-König Link: https://patch.msgid.link/20260930023923.49917-1-quchaosheng000406@163.com Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/lp3943.txt | 33 -------- .../devicetree/bindings/mfd/ti,lp3943.yaml | 64 +++++++++++++++ .../devicetree/bindings/pwm/pwm-lp3943.txt | 58 -------------- .../devicetree/bindings/pwm/pwm-lp3943.yaml | 80 +++++++++++++++++++ 4 files changed, 144 insertions(+), 91 deletions(-) delete mode 100644 Documentation/devicetree/bindings/mfd/lp3943.txt create mode 100644 Documentation/devicetree/bindings/mfd/ti,lp3943.yaml delete mode 100644 Documentation/devicetree/bindings/pwm/pwm-lp3943.txt create mode 100644 Documentation/devicetree/bindings/pwm/pwm-lp3943.yaml diff --git a/Documentation/devicetree/bindings/mfd/lp3943.txt b/Documentation/devicetree/bindings/mfd/lp3943.txt deleted file mode 100644 index ca5324ed0df4..000000000000 --- a/Documentation/devicetree/bindings/mfd/lp3943.txt +++ /dev/null @@ -1,33 +0,0 @@ -TI/National Semiconductor LP3943 MFD driver - -Required properties: - - compatible: "ti,lp3943" - - reg: I2C slave address. From 0x60 to 0x67. - -LP3943 consists of two sub-devices, lp3943-gpio and lp3943-pwm. - -For the LP3943 GPIO properties please refer to: -Documentation/devicetree/bindings/gpio/trivial-gpio.yaml - -For the LP3943 PWM properties please refer to: -Documentation/devicetree/bindings/pwm/pwm-lp3943.txt - -Example: - -lp3943@60 { - compatible = "ti,lp3943"; - reg = <0x60>; - - gpioex: gpio { - compatible = "ti,lp3943-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - - pwm3943: pwm { - compatible = "ti,lp3943-pwm"; - #pwm-cells = <2>; - ti,pwm0 = <8 9 10>; - ti,pwm1 = <15>; - }; -}; diff --git a/Documentation/devicetree/bindings/mfd/ti,lp3943.yaml b/Documentation/devicetree/bindings/mfd/ti,lp3943.yaml new file mode 100644 index 000000000000..eff32b05c21c --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/ti,lp3943.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/ti,lp3943.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI/National Semiconductor LP3943 MFD driver + +maintainers: + - Chaosheng Qu + +description: + LP3943 is an integrated device capable of driving LEDs. It contains two + sub-devices, lp3943-gpio and lp3943-pwm, which are instantiated by the MFD + core from the child nodes described below. + +properties: + compatible: + const: ti,lp3943 + + reg: + maxItems: 1 + description: I2C slave address, from 0x60 to 0x67 + +patternProperties: + "^gpio": + type: object + $ref: /schemas/gpio/trivial-gpio.yaml# + + "^pwm": + type: object + $ref: /schemas/pwm/pwm-lp3943.yaml# + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + lp3943: gpio@60 { + compatible = "ti,lp3943"; + reg = <0x60>; + + gpio { + compatible = "ti,lp3943-gpio"; + gpio-controller; + #gpio-cells = <2>; + }; + + pwm { + compatible = "ti,lp3943-pwm"; + #pwm-cells = <2>; + ti,pwm0 = <8 9 10>; + ti,pwm1 = <15>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pwm/pwm-lp3943.txt b/Documentation/devicetree/bindings/pwm/pwm-lp3943.txt deleted file mode 100644 index f214305a8f5e..000000000000 --- a/Documentation/devicetree/bindings/pwm/pwm-lp3943.txt +++ /dev/null @@ -1,58 +0,0 @@ -TI/National Semiconductor LP3943 PWM controller - -Required properties: - - compatible: "ti,lp3943-pwm" - - #pwm-cells: Should be 2. See pwm.yaml in this directory for a - description of the cells format. - Note that this hardware limits the period length to the - range 6250~1600000. - - ti,pwm0 or ti,pwm1: Output pin number(s) for PWM channel 0 or 1. - 0 = output 0 - 1 = output 1 - . - . - 15 = output 15 - -Example: -PWM 0 is for RGB LED brightness control -PWM 1 is for brightness control of LP8557 backlight device - -&i2c3 { - lp3943@60 { - compatible = "ti,lp3943"; - reg = <0x60>; - - /* - * PWM 0 : output 8, 9 and 10 - * PWM 1 : output 15 - */ - pwm3943: pwm { - compatible = "ti,lp3943-pwm"; - #pwm-cells = <2>; - ti,pwm0 = <8 9 10>; - ti,pwm1 = <15>; - }; - }; - -}; - -/* LEDs control with PWM 0 of LP3943 */ -pwmleds { - compatible = "pwm-leds"; - rgb { - label = "indi::rgb"; - pwms = <&pwm3943 0 10000>; - max-brightness = <255>; - }; -}; - -&i2c4 { - /* Backlight control with PWM 1 of LP3943 */ - backlight@2c { - compatible = "ti,lp8557"; - reg = <0x2c>; - - pwms = <&pwm3943 1 10000>; - pwm-names = "lp8557"; - }; -}; diff --git a/Documentation/devicetree/bindings/pwm/pwm-lp3943.yaml b/Documentation/devicetree/bindings/pwm/pwm-lp3943.yaml new file mode 100644 index 000000000000..3da5cd4b2ada --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-lp3943.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/pwm-lp3943.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI/National Semiconductor LP3943 PWM controller + +maintainers: + - Chaosheng Qu + +description: + LP3943 has two internal PWM channels. Each channel can be routed to one or + more of the 16 outputs of the device. + + Note that the hardware limits the period length to the range 6250~1600000. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: ti,lp3943-pwm + + "#pwm-cells": + const: 2 + + ti,pwm0: + description: Output pin number(s) for PWM channel 0. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 16 + items: + minimum: 0 + maximum: 15 + + ti,pwm1: + description: Output pin number(s) for PWM channel 1. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 16 + items: + minimum: 0 + maximum: 15 + +anyOf: + - required: + - ti,pwm0 + - required: + - ti,pwm1 + +required: + - compatible + - "#pwm-cells" + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + gpio@60 { + compatible = "ti,lp3943"; + reg = <0x60>; + + /* + * PWM 0 : output 8, 9 and 10 + * PWM 1 : output 15 + */ + pwm { + compatible = "ti,lp3943-pwm"; + #pwm-cells = <2>; + ti,pwm0 = <8 9 10>; + ti,pwm1 = <15>; + }; + }; + }; +... From b783c46fb685a38d1f5cde57f59e5ed520846ed7 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 21 Sep 2026 18:26:38 -0500 Subject: [PATCH 54/74] dt-bindings: mfd: ti,tps65086: Make regulators optional The TPS65086 MFD driver adds the regulator child device unconditionally, and the regulator driver registers the regulators from the parent node even when there is no regulators child node containing init data. Do not require the regulators container in the binding. Existing ZynqMP boards that only describe the PMIC core and GPIO function are valid without regulator init data. Assisted-by: LLM Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260921232639.3132083-1-robh@kernel.org Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/ti,tps65086.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mfd/ti,tps65086.yaml b/Documentation/devicetree/bindings/mfd/ti,tps65086.yaml index a8eed9065d96..64f02d1e1e4d 100644 --- a/Documentation/devicetree/bindings/mfd/ti,tps65086.yaml +++ b/Documentation/devicetree/bindings/mfd/ti,tps65086.yaml @@ -90,7 +90,6 @@ required: - reg - gpio-controller - '#gpio-cells' - - regulators examples: - | From 89089e041cd522b5e64c3f0c5782361db13322d7 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 21 Sep 2026 18:26:31 -0500 Subject: [PATCH 55/74] dt-bindings: mfd: mediatek: Allow MT6357 ADC MT6357 PMIC nodes can contain an adc child using the MT6359 AUXADC binding. Add the child node reference so the ADC subnode is evaluated instead of rejected. Assisted-by: LLM Signed-off-by: Rob Herring (Arm) Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20260921232632.3131680-1-robh@kernel.org Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/mediatek,mt6357.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6357.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6357.yaml index b67fbe0e7a63..dbb48e380e77 100644 --- a/Documentation/devicetree/bindings/mfd/mediatek,mt6357.yaml +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6357.yaml @@ -62,6 +62,13 @@ properties: description: List of MT6357 BUCKs and LDOs regulators. + adc: + type: object + $ref: /schemas/iio/adc/mediatek,mt6359-auxadc.yaml# + unevaluatedProperties: false + description: + MT6357 auxiliary ADC. + rtc: type: object $ref: /schemas/rtc/rtc.yaml# From ee76e520857352ec34aaa5786db41610a4a25de7 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 21 Sep 2026 18:27:00 -0500 Subject: [PATCH 56/74] dt-bindings: mfd: Allow MT6360 interrupt flags MT6360 interrupt controllers use either one interrupt cell or two cells, with the optional second cell carrying trigger flags. Allow both forms. Assisted-by: LLM Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260921232701.3133291-1-robh@kernel.org Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/mediatek,mt6360.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6360.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6360.yaml index fb65abf30d57..828ed7f558f6 100644 --- a/Documentation/devicetree/bindings/mfd/mediatek,mt6360.yaml +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6360.yaml @@ -33,9 +33,10 @@ properties: interrupt-controller: true "#interrupt-cells": - const: 1 + enum: [1, 2] description: - The first cell is the IRQ number. + The first cell is the IRQ number. The optional second cell is the IRQ + trigger type. regulators: $ref: /schemas/regulator/mt6360-regulator.yaml# From c3746b35b0503a90d744347bee22c9a9c04b7fd4 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Thu, 17 Sep 2026 16:44:39 +0400 Subject: [PATCH 57/74] dt-bindings: mfd: ti,bq25703a: Expand to include BQ25798 TI has unpublished the BQ25792 charger variant and pulled its docs from their website. BQ25798 is a pin and register compatible replacement, which also adds MPPT tracking support (reg 0x15, added in BQ25798 and reserved in BQ25792) and backup mode (reg 0x0f[1], reg 0x10[7:6], reg 0x16[0] and reg 0x1c[7:5] on BQ25798 - all of them reserved on BQ25792). Expand the existing binding to include BQ25798 with a fallback compatible to BQ25792. Link: https://www.ti.com/product/BQ25798 Signed-off-by: Alexey Charkov Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260917-bq25798-v2-1-5086845cd3eb@flipper.net Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/ti,bq25703a.yaml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/ti,bq25703a.yaml b/Documentation/devicetree/bindings/mfd/ti,bq25703a.yaml index cdce83f05804..3bc07f25d063 100644 --- a/Documentation/devicetree/bindings/mfd/ti,bq25703a.yaml +++ b/Documentation/devicetree/bindings/mfd/ti,bq25703a.yaml @@ -11,9 +11,14 @@ maintainers: properties: compatible: - enum: - - ti,bq25703a - - ti,bq25792 + oneOf: + - enum: + - ti,bq25703a + - ti,bq25792 + - items: + - enum: + - ti,bq25798 + - const: ti,bq25792 reg: const: 0x6b @@ -103,7 +108,8 @@ allOf: - if: properties: compatible: - const: ti,bq25792 + contains: + const: ti,bq25792 then: properties: input-current-limit-microamp: From 1b482faa86591dbd8524d294383281f4adf2f6cf Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Wed, 23 Sep 2026 14:15:25 -0500 Subject: [PATCH 58/74] dt-bindings: mfd: hisilicon: Fix Hi6421 SPMI compatible Use the hisilicon,hi6421-spmi compatible documented by the in-tree DTS and matched by the Hi6421 SPMI PMIC driver. Assisted-by: LLM Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260923191526.2206628-1-robh@kernel.org Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml b/Documentation/devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml index 6a824351834e..0dd37c40eeff 100644 --- a/Documentation/devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml @@ -24,7 +24,7 @@ properties: pattern: "pmic@[0-9a-f]" compatible: - const: hisilicon,hi6421v600-spmi + const: hisilicon,hi6421-spmi reg: maxItems: 1 @@ -66,7 +66,7 @@ examples: #size-cells = <0>; pmic@0 { - compatible = "hisilicon,hi6421v600-spmi"; + compatible = "hisilicon,hi6421-spmi"; reg = <0 SPMI_USID>; #interrupt-cells = <2>; From 017b13566a0dd41188ccea52f9c7cb0074ad8cdc Mon Sep 17 00:00:00 2001 From: Roman Vivchar Date: Mon, 21 Sep 2026 10:50:02 +0300 Subject: [PATCH 59/74] dt-bindings: mfd: mediatek: mt6397: Add mt6323 PMIC EFUSE The MediaTek mt6323 PMIC includes an EFUSE used for storing calibration data. Add the devicetree binding documentation for the MediaTek mt6323 EFUSE. Signed-off-by: Roman Vivchar Reviewed-by: Rob Herring (Arm) Acked-by: Conor Dooley Link: https://patch.msgid.link/20260921-mt6323-nvmem-v5-1-2f5d308369f0@protonmail.com Signed-off-by: Lee Jones --- .../bindings/mfd/mediatek,mt6397.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml index 3cbc0dc12c31..d2de227ae7a6 100644 --- a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml @@ -159,6 +159,23 @@ properties: required: - compatible + efuse: + type: object + unevaluatedProperties: false + description: + The efuse is responsible for storing calibration data, such as thermal + sensor calibration. + + properties: + compatible: + const: mediatek,mt6323-efuse + + nvmem-layout: + $ref: /schemas/nvmem/layouts/nvmem-layout.yaml# + + required: + - compatible + leds: type: object additionalProperties: false @@ -307,6 +324,10 @@ examples: interrupt-controller; #interrupt-cells = <2>; + efuse { + compatible = "mediatek,mt6323-efuse"; + }; + leds { compatible = "mediatek,mt6323-led"; #address-cells = <1>; From 198f3bfc6658c2f1a75cf0e9b03231242cc711cf Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 21 Sep 2026 18:26:48 -0500 Subject: [PATCH 60/74] dt-bindings: mfd: Add MT6397 sound DAI cells Document the one sound DAI cell provided by MT6397 PMIC nodes. Assisted-by: LLM Signed-off-by: Rob Herring (Arm) Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20260921232649.3132592-1-robh@kernel.org Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml index d2de227ae7a6..bbd56e522a75 100644 --- a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml @@ -57,6 +57,9 @@ properties: "#interrupt-cells": const: 2 + "#sound-dai-cells": + const: 1 + rtc: type: object $ref: /schemas/rtc/rtc.yaml# From 5bf2ae8b131d4ae9c0ed5f7aa02532645f512022 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Thu, 24 Sep 2026 22:43:55 +0800 Subject: [PATCH 61/74] mfd: ab8500: Fix child device leaks on probe failure ab8500_probe() registers MFD child devices before completing the remaining device initialization. If a later MFD registration or sysfs group creation fails, previously registered child devices are not removed. Use devm_mfd_add_devices() so registered MFD child devices are removed when probe fails. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fixes: cca69b67b3ba ("mfd: Export ab8500 chip id to sysfs") Fixes: 9c717cf3fa16 ("mfd: ab8500-core: Add device for new RTC version for AB8540 cut2") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260924144355.1604369-1-lgs201920130244@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/ab8500-core.c | 44 ++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index ceea1ae34dd8..d5602b2f4a0e 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1194,39 +1194,41 @@ static int ab8500_probe(struct platform_device *pdev) return ret; if (is_ab9540(ab8500)) - ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, - ARRAY_SIZE(ab9540_devs), NULL, - 0, ab8500->domain); + ret = devm_mfd_add_devices(ab8500->dev, 0, ab9540_devs, + ARRAY_SIZE(ab9540_devs), NULL, + 0, ab8500->domain); else if (is_ab8540(ab8500)) { - ret = mfd_add_devices(ab8500->dev, 0, ab8540_devs, - ARRAY_SIZE(ab8540_devs), NULL, - 0, ab8500->domain); + ret = devm_mfd_add_devices(ab8500->dev, 0, ab8540_devs, + ARRAY_SIZE(ab8540_devs), NULL, + 0, ab8500->domain); if (ret) return ret; if (is_ab8540_1p2_or_earlier(ab8500)) - ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut1_devs, - ARRAY_SIZE(ab8540_cut1_devs), NULL, - 0, ab8500->domain); + ret = devm_mfd_add_devices(ab8500->dev, 0, + ab8540_cut1_devs, + ARRAY_SIZE(ab8540_cut1_devs), + NULL, 0, ab8500->domain); else /* ab8540 >= cut2 */ - ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut2_devs, - ARRAY_SIZE(ab8540_cut2_devs), NULL, - 0, ab8500->domain); + ret = devm_mfd_add_devices(ab8500->dev, 0, + ab8540_cut2_devs, + ARRAY_SIZE(ab8540_cut2_devs), + NULL, 0, ab8500->domain); } else if (is_ab8505(ab8500)) - ret = mfd_add_devices(ab8500->dev, 0, ab8505_devs, - ARRAY_SIZE(ab8505_devs), NULL, - 0, ab8500->domain); + ret = devm_mfd_add_devices(ab8500->dev, 0, ab8505_devs, + ARRAY_SIZE(ab8505_devs), NULL, + 0, ab8500->domain); else - ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, - ARRAY_SIZE(ab8500_devs), NULL, - 0, ab8500->domain); + ret = devm_mfd_add_devices(ab8500->dev, 0, ab8500_devs, + ARRAY_SIZE(ab8500_devs), NULL, + 0, ab8500->domain); if (ret) return ret; /* Add battery management devices */ - ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs, - ARRAY_SIZE(ab8500_bm_devs), NULL, - 0, ab8500->domain); + ret = devm_mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs, + ARRAY_SIZE(ab8500_bm_devs), NULL, + 0, ab8500->domain); if (ret) dev_err(ab8500->dev, "error adding bm devices\n"); From 42a947779e7d04f11c61abedb25799b1366a1b9d Mon Sep 17 00:00:00 2001 From: Denys Romaniuk Date: Mon, 28 Sep 2026 22:58:07 +0300 Subject: [PATCH 62/74] mfd: max77705: Mark PASS2 revision as supported The existing driver takes a safe approach and only probes on tested MAX77705 revisions. The only tested revision at the time was PASS3. PASS2 chips are present in the Samsung Galaxy S10/S10+/S10e devices. Testing on the S10 and S10+ shows the driver operates correctly on PASS2: interrupts are registered and fire, bind and unbind release the I2C device cleanly, and the charger and fuel gauge report plausible values, charging the battery to the same voltage as the vendor kernel, haptic feedback works. Link: https://lore.kernel.org/all/20251023131732.GN475031@google.com/ Signed-off-by: Denys Romaniuk Link: https://patch.msgid.link/20260928-max77705-pass2-supported-v2-1-c04f3932c182@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/max77705.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/max77705.c b/drivers/mfd/max77705.c index e98c76d6b699..2e9b81f26414 100644 --- a/drivers/mfd/max77705.c +++ b/drivers/mfd/max77705.c @@ -105,7 +105,7 @@ static int max77705_i2c_probe(struct i2c_client *i2c) return -ENODEV; pmic_rev = pmic_rev_value & MAX77705_REVISION_MASK; - if (pmic_rev != MAX77705_PASS3) + if (pmic_rev != MAX77705_PASS2 && pmic_rev != MAX77705_PASS3) return dev_err_probe(dev, -ENODEV, "Rev.0x%x is not tested\n", pmic_rev); /* Active Discharge Enable */ From cc6597f4e3b752de42249879e0c7054fd23628a1 Mon Sep 17 00:00:00 2001 From: Ryan Brue Date: Mon, 28 Sep 2026 15:57:21 -0500 Subject: [PATCH 63/74] mfd: mt6397-core: Add mt6397 AUXADC support The mt6397 PMIC includes an AUXADC. Register the AUXADC in the mt6397 devices array to allow the corresponding driver to probe using compatible string. Assisted-by: LLM Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Ryan Brue Link: https://patch.msgid.link/20260928-rbrue-suez-upstreaming-mt6397-auxadc-v4-3-23533a0d30e3@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/mt6397-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index 9ec951996588..5d5f816492aa 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -261,6 +261,9 @@ static const struct mfd_cell mt6359_devs[] = { static const struct mfd_cell mt6397_devs[] = { { + .name = "mt6397-auxadc", + .of_compatible = "mediatek,mt6397-auxadc", + }, { .name = "mt6397-rtc", .num_resources = ARRAY_SIZE(mt6397_rtc_resources), .resources = mt6397_rtc_resources, From 3606288af5e8c09a14ab5b19e121feec75296354 Mon Sep 17 00:00:00 2001 From: Ryan Brue Date: Thu, 17 Sep 2026 23:57:46 -0500 Subject: [PATCH 64/74] dt-bindings: mfd: mediatek: mt6397: describe the RTC's nvmem layout Four of the MT6397 RTC's alarm registers use only their low bits; the high byte of each is storage the clock and the alarm don't touch. MediaTek's documentation names them RTC_NEW_SPARE0 to RTC_NEW_SPARE3 and assigns the first to a fuel gauge, which is what a battery driver reads at boot so that the reported capacity does not jump across a reboot. They sit in the RTC's always-on domain, so the driver can offer them as a battery-backed nvmem provider. Allow a board to lay cells out over them. Assisted-by: LLM Signed-off-by: Ryan Brue Reviewed-by: Rob Herring (Arm) Reviewed-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20260917-rbrue-suez-upstreaming-mt6397-rtc-nvmem-v1-1-558b8f95cfbc@gmail.com Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/mediatek,mt6397.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml index bbd56e522a75..055625362951 100644 --- a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml @@ -84,6 +84,15 @@ properties: start-year: true + nvmem-layout: + $ref: /schemas/nvmem/layouts/nvmem-layout.yaml + description: + The RTC carries four bytes of storage that neither the clock nor the + alarm uses, in the high half of four of the alarm registers, and + offers them as a battery-backed nvmem provider. MediaTek's + documentation names them RTC_NEW_SPARE0 to RTC_NEW_SPARE3 and gives + the first to a fuel gauge. + required: - compatible From 56d974f3ad074a60bf877d131eab8b1df5b4f069 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Thu, 1 Oct 2026 17:30:23 +0400 Subject: [PATCH 65/74] mfd: bq257xx: Mark self-modifying BQ25792 registers volatile Some BQ25792 configuration registers change without being written: input source detection sets VINDPM and IINDPM at plug-in, plug-in also clears EN_HIZ, the FORCE_* bits clear once their operation starts or completes, EN_ACDRV1/2 follow input path changes, and ADC_EN clears after a one-shot conversion. Reading them from the cache returns stale values. Mark these registers volatile, keeping the cache for the rest. Cc: stable@vger.kernel.org Fixes: 9adbe53e32cd ("mfd: bq257xx: Add BQ25792 support") Signed-off-by: Alexey Charkov Link: https://patch.msgid.link/20261001-bq25792-volatile-v1-1-cb3d4ec8a414@flipper.net Signed-off-by: Lee Jones --- drivers/mfd/bq257xx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mfd/bq257xx.c b/drivers/mfd/bq257xx.c index 054342c60b73..01f5fc97f538 100644 --- a/drivers/mfd/bq257xx.c +++ b/drivers/mfd/bq257xx.c @@ -52,8 +52,18 @@ static const struct regmap_access_table bq25792_writeable_regs = { }; static const struct regmap_range bq25792_volatile_reg_ranges[] = { + regmap_reg_range(BQ25792_REG05_INPUT_VOLTAGE_LIMIT, + BQ25792_REG06_INPUT_CURRENT_LIMIT + 1), + regmap_reg_range(BQ25792_REG0F_CHARGER_CONTROL_0, + BQ25792_REG0F_CHARGER_CONTROL_0), + regmap_reg_range(BQ25792_REG11_CHARGER_CONTROL_2, + BQ25792_REG11_CHARGER_CONTROL_2), + regmap_reg_range(BQ25792_REG13_CHARGER_CONTROL_4, + BQ25792_REG13_CHARGER_CONTROL_4), regmap_reg_range(BQ25792_REG19_ICO_CURRENT_LIMIT, BQ25792_REG27_FAULT_FLAG_1), + regmap_reg_range(BQ25792_REG2E_ADC_CONTROL, + BQ25792_REG2E_ADC_CONTROL), regmap_reg_range(BQ25792_REG31_IBUS_ADC, BQ25792_REG47_DPDM_DRIVER), }; From 4c06f3659671be3c95e18519bf40e32ecde21382 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 30 Sep 2026 13:46:03 +0100 Subject: [PATCH 66/74] dt-bindings: mfd: qcom,spmi-pmic: Add compatibles for pm4125-codec Qualcomm Agatti SoC has PM4125 PMIC, which includes audio codec. Audio codec has TX and RX soundwire slave devices to connect to on-chip soundwire master. Add missing qcom,pm4125-codec compatible to pattern of audio-codec node properties in mfd qcom,spmi-pmic schema to complete the audio codec support. Signed-off-by: Srinivas Kandagatla Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260930124607.1109167-2-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml index f6534e9e9912..7a889d888383 100644 --- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml @@ -148,7 +148,9 @@ patternProperties: "^audio-codec@[0-9a-f]+$": type: object - $ref: /schemas/sound/qcom,pm8916-wcd-analog-codec.yaml# + oneOf: + - $ref: /schemas/sound/qcom,pm4125-codec.yaml# + - $ref: /schemas/sound/qcom,pm8916-wcd-analog-codec.yaml# "^battery@[0-9a-f]+$": type: object From 0473a5b0663663b75573bf284beb77459e07c461 Mon Sep 17 00:00:00 2001 From: Radiy Zhambakiev Date: Fri, 2 Oct 2026 11:36:52 +0000 Subject: [PATCH 67/74] mfd: tps65217: Fix NULL pointer dereference on IRQ init failure tps65217_probe() ignores the return value of tps65217_irq_init(), so when the irq domain creation fails the probe still completes and the driver ends up bound with a NULL tps->irq_domain. Unloading the module then makes tps65217_remove() call irq_domain_remove() on the NULL pointer and oops the kernel. On top of that, irq_find_mapping() may fall back to the default irq domain and dispose of mappings that belong to other interrupt controllers. Check the return value and abort the probe on failure so the error is reported and no inconsistent state is left for removal. Fixes: 6556bdacf646fcaa ("mfd: tps65217: Add support for IRQs") Cc: stable@vger.kernel.org Signed-off-by: Radiy Zhambakiev Reviewed-by: Andreas Kemnade Link: https://patch.msgid.link/20261002113624.178588-2-r.zhambakiev@prosoftsystems.ru Signed-off-by: Lee Jones --- drivers/mfd/tps65217.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index c240fac0ede7..2d04d9e0ae29 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -333,7 +333,9 @@ static int tps65217_probe(struct i2c_client *client) } if (client->irq) { - tps65217_irq_init(tps, client->irq); + ret = tps65217_irq_init(tps, client->irq); + if (ret) + return ret; } else { int i; From 66ed12c1e5a986bee1dc63f9bd9c7f068138a69a Mon Sep 17 00:00:00 2001 From: Radiy Zhambakiev Date: Fri, 2 Oct 2026 11:36:53 +0000 Subject: [PATCH 68/74] mfd: tps65217: Check return value when masking interrupt sources tps65217_irq_init() ignores the error returned by tps65217_set_bits() when masking all interrupt sources. A failed register write leaves the driver's software mask out of sync with the hardware and may result in spurious interrupts. Check the return value and propagate the error to the caller. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 6556bdacf646fcaa ("mfd: tps65217: Add support for IRQs") Cc: stable@vger.kernel.org Signed-off-by: Radiy Zhambakiev Link: https://patch.msgid.link/20261002113624.178588-3-r.zhambakiev@prosoftsystems.ru Signed-off-by: Lee Jones --- drivers/mfd/tps65217.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index 2d04d9e0ae29..ee640e3fcf7b 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -155,8 +155,12 @@ static int tps65217_irq_init(struct tps65217 *tps, int irq) /* Mask all interrupt sources */ tps->irq_mask = TPS65217_INT_MASK; - tps65217_set_bits(tps, TPS65217_REG_INT, TPS65217_INT_MASK, - TPS65217_INT_MASK, TPS65217_PROTECT_NONE); + ret = tps65217_set_bits(tps, TPS65217_REG_INT, TPS65217_INT_MASK, + TPS65217_INT_MASK, TPS65217_PROTECT_NONE); + if (ret) { + dev_err(tps->dev, "Failed to mask interrupt sources: %d\n", ret); + return ret; + } tps->irq_domain = irq_domain_create_linear(dev_fwnode(tps->dev), TPS65217_NUM_IRQ, &tps65217_irq_domain_ops, tps); From cffd44d2d89524e2dda6adc7b0dd59363be1c8e6 Mon Sep 17 00:00:00 2001 From: Radiy Zhambakiev Date: Fri, 2 Oct 2026 11:36:54 +0000 Subject: [PATCH 69/74] mfd: tps65217: Use devres for IRQ domain and wake teardown Removing the parent device used to tear the IRQ domain down in tps65217_remove() before the MFD children are unbound: the i2c core only releases the parent's devres after the remove callback returns, whereas devm_mfd_add_devices() registered its own devres action which unbinds the children. Children that requested interrupts from the domain (e.g. tps65217-charger) therefore call free_irq() on virtual IRQs whose descriptors have already been disposed. With CONFIG_SPARSE_IRQ free_irq() then returns early without stopping the threaded handler, leaking the irqaction, the IRQ kthread and a module reference. Register the domain teardown as a devres action right after the domain is created instead. Devres actions are released in reverse order of registration, so the MFD children release their IRQs first, then the parent interrupt is freed and its thread stopped, and only then is the domain torn down. The same ordering covers probe failure, which no longer needs manual cleanup, and the remove callback can be dropped entirely. Balance the enable_irq_wake() in tps65217_irq_init() by registering disable_irq_wake() as a devres action as well. Only do so when enabling actually succeeded, since the parent chip may not support setting wake-up. Fixes: 6556bdacf646fcaa ("mfd: tps65217: Add support for IRQs") Cc: stable@vger.kernel.org Signed-off-by: Radiy Zhambakiev Link: https://patch.msgid.link/20261002113624.178588-4-r.zhambakiev@prosoftsystems.ru Signed-off-by: Lee Jones --- drivers/mfd/tps65217.c | 57 +++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index ee640e3fcf7b..569c28c8b20f 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -146,6 +146,31 @@ static const struct irq_domain_ops tps65217_irq_domain_ops = { .map = tps65217_irq_map, }; +static void tps65217_irq_cleanup(void *data) +{ + struct tps65217 *tps = data; + unsigned int virq; + + if (!tps->irq_domain) + return; + + for (int i = 0; i < TPS65217_NUM_IRQ; i++) { + virq = irq_find_mapping(tps->irq_domain, i); + if (virq) + irq_dispose_mapping(virq); + } + + irq_domain_remove(tps->irq_domain); + tps->irq_domain = NULL; +} + +static void tps65217_irq_wake_disable(void *data) +{ + unsigned long irq = (unsigned long)data; + + disable_irq_wake(irq); +} + static int tps65217_irq_init(struct tps65217 *tps, int irq) { int ret; @@ -169,6 +194,10 @@ static int tps65217_irq_init(struct tps65217 *tps, int irq) return -ENOMEM; } + ret = devm_add_action_or_reset(tps->dev, tps65217_irq_cleanup, tps); + if (ret) + return ret; + ret = devm_request_threaded_irq(tps->dev, irq, NULL, tps65217_irq_thread, IRQF_ONESHOT, "tps65217-irq", tps); @@ -178,7 +207,16 @@ static int tps65217_irq_init(struct tps65217 *tps, int irq) return ret; } - enable_irq_wake(irq); + ret = enable_irq_wake(irq); + if (ret) { + dev_warn(tps->dev, "failed to enable IRQ wake: %d\n", ret); + return 0; + } + + ret = devm_add_action_or_reset(tps->dev, tps65217_irq_wake_disable, + (void *)(unsigned long)irq); + if (ret) + return ret; return 0; } @@ -379,22 +417,6 @@ static int tps65217_probe(struct i2c_client *client) return 0; } -static void tps65217_remove(struct i2c_client *client) -{ - struct tps65217 *tps = i2c_get_clientdata(client); - unsigned int virq; - int i; - - for (i = 0; i < TPS65217_NUM_IRQ; i++) { - virq = irq_find_mapping(tps->irq_domain, i); - if (virq) - irq_dispose_mapping(virq); - } - - irq_domain_remove(tps->irq_domain); - tps->irq_domain = NULL; -} - static const struct i2c_device_id tps65217_id_table[] = { {"tps65217", TPS65217}, { /* sentinel */ } @@ -408,7 +430,6 @@ static struct i2c_driver tps65217_driver = { }, .id_table = tps65217_id_table, .probe = tps65217_probe, - .remove = tps65217_remove, }; static int __init tps65217_init(void) From 194da454d5e204e8bb995359e995fac232fb6da5 Mon Sep 17 00:00:00 2001 From: Thierry Chatard Date: Fri, 2 Oct 2026 09:33:42 -0700 Subject: [PATCH 70/74] mfd: intel-lpss: Add resource conflict quirk for Dell Latitude 5285 The Dell Latitude 5285 2-in-1 has a BIOS bug where the ACPI GEXP device and the I2C4 controller (INT3446) both claim the same MMIO region via the shared SB04 variable. This causes intel_lpss_acpi to fail binding to I2C4 with -EBUSY, preventing the front camera (OV5670) sensor from being registered. Match the affected machine by DMI and the affected device by ACPI ID, carrying QUIRK_IGNORE_RESOURCE_CONFLICTS in the ACPI match data and applying it to the per-device info in probe. This follows the quirk organisation used by the PCI counterpart. Assisted-by: LLM Reviewed-by: Andy Shevchenko Reviewed-by: Sakari Ailus Signed-off-by: Thierry Chatard Link: https://patch.msgid.link/20261002163350.1248431-2-tchatard@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss-acpi.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index d4b24a717848..3f90942f3b36 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -8,7 +8,9 @@ * Mika Westerberg */ +#include #include +#include #include #include #include @@ -171,6 +173,27 @@ static const struct acpi_device_id intel_lpss_acpi_ids[] = { }; MODULE_DEVICE_TABLE(acpi, intel_lpss_acpi_ids); +/* + * Devices that need a quirk applied only on the machines matched by + * intel_lpss_quirk_dmi[] below. The quirk to apply is carried in the + * matching entry's driver_data. + */ +static const struct acpi_device_id intel_lpss_acpi_quirk_ids[] = { + /* Dell Latitude 5285: ACPI GEXP device conflicts with I2C4 (INT3446) */ + { .id = "INT3446", .driver_data = QUIRK_IGNORE_RESOURCE_CONFLICTS }, + { } +}; + +static const struct dmi_system_id intel_lpss_quirk_dmi[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5285"), + }, + }, + { } +}; + static int intel_lpss_acpi_probe(struct platform_device *pdev) { const struct intel_lpss_platform_info *data; @@ -185,6 +208,15 @@ static int intel_lpss_acpi_probe(struct platform_device *pdev) if (!info) return -ENOMEM; + /* Apply per-device quirks that are only needed on certain machines */ + if (dmi_check_system(intel_lpss_quirk_dmi)) { + const struct acpi_device_id *id; + + id = acpi_match_device(intel_lpss_acpi_quirk_ids, &pdev->dev); + if (id) + info->quirks |= id->driver_data; + } + /* No need to check mem and irq here as intel_lpss_probe() does it for us */ info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); info->irq = platform_get_irq(pdev, 0); From 495d5f41a6161dffafe61ac458459c62d440344a Mon Sep 17 00:00:00 2001 From: "D. Manresa" Date: Fri, 2 Oct 2026 09:33:43 -0700 Subject: [PATCH 71/74] mfd: intel-lpss: Add Dell Latitude 7275 to the resource conflict quirk The Dell Latitude 7275 2-in-1 has the same BIOS bug as the Latitude 5285: the ACPI GEXP device declares an OperationRegion (BAR0) on the GNVS variable SB04 and the I2C4 controller (INT3446) builds its _CRS from that same variable, so intel_lpss_acpi fails to bind the controller with -EBUSY and the front camera sensor (OV5670, INT3479) is never registered unless acpi_enforce_resources=lax is passed. Add the machine to the DMI table so QUIRK_IGNORE_RESOURCE_CONFLICTS is applied to INT3446 on it as well. Assisted-by: LLM Reviewed-by: Hans de Goede Reviewed-by: Sakari Ailus Reviewed-by: Andy Shevchenko Signed-off-by: D. Manresa Signed-off-by: Thierry Chatard Link: https://patch.msgid.link/20261002163350.1248431-3-tchatard@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss-acpi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index 3f90942f3b36..720adb0ee21f 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -191,6 +191,12 @@ static const struct dmi_system_id intel_lpss_quirk_dmi[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5285"), }, }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 7275"), + }, + }, { } }; From c2f8288a58e22feeeb9c444bbaff179b0058e459 Mon Sep 17 00:00:00 2001 From: Quchaosheng Date: Wed, 23 Sep 2026 16:30:01 +0800 Subject: [PATCH 72/74] dt-bindings: mfd: rockchip,rk805: Convert pinctrl to DT schema pinctrl-rk805.txt describes the pinmux controller that is part of the Rockchip RK805 PMIC. The PMIC itself is already described by rockchip,rk805.yaml, so the pinmux part belongs there as well: the pinctrl node is a child of the PMIC node, not a separate device. Fold the pinmux description into rockchip,rk805.yaml and drop the standalone text binding. While doing so, add the missing child node schema. The rockchip,rk805.yaml schema ends with 'additionalProperties: false' but did not describe the pin configuration child nodes at all, so a valid pinmux node like the one shown in pinctrl-rk805.txt was rejected: pmic@18 (rockchip,rk805): 'rk805-default-pins' does not match any of the regexes: '^pinctrl-[0-9]+$' The pin configuration nodes require 'pins'; without it a node that names no pin is accepted silently, while the text binding it replaces listed 'pins' as required. The RK805 exposes two output-only GPIOs, so 'pins' is an enum of gpio0 and gpio1 and a typo such as "gpio2" is caught. The nodes may be direct children of the PMIC, or sit one level deeper inside a "pinmux" grouping node, which is the layout the example of the text binding used. pinctrl-bindings.txt allows the configuration nodes to be grandchildren of the pin controller, and the driver reaches both layouts: pinconf_generic_dt_node_to_map() parses the referenced node and then walks its children. The example shows both. The example also regains the gpio-controller and #gpio-cells lines that the example of the text binding had. Reported-by: Sashiko Link: https://sashiko.dev/#/patchset/20260923034245.396417-1-quchaosheng000406@163.com?part=1 Link: https://sashiko.dev/#/patchset/20260923062746.516666-1-quchaosheng000406@163.com?part=1 Link: https://sashiko.dev/#/patchset/20260923074337.575122-1-quchaosheng000406@163.com?part=1 Assisted-by: LLM Signed-off-by: Quchaosheng Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260923083001.606935-1-quchaosheng000406@163.com Signed-off-by: Lee Jones --- .../bindings/mfd/rockchip,rk805.yaml | 60 +++++++++++++++++- .../bindings/pinctrl/pinctrl-rk805.txt | 63 ------------------- 2 files changed, 59 insertions(+), 64 deletions(-) delete mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-rk805.txt diff --git a/Documentation/devicetree/bindings/mfd/rockchip,rk805.yaml b/Documentation/devicetree/bindings/mfd/rockchip,rk805.yaml index da2391530c16..ecd9846dffec 100644 --- a/Documentation/devicetree/bindings/mfd/rockchip,rk805.yaml +++ b/Documentation/devicetree/bindings/mfd/rockchip,rk805.yaml @@ -77,6 +77,16 @@ properties: description: The input supply for LDO_REG3. + # pinctrl-bindings.txt allows the pin configuration nodes to be grandchildren + # of the pin controller, and the text binding this replaces used that layout: + # a "pinmux" node holding the nodes that carry "pins". The driver reaches both + # layouts, because pinconf_generic_dt_node_to_map() parses the referenced node + # and then walks its children. + pinmux: + type: object + additionalProperties: + $ref: '#/$defs/rk805-pin-config' + regulators: type: object patternProperties: @@ -86,6 +96,38 @@ properties: unevaluatedProperties: false unevaluatedProperties: false +$defs: + rk805-pin-config: + type: object + description: + The RK805 has two output-only GPIOs which can be muxed to the GPIO + function and driven low or high at boot. + additionalProperties: false + + properties: + pins: + description: + List of GPIO pins to configure. + items: + enum: [gpio0, gpio1] + minItems: 1 + maxItems: 2 + + function: + description: + The RK805 only supports the GPIO function. + enum: [gpio] + + output-low: true + output-high: true + + required: + - pins + +patternProperties: + '-pins$': + $ref: '#/$defs/rk805-pin-config' + allOf: - if: properties: @@ -125,9 +167,11 @@ examples: interrupt-parent = <&gpio2>; interrupts = ; pinctrl-names = "default"; - pinctrl-0 = <&pmic_int_l>; + pinctrl-0 = <&pmic_int_l>, <&rk805_default>, <&rk805_grouped>; rockchip,system-power-controller; wakeup-source; + gpio-controller; + #gpio-cells = <2>; #clock-cells = <0>; vcc1-supply = <&vcc_sys>; @@ -137,6 +181,20 @@ examples: vcc5-supply = <&vcc_io>; vcc6-supply = <&vcc_io>; + rk805_default: rk805-default-pins { + pins = "gpio0"; + function = "gpio"; + output-high; + }; + + rk805_grouped: pinmux { + gpio01 { + pins = "gpio1"; + function = "gpio"; + output-low; + }; + }; + regulators { vdd_logic: DCDC_REG1 { regulator-name = "vdd_logic"; diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-rk805.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-rk805.txt deleted file mode 100644 index 6ad49e51c72e..000000000000 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-rk805.txt +++ /dev/null @@ -1,63 +0,0 @@ -Pincontrol driver for RK805 Power management IC. - -RK805 has 2 pins which can be configured as GPIO output only. - -Please refer file -for details of the common pinctrl bindings used by client devices, -including the meaning of the phrase "pin configuration node". - -Optional Pinmux properties: --------------------------- -Following properties are required if default setting of pins are required -at boot. -- pinctrl-names: A pinctrl state named per . -- pinctrl[0...n]: Properties to contain the phandle for pinctrl states per - . - -The pin configurations are defined as child of the pinctrl states node. Each -sub-node have following properties: - -Required properties: ------------------- -- #gpio-cells: Should be two. The first cell is the pin number and the - second is the GPIO flags. - -- gpio-controller: Marks the device node as a GPIO controller. - -- pins: List of pins. Valid values of pins properties are: gpio0, gpio1. - -First 2 properties must be added in the RK805 PMIC node, documented in -Documentation/devicetree/bindings/mfd/rockchip,rk808.yaml - -Optional properties: -------------------- -Following are optional properties defined as pinmux DT binding document -. Absence of properties will leave the configuration -on default. - function, - output-low, - output-high. - -Valid values for function properties are: gpio. - -There are also not customised properties for any GPIO. - -Example: --------- -rk805: rk805@18 { - compatible = "rockchip,rk805"; - ... - gpio-controller; - #gpio-cells = <2>; - - pinctrl-names = "default"; - pinctrl-0 = <&pmic_int_l>, <&rk805_default>; - - rk805_default: pinmux { - gpio01 { - pins = "gpio0", "gpio1"; - function = "gpio"; - output-high; - }; - }; -}; From 1c92a441330ce392fb17b348c84fa50c60493b20 Mon Sep 17 00:00:00 2001 From: Long Zhao Date: Fri, 9 Oct 2026 19:00:23 +0800 Subject: [PATCH 73/74] dt-bindings: mfd: syscon: Add Ambarella CV75 secure scratchpad Allow ambarella,cv75-sec-scratchpad as a syscon for the shared secure scratchpad registers on CV75. Signed-off-by: Long Zhao Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20261009-cv75-soc-v1-2-808330e93ff1@ambarella.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/syscon.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml index 945a168b613d..6c62f2041a1f 100644 --- a/Documentation/devicetree/bindings/mfd/syscon.yaml +++ b/Documentation/devicetree/bindings/mfd/syscon.yaml @@ -32,6 +32,7 @@ properties: - allwinner,sun50i-a64-system-controller - altr,l3regs - altr,sdr-ctl + - ambarella,cv75-sec-scratchpad - amd,pensando-elba-syscon - amlogic,meson-mx-assist - amlogic,meson-mx-bootrom From c8132247fe4df52e9982d74c986bb199d792b85b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 21 Sep 2026 00:48:10 +0200 Subject: [PATCH 74/74] dt-bindings: mfd: db8500-prcmu: Deprecate EPOD regulators Deprecate the db8500-prcmu-regulators node and make it optional as EPOD control moves to generic power domains. External consumers of VAPE and VSMPS2 now use the actual AB8500 or AB8505 buck regulators. Stop requiring the individual regulator child nodes while retaining their definitions for existing device trees. Assisted-by: LLM Acked-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260921-ux500-power-domains-v7-1-v3-4-9352be6b86b1@kernel.org Signed-off-by: Lee Jones --- .../bindings/mfd/stericsson,db8500-prcmu.yaml | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/stericsson,db8500-prcmu.yaml b/Documentation/devicetree/bindings/mfd/stericsson,db8500-prcmu.yaml index 4edd4a3bab88..ce9060340e0c 100644 --- a/Documentation/devicetree/bindings/mfd/stericsson,db8500-prcmu.yaml +++ b/Documentation/devicetree/bindings/mfd/stericsson,db8500-prcmu.yaml @@ -58,6 +58,7 @@ properties: const: 2 db8500-prcmu-regulators: + deprecated: true description: Node describing the DB8500 regulators. These are mainly power rails inside the silicon but some of those are also routed out to external pins. @@ -207,26 +208,6 @@ properties: required: - compatible - - db8500_vape - - db8500_varm - - db8500_vmodem - - db8500_vpll - - db8500_vsmps1 - - db8500_vsmps2 - - db8500_vsmps3 - - db8500_vrf1 - - db8500_sva_mmdsp - - db8500_sva_mmdsp_ret - - db8500_sva_pipe - - db8500_sia_mmdsp - - db8500_sia_mmdsp_ret - - db8500_sia_pipe - - db8500_sga - - db8500_b2r2_mcde - - db8500_esram12 - - db8500_esram12_ret - - db8500_esram34 - - db8500_esram34_ret additionalProperties: false @@ -294,6 +275,5 @@ required: - interrupts - interrupt-controller - '#interrupt-cells' - - db8500-prcmu-regulators additionalProperties: false