diff options
author | Arnd Bergmann <arnd@arndb.de> | 2016-09-02 18:29:04 +0200 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2016-09-02 18:29:04 +0200 |
commit | 7d3ef43f1f99d4250ff120c8965b3e5cd81b3c71 (patch) | |
tree | dd9398cd57f54f966b1b8fd66652a487ee226ecf | |
parent | 2b97f10b3896f5c155b28f6c733735fd60f4b3f9 (diff) | |
parent | cddb4800162e543c202cbb05795a324f1093d168 (diff) |
Merge tag 'reset-for-4.9' of git://git.pengutronix.de/git/pza/linux into next/drivers
Merge "Reset controller changes for v4.9" from Philipp Zabel:
- add reset controller driver Kconfig options,
allow building drivers with COMPILE_TEST
- add reset controller driver for UniPhier SoCs
- add reset controller driver for STM32 MCUs
- simplify SoCFPGA reset controller driver a bit
* tag 'reset-for-4.9' of git://git.pengutronix.de/git/pza/linux:
reset: hi6220: allow to compile test driver on other architectures
reset: zynq: add driver Kconfig option
reset: sunxi: add driver Kconfig option
reset: stm32: add driver Kconfig option
reset: socfpga: add driver Kconfig option
reset: pistachio: add driver Kconfig option
reset: meson: add driver Kconfig option
reset: lpc18xx: add driver Kconfig option
reset: berlin: add driver Kconfig option
reset: ath79: add driver Kconfig option
reset: ath79: add missing include
reset: warn on invalid input to reset_control_reset/assert/deassert/status
reset: uniphier: add reset controller driver for UniPhier SoCs
ARM: dts: stm32f429: add missing #reset-cells of rcc
drivers: reset: Add STM32 reset driver
dt-bindings: Document the STM32 reset bindings
dt-bindings: mfd: Add STM32F4 RCC numeric constants into DT include file
reset: socfpga: no need to store modrst_offset
-rw-r--r-- | Documentation/devicetree/bindings/clock/st,stm32-rcc.txt | 42 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/reset/st,stm32-rcc.txt | 6 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/reset/uniphier-reset.txt | 93 | ||||
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | arch/arm/boot/dts/stm32f429.dtsi | 1 | ||||
-rw-r--r-- | drivers/reset/Kconfig | 65 | ||||
-rw-r--r-- | drivers/reset/Makefile | 20 | ||||
-rw-r--r-- | drivers/reset/core.c | 12 | ||||
-rw-r--r-- | drivers/reset/hisilicon/Kconfig | 3 | ||||
-rw-r--r-- | drivers/reset/reset-ath79.c | 1 | ||||
-rw-r--r-- | drivers/reset/reset-socfpga.c | 19 | ||||
-rw-r--r-- | drivers/reset/reset-stm32.c | 108 | ||||
-rw-r--r-- | drivers/reset/reset-uniphier.c | 440 | ||||
-rw-r--r-- | include/dt-bindings/mfd/stm32f4-rcc.h | 98 |
14 files changed, 881 insertions, 28 deletions
diff --git a/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt index fee3205cdff9..c209de6cfadb 100644 --- a/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt +++ b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt @@ -1,16 +1,16 @@ STMicroelectronics STM32 Reset and Clock Controller =================================================== -The RCC IP is both a reset and a clock controller. This documentation only -describes the clock part. +The RCC IP is both a reset and a clock controller. -Please also refer to clock-bindings.txt in this directory for common clock -controller binding usage. +Please refer to clock-bindings.txt for common clock controller binding usage. +Please also refer to reset.txt for common reset controller binding usage. Required properties: - compatible: Should be "st,stm32f42xx-rcc" - reg: should be register base and length as documented in the datasheet +- #reset-cells: 1, see below - #clock-cells: 2, device nodes should specify the clock in their "clocks" property, containing a phandle to the clock device node, an index selecting between gated clocks and other clocks and an index specifying the clock to @@ -19,6 +19,7 @@ Required properties: Example: rcc: rcc@40023800 { + #reset-cells = <1>; #clock-cells = <2> compatible = "st,stm32f42xx-rcc", "st,stm32-rcc"; reg = <0x40023800 0x400>; @@ -35,16 +36,23 @@ from the first RCC clock enable register (RCC_AHB1ENR, address offset 0x30). It is calculated as: index = register_offset / 4 * 32 + bit_offset. Where bit_offset is the bit offset within the register (LSB is 0, MSB is 31). +To simplify the usage and to share bit definition with the reset and clock +drivers of the RCC IP, macros are available to generate the index in +human-readble format. + +For STM32F4 series, the macro are available here: + - include/dt-bindings/mfd/stm32f4-rcc.h + Example: /* Gated clock, AHB1 bit 0 (GPIOA) */ ... { - clocks = <&rcc 0 0> + clocks = <&rcc 0 STM32F4_AHB1_CLOCK(GPIOA)> }; /* Gated clock, AHB2 bit 4 (CRYP) */ ... { - clocks = <&rcc 0 36> + clocks = <&rcc 0 STM32F4_AHB2_CLOCK(CRYP)> }; Specifying other clocks @@ -61,5 +69,25 @@ Example: /* Misc clock, FCLK */ ... { - clocks = <&rcc 1 1> + clocks = <&rcc 1 STM32F4_APB1_CLOCK(TIM2)> + }; + + +Specifying softreset control of devices +======================================= + +Device nodes should specify the reset channel required in their "resets" +property, containing a phandle to the reset device node and an index specifying +which channel to use. +The index is the bit number within the RCC registers bank, starting from RCC +base address. +It is calculated as: index = register_offset / 4 * 32 + bit_offset. +Where bit_offset is the bit offset within the register. +For example, for CRC reset: + crc = AHB1RSTR_offset / 4 * 32 + CRCRST_bit_offset = 0x10 / 4 * 32 + 12 = 140 + +example: + + timer2 { + resets = <&rcc STM32F4_APB1_RESET(TIM2)>; }; diff --git a/Documentation/devicetree/bindings/reset/st,stm32-rcc.txt b/Documentation/devicetree/bindings/reset/st,stm32-rcc.txt new file mode 100644 index 000000000000..01db34375192 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/st,stm32-rcc.txt @@ -0,0 +1,6 @@ +STMicroelectronics STM32 Peripheral Reset Controller +==================================================== + +The RCC IP is both a reset and a clock controller. + +Please see Documentation/devicetree/bindings/clock/st,stm32-rcc.txt diff --git a/Documentation/devicetree/bindings/reset/uniphier-reset.txt b/Documentation/devicetree/bindings/reset/uniphier-reset.txt new file mode 100644 index 000000000000..e6bbfccd56c3 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/uniphier-reset.txt @@ -0,0 +1,93 @@ +UniPhier reset controller + + +System reset +------------ + +Required properties: +- compatible: should be one of the following: + "socionext,uniphier-sld3-reset" - for PH1-sLD3 SoC. + "socionext,uniphier-ld4-reset" - for PH1-LD4 SoC. + "socionext,uniphier-pro4-reset" - for PH1-Pro4 SoC. + "socionext,uniphier-sld8-reset" - for PH1-sLD8 SoC. + "socionext,uniphier-pro5-reset" - for PH1-Pro5 SoC. + "socionext,uniphier-pxs2-reset" - for ProXstream2/PH1-LD6b SoC. + "socionext,uniphier-ld11-reset" - for PH1-LD11 SoC. + "socionext,uniphier-ld20-reset" - for PH1-LD20 SoC. +- #reset-cells: should be 1. + +Example: + + sysctrl@61840000 { + compatible = "socionext,uniphier-ld20-sysctrl", + "simple-mfd", "syscon"; + reg = <0x61840000 0x4000>; + + reset { + compatible = "socionext,uniphier-ld20-reset"; + #reset-cells = <1>; + }; + + other nodes ... + }; + + +Media I/O (MIO) reset +--------------------- + +Required properties: +- compatible: should be one of the following: + "socionext,uniphier-sld3-mio-reset" - for PH1-sLD3 SoC. + "socionext,uniphier-ld4-mio-reset" - for PH1-LD4 SoC. + "socionext,uniphier-pro4-mio-reset" - for PH1-Pro4 SoC. + "socionext,uniphier-sld8-mio-reset" - for PH1-sLD8 SoC. + "socionext,uniphier-pro5-mio-reset" - for PH1-Pro5 SoC. + "socionext,uniphier-pxs2-mio-reset" - for ProXstream2/PH1-LD6b SoC. + "socionext,uniphier-ld11-mio-reset" - for PH1-LD11 SoC. + "socionext,uniphier-ld20-mio-reset" - for PH1-LD20 SoC. +- #reset-cells: should be 1. + +Example: + + mioctrl@59810000 { + compatible = "socionext,uniphier-ld20-mioctrl", + "simple-mfd", "syscon"; + reg = <0x59810000 0x800>; + + reset { + compatible = "socionext,uniphier-ld20-mio-reset"; + #reset-cells = <1>; + }; + + other nodes ... + }; + + +Peripheral reset +---------------- + +Required properties: +- compatible: should be one of the following: + "socionext,uniphier-ld4-peri-reset" - for PH1-LD4 SoC. + "socionext,uniphier-pro4-peri-reset" - for PH1-Pro4 SoC. + "socionext,uniphier-sld8-peri-reset" - for PH1-sLD8 SoC. + "socionext,uniphier-pro5-peri-reset" - for PH1-Pro5 SoC. + "socionext,uniphier-pxs2-peri-reset" - for ProXstream2/PH1-LD6b SoC. + "socionext,uniphier-ld11-peri-reset" - for PH1-LD11 SoC. + "socionext,uniphier-ld20-peri-reset" - for PH1-LD20 SoC. +- #reset-cells: should be 1. + +Example: + + perictrl@59820000 { + compatible = "socionext,uniphier-ld20-perictrl", + "simple-mfd", "syscon"; + reg = <0x59820000 0x200>; + + reset { + compatible = "socionext,uniphier-ld20-peri-reset"; + #reset-cells = <1>; + }; + + other nodes ... + }; diff --git a/MAINTAINERS b/MAINTAINERS index 71aa5daeae8f..1cb3ce2db23c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1840,6 +1840,7 @@ F: arch/arm64/boot/dts/socionext/ F: drivers/bus/uniphier-system-bus.c F: drivers/i2c/busses/i2c-uniphier* F: drivers/pinctrl/uniphier/ +F: drivers/reset/reset-uniphier.c F: drivers/tty/serial/8250/8250_uniphier.c N: uniphier diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi index 35df462559ca..fe892361a8b9 100644 --- a/arch/arm/boot/dts/stm32f429.dtsi +++ b/arch/arm/boot/dts/stm32f429.dtsi @@ -326,6 +326,7 @@ }; rcc: rcc@40023810 { + #reset-cells = <1>; #clock-cells = <2>; compatible = "st,stm32f42xx-rcc", "st,stm32-rcc"; reg = <0x40023800 0x400>; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 4be1b8c21f6f..06d9fa2f3bc0 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -14,9 +14,58 @@ menuconfig RESET_CONTROLLER if RESET_CONTROLLER +config RESET_ATH79 + bool "AR71xx Reset Driver" if COMPILE_TEST + default ATH79 + help + This enables the ATH79 reset controller driver that supports the + AR71xx SoC reset controller. + +config RESET_BERLIN + bool "Berlin Reset Driver" if COMPILE_TEST + default ARCH_BERLIN + help + This enables the reset controller driver for Marvell Berlin SoCs. + +config RESET_LPC18XX + bool "LPC18xx/43xx Reset Driver" if COMPILE_TEST + default ARCH_LPC18XX + help + This enables the reset controller driver for NXP LPC18xx/43xx SoCs. + +config RESET_MESON + bool "Meson Reset Driver" if COMPILE_TEST + default ARCH_MESON + help + This enables the reset driver for Amlogic Meson SoCs. + config RESET_OXNAS bool +config RESET_PISTACHIO + bool "Pistachio Reset Driver" if COMPILE_TEST + default MACH_PISTACHIO + help + This enables the reset driver for ImgTec Pistachio SoCs. + +config RESET_SOCFPGA + bool "SoCFPGA Reset Driver" if COMPILE_TEST + default ARCH_SOCFPGA + help + This enables the reset controller driver for Altera SoCFPGAs. + +config RESET_STM32 + bool "STM32 Reset Driver" if COMPILE_TEST + default ARCH_STM32 + help + This enables the RCC reset controller driver for STM32 MCUs. + +config RESET_SUNXI + bool "Allwinner SoCs Reset Driver" if COMPILE_TEST && !ARCH_SUNXI + default ARCH_SUNXI + help + This enables the reset driver for Allwinner SoCs. + config TI_SYSCON_RESET tristate "TI SYSCON Reset Driver" depends on HAS_IOMEM @@ -27,6 +76,22 @@ config TI_SYSCON_RESET you wish to use the reset framework for such memory-mapped devices, say Y here. Otherwise, say N. +config RESET_UNIPHIER + tristate "Reset controller driver for UniPhier SoCs" + depends on ARCH_UNIPHIER || COMPILE_TEST + depends on OF && MFD_SYSCON + default ARCH_UNIPHIER + help + Support for reset controllers on UniPhier SoCs. + Say Y if you want to control reset signals provided by System Control + block, Media I/O block, Peripheral Block. + +config RESET_ZYNQ + bool "ZYNQ Reset Driver" if COMPILE_TEST + default ARCH_ZYNQ + help + This enables the reset controller driver for Xilinx Zynq SoCs. + source "drivers/reset/sti/Kconfig" source "drivers/reset/hisilicon/Kconfig" diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 5d65a93d3c43..bbe7026617fc 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,13 +1,15 @@ obj-y += core.o -obj-$(CONFIG_ARCH_LPC18XX) += reset-lpc18xx.o -obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o -obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o -obj-$(CONFIG_MACH_PISTACHIO) += reset-pistachio.o -obj-$(CONFIG_ARCH_MESON) += reset-meson.o -obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o +obj-y += hisilicon/ obj-$(CONFIG_ARCH_STI) += sti/ -obj-$(CONFIG_ARCH_HISI) += hisilicon/ -obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o -obj-$(CONFIG_ATH79) += reset-ath79.o +obj-$(CONFIG_RESET_ATH79) += reset-ath79.o +obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o +obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o +obj-$(CONFIG_RESET_MESON) += reset-meson.o obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o +obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o +obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o +obj-$(CONFIG_RESET_STM32) += reset-stm32.o +obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_TI_SYSCON_RESET) += reset-ti-syscon.o +obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o +obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 395dc9ce492e..b8ae1dbd4c17 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -138,7 +138,8 @@ EXPORT_SYMBOL_GPL(devm_reset_controller_register); */ int reset_control_reset(struct reset_control *rstc) { - if (WARN_ON(rstc->shared)) + if (WARN_ON(IS_ERR_OR_NULL(rstc)) || + WARN_ON(rstc->shared)) return -EINVAL; if (rstc->rcdev->ops->reset) @@ -161,6 +162,9 @@ EXPORT_SYMBOL_GPL(reset_control_reset); */ int reset_control_assert(struct reset_control *rstc) { + if (WARN_ON(IS_ERR_OR_NULL(rstc))) + return -EINVAL; + if (!rstc->rcdev->ops->assert) return -ENOTSUPP; @@ -184,6 +188,9 @@ EXPORT_SYMBOL_GPL(reset_control_assert); */ int reset_control_deassert(struct reset_control *rstc) { + if (WARN_ON(IS_ERR_OR_NULL(rstc))) + return -EINVAL; + if (!rstc->rcdev->ops->deassert) return -ENOTSUPP; @@ -204,6 +211,9 @@ EXPORT_SYMBOL_GPL(reset_control_deassert); */ int reset_control_status(struct reset_control *rstc) { + if (WARN_ON(IS_ERR_OR_NULL(rstc))) + return -EINVAL; + if (rstc->rcdev->ops->status) return rstc->rcdev->ops->status(rstc->rcdev, rstc->id); diff --git a/drivers/reset/hisilicon/Kconfig b/drivers/reset/hisilicon/Kconfig index 26bf95a83a8e..1ff8b0c80980 100644 --- a/drivers/reset/hisilicon/Kconfig +++ b/drivers/reset/hisilicon/Kconfig @@ -1,5 +1,6 @@ config COMMON_RESET_HI6220 tristate "Hi6220 Reset Driver" - depends on (ARCH_HISI && RESET_CONTROLLER) + depends on ARCH_HISI || COMPILE_TEST + default ARCH_HISI help Build the Hisilicon Hi6220 reset driver. diff --git a/drivers/reset/reset-ath79.c b/drivers/reset/reset-ath79.c index 16d410cd6146..6b97631f5489 100644 --- a/drivers/reset/reset-ath79.c +++ b/drivers/reset/reset-ath79.c @@ -12,6 +12,7 @@ * GNU General Public License for more details. */ +#include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/reset-controller.h> diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index 12add9b0fa49..78ebf8424375 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c @@ -28,7 +28,6 @@ struct socfpga_reset_data { spinlock_t lock; void __iomem *membase; - u32 modrst_offset; struct reset_controller_dev rcdev; }; @@ -45,9 +44,8 @@ static int socfpga_reset_assert(struct reset_controller_dev *rcdev, spin_lock_irqsave(&data->lock, flags); - reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS)); - writel(reg | BIT(offset), data->membase + data->modrst_offset + - (bank * NR_BANKS)); + reg = readl(data->membase + (bank * NR_BANKS)); + writel(reg | BIT(offset), data->membase + (bank * NR_BANKS)); spin_unlock_irqrestore(&data->lock, flags); return 0; @@ -67,9 +65,8 @@ static int socfpga_reset_deassert(struct reset_controller_dev *rcdev, spin_lock_irqsave(&data->lock, flags); - reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS)); - writel(reg & ~BIT(offset), data->membase + data->modrst_offset + - (bank * NR_BANKS)); + reg = readl(data->membase + (bank * NR_BANKS)); + writel(reg & ~BIT(offset), data->membase + (bank * NR_BANKS)); spin_unlock_irqrestore(&data->lock, flags); @@ -85,7 +82,7 @@ static int socfpga_reset_status(struct reset_controller_dev *rcdev, int offset = id % BITS_PER_LONG; u32 reg; - reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS)); + reg = readl(data->membase + (bank * NR_BANKS)); return !(reg & BIT(offset)); } @@ -102,6 +99,7 @@ static int socfpga_reset_probe(struct platform_device *pdev) struct resource *res; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; + u32 modrst_offset; /* * The binding was mainlined without the required property. @@ -122,10 +120,11 @@ static int socfpga_reset_probe(struct platform_device *pdev) if (IS_ERR(data->membase)) return PTR_ERR(data->membase); - if (of_property_read_u32(np, "altr,modrst-offset", &data->modrst_offset)) { + if (of_property_read_u32(np, "altr,modrst-offset", &modrst_offset)) { dev_warn(dev, "missing altr,modrst-offset property, assuming 0x10!\n"); - data->modrst_offset = 0x10; + modrst_offset = 0x10; } + data->membase += modrst_offset; spin_lock_init(&data->lock); diff --git a/drivers/reset/reset-stm32.c b/drivers/reset/reset-stm32.c new file mode 100644 index 000000000000..3a7c8527e66a --- /dev/null +++ b/drivers/reset/reset-stm32.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) Maxime Coquelin 2015 + * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com> + * License terms: GNU General Public License (GPL), version 2 + * + * Heavily based on sunxi driver from Maxime Ripard. + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +struct stm32_reset_data { + spinlock_t lock; + void __iomem *membase; + struct reset_controller_dev rcdev; +}; + +static int stm32_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct stm32_reset_data *data = container_of(rcdev, + struct stm32_reset_data, + rcdev); + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&data->lock, flags); + + reg = readl(data->membase + (bank * 4)); + writel(reg | BIT(offset), data->membase + (bank * 4)); + + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static int stm32_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct stm32_reset_data *data = container_of(rcdev, + struct stm32_reset_data, + rcdev); + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&data->lock, flags); + + reg = readl(data->membase + (bank * 4)); + writel(reg & ~BIT(offset), data->membase + (bank * 4)); + + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static const struct reset_control_ops stm32_reset_ops = { + .assert = stm32_reset_assert, + .deassert = stm32_reset_deassert, +}; + +static const struct of_device_id stm32_reset_dt_ids[] = { + { .compatible = "st,stm32-rcc", }, + { /* sentinel */ }, +}; + +static int stm32_reset_probe(struct platform_device *pdev) +{ + struct stm32_reset_data *data; + struct resource *res; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->membase)) + return PTR_ERR(data->membase); + + spin_lock_init(&data->lock); + + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = resource_size(res) * 8; + data->rcdev.ops = &stm32_reset_ops; + data->rcdev.of_node = pdev->dev.of_node; + + return devm_reset_controller_register(&pdev->dev, &data->rcdev); +} + +static struct platform_driver stm32_reset_driver = { + .probe = stm32_reset_probe, + .driver = { + .name = "stm32-rcc-reset", + .of_match_table = stm32_reset_dt_ids, + }, +}; +builtin_platform_driver(stm32_reset_driver); diff --git a/drivers/reset/reset-uniphier.c b/drivers/reset/reset-uniphier.c new file mode 100644 index 000000000000..8b2558e7363e --- /dev/null +++ b/drivers/reset/reset-uniphier.c @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> + +struct uniphier_reset_data { + unsigned int id; + unsigned int reg; + unsigned int bit; + unsigned int flags; +#define UNIPHIER_RESET_ACTIVE_LOW BIT(0) +}; + +#define UNIPHIER_RESET_ID_END (unsigned int)(-1) + +#define UNIPHIER_RESET_END \ + { .id = UNIPHIER_RESET_ID_END } + +#define UNIPHIER_RESET(_id, _reg, _bit) \ + { \ + .id = (_id), \ + .reg = (_reg), \ + .bit = (_bit), \ + } + +#define UNIPHIER_RESETX(_id, _reg, _bit) \ + { \ + .id = (_id), \ + .reg = (_reg), \ + .bit = (_bit), \ + .flags = UNIPHIER_RESET_ACTIVE_LOW, \ + } + +/* System reset data */ +#define UNIPHIER_SLD3_SYS_RESET_STDMAC(id) \ + UNIPHIER_RESETX((id), 0x2000, 10) + +#define UNIPHIER_LD11_SYS_RESET_STDMAC(id) \ + UNIPHIER_RESETX((id), 0x200c, 8) + +#define UNIPHIER_PRO4_SYS_RESET_GIO(id) \ + UNIPHIER_RESETX((id), 0x2000, 6) + +#define UNIPHIER_LD20_SYS_RESET_GIO(id) \ + UNIPHIER_RESETX((id), 0x200c, 5) + +#define UNIPHIER_PRO4_SYS_RESET_USB3(id, ch) \ + UNIPHIER_RESETX((id), 0x2000 + 0x4 * (ch), 17) + +const struct uniphier_reset_data uniphier_sld3_sys_reset_data[] = { + UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* Ether, HSC, MIO */ + UNIPHIER_RESET_END, +}; + +const struct uniphier_reset_data uniphier_pro4_sys_reset_data[] = { + UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC, MIO, RLE */ + UNIPHIER_PRO4_SYS_RESET_GIO(12), /* Ether, SATA, USB3 */ + UNIPHIER_PRO4_SYS_RESET_USB3(14, 0), + UNIPHIER_PRO4_SYS_RESET_USB3(15, 1), + UNIPHIER_RESET_END, +}; + +const struct uniphier_reset_data uniphier_pro5_sys_reset_data[] = { + UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC */ + UNIPHIER_PRO4_SYS_RESET_GIO(12), /* PCIe, USB3 */ + UNIPHIER_PRO4_SYS_RESET_USB3(14, 0), + UNIPHIER_PRO4_SYS_RESET_USB3(15, 1), + UNIPHIER_RESET_END, +}; + +const struct uniphier_reset_data uniphier_pxs2_sys_reset_data[] = { + UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC, RLE */ + UNIPHIER_PRO4_SYS_RESET_USB3(14, 0), + UNIPHIER_PRO4_SYS_RESET_USB3(15, 1), + UNIPHIER_RESETX(16, 0x2014, 4), /* USB30-PHY0 */ + UNIPHIER_RESETX(17, 0x2014, 0), /* USB30-PHY1 */ + UNIPHIER_RESETX(18, 0x2014, 2), /* USB30-PHY2 */ + UNIPHIER_RESETX(20, 0x2014, 5), /* USB31-PHY0 */ + UNIPHIER_RESETX(21, 0x2014, 1), /* USB31-PHY1 */ + UNIPHIER_RESETX(28, 0x2014, 12), /* SATA */ + UNIPHIER_RESET(29, 0x2014, 8), /* SATA-PHY (active high) */ + UNIPHIER_RESET_END, +}; + +const struct uniphier_reset_data uniphier_ld11_sys_reset_data[] = { + UNIPHIER_LD11_SYS_RESET_STDMAC(8), /* HSC, MIO */ + UNIPHIER_RESET_END, +}; + +const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = { + UNIPHIER_LD11_SYS_RESET_STDMAC(8), /* HSC */ + UNIPHIER_LD20_SYS_RESET_GIO(12), /* PCIe, USB3 */ + UNIPHIER_RESETX(16, 0x200c, 12), /* USB30-PHY0 */ + UNIPHIER_RESETX(17, 0x200c, 13), /* USB30-PHY1 */ + UNIPHIER_RESETX(18, 0x200c, 14), /* USB30-PHY2 */ + UNIPHIER_RESETX(19, 0x200c, 15), /* USB30-PHY3 */ + UNIPHIER_RESET_END, +}; + +/* Media I/O reset data */ +#define UNIPHIER_MIO_RESET_SD(id, ch) \ + UNIPHIER_RESETX((id), 0x110 + 0x200 * (ch), 0) + +#define UNIPHIER_MIO_RESET_SD_BRIDGE(id, ch) \ + UNIPHIER_RESETX((id), 0x110 + 0x200 * (ch), 26) + +#define UNIPHIER_MIO_RESET_EMMC_HW_RESET(id, ch) \ + UNIPHIER_RESETX((id), 0x80 + 0x200 * (ch), 0) + +#define UNIPHIER_MIO_RESET_USB2(id, ch) \ + UNIPHIER_RESETX((id), 0x114 + 0x200 * (ch), 0) + +#define UNIPHIER_MIO_RESET_USB2_BRIDGE(id, ch) \ + UNIPHIER_RESETX((id), 0x110 + 0x200 * (ch), 24) + +#define UNIPHIER_MIO_RESET_DMAC(id) \ + UNIPHIER_RESETX((id), 0x110, 17) + +const struct uniphier_reset_data uniphier_sld3_mio_reset_data[] = { + UNIPHIER_MIO_RESET_SD(0, 0), + UNIPHIER_MIO_RESET_SD(1, 1), + UNIPHIER_MIO_RESET_SD(2, 2), + UNIPHIER_MIO_RESET_SD_BRIDGE(3, 0), + UNIPHIER_MIO_RESET_SD_BRIDGE(4, 1), + UNIPHIER_MIO_RESET_SD_BRIDGE(5, 2), + UNIPHIER_MIO_RESET_EMMC_HW_RESET(6, 1), + UNIPHIER_MIO_RESET_DMAC(7), + UNIPHIER_MIO_RESET_USB2(8, 0), + UNIPHIER_MIO_RESET_USB2(9, 1), + UNIPHIER_MIO_RESET_USB2(10, 2), + UNIPHIER_MIO_RESET_USB2(11, 3), + UNIPHIER_MIO_RESET_USB2_BRIDGE(12, 0), + UNIPHIER_MIO_RESET_USB2_BRIDGE(13, 1), + UNIPHIER_MIO_RESET_USB2_BRIDGE(14, 2), + UNIPHIER_MIO_RESET_USB2_BRIDGE(15, 3), + UNIPHIER_RESET_END, +}; + +const struct uniphier_reset_data uniphier_pro5_mio_reset_data[] = { + UNIPHIER_MIO_RESET_SD(0, 0), + UNIPHIER_MIO_RESET_SD(1, 1), + UNIPHIER_MIO_RESET_EMMC_HW_RESET(6, 1), + UNIPHIER_RESET_END, +}; + +/* Peripheral reset data */ +#define UNIPHIER_PERI_RESET_UART(id, ch) \ + UNIPHIER_RESETX((id), 0x114, 19 + (ch)) + +#define UNIPHIER_PERI_RESET_I2C(id, ch) \ + UNIPHIER_RESETX((id), 0x114, 5 + (ch)) + +#define UNIPHIER_PERI_RESET_FI2C(id, ch) \ + UNIPHIER_RESETX((id), 0x114, 24 + (ch)) + +const struct uniphier_reset_data uniphier_ld4_peri_reset_data[] = { + UNIPHIER_PERI_RESET_UART(0, 0), + UNIPHIER_PERI_RESET_UART(1, 1), + UNIPHIER_PERI_RESET_UART(2, 2), + UNIPHIER_PERI_RESET_UART(3, 3), + UNIPHIER_PERI_RESET_I2C(4, 0), + UNIPHIER_PERI_RESET_I2C(5, 1), + UNIPHIER_PERI_RESET_I2C(6, 2), + UNIPHIER_PERI_RESET_I2C(7, 3), + UNIPHIER_PERI_RESET_I2C(8, 4), + UNIPHIER_RESET_END, +}; + +const struct uniphier_reset_data uniphier_pro4_peri_reset_data[] = { + UNIPHIER_PERI_RESET_UART(0, 0), + UNIPHIER_PERI_RESET_UART(1, 1), + UNIPHIER_PERI_RESET_UART(2, 2), + UNIPHIER_PERI_RESET_UART(3, 3), + UNIPHIER_PERI_RESET_FI2C(4, 0), + UNIPHIER_PERI_RESET_FI2C(5, 1), + UNIPHIER_PERI_RESET_FI2C(6, 2), + UNIPHIER_PERI_RESET_FI2C(7, 3), + UNIPHIER_PERI_RESET_FI2C(8, 4), + UNIPHIER_PERI_RESET_FI2C(9, 5), + UNIPHIER_PERI_RESET_FI2C(10, 6), + UNIPHIER_RESET_END, +}; + +/* core implementaton */ +struct uniphier_reset_priv { + struct reset_controller_dev rcdev; + struct device *dev; + struct regmap *regmap; + const struct uniphier_reset_data *data; +}; + +#define to_uniphier_reset_priv(_rcdev) \ + container_of(_rcdev, struct uniphier_reset_priv, rcdev) + +static int uniphier_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, int assert) +{ + struct uniphier_reset_priv *priv = to_uniphier_reset_priv(rcdev); + const struct uniphier_reset_data *p; + + for (p = priv->data; p->id != UNIPHIER_RESET_ID_END; p++) { + unsigned int mask, val; + + if (p->id != id) + continue; + + mask = BIT(p->bit); + + if (assert) + val = mask; + else + val = ~mask; + + if (p->flags & UNIPHIER_RESET_ACTIVE_LOW) + val = ~val; + + return regmap_write_bits(priv->regmap, p->reg, mask, val); + } + + dev_err(priv->dev, "reset_id=%lu was not handled\n", id); + return -EINVAL; +} + +static int uniphier_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return uniphier_reset_update(rcdev, id, 1); +} + +static int uniphier_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return uniphier_reset_update(rcdev, id, 0); +} + +static int uniphier_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct uniphier_reset_priv *priv = to_uniphier_reset_priv(rcdev); + const struct uniphier_reset_data *p; + + for (p = priv->data; p->id != UNIPHIER_RESET_ID_END; p++) { + unsigned int val; + int ret, asserted; + + if (p->id != id) + continue; + + ret = regmap_read(priv->regmap, p->reg, &val); + if (ret) + return ret; + + asserted = !!(val & BIT(p->bit)); + + if (p->flags & UNIPHIER_RESET_ACTIVE_LOW) + asserted = !asserted; + + return asserted; + } + + dev_err(priv->dev, "reset_id=%lu was not found\n", id); + return -EINVAL; +} + +static const struct reset_control_ops uniphier_reset_ops = { + .assert = uniphier_reset_assert, + .deassert = uniphier_reset_deassert, + .status = uniphier_reset_status, +}; + +static int uniphier_reset_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uniphier_reset_priv *priv; + const struct uniphier_reset_data *p, *data; + struct regmap *regmap; + struct device_node *parent; + unsigned int nr_resets = 0; + + data = of_device_get_match_data(dev); + if (WARN_ON(!data)) + return -EINVAL; + + parent = of_get_parent(dev->of_node); /* parent should be syscon node */ + regmap = syscon_node_to_regmap(parent); + of_node_put(parent); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to get regmap (error %ld)\n", + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + for (p = data; p->id != UNIPHIER_RESET_ID_END; p++) + nr_resets = max(nr_resets, p->id + 1); + + priv->rcdev.ops = &uniphier_reset_ops; + priv->rcdev.owner = dev->driver->owner; + priv->rcdev.of_node = dev->of_node; + priv->rcdev.nr_resets = nr_resets; + priv->dev = dev; + priv->regmap = regmap; + priv->data = data; + + return devm_reset_controller_register(&pdev->dev, &priv->rcdev); +} + +static const struct of_device_id uniphier_reset_match[] = { + /* System reset */ + { + .compatible = "socionext,uniphier-sld3-reset", + .data = uniphier_sld3_sys_reset_data, + }, + { + .compatible = "socionext,uniphier-ld4-reset", + .data = uniphier_sld3_sys_reset_data, + }, + { + .compatible = "socionext,uniphier-pro4-reset", + .data = uniphier_pro4_sys_reset_data, + }, + { + .compatible = "socionext,uniphier-sld8-reset", + .data = uniphier_sld3_sys_reset_data, + }, + { + .compatible = "socionext,uniphier-pro5-reset", + .data = uniphier_pro5_sys_reset_data, + }, + { + .compatible = "socionext,uniphier-pxs2-reset", + .data = uniphier_pxs2_sys_reset_data, + }, + { + .compatible = "socionext,uniphier-ld11-reset", + .data = uniphier_ld11_sys_reset_data, + }, + { + .compatible = "socionext,uniphier-ld20-reset", + .data = uniphier_ld20_sys_reset_data, + }, + /* Media I/O reset */ + { + .compatible = "socionext,uniphier-sld3-mio-reset", + .data = uniphier_sld3_mio_reset_data, + }, + { + .compatible = "socionext,uniphier-ld4-mio-reset", + .data = uniphier_sld3_mio_reset_data, + }, + { + .compatible = "socionext,uniphier-pro4-mio-reset", + .data = uniphier_sld3_mio_reset_data, + }, + { + .compatible = "socionext,uniphier-sld8-mio-reset", + .data = uniphier_sld3_mio_reset_data, + }, + { + .compatible = "socionext,uniphier-pro5-mio-reset", + .data = uniphier_pro5_mio_reset_data, + }, + { + .compatible = "socionext,uniphier-pxs2-mio-reset", + .data = uniphier_pro5_mio_reset_data, + }, + { + .compatible = "socionext,uniphier-ld11-mio-reset", + .data = uniphier_sld3_mio_reset_data, + }, + { + .compatible = "socionext,uniphier-ld20-mio-reset", + .data = uniphier_pro5_mio_reset_data, + }, + /* Peripheral reset */ + { + .compatible = "socionext,uniphier-ld4-peri-reset", + .data = uniphier_ld4_peri_reset_data, + }, + { + .compatible = "socionext,uniphier-pro4-peri-reset", + .data = uniphier_pro4_peri_reset_data, + }, + { + .compatible = "socionext,uniphier-sld8-peri-reset", + .data = uniphier_ld4_peri_reset_data, + }, + { + .compatible = "socionext,uniphier-pro5-peri-reset", + .data = uniphier_pro4_peri_reset_data, + }, + { + .compatible = "socionext,uniphier-pxs2-peri-reset", + .data = uniphier_pro4_peri_reset_data, + }, + { + .compatible = "socionext,uniphier-ld11-peri-reset", + .data = uniphier_pro4_peri_reset_data, + }, + { + .compatible = "socionext,uniphier-ld20-peri-reset", + .data = uniphier_pro4_peri_reset_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, uniphier_reset_match); + +static struct platform_driver uniphier_reset_driver = { + .probe = uniphier_reset_probe, + .driver = { + .name = "uniphier-reset", + .of_match_table = uniphier_reset_match, + }, +}; +module_platform_driver(uniphier_reset_driver); + +MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>"); +MODULE_DESCRIPTION("UniPhier Reset Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/dt-bindings/mfd/stm32f4-rcc.h b/include/dt-bindings/mfd/stm32f4-rcc.h new file mode 100644 index 000000000000..e98942dc0d44 --- /dev/null +++ b/include/dt-bindings/mfd/stm32f4-rcc.h @@ -0,0 +1,98 @@ +/* + * This header provides constants for the STM32F4 RCC IP + */ + +#ifndef _DT_BINDINGS_MFD_STM32F4_RCC_H +#define _DT_BINDINGS_MFD_STM32F4_RCC_H + +/* AHB1 */ +#define STM32F4_RCC_AHB1_GPIOA 0 +#define STM32F4_RCC_AHB1_GPIOB 1 +#define STM32F4_RCC_AHB1_GPIOC 2 +#define STM32F4_RCC_AHB1_GPIOD 3 +#define STM32F4_RCC_AHB1_GPIOE 4 +#define STM32F4_RCC_AHB1_GPIOF 5 +#define STM32F4_RCC_AHB1_GPIOG 6 +#define STM32F4_RCC_AHB1_GPIOH 7 +#define STM32F4_RCC_AHB1_GPIOI 8 +#define STM32F4_RCC_AHB1_GPIOJ 9 +#define STM32F4_RCC_AHB1_GPIOK 10 +#define STM32F4_RCC_AHB1_CRC 12 +#define STM32F4_RCC_AHB1_DMA1 21 +#define STM32F4_RCC_AHB1_DMA2 22 +#define STM32F4_RCC_AHB1_DMA2D 23 +#define STM32F4_RCC_AHB1_ETHMAC 25 +#define STM32F4_RCC_AHB1_OTGHS 29 + +#define STM32F4_AHB1_RESET(bit) (STM32F4_RCC_AHB1_##bit + (0x10 * 8)) +#define STM32F4_AHB1_CLOCK(bit) (STM32F4_RCC_AHB1_##bit + (0x30 * 8)) + + +/* AHB2 */ +#define STM32F4_RCC_AHB2_DCMI 0 +#define STM32F4_RCC_AHB2_CRYP 4 +#define STM32F4_RCC_AHB2_HASH 5 +#define STM32F4_RCC_AHB2_RNG 6 +#define STM32F4_RCC_AHB2_OTGFS 7 + +#define STM32F4_AHB2_RESET(bit) (STM32F4_RCC_AHB2_##bit + (0x14 * 8)) +#define STM32F4_AHB2_CLOCK(bit) (STM32F4_RCC_AHB2_##bit + (0x34 * 8)) + +/* AHB3 */ +#define STM32F4_RCC_AHB3_FMC 0 + +#define STM32F4_AHB3_RESET(bit) (STM32F4_RCC_AHB3_##bit + (0x18 * 8)) +#define STM32F4_AHB3_CLOCK(bit) (STM32F4_RCC_AHB3_##bit + (0x38 * 8)) + +/* APB1 */ +#define STM32F4_RCC_APB1_TIM2 0 +#define STM32F4_RCC_APB1_TIM3 1 +#define STM32F4_RCC_APB1_TIM4 2 +#define STM32F4_RCC_APB1_TIM5 3 +#define STM32F4_RCC_APB1_TIM6 4 +#define STM32F4_RCC_APB1_TIM7 5 +#define STM32F4_RCC_APB1_TIM12 6 +#define STM32F4_RCC_APB1_TIM13 7 +#define STM32F4_RCC_APB1_TIM14 8 +#define STM32F4_RCC_APB1_WWDG 11 +#define STM32F4_RCC_APB1_SPI2 14 +#define STM32F4_RCC_APB1_SPI3 15 +#define STM32F4_RCC_APB1_UART2 17 +#define STM32F4_RCC_APB1_UART3 18 +#define STM32F4_RCC_APB1_UART4 19 +#define STM32F4_RCC_APB1_UART5 20 +#define STM32F4_RCC_APB1_I2C1 21 +#define STM32F4_RCC_APB1_I2C2 22 +#define STM32F4_RCC_APB1_I2C3 23 +#define STM32F4_RCC_APB1_CAN1 25 +#define STM32F4_RCC_APB1_CAN2 26 +#define STM32F4_RCC_APB1_PWR 28 +#define STM32F4_RCC_APB1_DAC 29 +#define STM32F4_RCC_APB1_UART7 30 +#define STM32F4_RCC_APB1_UART8 31 + +#define STM32F4_APB1_RESET(bit) (STM32F4_RCC_APB1_##bit + (0x20 * 8)) +#define STM32F4_APB1_CLOCK(bit) (STM32F4_RCC_APB1_##bit + (0x40 * 8)) + +/* APB2 */ +#define STM32F4_RCC_APB2_TIM1 0 +#define STM32F4_RCC_APB2_TIM8 1 +#define STM32F4_RCC_APB2_USART1 4 +#define STM32F4_RCC_APB2_USART6 5 +#define STM32F4_RCC_APB2_ADC 8 +#define STM32F4_RCC_APB2_SDIO 11 +#define STM32F4_RCC_APB2_SPI1 12 +#define STM32F4_RCC_APB2_SPI4 13 +#define STM32F4_RCC_APB2_SYSCFG 14 +#define STM32F4_RCC_APB2_TIM9 16 +#define STM32F4_RCC_APB2_TIM10 17 +#define STM32F4_RCC_APB2_TIM11 18 +#define STM32F4_RCC_APB2_SPI5 20 +#define STM32F4_RCC_APB2_SPI6 21 +#define STM32F4_RCC_APB2_SAI1 22 +#define STM32F4_RCC_APB2_LTDC 26 + +#define STM32F4_APB2_RESET(bit) (STM32F4_RCC_APB2_##bit + (0x24 * 8)) +#define STM32F4_APB2_CLOCK(bit) (STM32F4_RCC_APB2_##bit + (0x44 * 8)) + +#endif /* _DT_BINDINGS_MFD_STM32F4_RCC_H */ |