diff options
Diffstat (limited to 'drivers/reset')
-rw-r--r-- | drivers/reset/Kconfig | 19 | ||||
-rw-r--r-- | drivers/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/reset/reset-simple.c | 189 | ||||
-rw-r--r-- | drivers/reset/reset-stm32.c | 15 |
4 files changed, 209 insertions, 15 deletions
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 6c70c10269..b12159094d 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -15,6 +15,23 @@ menuconfig RESET_CONTROLLER if RESET_CONTROLLER +config RESET_SIMPLE + bool "Simple Reset Controller Driver" if COMPILE_TEST + help + This enables a simple reset controller driver for reset lines that + that can be asserted and deasserted by toggling bits in a contiguous, + exclusive register space. + + Currently this driver supports: + - Altera 64-Bit SoCFPGAs + - ASPEED BMC SoCs + - Bitmain BM1880 SoC + - Realtek SoCs + - RCC reset controller in STM32 MCUs + - Allwinner SoCs + - SiFive FU740 SoCs + + config RESET_IMX7 bool "i.MX7 Reset Driver" default ARCH_IMX7 @@ -26,7 +43,7 @@ config RESET_STM32 bool "STM32 Reset Driver" depends on ARCH_STM32MP || COMPILE_TEST help - This enables the reset controller driver for STM32MP and STM32 MCUs. + This enables the reset controller driver for STM32MP1. config RESET_STARFIVE bool "StarFive Controller Driver" if COMPILE_TEST diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index d884a83aa3..b4270411fd 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_RESET_CONTROLLER) += core.o +obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_IMX7) += reset-imx7.o obj-$(CONFIG_RESET_STM32) += reset-stm32.o diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c new file mode 100644 index 0000000000..082956d94d --- /dev/null +++ b/drivers/reset/reset-simple.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Simple Reset Controller Driver + * + * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de> + * + * Based on Allwinner SoCs Reset Controller driver + * + * Copyright 2013 Maxime Ripard + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + */ + +#include <common.h> +#include <init.h> +#include <linux/err.h> +#include <linux/reset-controller.h> +#include <linux/reset/reset-simple.h> +#include <restart.h> +#include <reset_source.h> +#include <asm/io.h> + +static inline struct reset_simple_data * +to_reset_simple_data(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct reset_simple_data, rcdev); +} + +static int reset_simple_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct reset_simple_data *data = to_reset_simple_data(rcdev); + int reg_width = sizeof(u32); + int bank = id / (reg_width * BITS_PER_BYTE); + int offset = id % (reg_width * BITS_PER_BYTE); + u32 reg; + + reg = readl(data->membase + (bank * reg_width)); + if (assert ^ data->active_low) + reg |= BIT(offset); + else + reg &= ~BIT(offset); + writel(reg, data->membase + (bank * reg_width)); + + return 0; +} + +static int reset_simple_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return reset_simple_update(rcdev, id, true); +} + +static int reset_simple_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return reset_simple_update(rcdev, id, false); +} + +static int reset_simple_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_simple_data *data = to_reset_simple_data(rcdev); + int ret; + + if (!data->reset_us) + return -ENOTSUPP; + + ret = reset_simple_assert(rcdev, id); + if (ret) + return ret; + + udelay(data->reset_us); + + return reset_simple_deassert(rcdev, id); +} + +static int __maybe_unused reset_simple_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_simple_data *data = to_reset_simple_data(rcdev); + int reg_width = sizeof(u32); + int bank = id / (reg_width * BITS_PER_BYTE); + int offset = id % (reg_width * BITS_PER_BYTE); + u32 reg; + + reg = readl(data->membase + (bank * reg_width)); + + return !(reg & BIT(offset)) ^ !data->status_active_low; +} + +const struct reset_control_ops reset_simple_ops = { + .assert = reset_simple_assert, + .deassert = reset_simple_deassert, + .reset = reset_simple_reset, +}; +EXPORT_SYMBOL_GPL(reset_simple_ops); + +/** + * struct reset_simple_devdata - simple reset controller properties + * @reg_offset: offset between base address and first reset register. + * @nr_resets: number of resets. If not set, default to resource size in bits. + * @active_low: if true, bits are cleared to assert the reset. Otherwise, bits + * are set to assert the reset. + * @status_active_low: if true, bits read back as cleared while the reset is + * asserted. Otherwise, bits read back as set while the + * reset is asserted. + */ +struct reset_simple_devdata { + u32 reg_offset; + u32 nr_resets; + bool active_low; + bool status_active_low; +}; + +#define SOCFPGA_NR_BANKS 8 + +static const struct reset_simple_devdata reset_simple_socfpga = { + .reg_offset = 0x20, + .nr_resets = SOCFPGA_NR_BANKS * 32, + .status_active_low = true, +}; + +static const struct reset_simple_devdata reset_simple_active_low = { + .active_low = true, + .status_active_low = true, +}; + +static const struct of_device_id reset_simple_dt_ids[] = { + { .compatible = "altr,stratix10-rst-mgr", + .data = &reset_simple_socfpga }, + { .compatible = "st,stm32-rcc", }, + { .compatible = "allwinner,sun6i-a31-clock-reset", + .data = &reset_simple_active_low }, + { .compatible = "zte,zx296718-reset", + .data = &reset_simple_active_low }, + { .compatible = "aspeed,ast2400-lpc-reset" }, + { .compatible = "aspeed,ast2500-lpc-reset" }, + { .compatible = "bitmain,bm1880-reset", + .data = &reset_simple_active_low }, + { .compatible = "brcm,bcm4908-misc-pcie-reset", + .data = &reset_simple_active_low }, + { .compatible = "snps,dw-high-reset" }, + { .compatible = "snps,dw-low-reset", + .data = &reset_simple_active_low }, + { /* sentinel */ }, +}; + +static int reset_simple_probe(struct device_d *dev) +{ + const struct reset_simple_devdata *devdata; + struct reset_simple_data *data; + struct resource *res; + u32 reg_offset = 0; + + devdata = device_get_match_data(dev); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = dev_request_mem_resource(dev, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + + data->membase = IOMEM(res->start); + data->rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE; + data->rcdev.ops = &reset_simple_ops; + data->rcdev.of_node = dev->device_node; + + if (devdata) { + reg_offset = devdata->reg_offset; + if (devdata->nr_resets) + data->rcdev.nr_resets = devdata->nr_resets; + data->active_low = devdata->active_low; + data->status_active_low = devdata->status_active_low; + } + + data->membase += reg_offset; + + return reset_controller_register(&data->rcdev); +} + +static struct driver_d reset_simple_driver = { + .probe = reset_simple_probe, + .name = "simple-reset", + .of_compatible = reset_simple_dt_ids, +}; +postcore_platform_driver(reset_simple_driver); diff --git a/drivers/reset/reset-stm32.c b/drivers/reset/reset-stm32.c index 703ba1f072..186b2a8bc6 100644 --- a/drivers/reset/reset-stm32.c +++ b/drivers/reset/reset-stm32.c @@ -66,14 +66,6 @@ static void stm32mp_reset(void __iomem *reg, unsigned offset, bool assert) writel(BIT(offset), reg); } -static void stm32mcu_reset(void __iomem *reg, unsigned offset, bool assert) -{ - if (assert) - setbits_le32(reg, BIT(offset)); - else - clrbits_le32(reg, BIT(offset)); -} - static u32 stm32_reset_status(struct stm32_reset *priv, unsigned long bank) { return readl(priv->base + bank); @@ -195,18 +187,13 @@ static const struct stm32_reset_ops stm32mp1_reset_ops = { .reset_reasons = stm32mp_reset_reasons, }; -static const struct stm32_reset_ops stm32mcu_reset_ops = { - .reset = stm32mcu_reset, -}; - static const struct of_device_id stm32_rcc_reset_dt_ids[] = { { .compatible = "st,stm32mp1-rcc", .data = &stm32mp1_reset_ops }, - { .compatible = "st,stm32-rcc", .data = &stm32mcu_reset_ops }, { /* sentinel */ }, }; static struct driver_d stm32_rcc_reset_driver = { - .name = "stm32_rcc_reset", + .name = "stm32mp_rcc_reset", .probe = stm32_reset_probe, .of_compatible = DRV_OF_COMPAT(stm32_rcc_reset_dt_ids), }; |