Module Name: src Committed By: ryo Date: Thu Dec 31 15:12:34 UTC 2020
Modified Files: src/sys/dev/fdt: files.fdt Added Files: src/sys/dev/fdt: pwmregulator.c Log Message: add fdt pwm regulator To generate a diff of this commit: cvs rdiff -u -r1.59 -r1.60 src/sys/dev/fdt/files.fdt cvs rdiff -u -r0 -r1.1 src/sys/dev/fdt/pwmregulator.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/fdt/files.fdt diff -u src/sys/dev/fdt/files.fdt:1.59 src/sys/dev/fdt/files.fdt:1.60 --- src/sys/dev/fdt/files.fdt:1.59 Mon Dec 28 20:29:57 2020 +++ src/sys/dev/fdt/files.fdt Thu Dec 31 15:12:33 2020 @@ -1,4 +1,4 @@ -# $NetBSD: files.fdt,v 1.59 2020/12/28 20:29:57 thorpej Exp $ +# $NetBSD: files.fdt,v 1.60 2020/12/31 15:12:33 ryo Exp $ include "external/bsd/libfdt/conf/files.libfdt" @@ -21,6 +21,10 @@ device gregulator attach gregulator at fdt file dev/fdt/gpioregulator.c gregulator +device pregulator +attach pregulator at fdt +file dev/fdt/pwmregulator.c pregulator + device fclock: clk attach fclock at fdt file dev/fdt/fixedclock.c fclock Added files: Index: src/sys/dev/fdt/pwmregulator.c diff -u /dev/null src/sys/dev/fdt/pwmregulator.c:1.1 --- /dev/null Thu Dec 31 15:12:34 2020 +++ src/sys/dev/fdt/pwmregulator.c Thu Dec 31 15:12:33 2020 @@ -0,0 +1,309 @@ +/* $NetBSD: pwmregulator.c,v 1.1 2020/12/31 15:12:33 ryo Exp $ */ + +/* + * Copyright (c) 2020 Ryo Shimizu <r...@nerv.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: pwmregulator.c,v 1.1 2020/12/31 15:12:33 ryo Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kmem.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/pwm/pwmvar.h> + +static int pwmregulator_match(device_t, cfdata_t, void *); +static void pwmregulator_attach(device_t, device_t, void *); + +/* fdtbus_regulator_controller_func callback */ +static int pwmregulator_acquire(device_t); +static void pwmregulator_release(device_t); +static int pwmregulator_enable(device_t, bool); +static int pwmregulator_set_voltage(device_t, u_int, u_int); +static int pwmregulator_get_voltage(device_t, u_int *); + +static const struct fdtbus_regulator_controller_func pwmregulator_funcs = { + .acquire = pwmregulator_acquire, + .release = pwmregulator_release, + .enable = pwmregulator_enable, + .set_voltage = pwmregulator_set_voltage, + .get_voltage = pwmregulator_get_voltage +}; + +struct voltage_duty { + uint32_t microvolt; + uint32_t duty; /* percentage; 0-100 */ +}; + +struct pwmregulator_softc { + device_t sc_dev; + pwm_tag_t sc_pwm; + struct voltage_duty *sc_voltage_table; + int sc_voltage_table_num; + int sc_phandle; + uint32_t sc_microvolt_min; + uint32_t sc_microvolt_max; + uint32_t sc_dutycycle_unit; + uint32_t sc_dutycycle_range[2]; + bool sc_always_on; + bool sc_boot_on; +}; + +CFATTACH_DECL_NEW(pregulator, sizeof(struct pwmregulator_softc), + pwmregulator_match, pwmregulator_attach, NULL, NULL); + +static const char * const compatible[] = { + "pwm-regulator", + NULL +}; + +static int +pwmregulator_match(device_t parent, cfdata_t cf, void *aux) +{ + const struct fdt_attach_args *faa = aux; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +pwmregulator_attach(device_t parent, device_t self, void *aux) +{ + struct pwmregulator_softc * const sc = device_private(self); + const struct fdt_attach_args *faa = aux; + const int phandle = faa->faa_phandle; + int len; + char *name; + + sc->sc_dev = self; + sc->sc_phandle = phandle; + + aprint_naive("\n"); + len = OF_getproplen(phandle, "regulator-name"); + if (len > 0) { + name = kmem_zalloc(len, KM_SLEEP); + if (OF_getprop(phandle, "regulator-name", name, len) == len) + aprint_normal(": %s\n", name); + else + aprint_normal("\n"); + kmem_free(name, len); + } else { + aprint_normal("\n"); + } + + if (of_getprop_uint32(phandle, "regulator-min-microvolt", + &sc->sc_microvolt_min) != 0) { + aprint_error_dev(sc->sc_dev, + "missing regulator-min-microvolt properties\n"); + return; + } + if (of_getprop_uint32(phandle, "regulator-max-microvolt", + &sc->sc_microvolt_max) != 0) { + aprint_error_dev(sc->sc_dev, + "missing regulator-max-microvolt properties\n"); + return; + } + + if (of_getprop_uint32(phandle, "pwm-dutycycle-unit", + &sc->sc_dutycycle_unit) != 0) + sc->sc_dutycycle_unit = 100; + + len = of_getprop_uint32_array(phandle, "pwm-dutycycle-range", + sc->sc_dutycycle_range, 2); + if (of_getprop_uint32_array(phandle, "pwm-dutycycle-range", + sc->sc_dutycycle_range, 2) != 0) { + sc->sc_dutycycle_range[0] = 0; + sc->sc_dutycycle_range[1] = 100; + } + + len = OF_getproplen(phandle, "voltage-table"); + if (len > 0) { + struct voltage_duty *voltage_table = kmem_zalloc(len, KM_SLEEP); + if (of_getprop_uint32_array(phandle, "voltage-table", + (uint32_t *)voltage_table, len / sizeof(uint32_t)) == 0) { + sc->sc_voltage_table = voltage_table; + sc->sc_voltage_table_num = + len / sizeof(struct voltage_duty); +#ifdef PWMREGULATOR_DEBUG + for (int i = 0; i < sc->sc_voltage_table_num; i++) { + aprint_debug_dev(sc->sc_dev, + "VoltageTable[%d]: %uuV = Duty:%u%%\n", i, + voltage_table[i].voltage, + voltage_table[i].duty); + } +#endif + /* + * if voltage-table is provided, the duty in the table + * represents a percentage, i.e. 0-100%, so + * dutycycle_unit is 100. + */ + sc->sc_dutycycle_unit = 100; + } else { + kmem_free(sc->sc_voltage_table, len); + } + } +#ifdef PWMREGULATOR_DEBUG + if (sc->sc_voltage_table_num == 0) { + aprint_debug_dev(sc->sc_dev, "Duty:%u%%=%uuV, Duty:%u%%=%uuV\n", + sc->sc_dutycycle_range[0], sc->sc_microvolt_min, + sc->sc_dutycycle_range[1], sc->sc_microvolt_max); + } +#endif + + sc->sc_always_on = of_getprop_bool(phandle, "regulator-always-on"); + sc->sc_boot_on = of_getprop_bool(phandle, "regulator-boot-on"); + + fdtbus_register_regulator_controller(self, phandle, + &pwmregulator_funcs); + + /* + * If the regulator is flagged as always on or enabled at boot, + * ensure that it is enabled + */ + if (sc->sc_always_on || sc->sc_boot_on) + pwmregulator_enable(self, true); +} + +static int +pwmregulator_acquire(device_t dev) +{ + struct pwmregulator_softc * const sc = device_private(dev); + + sc->sc_pwm = fdtbus_pwm_acquire(sc->sc_phandle, "pwms"); + if (sc->sc_pwm == NULL) + return ENXIO; + + return 0; +} + +static void +pwmregulator_release(device_t dev) +{ + struct pwmregulator_softc * const sc = device_private(dev); + + sc->sc_pwm = NULL; +} + +static int +pwmregulator_enable(device_t dev, bool enable) +{ + struct pwmregulator_softc * const sc = device_private(dev); + + if (sc->sc_pwm == NULL) + return ENXIO; + + if (enable) + return pwm_enable(sc->sc_pwm); + else + return pwm_disable(sc->sc_pwm); +} + +static int +pwmregulator_set_voltage(device_t dev, u_int min_uvolt, u_int max_uvolt) +{ + struct pwmregulator_softc * const sc = device_private(dev); + struct pwm_config conf; + int duty, d0, d1, v0, v1, uv, rc; + + if (sc->sc_pwm == NULL) + return ENXIO; + + rc = pwm_get_config(sc->sc_pwm, &conf); + if (rc != 0) { + device_printf(dev, "%s: couldn't get pwm config, error=%d\n", + __func__, rc); + return rc; + } + + uv = (min_uvolt + max_uvolt) / 2; + + if (sc->sc_voltage_table_num > 0) { + /* find the nearest duty from voltage-table */ + int i, bestidx = 0; + for (i = 1; i < sc->sc_voltage_table_num; i++) { + if (abs(sc->sc_voltage_table[i].microvolt - uv) < + abs(sc->sc_voltage_table[bestidx].microvolt - uv)) + bestidx = i; + } + duty = sc->sc_voltage_table[bestidx].duty; + } else { + /* calculate duty from voltage */ + v0 = sc->sc_microvolt_min; + v1 = sc->sc_microvolt_max; + d0 = sc->sc_dutycycle_range[0]; + d1 = sc->sc_dutycycle_range[1]; + duty = (uv - v0) * (d1 - d0) / (v1 - v0) + d0; + } + + conf.duty_cycle = duty * conf.period / sc->sc_dutycycle_unit; + + rc = pwm_set_config(sc->sc_pwm, &conf); + if (rc != 0) + device_printf(dev, "couldn't set pwm config, error=%d\n", rc); + return rc; +} + +static int +pwmregulator_get_voltage(device_t dev, u_int *puvolt) +{ + struct pwmregulator_softc * const sc = device_private(dev); + struct pwm_config conf; + int duty, d0, d1, v0, v1, uv, rc; + + if (sc->sc_pwm == NULL) + return ENXIO; + + rc = pwm_get_config(sc->sc_pwm, &conf); + if (rc != 0) { + device_printf(dev, "%s: couldn't get pwm config, error=%d\n", + __func__, rc); + return rc; + } + + duty = conf.duty_cycle * sc->sc_dutycycle_unit / conf.period; + + if (sc->sc_voltage_table_num > 0) { + /* find the nearest voltage from voltage-table */ + int i, bestidx = 0; + for (i = 1; i < sc->sc_voltage_table_num; i++) { + if (abs(sc->sc_voltage_table[i].duty - duty) < + abs(sc->sc_voltage_table[bestidx].duty - duty)) + bestidx = i; + } + uv = sc->sc_voltage_table[bestidx].microvolt; + } else { + /* calculate voltage from duty */ + d0 = sc->sc_dutycycle_range[0]; + d1 = sc->sc_dutycycle_range[1]; + v0 = sc->sc_microvolt_min; + v1 = sc->sc_microvolt_max; + uv = (duty - d0) * (v1 - v0) / (d1 - d0) + v0; + } + + *puvolt = uv; + return 0; +}