pinctrl: sunxi: Deal with per-bank regulators
The Allwinner SoCs have on most of their GPIO banks a regulator input. This issue was mainly ignored so far because either the regulator was a static regulator that would be providing power anyway, or the bank was used for a feature unsupported so far (CSI). For the odd cases, enabling it in the bootloader was the preferred option. However, now that we are starting to support those features, and that we can't really rely on the bootloader for this, we need to model those regulators as such in the DT. This is slightly more complicated than what it looks like, since some regulators will be tied to the PMIC, and in order to have access to the PMIC bus, you need to mux its pins, which will need the pinctrl driver, that needs the regulator driver to be registered. And this is how you get a circular dependency. In practice however, the hardware cannot fall into this case since it would result in a completely unusable bus. In order to avoid that circular dependency, we can thus get and enable the regulators at pin_request time. We'll then need to account for the references of all the pins of a particular branch to know when to put the reference, but it works pretty nicely once implemented. Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
parent
eaeee373c9
commit
9a2a566adb
|
@ -26,6 +26,7 @@
|
|||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
#include <linux/pinctrl/pinmux.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -693,12 +694,74 @@ sunxi_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_pmx_request(struct pinctrl_dev *pctldev, unsigned offset)
|
||||
{
|
||||
struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
unsigned short bank = offset / PINS_PER_BANK;
|
||||
struct sunxi_pinctrl_regulator *s_reg = &pctl->regulators[bank];
|
||||
struct regulator *reg;
|
||||
int ret;
|
||||
|
||||
reg = s_reg->regulator;
|
||||
if (!reg) {
|
||||
char supply[16];
|
||||
|
||||
snprintf(supply, sizeof(supply), "vcc-p%c", 'a' + bank);
|
||||
reg = regulator_get(pctl->dev, supply);
|
||||
if (IS_ERR(reg)) {
|
||||
dev_err(pctl->dev, "Couldn't get bank P%c regulator\n",
|
||||
'A' + bank);
|
||||
return PTR_ERR(reg);
|
||||
}
|
||||
|
||||
s_reg->regulator = reg;
|
||||
refcount_set(&s_reg->refcount, 1);
|
||||
} else {
|
||||
refcount_inc(&s_reg->refcount);
|
||||
}
|
||||
|
||||
ret = regulator_enable(reg);
|
||||
if (ret) {
|
||||
dev_err(pctl->dev,
|
||||
"Couldn't enable bank P%c regulator\n", 'A' + bank);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (refcount_dec_and_test(&s_reg->refcount)) {
|
||||
regulator_put(s_reg->regulator);
|
||||
s_reg->regulator = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sunxi_pmx_free(struct pinctrl_dev *pctldev, unsigned offset)
|
||||
{
|
||||
struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
unsigned short bank = offset / PINS_PER_BANK;
|
||||
struct sunxi_pinctrl_regulator *s_reg = &pctl->regulators[bank];
|
||||
|
||||
if (!refcount_dec_and_test(&s_reg->refcount))
|
||||
return 0;
|
||||
|
||||
regulator_disable(s_reg->regulator);
|
||||
regulator_put(s_reg->regulator);
|
||||
s_reg->regulator = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pinmux_ops sunxi_pmx_ops = {
|
||||
.get_functions_count = sunxi_pmx_get_funcs_cnt,
|
||||
.get_function_name = sunxi_pmx_get_func_name,
|
||||
.get_function_groups = sunxi_pmx_get_func_groups,
|
||||
.set_mux = sunxi_pmx_set_mux,
|
||||
.gpio_set_direction = sunxi_pmx_gpio_set_direction,
|
||||
.request = sunxi_pmx_request,
|
||||
.free = sunxi_pmx_free,
|
||||
.strict = true,
|
||||
};
|
||||
|
||||
|
|
|
@ -126,11 +126,17 @@ struct sunxi_pinctrl_group {
|
|||
unsigned pin;
|
||||
};
|
||||
|
||||
struct sunxi_pinctrl_regulator {
|
||||
struct regulator *regulator;
|
||||
refcount_t refcount;
|
||||
};
|
||||
|
||||
struct sunxi_pinctrl {
|
||||
void __iomem *membase;
|
||||
struct gpio_chip *chip;
|
||||
const struct sunxi_pinctrl_desc *desc;
|
||||
struct device *dev;
|
||||
struct sunxi_pinctrl_regulator regulators[12];
|
||||
struct irq_domain *domain;
|
||||
struct sunxi_pinctrl_function *functions;
|
||||
unsigned nfunctions;
|
||||
|
|
Loading…
Reference in New Issue