Date: Tue, 15 Mar 2016 15:30:17 +0000 (UTC) From: Michal Meloun <mmel@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r296906 - in head/sys: conf dev/extres/regulator Message-ID: <201603151530.u2FFUHoH072725@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: mmel Date: Tue Mar 15 15:30:17 2016 New Revision: 296906 URL: https://svnweb.freebsd.org/changeset/base/296906 Log: Add regulator framework, a next part of new 'extended resources' family of support frameworks(i.e. clk/reset/phy/tsensors/fuses...). The framework is still far from perfect and probably doesn't have stable interface yet, but we want to start testing it on more real boards and different architectures. Added: head/sys/dev/extres/regulator/ head/sys/dev/extres/regulator/regdev_if.m (contents, props changed) head/sys/dev/extres/regulator/regnode_if.m (contents, props changed) head/sys/dev/extres/regulator/regulator.c (contents, props changed) head/sys/dev/extres/regulator/regulator.h (contents, props changed) head/sys/dev/extres/regulator/regulator_bus.c (contents, props changed) head/sys/dev/extres/regulator/regulator_fixed.c (contents, props changed) head/sys/dev/extres/regulator/regulator_fixed.h (contents, props changed) Modified: head/sys/conf/files Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Tue Mar 15 15:28:24 2016 (r296905) +++ head/sys/conf/files Tue Mar 15 15:30:17 2016 (r296906) @@ -1420,6 +1420,11 @@ dev/extres/clk/clk_gate.c optional ext_r dev/extres/clk/clk_mux.c optional ext_resources clk dev/extres/hwreset/hwreset.c optional ext_resources hwreset dev/extres/hwreset/hwreset_if.m optional ext_resources hwreset +dev/extres/regulator/regdev_if.m optional ext_resources regulator +dev/extres/regulator/regnode_if.m optional ext_resources regulator +dev/extres/regulator/regulator.c optional ext_resources regulator +dev/extres/regulator/regulator_bus.c optional ext_resources regulator fdt +dev/extres/regulator/regulator_fixed.c optional ext_resources regulator dev/fatm/if_fatm.c optional fatm pci dev/fb/fbd.c optional fbd | vt dev/fb/fb_if.m standard @@ -1561,10 +1566,10 @@ ipw_monitor.fw optional ipwmonitorfw | compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "ipw_monitor.fw" -dev/iscsi/icl.c optional iscsi | ctl -dev/iscsi/icl_conn_if.m optional iscsi | ctl +dev/iscsi/icl.c optional iscsi | ctl +dev/iscsi/icl_conn_if.m optional iscsi | ctl dev/iscsi/icl_proxy.c optional iscsi | ctl -dev/iscsi/icl_soft.c optional iscsi | ctl +dev/iscsi/icl_soft.c optional iscsi | ctl dev/iscsi/iscsi.c optional iscsi scbus dev/iscsi_initiator/iscsi.c optional iscsi_initiator scbus dev/iscsi_initiator/iscsi_subr.c optional iscsi_initiator scbus Added: head/sys/dev/extres/regulator/regdev_if.m ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/extres/regulator/regdev_if.m Tue Mar 15 15:30:17 2016 (r296906) @@ -0,0 +1,56 @@ +#- +# Copyright 2016 Michal Meloun <mmel@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$ +# + +#ifdef FDT +#include <sys/types.h> +#include <dev/ofw/ofw_bus.h> +#endif + +#include <machine/bus.h> + +INTERFACE regdev; + +#ifdef FDT + +HEADER { +int regdev_default_ofw_map(device_t , phandle_t, int, pcell_t *, intptr_t *); +} + +# +# map fdt property cells to regulator number +# Returns 0 on success or a standard errno value. +# +METHOD int map { + device_t provider_dev; + phandle_t xref; + int ncells; + pcell_t *cells; + intptr_t *id; +} DEFAULT regdev_default_ofw_map; + +#endif Added: head/sys/dev/extres/regulator/regnode_if.m ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/extres/regulator/regnode_if.m Tue Mar 15 15:30:17 2016 (r296906) @@ -0,0 +1,82 @@ +#- +# Copyright (c) 2016 Michal Meloun <mmel@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$ +# + +INTERFACE regnode; +HEADER { + struct regnode; +} + +# +# Initialize regulator +# Returns 0 on success or a standard errno value. +# +METHOD int init { + struct regnode *regnode; +}; + +# +# Enable/disable regulator +# Returns 0 on success or a standard errno value. +# - enable - input. +# - delay - output, delay needed to stabilize voltage (in us) +# +METHOD int enable { + struct regnode *regnode; + bool enable; + int *udelay; +}; + +# +# Get regulator status +# Returns 0 on success or a standard errno value. +# +METHOD int status { + struct regnode *regnode; + int *status; /* REGULATOR_STATUS_* */ +}; + +# +# Set regulator voltage +# Returns 0 on success or a standard errno value. +# - min_uvolt, max_uvolt - input, requested voltage range (in uV) +# - delay - output, delay needed to stabilize voltage (in us) +METHOD int set_voltage { + struct regnode *regnode; + int min_uvolt; + int max_uvolt; + int *udelay; +}; + +# +# Get regulator voltage +# Returns 0 on success or a standard errno value. +# +METHOD int get_voltage { + struct regnode *regnode; + int *uvolt; +}; Added: head/sys/dev/extres/regulator/regulator.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/extres/regulator/regulator.c Tue Mar 15 15:30:17 2016 (r296906) @@ -0,0 +1,984 @@ +/*- + * Copyright 2016 Michal Meloun <mmel@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 "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 "regdev_if.h" + +MALLOC_DEFINE(M_REGULATOR, "regulator", "Regulator framework"); + +/* 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_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); + +/* + * Regulator controller methods. + */ +static regnode_method_t regnode_methods[] = { + 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_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 */ +}; + +/* + * 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)) + +/* ---------------------------------------------------------------------------- + * + * Default regulator methods for base class. + * + */ +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); +} + +/* ---------------------------------------------------------------------------- + * + * 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; + + 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 + + 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)) { + 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_ENABLE(regnode, false, &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) + 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); +} + +#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); +} + +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) +{ *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201603151530.u2FFUHoH072725>