]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
regulator: tps65219: fix irq_data.rdev not being assigned
authorAlexander Sverdlin <alexander.sverdlin@siemens.com>
Mon, 18 May 2026 08:31:11 +0000 (10:31 +0200)
committerMark Brown <broonie@kernel.org>
Mon, 18 May 2026 09:52:24 +0000 (10:52 +0100)
Commit 64a6b577490c ("regulator: tps65219: Remove debugging helper
function") removed the tps65219_get_rdev_by_name() helper along with
the irq_data.rdev assignment that depended on it. This left
irq_data.rdev uninitialized for all IRQs, causing undefined behavior
when regulator_notifier_call_chain() is called from the IRQ handler:

  Internal error: Oops: 0000000096000004
  pc : regulator_notifier_call_chain
  lr : tps65219_regulator_irq_handler
  Call trace:
   regulator_notifier_call_chain
   tps65219_regulator_irq_handler
   handle_nested_irq
   regmap_irq_thread
   irq_thread_fn
   irq_thread
   kthread
   ret_from_fork

Instead of restoring a dedicated lookup array, restructure the probe
function to combine regulator registration with IRQ registration in
the same loop. This way the rdev returned by devm_regulator_register()
is naturally available for assigning to irq_data.rdev without any
auxiliary data structure.

Non-regulator IRQs (SENSOR, TIMEOUT) that don't correspond to any
registered regulator are registered with rdev=NULL, and the IRQ handler
is protected with a NULL check to avoid crashing.

Cc: stable@vger.kernel.org
Closes: https://lore.kernel.org/all/aBDSTxALaOc-PD7X@gaggiata.pivistrello.it/
Reported-by: Francesco Dolcini <francesco@dolcini.it>
Fixes: 64a6b577490c ("regulator: tps65219: Remove debugging helper function")
Signed-off-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Link: https://patch.msgid.link/20260518083113.2063368-1-alexander.sverdlin@siemens.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/regulator/tps65219-regulator.c

index d77ca486879fd610ce5de29999075dab9c36d93e..324c3a33af8a42e38b3e191f4ee316454e30d44c 100644 (file)
@@ -346,8 +346,9 @@ static irqreturn_t tps65219_regulator_irq_handler(int irq, void *data)
                return IRQ_HANDLED;
        }
 
-       regulator_notifier_call_chain(irq_data->rdev,
-                                     irq_data->type->event, NULL);
+       if (irq_data->rdev)
+               regulator_notifier_call_chain(irq_data->rdev,
+                                             irq_data->type->event, NULL);
 
        dev_err(irq_data->dev, "Error IRQ trap %s for %s\n",
                irq_data->type->event_name, irq_data->type->regulator_name);
@@ -398,14 +399,65 @@ static struct tps65219_chip_data chip_info_table[] = {
        },
 };
 
-static int tps65219_regulator_probe(struct platform_device *pdev)
+static bool tps65219_is_regulator_name(const struct tps65219_chip_data *pmic,
+                                      const char *name)
+{
+       int i;
+
+       for (i = 0; i < pmic->common_rdesc_size; i++)
+               if (!strcmp(pmic->common_rdesc[i].name, name))
+                       return true;
+       for (i = 0; i < pmic->rdesc_size; i++)
+               if (!strcmp(pmic->rdesc[i].name, name))
+                       return true;
+       return false;
+}
+
+static int tps65219_register_irqs(struct platform_device *pdev,
+                                 struct tps65219 *tps,
+                                 struct regulator_dev *rdev,
+                                 struct tps65219_regulator_irq_type *irq_types,
+                                 int nirqs,
+                                 const char *regulator_name)
 {
        struct tps65219_regulator_irq_data *irq_data;
+       int i, irq, error;
+
+       for (i = 0; i < nirqs; i++) {
+               if (strcmp(irq_types[i].regulator_name, regulator_name))
+                       continue;
+
+               irq = platform_get_irq_byname(pdev, irq_types[i].irq_name);
+               if (irq < 0)
+                       return -EINVAL;
+
+               irq_data = devm_kmalloc(tps->dev, sizeof(*irq_data), GFP_KERNEL);
+               if (!irq_data)
+                       return -ENOMEM;
+
+               irq_data->dev = tps->dev;
+               irq_data->type = &irq_types[i];
+               irq_data->rdev = rdev;
+
+               error = devm_request_threaded_irq(tps->dev, irq, NULL,
+                                                 tps65219_regulator_irq_handler,
+                                                 IRQF_ONESHOT,
+                                                 irq_types[i].irq_name,
+                                                 irq_data);
+               if (error)
+                       return dev_err_probe(tps->dev, error,
+                                            "Failed to request %s IRQ %d\n",
+                                            irq_types[i].irq_name, irq);
+       }
+       return 0;
+}
+
+static int tps65219_regulator_probe(struct platform_device *pdev)
+{
        struct tps65219_regulator_irq_type *irq_type;
        struct tps65219_chip_data *pmic;
        struct regulator_dev *rdev;
        int error;
-       int irq;
        int i;
 
        struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent);
@@ -425,6 +477,19 @@ static int tps65219_regulator_probe(struct platform_device *pdev)
                        return dev_err_probe(tps->dev, PTR_ERR(rdev),
                                              "Failed to register %s regulator\n",
                                              pmic->common_rdesc[i].name);
+
+               error = tps65219_register_irqs(pdev, tps, rdev,
+                                              pmic->common_irq_types,
+                                              pmic->common_irq_size,
+                                              pmic->common_rdesc[i].name);
+               if (error)
+                       return error;
+               error = tps65219_register_irqs(pdev, tps, rdev,
+                                              pmic->irq_types,
+                                              pmic->dev_irq_size,
+                                              pmic->common_rdesc[i].name);
+               if (error)
+                       return error;
        }
 
        for (i = 0; i <  pmic->rdesc_size; i++) {
@@ -434,52 +499,42 @@ static int tps65219_regulator_probe(struct platform_device *pdev)
                        return dev_err_probe(tps->dev, PTR_ERR(rdev),
                                             "Failed to register %s regulator\n",
                                             pmic->rdesc[i].name);
+
+               error = tps65219_register_irqs(pdev, tps, rdev,
+                                              pmic->common_irq_types,
+                                              pmic->common_irq_size,
+                                              pmic->rdesc[i].name);
+               if (error)
+                       return error;
+               error = tps65219_register_irqs(pdev, tps, rdev,
+                                              pmic->irq_types,
+                                              pmic->dev_irq_size,
+                                              pmic->rdesc[i].name);
+               if (error)
+                       return error;
        }
 
+       /* Register non-regulator IRQs (TIMEOUT, SENSOR) with rdev=NULL */
        for (i = 0; i < pmic->common_irq_size; ++i) {
                irq_type = &pmic->common_irq_types[i];
-               irq = platform_get_irq_byname(pdev, irq_type->irq_name);
-               if (irq < 0)
-                       return -EINVAL;
-
-               irq_data = devm_kmalloc(tps->dev, sizeof(*irq_data), GFP_KERNEL);
-               if (!irq_data)
-                       return -ENOMEM;
-
-               irq_data->dev = tps->dev;
-               irq_data->type = irq_type;
-               error = devm_request_threaded_irq(tps->dev, irq, NULL,
-                                                 tps65219_regulator_irq_handler,
-                                                 IRQF_ONESHOT,
-                                                 irq_type->irq_name,
-                                                 irq_data);
+               if (tps65219_is_regulator_name(pmic, irq_type->regulator_name))
+                       continue;
+               error = tps65219_register_irqs(pdev, tps, NULL,
+                                              irq_type, 1,
+                                              irq_type->regulator_name);
                if (error)
-                       return dev_err_probe(tps->dev, error,
-                                            "Failed to request %s IRQ %d\n",
-                                            irq_type->irq_name, irq);
+                       return error;
        }
 
        for (i = 0; i < pmic->dev_irq_size; ++i) {
                irq_type = &pmic->irq_types[i];
-               irq = platform_get_irq_byname(pdev, irq_type->irq_name);
-               if (irq < 0)
-                       return -EINVAL;
-
-               irq_data = devm_kmalloc(tps->dev, sizeof(*irq_data), GFP_KERNEL);
-               if (!irq_data)
-                       return -ENOMEM;
-
-               irq_data->dev = tps->dev;
-               irq_data->type = irq_type;
-               error = devm_request_threaded_irq(tps->dev, irq, NULL,
-                                                 tps65219_regulator_irq_handler,
-                                                 IRQF_ONESHOT,
-                                                 irq_type->irq_name,
-                                                 irq_data);
+               if (tps65219_is_regulator_name(pmic, irq_type->regulator_name))
+                       continue;
+               error = tps65219_register_irqs(pdev, tps, NULL,
+                                              irq_type, 1,
+                                              irq_type->regulator_name);
                if (error)
-                       return dev_err_probe(tps->dev, error,
-                                            "Failed to request %s IRQ %d\n",
-                                            irq_type->irq_name, irq);
+                       return error;
        }
 
        return 0;