diff options
24 files changed, 768 insertions, 20 deletions
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,<soctype>-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>; + }; 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"; +}; 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/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 + diff --git a/MAINTAINERS b/MAINTAINERS index fd6078443083..e44c23fed3cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8553,6 +8553,7 @@ M: Philipp Zabel <p.zabel@pengutronix.de> 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 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 <dt-bindings/mfd/st-lpc.h> #include <dt-bindings/phy/phy.h> -#include <dt-bindings/reset-controller/stih407-resets.h> +#include <dt-bindings/reset/stih407-resets.h> #include <dt-bindings/interrupt-controller/irq-st.h> / { #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 <dt-bindings/interrupt-controller/arm-gic.h> -#include <dt-bindings/reset-controller/stih415-resets.h> +#include <dt-bindings/reset/stih415-resets.h> / { 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 <dt-bindings/phy/phy.h> #include <dt-bindings/interrupt-controller/arm-gic.h> -#include <dt-bindings/reset-controller/stih416-resets.h> +#include <dt-bindings/reset/stih416-resets.h> #include <dt-bindings/interrupt-controller/irq-st.h> / { L2: cache-controller { 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/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 { diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 157d421f755b..85d5904e5480 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,5 +1,8 @@ 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 obj-$(CONFIG_ARCH_STI) += sti/ +obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o +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..9aaf646ece55 --- /dev/null +++ b/drivers/reset/reset-ath79.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2015 Alban Bedel <albeu@free.fr> + * + * 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/module.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> + +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); + + 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; + 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 <albeu@free.fr>"); +MODULE_DESCRIPTION("AR71xx Reset Controller Driver"); +MODULE_LICENSE("GPL"); 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 <manabian@gmail.com> + * + * 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 <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/reset-controller.h> +#include <linux/spinlock.h> + +/* 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 <manabian@gmail.com>"); +MODULE_DESCRIPTION("Reset driver for LPC18xx/43xx RGU"); +MODULE_LICENSE("GPL v2"); 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 <linux/types.h> #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; 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 <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/regmap.h> +#include <linux/types.h> + +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 <moritz.fischer@ettus.com>"); +MODULE_DESCRIPTION("Zynq Reset Controller Driver"); diff --git a/drivers/reset/sti/reset-stih407.c b/drivers/reset/sti/reset-stih407.c index d83db5d72d08..827eb3dae47d 100644 --- a/drivers/reset/sti/reset-stih407.c +++ b/drivers/reset/sti/reset-stih407.c @@ -11,7 +11,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> -#include <dt-bindings/reset-controller/stih407-resets.h> +#include <dt-bindings/reset/stih407-resets.h> #include "reset-syscfg.h" /* STiH407 Peripheral powerdown definitions. */ @@ -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 8dad603d863c..6f220cdbef46 100644 --- a/drivers/reset/sti/reset-stih415.c +++ b/drivers/reset/sti/reset-stih415.c @@ -13,7 +13,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> -#include <dt-bindings/reset-controller/stih415-resets.h> +#include <dt-bindings/reset/stih415-resets.h> #include "reset-syscfg.h" @@ -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 79aed70a26c0..c581d606ef0f 100644 --- a/drivers/reset/sti/reset-stih416.c +++ b/drivers/reset/sti/reset-stih416.c @@ -13,7 +13,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> -#include <dt-bindings/reset-controller/stih416-resets.h> +#include <dt-bindings/reset/stih416-resets.h> #include "reset-syscfg.h" @@ -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", diff --git a/include/dt-bindings/reset-controller/stih407-resets.h b/include/dt-bindings/reset/stih407-resets.h index 02d4328fe479..02d4328fe479 100644 --- a/include/dt-bindings/reset-controller/stih407-resets.h +++ b/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 index c2329fe29cf6..c2329fe29cf6 100644 --- a/include/dt-bindings/reset-controller/stih415-resets.h +++ b/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 index fcf9af1ac0b2..fcf9af1ac0b2 100644 --- a/include/dt-bindings/reset-controller/stih416-resets.h +++ b/include/dt-bindings/reset/stih416-resets.h 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) { |