Hello Jan, On 17/04/2020 10:11, jan.som...@dlr.de wrote: > Hi Christian, > > Yes, with this patch my build works again. > Thank you very much.
Thanks for testing. Best regards Christian > > Best regards, > > Jan > >> -----Ursprüngliche Nachricht----- >> Von: Christian Mauderer [mailto:christian.maude...@embedded-brains.de] >> Gesendet: Freitag, 17. April 2020 09:17 >> An: Sommer, Jan; devel@rtems.org >> Betreff: Re: [PATCH rtems-libbsd v2 06/14] regulator: Import from FreeBSD. >> >> Hello Jan, >> >> can you please try this patch: >> >> https://lists.rtems.org/pipermail/devel/2020-April/059419.html >> >> I've still seen quite some linker errors in if_em.c. But as far as I can >> tell they doesn't seem to be related. Are they? >> >> Best regards >> >> Christian >> >> On 17/04/2020 08:17, Christian Mauderer wrote: >>> Hello Jan, >>> >>> On 16/04/2020 22:27, jan.som...@dlr.de wrote: >>>> I have a question: >>>> I just rebased my working tree for the i386 target to the current 5- >> freebsd-12 branch. >>>> The compilation now fails in regulator related files. >>>> It seems that I am missing the FDT define and that comments out some >> includes. >>>> Could you tell me what FDT means in this context and do you know where >> I can activate it? >>> >>> FDT is a flattened device tree. That's something that isn't used on >>> i386. So most likely quite some more BSPs will have that problem. >>> >>> It's some code that I took over quite unchanged from FreeBSD. I would >>> have expected that it is used in FreeBSD for a lot of different cases >>> and therefore didn't expect a problem like that. I'll have a look at it. >>> >>> Best regards >>> >>> Christian >>> >>>> >>>> Cheers, >>>> >>>> Jan >>>> >>>>> -----Ursprüngliche Nachricht----- >>>>> Von: devel [mailto:devel-boun...@rtems.org] Im Auftrag von Christian >>>>> Mauderer >>>>> Gesendet: Dienstag, 14. April 2020 16:51 >>>>> An: devel@rtems.org >>>>> Betreff: [PATCH rtems-libbsd v2 06/14] regulator: Import from FreeBSD. >>>>> >>>>> Update #3869. >>>>> --- >>>>> freebsd/sys/dev/extres/regulator/regulator.c | 1321 >>>>> ++++++++++++++++++++ >>>>> freebsd/sys/dev/extres/regulator/regulator.h | 156 +++ >>>>> freebsd/sys/dev/extres/regulator/regulator_bus.c | 89 ++ >>>>> freebsd/sys/dev/extres/regulator/regulator_fixed.c | 502 ++++++++ >>>>> freebsd/sys/dev/extres/regulator/regulator_fixed.h | 44 + >>>>> freebsd/sys/dev/gpio/gpioregulator.c | 350 ++++++ >>>>> 6 files changed, 2462 insertions(+) >>>>> create mode 100644 freebsd/sys/dev/extres/regulator/regulator.c >>>>> create mode 100644 freebsd/sys/dev/extres/regulator/regulator.h >>>>> create mode 100644 freebsd/sys/dev/extres/regulator/regulator_bus.c >>>>> create mode 100644 >> freebsd/sys/dev/extres/regulator/regulator_fixed.c >>>>> create mode 100644 >> freebsd/sys/dev/extres/regulator/regulator_fixed.h >>>>> create mode 100644 freebsd/sys/dev/gpio/gpioregulator.c >>>>> >>>>> diff --git a/freebsd/sys/dev/extres/regulator/regulator.c >>>>> b/freebsd/sys/dev/extres/regulator/regulator.c >>>>> new file mode 100644 >>>>> index 00000000..4ad6e94a >>>>> --- /dev/null >>>>> +++ b/freebsd/sys/dev/extres/regulator/regulator.c >>>>> @@ -0,0 +1,1321 @@ >>>>> +#include <machine/rtems-bsd-kernel-space.h> >>>>> + >>>>> +/*- >>>>> + * Copyright 2016 Michal Meloun <m...@freebsd.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 AND CONTRIBUTORS >> ``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 OR >> CONTRIBUTORS >>>>> 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> >>>>> +__FBSDID("$FreeBSD$"); >>>>> + >>>>> +#include <rtems/bsd/local/opt_platform.h> >>>>> +#include <sys/param.h> >>>>> +#include <sys/conf.h> >>>>> +#include <sys/bus.h> >>>>> +#include <sys/kernel.h> >>>>> +#include <sys/queue.h> >>>>> +#include <sys/kobj.h> >>>>> +#include <sys/malloc.h> >>>>> +#include <sys/mutex.h> >>>>> +#include <sys/limits.h> >>>>> +#include <sys/lock.h> >>>>> +#include <sys/sysctl.h> >>>>> +#include <sys/systm.h> >>>>> +#include <sys/sx.h> >>>>> + >>>>> +#ifdef FDT >>>>> +#include <dev/fdt/fdt_common.h> >>>>> +#include <dev/ofw/ofw_bus.h> >>>>> +#include <dev/ofw/ofw_bus_subr.h> >>>>> +#endif >>>>> +#include <dev/extres/regulator/regulator.h> >>>>> + >>>>> +#include <rtems/bsd/local/regdev_if.h> >>>>> + >>>>> +SYSCTL_NODE(_hw, OID_AUTO, regulator, CTLFLAG_RD, NULL, >>>>> "Regulators"); >>>>> + >>>>> +MALLOC_DEFINE(M_REGULATOR, "regulator", "Regulator >> framework"); >>>>> + >>>>> +#define DIV_ROUND_UP(n,d) howmany(n, d) >>>>> + >>>>> +/* Forward declarations. */ >>>>> +struct regulator; >>>>> +struct regnode; >>>>> + >>>>> +typedef TAILQ_HEAD(regnode_list, regnode) regnode_list_t; >>>>> +typedef TAILQ_HEAD(regulator_list, regulator) regulator_list_t; >>>>> + >>>>> +/* Default regulator methods. */ >>>>> +static int regnode_method_init(struct regnode *regnode); >>>>> +static int regnode_method_enable(struct regnode *regnode, bool >> enable, >>>>> + int *udelay); >>>>> +static int regnode_method_status(struct regnode *regnode, int >> *status); >>>>> +static int regnode_method_set_voltage(struct regnode *regnode, int >>>>> min_uvolt, >>>>> + int max_uvolt, int *udelay); >>>>> +static int regnode_method_get_voltage(struct regnode *regnode, int >>>>> *uvolt); >>>>> +static void regulator_constraint(void *dummy); >>>>> +static void regulator_shutdown(void *dummy); >>>>> + >>>>> +/* >>>>> + * Regulator controller methods. >>>>> + */ >>>>> +static regnode_method_t regnode_methods[] = { >>>>> + REGNODEMETHOD(regnode_init, >>>>> regnode_method_init), >>>>> + REGNODEMETHOD(regnode_enable, >>>>> regnode_method_enable), >>>>> + REGNODEMETHOD(regnode_status, >>>>> regnode_method_status), >>>>> + REGNODEMETHOD(regnode_set_voltage, >>>>> regnode_method_set_voltage), >>>>> + REGNODEMETHOD(regnode_get_voltage, >>>>> regnode_method_get_voltage), >>>>> + REGNODEMETHOD(regnode_check_voltage, >>>>> regnode_method_check_voltage), >>>>> + >>>>> + REGNODEMETHOD_END >>>>> +}; >>>>> +DEFINE_CLASS_0(regnode, regnode_class, regnode_methods, 0); >>>>> + >>>>> +/* >>>>> + * Regulator node - basic element for modelling SOC and bard power >> supply >>>>> + * chains. Its contains producer data. >>>>> + */ >>>>> +struct regnode { >>>>> + KOBJ_FIELDS; >>>>> + >>>>> + TAILQ_ENTRY(regnode) reglist_link; /* Global list entry */ >>>>> + regulator_list_t consumers_list; /* Consumers list */ >>>>> + >>>>> + /* Cache for already resolved names */ >>>>> + struct regnode *parent; /* Resolved parent */ >>>>> + >>>>> + /* Details of this device. */ >>>>> + const char *name; /* Globally unique >>>>> name */ >>>>> + const char *parent_name; /* Parent name */ >>>>> + >>>>> + device_t pdev; /* Producer device_t */ >>>>> + void *softc; /* Producer softc */ >>>>> + intptr_t id; /* Per producer unique id */ >>>>> +#ifdef FDT >>>>> + phandle_t ofw_node; /* OFW node of regulator */ >>>>> +#endif >>>>> + int flags; /* REGULATOR_FLAGS_ */ >>>>> + struct sx lock; /* Lock for this regulator */ >>>>> + int ref_cnt; /* Reference counter */ >>>>> + int enable_cnt; /* Enabled counter */ >>>>> + >>>>> + struct regnode_std_param std_param; /* Standard >>>>> parameters */ >>>>> + >>>>> + struct sysctl_ctx_list sysctl_ctx; >>>>> +}; >>>>> + >>>>> +/* >>>>> + * Per consumer data, information about how a consumer is using a >>>>> regulator >>>>> + * node. >>>>> + * A pointer to this structure is used as a handle in the consumer >> interface. >>>>> + */ >>>>> +struct regulator { >>>>> + device_t cdev; /* Consumer device */ >>>>> + struct regnode *regnode; >>>>> + TAILQ_ENTRY(regulator) link; /* Consumers list >>>>> entry */ >>>>> + >>>>> + int enable_cnt; >>>>> + int min_uvolt; /* Requested uvolt range */ >>>>> + int max_uvolt; >>>>> +}; >>>>> + >>>>> +/* >>>>> + * Regulator names must be system wide unique. >>>>> + */ >>>>> +static regnode_list_t regnode_list = >>>>> TAILQ_HEAD_INITIALIZER(regnode_list); >>>>> + >>>>> +static struct sx regnode_topo_lock; >>>>> +SX_SYSINIT(regulator_topology, ®node_topo_lock, "Regulator >> topology >>>>> lock"); >>>>> + >>>>> +#define REG_TOPO_SLOCK() sx_slock(®node_topo_lock) >>>>> +#define REG_TOPO_XLOCK() sx_xlock(®node_topo_lock) >>>>> +#define REG_TOPO_UNLOCK() sx_unlock(®node_topo_lock) >>>>> +#define REG_TOPO_ASSERT() sx_assert(®node_topo_lock, >>>>> SA_LOCKED) >>>>> +#define REG_TOPO_XASSERT() sx_assert(®node_topo_lock, >>>>> SA_XLOCKED) >>>>> + >>>>> +#define REGNODE_SLOCK(_sc) sx_slock(&((_sc)->lock)) >>>>> +#define REGNODE_XLOCK(_sc) sx_xlock(&((_sc)->lock)) >>>>> +#define REGNODE_UNLOCK(_sc) sx_unlock(&((_sc)->lock)) >>>>> + >>>>> +SYSINIT(regulator_constraint, SI_SUB_LAST, SI_ORDER_ANY, >>>>> regulator_constraint, >>>>> + NULL); >>>>> +SYSINIT(regulator_shutdown, SI_SUB_LAST, SI_ORDER_ANY, >>>>> regulator_shutdown, >>>>> + NULL); >>>>> + >>>>> +static void >>>>> +regulator_constraint(void *dummy) >>>>> +{ >>>>> + struct regnode *entry; >>>>> + int rv; >>>>> + >>>>> + REG_TOPO_SLOCK(); >>>>> + TAILQ_FOREACH(entry, ®node_list, reglist_link) { >>>>> + rv = regnode_set_constraint(entry); >>>>> + if (rv != 0 && bootverbose) >>>>> + printf("regulator: setting constraint on %s failed >>>>> (%d)\n", >>>>> + entry->name, rv); >>>>> + } >>>>> + REG_TOPO_UNLOCK(); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Disable unused regulator >>>>> + * We run this function at SI_SUB_LAST which mean that every driver >> that >>>>> needs >>>>> + * regulator should have already enable them. >>>>> + * All the remaining regulators should be those left enabled by the >>>>> bootloader >>>>> + * or enable by default by the PMIC. >>>>> + */ >>>>> +static void >>>>> +regulator_shutdown(void *dummy) >>>>> +{ >>>>> + struct regnode *entry; >>>>> + int status, ret; >>>>> + int disable = 1; >>>>> + >>>>> + TUNABLE_INT_FETCH("hw.regulator.disable_unused", &disable); >>>>> + if (!disable) >>>>> + return; >>>>> + REG_TOPO_SLOCK(); >>>>> + >>>>> + if (bootverbose) >>>>> + printf("regulator: shutting down unused regulators\n"); >>>>> + TAILQ_FOREACH(entry, ®node_list, reglist_link) { >>>>> + if (!entry->std_param.always_on) { >>>>> + ret = regnode_status(entry, &status); >>>>> + if (ret == 0 && status == >>>>> REGULATOR_STATUS_ENABLED) { >>>>> + if (bootverbose) >>>>> + printf("regulator: shutting down %s... >>>>> ", >>>>> + entry->name); >>>>> + ret = regnode_stop(entry, 0); >>>>> + if (bootverbose) { >>>>> + /* >>>>> + * Call out busy in particular, here, >>>>> + * because it's not unexpected to fail >>>>> + * shutdown if the regulator is simply >>>>> + * in-use. >>>>> + */ >>>>> + if (ret == EBUSY) >>>>> + printf("busy\n"); >>>>> + else if (ret != 0) >>>>> + printf("error (%d)\n", ret); >>>>> + else >>>>> + printf("ok\n"); >>>>> + } >>>>> + } >>>>> + } >>>>> + } >>>>> + REG_TOPO_UNLOCK(); >>>>> +} >>>>> + >>>>> +/* >>>>> + * sysctl handler >>>>> + */ >>>>> +static int >>>>> +regnode_uvolt_sysctl(SYSCTL_HANDLER_ARGS) >>>>> +{ >>>>> + struct regnode *regnode = arg1; >>>>> + int rv, uvolt; >>>>> + >>>>> + if (regnode->std_param.min_uvolt == regnode- >>>>>> std_param.max_uvolt) { >>>>> + uvolt = regnode->std_param.min_uvolt; >>>>> + } else { >>>>> + REG_TOPO_SLOCK(); >>>>> + if ((rv = regnode_get_voltage(regnode, &uvolt)) != 0) { >>>>> + REG_TOPO_UNLOCK(); >>>>> + return (rv); >>>>> + } >>>>> + REG_TOPO_UNLOCK(); >>>>> + } >>>>> + >>>>> + return sysctl_handle_int(oidp, &uvolt, sizeof(uvolt), req); >>>>> +} >>>>> + >>>>> +/* >>>>> ---------------------------------------------------------------------------- >>>>> + * >>>>> + * Default regulator methods for base class. >>>>> + * >>>>> + */ >>>>> +static int >>>>> +regnode_method_init(struct regnode *regnode) >>>>> +{ >>>>> + >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static int >>>>> +regnode_method_enable(struct regnode *regnode, bool enable, int >>>>> *udelay) >>>>> +{ >>>>> + >>>>> + if (!enable) >>>>> + return (ENXIO); >>>>> + >>>>> + *udelay = 0; >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static int >>>>> +regnode_method_status(struct regnode *regnode, int *status) >>>>> +{ >>>>> + *status = REGULATOR_STATUS_ENABLED; >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static int >>>>> +regnode_method_set_voltage(struct regnode *regnode, int >> min_uvolt, int >>>>> max_uvolt, >>>>> + int *udelay) >>>>> +{ >>>>> + >>>>> + if ((min_uvolt > regnode->std_param.max_uvolt) || >>>>> + (max_uvolt < regnode->std_param.min_uvolt)) >>>>> + return (ERANGE); >>>>> + *udelay = 0; >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static int >>>>> +regnode_method_get_voltage(struct regnode *regnode, int *uvolt) >>>>> +{ >>>>> + >>>>> + return (regnode->std_param.min_uvolt + >>>>> + (regnode->std_param.max_uvolt - regnode- >>>>>> std_param.min_uvolt) / 2); >>>>> +} >>>>> + >>>>> +int >>>>> +regnode_method_check_voltage(struct regnode *regnode, int uvolt) >>>>> +{ >>>>> + >>>>> + if ((uvolt > regnode->std_param.max_uvolt) || >>>>> + (uvolt < regnode->std_param.min_uvolt)) >>>>> + return (ERANGE); >>>>> + return (0); >>>>> +} >>>>> + >>>>> +/* >>>>> ---------------------------------------------------------------------------- >>>>> + * >>>>> + * Internal functions. >>>>> + * >>>>> + */ >>>>> + >>>>> +static struct regnode * >>>>> +regnode_find_by_name(const char *name) >>>>> +{ >>>>> + struct regnode *entry; >>>>> + >>>>> + REG_TOPO_ASSERT(); >>>>> + >>>>> + TAILQ_FOREACH(entry, ®node_list, reglist_link) { >>>>> + if (strcmp(entry->name, name) == 0) >>>>> + return (entry); >>>>> + } >>>>> + return (NULL); >>>>> +} >>>>> + >>>>> +static struct regnode * >>>>> +regnode_find_by_id(device_t dev, intptr_t id) >>>>> +{ >>>>> + struct regnode *entry; >>>>> + >>>>> + REG_TOPO_ASSERT(); >>>>> + >>>>> + TAILQ_FOREACH(entry, ®node_list, reglist_link) { >>>>> + if ((entry->pdev == dev) && (entry->id == id)) >>>>> + return (entry); >>>>> + } >>>>> + >>>>> + return (NULL); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Create and initialize regulator object, but do not register it. >>>>> + */ >>>>> +struct regnode * >>>>> +regnode_create(device_t pdev, regnode_class_t regnode_class, >>>>> + struct regnode_init_def *def) >>>>> +{ >>>>> + struct regnode *regnode; >>>>> + struct sysctl_oid *regnode_oid; >>>>> + >>>>> + KASSERT(def->name != NULL, ("regulator name is NULL")); >>>>> + KASSERT(def->name[0] != '\0', ("regulator name is empty")); >>>>> + >>>>> + REG_TOPO_SLOCK(); >>>>> + if (regnode_find_by_name(def->name) != NULL) >>>>> + panic("Duplicated regulator registration: %s\n", def->name); >>>>> + REG_TOPO_UNLOCK(); >>>>> + >>>>> + /* Create object and initialize it. */ >>>>> + regnode = malloc(sizeof(struct regnode), M_REGULATOR, >>>>> + M_WAITOK | M_ZERO); >>>>> + kobj_init((kobj_t)regnode, (kobj_class_t)regnode_class); >>>>> + sx_init(®node->lock, "Regulator node lock"); >>>>> + >>>>> + /* Allocate softc if required. */ >>>>> + if (regnode_class->size > 0) { >>>>> + regnode->softc = malloc(regnode_class->size, >>>>> M_REGULATOR, >>>>> + M_WAITOK | M_ZERO); >>>>> + } >>>>> + >>>>> + >>>>> + /* Copy all strings unless they're flagged as static. */ >>>>> + if (def->flags & REGULATOR_FLAGS_STATIC) { >>>>> + regnode->name = def->name; >>>>> + regnode->parent_name = def->parent_name; >>>>> + } else { >>>>> + regnode->name = strdup(def->name, M_REGULATOR); >>>>> + if (def->parent_name != NULL) >>>>> + regnode->parent_name = strdup(def- >>>>>> parent_name, >>>>> + M_REGULATOR); >>>>> + } >>>>> + >>>>> + /* Rest of init. */ >>>>> + TAILQ_INIT(®node->consumers_list); >>>>> + regnode->id = def->id; >>>>> + regnode->pdev = pdev; >>>>> + regnode->flags = def->flags; >>>>> + regnode->parent = NULL; >>>>> + regnode->std_param = def->std_param; >>>>> +#ifdef FDT >>>>> + regnode->ofw_node = def->ofw_node; >>>>> +#endif >>>>> + >>>>> + sysctl_ctx_init(®node->sysctl_ctx); >>>>> + regnode_oid = SYSCTL_ADD_NODE(®node->sysctl_ctx, >>>>> + SYSCTL_STATIC_CHILDREN(_hw_regulator), >>>>> + OID_AUTO, regnode->name, >>>>> + CTLFLAG_RD, 0, "A regulator node"); >>>>> + >>>>> + SYSCTL_ADD_INT(®node->sysctl_ctx, >>>>> + SYSCTL_CHILDREN(regnode_oid), >>>>> + OID_AUTO, "min_uvolt", >>>>> + CTLFLAG_RD, ®node->std_param.min_uvolt, 0, >>>>> + "Minimal voltage (in uV)"); >>>>> + SYSCTL_ADD_INT(®node->sysctl_ctx, >>>>> + SYSCTL_CHILDREN(regnode_oid), >>>>> + OID_AUTO, "max_uvolt", >>>>> + CTLFLAG_RD, ®node->std_param.max_uvolt, 0, >>>>> + "Maximal voltage (in uV)"); >>>>> + SYSCTL_ADD_INT(®node->sysctl_ctx, >>>>> + SYSCTL_CHILDREN(regnode_oid), >>>>> + OID_AUTO, "min_uamp", >>>>> + CTLFLAG_RD, ®node->std_param.min_uamp, 0, >>>>> + "Minimal amperage (in uA)"); >>>>> + SYSCTL_ADD_INT(®node->sysctl_ctx, >>>>> + SYSCTL_CHILDREN(regnode_oid), >>>>> + OID_AUTO, "max_uamp", >>>>> + CTLFLAG_RD, ®node->std_param.max_uamp, 0, >>>>> + "Maximal amperage (in uA)"); >>>>> + SYSCTL_ADD_INT(®node->sysctl_ctx, >>>>> + SYSCTL_CHILDREN(regnode_oid), >>>>> + OID_AUTO, "ramp_delay", >>>>> + CTLFLAG_RD, ®node->std_param.ramp_delay, 0, >>>>> + "Ramp delay (in uV/us)"); >>>>> + SYSCTL_ADD_INT(®node->sysctl_ctx, >>>>> + SYSCTL_CHILDREN(regnode_oid), >>>>> + OID_AUTO, "enable_delay", >>>>> + CTLFLAG_RD, ®node->std_param.enable_delay, 0, >>>>> + "Enable delay (in us)"); >>>>> + SYSCTL_ADD_INT(®node->sysctl_ctx, >>>>> + SYSCTL_CHILDREN(regnode_oid), >>>>> + OID_AUTO, "enable_cnt", >>>>> + CTLFLAG_RD, ®node->enable_cnt, 0, >>>>> + "The regulator enable counter"); >>>>> + SYSCTL_ADD_U8(®node->sysctl_ctx, >>>>> + SYSCTL_CHILDREN(regnode_oid), >>>>> + OID_AUTO, "boot_on", >>>>> + CTLFLAG_RD, (uint8_t *) ®node->std_param.boot_on, 0, >>>>> + "Is enabled on boot"); >>>>> + SYSCTL_ADD_U8(®node->sysctl_ctx, >>>>> + SYSCTL_CHILDREN(regnode_oid), >>>>> + OID_AUTO, "always_on", >>>>> + CTLFLAG_RD, (uint8_t *)®node->std_param.always_on, 0, >>>>> + "Is always enabled"); >>>>> + >>>>> + SYSCTL_ADD_PROC(®node->sysctl_ctx, >>>>> + SYSCTL_CHILDREN(regnode_oid), >>>>> + OID_AUTO, "uvolt", >>>>> + CTLTYPE_INT | CTLFLAG_RD, >>>>> + regnode, 0, regnode_uvolt_sysctl, >>>>> + "I", >>>>> + "Current voltage (in uV)"); >>>>> + >>>>> + return (regnode); >>>>> +} >>>>> + >>>>> +/* Register regulator object. */ >>>>> +struct regnode * >>>>> +regnode_register(struct regnode *regnode) >>>>> +{ >>>>> + int rv; >>>>> + >>>>> +#ifdef FDT >>>>> + if (regnode->ofw_node <= 0) >>>>> + regnode->ofw_node = ofw_bus_get_node(regnode->pdev); >>>>> + if (regnode->ofw_node <= 0) >>>>> + return (NULL); >>>>> +#endif >>>>> + >>>>> + rv = REGNODE_INIT(regnode); >>>>> + if (rv != 0) { >>>>> + printf("REGNODE_INIT failed: %d\n", rv); >>>>> + return (NULL); >>>>> + } >>>>> + >>>>> + REG_TOPO_XLOCK(); >>>>> + TAILQ_INSERT_TAIL(®node_list, regnode, reglist_link); >>>>> + REG_TOPO_UNLOCK(); >>>>> +#ifdef FDT >>>>> + OF_device_register_xref(OF_xref_from_node(regnode- >>>>>> ofw_node), >>>>> + regnode->pdev); >>>>> +#endif >>>>> + return (regnode); >>>>> +} >>>>> + >>>>> +static int >>>>> +regnode_resolve_parent(struct regnode *regnode) >>>>> +{ >>>>> + >>>>> + /* All ready resolved or no parent? */ >>>>> + if ((regnode->parent != NULL) || >>>>> + (regnode->parent_name == NULL)) >>>>> + return (0); >>>>> + >>>>> + regnode->parent = regnode_find_by_name(regnode- >>>>>> parent_name); >>>>> + if (regnode->parent == NULL) >>>>> + return (ENODEV); >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static void >>>>> +regnode_delay(int usec) >>>>> +{ >>>>> + int ticks; >>>>> + >>>>> + if (usec == 0) >>>>> + return; >>>>> + ticks = (usec * hz + 999999) / 1000000; >>>>> + >>>>> + if (cold || ticks < 2) >>>>> + DELAY(usec); >>>>> + else >>>>> + pause("REGULATOR", ticks); >>>>> +} >>>>> + >>>>> +/* >>>>> -------------------------------------------------------------------------- >>>>> + * >>>>> + * Regulator providers interface >>>>> + * >>>>> + */ >>>>> + >>>>> +const char * >>>>> +regnode_get_name(struct regnode *regnode) >>>>> +{ >>>>> + >>>>> + return (regnode->name); >>>>> +} >>>>> + >>>>> +const char * >>>>> +regnode_get_parent_name(struct regnode *regnode) >>>>> +{ >>>>> + >>>>> + return (regnode->parent_name); >>>>> +} >>>>> + >>>>> +int >>>>> +regnode_get_flags(struct regnode *regnode) >>>>> +{ >>>>> + >>>>> + return (regnode->flags); >>>>> +} >>>>> + >>>>> +void * >>>>> +regnode_get_softc(struct regnode *regnode) >>>>> +{ >>>>> + >>>>> + return (regnode->softc); >>>>> +} >>>>> + >>>>> +device_t >>>>> +regnode_get_device(struct regnode *regnode) >>>>> +{ >>>>> + >>>>> + return (regnode->pdev); >>>>> +} >>>>> + >>>>> +struct regnode_std_param *regnode_get_stdparam(struct regnode >>>>> *regnode) >>>>> +{ >>>>> + >>>>> + return (®node->std_param); >>>>> +} >>>>> + >>>>> +void regnode_topo_unlock(void) >>>>> +{ >>>>> + >>>>> + REG_TOPO_UNLOCK(); >>>>> +} >>>>> + >>>>> +void regnode_topo_xlock(void) >>>>> +{ >>>>> + >>>>> + REG_TOPO_XLOCK(); >>>>> +} >>>>> + >>>>> +void regnode_topo_slock(void) >>>>> +{ >>>>> + >>>>> + REG_TOPO_SLOCK(); >>>>> +} >>>>> + >>>>> + >>>>> +/* >>>>> -------------------------------------------------------------------------- >>>>> + * >>>>> + * Real consumers executive >>>>> + * >>>>> + */ >>>>> +struct regnode * >>>>> +regnode_get_parent(struct regnode *regnode) >>>>> +{ >>>>> + int rv; >>>>> + >>>>> + REG_TOPO_ASSERT(); >>>>> + >>>>> + rv = regnode_resolve_parent(regnode); >>>>> + if (rv != 0) >>>>> + return (NULL); >>>>> + >>>>> + return (regnode->parent); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Enable regulator. >>>>> + */ >>>>> +int >>>>> +regnode_enable(struct regnode *regnode) >>>>> +{ >>>>> + int udelay; >>>>> + int rv; >>>>> + >>>>> + REG_TOPO_ASSERT(); >>>>> + >>>>> + /* Enable regulator for each node in chain, starting from source. */ >>>>> + rv = regnode_resolve_parent(regnode); >>>>> + if (rv != 0) >>>>> + return (rv); >>>>> + if (regnode->parent != NULL) { >>>>> + rv = regnode_enable(regnode->parent); >>>>> + if (rv != 0) >>>>> + return (rv); >>>>> + } >>>>> + >>>>> + /* Handle this node. */ >>>>> + REGNODE_XLOCK(regnode); >>>>> + if (regnode->enable_cnt == 0) { >>>>> + rv = REGNODE_ENABLE(regnode, true, &udelay); >>>>> + if (rv != 0) { >>>>> + REGNODE_UNLOCK(regnode); >>>>> + return (rv); >>>>> + } >>>>> + regnode_delay(udelay); >>>>> + } >>>>> + regnode->enable_cnt++; >>>>> + REGNODE_UNLOCK(regnode); >>>>> + return (0); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Disable regulator. >>>>> + */ >>>>> +int >>>>> +regnode_disable(struct regnode *regnode) >>>>> +{ >>>>> + int udelay; >>>>> + int rv; >>>>> + >>>>> + REG_TOPO_ASSERT(); >>>>> + rv = 0; >>>>> + >>>>> + REGNODE_XLOCK(regnode); >>>>> + /* Disable regulator for each node in chain, starting from consumer. >>>>> */ >>>>> + if (regnode->enable_cnt == 1 && >>>>> + (regnode->flags & REGULATOR_FLAGS_NOT_DISABLE) == 0 && >>>>> + !regnode->std_param.always_on) { >>>>> + rv = REGNODE_ENABLE(regnode, false, &udelay); >>>>> + if (rv != 0) { >>>>> + REGNODE_UNLOCK(regnode); >>>>> + return (rv); >>>>> + } >>>>> + regnode_delay(udelay); >>>>> + } >>>>> + regnode->enable_cnt--; >>>>> + REGNODE_UNLOCK(regnode); >>>>> + >>>>> + rv = regnode_resolve_parent(regnode); >>>>> + if (rv != 0) >>>>> + return (rv); >>>>> + if (regnode->parent != NULL) >>>>> + rv = regnode_disable(regnode->parent); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Stop regulator. >>>>> + */ >>>>> +int >>>>> +regnode_stop(struct regnode *regnode, int depth) >>>>> +{ >>>>> + int udelay; >>>>> + int rv; >>>>> + >>>>> + REG_TOPO_ASSERT(); >>>>> + rv = 0; >>>>> + >>>>> + REGNODE_XLOCK(regnode); >>>>> + /* The first node must not be enabled. */ >>>>> + if ((regnode->enable_cnt != 0) && (depth == 0)) { >>>>> + REGNODE_UNLOCK(regnode); >>>>> + return (EBUSY); >>>>> + } >>>>> + /* Disable regulator for each node in chain, starting from consumer >>>>> */ >>>>> + if ((regnode->enable_cnt == 0) && >>>>> + ((regnode->flags & REGULATOR_FLAGS_NOT_DISABLE) == 0)) { >>>>> + rv = REGNODE_STOP(regnode, &udelay); >>>>> + if (rv != 0) { >>>>> + REGNODE_UNLOCK(regnode); >>>>> + return (rv); >>>>> + } >>>>> + regnode_delay(udelay); >>>>> + } >>>>> + REGNODE_UNLOCK(regnode); >>>>> + >>>>> + rv = regnode_resolve_parent(regnode); >>>>> + if (rv != 0) >>>>> + return (rv); >>>>> + if (regnode->parent != NULL && regnode->parent->enable_cnt == 0) >>>>> + rv = regnode_stop(regnode->parent, depth + 1); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Get regulator status. (REGULATOR_STATUS_*) >>>>> + */ >>>>> +int >>>>> +regnode_status(struct regnode *regnode, int *status) >>>>> +{ >>>>> + int rv; >>>>> + >>>>> + REG_TOPO_ASSERT(); >>>>> + >>>>> + REGNODE_XLOCK(regnode); >>>>> + rv = REGNODE_STATUS(regnode, status); >>>>> + REGNODE_UNLOCK(regnode); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Get actual regulator voltage. >>>>> + */ >>>>> +int >>>>> +regnode_get_voltage(struct regnode *regnode, int *uvolt) >>>>> +{ >>>>> + int rv; >>>>> + >>>>> + REG_TOPO_ASSERT(); >>>>> + >>>>> + REGNODE_XLOCK(regnode); >>>>> + rv = REGNODE_GET_VOLTAGE(regnode, uvolt); >>>>> + REGNODE_UNLOCK(regnode); >>>>> + >>>>> + /* Pass call into parent, if regulator is in bypass mode. */ >>>>> + if (rv == ENOENT) { >>>>> + rv = regnode_resolve_parent(regnode); >>>>> + if (rv != 0) >>>>> + return (rv); >>>>> + if (regnode->parent != NULL) >>>>> + rv = regnode_get_voltage(regnode->parent, uvolt); >>>>> + >>>>> + } >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Set regulator voltage. >>>>> + */ >>>>> +int >>>>> +regnode_set_voltage(struct regnode *regnode, int min_uvolt, int >>>>> max_uvolt) >>>>> +{ >>>>> + int udelay; >>>>> + int rv; >>>>> + >>>>> + REG_TOPO_ASSERT(); >>>>> + >>>>> + REGNODE_XLOCK(regnode); >>>>> + >>>>> + rv = REGNODE_SET_VOLTAGE(regnode, min_uvolt, max_uvolt, >>>>> &udelay); >>>>> + if (rv == 0) >>>>> + regnode_delay(udelay); >>>>> + REGNODE_UNLOCK(regnode); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Consumer variant of regnode_set_voltage(). >>>>> + */ >>>>> +static int >>>>> +regnode_set_voltage_checked(struct regnode *regnode, struct >> regulator >>>>> *reg, >>>>> + int min_uvolt, int max_uvolt) >>>>> +{ >>>>> + int udelay; >>>>> + int all_max_uvolt; >>>>> + int all_min_uvolt; >>>>> + struct regulator *tmp; >>>>> + int rv; >>>>> + >>>>> + REG_TOPO_ASSERT(); >>>>> + >>>>> + REGNODE_XLOCK(regnode); >>>>> + /* Return error if requested range is outside of regulator range. */ >>>>> + if ((min_uvolt > regnode->std_param.max_uvolt) || >>>>> + (max_uvolt < regnode->std_param.min_uvolt)) { >>>>> + REGNODE_UNLOCK(regnode); >>>>> + return (ERANGE); >>>>> + } >>>>> + >>>>> + /* Get actual voltage range for all consumers. */ >>>>> + all_min_uvolt = regnode->std_param.min_uvolt; >>>>> + all_max_uvolt = regnode->std_param.max_uvolt; >>>>> + TAILQ_FOREACH(tmp, ®node->consumers_list, link) { >>>>> + /* Don't take requestor in account. */ >>>>> + if (tmp == reg) >>>>> + continue; >>>>> + if (all_min_uvolt < tmp->min_uvolt) >>>>> + all_min_uvolt = tmp->min_uvolt; >>>>> + if (all_max_uvolt > tmp->max_uvolt) >>>>> + all_max_uvolt = tmp->max_uvolt; >>>>> + } >>>>> + >>>>> + /* Test if request fits to actual contract. */ >>>>> + if ((min_uvolt > all_max_uvolt) || >>>>> + (max_uvolt < all_min_uvolt)) { >>>>> + REGNODE_UNLOCK(regnode); >>>>> + return (ERANGE); >>>>> + } >>>>> + >>>>> + /* Adjust new range.*/ >>>>> + if (min_uvolt < all_min_uvolt) >>>>> + min_uvolt = all_min_uvolt; >>>>> + if (max_uvolt > all_max_uvolt) >>>>> + max_uvolt = all_max_uvolt; >>>>> + >>>>> + rv = REGNODE_SET_VOLTAGE(regnode, min_uvolt, max_uvolt, >>>>> &udelay); >>>>> + regnode_delay(udelay); >>>>> + REGNODE_UNLOCK(regnode); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +int >>>>> +regnode_set_constraint(struct regnode *regnode) >>>>> +{ >>>>> + int status, rv, uvolt; >>>>> + >>>>> + if (regnode->std_param.boot_on != true && >>>>> + regnode->std_param.always_on != true) >>>>> + return (0); >>>>> + >>>>> + rv = regnode_status(regnode, &status); >>>>> + if (rv != 0) { >>>>> + if (bootverbose) >>>>> + printf("Cannot get regulator status for %s\n", >>>>> + regnode_get_name(regnode)); >>>>> + return (rv); >>>>> + } >>>>> + >>>>> + if (status == REGULATOR_STATUS_ENABLED) >>>>> + return (0); >>>>> + >>>>> + rv = regnode_get_voltage(regnode, &uvolt); >>>>> + if (rv != 0) { >>>>> + if (bootverbose) >>>>> + printf("Cannot get regulator voltage for %s\n", >>>>> + regnode_get_name(regnode)); >>>>> + return (rv); >>>>> + } >>>>> + >>>>> + if (uvolt < regnode->std_param.min_uvolt || >>>>> + uvolt > regnode->std_param.max_uvolt) { >>>>> + if (bootverbose) >>>>> + printf("Regulator %s current voltage %d is not in the" >>>>> + " acceptable range : %d<->%d\n", >>>>> + regnode_get_name(regnode), >>>>> + uvolt, regnode->std_param.min_uvolt, >>>>> + regnode->std_param.max_uvolt); >>>>> + return (ERANGE); >>>>> + } >>>>> + >>>>> + rv = regnode_enable(regnode); >>>>> + if (rv != 0) { >>>>> + if (bootverbose) >>>>> + printf("Cannot enable regulator %s\n", >>>>> + regnode_get_name(regnode)); >>>>> + return (rv); >>>>> + } >>>>> + >>>>> + return (0); >>>>> +} >>>>> + >>>>> +#ifdef FDT >>>>> +phandle_t >>>>> +regnode_get_ofw_node(struct regnode *regnode) >>>>> +{ >>>>> + >>>>> + return (regnode->ofw_node); >>>>> +} >>>>> +#endif >>>>> + >>>>> +/* >>>>> -------------------------------------------------------------------------- >>>>> + * >>>>> + * Regulator consumers interface. >>>>> + * >>>>> + */ >>>>> +/* Helper function for regulator_get*() */ >>>>> +static regulator_t >>>>> +regulator_create(struct regnode *regnode, device_t cdev) >>>>> +{ >>>>> + struct regulator *reg; >>>>> + >>>>> + REG_TOPO_ASSERT(); >>>>> + >>>>> + reg = malloc(sizeof(struct regulator), M_REGULATOR, >>>>> + M_WAITOK | M_ZERO); >>>>> + reg->cdev = cdev; >>>>> + reg->regnode = regnode; >>>>> + reg->enable_cnt = 0; >>>>> + >>>>> + REGNODE_XLOCK(regnode); >>>>> + regnode->ref_cnt++; >>>>> + TAILQ_INSERT_TAIL(®node->consumers_list, reg, link); >>>>> + reg ->min_uvolt = regnode->std_param.min_uvolt; >>>>> + reg ->max_uvolt = regnode->std_param.max_uvolt; >>>>> + REGNODE_UNLOCK(regnode); >>>>> + >>>>> + return (reg); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_enable(regulator_t reg) >>>>> +{ >>>>> + int rv; >>>>> + struct regnode *regnode; >>>>> + >>>>> + regnode = reg->regnode; >>>>> + KASSERT(regnode->ref_cnt > 0, >>>>> + ("Attempt to access unreferenced regulator: %s\n", regnode- >>>>>> name)); >>>>> + REG_TOPO_SLOCK(); >>>>> + rv = regnode_enable(regnode); >>>>> + if (rv == 0) >>>>> + reg->enable_cnt++; >>>>> + REG_TOPO_UNLOCK(); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_disable(regulator_t reg) >>>>> +{ >>>>> + int rv; >>>>> + struct regnode *regnode; >>>>> + >>>>> + regnode = reg->regnode; >>>>> + KASSERT(regnode->ref_cnt > 0, >>>>> + ("Attempt to access unreferenced regulator: %s\n", regnode- >>>>>> name)); >>>>> + KASSERT(reg->enable_cnt > 0, >>>>> + ("Attempt to disable already disabled regulator: %s\n", >>>>> + regnode->name)); >>>>> + REG_TOPO_SLOCK(); >>>>> + rv = regnode_disable(regnode); >>>>> + if (rv == 0) >>>>> + reg->enable_cnt--; >>>>> + REG_TOPO_UNLOCK(); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_stop(regulator_t reg) >>>>> +{ >>>>> + int rv; >>>>> + struct regnode *regnode; >>>>> + >>>>> + regnode = reg->regnode; >>>>> + KASSERT(regnode->ref_cnt > 0, >>>>> + ("Attempt to access unreferenced regulator: %s\n", regnode- >>>>>> name)); >>>>> + KASSERT(reg->enable_cnt == 0, >>>>> + ("Attempt to stop already enabled regulator: %s\n", regnode- >>>>>> name)); >>>>> + >>>>> + REG_TOPO_SLOCK(); >>>>> + rv = regnode_stop(regnode, 0); >>>>> + REG_TOPO_UNLOCK(); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_status(regulator_t reg, int *status) >>>>> +{ >>>>> + int rv; >>>>> + struct regnode *regnode; >>>>> + >>>>> + regnode = reg->regnode; >>>>> + KASSERT(regnode->ref_cnt > 0, >>>>> + ("Attempt to access unreferenced regulator: %s\n", regnode- >>>>>> name)); >>>>> + >>>>> + REG_TOPO_SLOCK(); >>>>> + rv = regnode_status(regnode, status); >>>>> + REG_TOPO_UNLOCK(); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_get_voltage(regulator_t reg, int *uvolt) >>>>> +{ >>>>> + int rv; >>>>> + struct regnode *regnode; >>>>> + >>>>> + regnode = reg->regnode; >>>>> + KASSERT(regnode->ref_cnt > 0, >>>>> + ("Attempt to access unreferenced regulator: %s\n", regnode- >>>>>> name)); >>>>> + >>>>> + REG_TOPO_SLOCK(); >>>>> + rv = regnode_get_voltage(regnode, uvolt); >>>>> + REG_TOPO_UNLOCK(); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_set_voltage(regulator_t reg, int min_uvolt, int max_uvolt) >>>>> +{ >>>>> + struct regnode *regnode; >>>>> + int rv; >>>>> + >>>>> + regnode = reg->regnode; >>>>> + KASSERT(regnode->ref_cnt > 0, >>>>> + ("Attempt to access unreferenced regulator: %s\n", regnode- >>>>>> name)); >>>>> + >>>>> + REG_TOPO_SLOCK(); >>>>> + >>>>> + rv = regnode_set_voltage_checked(regnode, reg, min_uvolt, >>>>> max_uvolt); >>>>> + if (rv == 0) { >>>>> + reg->min_uvolt = min_uvolt; >>>>> + reg->max_uvolt = max_uvolt; >>>>> + } >>>>> + REG_TOPO_UNLOCK(); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_check_voltage(regulator_t reg, int uvolt) >>>>> +{ >>>>> + int rv; >>>>> + struct regnode *regnode; >>>>> + >>>>> + regnode = reg->regnode; >>>>> + KASSERT(regnode->ref_cnt > 0, >>>>> + ("Attempt to access unreferenced regulator: %s\n", regnode- >>>>>> name)); >>>>> + >>>>> + REG_TOPO_SLOCK(); >>>>> + rv = REGNODE_CHECK_VOLTAGE(regnode, uvolt); >>>>> + REG_TOPO_UNLOCK(); >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +const char * >>>>> +regulator_get_name(regulator_t reg) >>>>> +{ >>>>> + struct regnode *regnode; >>>>> + >>>>> + regnode = reg->regnode; >>>>> + KASSERT(regnode->ref_cnt > 0, >>>>> + ("Attempt to access unreferenced regulator: %s\n", regnode- >>>>>> name)); >>>>> + return (regnode->name); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_get_by_name(device_t cdev, const char *name, regulator_t >>>>> *reg) >>>>> +{ >>>>> + struct regnode *regnode; >>>>> + >>>>> + REG_TOPO_SLOCK(); >>>>> + regnode = regnode_find_by_name(name); >>>>> + if (regnode == NULL) { >>>>> + REG_TOPO_UNLOCK(); >>>>> + return (ENODEV); >>>>> + } >>>>> + *reg = regulator_create(regnode, cdev); >>>>> + REG_TOPO_UNLOCK(); >>>>> + return (0); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_get_by_id(device_t cdev, device_t pdev, intptr_t id, >> regulator_t >>>>> *reg) >>>>> +{ >>>>> + struct regnode *regnode; >>>>> + >>>>> + REG_TOPO_SLOCK(); >>>>> + >>>>> + regnode = regnode_find_by_id(pdev, id); >>>>> + if (regnode == NULL) { >>>>> + REG_TOPO_UNLOCK(); >>>>> + return (ENODEV); >>>>> + } >>>>> + *reg = regulator_create(regnode, cdev); >>>>> + REG_TOPO_UNLOCK(); >>>>> + >>>>> + return (0); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_release(regulator_t reg) >>>>> +{ >>>>> + struct regnode *regnode; >>>>> + >>>>> + regnode = reg->regnode; >>>>> + KASSERT(regnode->ref_cnt > 0, >>>>> + ("Attempt to access unreferenced regulator: %s\n", regnode- >>>>>> name)); >>>>> + REG_TOPO_SLOCK(); >>>>> + while (reg->enable_cnt > 0) { >>>>> + regnode_disable(regnode); >>>>> + reg->enable_cnt--; >>>>> + } >>>>> + REGNODE_XLOCK(regnode); >>>>> + TAILQ_REMOVE(®node->consumers_list, reg, link); >>>>> + regnode->ref_cnt--; >>>>> + REGNODE_UNLOCK(regnode); >>>>> + REG_TOPO_UNLOCK(); >>>>> + >>>>> + free(reg, M_REGULATOR); >>>>> + return (0); >>>>> +} >>>>> + >>>>> +#ifdef FDT >>>>> +/* Default DT mapper. */ >>>>> +int >>>>> +regdev_default_ofw_map(device_t dev, phandle_t xref, int ncells, >>>>> + pcell_t *cells, intptr_t *id) >>>>> +{ >>>>> + if (ncells == 0) >>>>> + *id = 1; >>>>> + else if (ncells == 1) >>>>> + *id = cells[0]; >>>>> + else >>>>> + return (ERANGE); >>>>> + >>>>> + return (0); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_parse_ofw_stdparam(device_t pdev, phandle_t node, >>>>> + struct regnode_init_def *def) >>>>> +{ >>>>> + phandle_t supply_xref; >>>>> + struct regnode_std_param *par; >>>>> + int rv; >>>>> + >>>>> + par = &def->std_param; >>>>> + rv = OF_getprop_alloc(node, "regulator-name", >>>>> + (void **)&def->name); >>>>> + if (rv <= 0) { >>>>> + device_printf(pdev, "%s: Missing regulator name\n", >>>>> + __func__); >>>>> + return (ENXIO); >>>>> + } >>>>> + >>>>> + rv = OF_getencprop(node, "regulator-min-microvolt", &par- >>>>>> min_uvolt, >>>>> + sizeof(par->min_uvolt)); >>>>> + if (rv <= 0) >>>>> + par->min_uvolt = 0; >>>>> + >>>>> + rv = OF_getencprop(node, "regulator-max-microvolt", &par- >>>>>> max_uvolt, >>>>> + sizeof(par->max_uvolt)); >>>>> + if (rv <= 0) >>>>> + par->max_uvolt = 0; >>>>> + >>>>> + rv = OF_getencprop(node, "regulator-min-microamp", &par- >>>>>> min_uamp, >>>>> + sizeof(par->min_uamp)); >>>>> + if (rv <= 0) >>>>> + par->min_uamp = 0; >>>>> + >>>>> + rv = OF_getencprop(node, "regulator-max-microamp", &par- >>>>>> max_uamp, >>>>> + sizeof(par->max_uamp)); >>>>> + if (rv <= 0) >>>>> + par->max_uamp = 0; >>>>> + >>>>> + rv = OF_getencprop(node, "regulator-ramp-delay", &par- >>>>>> ramp_delay, >>>>> + sizeof(par->ramp_delay)); >>>>> + if (rv <= 0) >>>>> + par->ramp_delay = 0; >>>>> + >>>>> + rv = OF_getencprop(node, "regulator-enable-ramp-delay", >>>>> + &par->enable_delay, sizeof(par->enable_delay)); >>>>> + if (rv <= 0) >>>>> + par->enable_delay = 0; >>>>> + >>>>> + if (OF_hasprop(node, "regulator-boot-on")) >>>>> + par->boot_on = true; >>>>> + >>>>> + if (OF_hasprop(node, "regulator-always-on")) >>>>> + par->always_on = true; >>>>> + >>>>> + if (OF_hasprop(node, "enable-active-high")) >>>>> + par->enable_active_high = 1; >>>>> + >>>>> + rv = OF_getencprop(node, "vin-supply", &supply_xref, >>>>> + sizeof(supply_xref)); >>>>> + if (rv >= 0) { >>>>> + rv = OF_getprop_alloc(supply_xref, "regulator-name", >>>>> + (void **)&def->parent_name); >>>>> + if (rv <= 0) >>>>> + def->parent_name = NULL; >>>>> + } >>>>> + return (0); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_get_by_ofw_property(device_t cdev, phandle_t cnode, char >>>>> *name, >>>>> + regulator_t *reg) >>>>> +{ >>>>> + phandle_t *cells; >>>>> + device_t regdev; >>>>> + int ncells, rv; >>>>> + intptr_t id; >>>>> + >>>>> + *reg = NULL; >>>>> + >>>>> + if (cnode <= 0) >>>>> + cnode = ofw_bus_get_node(cdev); >>>>> + if (cnode <= 0) { >>>>> + device_printf(cdev, "%s called on not ofw based device\n", >>>>> + __func__); >>>>> + return (ENXIO); >>>>> + } >>>>> + >>>>> + cells = NULL; >>>>> + ncells = OF_getencprop_alloc_multi(cnode, name, sizeof(*cells), >>>>> + (void **)&cells); >>>>> + if (ncells <= 0) >>>>> + return (ENOENT); >>>>> + >>>>> + /* Translate xref to device */ >>>>> + regdev = OF_device_from_xref(cells[0]); >>>>> + if (regdev == NULL) { >>>>> + OF_prop_free(cells); >>>>> + return (ENODEV); >>>>> + } >>>>> + >>>>> + /* Map regulator to number */ >>>>> + rv = REGDEV_MAP(regdev, cells[0], ncells - 1, cells + 1, &id); >>>>> + OF_prop_free(cells); >>>>> + if (rv != 0) >>>>> + return (rv); >>>>> + return (regulator_get_by_id(cdev, regdev, id, reg)); >>>>> +} >>>>> +#endif >>>>> + >>>>> +/* >>>>> -------------------------------------------------------------------------- >>>>> + * >>>>> + * Regulator utility functions. >>>>> + * >>>>> + */ >>>>> + >>>>> +/* Convert raw selector value to real voltage */ >>>>> +int >>>>> +regulator_range_sel8_to_volt(struct regulator_range *ranges, int >> nranges, >>>>> + uint8_t sel, int *volt) >>>>> +{ >>>>> + struct regulator_range *range; >>>>> + int i; >>>>> + >>>>> + if (nranges == 0) >>>>> + panic("Voltage regulator have zero ranges\n"); >>>>> + >>>>> + for (i = 0; i < nranges ; i++) { >>>>> + range = ranges + i; >>>>> + >>>>> + if (!(sel >= range->min_sel && >>>>> + sel <= range->max_sel)) >>>>> + continue; >>>>> + >>>>> + sel -= range->min_sel; >>>>> + >>>>> + *volt = range->min_uvolt + sel * range->step_uvolt; >>>>> + return (0); >>>>> + } >>>>> + >>>>> + return (ERANGE); >>>>> +} >>>>> + >>>>> +int >>>>> +regulator_range_volt_to_sel8(struct regulator_range *ranges, int >> nranges, >>>>> + int min_uvolt, int max_uvolt, uint8_t *out_sel) >>>>> +{ >>>>> + struct regulator_range *range; >>>>> + uint8_t sel; >>>>> + int uvolt; >>>>> + int rv, i; >>>>> + >>>>> + if (nranges == 0) >>>>> + panic("Voltage regulator have zero ranges\n"); >>>>> + >>>>> + for (i = 0; i < nranges; i++) { >>>>> + range = ranges + i; >>>>> + uvolt = range->min_uvolt + >>>>> + (range->max_sel - range->min_sel) * range->step_uvolt; >>>>> + >>>>> + if ((min_uvolt > uvolt) || >>>>> + (max_uvolt < range->min_uvolt)) >>>>> + continue; >>>>> + >>>>> + if (min_uvolt <= range->min_uvolt) >>>>> + min_uvolt = range->min_uvolt; >>>>> + >>>>> + /* if step == 0 -> fixed voltage range. */ >>>>> + if (range->step_uvolt == 0) >>>>> + sel = 0; >>>>> + else >>>>> + sel = DIV_ROUND_UP(min_uvolt - range->min_uvolt, >>>>> + range->step_uvolt); >>>>> + >>>>> + >>>>> + sel += range->min_sel; >>>>> + >>>>> + break; >>>>> + } >>>>> + >>>>> + if (i >= nranges) >>>>> + return (ERANGE); >>>>> + >>>>> + /* Verify new settings. */ >>>>> + rv = regulator_range_sel8_to_volt(ranges, nranges, sel, &uvolt); >>>>> + if (rv != 0) >>>>> + return (rv); >>>>> + if ((uvolt < min_uvolt) || (uvolt > max_uvolt)) >>>>> + return (ERANGE); >>>>> + >>>>> + *out_sel = sel; >>>>> + return (0); >>>>> +} >>>>> diff --git a/freebsd/sys/dev/extres/regulator/regulator.h >>>>> b/freebsd/sys/dev/extres/regulator/regulator.h >>>>> new file mode 100644 >>>>> index 00000000..3905aa36 >>>>> --- /dev/null >>>>> +++ b/freebsd/sys/dev/extres/regulator/regulator.h >>>>> @@ -0,0 +1,156 @@ >>>>> +/*- >>>>> + * Copyright 2016 Michal Meloun <m...@freebsd.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 AND CONTRIBUTORS >> ``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 OR >> CONTRIBUTORS >>>>> 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. >>>>> + * >>>>> + * $FreeBSD$ >>>>> + */ >>>>> + >>>>> +#ifndef _DEV_EXTRES_REGULATOR_H_ >>>>> +#define _DEV_EXTRES_REGULATOR_H_ >>>>> +#include <rtems/bsd/local/opt_platform.h> >>>>> + >>>>> +#include <sys/kobj.h> >>>>> +#include <sys/sysctl.h> >>>>> +#ifdef FDT >>>>> +#include <dev/ofw/ofw_bus.h> >>>>> +#endif >>>>> +#include <rtems/bsd/local/regnode_if.h> >>>>> + >>>>> +SYSCTL_DECL(_hw_regulator); >>>>> + >>>>> +#define REGULATOR_FLAGS_STATIC 0x00000001 /* Static >> strings */ >>>>> +#define REGULATOR_FLAGS_NOT_DISABLE 0x00000002 /* Cannot >> be >>>>> disabled */ >>>>> + >>>>> +#define REGULATOR_STATUS_ENABLED 0x00000001 >>>>> +#define REGULATOR_STATUS_OVERCURRENT 0x00000002 >>>>> + >>>>> +typedef struct regulator *regulator_t; >>>>> + >>>>> +/* Standard regulator parameters. */ >>>>> +struct regnode_std_param { >>>>> + int min_uvolt; /* In uV */ >>>>> + int max_uvolt; /* In uV */ >>>>> + int min_uamp; /* In uA */ >>>>> + int max_uamp; /* In uA */ >>>>> + int ramp_delay; /* In uV/usec */ >>>>> + int enable_delay; /* In usec */ >>>>> + bool boot_on; /* Is enabled on boot */ >>>>> + bool always_on; /* Must be enabled */ >>>>> + int enable_active_high; >>>>> +}; >>>>> + >>>>> +/* Initialization parameters. */ >>>>> +struct regnode_init_def { >>>>> + char *name; /* Regulator name */ >>>>> + char *parent_name; /* Name of parent >>>>> regulator */ >>>>> + struct regnode_std_param std_param; /* Standard >>>>> parameters */ >>>>> + intptr_t id; /* Regulator ID */ >>>>> + int flags; /* Flags */ >>>>> +#ifdef FDT >>>>> + phandle_t ofw_node; /* OFW node of regulator */ >>>>> +#endif >>>>> +}; >>>>> + >>>>> +struct regulator_range { >>>>> + int min_uvolt; >>>>> + int step_uvolt; >>>>> + uint8_t min_sel; >>>>> + uint8_t max_sel; >>>>> +}; >>>>> + >>>>> +#define REG_RANGE_INIT(_min_sel, _max_sel, _min_uvolt, >>>>> _step_uvolt) { \ >>>>> + .min_sel = _min_sel, \ >>>>> + .max_sel = _max_sel, \ >>>>> + .min_uvolt = _min_uvolt, \ >>>>> + .step_uvolt = _step_uvolt, \ >>>>> +} >>>>> + >>>>> +/* >>>>> + * Shorthands for constructing method tables. >>>>> + */ >>>>> +#define REGNODEMETHOD KOBJMETHOD >>>>> +#define REGNODEMETHOD_END KOBJMETHOD_END >>>>> +#define regnode_method_t kobj_method_t >>>>> +#define regnode_class_t kobj_class_t >>>>> +DECLARE_CLASS(regnode_class); >>>>> + >>>>> +/* Providers interface. */ >>>>> +struct regnode *regnode_create(device_t pdev, regnode_class_t >>>>> regnode_class, >>>>> + struct regnode_init_def *def); >>>>> +struct regnode *regnode_register(struct regnode *regnode); >>>>> +const char *regnode_get_name(struct regnode *regnode); >>>>> +const char *regnode_get_parent_name(struct regnode *regnode); >>>>> +struct regnode *regnode_get_parent(struct regnode *regnode); >>>>> +int regnode_get_flags(struct regnode *regnode); >>>>> +void *regnode_get_softc(struct regnode *regnode); >>>>> +device_t regnode_get_device(struct regnode *regnode); >>>>> +struct regnode_std_param *regnode_get_stdparam(struct regnode >>>>> *regnode); >>>>> +void regnode_topo_unlock(void); >>>>> +void regnode_topo_xlock(void); >>>>> +void regnode_topo_slock(void); >>>>> + >>>>> +int regnode_enable(struct regnode *regnode); >>>>> +int regnode_disable(struct regnode *regnode); >>>>> +int regnode_stop(struct regnode *regnode, int depth); >>>>> +int regnode_status(struct regnode *regnode, int *status); >>>>> +int regnode_get_voltage(struct regnode *regnode, int *uvolt); >>>>> +int regnode_set_voltage(struct regnode *regnode, int min_uvolt, int >>>>> max_uvolt); >>>>> +int regnode_set_constraint(struct regnode *regnode); >>>>> + >>>>> +/* Standard method that aren't default */ >>>>> +int regnode_method_check_voltage(struct regnode *regnode, int >> uvolt); >>>>> + >>>>> +#ifdef FDT >>>>> +phandle_t regnode_get_ofw_node(struct regnode *regnode); >>>>> +#endif >>>>> + >>>>> +/* Consumers interface. */ >>>>> +int regulator_get_by_name(device_t cdev, const char *name, >>>>> + regulator_t *regulator); >>>>> +int regulator_get_by_id(device_t cdev, device_t pdev, intptr_t id, >>>>> + regulator_t *regulator); >>>>> +int regulator_release(regulator_t regulator); >>>>> +const char *regulator_get_name(regulator_t regulator); >>>>> +int regulator_enable(regulator_t reg); >>>>> +int regulator_disable(regulator_t reg); >>>>> +int regulator_stop(regulator_t reg); >>>>> +int regulator_status(regulator_t reg, int *status); >>>>> +int regulator_get_voltage(regulator_t reg, int *uvolt); >>>>> +int regulator_set_voltage(regulator_t reg, int min_uvolt, int >> max_uvolt); >>>>> +int regulator_check_voltage(regulator_t reg, int uvolt); >>>>> + >>>>> +#ifdef FDT >>>>> +int regulator_get_by_ofw_property(device_t dev, phandle_t node, >> char >>>>> *name, >>>>> + regulator_t *reg); >>>>> +int regulator_parse_ofw_stdparam(device_t dev, phandle_t node, >>>>> + struct regnode_init_def *def); >>>>> +#endif >>>>> + >>>>> +/* Utility functions */ >>>>> +int regulator_range_volt_to_sel8(struct regulator_range *ranges, int >>>>> nranges, >>>>> + int min_uvolt, int max_uvolt, uint8_t *out_sel); >>>>> +int regulator_range_sel8_to_volt(struct regulator_range *ranges, int >>>>> nranges, >>>>> + uint8_t sel, int *volt); >>>>> + >>>>> +#endif /* _DEV_EXTRES_REGULATOR_H_ */ >>>>> diff --git a/freebsd/sys/dev/extres/regulator/regulator_bus.c >>>>> b/freebsd/sys/dev/extres/regulator/regulator_bus.c >>>>> new file mode 100644 >>>>> index 00000000..b79c2633 >>>>> --- /dev/null >>>>> +++ b/freebsd/sys/dev/extres/regulator/regulator_bus.c >>>>> @@ -0,0 +1,89 @@ >>>>> +#include <machine/rtems-bsd-kernel-space.h> >>>>> + >>>>> +/*- >>>>> + * Copyright 2016 Michal Meloun <m...@freebsd.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 AND CONTRIBUTORS >> ``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 OR >> CONTRIBUTORS >>>>> 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> >>>>> +__FBSDID("$FreeBSD$"); >>>>> + >>>>> +#include <sys/param.h> >>>>> +#include <sys/systm.h> >>>>> +#include <sys/kernel.h> >>>>> +#include <sys/module.h> >>>>> +#include <sys/bus.h> >>>>> + >>>>> +#include <dev/fdt/simplebus.h> >>>>> + >>>>> +#include <dev/ofw/openfirm.h> >>>>> +#include <dev/ofw/ofw_bus_subr.h> >>>>> + >>>>> +struct ofw_regulator_bus_softc { >>>>> + struct simplebus_softc simplebus_sc; >>>>> +}; >>>>> + >>>>> +static int >>>>> +ofw_regulator_bus_probe(device_t dev) >>>>> +{ >>>>> + const char *name; >>>>> + >>>>> + name = ofw_bus_get_name(dev); >>>>> + if (name == NULL || strcmp(name, "regulators") != 0) >>>>> + return (ENXIO); >>>>> + device_set_desc(dev, "OFW regulators bus"); >>>>> + >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static int >>>>> +ofw_regulator_bus_attach(device_t dev) >>>>> +{ >>>>> + phandle_t node, child; >>>>> + >>>>> + node = ofw_bus_get_node(dev); >>>>> + simplebus_init(dev, node); >>>>> + >>>>> + for (child = OF_child(node); child > 0; child = OF_peer(child)) { >>>>> + simplebus_add_device(dev, child, 0, NULL, -1, NULL); >>>>> + } >>>>> + >>>>> + return (bus_generic_attach(dev)); >>>>> +} >>>>> + >>>>> +static device_method_t ofw_regulator_bus_methods[] = { >>>>> + /* Device interface */ >>>>> + DEVMETHOD(device_probe, ofw_regulator_bus_probe), >>>>> + DEVMETHOD(device_attach, ofw_regulator_bus_attach), >>>>> + >>>>> + DEVMETHOD_END >>>>> +}; >>>>> + >>>>> +DEFINE_CLASS_1(ofw_regulator_bus, ofw_regulator_bus_driver, >>>>> + ofw_regulator_bus_methods, sizeof(struct >> ofw_regulator_bus_softc), >>>>> + simplebus_driver); >>>>> +static devclass_t ofw_regulator_bus_devclass; >>>>> +EARLY_DRIVER_MODULE(ofw_regulator_bus, simplebus, >>>>> ofw_regulator_bus_driver, >>>>> + ofw_regulator_bus_devclass, 0, 0, BUS_PASS_BUS + >>>>> BUS_PASS_ORDER_MIDDLE); >>>>> +MODULE_VERSION(ofw_regulator_bus, 1); >>>>> diff --git a/freebsd/sys/dev/extres/regulator/regulator_fixed.c >>>>> b/freebsd/sys/dev/extres/regulator/regulator_fixed.c >>>>> new file mode 100644 >>>>> index 00000000..0bc6d96e >>>>> --- /dev/null >>>>> +++ b/freebsd/sys/dev/extres/regulator/regulator_fixed.c >>>>> @@ -0,0 +1,502 @@ >>>>> +#include <machine/rtems-bsd-kernel-space.h> >>>>> + >>>>> +/*- >>>>> + * Copyright 2016 Michal Meloun <m...@freebsd.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 AND CONTRIBUTORS >> ``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 OR >> CONTRIBUTORS >>>>> 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> >>>>> +__FBSDID("$FreeBSD$"); >>>>> + >>>>> +#include <rtems/bsd/local/opt_platform.h> >>>>> +#include <sys/param.h> >>>>> +#include <sys/conf.h> >>>>> +#include <sys/gpio.h> >>>>> +#include <sys/kernel.h> >>>>> +#include <sys/kobj.h> >>>>> +#include <sys/systm.h> >>>>> +#include <sys/module.h> >>>>> +#include <sys/mutex.h> >>>>> + >>>>> +#ifdef FDT >>>>> +#include <dev/fdt/fdt_common.h> >>>>> +#include <dev/ofw/ofw_bus.h> >>>>> +#include <dev/ofw/ofw_bus_subr.h> >>>>> +#endif >>>>> +#include <dev/gpio/gpiobusvar.h> >>>>> +#include <dev/extres/regulator/regulator_fixed.h> >>>>> + >>>>> +#include <rtems/bsd/local/regdev_if.h> >>>>> + >>>>> +MALLOC_DEFINE(M_FIXEDREGULATOR, "fixedregulator", "Fixed >>>>> regulator"); >>>>> + >>>>> +/* GPIO list for shared pins. */ >>>>> +typedef TAILQ_HEAD(gpio_list, gpio_entry) gpio_list_t; >>>>> +struct gpio_entry { >>>>> + TAILQ_ENTRY(gpio_entry) link; >>>>> + struct gpiobus_pin gpio_pin; >>>>> + int use_cnt; >>>>> + int enable_cnt; >>>>> + bool always_on; >>>>> +}; >>>>> +static gpio_list_t gpio_list = TAILQ_HEAD_INITIALIZER(gpio_list); >>>>> +static struct mtx gpio_list_mtx; >>>>> +MTX_SYSINIT(gpio_list_lock, &gpio_list_mtx, "Regulator GPIO lock", >>>>> MTX_DEF); >>>>> + >>>>> +struct regnode_fixed_sc { >>>>> + struct regnode_std_param *param; >>>>> + bool gpio_open_drain; >>>>> + struct gpio_entry *gpio_entry; >>>>> +}; >>>>> + >>>>> +static int regnode_fixed_init(struct regnode *regnode); >>>>> +static int regnode_fixed_enable(struct regnode *regnode, bool >> enable, >>>>> + int *udelay); >>>>> +static int regnode_fixed_status(struct regnode *regnode, int *status); >>>>> +static int regnode_fixed_stop(struct regnode *regnode, int *udelay); >>>>> + >>>>> +static regnode_method_t regnode_fixed_methods[] = { >>>>> + /* Regulator interface */ >>>>> + REGNODEMETHOD(regnode_init, regnode_fixed_init), >>>>> + REGNODEMETHOD(regnode_enable, >>>>> regnode_fixed_enable), >>>>> + REGNODEMETHOD(regnode_status, >>>>> regnode_fixed_status), >>>>> + REGNODEMETHOD(regnode_stop, regnode_fixed_stop), >>>>> + REGNODEMETHOD(regnode_check_voltage, >>>>> regnode_method_check_voltage), >>>>> + REGNODEMETHOD_END >>>>> +}; >>>>> +DEFINE_CLASS_1(regnode_fixed, regnode_fixed_class, >>>>> regnode_fixed_methods, >>>>> + sizeof(struct regnode_fixed_sc), regnode_class); >>>>> + >>>>> +/* >>>>> + * GPIO list functions. >>>>> + * Two or more regulators can share single GPIO pins, so we must track >> all >>>>> + * GPIOs in gpio_list. >>>>> + * The GPIO pin is registerd and reseved for first consumer, all others >> share >>>>> + * gpio_entry with it. >>>>> + */ >>>>> +static struct gpio_entry * >>>>> +regnode_get_gpio_entry(struct gpiobus_pin *gpio_pin) >>>>> +{ >>>>> + struct gpio_entry *entry, *tmp; >>>>> + device_t busdev; >>>>> + int rv; >>>>> + >>>>> + busdev = GPIO_GET_BUS(gpio_pin->dev); >>>>> + if (busdev == NULL) >>>>> + return (NULL); >>>>> + entry = malloc(sizeof(struct gpio_entry), M_FIXEDREGULATOR, >>>>> + M_WAITOK | M_ZERO); >>>>> + >>>>> + mtx_lock(&gpio_list_mtx); >>>>> + >>>>> + TAILQ_FOREACH(tmp, &gpio_list, link) { >>>>> + if (tmp->gpio_pin.dev == gpio_pin->dev && >>>>> + tmp->gpio_pin.pin == gpio_pin->pin) { >>>>> + tmp->use_cnt++; >>>>> + mtx_unlock(&gpio_list_mtx); >>>>> + free(entry, M_FIXEDREGULATOR); >>>>> + return (tmp); >>>>> + } >>>>> + } >>>>> + >>>>> + /* Reserve pin. */ >>>>> + /* XXX Can we call gpiobus_acquire_pin() with gpio_list_mtx held? */ >>>>> + rv = gpiobus_acquire_pin(busdev, gpio_pin->pin); >>>>> + if (rv != 0) { >>>>> + mtx_unlock(&gpio_list_mtx); >>>>> + free(entry, M_FIXEDREGULATOR); >>>>> + return (NULL); >>>>> + } >>>>> + /* Everything is OK, build new entry and insert it to list. */ >>>>> + entry->gpio_pin = *gpio_pin; >>>>> + entry->use_cnt = 1; >>>>> + TAILQ_INSERT_TAIL(&gpio_list, entry, link); >>>>> + >>>>> + mtx_unlock(&gpio_list_mtx); >>>>> + return (entry); >>>>> +} >>>>> + >>>>> + >>>>> +/* >>>>> + * Regulator class implementation. >>>>> + */ >>>>> +static int >>>>> +regnode_fixed_init(struct regnode *regnode) >>>>> +{ >>>>> + device_t dev; >>>>> + struct regnode_fixed_sc *sc; >>>>> + struct gpiobus_pin *pin; >>>>> + uint32_t flags; >>>>> + int rv; >>>>> + >>>>> + sc = regnode_get_softc(regnode); >>>>> + dev = regnode_get_device(regnode); >>>>> + sc->param = regnode_get_stdparam(regnode); >>>>> + if (sc->gpio_entry == NULL) >>>>> + return (0); >>>>> + pin = &sc->gpio_entry->gpio_pin; >>>>> + >>>>> + flags = GPIO_PIN_OUTPUT; >>>>> + if (sc->gpio_open_drain) >>>>> + flags |= GPIO_PIN_OPENDRAIN; >>>>> + if (sc->param->boot_on || sc->param->always_on) { >>>>> + rv = GPIO_PIN_SET(pin->dev, pin->pin, sc->param- >>>>>> enable_active_high); >>>>> + if (rv != 0) { >>>>> + device_printf(dev, "Cannot set GPIO pin: %d\n", >>>>> + pin->pin); >>>>> + return (rv); >>>>> + } >>>>> + } >>>>> + >>>>> + rv = GPIO_PIN_SETFLAGS(pin->dev, pin->pin, flags); >>>>> + if (rv != 0) { >>>>> + device_printf(dev, "Cannot configure GPIO pin: %d\n", pin- >>>>>> pin); >>>>> + return (rv); >>>>> + } >>>>> + >>>>> + return (0); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Enable/disable regulator. >>>>> + * Take shared GPIO pins in account >>>>> + */ >>>>> +static int >>>>> +regnode_fixed_enable(struct regnode *regnode, bool enable, int >> *udelay) >>>>> +{ >>>>> + device_t dev; >>>>> + struct regnode_fixed_sc *sc; >>>>> + struct gpiobus_pin *pin; >>>>> + int rv; >>>>> + >>>>> + sc = regnode_get_softc(regnode); >>>>> + dev = regnode_get_device(regnode); >>>>> + >>>>> + *udelay = 0; >>>>> + if (sc->gpio_entry == NULL) >>>>> + return (0); >>>>> + pin = &sc->gpio_entry->gpio_pin; >>>>> + if (enable) { >>>>> + sc->gpio_entry->enable_cnt++; >>>>> + if (sc->gpio_entry->enable_cnt > 1) >>>>> + return (0); >>>>> + } else { >>>>> + KASSERT(sc->gpio_entry->enable_cnt > 0, >>>>> + ("Invalid enable count")); >>>>> + sc->gpio_entry->enable_cnt--; >>>>> + if (sc->gpio_entry->enable_cnt >= 1) >>>>> + return (0); >>>>> + } >>>>> + if (sc->gpio_entry->always_on && !enable) >>>>> + return (0); >>>>> + if (!sc->param->enable_active_high) >>>>> + enable = !enable; >>>>> + rv = GPIO_PIN_SET(pin->dev, pin->pin, enable); >>>>> + if (rv != 0) { >>>>> + device_printf(dev, "Cannot set GPIO pin: %d\n", pin->pin); >>>>> + return (rv); >>>>> + } >>>>> + *udelay = sc->param->enable_delay; >>>>> + return (0); >>>>> +} >>>>> + >>>>> +/* >>>>> + * Stop (physicaly shutdown) regulator. >>>>> + * Take shared GPIO pins in account >>>>> + */ >>>>> +static int >>>>> +regnode_fixed_stop(struct regnode *regnode, int *udelay) >>>>> +{ >>>>> + device_t dev; >>>>> + struct regnode_fixed_sc *sc; >>>>> + struct gpiobus_pin *pin; >>>>> + int rv; >>>>> + >>>>> + sc = regnode_get_softc(regnode); >>>>> + dev = regnode_get_device(regnode); >>>>> + >>>>> + *udelay = 0; >>>>> + if (sc->gpio_entry == NULL) >>>>> + return (0); >>>>> + if (sc->gpio_entry->always_on) >>>>> + return (0); >>>>> + pin = &sc->gpio_entry->gpio_pin; >>>>> + if (sc->gpio_entry->enable_cnt > 0) { >>>>> + /* Other regulator(s) are enabled. */ >>>>> + /* XXXX Any diagnostic message? Or error? */ >>>>> + return (0); >>>>> + } >>>>> + rv = GPIO_PIN_SET(pin->dev, pin->pin, >>>>> + sc->param->enable_active_high ? false: true); >>>>> + if (rv != 0) { >>>>> + device_printf(dev, "Cannot set GPIO pin: %d\n", pin->pin); >>>>> + return (rv); >>>>> + } >>>>> + *udelay = sc->param->enable_delay; >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static int >>>>> +regnode_fixed_status(struct regnode *regnode, int *status) >>>>> +{ >>>>> + struct regnode_fixed_sc *sc; >>>>> + struct gpiobus_pin *pin; >>>>> + uint32_t val; >>>>> + int rv; >>>>> + >>>>> + sc = regnode_get_softc(regnode); >>>>> + >>>>> + *status = 0; >>>>> + if (sc->gpio_entry == NULL) { >>>>> + *status = REGULATOR_STATUS_ENABLED; >>>>> + return (0); >>>>> + } >>>>> + pin = &sc->gpio_entry->gpio_pin; >>>>> + >>>>> + rv = GPIO_PIN_GET(pin->dev, pin->pin, &val); >>>>> + if (rv == 0) { >>>>> + if (!sc->param->enable_active_high ^ (val != 0)) >>>>> + *status = REGULATOR_STATUS_ENABLED; >>>>> + } >>>>> + return (rv); >>>>> +} >>>>> + >>>>> +int >>>>> +regnode_fixed_register(device_t dev, struct regnode_fixed_init_def >>>>> *init_def) >>>>> +{ >>>>> + struct regnode *regnode; >>>>> + struct regnode_fixed_sc *sc; >>>>> + >>>>> + regnode = regnode_create(dev, ®node_fixed_class, >>>>> + &init_def->reg_init_def); >>>>> + if (regnode == NULL) { >>>>> + device_printf(dev, "Cannot create regulator.\n"); >>>>> + return(ENXIO); >>>>> + } >>>>> + sc = regnode_get_softc(regnode); >>>>> + sc->gpio_open_drain = init_def->gpio_open_drain; >>>>> + if (init_def->gpio_pin != NULL) { >>>>> + sc->gpio_entry = regnode_get_gpio_entry(init_def- >>>>>> gpio_pin); >>>>> + if (sc->gpio_entry == NULL) >>>>> + return(ENXIO); >>>>> + } >>>>> + regnode = regnode_register(regnode); >>>>> + if (regnode == NULL) { >>>>> + device_printf(dev, "Cannot register regulator.\n"); >>>>> + return(ENXIO); >>>>> + } >>>>> + >>>>> + if (sc->gpio_entry != NULL) >>>>> + sc->gpio_entry->always_on |= sc->param->always_on; >>>>> + >>>>> + return (0); >>>>> +} >>>>> + >>>>> +/* >>>>> + * OFW Driver implementation. >>>>> + */ >>>>> +#ifdef FDT >>>>> + >>>>> +struct regfix_softc >>>>> +{ >>>>> + device_t dev; >>>>> + bool attach_done; >>>>> + struct regnode_fixed_init_def init_def; >>>>> + phandle_t gpio_prodxref; >>>>> + pcell_t *gpio_cells; >>>>> + int gpio_ncells; >>>>> + struct gpiobus_pin gpio_pin; >>>>> +}; >>>>> + >>>>> +static struct ofw_compat_data compat_data[] = { >>>>> + {"regulator-fixed", 1}, >>>>> + {NULL, 0}, >>>>> +}; >>>>> + >>>>> +static int >>>>> +regfix_get_gpio(struct regfix_softc * sc) >>>>> +{ >>>>> + device_t busdev; >>>>> + phandle_t node; >>>>> + >>>>> + int rv; >>>>> + >>>>> + if (sc->gpio_prodxref == 0) >>>>> + return (0); >>>>> + >>>>> + node = ofw_bus_get_node(sc->dev); >>>>> + >>>>> + /* Test if controller exist. */ >>>>> + sc->gpio_pin.dev = OF_device_from_xref(sc->gpio_prodxref); >>>>> + if (sc->gpio_pin.dev == NULL) >>>>> + return (ENODEV); >>>>> + >>>>> + /* Test if GPIO bus already exist. */ >>>>> + busdev = GPIO_GET_BUS(sc->gpio_pin.dev); >>>>> + if (busdev == NULL) >>>>> + return (ENODEV); >>>>> + >>>>> + rv = gpio_map_gpios(sc->gpio_pin.dev, node, >>>>> + OF_node_from_xref(sc->gpio_prodxref), sc->gpio_ncells, >>>>> + sc->gpio_cells, &(sc->gpio_pin.pin), &(sc->gpio_pin.flags)); >>>>> + if (rv != 0) { >>>>> + device_printf(sc->dev, "Cannot map the gpio property.\n"); >>>>> + return (ENXIO); >>>>> + } >>>>> + sc->init_def.gpio_pin = &sc->gpio_pin; >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static int >>>>> +regfix_parse_fdt(struct regfix_softc * sc) >>>>> +{ >>>>> + phandle_t node; >>>>> + int rv; >>>>> + struct regnode_init_def *init_def; >>>>> + >>>>> + node = ofw_bus_get_node(sc->dev); >>>>> + init_def = &sc->init_def.reg_init_def; >>>>> + >>>>> + rv = regulator_parse_ofw_stdparam(sc->dev, node, init_def); >>>>> + if (rv != 0) { >>>>> + device_printf(sc->dev, "Cannot parse standard >>>>> parameters.\n"); >>>>> + return(rv); >>>>> + } >>>>> + >>>>> + /* Fixed regulator uses 'startup-delay-us' property for enable_delay >>>>> */ >>>>> + rv = OF_getencprop(node, "startup-delay-us", >>>>> + &init_def->std_param.enable_delay, >>>>> + sizeof(init_def->std_param.enable_delay)); >>>>> + if (rv <= 0) >>>>> + init_def->std_param.enable_delay = 0; >>>>> + /* GPIO pin */ >>>>> + if (OF_hasprop(node, "gpio-open-drain")) >>>>> + sc->init_def.gpio_open_drain = true; >>>>> + >>>>> + if (!OF_hasprop(node, "gpio")) >>>>> + return (0); >>>>> + rv = ofw_bus_parse_xref_list_alloc(node, "gpio", "#gpio-cells", 0, >>>>> + &sc->gpio_prodxref, &sc->gpio_ncells, &sc->gpio_cells); >>>>> + if (rv != 0) { >>>>> + sc->gpio_prodxref = 0; >>>>> + device_printf(sc->dev, "Malformed gpio property\n"); >>>>> + return (ENXIO); >>>>> + } >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static void >>>>> +regfix_new_pass(device_t dev) >>>>> +{ >>>>> + struct regfix_softc * sc; >>>>> + int rv; >>>>> + >>>>> + sc = device_get_softc(dev); >>>>> + bus_generic_new_pass(dev); >>>>> + >>>>> + if (sc->attach_done) >>>>> + return; >>>>> + >>>>> + /* Try to get and configure GPIO. */ >>>>> + rv = regfix_get_gpio(sc); >>>>> + if (rv != 0) >>>>> + return; >>>>> + >>>>> + /* Register regulator. */ >>>>> + regnode_fixed_register(sc->dev, &sc->init_def); >>>>> + sc->attach_done = true; >>>>> +} >>>>> + >>>>> +static int >>>>> +regfix_probe(device_t dev) >>>>> +{ >>>>> + >>>>> + if (!ofw_bus_status_okay(dev)) >>>>> + return (ENXIO); >>>>> + >>>>> + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) >>>>> + return (ENXIO); >>>>> + >>>>> + device_set_desc(dev, "Fixed Regulator"); >>>>> + return (BUS_PROBE_DEFAULT); >>>>> +} >>>>> + >>>>> +static int >>>>> +regfix_detach(device_t dev) >>>>> +{ >>>>> + >>>>> + /* This device is always present. */ >>>>> + return (EBUSY); >>>>> +} >>>>> + >>>>> +static int >>>>> +regfix_attach(device_t dev) >>>>> +{ >>>>> + struct regfix_softc * sc; >>>>> + int rv; >>>>> + >>>>> + sc = device_get_softc(dev); >>>>> + sc->dev = dev; >>>>> + >>>>> + /* Parse FDT data. */ >>>>> + rv = regfix_parse_fdt(sc); >>>>> + if (rv != 0) >>>>> + return(ENXIO); >>>>> + >>>>> + /* Fill reset of init. */ >>>>> + sc->init_def.reg_init_def.id = 1; >>>>> + sc->init_def.reg_init_def.flags = REGULATOR_FLAGS_STATIC; >>>>> + >>>>> + /* Try to get and configure GPIO. */ >>>>> + rv = regfix_get_gpio(sc); >>>>> + if (rv != 0) >>>>> + return (bus_generic_attach(dev)); >>>>> + >>>>> + /* Register regulator. */ >>>>> + regnode_fixed_register(sc->dev, &sc->init_def); >>>>> + sc->attach_done = true; >>>>> + >>>>> + return (bus_generic_attach(dev)); >>>>> +} >>>>> + >>>>> +static device_method_t regfix_methods[] = { >>>>> + /* Device interface */ >>>>> + DEVMETHOD(device_probe, regfix_probe), >>>>> + DEVMETHOD(device_attach, regfix_attach), >>>>> + DEVMETHOD(device_detach, regfix_detach), >>>>> + /* Bus interface */ >>>>> + DEVMETHOD(bus_new_pass, regfix_new_pass), >>>>> + /* Regdev interface */ >>>>> + DEVMETHOD(regdev_map, regdev_default_ofw_map), >>>>> + >>>>> + DEVMETHOD_END >>>>> +}; >>>>> + >>>>> +static devclass_t regfix_devclass; >>>>> +DEFINE_CLASS_0(regfix, regfix_driver, regfix_methods, >>>>> + sizeof(struct regfix_softc)); >>>>> +EARLY_DRIVER_MODULE(regfix, simplebus, regfix_driver, >>>>> + regfix_devclass, 0, 0, BUS_PASS_BUS); >>>>> + >>>>> +#endif /* FDT */ >>>>> diff --git a/freebsd/sys/dev/extres/regulator/regulator_fixed.h >>>>> b/freebsd/sys/dev/extres/regulator/regulator_fixed.h >>>>> new file mode 100644 >>>>> index 00000000..5cc07516 >>>>> --- /dev/null >>>>> +++ b/freebsd/sys/dev/extres/regulator/regulator_fixed.h >>>>> @@ -0,0 +1,44 @@ >>>>> +/*- >>>>> + * Copyright 2016 Michal Meloun <m...@freebsd.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 AND CONTRIBUTORS >> ``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 OR >> CONTRIBUTORS >>>>> 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. >>>>> + * >>>>> + * $FreeBSD$ >>>>> + */ >>>>> + >>>>> +#ifndef _DEV_EXTRES_REGULATOR_FIXED_H_ >>>>> +#define _DEV_EXTRES_REGULATOR_FIXED_H_ >>>>> + >>>>> +#include <dev/gpio/gpiobusvar.h> >>>>> +#include <dev/extres/regulator/regulator.h> >>>>> + >>>>> +struct regnode_fixed_init_def { >>>>> + struct regnode_init_def reg_init_def; >>>>> + bool gpio_open_drain; >>>>> + struct gpiobus_pin *gpio_pin; >>>>> +}; >>>>> + >>>>> +int regnode_fixed_register(device_t dev, >>>>> + struct regnode_fixed_init_def *init_def); >>>>> + >>>>> +#endif /*_DEV_EXTRES_REGULATOR_FIXED_H_*/ >>>>> diff --git a/freebsd/sys/dev/gpio/gpioregulator.c >>>>> b/freebsd/sys/dev/gpio/gpioregulator.c >>>>> new file mode 100644 >>>>> index 00000000..6d05e52e >>>>> --- /dev/null >>>>> +++ b/freebsd/sys/dev/gpio/gpioregulator.c >>>>> @@ -0,0 +1,350 @@ >>>>> +#include <machine/rtems-bsd-kernel-space.h> >>>>> + >>>>> +/*- >>>>> + * Copyright (c) 2016 Jared McNeill <jmcne...@invisible.ca> >>>>> + * 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. >>>>> + * >>>>> + * $FreeBSD$ >>>>> + */ >>>>> + >>>>> +/* >>>>> + * GPIO controlled regulators >>>>> + */ >>>>> + >>>>> +#include <sys/cdefs.h> >>>>> +__FBSDID("$FreeBSD$"); >>>>> + >>>>> +#include <sys/param.h> >>>>> +#include <sys/systm.h> >>>>> +#include <sys/bus.h> >>>>> +#include <sys/rman.h> >>>>> +#include <sys/kernel.h> >>>>> +#include <sys/module.h> >>>>> +#include <sys/gpio.h> >>>>> + >>>>> +#include <dev/ofw/ofw_bus.h> >>>>> +#include <dev/ofw/ofw_bus_subr.h> >>>>> + >>>>> +#include <dev/gpio/gpiobusvar.h> >>>>> + >>>>> +#include <dev/extres/regulator/regulator.h> >>>>> + >>>>> +#include <rtems/bsd/local/regdev_if.h> >>>>> + >>>>> +struct gpioregulator_state { >>>>> + int val; >>>>> + uint32_t mask; >>>>> +}; >>>>> + >>>>> +struct gpioregulator_init_def { >>>>> + struct regnode_init_def reg_init_def; >>>>> + struct gpiobus_pin *enable_pin; >>>>> + int enable_pin_valid; >>>>> + int startup_delay_us; >>>>> + int nstates; >>>>> + struct gpioregulator_state *states; >>>>> + int npins; >>>>> + struct gpiobus_pin **pins; >>>>> +}; >>>>> + >>>>> +struct gpioregulator_reg_sc { >>>>> + struct regnode *regnode; >>>>> + device_t base_dev; >>>>> + struct regnode_std_param *param; >>>>> + struct gpioregulator_init_def *def; >>>>> +}; >>>>> + >>>>> +struct gpioregulator_softc { >>>>> + device_t dev; >>>>> + struct gpioregulator_reg_sc *reg_sc; >>>>> + struct gpioregulator_init_def init_def; >>>>> +}; >>>>> + >>>>> +static int >>>>> +gpioregulator_regnode_init(struct regnode *regnode) >>>>> +{ >>>>> + struct gpioregulator_reg_sc *sc; >>>>> + int error, n; >>>>> + >>>>> + sc = regnode_get_softc(regnode); >>>>> + >>>>> + if (sc->def->enable_pin_valid == 1) { >>>>> + error = gpio_pin_setflags(sc->def->enable_pin, >>>>> GPIO_PIN_OUTPUT); >>>>> + if (error != 0) >>>>> + return (error); >>>>> + } >>>>> + >>>>> + for (n = 0; n < sc->def->npins; n++) { >>>>> + error = gpio_pin_setflags(sc->def->pins[n], >>>>> GPIO_PIN_OUTPUT); >>>>> + if (error != 0) >>>>> + return (error); >>>>> + } >>>>> + >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static int >>>>> +gpioregulator_regnode_enable(struct regnode *regnode, bool enable, >> int >>>>> *udelay) >>>>> +{ >>>>> + struct gpioregulator_reg_sc *sc; >>>>> + bool active; >>>>> + int error; >>>>> + >>>>> + sc = regnode_get_softc(regnode); >>>>> + >>>>> + if (sc->def->enable_pin_valid == 1) { >>>>> + active = enable; >>>>> + if (!sc->param->enable_active_high) >>>>> + active = !active; >>>>> + error = gpio_pin_set_active(sc->def->enable_pin, active); >>>>> + if (error != 0) >>>>> + return (error); >>>>> + } >>>>> + >>>>> + *udelay = sc->def->startup_delay_us; >>>>> + >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static int >>>>> +gpioregulator_regnode_set_voltage(struct regnode *regnode, int >>>>> min_uvolt, >>>>> + int max_uvolt, int *udelay) >>>>> +{ >>>>> + struct gpioregulator_reg_sc *sc; >>>>> + const struct gpioregulator_state *state; >>>>> + int error, n; >>>>> + >>>>> + sc = regnode_get_softc(regnode); >>>>> + state = NULL; >>>>> + >>>>> + for (n = 0; n < sc->def->nstates; n++) { >>>>> + if (sc->def->states[n].val >= min_uvolt && >>>>> + sc->def->states[n].val <= max_uvolt) { >>>>> + state = &sc->def->states[n]; >>>>> + break; >>>>> + } >>>>> + } >>>>> + if (state == NULL) >>>>> + return (EINVAL); >>>>> + >>>>> + for (n = 0; n < sc->def->npins; n++) { >>>>> + error = gpio_pin_set_active(sc->def->pins[n], >>>>> + (state->mask >> n) & 1); >>>>> + if (error != 0) >>>>> + return (error); >>>>> + } >>>>> + >>>>> + *udelay = sc->def->startup_delay_us; >>>>> + >>>>> + return (0); >>>>> +} >>>>> + >>>>> +static int >>>>> +gpioregulator_regnode_get_voltage(struct regnode *regnode, int >> *uvolt) >>>>> +{ >>>>> + struct gpioregulator_reg_sc *sc; >>>>> + uint32_t mask; >>>>> + int error, n; >>>>> + bool active; >>>>> + >>>>> + sc = regnode_get_softc(regnode); >>>>> + mask = 0; >>>>> + >>>>> + for (n = 0; n < sc->def->npins; n++) { >>>>> + error = gpio_pin_is_active(sc->def->pins[n], &active); >>>>> + if (error != 0) >>>>> + return (error); >>>>> + mask |= (active << n); >>>>> + } >>>>> + >>>>> + for (n = 0; n < sc->def->nstates; n++) { >>>>> + if (sc->def->states[n].mask == mask) { >>>>> + *uvolt = sc->def->states[n].val; >>>>> + return (0); >>>>> + } >>>>> + } >>>>> + >>>>> + return (EIO); >>>>> +} >>>>> + >>>>> +static regnode_method_t gpioregulator_regnode_methods[] = { >>>>> + /* Regulator interface */ >>>>> + REGNODEMETHOD(regnode_init, gpioregulator_regnode_init), >>>>> + REGNODEMETHOD(regnode_enable, >>>>> gpioregulator_regnode_enable), >>>>> + REGNODEMETHOD(regnode_set_voltage, >>>>> gpioregulator_regnode_set_voltage), >>>>> + REGNODEMETHOD(regnode_get_voltage, >>>>> gpioregulator_regnode_get_voltage), >>>>> + REGNODEMETHOD_END >>>>> +}; >>>>> +DEFINE_CLASS_1(gpioregulator_regnode, >> gpioregulator_regnode_class, >>>>> + gpioregulator_regnode_methods, sizeof(struct >> gpioregulator_reg_sc), >>>>> + regnode_class); >>>>> + >>>>> +static int >>>>> +gpioregulator_parse_fdt(struct gpioregulator_softc *sc) >>>>> +{ >>>>> + uint32_t *pstates, mask; >>>>> + phandle_t node; >>>>> + ssize_t len; >>>>> + int error, n; >>>>> + >>>>> + node = ofw_bus_get_node(sc->dev); >>>>> + pstates = NULL; >>>>> + mask = 0; >>>>> + >>>>> + error = regulator_parse_ofw_stdparam(sc->dev, node, >>>>> + &sc->init_def.reg_init_def); >>>>> + if (error != 0) >>>>> + return (error); >>>>> + >>>>> + /* "states" property (required) */ >>>>> + len = OF_getencprop_alloc_multi(node, "states", sizeof(*pstates), >>>>> + (void **)&pstates); >>>>> + if (len < 2) { >>>>> + device_printf(sc->dev, "invalid 'states' property\n"); >>>>> + error = EINVAL; >>>>> + goto done; >>>>> + } >>>>> + sc->init_def.nstates = len / 2; >>>>> + sc->init_def.states = malloc(sc->init_def.nstates * >>>>> + sizeof(*sc->init_def.states), M_DEVBUF, M_WAITOK); >>>>> + for (n = 0; n < sc->init_def.nstates; n++) { >>>>> + sc->init_def.states[n].val = pstates[n * 2 + 0]; >>>>> + sc->init_def.states[n].mask = pstates[n * 2 + 1]; >>>>> + mask |= sc->init_def.states[n].mask; >>>>> + } >>>>> + >>>>> + /* "startup-delay-us" property (optional) */ >>>>> + len = OF_getencprop(node, "startup-delay-us", >>>>> + &sc->init_def.startup_delay_us, >>>>> + sizeof(sc->init_def.startup_delay_us)); >>>>> + if (len <= 0) >>>>> + sc->init_def.startup_delay_us = 0; >>>>> + >>>>> + /* "enable-gpio" property (optional) */ >>>>> + error = gpio_pin_get_by_ofw_property(sc->dev, node, "enable- >>>>> gpio", >>>>> + &sc->init_def.enable_pin); >>>>> + if (error == 0) >>>>> + sc->init_def.enable_pin_valid = 1; >>>>> + >>>>> + /* "gpios" property */ >>>>> + sc->init_def.npins = 32 - __builtin_clz(mask); >>>>> + sc->init_def.pins = malloc(sc->init_def.npins * >>>>> + sizeof(sc->init_def.pins), M_DEVBUF, M_WAITOK); >>>>> + for (n = 0; n < sc->init_def.npins; n++) { >>>>> + error = gpio_pin_get_by_ofw_idx(sc->dev, node, n, >>>>> + &sc->init_def.pins[n]); >>>>> + if (error != 0) { >>>>> + device_printf(sc->dev, "cannot get pin %d\n", n); >>>>> + goto done; >>>>> + } >>>>> + } >>>>> + >>>>> +done: >>>>> + if (error != 0) { >>>>> + for (n = 0; n < sc->init_def.npins; n++) { >>>>> + if (sc->init_def.pins[n] != NULL) >>>>> + gpio_pin_release(sc->init_def.pins[n]); >>>>> + } >>>>> + >>>>> + free(sc->init_def.states, M_DEVBUF); >>>>> + free(sc->init_def.pins, M_DEVBUF); >>>>> + >>>>> + } >>>>> + OF_prop_free(pstates); >>>>> + >>>>> + return (error); >>>>> +} >>>>> + >>>>> +static int >>>>> +gpioregulator_probe(device_t dev) >>>>> +{ >>>>> + >>>>> + if (!ofw_bus_is_compatible(dev, "regulator-gpio")) >>>>> + return (ENXIO); >>>>> + >>>>> + device_set_desc(dev, "GPIO controlled regulator"); >>>>> + return (BUS_PROBE_GENERIC); >>>>> +} >>>>> + >>>>> +static int >>>>> +gpioregulator_attach(device_t dev) >>>>> +{ >>>>> + struct gpioregulator_softc *sc; >>>>> + struct regnode *regnode; >>>>> + phandle_t node; >>>>> + int error; >>>>> + >>>>> + sc = device_get_softc(dev); >>>>> + sc->dev = dev; >>>>> + node = ofw_bus_get_node(dev); >>>>> + >>>>> + error = gpioregulator_parse_fdt(sc); >>>>> + if (error != 0) { >>>>> + device_printf(dev, "cannot parse parameters\n"); >>>>> + return (ENXIO); >>>>> + } >>>>> + sc->init_def.reg_init_def.id = 1; >>>>> + sc->init_def.reg_init_def.ofw_node = node; >>>>> + >>>>> + regnode = regnode_create(dev, &gpioregulator_regnode_class, >>>>> + &sc->init_def.reg_init_def); >>>>> + if (regnode == NULL) { >>>>> + device_printf(dev, "cannot create regulator\n"); >>>>> + return (ENXIO); >>>>> + } >>>>> + >>>>> + sc->reg_sc = regnode_get_softc(regnode); >>>>> + sc->reg_sc->regnode = regnode; >>>>> + sc->reg_sc->base_dev = dev; >>>>> + sc->reg_sc->param = regnode_get_stdparam(regnode); >>>>> + sc->reg_sc->def = &sc->init_def; >>>>> + >>>>> + regnode_register(regnode); >>>>> + >>>>> + return (0); >>>>> +} >>>>> + >>>>> + >>>>> +static device_method_t gpioregulator_methods[] = { >>>>> + /* Device interface */ >>>>> + DEVMETHOD(device_probe, gpioregulator_probe), >>>>> + DEVMETHOD(device_attach, gpioregulator_attach), >>>>> + >>>>> + /* Regdev interface */ >>>>> + DEVMETHOD(regdev_map, regdev_default_ofw_map), >>>>> + >>>>> + DEVMETHOD_END >>>>> +}; >>>>> + >>>>> +static driver_t gpioregulator_driver = { >>>>> + "gpioregulator", >>>>> + gpioregulator_methods, >>>>> + sizeof(struct gpioregulator_softc), >>>>> +}; >>>>> + >>>>> +static devclass_t gpioregulator_devclass; >>>>> + >>>>> +EARLY_DRIVER_MODULE(gpioregulator, simplebus, >> gpioregulator_driver, >>>>> + gpioregulator_devclass, 0, 0, BUS_PASS_INTERRUPT + >>>>> BUS_PASS_ORDER_LAST); >>>>> +MODULE_VERSION(gpioregulator, 1); >>>>> -- >>>>> 2.16.4 >>>>> >>>>> _______________________________________________ >>>>> devel mailing list >>>>> devel@rtems.org >>>>> http://lists.rtems.org/mailman/listinfo/devel >>> >> >> -- >> -------------------------------------------- >> embedded brains GmbH >> Herr Christian Mauderer >> Dornierstr. 4 >> D-82178 Puchheim >> Germany >> email: christian.maude...@embedded-brains.de >> Phone: +49-89-18 94 741 - 18 >> Fax: +49-89-18 94 741 - 08 >> PGP: Public key available on request. >> >> Diese Nachricht ist keine geschäftliche Mitteilung im Sinne des EHUG. -- -------------------------------------------- embedded brains GmbH Herr Christian Mauderer Dornierstr. 4 D-82178 Puchheim Germany email: christian.maude...@embedded-brains.de Phone: +49-89-18 94 741 - 18 Fax: +49-89-18 94 741 - 08 PGP: Public key available on request. Diese Nachricht ist keine geschäftliche Mitteilung im Sinne des EHUG. _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel