meta-st-stm32mp/recipes-kernel/linux/linux-stm32mp/5.15/5.15.24/0016-ARM-5.15.24-stm32mp1-r...

1704 lines
50 KiB
Diff

From 1670971dadf723ce3bb0e34c27fb7046e64fa74d Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
Date: Tue, 31 May 2022 12:05:37 +0200
Subject: [PATCH 16/22] ARM-5.15.24-stm32mp1-r1-PINCTRL-REGULATOR-SPI
Signed-off-by: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
---
.../bindings/pinctrl/st,stm32-pinctrl.yaml | 8 +
.../regulator/protection-consumer.txt | 23 ++
.../bindings/regulator/st,stm32-vrefbuf.yaml | 4 +-
drivers/pinctrl/stm32/pinctrl-stm32.c | 201 +++++++++++++-----
drivers/pinctrl/stm32/pinctrl-stm32.h | 19 +-
drivers/pinctrl/stm32/pinctrl-stm32mp135.c | 3 +-
drivers/pinctrl/stm32/pinctrl-stm32mp157.c | 3 +-
drivers/regulator/Kconfig | 11 +
drivers/regulator/Makefile | 1 +
drivers/regulator/protection-consumer.c | 137 ++++++++++++
drivers/regulator/scmi-regulator.c | 99 ++++++---
drivers/regulator/stm32-pwr.c | 85 +++++++-
drivers/regulator/stm32-vrefbuf.c | 69 +++++-
drivers/regulator/stpmic1_regulator.c | 182 ++++++++++++++--
drivers/spi/spi-stm32-qspi.c | 11 +-
include/dt-bindings/pinctrl/stm32-pinfunc.h | 1 +
16 files changed, 724 insertions(+), 133 deletions(-)
create mode 100644 Documentation/devicetree/bindings/regulator/protection-consumer.txt
create mode 100644 drivers/regulator/protection-consumer.c
diff --git a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml
index dfee6d38a..7348f40d4 100644
--- a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml
@@ -140,9 +140,13 @@ patternProperties:
* ...
* 16 : Alternate Function 15
* 17 : Analog
+ * 18 : Reserved
To simplify the usage, macro is available to generate "pinmux" field.
This macro is available here:
- include/dt-bindings/pinctrl/stm32-pinfunc.h
+ Setting the pinmux's function to the Reserved (RSVD) value is used to inform
+ the driver that it shall not apply the mux setting. This can be used to
+ reserve some pins, for example to a co-processor not running Linux.
Some examples of using macro:
/* GPIO A9 set as alernate function 2 */
... {
@@ -156,6 +160,10 @@ patternProperties:
... {
pinmux = <STM32_PINMUX('A', 9, ANALOG)>;
};
+ /* GPIO A9 reserved for co-processor */
+ ... {
+ pinmux = <STM32_PINMUX('A', 9, RSVD)>;
+ };
bias-disable:
type: boolean
diff --git a/Documentation/devicetree/bindings/regulator/protection-consumer.txt b/Documentation/devicetree/bindings/regulator/protection-consumer.txt
new file mode 100644
index 000000000..bf8169e00
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/protection-consumer.txt
@@ -0,0 +1,23 @@
+regulator protection bindings
+
+
+Required properties:
+ compatible: "protection-consumer"
+ protection-supply: the phandle of the regulator to control
+
+Optional properties:
+--------------------
+ interrupt
+
+
+Example:
+
+ regulator_protection {
+ compatible = "protection-consumer";
+ protection-supply = <&vdd>;
+ status = "okay";
+
+ interrupts = <15 2>;
+ interrupt-parent = <&gpiof>;
+ };
+
diff --git a/Documentation/devicetree/bindings/regulator/st,stm32-vrefbuf.yaml b/Documentation/devicetree/bindings/regulator/st,stm32-vrefbuf.yaml
index 3cd4a254e..fe9c5e83c 100644
--- a/Documentation/devicetree/bindings/regulator/st,stm32-vrefbuf.yaml
+++ b/Documentation/devicetree/bindings/regulator/st,stm32-vrefbuf.yaml
@@ -19,7 +19,9 @@ allOf:
properties:
compatible:
- const: st,stm32-vrefbuf
+ enum:
+ - st,stm32-vrefbuf
+ - st,stm32mp13-vrefbuf
reg:
maxItems: 1
diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c
index 8934b4878..271dcdbb5 100644
--- a/drivers/pinctrl/stm32/pinctrl-stm32.c
+++ b/drivers/pinctrl/stm32/pinctrl-stm32.c
@@ -43,6 +43,7 @@
#define STM32_GPIO_LCKR 0x1c
#define STM32_GPIO_AFRL 0x20
#define STM32_GPIO_AFRH 0x24
+#define STM32_GPIO_SECCFGR 0x30
/* custom bitfield to backup pin status */
#define STM32_GPIO_BKP_MODE_SHIFT 0
@@ -73,6 +74,7 @@ static const char * const stm32_gpio_functions[] = {
"af8", "af9", "af10",
"af11", "af12", "af13",
"af14", "af15", "analog",
+ "reserved",
};
struct stm32_pinctrl_group {
@@ -94,6 +96,7 @@ struct stm32_gpio_bank {
u32 bank_ioport_nr;
u32 pin_backup[STM32_GPIO_PINS_PER_BANK];
u8 irq_type[STM32_GPIO_PINS_PER_BANK];
+ bool secure_control;
};
struct stm32_pinctrl {
@@ -115,6 +118,7 @@ struct stm32_pinctrl {
u32 pkg;
u16 irqmux_map;
spinlock_t irqmux_lock;
+ u32 pin_base_shift;
};
static inline int stm32_gpio_pin(int gpio)
@@ -197,11 +201,7 @@ static inline void __stm32_gpio_set(struct stm32_gpio_bank *bank,
if (!value)
offset += STM32_GPIO_PINS_PER_BANK;
- clk_enable(bank->clk);
-
writel_relaxed(BIT(offset), bank->base + STM32_GPIO_BSRR);
-
- clk_disable(bank->clk);
}
static int stm32_gpio_request(struct gpio_chip *chip, unsigned offset)
@@ -228,15 +228,8 @@ static void stm32_gpio_free(struct gpio_chip *chip, unsigned offset)
static int stm32_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct stm32_gpio_bank *bank = gpiochip_get_data(chip);
- int ret;
-
- clk_enable(bank->clk);
-
- ret = !!(readl_relaxed(bank->base + STM32_GPIO_IDR) & BIT(offset));
-
- clk_disable(bank->clk);
- return ret;
+ return !!(readl_relaxed(bank->base + STM32_GPIO_IDR) & BIT(offset));
}
static void stm32_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
@@ -294,6 +287,33 @@ static int stm32_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
return ret;
}
+static int stm32_gpio_init_valid_mask(struct gpio_chip *chip,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ struct stm32_gpio_bank *bank = gpiochip_get_data(chip);
+ struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent);
+ unsigned int i;
+ u32 sec;
+
+ /* All gpio are valid per default */
+ bitmap_fill(valid_mask, ngpios);
+
+ if (bank->secure_control) {
+ /* Tag secured pins as invalid */
+ sec = readl_relaxed(bank->base + STM32_GPIO_SECCFGR);
+
+ for (i = 0; i < ngpios; i++) {
+ if (sec & BIT(i)) {
+ clear_bit(i, valid_mask);
+ dev_dbg(pctl->dev, "No access to gpio %d - %d\n", bank->bank_nr, i);
+ }
+ }
+ }
+
+ return 0;
+}
+
static const struct gpio_chip stm32_gpio_template = {
.request = stm32_gpio_request,
.free = stm32_gpio_free,
@@ -304,6 +324,7 @@ static const struct gpio_chip stm32_gpio_template = {
.to_irq = stm32_gpio_to_irq,
.get_direction = stm32_gpio_get_direction,
.set_config = gpiochip_generic_config,
+ .init_valid_mask = stm32_gpio_init_valid_mask,
};
static void stm32_gpio_irq_trigger(struct irq_data *d)
@@ -514,7 +535,7 @@ stm32_pctrl_find_group_by_pin(struct stm32_pinctrl *pctl, u32 pin)
static bool stm32_pctrl_is_function_valid(struct stm32_pinctrl *pctl,
u32 pin_num, u32 fnum)
{
- int i;
+ int i, k;
for (i = 0; i < pctl->npins; i++) {
const struct stm32_desc_pin *pin = pctl->pins + i;
@@ -523,7 +544,10 @@ static bool stm32_pctrl_is_function_valid(struct stm32_pinctrl *pctl,
if (pin->pin.number != pin_num)
continue;
- while (func && func->name) {
+ if (fnum == STM32_PIN_RSVD)
+ return true;
+
+ for (k = 0; k < STM32_CONFIG_NUM; k++) {
if (func->num == fnum)
return true;
func++;
@@ -750,7 +774,6 @@ static int stm32_pmx_set_mode(struct stm32_gpio_bank *bank,
unsigned long flags;
int err = 0;
- clk_enable(bank->clk);
spin_lock_irqsave(&bank->lock, flags);
if (pctl->hwlock) {
@@ -779,7 +802,6 @@ static int stm32_pmx_set_mode(struct stm32_gpio_bank *bank,
unlock:
spin_unlock_irqrestore(&bank->lock, flags);
- clk_disable(bank->clk);
return err;
}
@@ -792,7 +814,6 @@ void stm32_pmx_get_mode(struct stm32_gpio_bank *bank, int pin, u32 *mode,
int alt_offset = STM32_GPIO_AFRL + (pin / 8) * 4;
unsigned long flags;
- clk_enable(bank->clk);
spin_lock_irqsave(&bank->lock, flags);
val = readl_relaxed(bank->base + alt_offset);
@@ -804,7 +825,6 @@ void stm32_pmx_get_mode(struct stm32_gpio_bank *bank, int pin, u32 *mode,
*mode = val >> (pin * 2);
spin_unlock_irqrestore(&bank->lock, flags);
- clk_disable(bank->clk);
}
static int stm32_pmx_set_mux(struct pinctrl_dev *pctldev,
@@ -829,6 +849,11 @@ static int stm32_pmx_set_mux(struct pinctrl_dev *pctldev,
return -EINVAL;
}
+ if (function == STM32_PIN_RSVD) {
+ dev_dbg(pctl->dev, "Reserved pins, skipping HW update.\n");
+ return 0;
+ }
+
bank = gpiochip_get_data(range->gc);
pin = stm32_gpio_pin(g->pin);
@@ -848,12 +873,32 @@ static int stm32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
return stm32_pmx_set_mode(bank, pin, !input, 0);
}
+static int stm32_pmx_request(struct pinctrl_dev *pctldev, unsigned gpio)
+{
+ struct stm32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ struct pinctrl_gpio_range *range;
+
+ range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, gpio);
+ if (!range) {
+ dev_err(pctl->dev, "No gpio range defined.\n");
+ return -EINVAL;
+ }
+
+ if (!gpiochip_line_is_valid(range->gc, stm32_gpio_pin(gpio))) {
+ dev_warn(pctl->dev, "Can't access gpio %d\n", gpio);
+ return -EACCES;
+ }
+
+ return 0;
+}
+
static const struct pinmux_ops stm32_pmx_ops = {
.get_functions_count = stm32_pmx_get_funcs_cnt,
.get_function_name = stm32_pmx_get_func_name,
.get_function_groups = stm32_pmx_get_func_groups,
.set_mux = stm32_pmx_set_mux,
.gpio_set_direction = stm32_pmx_gpio_set_direction,
+ .request = stm32_pmx_request,
.strict = true,
};
@@ -867,7 +912,6 @@ static int stm32_pconf_set_driving(struct stm32_gpio_bank *bank,
u32 val;
int err = 0;
- clk_enable(bank->clk);
spin_lock_irqsave(&bank->lock, flags);
if (pctl->hwlock) {
@@ -891,7 +935,6 @@ static int stm32_pconf_set_driving(struct stm32_gpio_bank *bank,
unlock:
spin_unlock_irqrestore(&bank->lock, flags);
- clk_disable(bank->clk);
return err;
}
@@ -902,14 +945,12 @@ static u32 stm32_pconf_get_driving(struct stm32_gpio_bank *bank,
unsigned long flags;
u32 val;
- clk_enable(bank->clk);
spin_lock_irqsave(&bank->lock, flags);
val = readl_relaxed(bank->base + STM32_GPIO_TYPER);
val &= BIT(offset);
spin_unlock_irqrestore(&bank->lock, flags);
- clk_disable(bank->clk);
return (val >> offset);
}
@@ -922,7 +963,6 @@ static int stm32_pconf_set_speed(struct stm32_gpio_bank *bank,
u32 val;
int err = 0;
- clk_enable(bank->clk);
spin_lock_irqsave(&bank->lock, flags);
if (pctl->hwlock) {
@@ -946,7 +986,6 @@ static int stm32_pconf_set_speed(struct stm32_gpio_bank *bank,
unlock:
spin_unlock_irqrestore(&bank->lock, flags);
- clk_disable(bank->clk);
return err;
}
@@ -957,14 +996,12 @@ static u32 stm32_pconf_get_speed(struct stm32_gpio_bank *bank,
unsigned long flags;
u32 val;
- clk_enable(bank->clk);
spin_lock_irqsave(&bank->lock, flags);
val = readl_relaxed(bank->base + STM32_GPIO_SPEEDR);
val &= GENMASK(offset * 2 + 1, offset * 2);
spin_unlock_irqrestore(&bank->lock, flags);
- clk_disable(bank->clk);
return (val >> (offset * 2));
}
@@ -977,7 +1014,6 @@ static int stm32_pconf_set_bias(struct stm32_gpio_bank *bank,
u32 val;
int err = 0;
- clk_enable(bank->clk);
spin_lock_irqsave(&bank->lock, flags);
if (pctl->hwlock) {
@@ -1001,7 +1037,6 @@ static int stm32_pconf_set_bias(struct stm32_gpio_bank *bank,
unlock:
spin_unlock_irqrestore(&bank->lock, flags);
- clk_disable(bank->clk);
return err;
}
@@ -1012,14 +1047,12 @@ static u32 stm32_pconf_get_bias(struct stm32_gpio_bank *bank,
unsigned long flags;
u32 val;
- clk_enable(bank->clk);
spin_lock_irqsave(&bank->lock, flags);
val = readl_relaxed(bank->base + STM32_GPIO_PUPDR);
val &= GENMASK(offset * 2 + 1, offset * 2);
spin_unlock_irqrestore(&bank->lock, flags);
- clk_disable(bank->clk);
return (val >> (offset * 2));
}
@@ -1030,7 +1063,6 @@ static bool stm32_pconf_get(struct stm32_gpio_bank *bank,
unsigned long flags;
u32 val;
- clk_enable(bank->clk);
spin_lock_irqsave(&bank->lock, flags);
if (dir)
@@ -1041,7 +1073,6 @@ static bool stm32_pconf_get(struct stm32_gpio_bank *bank,
BIT(offset));
spin_unlock_irqrestore(&bank->lock, flags);
- clk_disable(bank->clk);
return val;
}
@@ -1064,6 +1095,11 @@ static int stm32_pconf_parse_conf(struct pinctrl_dev *pctldev,
bank = gpiochip_get_data(range->gc);
offset = stm32_gpio_pin(pin);
+ if (!gpiochip_line_is_valid(range->gc, offset)) {
+ dev_warn(pctl->dev, "Can't access gpio %d\n", pin);
+ return -EACCES;
+ }
+
switch (param) {
case PIN_CONFIG_DRIVE_PUSH_PULL:
ret = stm32_pconf_set_driving(bank, offset, 0);
@@ -1143,10 +1179,27 @@ static int stm32_pconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
return 0;
}
+static struct stm32_desc_pin *
+stm32_pconf_get_pin_desc_by_pin_number(struct stm32_pinctrl *pctl,
+ unsigned int pin_number)
+{
+ struct stm32_desc_pin *pins = pctl->pins;
+ int i;
+
+ for (i = 0; i < pctl->npins; i++) {
+ if (pins->pin.number == pin_number)
+ return pins;
+ pins++;
+ }
+ return NULL;
+}
+
static void stm32_pconf_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned int pin)
{
+ struct stm32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct stm32_desc_pin *pin_desc;
struct pinctrl_gpio_range *range;
struct stm32_gpio_bank *bank;
int offset;
@@ -1166,6 +1219,11 @@ static void stm32_pconf_dbg_show(struct pinctrl_dev *pctldev,
bank = gpiochip_get_data(range->gc);
offset = stm32_gpio_pin(pin);
+ if (!gpiochip_line_is_valid(range->gc, offset)) {
+ seq_puts(s, "NO ACCESS");
+ return;
+ }
+
stm32_pmx_get_mode(bank, offset, &mode, &alt);
bias = stm32_pconf_get_bias(bank, offset);
@@ -1196,7 +1254,12 @@ static void stm32_pconf_dbg_show(struct pinctrl_dev *pctldev,
case 2:
drive = stm32_pconf_get_driving(bank, offset);
speed = stm32_pconf_get_speed(bank, offset);
- seq_printf(s, "%d - %s - %s - %s %s", alt,
+ pin_desc = stm32_pconf_get_pin_desc_by_pin_number(pctl, pin);
+ if (!pin_desc)
+ return;
+
+ seq_printf(s, "%d (%s) - %s - %s - %s %s", alt,
+ pin_desc->functions[alt + 1].name,
drive ? "open drain" : "push pull",
biasing[bias],
speeds[speed], "speed");
@@ -1237,9 +1300,9 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl,
if (IS_ERR(bank->base))
return PTR_ERR(bank->base);
- err = clk_prepare(bank->clk);
+ err = clk_prepare_enable(bank->clk);
if (err) {
- dev_err(dev, "failed to prepare clk (%d)\n", err);
+ dev_err(dev, "failed to prepare_enable clk (%d)\n", err);
return err;
}
@@ -1278,26 +1341,35 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl,
bank->gpio_chip.parent = dev;
bank->bank_nr = bank_nr;
bank->bank_ioport_nr = bank_ioport_nr;
+ bank->secure_control = pctl->match_data->secure_control;
spin_lock_init(&bank->lock);
- /* create irq hierarchical domain */
- bank->fwnode = of_node_to_fwnode(np);
+ if (pctl->domain) {
+ /* create irq hierarchical domain */
+ bank->fwnode = of_node_to_fwnode(np);
- bank->domain = irq_domain_create_hierarchy(pctl->domain, 0,
- STM32_GPIO_IRQ_LINE, bank->fwnode,
- &stm32_gpio_domain_ops, bank);
+ bank->domain = irq_domain_create_hierarchy(pctl->domain, 0, STM32_GPIO_IRQ_LINE,
+ bank->fwnode, &stm32_gpio_domain_ops,
+ bank);
- if (!bank->domain)
- return -ENODEV;
+ if (!bank->domain) {
+ err = -ENODEV;
+ goto err_clk;
+ }
+ }
err = gpiochip_add_data(&bank->gpio_chip, bank);
if (err) {
dev_err(dev, "Failed to add gpiochip(%d)!\n", bank_nr);
- return err;
+ goto err_clk;
}
dev_info(dev, "%s bank added\n", bank->gpio_chip.label);
return 0;
+
+err_clk:
+ clk_disable_unprepare(bank->clk);
+ return err;
}
static struct irq_domain *stm32_pctrl_get_irq_domain(struct device_node *np)
@@ -1405,7 +1477,8 @@ static int stm32_pctrl_create_pins_tab(struct stm32_pinctrl *pctl,
if (pctl->pkg && !(pctl->pkg & p->pkg))
continue;
pins->pin = p->pin;
- pins->functions = p->functions;
+ memcpy((struct stm32_desc_pin *)pins->functions, p->functions,
+ STM32_CONFIG_NUM * sizeof(struct stm32_desc_function));
pins++;
nb_pins_available++;
}
@@ -1415,17 +1488,6 @@ static int stm32_pctrl_create_pins_tab(struct stm32_pinctrl *pctl,
return 0;
}
-static void stm32_pctl_get_package(struct device_node *np,
- struct stm32_pinctrl *pctl)
-{
- if (of_property_read_u32(np, "st,package", &pctl->pkg)) {
- pctl->pkg = 0;
- dev_warn(pctl->dev, "No package detected, use default one\n");
- } else {
- dev_dbg(pctl->dev, "package detected: %x\n", pctl->pkg);
- }
-}
-
int stm32_pctl_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -1458,6 +1520,8 @@ int stm32_pctl_probe(struct platform_device *pdev)
pctl->domain = stm32_pctrl_get_irq_domain(np);
if (IS_ERR(pctl->domain))
return PTR_ERR(pctl->domain);
+ if (!pctl->domain)
+ dev_warn(dev, "pinctrl without interrupt support\n");
/* hwspinlock is optional */
hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
@@ -1473,8 +1537,9 @@ int stm32_pctl_probe(struct platform_device *pdev)
pctl->dev = dev;
pctl->match_data = match->data;
- /* get package information */
- stm32_pctl_get_package(np, pctl);
+ /* get optional package information */
+ if (!of_property_read_u32(np, "st,package", &pctl->pkg))
+ dev_dbg(pctl->dev, "package detected: %x\n", pctl->pkg);
pctl->pins = devm_kcalloc(pctl->dev, pctl->match_data->npins,
sizeof(*pctl->pins), GFP_KERNEL);
@@ -1514,6 +1579,7 @@ int stm32_pctl_probe(struct platform_device *pdev)
pctl->pctl_desc.pctlops = &stm32_pctrl_ops;
pctl->pctl_desc.pmxops = &stm32_pmx_ops;
pctl->dev = &pdev->dev;
+ pctl->pin_base_shift = pctl->match_data->pin_base_shift;
pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, &pctl->pctl_desc,
pctl);
@@ -1566,6 +1632,10 @@ int stm32_pctl_probe(struct platform_device *pdev)
ret = stm32_gpiolib_register_bank(pctl, child);
if (ret) {
of_node_put(child);
+
+ for (i = 0; i < pctl->nbanks; i++)
+ clk_disable_unprepare(pctl->banks[i].clk);
+
return ret;
}
@@ -1592,6 +1662,9 @@ static int __maybe_unused stm32_pinctrl_restore_gpio_regs(
if (!range)
return 0;
+ if (!gpiochip_line_is_valid(range->gc, offset))
+ return 0;
+
pin_is_irq = gpiochip_line_is_irq(range->gc, offset);
if (!desc || (!pin_is_irq && !desc->gpio_owner))
@@ -1638,12 +1711,26 @@ static int __maybe_unused stm32_pinctrl_restore_gpio_regs(
return 0;
}
+int __maybe_unused stm32_pinctrl_suspend(struct device *dev)
+{
+ struct stm32_pinctrl *pctl = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < pctl->nbanks; i++)
+ clk_disable(pctl->banks[i].clk);
+
+ return 0;
+}
+
int __maybe_unused stm32_pinctrl_resume(struct device *dev)
{
struct stm32_pinctrl *pctl = dev_get_drvdata(dev);
struct stm32_pinctrl_group *g = pctl->groups;
int i;
+ for (i = 0; i < pctl->nbanks; i++)
+ clk_enable(pctl->banks[i].clk);
+
for (i = 0; i < pctl->ngroups; i++, g++)
stm32_pinctrl_restore_gpio_regs(pctl, g->pin);
diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.h b/drivers/pinctrl/stm32/pinctrl-stm32.h
index b0882d120..319dff763 100644
--- a/drivers/pinctrl/stm32/pinctrl-stm32.h
+++ b/drivers/pinctrl/stm32/pinctrl-stm32.h
@@ -17,6 +17,8 @@
#define STM32_PIN_GPIO 0
#define STM32_PIN_AF(x) ((x) + 1)
#define STM32_PIN_ANALOG (STM32_PIN_AF(15) + 1)
+#define STM32_PIN_RSVD (STM32_PIN_ANALOG + 1)
+#define STM32_CONFIG_NUM (STM32_PIN_RSVD + 1)
/* package information */
#define STM32MP_PKG_AA BIT(0)
@@ -24,6 +26,8 @@
#define STM32MP_PKG_AC BIT(2)
#define STM32MP_PKG_AD BIT(3)
+#define STM32MP157_Z_BASE_SHIFT 400
+
struct stm32_desc_function {
const char *name;
const unsigned char num;
@@ -31,26 +35,26 @@ struct stm32_desc_function {
struct stm32_desc_pin {
struct pinctrl_pin_desc pin;
- const struct stm32_desc_function *functions;
+ const struct stm32_desc_function functions[STM32_CONFIG_NUM];
const unsigned int pkg;
};
#define STM32_PIN(_pin, ...) \
{ \
.pin = _pin, \
- .functions = (struct stm32_desc_function[]){ \
- __VA_ARGS__, { } }, \
+ .functions = { \
+ __VA_ARGS__}, \
}
#define STM32_PIN_PKG(_pin, _pkg, ...) \
{ \
.pin = _pin, \
.pkg = _pkg, \
- .functions = (struct stm32_desc_function[]){ \
- __VA_ARGS__, { } }, \
+ .functions = { \
+ __VA_ARGS__}, \
}
#define STM32_FUNCTION(_num, _name) \
- { \
+ [_num] = { \
.num = _num, \
.name = _name, \
}
@@ -58,6 +62,8 @@ struct stm32_desc_pin {
struct stm32_pinctrl_match_data {
const struct stm32_desc_pin *pins;
const unsigned int npins;
+ bool secure_control;
+ const unsigned int pin_base_shift;
};
struct stm32_gpio_bank;
@@ -65,6 +71,7 @@ struct stm32_gpio_bank;
int stm32_pctl_probe(struct platform_device *pdev);
void stm32_pmx_get_mode(struct stm32_gpio_bank *bank,
int pin, u32 *mode, u32 *alt);
+int stm32_pinctrl_suspend(struct device *dev);
int stm32_pinctrl_resume(struct device *dev);
#endif /* __PINCTRL_STM32_H */
diff --git a/drivers/pinctrl/stm32/pinctrl-stm32mp135.c b/drivers/pinctrl/stm32/pinctrl-stm32mp135.c
index 4ab03520c..fde1df191 100644
--- a/drivers/pinctrl/stm32/pinctrl-stm32mp135.c
+++ b/drivers/pinctrl/stm32/pinctrl-stm32mp135.c
@@ -1649,6 +1649,7 @@ static const struct stm32_desc_pin stm32mp135_pins[] = {
static struct stm32_pinctrl_match_data stm32mp135_match_data = {
.pins = stm32mp135_pins,
.npins = ARRAY_SIZE(stm32mp135_pins),
+ .secure_control = true,
};
static const struct of_device_id stm32mp135_pctrl_match[] = {
@@ -1660,7 +1661,7 @@ static const struct of_device_id stm32mp135_pctrl_match[] = {
};
static const struct dev_pm_ops stm32_pinctrl_dev_pm_ops = {
- SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, stm32_pinctrl_resume)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(stm32_pinctrl_suspend, stm32_pinctrl_resume)
};
static struct platform_driver stm32mp135_pinctrl_driver = {
diff --git a/drivers/pinctrl/stm32/pinctrl-stm32mp157.c b/drivers/pinctrl/stm32/pinctrl-stm32mp157.c
index 2ccb99d64..2d4f8c1e6 100644
--- a/drivers/pinctrl/stm32/pinctrl-stm32mp157.c
+++ b/drivers/pinctrl/stm32/pinctrl-stm32mp157.c
@@ -2328,6 +2328,7 @@ static struct stm32_pinctrl_match_data stm32mp157_match_data = {
static struct stm32_pinctrl_match_data stm32mp157_z_match_data = {
.pins = stm32mp157_z_pins,
.npins = ARRAY_SIZE(stm32mp157_z_pins),
+ .pin_base_shift = STM32MP157_Z_BASE_SHIFT,
};
static const struct of_device_id stm32mp157_pctrl_match[] = {
@@ -2343,7 +2344,7 @@ static const struct of_device_id stm32mp157_pctrl_match[] = {
};
static const struct dev_pm_ops stm32_pinctrl_dev_pm_ops = {
- SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, stm32_pinctrl_resume)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(stm32_pinctrl_suspend, stm32_pinctrl_resume)
};
static struct platform_driver stm32mp157_pinctrl_driver = {
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 4fd13b062..285931c18 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -37,6 +37,17 @@ config REGULATOR_FIXED_VOLTAGE
useful for systems which use a combination of software
managed regulators and simple non-configurable regulators.
+config REGULATOR_PROTECTION_CONSUMER
+ tristate "Regulator protection consumer"
+ depends on OF
+ help
+ This driver handles regulator over-current detection in order to
+ protect regulators from crashing. In case of over-current event
+ or any IRQ, the protection consumer forces disable the regulator
+ that was declared as supply.
+
+ If unsure, say no.
+
config REGULATOR_VIRTUAL_CONSUMER
tristate "Virtual regulator consumer support"
help
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 9e382b50a..543e04fa2 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_REGULATOR) += core.o dummy.o fixed-helper.o helpers.o devres.o irq_helpers.o
obj-$(CONFIG_OF) += of_regulator.o
obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
+obj-$(CONFIG_REGULATOR_PROTECTION_CONSUMER) += protection-consumer.o
obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
diff --git a/drivers/regulator/protection-consumer.c b/drivers/regulator/protection-consumer.c
new file mode 100644
index 000000000..a4d299f85
--- /dev/null
+++ b/drivers/regulator/protection-consumer.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2021
+// Author: Pascal Paillet <p.paillet@foss.st.com> for STMicroelectronics.
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+/**
+ * struct protection_data - regulator driver data
+ * @reg: regulator consumer structure
+ * @nb: notifier_block structure
+ * @dev: device driver
+ */
+struct protection_data {
+ struct regulator *reg;
+ struct notifier_block nb;
+ struct device *dev;
+};
+
+/**
+ * protection_irq_handler() - irq handler
+ * @irq: irq number
+ * @dev: struct protection_data
+ *
+ * force disable the regulator
+ */
+static irqreturn_t protection_irq_handler(int irq, void *dev)
+{
+ struct protection_data *protection = (struct protection_data *)dev;
+
+ dev_warn(protection->dev, "Interrupt received on regulator\n");
+ if (regulator_is_enabled(protection->reg))
+ regulator_force_disable(protection->reg);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * regulator_event() - regulator framework callback
+ * @nb: notifier_block
+ * @event: regulator framework event
+ * @data: struct protection_data
+ *
+ * force disable the regulator in case of regulator event
+ *
+ * Return: 0 for successful probe else appropriate error
+ */
+static int regulator_event(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ struct protection_data *protection =
+ container_of(nb, struct protection_data, nb);
+
+ if ((event & REGULATOR_EVENT_OVER_CURRENT) ||
+ (event & REGULATOR_EVENT_OVER_TEMP)) {
+ dev_warn(protection->dev, "Event received on regulator\n");
+ if (regulator_is_enabled(protection->reg))
+ regulator_force_disable(protection->reg);
+ }
+
+ return 0;
+}
+
+/**
+ * protection_probe() - probe
+ * @pdev: platform_device
+ *
+ * Return: 0 for successful probe else appropriate error
+ */
+static int protection_probe(struct platform_device *pdev)
+{
+ struct protection_data *protection;
+ int irq, ret;
+
+ protection = devm_kzalloc(&pdev->dev, sizeof(struct protection_data), GFP_KERNEL);
+ if (!protection)
+ return -ENOMEM;
+
+ protection->dev = &pdev->dev;
+
+ protection->reg = devm_regulator_get(&pdev->dev, "protection");
+ if (IS_ERR(protection->reg))
+ return PTR_ERR(protection->reg);
+
+ protection->nb.notifier_call = regulator_event;
+ ret = devm_regulator_register_notifier(protection->reg, &protection->nb);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register regulator notifier: %d\n", ret);
+ return ret;
+ }
+
+ /* irq is optional, the driver can be used with regulator events */
+ irq = platform_get_irq_optional(pdev, 0);
+ if (irq <= 0 && (irq != -ENXIO))
+ return irq ? : -ENOENT;
+
+ if (irq > 0) {
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ protection_irq_handler,
+ IRQF_ONESHOT | IRQF_SHARED,
+ pdev->name, protection);
+ if (ret) {
+ dev_err(&pdev->dev, "Request IRQ failed\n");
+ return ret;
+ }
+ }
+ platform_set_drvdata(pdev, protection);
+ dev_dbg(&pdev->dev, "protection probed\n");
+
+ return 0;
+}
+
+static const struct of_device_id protection_dt_match[] = {
+ { .compatible = "protection-consumer" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, protection_dt_match);
+
+static struct platform_driver protection_driver = {
+ .driver = {
+ .name = "protection-consumer",
+ .owner = THIS_MODULE,
+ .of_match_table = protection_dt_match,
+ },
+ .probe = protection_probe,
+};
+
+module_platform_driver(protection_driver);
+
+MODULE_AUTHOR("<p.paillet@foss.st.com>");
+MODULE_DESCRIPTION("protection consumer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c
index 1f02f60ad..dffd9f5ce 100644
--- a/drivers/regulator/scmi-regulator.c
+++ b/drivers/regulator/scmi-regulator.c
@@ -219,10 +219,11 @@ static int scmi_regulator_common_init(struct scmi_regulator *sreg)
*/
if (vinfo->negative_volts_allowed) {
dev_warn(dev, "Negative voltages NOT supported...skip %s\n",
- sreg->of_node->full_name);
+ vinfo->name);
return -EOPNOTSUPP;
}
+
sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name);
if (!sreg->desc.name)
return -ENOMEM;
@@ -230,8 +231,6 @@ static int scmi_regulator_common_init(struct scmi_regulator *sreg)
sreg->desc.id = sreg->id;
sreg->desc.type = REGULATOR_VOLTAGE;
sreg->desc.owner = THIS_MODULE;
- sreg->desc.of_match_full_name = true;
- sreg->desc.of_match = sreg->of_node->full_name;
sreg->desc.regulators_node = "regulators";
if (vinfo->segmented)
ret = scmi_config_linear_regulator_mappings(sreg, vinfo);
@@ -239,7 +238,6 @@ static int scmi_regulator_common_init(struct scmi_regulator *sreg)
ret = scmi_config_discrete_regulator_mappings(sreg, vinfo);
if (ret)
return ret;
-
/*
* Using the scmi device here to have DT searched from Voltage
* protocol node down.
@@ -252,40 +250,59 @@ static int scmi_regulator_common_init(struct scmi_regulator *sreg)
return 0;
}
+static int scmi_find_domain_from_name(struct scmi_device *sdev,
+ struct device_node *np,
+ struct scmi_regulator_info *rinfo,
+ u32 *dom)
+{
+ const char *name = of_get_property(np, "voltd-name", NULL);
+ int d;
+
+ if (!name)
+ return -EINVAL;
+
+ for (d = 0; d < rinfo->num_doms; d++) {
+ struct scmi_regulator *sreg = rinfo->sregv[d];
+
+ if (!sreg || !sreg->desc.name || strcmp(sreg->desc.name, name))
+ continue;
+
+ *dom=d;
+ return 0;
+ }
+
+ dev_warn(&sdev->dev, "scmi voltage domain %s not found\n", name);
+ return -ENODEV;
+}
+
static int process_scmi_regulator_of_node(struct scmi_device *sdev,
- struct scmi_protocol_handle *ph,
struct device_node *np,
struct scmi_regulator_info *rinfo)
{
u32 dom, ret;
ret = of_property_read_u32(np, "reg", &dom);
- if (ret)
- return ret;
+ if (ret == -EINVAL) {
+ ret = scmi_find_domain_from_name(sdev, np, rinfo, &dom);
+ if (ret < 0) {
+ return ret;
+ }
+ }
if (dom >= rinfo->num_doms)
return -ENODEV;
- if (rinfo->sregv[dom]) {
- dev_err(&sdev->dev,
- "SCMI Voltage Domain %d already in use. Skipping: %s\n",
- dom, np->full_name);
- return -EINVAL;
- }
-
- rinfo->sregv[dom] = devm_kzalloc(&sdev->dev,
- sizeof(struct scmi_regulator),
- GFP_KERNEL);
if (!rinfo->sregv[dom])
- return -ENOMEM;
+ return -EINVAL;
rinfo->sregv[dom]->id = dom;
rinfo->sregv[dom]->sdev = sdev;
- rinfo->sregv[dom]->ph = ph;
/* get hold of good nodes */
of_node_get(np);
rinfo->sregv[dom]->of_node = np;
+ rinfo->sregv[dom]->desc.of_match_full_name = true;
+ rinfo->sregv[dom]->desc.of_match = rinfo->sregv[dom]->of_node->name;
dev_dbg(&sdev->dev,
"Found SCMI Regulator entry -- OF node [%d] -> %s\n",
@@ -338,21 +355,38 @@ static int scmi_regulator_probe(struct scmi_device *sdev)
rinfo->num_doms = num_doms;
/*
- * Start collecting into rinfo->sregv possibly good SCMI Regulators as
- * described by a well-formed DT entry and associated with an existing
- * plausible SCMI Voltage Domain number, all belonging to this SCMI
- * platform instance node (handle->dev->of_node).
+ * Start collecting into rinfo->sregv for each regulator that we
+ * can successfully reach via SCMI.
*/
- np = of_find_node_by_name(handle->dev->of_node, "regulators");
- for_each_child_of_node(np, child) {
- ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo);
- /* abort on any mem issue */
- if (ret == -ENOMEM) {
- of_node_put(child);
- return ret;
+ for (d = 0; d < num_doms; d++) {
+ struct scmi_regulator *sreg;
+
+ sreg = devm_kzalloc(&sdev->dev, sizeof(struct scmi_regulator),
+ GFP_KERNEL);
+ if (!sreg)
+ return -ENOMEM;
+
+ sreg->sdev = sdev;
+ sreg->id = d;
+ sreg->ph = ph;
+
+ ret = scmi_regulator_common_init(sreg);
+ if (ret) {
+ devm_kfree(&sdev->dev, sreg);
+ continue;
}
+
+ rinfo->sregv[d] = sreg;
}
+ /*
+ * Map each DT entry with an existing SCMI Voltage Domain number
+ * all belonging to this SCMI platform instance node (handle->dev->of_node).
+ */
+ np = of_find_node_by_name(handle->dev->of_node, "regulators");
+ for_each_child_of_node(np, child)
+ process_scmi_regulator_of_node(sdev, child, rinfo);
+
/*
* Register a regulator for each valid regulator-DT-entry that we
* can successfully reach via SCMI and has a valid associated voltage
@@ -365,9 +399,8 @@ static int scmi_regulator_probe(struct scmi_device *sdev)
if (!sreg)
continue;
- ret = scmi_regulator_common_init(sreg);
- /* Skip invalid voltage domains */
- if (ret)
+ /* Skip if not described in the device-tree */
+ if (!sreg->of_node)
continue;
sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc,
diff --git a/drivers/regulator/stm32-pwr.c b/drivers/regulator/stm32-pwr.c
index 2a42acb7c..2b328b970 100644
--- a/drivers/regulator/stm32-pwr.c
+++ b/drivers/regulator/stm32-pwr.c
@@ -3,12 +3,15 @@
// Authors: Gabriel Fernandez <gabriel.fernandez@st.com>
// Pascal Paillet <p.paillet@st.com>.
+#include <linux/arm-smccc.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
@@ -24,6 +27,11 @@
#define REG_1_1_EN BIT(30)
#define REG_1_1_RDY BIT(31)
+#define STM32_SMC_PWR 0x82001001
+#define STM32_WRITE 0x1
+#define STM32_SMC_REG_SET 0x2
+#define STM32_SMC_REG_CLEAR 0x3
+
/* list of supported regulators */
enum {
PWR_REG11,
@@ -39,10 +47,18 @@ static u32 ready_mask_table[STM32PWR_REG_NUM_REGS] = {
};
struct stm32_pwr_reg {
+ int tzen;
void __iomem *base;
u32 ready_mask;
};
+#define SMC(class, op, address, val)\
+ ({\
+ struct arm_smccc_res res;\
+ arm_smccc_smc(class, op, address, val,\
+ 0, 0, 0, 0, &res);\
+ })
+
static int stm32_pwr_reg_is_ready(struct regulator_dev *rdev)
{
struct stm32_pwr_reg *priv = rdev_get_drvdata(rdev);
@@ -69,9 +85,15 @@ static int stm32_pwr_reg_enable(struct regulator_dev *rdev)
int ret;
u32 val;
- val = readl_relaxed(priv->base + REG_PWR_CR3);
- val |= rdev->desc->enable_mask;
- writel_relaxed(val, priv->base + REG_PWR_CR3);
+ if (priv->tzen) {
+ SMC(STM32_SMC_PWR, STM32_SMC_REG_SET, REG_PWR_CR3,
+ rdev->desc->enable_mask);
+ } else {
+ val = readl_relaxed(priv->base + REG_PWR_CR3);
+ val |= rdev->desc->enable_mask;
+ writel_relaxed(val, priv->base + REG_PWR_CR3);
+ }
+
/* use an arbitrary timeout of 20ms */
ret = readx_poll_timeout(stm32_pwr_reg_is_ready, rdev, val, val,
@@ -88,9 +110,14 @@ static int stm32_pwr_reg_disable(struct regulator_dev *rdev)
int ret;
u32 val;
- val = readl_relaxed(priv->base + REG_PWR_CR3);
- val &= ~rdev->desc->enable_mask;
- writel_relaxed(val, priv->base + REG_PWR_CR3);
+ if (priv->tzen) {
+ SMC(STM32_SMC_PWR, STM32_SMC_REG_CLEAR, REG_PWR_CR3,
+ rdev->desc->enable_mask);
+ } else {
+ val = readl_relaxed(priv->base + REG_PWR_CR3);
+ val &= ~rdev->desc->enable_mask;
+ writel_relaxed(val, priv->base + REG_PWR_CR3);
+ }
/* use an arbitrary timeout of 20ms */
ret = readx_poll_timeout(stm32_pwr_reg_is_ready, rdev, val, !val,
@@ -121,12 +148,50 @@ static const struct regulator_ops stm32_pwr_reg_ops = {
.supply_name = _supply, \
} \
-static const struct regulator_desc stm32_pwr_desc[] = {
+static struct regulator_desc stm32_pwr_desc[] = {
PWR_REG(PWR_REG11, "reg11", 1100000, REG_1_1_EN, "vdd"),
PWR_REG(PWR_REG18, "reg18", 1800000, REG_1_8_EN, "vdd"),
PWR_REG(PWR_USB33, "usb33", 3300000, USB_3_3_EN, "vdd_3v3_usbfs"),
};
+static int is_stm32_soc_secured(struct platform_device *pdev, int *val)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct regmap *syscon;
+ u32 reg, mask;
+ int tzc_val = 0;
+ int err;
+
+ syscon = syscon_regmap_lookup_by_phandle(np, "st,tzcr");
+ if (IS_ERR(syscon)) {
+ if (PTR_ERR(syscon) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "tzcr syscon required\n");
+ return PTR_ERR(syscon);
+ }
+
+ err = of_property_read_u32_index(np, "st,tzcr", 1, &reg);
+ if (err) {
+ dev_err(&pdev->dev, "tzcr offset required !\n");
+ return err;
+ }
+
+ err = of_property_read_u32_index(np, "st,tzcr", 2, &mask);
+ if (err) {
+ dev_err(&pdev->dev, "tzcr mask required !\n");
+ return err;
+ }
+
+ err = regmap_read(syscon, reg, &tzc_val);
+ if (err) {
+ dev_err(&pdev->dev, "failed to read tzcr status !\n");
+ return err;
+ }
+
+ *val = tzc_val & mask;
+
+ return 0;
+}
+
static int stm32_pwr_regulator_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -135,6 +200,11 @@ static int stm32_pwr_regulator_probe(struct platform_device *pdev)
struct regulator_dev *rdev;
struct regulator_config config = { };
int i, ret = 0;
+ int tzen = 0;
+
+ ret = is_stm32_soc_secured(pdev, &tzen);
+ if (ret)
+ return ret;
base = of_iomap(np, 0);
if (!base) {
@@ -149,6 +219,7 @@ static int stm32_pwr_regulator_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ priv->tzen = tzen;
priv->base = base;
priv->ready_mask = ready_mask_table[i];
config.driver_data = priv;
diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c
index 161622ea7..d7eb7607b 100644
--- a/drivers/regulator/stm32-vrefbuf.c
+++ b/drivers/regulator/stm32-vrefbuf.c
@@ -31,6 +31,7 @@ struct stm32_vrefbuf {
void __iomem *base;
struct clk *clk;
struct device *dev;
+ u32 backup_val;
};
static const unsigned int stm32_vrefbuf_voltages[] = {
@@ -38,6 +39,11 @@ static const unsigned int stm32_vrefbuf_voltages[] = {
2500000, 2048000, 1800000, 1500000,
};
+static const unsigned int stm32mp13_vrefbuf_voltages[] = {
+ /* Matches resp. VRS = 000b, 001b, 010b, 011b */
+ 2500000, 2048000, 1800000, 1650000,
+};
+
static int stm32_vrefbuf_enable(struct regulator_dev *rdev)
{
struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
@@ -180,11 +186,24 @@ static const struct regulator_desc stm32_vrefbuf_regu = {
.owner = THIS_MODULE,
};
+static const struct regulator_desc stm32mp13_vrefbuf_regu = {
+ .name = "vref",
+ .supply_name = "vdda",
+ .volt_table = stm32mp13_vrefbuf_voltages,
+ .n_voltages = ARRAY_SIZE(stm32mp13_vrefbuf_voltages),
+ .ops = &stm32_vrefbuf_volt_ops,
+ .off_on_delay = 1000,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+};
+
static int stm32_vrefbuf_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct stm32_vrefbuf *priv;
struct regulator_config config = { };
struct regulator_dev *rdev;
+ const struct regulator_desc *desc;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
@@ -213,14 +232,19 @@ static int stm32_vrefbuf_probe(struct platform_device *pdev)
goto err_pm_stop;
}
+ desc = (const struct regulator_desc *)
+ of_match_device(dev->driver->of_match_table, dev)->data;
+ if (!desc)
+ return -EINVAL;
+
config.dev = &pdev->dev;
config.driver_data = priv;
config.of_node = pdev->dev.of_node;
config.init_data = of_get_regulator_init_data(&pdev->dev,
pdev->dev.of_node,
- &stm32_vrefbuf_regu);
+ desc);
- rdev = regulator_register(&stm32_vrefbuf_regu, &config);
+ rdev = regulator_register(desc, &config);
if (IS_ERR(rdev)) {
ret = PTR_ERR(rdev);
dev_err(&pdev->dev, "register failed with error %d\n", ret);
@@ -276,16 +300,51 @@ static int __maybe_unused stm32_vrefbuf_runtime_resume(struct device *dev)
return clk_prepare_enable(priv->clk);
}
+#if defined(CONFIG_PM_SLEEP)
+static int stm32_vrefbuf_suspend(struct device *dev)
+{
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
+ struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+ int ret;
+
+ ret = pm_runtime_get_sync(priv->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(priv->dev);
+ return ret;
+ }
+
+ priv->backup_val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int stm32_vrefbuf_resume(struct device *dev)
+{
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
+ struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret < 0)
+ return ret;
+
+ writel_relaxed(priv->backup_val, priv->base + STM32_VREFBUF_CSR);
+
+ return 0;
+}
+#endif
+
static const struct dev_pm_ops stm32_vrefbuf_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_vrefbuf_suspend,
+ stm32_vrefbuf_resume)
SET_RUNTIME_PM_OPS(stm32_vrefbuf_runtime_suspend,
stm32_vrefbuf_runtime_resume,
NULL)
};
static const struct of_device_id __maybe_unused stm32_vrefbuf_of_match[] = {
- { .compatible = "st,stm32-vrefbuf", },
+ { .compatible = "st,stm32-vrefbuf", .data = (void *)&stm32_vrefbuf_regu },
+ { .compatible = "st,stm32mp13-vrefbuf", .data = (void *)&stm32mp13_vrefbuf_regu },
{},
};
MODULE_DEVICE_TABLE(of, stm32_vrefbuf_of_match);
diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c
index 2d7597c76..c2cddba6d 100644
--- a/drivers/regulator/stpmic1_regulator.c
+++ b/drivers/regulator/stpmic1_regulator.c
@@ -2,7 +2,9 @@
// Copyright (C) STMicroelectronics 2018
// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/ktime.h>
#include <linux/mfd/stpmic1.h>
#include <linux/module.h>
#include <linux/of_irq.h>
@@ -30,11 +32,27 @@ struct stpmic1_regulator_cfg {
u8 icc_mask;
};
+/**
+ * struct boost_data - this structure is used as driver data for the usb boost
+ * @boost_rdev: device for boost regulator
+ * @vbus_otg_rdev: device for vbus_otg regulator
+ * @sw_out_rdev: device for sw_out regulator
+ * @occ_timeout: overcurrent detection timeout
+ */
+struct boost_data {
+ struct regulator_dev *boost_rdev;
+ struct regulator_dev *vbus_otg_rdev;
+ struct regulator_dev *sw_out_rdev;
+ ktime_t occ_timeout;
+};
+
static int stpmic1_set_mode(struct regulator_dev *rdev, unsigned int mode);
static unsigned int stpmic1_get_mode(struct regulator_dev *rdev);
static int stpmic1_set_icc(struct regulator_dev *rdev, int lim, int severity,
bool enable);
static unsigned int stpmic1_map_mode(unsigned int mode);
+static int regulator_enable_boost(struct regulator_dev *rdev);
+static int regulator_disable_boost(struct regulator_dev *rdev);
enum {
STPMIC1_BUCK1 = 0,
@@ -182,8 +200,8 @@ static const struct regulator_ops stpmic1_vref_ddr_ops = {
static const struct regulator_ops stpmic1_boost_regul_ops = {
.is_enabled = regulator_is_enabled_regmap,
- .enable = regulator_enable_regmap,
- .disable = regulator_disable_regmap,
+ .enable = regulator_enable_boost,
+ .disable = regulator_disable_boost,
.set_over_current_protection = stpmic1_set_icc,
};
@@ -529,6 +547,79 @@ static irqreturn_t stpmic1_curlim_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
+static int regulator_enable_boost(struct regulator_dev *rdev)
+{
+ struct boost_data *usb_data = rdev_get_drvdata(rdev);
+
+ usb_data->occ_timeout = ktime_add_us(ktime_get(), 100000);
+
+ return regulator_enable_regmap(rdev);
+}
+
+static int regulator_disable_boost(struct regulator_dev *rdev)
+{
+ struct boost_data *usb_data = rdev_get_drvdata(rdev);
+
+ usb_data->occ_timeout = 0;
+
+ return regulator_disable_regmap(rdev);
+}
+
+static void stpmic1_reset_boost(struct boost_data *usb_data)
+{
+ int otg_on = 0;
+ int sw_out_on = 0;
+
+ dev_dbg(rdev_get_dev(usb_data->boost_rdev), "reset usb boost\n");
+
+ /* the boost was actually disabled by the over-current protection */
+ regulator_disable_regmap(usb_data->boost_rdev);
+
+ if (usb_data->vbus_otg_rdev)
+ otg_on = regulator_is_enabled_regmap(usb_data->vbus_otg_rdev);
+ if (otg_on)
+ regulator_disable_regmap(usb_data->vbus_otg_rdev);
+
+ if (usb_data->sw_out_rdev)
+ sw_out_on = regulator_is_enabled_regmap(usb_data->sw_out_rdev);
+ if (sw_out_on)
+ regulator_disable_regmap(usb_data->sw_out_rdev);
+
+ regulator_enable_regmap(usb_data->boost_rdev);
+
+ /* sleep at least 5ms */
+ usleep_range(5000, 10000);
+
+ if (otg_on)
+ regulator_enable_regmap(usb_data->vbus_otg_rdev);
+
+ if (sw_out_on)
+ regulator_enable_regmap(usb_data->sw_out_rdev);
+
+}
+
+static irqreturn_t stpmic1_boost_irq_handler(int irq, void *data)
+{
+ struct boost_data *usb_data = (struct boost_data *)data;
+
+ dev_dbg(rdev_get_dev(usb_data->boost_rdev), "usb boost irq handler\n");
+
+ /* overcurrent detected on boost after timeout */
+ if (usb_data->occ_timeout != 0 &&
+ ktime_compare(ktime_get(), usb_data->occ_timeout) > 0) {
+ /* reset usb boost and usb power switches */
+ stpmic1_reset_boost(usb_data);
+ return IRQ_HANDLED;
+ }
+
+ /* Send an overcurrent notification */
+ regulator_notifier_call_chain(usb_data->boost_rdev,
+ REGULATOR_EVENT_OVER_CURRENT,
+ NULL);
+
+ return IRQ_HANDLED;
+}
+
#define MATCH(_name, _id) \
[STPMIC1_##_id] = { \
.name = #_name, \
@@ -552,9 +643,10 @@ static struct of_regulator_match stpmic1_matches[] = {
MATCH(pwr_sw2, SW_OUT),
};
-static int stpmic1_regulator_register(struct platform_device *pdev, int id,
- struct of_regulator_match *match,
- const struct stpmic1_regulator_cfg *cfg)
+static struct regulator_dev *
+stpmic1_regulator_register(struct platform_device *pdev, int id,
+ struct of_regulator_match *match,
+ const struct stpmic1_regulator_cfg *cfg)
{
struct stpmic1 *pmic_dev = dev_get_drvdata(pdev->dev.parent);
struct regulator_dev *rdev;
@@ -572,7 +664,7 @@ static int stpmic1_regulator_register(struct platform_device *pdev, int id,
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "failed to register %s regulator\n",
cfg->desc.name);
- return PTR_ERR(rdev);
+ return rdev;
}
/* set mask reset */
@@ -584,7 +676,7 @@ static int stpmic1_regulator_register(struct platform_device *pdev, int id,
cfg->mask_reset_mask);
if (ret) {
dev_err(&pdev->dev, "set mask reset failed\n");
- return ret;
+ return ERR_PTR(ret);
}
}
@@ -598,15 +690,60 @@ static int stpmic1_regulator_register(struct platform_device *pdev, int id,
pdev->name, rdev);
if (ret) {
dev_err(&pdev->dev, "Request IRQ failed\n");
- return ret;
+ return ERR_PTR(ret);
}
}
- return 0;
+
+ return rdev;
+}
+
+static struct regulator_dev *
+stpmic1_boost_register(struct platform_device *pdev, int id,
+ struct of_regulator_match *match,
+ const struct stpmic1_regulator_cfg *cfg,
+ struct boost_data *usb_data)
+{
+ struct stpmic1 *pmic_dev = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_dev *rdev;
+ struct regulator_config config = {};
+ int ret = 0;
+ int irq;
+
+ config.dev = &pdev->dev;
+ config.init_data = match->init_data;
+ config.of_node = match->of_node;
+ config.regmap = pmic_dev->regmap;
+ config.driver_data = (void *)usb_data;
+
+ rdev = devm_regulator_register(&pdev->dev, &cfg->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s regulator\n",
+ cfg->desc.name);
+ return rdev;
+ }
+
+ /* setup an irq handler for over-current detection */
+ irq = of_irq_get(config.of_node, 0);
+ if (irq > 0) {
+ ret = devm_request_threaded_irq(&pdev->dev,
+ irq, NULL,
+ stpmic1_boost_irq_handler,
+ IRQF_ONESHOT, pdev->name,
+ usb_data);
+ if (ret) {
+ dev_err(&pdev->dev, "Request IRQ failed\n");
+ return ERR_PTR(ret);
+ }
+ }
+
+ return rdev;
}
static int stpmic1_regulator_probe(struct platform_device *pdev)
{
int i, ret;
+ struct boost_data *usb_data;
+ struct regulator_dev *rdev;
ret = of_regulator_match(&pdev->dev, pdev->dev.of_node, stpmic1_matches,
ARRAY_SIZE(stpmic1_matches));
@@ -616,11 +753,30 @@ static int stpmic1_regulator_probe(struct platform_device *pdev)
return ret;
}
+ usb_data = devm_kzalloc(&pdev->dev, sizeof(*usb_data), GFP_KERNEL);
+ if (!usb_data)
+ return -ENOMEM;
+
for (i = 0; i < ARRAY_SIZE(stpmic1_regulator_cfgs); i++) {
- ret = stpmic1_regulator_register(pdev, i, &stpmic1_matches[i],
- &stpmic1_regulator_cfgs[i]);
- if (ret < 0)
- return ret;
+ if (i == STPMIC1_BOOST) {
+ rdev =
+ stpmic1_boost_register(pdev, i, &stpmic1_matches[i],
+ &stpmic1_regulator_cfgs[i],
+ usb_data);
+
+ usb_data->boost_rdev = rdev;
+ } else {
+ rdev =
+ stpmic1_regulator_register(pdev, i, &stpmic1_matches[i],
+ &stpmic1_regulator_cfgs[i]);
+
+ if (i == STPMIC1_VBUS_OTG)
+ usb_data->vbus_otg_rdev = rdev;
+ else if (i == STPMIC1_SW_OUT)
+ usb_data->sw_out_rdev = rdev;
+ }
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
}
dev_dbg(&pdev->dev, "stpmic1_regulator driver probed\n");
diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c
index ffdc55f87..1948a0090 100644
--- a/drivers/spi/spi-stm32-qspi.c
+++ b/drivers/spi/spi-stm32-qspi.c
@@ -305,10 +305,8 @@ static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi,
u32 cr, sr;
int err = 0;
- if (!op->data.nbytes)
- goto wait_nobusy;
-
- if (readl_relaxed(qspi->io_base + QSPI_SR) & SR_TCF)
+ if ((readl_relaxed(qspi->io_base + QSPI_SR) & SR_TCF) ||
+ qspi->fmode == CCR_FMODE_APM)
goto out;
reinit_completion(&qspi->data_completion);
@@ -327,7 +325,6 @@ static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi,
out:
/* clear flags */
writel_relaxed(FCR_CTCF | FCR_CTEF, qspi->io_base + QSPI_FCR);
-wait_nobusy:
if (!err)
err = stm32_qspi_wait_nobusy(qspi);
@@ -372,10 +369,6 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)
op->dummy.buswidth, op->data.buswidth,
op->addr.val, op->data.nbytes);
- err = stm32_qspi_wait_nobusy(qspi);
- if (err)
- goto abort;
-
cr = readl_relaxed(qspi->io_base + QSPI_CR);
cr &= ~CR_PRESC_MASK & ~CR_FSEL;
cr |= FIELD_PREP(CR_PRESC_MASK, flash->presc);
diff --git a/include/dt-bindings/pinctrl/stm32-pinfunc.h b/include/dt-bindings/pinctrl/stm32-pinfunc.h
index e6fb8ada3..370a25a93 100644
--- a/include/dt-bindings/pinctrl/stm32-pinfunc.h
+++ b/include/dt-bindings/pinctrl/stm32-pinfunc.h
@@ -26,6 +26,7 @@
#define AF14 0xf
#define AF15 0x10
#define ANALOG 0x11
+#define RSVD 0x12
/* define Pins number*/
#define PIN_NO(port, line) (((port) - 'A') * 0x10 + (line))
--
2.25.1