diff options
author | Patrick Wildt <patrick@cvs.openbsd.org> | 2018-08-13 15:14:28 +0000 |
---|---|---|
committer | Patrick Wildt <patrick@cvs.openbsd.org> | 2018-08-13 15:14:28 +0000 |
commit | 20b2cf9afaf84c4ca21c4fb15aa7f2314a94b04b (patch) | |
tree | bdc197fac6aaee0f89f059b626e771c351800a1a /sys/dev | |
parent | f2de1cc25a11b20f0ca5bb4944a04a72cccc9e9a (diff) |
Support GPIO-based voltage regulators.
ok kettenis@
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ofw/ofw_regulator.c | 120 |
1 files changed, 118 insertions, 2 deletions
diff --git a/sys/dev/ofw/ofw_regulator.c b/sys/dev/ofw/ofw_regulator.c index 4c2c7152721..cd916e88139 100644 --- a/sys/dev/ofw/ofw_regulator.c +++ b/sys/dev/ofw/ofw_regulator.c @@ -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, ®ulator_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; +} |