-/* $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
*
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)
{
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;
}
{
struct regulator_device *rd;
uint32_t old, delta;
- int error;
+ int error, node;
LIST_FOREACH(rd, ®ulator_devices, rd_list) {
if (rd->rd_phandle == phandle)
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;
+}