From: Axel Haslam <ahas...@baylibre.com>

Some regulator supplies have an over-current pin that is
activated when the hw detects an over current condition.
When this happens, the hardware enters a current limited
mode.

Extend the fixed regulator driver with the ability
to handle irq's from the over-current pin and report
an over current event to the consumers via a regulator
notifier. Also, add device tree bindings to allow to
pass a gpio for over current monitoring.

Signed-off-by: Axel Haslam <ahas...@baylibre.com>
---
 .../bindings/regulator/fixed-regulator.txt         |  4 ++
 drivers/regulator/fixed.c                          | 64 ++++++++++++++++++++++
 include/linux/regulator/consumer.h                 |  5 ++
 include/linux/regulator/fixed.h                    |  3 +
 4 files changed, 76 insertions(+)

diff --git a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt 
b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
index 4fae41d..d20bf67 100644
--- a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
@@ -11,6 +11,8 @@ If this property is missing, the default assumed is Active 
low.
 - gpio-open-drain: GPIO is open drain type.
   If this property is missing then default assumption is false.
 -vin-supply: Input supply name.
+- oc-gpio: Input gpio that signals an over current condition
+- oc-active-high: The polarity of the over current pin is high
 
 Any property defined as part of the core regulator
 binding, defined in regulator.txt, can also be used.
@@ -26,9 +28,11 @@ Example:
                regulator-min-microvolt = <1800000>;
                regulator-max-microvolt = <1800000>;
                gpio = <&gpio1 16 0>;
+               oc-gpio = <&gpio1 18 0>;
                startup-delay-us = <70000>;
                enable-active-high;
                regulator-boot-on;
                gpio-open-drain;
+               oc-active-high;
                vin-supply = <&parent_reg>;
        };
diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c
index 988a747..e7964bb 100644
--- a/drivers/regulator/fixed.c
+++ b/drivers/regulator/fixed.c
@@ -30,10 +30,14 @@
 #include <linux/of_gpio.h>
 #include <linux/regulator/of_regulator.h>
 #include <linux/regulator/machine.h>
+#include <linux/interrupt.h>
 
 struct fixed_voltage_data {
        struct regulator_desc desc;
        struct regulator_dev *dev;
+       int oc_gpio;
+       unsigned has_oc_gpio:1;
+       unsigned oc_high:1;
 };
 
 
@@ -82,6 +86,14 @@ struct fixed_voltage_data {
        if ((config->gpio < 0) && (config->gpio != -ENOENT))
                return ERR_PTR(config->gpio);
 
+       config->oc_gpio = of_get_named_gpio(np, "oc-gpio", 0);
+       if (config->oc_gpio >= 0)
+               config->has_oc_gpio = true;
+       else if (config->oc_gpio != -ENOENT)
+               return ERR_PTR(config->oc_gpio);
+
+       config->oc_high = of_property_read_bool(np, "oc-active-high");
+
        of_property_read_u32(np, "startup-delay-us", &config->startup_delay);
 
        config->enable_high = of_property_read_bool(np, "enable-active-high");
@@ -94,7 +106,34 @@ struct fixed_voltage_data {
        return config;
 }
 
+static irqreturn_t reg_fixed_overcurrent_irq(int irq, void *data)
+{
+       struct fixed_voltage_data *drvdata = data;
+
+       regulator_notifier_call_chain(drvdata->dev,
+                       REGULATOR_EVENT_OVER_CURRENT, NULL);
+
+       return IRQ_HANDLED;
+}
+
+static unsigned int reg_fixed_get_mode(struct regulator_dev *rdev)
+{
+       struct fixed_voltage_data *drvdata = rdev_get_drvdata(rdev);
+       unsigned int ret = REGULATOR_MODE_NORMAL;
+       int oc_value;
+
+       if (!drvdata->has_oc_gpio)
+               return ret;
+
+       oc_value = gpio_get_value(drvdata->oc_gpio);
+       if ((oc_value && drvdata->oc_high) || (!oc_value && !drvdata->oc_high))
+               ret = REGULATOR_MODE_OVERCURRENT;
+
+       return ret;
+}
+
 static struct regulator_ops fixed_voltage_ops = {
+       .get_mode = reg_fixed_get_mode,
 };
 
 static int reg_fixed_voltage_probe(struct platform_device *pdev)
@@ -175,6 +214,31 @@ static int reg_fixed_voltage_probe(struct platform_device 
*pdev)
        cfg.driver_data = drvdata;
        cfg.of_node = pdev->dev.of_node;
 
+       if (config->has_oc_gpio && gpio_is_valid(config->oc_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev,
+                                       config->oc_gpio,
+                                       GPIOF_DIR_IN, "oc_gpio");
+               if (ret) {
+                       pr_err("Failed to request gpio: %d\n", ret);
+                       return ret;
+               }
+
+               ret = devm_request_threaded_irq(&pdev->dev,
+                               gpio_to_irq(config->oc_gpio), NULL,
+                               reg_fixed_overcurrent_irq,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+                               IRQF_ONESHOT,
+                               "over_current", drvdata);
+               if (ret) {
+                       pr_err("Failed to request irq: %d\n", ret);
+                       return ret;
+               }
+
+               drvdata->oc_gpio = config->oc_gpio;
+               drvdata->oc_high = config->oc_high;
+               drvdata->has_oc_gpio = config->has_oc_gpio;
+       }
+
        drvdata->dev = devm_regulator_register(&pdev->dev, &drvdata->desc,
                                               &cfg);
        if (IS_ERR(drvdata->dev)) {
diff --git a/include/linux/regulator/consumer.h 
b/include/linux/regulator/consumer.h
index 6921082..9269217 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -74,6 +74,10 @@
  *             the most noisy and may not be able to handle fast load
  *             switching.
  *
+ * OVERCURRENT Regulator has detected an overcurrent condition, and
+ *             may be limiting the supply output.
+ *
+ *
  * NOTE: Most regulators will only support a subset of these modes. Some
  * will only just support NORMAL.
  *
@@ -84,6 +88,7 @@
 #define REGULATOR_MODE_NORMAL                  0x2
 #define REGULATOR_MODE_IDLE                    0x4
 #define REGULATOR_MODE_STANDBY                 0x8
+#define REGULATOR_MODE_OVERCURRENT             0x10
 
 /*
  * Regulator notifier events.
diff --git a/include/linux/regulator/fixed.h b/include/linux/regulator/fixed.h
index 48918be..79357be 100644
--- a/include/linux/regulator/fixed.h
+++ b/include/linux/regulator/fixed.h
@@ -50,10 +50,13 @@ struct fixed_voltage_config {
        const char *input_supply;
        int microvolts;
        int gpio;
+       int oc_gpio;
        unsigned startup_delay;
        unsigned gpio_is_open_drain:1;
        unsigned enable_high:1;
        unsigned enabled_at_boot:1;
+       unsigned has_oc_gpio:1;
+       unsigned oc_high:1;
        struct regulator_init_data *init_data;
 };
 
-- 
1.9.1

Reply via email to