Index: head/sys/dev/extres/regulator/regulator.c =================================================================== --- head/sys/dev/extres/regulator/regulator.c (revision 335194) +++ head/sys/dev/extres/regulator/regulator.c (revision 335195) @@ -1,1207 +1,1193 @@ /*- * Copyright 2016 Michal Meloun * 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 __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif #include #include "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_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_shutdown(void *dummy); /* * 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 */ 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_shutdown, SI_SUB_LAST, SI_ORDER_ANY, regulator_shutdown, NULL); /* * 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; REG_TOPO_SLOCK(); TUNABLE_INT_FETCH("hw.regulator.disable_unused", &disable); TAILQ_FOREACH(entry, ®node_list, reglist_link) { if (entry->std_param.always_on == 0 && disable) { if (bootverbose) printf("regulator: shutting down %s\n", entry->name); ret = regnode_status(entry, &status); if (ret == 0 && status == REGULATOR_STATUS_ENABLED) regnode_stop(entry, 0); } } 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_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; 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_INT(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "boot_on", CTLFLAG_RD, (int *) ®node->std_param.boot_on, 0, "Is enabled on boot"); SYSCTL_ADD_INT(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "always_on", CTLFLAG_RD, (int *)®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_enable_cnt_inc(struct regnode *regnode) -{ - - regnode->enable_cnt++; -} - -void -regnode_enable_cnt_dec(struct regnode *regnode) -{ - - regnode->enable_cnt--; -} - 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) { 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 = 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", (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 (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 /* -------------------------------------------------------------------------- * * 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); } Index: head/sys/dev/extres/regulator/regulator.h =================================================================== --- head/sys/dev/extres/regulator/regulator.h (revision 335194) +++ head/sys/dev/extres/regulator/regulator.h (revision 335195) @@ -1,152 +1,150 @@ /*- * Copyright 2016 Michal Meloun * 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 "opt_platform.h" #include #include #ifdef FDT #include #endif #include "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_enable_cnt_inc(struct regnode *regnode); -void regnode_enable_cnt_dec(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); #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); #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_ */ Index: head/sys/dev/extres/regulator/regulator_fixed.c =================================================================== --- head/sys/dev/extres/regulator/regulator_fixed.c (revision 335194) +++ head/sys/dev/extres/regulator/regulator_fixed.c (revision 335195) @@ -1,460 +1,456 @@ /*- * Copyright 2016 Michal Meloun * 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 __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif #include #include #include "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; }; 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 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_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; bool enable; 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; enable = sc->param->boot_on || sc->param->always_on; - if (enable) - regnode_enable_cnt_inc(regnode); 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); } 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->param->always_on && !enable) return (0); if (sc->gpio_entry == NULL) return (0); pin = &sc->gpio_entry->gpio_pin; if (enable) { - regnode_enable_cnt_inc(regnode); 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")); - regnode_enable_cnt_dec(regnode); sc->gpio_entry->enable_cnt--; if (sc->gpio_entry->enable_cnt >= 1) 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); } 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); } 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 */