diff options
author | Michal Meloun <mmel@FreeBSD.org> | 2018-06-16 08:25:38 +0000 |
---|---|---|
committer | Michal Meloun <mmel@FreeBSD.org> | 2018-06-16 08:25:38 +0000 |
commit | bcc0decdc98d144587f18312b69862524393c3f8 (patch) | |
tree | 5ca1820e36edf28082a64919ec7302c9fb19e23d /sys | |
parent | f567034d1b9115bd00fafcdd00eef34e8ccdd5c4 (diff) | |
download | src-bcc0decdc98d144587f18312b69862524393c3f8.tar.gz src-bcc0decdc98d144587f18312b69862524393c3f8.zip |
Fix handling of enable counter for shared GPIO line in fixed regulator.
For most regulators, the regulator_stop() method can be transformed to
regulator disable. But, in some cases, we needs to maintain shared data
across multiple regulators (e.g. single GPIO pin which works as enable
for multiple regulates). In this case, the implementation of regulator
should perform his own enable counting therefore it is necessary to
distinguish between the regulator enable/disable method (which
increments/decrements enable counter for shared resource) and regulator
stop method (which don't affect it).
So:
- add regnode_stop() method to regulator framework and default it to
regnode_enable(..., false, ...)
- implement it in regulator_fixed with proper enable counting.
While I'm in, also fix handling of always_on property. If any of regulators
sharing same GPIO pin have it enabled, then none of them can disable regulator.
Tested by: kevans
MFC after: 3 weeks
Notes
Notes:
svn path=/head/; revision=335249
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/extres/regulator/regnode_if.m | 18 | ||||
-rw-r--r-- | sys/dev/extres/regulator/regulator.c | 4 | ||||
-rw-r--r-- | sys/dev/extres/regulator/regulator_fixed.c | 47 |
3 files changed, 65 insertions, 4 deletions
diff --git a/sys/dev/extres/regulator/regnode_if.m b/sys/dev/extres/regulator/regnode_if.m index d92f26f29fcc..dfe59544a24e 100644 --- a/sys/dev/extres/regulator/regnode_if.m +++ b/sys/dev/extres/regulator/regnode_if.m @@ -31,6 +31,15 @@ HEADER { struct regnode; } +CODE { + static int + regnode_default_stop(struct regnode *regnode, int *udelay) + { + + return(REGNODE_ENABLE(regnode, false, udelay)); + } +} + # # Initialize regulator # Returns 0 on success or a standard errno value. @@ -80,3 +89,12 @@ METHOD int get_voltage { struct regnode *regnode; int *uvolt; }; + +# +# Stop (shutdown) regulator +# Returns 0 on success or a standard errno value. +# +METHOD int stop { + struct regnode *regnode; + int *udelay; +} DEFAULT regnode_default_stop; diff --git a/sys/dev/extres/regulator/regulator.c b/sys/dev/extres/regulator/regulator.c index c57d78dd5ca1..ef6e5216d7c5 100644 --- a/sys/dev/extres/regulator/regulator.c +++ b/sys/dev/extres/regulator/regulator.c @@ -636,7 +636,7 @@ regnode_stop(struct regnode *regnode, int depth) /* 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); + rv = REGNODE_STOP(regnode, &udelay); if (rv != 0) { REGNODE_UNLOCK(regnode); return (rv); @@ -648,7 +648,7 @@ regnode_stop(struct regnode *regnode, int depth) rv = regnode_resolve_parent(regnode); if (rv != 0) return (rv); - if (regnode->parent != NULL) + if (regnode->parent != NULL && regnode->parent->enable_cnt == 0) rv = regnode_stop(regnode->parent, depth + 1); return (rv); } diff --git a/sys/dev/extres/regulator/regulator_fixed.c b/sys/dev/extres/regulator/regulator_fixed.c index 1438181eafe5..1362d3a236cc 100644 --- a/sys/dev/extres/regulator/regulator_fixed.c +++ b/sys/dev/extres/regulator/regulator_fixed.c @@ -56,6 +56,7 @@ struct gpio_entry { 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; @@ -71,12 +72,14 @@ 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_END }; DEFINE_CLASS_1(regnode_fixed, regnode_fixed_class, regnode_fixed_methods, @@ -188,8 +191,6 @@ regnode_fixed_enable(struct regnode *regnode, bool enable, int *udelay) 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; @@ -204,6 +205,8 @@ regnode_fixed_enable(struct regnode *regnode, bool enable, int *udelay) 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); @@ -215,6 +218,42 @@ regnode_fixed_enable(struct regnode *regnode, bool enable, int *udelay) 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) { @@ -264,6 +303,10 @@ regnode_fixed_register(device_t dev, struct regnode_fixed_init_def *init_def) 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); } |