From b2f6dd7b94ee9c07b93fb8e69d782af6fde0410f Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 13 Feb 2015 12:14:40 +0100 Subject: [PATCH 01/13] MAINTAINERS: add include/dt-bindings/reset path to reset controller entry This is the path for reset definitions to be used in both device tree and reset controller drivers. Signed-off-by: Philipp Zabel --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index fd6078443083..e44c23fed3cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8553,6 +8553,7 @@ M: Philipp Zabel S: Maintained F: drivers/reset/ F: Documentation/devicetree/bindings/reset/ +F: include/dt-bindings/reset/ F: include/linux/reset.h F: include/linux/reset-controller.h From efdf5aa8f175c18def6407bb2b51643e4da88259 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 13 Feb 2015 12:20:49 +0100 Subject: [PATCH 02/13] ARM: STi: DT: Move reset controller constants into common location By popular vote, the DT binding includes for reset controllers are located in include/dt-bindings/reset/. Move the STi reset constants in there, too, to avoid confusion. Signed-off-by: Philipp Zabel Acked-by: Patrice Chotard --- .../devicetree/bindings/reset/st,sti-picophyreset.txt | 2 +- Documentation/devicetree/bindings/reset/st,sti-powerdown.txt | 4 ++-- Documentation/devicetree/bindings/reset/st,sti-softreset.txt | 4 ++-- arch/arm/boot/dts/stih407-family.dtsi | 2 +- arch/arm/boot/dts/stih415.dtsi | 2 +- arch/arm/boot/dts/stih416.dtsi | 2 +- drivers/reset/sti/reset-stih407.c | 2 +- drivers/reset/sti/reset-stih415.c | 2 +- drivers/reset/sti/reset-stih416.c | 2 +- .../dt-bindings/{reset-controller => reset}/stih407-resets.h | 0 .../dt-bindings/{reset-controller => reset}/stih415-resets.h | 0 .../dt-bindings/{reset-controller => reset}/stih416-resets.h | 0 12 files changed, 11 insertions(+), 11 deletions(-) rename include/dt-bindings/{reset-controller => reset}/stih407-resets.h (100%) rename include/dt-bindings/{reset-controller => reset}/stih415-resets.h (100%) rename include/dt-bindings/{reset-controller => reset}/stih416-resets.h (100%) diff --git a/Documentation/devicetree/bindings/reset/st,sti-picophyreset.txt b/Documentation/devicetree/bindings/reset/st,sti-picophyreset.txt index 54ae9f747e45..9ca27761f811 100644 --- a/Documentation/devicetree/bindings/reset/st,sti-picophyreset.txt +++ b/Documentation/devicetree/bindings/reset/st,sti-picophyreset.txt @@ -39,4 +39,4 @@ Example: }; Macro definitions for the supported reset channels can be found in: -include/dt-bindings/reset-controller/stih407-resets.h +include/dt-bindings/reset/stih407-resets.h diff --git a/Documentation/devicetree/bindings/reset/st,sti-powerdown.txt b/Documentation/devicetree/bindings/reset/st,sti-powerdown.txt index 5ab26b7e9d35..1cfd21d1dfa1 100644 --- a/Documentation/devicetree/bindings/reset/st,sti-powerdown.txt +++ b/Documentation/devicetree/bindings/reset/st,sti-powerdown.txt @@ -43,5 +43,5 @@ example: Macro definitions for the supported reset channels can be found in: -include/dt-bindings/reset-controller/stih415-resets.h -include/dt-bindings/reset-controller/stih416-resets.h +include/dt-bindings/reset/stih415-resets.h +include/dt-bindings/reset/stih416-resets.h diff --git a/Documentation/devicetree/bindings/reset/st,sti-softreset.txt b/Documentation/devicetree/bindings/reset/st,sti-softreset.txt index a8d3d3c25ca2..891a2fd85ed6 100644 --- a/Documentation/devicetree/bindings/reset/st,sti-softreset.txt +++ b/Documentation/devicetree/bindings/reset/st,sti-softreset.txt @@ -42,5 +42,5 @@ example: Macro definitions for the supported reset channels can be found in: -include/dt-bindings/reset-controller/stih415-resets.h -include/dt-bindings/reset-controller/stih416-resets.h +include/dt-bindings/reset/stih415-resets.h +include/dt-bindings/reset/stih416-resets.h diff --git a/arch/arm/boot/dts/stih407-family.dtsi b/arch/arm/boot/dts/stih407-family.dtsi index 838b812cbda1..eab3477e0a0e 100644 --- a/arch/arm/boot/dts/stih407-family.dtsi +++ b/arch/arm/boot/dts/stih407-family.dtsi @@ -9,7 +9,7 @@ #include "stih407-pinctrl.dtsi" #include #include -#include +#include #include / { #address-cells = <1>; diff --git a/arch/arm/boot/dts/stih415.dtsi b/arch/arm/boot/dts/stih415.dtsi index 19b019b5f30e..12427e651e5e 100644 --- a/arch/arm/boot/dts/stih415.dtsi +++ b/arch/arm/boot/dts/stih415.dtsi @@ -10,7 +10,7 @@ #include "stih415-clock.dtsi" #include "stih415-pinctrl.dtsi" #include -#include +#include / { L2: cache-controller { diff --git a/arch/arm/boot/dts/stih416.dtsi b/arch/arm/boot/dts/stih416.dtsi index 9dca173e694a..9e3170ccd18c 100644 --- a/arch/arm/boot/dts/stih416.dtsi +++ b/arch/arm/boot/dts/stih416.dtsi @@ -12,7 +12,7 @@ #include #include -#include +#include #include / { L2: cache-controller { diff --git a/drivers/reset/sti/reset-stih407.c b/drivers/reset/sti/reset-stih407.c index d83db5d72d08..f7a6cb093983 100644 --- a/drivers/reset/sti/reset-stih407.c +++ b/drivers/reset/sti/reset-stih407.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include "reset-syscfg.h" /* STiH407 Peripheral powerdown definitions. */ diff --git a/drivers/reset/sti/reset-stih415.c b/drivers/reset/sti/reset-stih415.c index 8dad603d863c..f7b49d5a62dc 100644 --- a/drivers/reset/sti/reset-stih415.c +++ b/drivers/reset/sti/reset-stih415.c @@ -13,7 +13,7 @@ #include #include -#include +#include #include "reset-syscfg.h" diff --git a/drivers/reset/sti/reset-stih416.c b/drivers/reset/sti/reset-stih416.c index 79aed70a26c0..d65a82e74004 100644 --- a/drivers/reset/sti/reset-stih416.c +++ b/drivers/reset/sti/reset-stih416.c @@ -13,7 +13,7 @@ #include #include -#include +#include #include "reset-syscfg.h" diff --git a/include/dt-bindings/reset-controller/stih407-resets.h b/include/dt-bindings/reset/stih407-resets.h similarity index 100% rename from include/dt-bindings/reset-controller/stih407-resets.h rename to include/dt-bindings/reset/stih407-resets.h diff --git a/include/dt-bindings/reset-controller/stih415-resets.h b/include/dt-bindings/reset/stih415-resets.h similarity index 100% rename from include/dt-bindings/reset-controller/stih415-resets.h rename to include/dt-bindings/reset/stih415-resets.h diff --git a/include/dt-bindings/reset-controller/stih416-resets.h b/include/dt-bindings/reset/stih416-resets.h similarity index 100% rename from include/dt-bindings/reset-controller/stih416-resets.h rename to include/dt-bindings/reset/stih416-resets.h From a518db45c2580fd726f6578539482596f46a8219 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 16 Mar 2015 20:54:37 +0100 Subject: [PATCH 03/13] reset: sti: constify of_device_id array of_device_id is always used as const. (See driver.of_match_table and open firmware functions) Signed-off-by: Fabian Frederick Acked-by: Maxime Coquelin Acked-by: Patrice Chotard Signed-off-by: Philipp Zabel --- drivers/reset/sti/reset-stih407.c | 2 +- drivers/reset/sti/reset-stih415.c | 2 +- drivers/reset/sti/reset-stih416.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/reset/sti/reset-stih407.c b/drivers/reset/sti/reset-stih407.c index f7a6cb093983..827eb3dae47d 100644 --- a/drivers/reset/sti/reset-stih407.c +++ b/drivers/reset/sti/reset-stih407.c @@ -126,7 +126,7 @@ static const struct syscfg_reset_controller_data stih407_picophyreset_controller .channels = stih407_picophyresets, }; -static struct of_device_id stih407_reset_match[] = { +static const struct of_device_id stih407_reset_match[] = { { .compatible = "st,stih407-powerdown", .data = &stih407_powerdown_controller, diff --git a/drivers/reset/sti/reset-stih415.c b/drivers/reset/sti/reset-stih415.c index f7b49d5a62dc..6f220cdbef46 100644 --- a/drivers/reset/sti/reset-stih415.c +++ b/drivers/reset/sti/reset-stih415.c @@ -89,7 +89,7 @@ static struct syscfg_reset_controller_data stih415_softreset_controller = { .channels = stih415_softresets, }; -static struct of_device_id stih415_reset_match[] = { +static const struct of_device_id stih415_reset_match[] = { { .compatible = "st,stih415-powerdown", .data = &stih415_powerdown_controller, }, { .compatible = "st,stih415-softreset", diff --git a/drivers/reset/sti/reset-stih416.c b/drivers/reset/sti/reset-stih416.c index d65a82e74004..c581d606ef0f 100644 --- a/drivers/reset/sti/reset-stih416.c +++ b/drivers/reset/sti/reset-stih416.c @@ -120,7 +120,7 @@ static struct syscfg_reset_controller_data stih416_softreset_controller = { .channels = stih416_softresets, }; -static struct of_device_id stih416_reset_match[] = { +static const struct of_device_id stih416_reset_match[] = { { .compatible = "st,stih416-powerdown", .data = &stih416_powerdown_controller, }, { .compatible = "st,stih416-softreset", From c392b65ba853f653cff3d1c7de2138bd6906d536 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Wed, 6 May 2015 00:10:26 +0200 Subject: [PATCH 04/13] reset: add driver for lpc18xx rgu Add reset driver for the Reset Generation Unit (RGU) found on NXP LPC18xx and LPC43xx devies. This reset controller features up to 64 reset lines connected to different blocks and peripheral in the SoC. Most reset lines on the controller are self clearing except for those dealing with the Cortex-M0 cores on LPC43xx devices. This driver also registers a restart handler that can be used to reset the entire device. Signed-off-by: Joachim Eastwood Signed-off-by: Philipp Zabel --- drivers/reset/Makefile | 1 + drivers/reset/reset-lpc18xx.c | 258 ++++++++++++++++++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 drivers/reset/reset-lpc18xx.c diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 157d421f755b..1d41feeb2dce 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_RESET_CONTROLLER) += 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_ARCH_SUNXI) += reset-sunxi.o diff --git a/drivers/reset/reset-lpc18xx.c b/drivers/reset/reset-lpc18xx.c new file mode 100644 index 000000000000..70922e9ac27f --- /dev/null +++ b/drivers/reset/reset-lpc18xx.c @@ -0,0 +1,258 @@ +/* + * Reset driver for NXP LPC18xx/43xx Reset Generation Unit (RGU). + * + * Copyright (C) 2015 Joachim Eastwood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* LPC18xx RGU registers */ +#define LPC18XX_RGU_CTRL0 0x100 +#define LPC18XX_RGU_CTRL1 0x104 +#define LPC18XX_RGU_ACTIVE_STATUS0 0x150 +#define LPC18XX_RGU_ACTIVE_STATUS1 0x154 + +#define LPC18XX_RGU_RESETS_PER_REG 32 + +/* Internal reset outputs */ +#define LPC18XX_RGU_CORE_RST 0 +#define LPC43XX_RGU_M0SUB_RST 12 +#define LPC43XX_RGU_M0APP_RST 56 + +struct lpc18xx_rgu_data { + struct reset_controller_dev rcdev; + struct clk *clk_delay; + struct clk *clk_reg; + void __iomem *base; + spinlock_t lock; + u32 delay_us; +}; + +#define to_rgu_data(p) container_of(p, struct lpc18xx_rgu_data, rcdev) + +static void __iomem *rgu_base; + +static int lpc18xx_rgu_restart(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + writel(BIT(LPC18XX_RGU_CORE_RST), rgu_base + LPC18XX_RGU_CTRL0); + mdelay(2000); + + pr_emerg("%s: unable to restart system\n", __func__); + + return NOTIFY_DONE; +} + +static struct notifier_block lpc18xx_rgu_restart_nb = { + .notifier_call = lpc18xx_rgu_restart, + .priority = 192, +}; + +/* + * The LPC18xx RGU has mostly self-deasserting resets except for the + * two reset lines going to the internal Cortex-M0 cores. + * + * To prevent the M0 core resets from accidentally getting deasserted + * status register must be check and bits in control register set to + * preserve the state. + */ +static int lpc18xx_rgu_setclear_reset(struct reset_controller_dev *rcdev, + unsigned long id, bool set) +{ + struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev); + u32 stat_offset = LPC18XX_RGU_ACTIVE_STATUS0; + u32 ctrl_offset = LPC18XX_RGU_CTRL0; + unsigned long flags; + u32 stat, rst_bit; + + stat_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32); + ctrl_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32); + rst_bit = 1 << (id % LPC18XX_RGU_RESETS_PER_REG); + + spin_lock_irqsave(&rc->lock, flags); + stat = ~readl(rc->base + stat_offset); + if (set) + writel(stat | rst_bit, rc->base + ctrl_offset); + else + writel(stat & ~rst_bit, rc->base + ctrl_offset); + spin_unlock_irqrestore(&rc->lock, flags); + + return 0; +} + +static int lpc18xx_rgu_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return lpc18xx_rgu_setclear_reset(rcdev, id, true); +} + +static int lpc18xx_rgu_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return lpc18xx_rgu_setclear_reset(rcdev, id, false); +} + +/* Only M0 cores require explicit reset deassert */ +static int lpc18xx_rgu_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev); + + lpc18xx_rgu_assert(rcdev, id); + udelay(rc->delay_us); + + switch (id) { + case LPC43XX_RGU_M0SUB_RST: + case LPC43XX_RGU_M0APP_RST: + lpc18xx_rgu_setclear_reset(rcdev, id, false); + } + + return 0; +} + +static int lpc18xx_rgu_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev); + u32 bit, offset = LPC18XX_RGU_ACTIVE_STATUS0; + + offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32); + bit = 1 << (id % LPC18XX_RGU_RESETS_PER_REG); + + return !(readl(rc->base + offset) & bit); +} + +static struct reset_control_ops lpc18xx_rgu_ops = { + .reset = lpc18xx_rgu_reset, + .assert = lpc18xx_rgu_assert, + .deassert = lpc18xx_rgu_deassert, + .status = lpc18xx_rgu_status, +}; + +static int lpc18xx_rgu_probe(struct platform_device *pdev) +{ + struct lpc18xx_rgu_data *rc; + struct resource *res; + u32 fcclk, firc; + int ret; + + rc = devm_kzalloc(&pdev->dev, sizeof(*rc), GFP_KERNEL); + if (!rc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rc->base)) + return PTR_ERR(rc->base); + + rc->clk_reg = devm_clk_get(&pdev->dev, "reg"); + if (IS_ERR(rc->clk_reg)) { + dev_err(&pdev->dev, "reg clock not found\n"); + return PTR_ERR(rc->clk_reg); + } + + rc->clk_delay = devm_clk_get(&pdev->dev, "delay"); + if (IS_ERR(rc->clk_delay)) { + dev_err(&pdev->dev, "delay clock not found\n"); + return PTR_ERR(rc->clk_delay); + } + + ret = clk_prepare_enable(rc->clk_reg); + if (ret) { + dev_err(&pdev->dev, "unable to enable reg clock\n"); + return ret; + } + + ret = clk_prepare_enable(rc->clk_delay); + if (ret) { + dev_err(&pdev->dev, "unable to enable delay clock\n"); + goto dis_clk_reg; + } + + fcclk = clk_get_rate(rc->clk_reg) / USEC_PER_SEC; + firc = clk_get_rate(rc->clk_delay) / USEC_PER_SEC; + if (fcclk == 0 || firc == 0) + rc->delay_us = 2; + else + rc->delay_us = DIV_ROUND_UP(fcclk, firc * firc); + + spin_lock_init(&rc->lock); + + rc->rcdev.owner = THIS_MODULE; + rc->rcdev.nr_resets = 64; + rc->rcdev.ops = &lpc18xx_rgu_ops; + rc->rcdev.of_node = pdev->dev.of_node; + + platform_set_drvdata(pdev, rc); + + ret = reset_controller_register(&rc->rcdev); + if (ret) { + dev_err(&pdev->dev, "unable to register device\n"); + goto dis_clks; + } + + rgu_base = rc->base; + ret = register_restart_handler(&lpc18xx_rgu_restart_nb); + if (ret) + dev_warn(&pdev->dev, "failed to register restart handler\n"); + + return 0; + +dis_clks: + clk_disable_unprepare(rc->clk_delay); +dis_clk_reg: + clk_disable_unprepare(rc->clk_reg); + + return ret; +} + +static int lpc18xx_rgu_remove(struct platform_device *pdev) +{ + struct lpc18xx_rgu_data *rc = platform_get_drvdata(pdev); + int ret; + + ret = unregister_restart_handler(&lpc18xx_rgu_restart_nb); + if (ret) + dev_warn(&pdev->dev, "failed to unregister restart handler\n"); + + reset_controller_unregister(&rc->rcdev); + + clk_disable_unprepare(rc->clk_delay); + clk_disable_unprepare(rc->clk_reg); + + return 0; +} + +static const struct of_device_id lpc18xx_rgu_match[] = { + { .compatible = "nxp,lpc1850-rgu" }, + { } +}; +MODULE_DEVICE_TABLE(of, lpc18xx_rgu_match); + +static struct platform_driver lpc18xx_rgu_driver = { + .probe = lpc18xx_rgu_probe, + .remove = lpc18xx_rgu_remove, + .driver = { + .name = "lpc18xx-reset", + .of_match_table = lpc18xx_rgu_match, + }, +}; +module_platform_driver(lpc18xx_rgu_driver); + +MODULE_AUTHOR("Joachim Eastwood "); +MODULE_DESCRIPTION("Reset driver for LPC18xx/43xx RGU"); +MODULE_LICENSE("GPL v2"); From 27dc2fb18fe814fab0a5035859c77b279cdaec80 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Wed, 6 May 2015 00:10:27 +0200 Subject: [PATCH 05/13] doc: dt: add documentation for lpc1850-rgu reset driver Add device tree binding documentation for the Reset Generation Unit (RGU) found on NXP LPC18xx and LPC43xx devies. This documentation also includes a table which shows the RGU reset number and the connected peripheral. Signed-off-by: Joachim Eastwood Signed-off-by: Philipp Zabel --- .../bindings/reset/nxp,lpc1850-rgu.txt | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.txt diff --git a/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.txt b/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.txt new file mode 100644 index 000000000000..b4e96a278445 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/nxp,lpc1850-rgu.txt @@ -0,0 +1,84 @@ +NXP LPC1850 Reset Generation Unit (RGU) +======================================== + +Please also refer to reset.txt in this directory for common reset +controller binding usage. + +Required properties: +- compatible: Should be "nxp,lpc1850-rgu" +- reg: register base and length +- clocks: phandle and clock specifier to RGU clocks +- clock-names: should contain "delay" and "reg" +- #reset-cells: should be 1 + +See table below for valid peripheral reset numbers. Numbers not +in the table below are either reserved or not applicable for +normal operation. + +Reset Peripheral + 9 System control unit (SCU) + 12 ARM Cortex-M0 subsystem core (LPC43xx only) + 13 CPU core + 16 LCD controller + 17 USB0 + 18 USB1 + 19 DMA + 20 SDIO + 21 External memory controller (EMC) + 22 Ethernet + 25 Flash bank A + 27 EEPROM + 28 GPIO + 29 Flash bank B + 32 Timer0 + 33 Timer1 + 34 Timer2 + 35 Timer3 + 36 Repetitive Interrupt timer (RIT) + 37 State Configurable Timer (SCT) + 38 Motor control PWM (MCPWM) + 39 QEI + 40 ADC0 + 41 ADC1 + 42 DAC + 44 USART0 + 45 UART1 + 46 USART2 + 47 USART3 + 48 I2C0 + 49 I2C1 + 50 SSP0 + 51 SSP1 + 52 I2S0 and I2S1 + 53 Serial Flash Interface (SPIFI) + 54 C_CAN1 + 55 C_CAN0 + 56 ARM Cortex-M0 application core (LPC4370 only) + 57 SGPIO (LPC43xx only) + 58 SPI (LPC43xx only) + 60 ADCHS (12-bit ADC) (LPC4370 only) + +Refer to NXP LPC18xx or LPC43xx user manual for more details about +the reset signals and the connected block/peripheral. + +Reset provider example: +rgu: reset-controller@40053000 { + compatible = "nxp,lpc1850-rgu"; + reg = <0x40053000 0x1000>; + clocks = <&cgu BASE_SAFE_CLK>, <&ccu1 CLK_CPU_BUS>; + clock-names = "delay", "reg"; + #reset-cells = <1>; +}; + +Reset consumer example: +mac: ethernet@40010000 { + compatible = "nxp,lpc1850-dwmac", "snps,dwmac-3.611", "snps,dwmac"; + reg = <0x40010000 0x2000>; + interrupts = <5>; + interrupt-names = "macirq"; + clocks = <&ccu1 CLK_CPU_ETHERNET>; + clock-names = "stmmaceth"; + resets = <&rgu 22>; + reset-names = "stmmaceth"; + status = "disabled"; +}; From 27e44646dc0083c931b71bbb8e179aeb38010d31 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Fri, 31 Jul 2015 16:03:10 -0500 Subject: [PATCH 06/13] reset: socfpga: Update reset-socfpga to read the altr,modrst-offset property In order for the Arria10 to be able to re-use the reset driver for SoCFPGA Cyclone5/Arria5, we need to read the 'altr,modrst-offset' property from the device tree entry. The 'altr,modrst-offset' property is the first register into the reset manager that is used for bringing peripherals out of reset. The driver assumes a modrst-offset of 0x10 in order to support legacy Cyclone5/Arria5 hardware. Signed-off-by: Dinh Nguyen --- drivers/reset/reset-socfpga.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index 0a8def35ea2e..1a6c5d66c83b 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c @@ -24,11 +24,11 @@ #include #define NR_BANKS 4 -#define OFFSET_MODRST 0x10 struct socfpga_reset_data { spinlock_t lock; void __iomem *membase; + u32 modrst_offset; struct reset_controller_dev rcdev; }; @@ -45,8 +45,8 @@ static int socfpga_reset_assert(struct reset_controller_dev *rcdev, spin_lock_irqsave(&data->lock, flags); - reg = readl(data->membase + OFFSET_MODRST + (bank * NR_BANKS)); - writel(reg | BIT(offset), data->membase + OFFSET_MODRST + + reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS)); + writel(reg | BIT(offset), data->membase + data->modrst_offset + (bank * NR_BANKS)); spin_unlock_irqrestore(&data->lock, flags); @@ -67,8 +67,8 @@ static int socfpga_reset_deassert(struct reset_controller_dev *rcdev, spin_lock_irqsave(&data->lock, flags); - reg = readl(data->membase + OFFSET_MODRST + (bank * NR_BANKS)); - writel(reg & ~BIT(offset), data->membase + OFFSET_MODRST + + reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS)); + writel(reg & ~BIT(offset), data->membase + data->modrst_offset + (bank * NR_BANKS)); spin_unlock_irqrestore(&data->lock, flags); @@ -85,7 +85,7 @@ static int socfpga_reset_status(struct reset_controller_dev *rcdev, int offset = id % BITS_PER_LONG; u32 reg; - reg = readl(data->membase + OFFSET_MODRST + (bank * NR_BANKS)); + reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS)); return !(reg & BIT(offset)); } @@ -100,6 +100,8 @@ static int socfpga_reset_probe(struct platform_device *pdev) { struct socfpga_reset_data *data; struct resource *res; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; /* * The binding was mainlined without the required property. @@ -120,6 +122,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)) { + dev_warn(dev, "missing altr,modrst-offset property, assuming 0x10!\n"); + data->modrst_offset = 0x10; + } + spin_lock_init(&data->lock); data->rcdev.owner = THIS_MODULE; From f25b5edac7be7d405965fb68c6b6f65d8852d860 Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Mon, 3 Aug 2015 19:23:51 +0200 Subject: [PATCH 07/13] devicetree: Add bindings for the ATH79 reset controller Signed-off-by: Alban Bedel Acked-by: Ralf Baechle Signed-off-by: Philipp Zabel --- .../devicetree/bindings/reset/ath79-reset.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/reset/ath79-reset.txt diff --git a/Documentation/devicetree/bindings/reset/ath79-reset.txt b/Documentation/devicetree/bindings/reset/ath79-reset.txt new file mode 100644 index 000000000000..4c56330bf398 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/ath79-reset.txt @@ -0,0 +1,20 @@ +Binding for Qualcomm Atheros AR7xxx/AR9XXX reset controller + +Please also refer to reset.txt in this directory for common reset +controller binding usage. + +Required Properties: +- compatible: has to be "qca,-reset", "qca,ar7100-reset" + as fallback +- reg: Base address and size of the controllers memory area +- #reset-cells : Specifies the number of cells needed to encode reset + line, should be 1 + +Example: + + reset-controller@1806001c { + compatible = "qca,ar9132-reset", "qca,ar7100-reset"; + reg = <0x1806001c 0x4>; + + #reset-cells = <1>; + }; From ff591a91225d3621a503bb18faa0f0d747a06e50 Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Mon, 3 Aug 2015 19:23:52 +0200 Subject: [PATCH 08/13] reset: Add a driver for the reset controller on the AR71XX/AR9XXX The AR71XX/AR9XXX SoC have a simple reset controller with one bit per reset line. Signed-off-by: Alban Bedel Acked-by: Ralf Baechle Signed-off-by: Philipp Zabel --- arch/mips/Kconfig | 1 + drivers/reset/Makefile | 1 + drivers/reset/reset-ath79.c | 128 ++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 drivers/reset/reset-ath79.c diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index aab7e46cadd5..3a3548f267b4 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -118,6 +118,7 @@ config ATH25 config ATH79 bool "Atheros AR71XX/AR724X/AR913X based boards" + select ARCH_HAS_RESET_CONTROLLER select ARCH_REQUIRE_GPIOLIB select BOOT_RAW select CEVT_R4K diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 157d421f755b..f8db9b744b3a 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o obj-$(CONFIG_ARCH_STI) += sti/ +obj-$(CONFIG_ATH79) += reset-ath79.o diff --git a/drivers/reset/reset-ath79.c b/drivers/reset/reset-ath79.c new file mode 100644 index 000000000000..d2d290413113 --- /dev/null +++ b/drivers/reset/reset-ath79.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2015 Alban Bedel + * + * 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 +#include +#include + +struct ath79_reset { + struct reset_controller_dev rcdev; + void __iomem *base; + spinlock_t lock; +}; + +static int ath79_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct ath79_reset *ath79_reset = + container_of(rcdev, struct ath79_reset, rcdev); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&ath79_reset->lock, flags); + val = readl(ath79_reset->base); + if (assert) + val |= BIT(id); + else + val &= ~BIT(id); + writel(val, ath79_reset->base); + spin_unlock_irqrestore(&ath79_reset->lock, flags); + + return 0; +} + +static int ath79_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return ath79_reset_update(rcdev, id, true); +} + +static int ath79_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return ath79_reset_update(rcdev, id, false); +} + +static int ath79_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct ath79_reset *ath79_reset = + container_of(rcdev, struct ath79_reset, rcdev); + u32 val; + + val = readl(ath79_reset->base); + + return !!(val & BIT(id)); +} + +static struct reset_control_ops ath79_reset_ops = { + .assert = ath79_reset_assert, + .deassert = ath79_reset_deassert, + .status = ath79_reset_status, +}; + +static int ath79_reset_probe(struct platform_device *pdev) +{ + struct ath79_reset *ath79_reset; + struct resource *res; + + ath79_reset = devm_kzalloc(&pdev->dev, + sizeof(*ath79_reset), GFP_KERNEL); + if (!ath79_reset) + return -ENOMEM; + + platform_set_drvdata(pdev, ath79_reset); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ath79_reset->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ath79_reset->base)) + return PTR_ERR(ath79_reset->base); + + ath79_reset->rcdev.ops = &ath79_reset_ops; + ath79_reset->rcdev.owner = THIS_MODULE; + ath79_reset->rcdev.of_node = pdev->dev.of_node; + ath79_reset->rcdev.of_reset_n_cells = 1; + ath79_reset->rcdev.nr_resets = 32; + + return reset_controller_register(&ath79_reset->rcdev); +} + +static int ath79_reset_remove(struct platform_device *pdev) +{ + struct ath79_reset *ath79_reset = platform_get_drvdata(pdev); + + reset_controller_unregister(&ath79_reset->rcdev); + + return 0; +} + +static const struct of_device_id ath79_reset_dt_ids[] = { + { .compatible = "qca,ar7100-reset", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ath79_reset_dt_ids); + +static struct platform_driver ath79_reset_driver = { + .probe = ath79_reset_probe, + .remove = ath79_reset_remove, + .driver = { + .name = "ath79-reset", + .of_match_table = ath79_reset_dt_ids, + }, +}; +module_platform_driver(ath79_reset_driver); + +MODULE_AUTHOR("Alban Bedel "); +MODULE_DESCRIPTION("AR71xx Reset Controller Driver"); +MODULE_LICENSE("GPL"); From 93a1ceea104169c4faff112bf18a782a72e0483e Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Mon, 3 Aug 2015 19:23:53 +0200 Subject: [PATCH 09/13] MIPS: ath79: Add the reset controller to the AR9132 dtsi Signed-off-by: Alban Bedel Acked-by: Ralf Baechle Signed-off-by: Philipp Zabel --- arch/mips/boot/dts/qca/ar9132.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/mips/boot/dts/qca/ar9132.dtsi b/arch/mips/boot/dts/qca/ar9132.dtsi index 4759cff814d1..fb7734eadbf0 100644 --- a/arch/mips/boot/dts/qca/ar9132.dtsi +++ b/arch/mips/boot/dts/qca/ar9132.dtsi @@ -115,6 +115,14 @@ interrupt-controller; #interrupt-cells = <1>; }; + + rst: reset-controller@1806001c { + compatible = "qca,ar9132-reset", + "qca,ar7100-reset"; + reg = <0x1806001c 0x4>; + + #reset-cells = <1>; + }; }; spi@1f000000 { From 575c215c00140e83b32ca6c01443b8a458e55c73 Mon Sep 17 00:00:00 2001 From: Moritz Fischer Date: Thu, 30 Jul 2015 18:13:54 -0700 Subject: [PATCH 10/13] docs: dts: Added documentation for Xilinx Zynq Reset Controller bindings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Moritz Fischer Reviewed-by: Michal Simek Acked-by: Sören Brinkmann Signed-off-by: Philipp Zabel --- .../devicetree/bindings/reset/zynq-reset.txt | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/reset/zynq-reset.txt diff --git a/Documentation/devicetree/bindings/reset/zynq-reset.txt b/Documentation/devicetree/bindings/reset/zynq-reset.txt new file mode 100644 index 000000000000..5860120e3064 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/zynq-reset.txt @@ -0,0 +1,68 @@ +Xilinx Zynq Reset Manager + +The Zynq AP-SoC has several different resets. + +See Chapter 26 of the Zynq TRM (UG585) for more information about Zynq resets. + +Required properties: +- compatible: "xlnx,zynq-reset" +- reg: SLCR offset and size taken via syscon <0x200 0x48> +- syscon: <&slcr> + This should be a phandle to the Zynq's SLCR registers. +- #reset-cells: Must be 1 + +The Zynq Reset Manager needs to be a childnode of the SLCR. + +Example: + rstc: rstc@200 { + compatible = "xlnx,zynq-reset"; + reg = <0x200 0x48>; + #reset-cells = <1>; + syscon = <&slcr>; + }; + +Reset outputs: + 0 : soft reset + 32 : ddr reset + 64 : topsw reset + 96 : dmac reset + 128: usb0 reset + 129: usb1 reset + 160: gem0 reset + 161: gem1 reset + 164: gem0 rx reset + 165: gem1 rx reset + 166: gem0 ref reset + 167: gem1 ref reset + 192: sdio0 reset + 193: sdio1 reset + 196: sdio0 ref reset + 197: sdio1 ref reset + 224: spi0 reset + 225: spi1 reset + 226: spi0 ref reset + 227: spi1 ref reset + 256: can0 reset + 257: can1 reset + 258: can0 ref reset + 259: can1 ref reset + 288: i2c0 reset + 289: i2c1 reset + 320: uart0 reset + 321: uart1 reset + 322: uart0 ref reset + 323: uart1 ref reset + 352: gpio reset + 384: lqspi reset + 385: qspi ref reset + 416: smc reset + 417: smc ref reset + 448: ocm reset + 512: fpga0 out reset + 513: fpga1 out reset + 514: fpga2 out reset + 515: fpga3 out reset + 544: a9 reset 0 + 545: a9 reset 1 + 552: peri reset + From fedf42b50d51758ce43fe0a652991dc01421f422 Mon Sep 17 00:00:00 2001 From: Moritz Fischer Date: Thu, 30 Jul 2015 18:13:56 -0700 Subject: [PATCH 11/13] reset: reset-zynq: Adding support for Xilinx Zynq reset controller. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a reset controller driver to control the Xilinx Zynq AP-SoC's various resets. Signed-off-by: Moritz Fischer Reviewed-by: Michal Simek Acked-by: Sören Brinkmann Signed-off-by: Philipp Zabel --- drivers/reset/Makefile | 1 + drivers/reset/reset-zynq.c | 155 +++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 drivers/reset/reset-zynq.c diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 1d41feeb2dce..ab06aa01cfc7 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o obj-$(CONFIG_ARCH_STI) += sti/ +obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o diff --git a/drivers/reset/reset-zynq.c b/drivers/reset/reset-zynq.c new file mode 100644 index 000000000000..89318a5d5bd7 --- /dev/null +++ b/drivers/reset/reset-zynq.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2015, National Instruments Corp. + * + * Xilinx Zynq Reset controller driver + * + * 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; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +struct zynq_reset_data { + struct regmap *slcr; + struct reset_controller_dev rcdev; + u32 offset; +}; + +#define to_zynq_reset_data(p) \ + container_of((p), struct zynq_reset_data, rcdev) + +static int zynq_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct zynq_reset_data *priv = to_zynq_reset_data(rcdev); + + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + + pr_debug("%s: %s reset bank %u offset %u\n", KBUILD_MODNAME, __func__, + bank, offset); + + return regmap_update_bits(priv->slcr, + priv->offset + (bank * 4), + BIT(offset), + BIT(offset)); +} + +static int zynq_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct zynq_reset_data *priv = to_zynq_reset_data(rcdev); + + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + + pr_debug("%s: %s reset bank %u offset %u\n", KBUILD_MODNAME, __func__, + bank, offset); + + return regmap_update_bits(priv->slcr, + priv->offset + (bank * 4), + BIT(offset), + ~BIT(offset)); +} + +static int zynq_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct zynq_reset_data *priv = to_zynq_reset_data(rcdev); + + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + int ret; + u32 reg; + + pr_debug("%s: %s reset bank %u offset %u\n", KBUILD_MODNAME, __func__, + bank, offset); + + ret = regmap_read(priv->slcr, priv->offset + (bank * 4), ®); + if (ret) + return ret; + + return !!(reg & BIT(offset)); +} + +static struct reset_control_ops zynq_reset_ops = { + .assert = zynq_reset_assert, + .deassert = zynq_reset_deassert, + .status = zynq_reset_status, +}; + +static int zynq_reset_probe(struct platform_device *pdev) +{ + struct resource *res; + struct zynq_reset_data *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->slcr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "syscon"); + if (IS_ERR(priv->slcr)) { + dev_err(&pdev->dev, "unable to get zynq-slcr regmap"); + return PTR_ERR(priv->slcr); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing IO resource\n"); + return -ENODEV; + } + + priv->offset = res->start; + + priv->rcdev.owner = THIS_MODULE; + priv->rcdev.nr_resets = resource_size(res) / 4 * BITS_PER_LONG; + priv->rcdev.ops = &zynq_reset_ops; + priv->rcdev.of_node = pdev->dev.of_node; + reset_controller_register(&priv->rcdev); + + return 0; +} + +static int zynq_reset_remove(struct platform_device *pdev) +{ + struct zynq_reset_data *priv = platform_get_drvdata(pdev); + + reset_controller_unregister(&priv->rcdev); + + return 0; +} + +static const struct of_device_id zynq_reset_dt_ids[] = { + { .compatible = "xlnx,zynq-reset", }, + { /* sentinel */ }, +}; + +static struct platform_driver zynq_reset_driver = { + .probe = zynq_reset_probe, + .remove = zynq_reset_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = zynq_reset_dt_ids, + }, +}; +module_platform_driver(zynq_reset_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Moritz Fischer "); +MODULE_DESCRIPTION("Zynq Reset Controller Driver"); From 5bcd0b7f3c56c616abffd89e11c841834dd1528c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Sep 2015 07:56:38 +0800 Subject: [PATCH 12/13] reset: Add (devm_)reset_control_get stub functions So the drivers can be compiled with CONFIG_RESET_CONTROLLER disabled. Signed-off-by: Axel Lin Signed-off-by: Philipp Zabel --- include/linux/reset.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/linux/reset.h b/include/linux/reset.h index da5602bd77d7..7f65f9cff951 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -74,6 +74,20 @@ static inline int device_reset_optional(struct device *dev) return -ENOSYS; } +static inline struct reset_control *__must_check reset_control_get( + struct device *dev, const char *id) +{ + WARN_ON(1); + return ERR_PTR(-EINVAL); +} + +static inline struct reset_control *__must_check devm_reset_control_get( + struct device *dev, const char *id) +{ + WARN_ON(1); + return ERR_PTR(-EINVAL); +} + static inline struct reset_control *reset_control_get_optional( struct device *dev, const char *id) { From f319cb8491e1b4cc44e7baeeb3ae7817dedf57fc Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Sep 2015 20:15:10 +0800 Subject: [PATCH 13/13] reset: ath79: Fix missing spin_lock_init Signed-off-by: Axel Lin Acked-by: Alban Bedel Signed-off-by: Philipp Zabel --- drivers/reset/reset-ath79.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/reset/reset-ath79.c b/drivers/reset/reset-ath79.c index d2d290413113..9aaf646ece55 100644 --- a/drivers/reset/reset-ath79.c +++ b/drivers/reset/reset-ath79.c @@ -89,6 +89,7 @@ static int ath79_reset_probe(struct platform_device *pdev) if (IS_ERR(ath79_reset->base)) return PTR_ERR(ath79_reset->base); + spin_lock_init(&ath79_reset->lock); ath79_reset->rcdev.ops = &ath79_reset_ops; ath79_reset->rcdev.owner = THIS_MODULE; ath79_reset->rcdev.of_node = pdev->dev.of_node;