Support GPIO-based voltage regulators.
authorpatrick <patrick@openbsd.org>
Mon, 13 Aug 2018 15:14:27 +0000 (15:14 +0000)
committerpatrick <patrick@openbsd.org>
Mon, 13 Aug 2018 15:14:27 +0000 (15:14 +0000)
ok kettenis@

sys/dev/ofw/ofw_regulator.c

index 4c2c715..cd916e8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ofw_regulator.c,v 1.5 2018/08/02 09:45:17 kettenis Exp $      */
+/*     $OpenBSD: ofw_regulator.c,v 1.6 2018/08/13 15:14:27 patrick Exp $       */
 /*
  * Copyright (c) 2016 Mark Kettenis
  *
@@ -27,6 +27,9 @@
 LIST_HEAD(, regulator_device) regulator_devices =
        LIST_HEAD_INITIALIZER(regulator_devices);
 
+uint32_t regulator_gpio_get_voltage(int);
+int regulator_gpio_set_voltage(int, uint32_t);
+
 void
 regulator_register(struct regulator_device *rd)
 {
@@ -149,6 +152,9 @@ regulator_get_voltage(uint32_t phandle)
        if (OF_is_compatible(node, "regulator-fixed"))
                return OF_getpropint(node, "regulator-min-voltage", 0);
 
+       if (OF_is_compatible(node, "regulator-gpio"))
+               return regulator_gpio_get_voltage(node);
+
        return 0;
 }
 
@@ -157,7 +163,7 @@ regulator_set_voltage(uint32_t phandle, uint32_t voltage)
 {
        struct regulator_device *rd;
        uint32_t old, delta;
-       int error;
+       int error, node;
 
        LIST_FOREACH(rd, &regulator_devices, rd_list) {
                if (rd->rd_phandle == phandle)
@@ -178,5 +184,115 @@ regulator_set_voltage(uint32_t phandle, uint32_t voltage)
                return error;
        }
 
+       node = OF_getnodebyphandle(phandle);
+       if (node == 0)
+               return ENODEV;
+
+       if (OF_is_compatible(node, "regulator-gpio"))
+               return regulator_gpio_set_voltage(node, voltage);
+
        return ENODEV;
 }
+
+uint32_t
+regulator_gpio_get_voltage(int node)
+{
+       uint32_t *gpio, *gpios, *states;
+       uint32_t idx, voltage;
+       size_t glen, slen;
+       int i;
+
+       pinctrl_byname(node, "default");
+
+       if ((glen = OF_getproplen(node, "gpios")) <= 0)
+               return EINVAL;
+       if ((slen = OF_getproplen(node, "states")) <= 0)
+               return EINVAL;
+
+       if (slen % (2 * sizeof(uint32_t)) != 0)
+               return EINVAL;
+
+       gpios = malloc(glen, M_TEMP, M_WAITOK);
+       states = malloc(slen, M_TEMP, M_WAITOK);
+
+       OF_getpropintarray(node, "gpios", gpios, glen);
+       OF_getpropintarray(node, "states", states, slen);
+
+       idx = 0;
+       gpio = gpios;
+       while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
+               idx |= (1 << i);
+               gpio = gpio_controller_next_pin(gpio);
+               i++;
+       }
+
+       voltage = 0;
+       for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
+               if (states[2 * i + 1] == idx) {
+                       voltage = states[2 * i];
+                       break;
+               }
+       }
+       if (i >= slen / (2 * sizeof(uint32_t)))
+               return 0;
+
+       free(gpios, M_TEMP, glen);
+       free(states, M_TEMP, slen);
+
+       return voltage;
+}
+
+int
+regulator_gpio_set_voltage(int node, uint32_t voltage)
+{
+       uint32_t *gpio, *gpios, *states;
+       size_t glen, slen;
+       uint32_t min, max;
+       uint32_t idx;
+       int i;
+
+       pinctrl_byname(node, "default");
+
+       /* Check limits. */
+       min = OF_getpropint(node, "regulator-min-microvolt", 0);
+       max = OF_getpropint(node, "regulator-max-microvolt", 0);
+       if (voltage < min || voltage > max)
+               return EINVAL;
+
+       if ((glen = OF_getproplen(node, "gpios")) <= 0)
+               return EINVAL;
+       if ((slen = OF_getproplen(node, "states")) <= 0)
+               return EINVAL;
+
+       if (slen % (2 * sizeof(uint32_t)) != 0)
+               return EINVAL;
+
+       gpios = malloc(glen, M_TEMP, M_WAITOK);
+       states = malloc(slen, M_TEMP, M_WAITOK);
+
+       OF_getpropintarray(node, "gpios", gpios, glen);
+       OF_getpropintarray(node, "states", states, slen);
+
+       idx = 0;
+       for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
+               if (states[2 * i] < min || states[2 * i] > max)
+                       continue;
+               if (states[2 * i] == voltage)
+                       idx = states[2 * i + 1];
+       }
+       if (i >= slen / (2 * sizeof(uint32_t)))
+               return EINVAL;
+
+       i = 0;
+       gpio = gpios;
+       while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
+               gpio_controller_set_pin(gpio, !!(idx & (1 << i)));
+               gpio = gpio_controller_next_pin(gpio);
+               i++;
+       }
+
+       free(gpios, M_TEMP, glen);
+       free(states, M_TEMP, slen);
+
+       return 0;
+}