From c37d67ecd03c14eabef9d8cde67913bdfe497ae8 Mon Sep 17 00:00:00 2001 From: Lionel VITTE 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 = ; + 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 = ; + 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 + +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 + #include + #include + nand-controller@58002000 { + compatible = "st,stm32mp15-fmc2"; + reg = <0x58002000 0x1000>, + <0x80000000 0x1000>, + <0x88010000 0x1000>, + <0x88020000 0x1000>, + <0x81000000 0x1000>, + <0x89010000 0x1000>, + <0x89020000 0x1000>; + interrupts = ; + 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 for STMicroelectronics. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ +#include #include #include #include @@ -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