regulator: Introduce handling for system-critical under-voltage events
authorOleksij Rempel <o.rempel@pengutronix.de>
Thu, 26 Oct 2023 14:48:21 +0000 (16:48 +0200)
committerMark Brown <broonie@kernel.org>
Mon, 13 Nov 2023 01:26:25 +0000 (01:26 +0000)
Handle under-voltage events for crucial regulators to maintain system
stability and avoid issues during power drops.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Link: https://lore.kernel.org/r/20231026144824.4065145-3-o.rempel@pengutronix.de
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/regulator/core.c
drivers/regulator/of_regulator.c
include/linux/regulator/machine.h

index 3137e40fcd3e0586a81b1626304ab955a87695d4..a072f721f288130c2a6b9375a2fb1915e2091454 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/delay.h>
 #include <linux/gpio/consumer.h>
 #include <linux/of.h>
+#include <linux/reboot.h>
 #include <linux/regmap.h>
 #include <linux/regulator/of_regulator.h>
 #include <linux/regulator/consumer.h>
@@ -5061,6 +5062,41 @@ void regulator_bulk_free(int num_consumers,
 }
 EXPORT_SYMBOL_GPL(regulator_bulk_free);
 
+/**
+ * regulator_handle_critical - Handle events for system-critical regulators.
+ * @rdev: The regulator device.
+ * @event: The event being handled.
+ *
+ * This function handles critical events such as under-voltage, over-current,
+ * and unknown errors for regulators deemed system-critical. On detecting such
+ * events, it triggers a hardware protection shutdown with a defined timeout.
+ */
+static void regulator_handle_critical(struct regulator_dev *rdev,
+                                     unsigned long event)
+{
+       const char *reason = NULL;
+
+       if (!rdev->constraints->system_critical)
+               return;
+
+       switch (event) {
+       case REGULATOR_EVENT_UNDER_VOLTAGE:
+               reason = "System critical regulator: voltage drop detected";
+               break;
+       case REGULATOR_EVENT_OVER_CURRENT:
+               reason = "System critical regulator: over-current detected";
+               break;
+       case REGULATOR_EVENT_FAIL:
+               reason = "System critical regulator: unknown error";
+       }
+
+       if (!reason)
+               return;
+
+       hw_protection_shutdown(reason,
+                              REGULATOR_DEF_UV_LESS_CRITICAL_WINDOW_MS);
+}
+
 /**
  * regulator_notifier_call_chain - call regulator event notifier
  * @rdev: regulator source
@@ -5073,6 +5109,8 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free);
 int regulator_notifier_call_chain(struct regulator_dev *rdev,
                                  unsigned long event, void *data)
 {
+       regulator_handle_critical(rdev, event);
+
        _notifier_call_chain(rdev, event, data);
        return NOTIFY_DONE;
 
index 1b65e5e4e40ffc281b69b632299cfaa6b034473a..3bdd6f1919a4c8a63fd3af695dda8705d475b416 100644 (file)
@@ -131,6 +131,8 @@ static int of_get_regulation_constraints(struct device *dev,
                constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS;
 
        constraints->pull_down = of_property_read_bool(np, "regulator-pull-down");
+       constraints->system_critical = of_property_read_bool(np,
+                                               "system-critical-regulator");
 
        if (of_property_read_bool(np, "regulator-allow-bypass"))
                constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS;
index 621b7f4a36395513180cb1c98df7ea77bbb8d3de..e0ddfb5593c9257b733662dfe2066c90273c4e1b 100644 (file)
@@ -49,6 +49,13 @@ struct regulator;
 #define DISABLE_IN_SUSPEND     1
 #define ENABLE_IN_SUSPEND      2
 
+/*
+ * Default time window (in milliseconds) following a critical under-voltage
+ * event during which less critical actions can be safely carried out by the
+ * system.
+ */
+#define REGULATOR_DEF_UV_LESS_CRITICAL_WINDOW_MS       10
+
 /* Regulator active discharge flags */
 enum regulator_active_discharge {
        REGULATOR_ACTIVE_DISCHARGE_DEFAULT,
@@ -127,6 +134,8 @@ struct notification_limit {
  * @ramp_disable: Disable ramp delay when initialising or when setting voltage.
  * @soft_start: Enable soft start so that voltage ramps slowly.
  * @pull_down: Enable pull down when regulator is disabled.
+ * @system_critical: Set if the regulator is critical to system stability or
+ *                   functionality.
  * @over_current_protection: Auto disable on over current event.
  *
  * @over_current_detection: Configure over current limits.
@@ -214,6 +223,7 @@ struct regulation_constraints {
        unsigned ramp_disable:1; /* disable ramp delay */
        unsigned soft_start:1;  /* ramp voltage slowly */
        unsigned pull_down:1;   /* pull down resistor when regulator off */
+       unsigned system_critical:1;     /* critical to system stability */
        unsigned over_current_protection:1; /* auto disable on over current */
        unsigned over_current_detection:1; /* notify on over current */
        unsigned over_voltage_detection:1; /* notify on over voltage */