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. _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel