930 lines
26 KiB
Diff
930 lines
26 KiB
Diff
From c37d67ecd03c14eabef9d8cde67913bdfe497ae8 Mon Sep 17 00:00:00 2001
|
|
From: Lionel VITTE <lionel.vitte@st.com>
|
|
Date: Mon, 5 Oct 2020 13:19:47 +0200
|
|
Subject: [PATCH 13/22] ARM-stm32mp1-r2-rc8-MFD
|
|
|
|
---
|
|
.../bindings/mfd/st,stm32mp1-pwr.txt | 57 +++
|
|
.../devicetree/bindings/mfd/stm32-lptimer.txt | 10 +
|
|
.../bindings/mtd/st,stm32-fmc2-nand.yaml | 129 ++++++
|
|
drivers/mfd/Kconfig | 10 +
|
|
drivers/mfd/Makefile | 1 +
|
|
drivers/mfd/stm32-lptimer.c | 1 +
|
|
drivers/mfd/stm32-pwr.c | 400 ++++++++++++++++++
|
|
drivers/mfd/stm32-timers.c | 36 +-
|
|
drivers/mfd/stpmic1.c | 6 +
|
|
drivers/mfd/wm8994-core.c | 21 +
|
|
include/dt-bindings/mfd/stm32f4-rcc.h | 1 -
|
|
include/linux/mfd/stm32-lptimer.h | 5 +
|
|
include/linux/mfd/stm32-timers.h | 12 +-
|
|
include/linux/mfd/wm8994/pdata.h | 6 +
|
|
14 files changed, 675 insertions(+), 20 deletions(-)
|
|
create mode 100644 Documentation/devicetree/bindings/mfd/st,stm32mp1-pwr.txt
|
|
create mode 100644 Documentation/devicetree/bindings/mtd/st,stm32-fmc2-nand.yaml
|
|
create mode 100644 drivers/mfd/stm32-pwr.c
|
|
|
|
diff --git a/Documentation/devicetree/bindings/mfd/st,stm32mp1-pwr.txt b/Documentation/devicetree/bindings/mfd/st,stm32mp1-pwr.txt
|
|
new file mode 100644
|
|
index 0000000000000..eb622387bb651
|
|
--- /dev/null
|
|
+++ b/Documentation/devicetree/bindings/mfd/st,stm32mp1-pwr.txt
|
|
@@ -0,0 +1,57 @@
|
|
+STMicroelectronics STM32MP1 Power Management Controller
|
|
+=======================================================
|
|
+
|
|
+The PWR IP is responsible for handling the power related resources such as
|
|
+clocks, power supplies and resets. It provides 6 wake-up pins that are handled
|
|
+by an interrupt-controller. Wake-up pin can be used to wake-up from STANDBY SoC state.
|
|
+
|
|
+Required properties:
|
|
+- compatible should be: "st,stm32mp1-pwr"
|
|
+- reg: should be register base and length as documented in the
|
|
+ datasheet
|
|
+- interrupts: contains the reference to the gic wake-up pin interrupt
|
|
+- interrupt-controller; Enable interrupt controller for wake-up pins.
|
|
+- #interrupt-cells = <3>
|
|
+- wakeup-gpios: contains a list of GPIO spec describing each wake-up pin.
|
|
+
|
|
+Optional Properties:
|
|
+- pwr-supply: main soc power supply
|
|
+
|
|
+Interrupt consumers have to specify 3 cells:
|
|
+ - cell 1: wake-up pin id from 0 to 5
|
|
+ - cell 2: IRQ_TYPE_EDGE_FALLING or IRQ_TYPE_EDGE_RISING
|
|
+ - cell 3: Pull config: 0 = No Pull, 1=Pull Up, 2=Pull Down
|
|
+
|
|
+
|
|
+Example:
|
|
+
|
|
+ pwr: pwr@50001000 {
|
|
+ compatible = "st,stm32mp1-pwr", "simple-mfd";
|
|
+ reg = <0x50001000 0x400>;
|
|
+ interrupts = <GIC_SPI 149 IRQ_TYPE_NONE>;
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <3>;
|
|
+
|
|
+ wakeup-gpios = <&gpioa 0 0>, <&gpioa 2 0>,
|
|
+ <&gpioc 13 0>, <&gpioi 8 0>,
|
|
+ <&gpioi 11 0>, <&gpioc 1 0>;
|
|
+
|
|
+ pwr-supply = <&vdd>;
|
|
+ };
|
|
+
|
|
+
|
|
+Example of interrupt user:
|
|
+gpio_keys {
|
|
+ compatible = "gpio-keys";
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+
|
|
+ button@4 {
|
|
+ label = "WakeUp4";
|
|
+ linux,code = <BTN_4>;
|
|
+ interrupt-parent = <&pwr>;
|
|
+ interrupts = <3 IRQ_TYPE_EDGE_FALLING 1>;
|
|
+ wakeup-source;
|
|
+ };
|
|
+};
|
|
+
|
|
diff --git a/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
|
|
index fb54e4dad5b3e..ef3b795b5d9f0 100644
|
|
--- a/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
|
|
+++ b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
|
|
@@ -14,10 +14,15 @@ Required properties:
|
|
- #address-cells: Should be '<1>'.
|
|
- #size-cells: Should be '<0>'.
|
|
|
|
+Optional properties:
|
|
+- interrupts: Interrupt line for the LP timer.
|
|
+
|
|
Optional subnodes:
|
|
- pwm: See ../pwm/pwm-stm32-lp.txt
|
|
- counter: See ../counter/stm32-lptimer-cnt.txt
|
|
- trigger: See ../iio/timer/stm32-lptimer-trigger.txt
|
|
+- timer: Must contain "st,stm32-lptimer-timer" compatible
|
|
+ property.
|
|
|
|
Example:
|
|
|
|
@@ -26,6 +31,7 @@ Example:
|
|
reg = <0x40002400 0x400>;
|
|
clocks = <&timer_clk>;
|
|
clock-names = "mux";
|
|
+ interrupts-extended = <&exti 47 IRQ_TYPE_LEVEL_HIGH>;
|
|
#address-cells = <1>;
|
|
#size-cells = <0>;
|
|
|
|
@@ -45,4 +51,8 @@ Example:
|
|
pinctrl-names = "default";
|
|
pinctrl-0 = <&lptim1_in_pins>;
|
|
};
|
|
+
|
|
+ timer {
|
|
+ compatible = "st,stm32-lptimer-timer";
|
|
+ };
|
|
};
|
|
diff --git a/Documentation/devicetree/bindings/mtd/st,stm32-fmc2-nand.yaml b/Documentation/devicetree/bindings/mtd/st,stm32-fmc2-nand.yaml
|
|
new file mode 100644
|
|
index 0000000000000..6ae7de15d172f
|
|
--- /dev/null
|
|
+++ b/Documentation/devicetree/bindings/mtd/st,stm32-fmc2-nand.yaml
|
|
@@ -0,0 +1,129 @@
|
|
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
|
+%YAML 1.2
|
|
+---
|
|
+$id: http://devicetree.org/schemas/mtd/st,stm32-fmc2-nand.yaml#
|
|
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
|
+
|
|
+title: STMicroelectronics Flexible Memory Controller 2 (FMC2) Bindings
|
|
+
|
|
+maintainers:
|
|
+ - Christophe Kerello <christophe.kerello@st.com>
|
|
+
|
|
+properties:
|
|
+ compatible:
|
|
+ enum:
|
|
+ - st,stm32mp15-fmc2
|
|
+ - st,stm32mp1-fmc2-nfc
|
|
+
|
|
+ reg:
|
|
+ minItems: 6
|
|
+ maxItems: 7
|
|
+
|
|
+ interrupts:
|
|
+ maxItems: 1
|
|
+
|
|
+ dmas:
|
|
+ items:
|
|
+ - description: tx DMA channel
|
|
+ - description: rx DMA channel
|
|
+ - description: ecc DMA channel
|
|
+
|
|
+ dma-names:
|
|
+ items:
|
|
+ - const: tx
|
|
+ - const: rx
|
|
+ - const: ecc
|
|
+
|
|
+patternProperties:
|
|
+ "^nand@[a-f0-9]$":
|
|
+ type: object
|
|
+ properties:
|
|
+ nand-ecc-step-size:
|
|
+ const: 512
|
|
+
|
|
+ nand-ecc-strength:
|
|
+ enum: [1, 4 ,8 ]
|
|
+
|
|
+allOf:
|
|
+ - $ref: "nand-controller.yaml#"
|
|
+
|
|
+ - if:
|
|
+ properties:
|
|
+ compatible:
|
|
+ contains:
|
|
+ const: st,stm32mp15-fmc2
|
|
+ then:
|
|
+ properties:
|
|
+ reg:
|
|
+ items:
|
|
+ - description: Registers
|
|
+ - description: Chip select 0 data
|
|
+ - description: Chip select 0 command
|
|
+ - description: Chip select 0 address space
|
|
+ - description: Chip select 1 data
|
|
+ - description: Chip select 1 command
|
|
+ - description: Chip select 1 address space
|
|
+
|
|
+ clocks:
|
|
+ maxItems: 1
|
|
+
|
|
+ resets:
|
|
+ maxItems: 1
|
|
+
|
|
+ required:
|
|
+ - clocks
|
|
+
|
|
+ - if:
|
|
+ properties:
|
|
+ compatible:
|
|
+ contains:
|
|
+ const: st,stm32mp1-fmc2-nfc
|
|
+ then:
|
|
+ properties:
|
|
+ reg:
|
|
+ items:
|
|
+ - description: Chip select 0 data
|
|
+ - description: Chip select 0 command
|
|
+ - description: Chip select 0 address space
|
|
+ - description: Chip select 1 data
|
|
+ - description: Chip select 1 command
|
|
+ - description: Chip select 1 address space
|
|
+
|
|
+required:
|
|
+ - compatible
|
|
+ - reg
|
|
+ - interrupts
|
|
+
|
|
+examples:
|
|
+ - |
|
|
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
|
|
+ #include <dt-bindings/clock/stm32mp1-clks.h>
|
|
+ #include <dt-bindings/reset/stm32mp1-resets.h>
|
|
+ nand-controller@58002000 {
|
|
+ compatible = "st,stm32mp15-fmc2";
|
|
+ reg = <0x58002000 0x1000>,
|
|
+ <0x80000000 0x1000>,
|
|
+ <0x88010000 0x1000>,
|
|
+ <0x88020000 0x1000>,
|
|
+ <0x81000000 0x1000>,
|
|
+ <0x89010000 0x1000>,
|
|
+ <0x89020000 0x1000>;
|
|
+ interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ dmas = <&mdma1 20 0x2 0x12000a02 0x0 0x0>,
|
|
+ <&mdma1 20 0x2 0x12000a08 0x0 0x0>,
|
|
+ <&mdma1 21 0x2 0x12000a0a 0x0 0x0>;
|
|
+ dma-names = "tx", "rx", "ecc";
|
|
+ clocks = <&rcc FMC_K>;
|
|
+ resets = <&rcc FMC_R>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+
|
|
+ nand@0 {
|
|
+ reg = <0>;
|
|
+ nand-on-flash-bbt;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+...
|
|
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
|
|
index 43169f25da1fd..48ef1822ab1ce 100644
|
|
--- a/drivers/mfd/Kconfig
|
|
+++ b/drivers/mfd/Kconfig
|
|
@@ -1955,6 +1955,16 @@ config MFD_STPMIC1
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called stpmic1.
|
|
|
|
+config MFD_STM32MP1_PWR
|
|
+ bool "STM32MP1 wake-up pins"
|
|
+ depends on MACH_STM32MP157
|
|
+ default y
|
|
+ help
|
|
+ Select this option to enable STM32 PWR Wake-up pins driver.
|
|
+
|
|
+ This driver provides interruptions that can be used to wake-up from
|
|
+ suspend.
|
|
+
|
|
config MFD_STMFX
|
|
tristate "Support for STMicroelectronics Multi-Function eXpander (STMFX)"
|
|
depends on I2C
|
|
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
|
|
index c1067ea462046..11acfa1bf8160 100644
|
|
--- a/drivers/mfd/Makefile
|
|
+++ b/drivers/mfd/Makefile
|
|
@@ -249,6 +249,7 @@ obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
|
|
|
|
obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o
|
|
obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
|
|
+obj-$(CONFIG_MFD_STM32MP1_PWR) += stm32-pwr.o
|
|
obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
|
|
obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o
|
|
obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o
|
|
diff --git a/drivers/mfd/stm32-lptimer.c b/drivers/mfd/stm32-lptimer.c
|
|
index a00f99f365595..746e51a17cc8e 100644
|
|
--- a/drivers/mfd/stm32-lptimer.c
|
|
+++ b/drivers/mfd/stm32-lptimer.c
|
|
@@ -17,6 +17,7 @@ static const struct regmap_config stm32_lptimer_regmap_cfg = {
|
|
.val_bits = 32,
|
|
.reg_stride = sizeof(u32),
|
|
.max_register = STM32_LPTIM_MAX_REGISTER,
|
|
+ .fast_io = true,
|
|
};
|
|
|
|
static int stm32_lptimer_detect_encoder(struct stm32_lptimer *ddata)
|
|
diff --git a/drivers/mfd/stm32-pwr.c b/drivers/mfd/stm32-pwr.c
|
|
new file mode 100644
|
|
index 0000000000000..48ca8b4745d34
|
|
--- /dev/null
|
|
+++ b/drivers/mfd/stm32-pwr.c
|
|
@@ -0,0 +1,400 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2017 - All Rights Reserved
|
|
+ * Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
|
|
+ */
|
|
+
|
|
+#include <linux/arm-smccc.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/irqchip.h>
|
|
+#include <linux/irqchip/chained_irq.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_address.h>
|
|
+#include <linux/of_irq.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <asm/exception.h>
|
|
+
|
|
+#define NB_WAKEUPPINS 6
|
|
+
|
|
+#define STM32_SVC_PWR 0x82001001
|
|
+#define STM32_WRITE 0x1
|
|
+#define STM32_SET_BITS 0x2
|
|
+#define STM32_CLEAR_BITS 0x3
|
|
+
|
|
+#define PWR_WKUP_OFFSET 0x20
|
|
+// PWR Registers
|
|
+#define WKUPCR 0x0
|
|
+#define WKUPFR 0x4
|
|
+#define MPUWKUPENR 0x8
|
|
+
|
|
+#define WKUP_FLAGS_MASK GENMASK(5, 0)
|
|
+
|
|
+// WKUPCR bits definition
|
|
+#define WKUP_EDGE_SHIFT 8
|
|
+#define WKUP_PULL_SHIFT 16
|
|
+#define WKUP_PULL_MASK GENMASK(1, 0)
|
|
+
|
|
+enum wkup_pull_setting {
|
|
+ WKUP_NO_PULL = 0,
|
|
+ WKUP_PULL_UP,
|
|
+ WKUP_PULL_DOWN,
|
|
+ WKUP_PULL_RESERVED
|
|
+};
|
|
+
|
|
+#define SMC(class, op, offset, val) do { \
|
|
+ struct arm_smccc_res res; \
|
|
+ arm_smccc_smc(class, op, PWR_WKUP_OFFSET + (offset), val, \
|
|
+ 0, 0, 0, 0, &res); \
|
|
+} while (0) \
|
|
+
|
|
+struct stm32_pwr_data {
|
|
+ struct device *dev;
|
|
+ void __iomem *base; /* IO Memory base address */
|
|
+ struct irq_domain *domain; /* Domain for this controller */
|
|
+ int irq; /* Parent interrupt */
|
|
+ u32 masked; /* IRQ is masked */
|
|
+ u32 wake; /* IRQ is wake on */
|
|
+ u32 pending; /* IRQ has been received while wake on*/
|
|
+ struct gpio_desc *gpio[NB_WAKEUPPINS];
|
|
+};
|
|
+
|
|
+static void stm32_pwr_irq_ack(struct irq_data *d)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = d->domain->host_data;
|
|
+
|
|
+ dev_dbg(priv->dev, "irq:%lu\n", d->hwirq);
|
|
+ SMC(STM32_SVC_PWR, STM32_SET_BITS, WKUPCR, BIT(d->hwirq));
|
|
+}
|
|
+
|
|
+static void stm32_pwr_irq_set_enable(struct irq_data *d)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = d->domain->host_data;
|
|
+
|
|
+ dev_dbg(priv->dev, "irq:%lu\n", d->hwirq);
|
|
+ if (!(priv->masked & BIT(d->hwirq)) || (priv->wake & BIT(d->hwirq)))
|
|
+ SMC(STM32_SVC_PWR, STM32_SET_BITS, MPUWKUPENR, BIT(d->hwirq));
|
|
+ else
|
|
+ SMC(STM32_SVC_PWR, STM32_CLEAR_BITS, MPUWKUPENR, BIT(d->hwirq));
|
|
+}
|
|
+
|
|
+static void stm32_pwr_irq_mask(struct irq_data *d)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = d->domain->host_data;
|
|
+
|
|
+ dev_dbg(priv->dev, "irq:%lu\n", d->hwirq);
|
|
+ priv->masked |= BIT(d->hwirq);
|
|
+ stm32_pwr_irq_set_enable(d);
|
|
+}
|
|
+
|
|
+static void stm32_pwr_irq_unmask(struct irq_data *d)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = d->domain->host_data;
|
|
+
|
|
+ dev_dbg(priv->dev, "irq:%lu\n", d->hwirq);
|
|
+ priv->masked &= ~BIT(d->hwirq);
|
|
+ stm32_pwr_irq_set_enable(d);
|
|
+}
|
|
+
|
|
+static int stm32_pwr_irq_set_wake(struct irq_data *d, unsigned int on)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = d->domain->host_data;
|
|
+ struct irq_data *parent = irq_get_irq_data(priv->irq);
|
|
+
|
|
+ dev_dbg(priv->dev, "irq:%lu on:%d\n", d->hwirq, on);
|
|
+ if (on) {
|
|
+ priv->wake |= BIT(d->hwirq);
|
|
+ } else {
|
|
+ priv->wake &= ~BIT(d->hwirq);
|
|
+ priv->pending &= ~BIT(d->hwirq);
|
|
+ }
|
|
+ stm32_pwr_irq_set_enable(d);
|
|
+
|
|
+ if (parent->chip && parent->chip->irq_set_wake)
|
|
+ return parent->chip->irq_set_wake(parent, on);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_pwr_irq_set_type(struct irq_data *d, unsigned int flow_type)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = d->domain->host_data;
|
|
+ int pin_id = d->hwirq;
|
|
+ u32 wkupcr;
|
|
+ int en;
|
|
+
|
|
+ dev_dbg(priv->dev, "irq:%lu\n", d->hwirq);
|
|
+
|
|
+ en = readl_relaxed(priv->base + MPUWKUPENR) & BIT(pin_id);
|
|
+ /* reference manual request to disable the wakeup pin while
|
|
+ * changing the edge detection setting
|
|
+ */
|
|
+ if (en)
|
|
+ stm32_pwr_irq_mask(d);
|
|
+
|
|
+ wkupcr = readl_relaxed(priv->base + WKUPCR);
|
|
+ switch (flow_type & IRQ_TYPE_SENSE_MASK) {
|
|
+ case IRQF_TRIGGER_FALLING:
|
|
+ wkupcr |= (1 << (WKUP_EDGE_SHIFT + pin_id));
|
|
+ break;
|
|
+ case IRQF_TRIGGER_RISING:
|
|
+ wkupcr &= ~(1 << (WKUP_EDGE_SHIFT + pin_id));
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ SMC(STM32_SVC_PWR, STM32_WRITE, WKUPCR, wkupcr);
|
|
+
|
|
+ if (en)
|
|
+ stm32_pwr_irq_unmask(d);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_SMP
|
|
+static int stm32_pwr_set_affinity_parent(struct irq_data *data,
|
|
+ const struct cpumask *dest, bool force)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = data->domain->host_data;
|
|
+ struct irq_data *parent = irq_get_irq_data(priv->irq);
|
|
+
|
|
+ if (parent->chip && parent->chip->irq_set_affinity)
|
|
+ return parent->chip->irq_set_affinity(parent, dest, force);
|
|
+
|
|
+ return IRQ_SET_MASK_OK_DONE;
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int stm32_pwr_irq_request_resources(struct irq_data *d)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = d->domain->host_data;
|
|
+ struct gpio_desc *gpio;
|
|
+ int ret;
|
|
+
|
|
+ dev_dbg(priv->dev, "irq:%lu\n", d->hwirq);
|
|
+ gpio = gpiod_get_index(priv->dev, "wakeup", d->hwirq, GPIOD_IN);
|
|
+ if (IS_ERR(gpio)) {
|
|
+ ret = PTR_ERR(gpio);
|
|
+ dev_err(priv->dev, "Failed to get wakeup gpio: %d", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ priv->gpio[d->hwirq] = gpio;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void stm32_pwr_irq_release_resources(struct irq_data *d)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = d->domain->host_data;
|
|
+
|
|
+ dev_dbg(priv->dev, "irq:%lu\n", d->hwirq);
|
|
+ gpiod_put(priv->gpio[d->hwirq]);
|
|
+}
|
|
+
|
|
+static struct irq_chip stm32_pwr_irq_chip = {
|
|
+ .name = "stm32-pwr-irq",
|
|
+ .irq_ack = stm32_pwr_irq_ack,
|
|
+ .irq_mask = stm32_pwr_irq_mask,
|
|
+ .irq_unmask = stm32_pwr_irq_unmask,
|
|
+ .irq_set_type = stm32_pwr_irq_set_type,
|
|
+ .irq_set_wake = stm32_pwr_irq_set_wake,
|
|
+ .irq_request_resources = stm32_pwr_irq_request_resources,
|
|
+ .irq_release_resources = stm32_pwr_irq_release_resources,
|
|
+#ifdef CONFIG_SMP
|
|
+ .irq_set_affinity = stm32_pwr_set_affinity_parent,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static int stm32_pwr_irq_set_pull_config(struct irq_domain *d, int pin_id,
|
|
+ enum wkup_pull_setting config)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = d->host_data;
|
|
+ u32 wkupcr;
|
|
+
|
|
+ dev_dbg(priv->dev, "irq:%d pull config:0x%x\n", pin_id, config);
|
|
+
|
|
+ if (config >= WKUP_PULL_RESERVED) {
|
|
+ pr_err("%s: bad irq pull config\n", __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ wkupcr = readl_relaxed(priv->base + WKUPCR);
|
|
+ wkupcr &= ~((WKUP_PULL_MASK) << (WKUP_PULL_SHIFT + pin_id * 2));
|
|
+ wkupcr |= (config & WKUP_PULL_MASK) << (WKUP_PULL_SHIFT + pin_id * 2);
|
|
+
|
|
+ SMC(STM32_SVC_PWR, STM32_WRITE, WKUPCR, wkupcr);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_pwr_xlate(struct irq_domain *d, struct device_node *ctrlr,
|
|
+ const u32 *intspec, unsigned int intsize,
|
|
+ irq_hw_number_t *out_hwirq, unsigned int *out_type)
|
|
+{
|
|
+ if (WARN_ON(intsize < 3)) {
|
|
+ pr_err("%s: bad irq config parameters\n", __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ *out_hwirq = intspec[0];
|
|
+ *out_type = intspec[1] & (IRQ_TYPE_SENSE_MASK);
|
|
+
|
|
+ return stm32_pwr_irq_set_pull_config(d, intspec[0], intspec[2]);
|
|
+}
|
|
+
|
|
+static int stm32_pwr_alloc(struct irq_domain *d, unsigned int virq,
|
|
+ unsigned int nr_irqs, void *data)
|
|
+{
|
|
+ struct irq_fwspec *fwspec = data;
|
|
+ irq_hw_number_t hwirq;
|
|
+
|
|
+ hwirq = fwspec->param[0];
|
|
+ irq_domain_set_info(d, virq, hwirq, &stm32_pwr_irq_chip, d->host_data,
|
|
+ handle_edge_irq, NULL, NULL);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct irq_domain_ops stm32_pwr_irq_domain_ops = {
|
|
+ .alloc = stm32_pwr_alloc,
|
|
+ .xlate = stm32_pwr_xlate,
|
|
+ .free = irq_domain_free_irqs_common,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Handler for the cascaded IRQ.
|
|
+ */
|
|
+static void stm32_pwr_handle_irq(struct irq_desc *desc)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = irq_desc_get_handler_data(desc);
|
|
+ struct irq_chip *chip = irq_desc_get_chip(desc);
|
|
+ u32 wkupfr, wkupenr, i;
|
|
+
|
|
+ chained_irq_enter(chip, desc);
|
|
+
|
|
+ wkupfr = readl_relaxed(priv->base + WKUPFR);
|
|
+ wkupenr = readl_relaxed(priv->base + MPUWKUPENR);
|
|
+
|
|
+ for (i = 0; i < NB_WAKEUPPINS; i++) {
|
|
+ if ((wkupfr & BIT(i)) && (wkupenr & BIT(i))) {
|
|
+ struct irq_desc *d;
|
|
+
|
|
+ d = irq_to_desc(irq_find_mapping(priv->domain, i));
|
|
+
|
|
+ if (priv->wake & BIT(i)) {
|
|
+ dev_dbg(priv->dev,
|
|
+ "irq %d while wake enabled\n", i);
|
|
+ priv->pending |= BIT(i);
|
|
+ }
|
|
+
|
|
+ dev_dbg(priv->dev, "handle wkup irq:%d\n", i);
|
|
+ handle_edge_irq(d);
|
|
+ }
|
|
+ }
|
|
+ chained_irq_exit(chip, desc);
|
|
+}
|
|
+
|
|
+static int __maybe_unused stm32_pwr_suspend(struct device *dev)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = dev_get_drvdata(dev);
|
|
+
|
|
+ pr_debug("suspend");
|
|
+ if (priv->pending != 0)
|
|
+ return -EBUSY;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct dev_pm_ops stm32_pwr_pm = {
|
|
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_pwr_suspend, NULL)
|
|
+};
|
|
+
|
|
+static int stm32_pwr_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct stm32_pwr_data *priv;
|
|
+ struct device_node *np = dev->of_node;
|
|
+ struct resource *res;
|
|
+ int ret;
|
|
+
|
|
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
+ if (!priv)
|
|
+ return -ENOMEM;
|
|
+ priv->dev = dev;
|
|
+ dev_set_drvdata(dev, priv);
|
|
+
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ priv->base = devm_ioremap_resource(dev, res);
|
|
+ if (IS_ERR(priv->base)) {
|
|
+ dev_err(dev, "Unable to map registers\n");
|
|
+ return PTR_ERR(priv->base);
|
|
+ }
|
|
+
|
|
+ /* Disable all wake-up pins */
|
|
+ SMC(STM32_SVC_PWR, STM32_WRITE, MPUWKUPENR, 0);
|
|
+ /* Clear all interrupts flags */
|
|
+ SMC(STM32_SVC_PWR, STM32_SET_BITS, WKUPCR, WKUP_FLAGS_MASK);
|
|
+
|
|
+ priv->domain = irq_domain_add_linear(np, NB_WAKEUPPINS,
|
|
+ &stm32_pwr_irq_domain_ops, priv);
|
|
+ if (!priv->domain) {
|
|
+ dev_err(dev, "%s: Unable to add irq domain!\n", __func__);
|
|
+ ret = -ENOMEM;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = irq_of_parse_and_map(np, 0);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "failed to get PWR IRQ\n");
|
|
+ ret = priv->irq;
|
|
+ goto out_domain;
|
|
+ }
|
|
+
|
|
+ priv->irq = ret;
|
|
+ irq_set_chained_handler_and_data(priv->irq, stm32_pwr_handle_irq, priv);
|
|
+
|
|
+ of_node_clear_flag(np, OF_POPULATED);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_domain:
|
|
+ irq_domain_remove(priv->domain);
|
|
+out:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int stm32_pwr_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct stm32_pwr_data *priv = dev_get_drvdata(&pdev->dev);
|
|
+
|
|
+ irq_domain_remove(priv->domain);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id stm32_pwr_ids[] = {
|
|
+ { .compatible = "st,stm32mp1-pwr", },
|
|
+ {},
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, stm32_pwr_ids);
|
|
+
|
|
+static struct platform_driver stm32_pwr_driver = {
|
|
+ .probe = stm32_pwr_probe,
|
|
+ .remove = stm32_pwr_remove,
|
|
+ .driver = {
|
|
+ .name = "stm32_pwr",
|
|
+ .of_match_table = stm32_pwr_ids,
|
|
+ .pm = &stm32_pwr_pm,
|
|
+ },
|
|
+};
|
|
+
|
|
+static int __init stm32_pwr_init(void)
|
|
+{
|
|
+ return platform_driver_register(&stm32_pwr_driver);
|
|
+}
|
|
+
|
|
+static void __exit stm32_pwr_exit(void)
|
|
+{
|
|
+ return platform_driver_unregister(&stm32_pwr_driver);
|
|
+}
|
|
+
|
|
+arch_initcall(stm32_pwr_init);
|
|
+module_exit(stm32_pwr_exit);
|
|
diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c
|
|
index efcd4b980c94c..65a160a264555 100644
|
|
--- a/drivers/mfd/stm32-timers.c
|
|
+++ b/drivers/mfd/stm32-timers.c
|
|
@@ -167,26 +167,36 @@ static void stm32_timers_get_arr_size(struct stm32_timers *ddata)
|
|
regmap_write(ddata->regmap, TIM_ARR, 0x0);
|
|
}
|
|
|
|
-static void stm32_timers_dma_probe(struct device *dev,
|
|
- struct stm32_timers *ddata)
|
|
+static int stm32_timers_dma_probe(struct device *dev,
|
|
+ struct stm32_timers *ddata)
|
|
{
|
|
int i;
|
|
+ int ret = 0;
|
|
char name[4];
|
|
|
|
init_completion(&ddata->dma.completion);
|
|
mutex_init(&ddata->dma.lock);
|
|
|
|
- /* Optional DMA support: get valid DMA channel(s) or NULL */
|
|
+ /* Optional DMA support but report if an existing one fails */
|
|
for (i = STM32_TIMERS_DMA_CH1; i <= STM32_TIMERS_DMA_CH4; i++) {
|
|
snprintf(name, ARRAY_SIZE(name), "ch%1d", i + 1);
|
|
- ddata->dma.chans[i] = dma_request_slave_channel(dev, name);
|
|
+ ddata->dma.chans[i] = dma_request_chan(dev, name);
|
|
}
|
|
- ddata->dma.chans[STM32_TIMERS_DMA_UP] =
|
|
- dma_request_slave_channel(dev, "up");
|
|
- ddata->dma.chans[STM32_TIMERS_DMA_TRIG] =
|
|
- dma_request_slave_channel(dev, "trig");
|
|
- ddata->dma.chans[STM32_TIMERS_DMA_COM] =
|
|
- dma_request_slave_channel(dev, "com");
|
|
+ ddata->dma.chans[STM32_TIMERS_DMA_UP] = dma_request_chan(dev, "up");
|
|
+ ddata->dma.chans[STM32_TIMERS_DMA_TRIG] = dma_request_chan(dev, "trig");
|
|
+ ddata->dma.chans[STM32_TIMERS_DMA_COM] = dma_request_chan(dev, "com");
|
|
+
|
|
+ for (i = STM32_TIMERS_DMA_CH1; i < STM32_TIMERS_MAX_DMAS; i++) {
|
|
+ if (IS_ERR(ddata->dma.chans[i])) {
|
|
+ /* Save the first error code to return */
|
|
+ if (PTR_ERR(ddata->dma.chans[i]) != -ENODEV && !ret)
|
|
+ ret = PTR_ERR(ddata->dma.chans[i]);
|
|
+
|
|
+ ddata->dma.chans[i] = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
}
|
|
|
|
static void stm32_timers_dma_remove(struct device *dev,
|
|
@@ -230,7 +240,11 @@ static int stm32_timers_probe(struct platform_device *pdev)
|
|
|
|
stm32_timers_get_arr_size(ddata);
|
|
|
|
- stm32_timers_dma_probe(dev, ddata);
|
|
+ ret = stm32_timers_dma_probe(dev, ddata);
|
|
+ if (ret) {
|
|
+ stm32_timers_dma_remove(dev, ddata);
|
|
+ return ret;
|
|
+ }
|
|
|
|
platform_set_drvdata(pdev, ddata);
|
|
|
|
diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c
|
|
index 7dfbe8906cb86..766c3217f30ed 100644
|
|
--- a/drivers/mfd/stpmic1.c
|
|
+++ b/drivers/mfd/stpmic1.c
|
|
@@ -170,6 +170,9 @@ static int stpmic1_suspend(struct device *dev)
|
|
|
|
disable_irq(pmic_dev->irq);
|
|
|
|
+ if (device_may_wakeup(dev))
|
|
+ enable_irq_wake(pmic_dev->irq);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -183,6 +186,9 @@ static int stpmic1_resume(struct device *dev)
|
|
if (ret)
|
|
return ret;
|
|
|
|
+ if (device_may_wakeup(dev))
|
|
+ disable_irq_wake(pmic_dev->irq);
|
|
+
|
|
enable_irq(pmic_dev->irq);
|
|
|
|
return 0;
|
|
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
|
|
index 737dede4a95c3..8161ee16c2f86 100644
|
|
--- a/drivers/mfd/wm8994-core.c
|
|
+++ b/drivers/mfd/wm8994-core.c
|
|
@@ -7,6 +7,7 @@
|
|
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
|
*/
|
|
|
|
+#include <linux/clk.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
@@ -185,6 +186,12 @@ static int wm8994_resume(struct device *dev)
|
|
if (!wm8994->suspended)
|
|
return 0;
|
|
|
|
+ /*
|
|
+ * LDO1/2 minimum cycle time is 36ms according to codec specification
|
|
+ * Wait before enabling regulator to make sure we fit this requirement
|
|
+ */
|
|
+ msleep(40);
|
|
+
|
|
ret = regulator_bulk_enable(wm8994->num_supplies,
|
|
wm8994->supplies);
|
|
if (ret != 0) {
|
|
@@ -300,6 +307,20 @@ static int wm8994_set_pdata_from_of(struct wm8994 *wm8994)
|
|
|
|
pdata->csnaddr_pd = of_property_read_bool(np, "wlf,csnaddr-pd");
|
|
|
|
+
|
|
+ pdata->mclk1 = devm_clk_get(wm8994->dev, "MCLK1");
|
|
+ if (IS_ERR(pdata->mclk1)) {
|
|
+ if (PTR_ERR(pdata->mclk1) != -ENOENT)
|
|
+ return PTR_ERR(pdata->mclk1);
|
|
+ pdata->mclk1 = NULL;
|
|
+ }
|
|
+
|
|
+ pdata->mclk2 = devm_clk_get(wm8994->dev, "MCLK2");
|
|
+ if (IS_ERR(pdata->mclk2)) {
|
|
+ if (PTR_ERR(pdata->mclk2) != -ENOENT)
|
|
+ return PTR_ERR(pdata->mclk2);
|
|
+ pdata->mclk2 = NULL;
|
|
+ }
|
|
return 0;
|
|
}
|
|
#else
|
|
diff --git a/include/dt-bindings/mfd/stm32f4-rcc.h b/include/dt-bindings/mfd/stm32f4-rcc.h
|
|
index 309e8c79f27b1..36448a5619a12 100644
|
|
--- a/include/dt-bindings/mfd/stm32f4-rcc.h
|
|
+++ b/include/dt-bindings/mfd/stm32f4-rcc.h
|
|
@@ -34,7 +34,6 @@
|
|
#define STM32F4_AHB1_RESET(bit) (STM32F4_RCC_AHB1_##bit + (0x10 * 8))
|
|
#define STM32F4_AHB1_CLOCK(bit) (STM32F4_RCC_AHB1_##bit)
|
|
|
|
-
|
|
/* AHB2 */
|
|
#define STM32F4_RCC_AHB2_DCMI 0
|
|
#define STM32F4_RCC_AHB2_CRYP 4
|
|
diff --git a/include/linux/mfd/stm32-lptimer.h b/include/linux/mfd/stm32-lptimer.h
|
|
index 605f622648258..90b20550c1c8b 100644
|
|
--- a/include/linux/mfd/stm32-lptimer.h
|
|
+++ b/include/linux/mfd/stm32-lptimer.h
|
|
@@ -27,10 +27,15 @@
|
|
#define STM32_LPTIM_CMPOK BIT(3)
|
|
|
|
/* STM32_LPTIM_ICR - bit fields */
|
|
+#define STM32_LPTIM_ARRMCF BIT(1)
|
|
#define STM32_LPTIM_CMPOKCF_ARROKCF GENMASK(4, 3)
|
|
|
|
+/* STM32_LPTIM_IER - bit flieds */
|
|
+#define STM32_LPTIM_ARRMIE BIT(1)
|
|
+
|
|
/* STM32_LPTIM_CR - bit fields */
|
|
#define STM32_LPTIM_CNTSTRT BIT(2)
|
|
+#define STM32_LPTIM_SNGSTRT BIT(1)
|
|
#define STM32_LPTIM_ENABLE BIT(0)
|
|
|
|
/* STM32_LPTIM_CFGR - bit fields */
|
|
diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h
|
|
index 067d14655c28b..f8db83aedb2b5 100644
|
|
--- a/include/linux/mfd/stm32-timers.h
|
|
+++ b/include/linux/mfd/stm32-timers.h
|
|
@@ -70,14 +70,11 @@
|
|
#define TIM_CCER_CC4E BIT(12) /* Capt/Comp 4 out Ena */
|
|
#define TIM_CCER_CC4P BIT(13) /* Capt/Comp 4 Polarity */
|
|
#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12))
|
|
-#define TIM_BDTR_BKE BIT(12) /* Break input enable */
|
|
-#define TIM_BDTR_BKP BIT(13) /* Break input polarity */
|
|
+#define TIM_BDTR_BKE(x) BIT(12 + (x) * 12) /* Break input enable */
|
|
+#define TIM_BDTR_BKP(x) BIT(13 + (x) * 12) /* Break input polarity */
|
|
#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */
|
|
#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */
|
|
-#define TIM_BDTR_BKF (BIT(16) | BIT(17) | BIT(18) | BIT(19))
|
|
-#define TIM_BDTR_BK2F (BIT(20) | BIT(21) | BIT(22) | BIT(23))
|
|
-#define TIM_BDTR_BK2E BIT(24) /* Break 2 input enable */
|
|
-#define TIM_BDTR_BK2P BIT(25) /* Break 2 input polarity */
|
|
+#define TIM_BDTR_BKF(x) (0xf << (16 + (x) * 4))
|
|
#define TIM_DCR_DBA GENMASK(4, 0) /* DMA base addr */
|
|
#define TIM_DCR_DBL GENMASK(12, 8) /* DMA burst len */
|
|
|
|
@@ -87,8 +84,7 @@
|
|
#define TIM_CR2_MMS2_SHIFT 20
|
|
#define TIM_SMCR_TS_SHIFT 4
|
|
#define TIM_BDTR_BKF_MASK 0xF
|
|
-#define TIM_BDTR_BKF_SHIFT 16
|
|
-#define TIM_BDTR_BK2F_SHIFT 20
|
|
+#define TIM_BDTR_BKF_SHIFT(x) (16 + (x) * 4)
|
|
|
|
enum stm32_timers_dmas {
|
|
STM32_TIMERS_DMA_CH1,
|
|
diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h
|
|
index 81e7dcbd94dfb..addb2fede83d8 100644
|
|
--- a/include/linux/mfd/wm8994/pdata.h
|
|
+++ b/include/linux/mfd/wm8994/pdata.h
|
|
@@ -231,6 +231,12 @@ struct wm8994_pdata {
|
|
* GPIO for the IRQ pin if host only supports edge triggering
|
|
*/
|
|
int irq_gpio;
|
|
+
|
|
+ /* MCLK1 clock provider */
|
|
+ struct clk *mclk1;
|
|
+
|
|
+ /* MCLK2 clock provider */
|
|
+ struct clk *mclk2;
|
|
};
|
|
|
|
#endif
|
|
--
|
|
2.17.1
|
|
|