opnsense-src/sys/dev/extres/regulator/regulator.c
Michal Meloun dac935533b EXTRES: Add OF node as argument to all <foo>_get_by_ofw_<bar>() functions.
In some cases, the driver must handle given properties located in
specific OF subnode. Instead of creating duplicate set of function, add
'node' as argument to existing functions, defaulting it to device OF node.

MFC after: 3 weeks
2016-07-10 18:28:15 +00:00

986 lines
21 KiB
C

/*-
* 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, &regnode_topo_lock, "Regulator topology lock");
#define REG_TOPO_SLOCK() sx_slock(&regnode_topo_lock)
#define REG_TOPO_XLOCK() sx_xlock(&regnode_topo_lock)
#define REG_TOPO_UNLOCK() sx_unlock(&regnode_topo_lock)
#define REG_TOPO_ASSERT() sx_assert(&regnode_topo_lock, SA_LOCKED)
#define REG_TOPO_XASSERT() sx_assert(&regnode_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, &regnode_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, &regnode_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(&regnode->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(&regnode->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(&regnode_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 (&regnode->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, &regnode->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(&regnode->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)
{
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(&regnode->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", 1,
(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 = 1;
if (OF_hasprop(node, "regulator-always-on"))
par->always_on = 1;
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", 1,
(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(cnode, name, sizeof(*cells),
(void **)&cells);
if (ncells <= 0)
return (ENXIO);
/* 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