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