diff options
author | Arnd Bergmann <arnd@arndb.de> | 2020-12-09 00:26:38 +0100 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2020-12-09 00:39:58 +0100 |
commit | 5dd65cf78dc3be1a48223e83764ccbd14ded99f2 (patch) | |
tree | 5ac5c57ec5e5d1166f32941f0c5d27b9005815cc | |
parent | e0839f3ea404138f0a48d861bb30d66d5cc7a6e2 (diff) | |
parent | d06c4dec98b655e2808d2a59681ec24197c59c9d (diff) |
Merge tag 'reset-for-v5.11' of git://git.pengutronix.de/pza/linux into arm/drivers
Reset controller updates for v5.11
This tag adds support for sharing pulsed resets under certain
conditions. For example, when multiple drivers go through a
suspend-resume cycle, a reset line they share can be triggered
again during resume.
Further, the reset-meson driver now can be built as a module, the
reset-socfpga driver properly releases its memory region in the
probe error path, and reset-ti-syscon driver uses regmap_write_bits()
instead of regmap_update_bits() to support devices without status
readback.
* tag 'reset-for-v5.11' of git://git.pengutronix.de/pza/linux:
reset-controller: ti: force the write operation when assert or deassert
reset: socfpga: add error handling and release mem-region
reset: meson: make it possible to build as a module
reset: make shared pulsed reset controls re-triggerable
Link: https://lore.kernel.org/r/d586ada0073f2ac50a02274f42bf07bfb0603835.camel@pengutronix.de
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r-- | drivers/reset/Kconfig | 3 | ||||
-rw-r--r-- | drivers/reset/core.c | 73 | ||||
-rw-r--r-- | drivers/reset/reset-meson.c | 8 | ||||
-rw-r--r-- | drivers/reset/reset-socfpga.c | 11 | ||||
-rw-r--r-- | drivers/reset/reset-ti-syscon.c | 4 | ||||
-rw-r--r-- | include/linux/reset.h | 1 |
6 files changed, 94 insertions, 6 deletions
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 07d162b179fc..84baec01aa30 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -95,7 +95,8 @@ config RESET_LPC18XX This enables the reset controller driver for NXP LPC18xx/43xx SoCs. config RESET_MESON - bool "Meson Reset Driver" if COMPILE_TEST + tristate "Meson Reset Driver" + depends on ARCH_MESON || COMPILE_TEST default ARCH_MESON help This enables the reset driver for Amlogic Meson SoCs. diff --git a/drivers/reset/core.c b/drivers/reset/core.c index a2df88e90011..34e89aa0fb5e 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -208,6 +208,39 @@ static int reset_control_array_reset(struct reset_control_array *resets) return 0; } +static int reset_control_array_rearm(struct reset_control_array *resets) +{ + struct reset_control *rstc; + int i; + + for (i = 0; i < resets->num_rstcs; i++) { + rstc = resets->rstc[i]; + + if (!rstc) + continue; + + if (WARN_ON(IS_ERR(rstc))) + return -EINVAL; + + if (rstc->shared) { + if (WARN_ON(atomic_read(&rstc->deassert_count) != 0)) + return -EINVAL; + } else { + if (!rstc->acquired) + return -EPERM; + } + } + + for (i = 0; i < resets->num_rstcs; i++) { + rstc = resets->rstc[i]; + + if (rstc && rstc->shared) + WARN_ON(atomic_dec_return(&rstc->triggered_count) < 0); + } + + return 0; +} + static int reset_control_array_assert(struct reset_control_array *resets) { int ret, i; @@ -326,6 +359,46 @@ int reset_control_reset(struct reset_control *rstc) EXPORT_SYMBOL_GPL(reset_control_reset); /** + * reset_control_rearm - allow shared reset line to be re-triggered" + * @rstc: reset controller + * + * On a shared reset line the actual reset pulse is only triggered once for the + * lifetime of the reset_control instance, except if this call is used. + * + * Calls to this function must be balanced with calls to reset_control_reset, + * a warning is thrown in case triggered_count ever dips below 0. + * + * Consumers must not use reset_control_(de)assert on shared reset lines when + * reset_control_reset or reset_control_rearm have been used. + * + * If rstc is NULL the function will just return 0. + */ +int reset_control_rearm(struct reset_control *rstc) +{ + if (!rstc) + return 0; + + if (WARN_ON(IS_ERR(rstc))) + return -EINVAL; + + if (reset_control_is_array(rstc)) + return reset_control_array_rearm(rstc_to_array(rstc)); + + if (rstc->shared) { + if (WARN_ON(atomic_read(&rstc->deassert_count) != 0)) + return -EINVAL; + + WARN_ON(atomic_dec_return(&rstc->triggered_count) < 0); + } else { + if (!rstc->acquired) + return -EPERM; + } + + return 0; +} +EXPORT_SYMBOL_GPL(reset_control_rearm); + +/** * reset_control_assert - asserts the reset line * @rstc: reset controller * diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c index 94d7ba88d7d2..c9bc325ad65a 100644 --- a/drivers/reset/reset-meson.c +++ b/drivers/reset/reset-meson.c @@ -9,6 +9,7 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/reset-controller.h> #include <linux/slab.h> @@ -104,6 +105,7 @@ static const struct of_device_id meson_reset_dt_ids[] = { { .compatible = "amlogic,meson-a1-reset", .data = &meson_a1_param}, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, meson_reset_dt_ids); static int meson_reset_probe(struct platform_device *pdev) { @@ -142,4 +144,8 @@ static struct platform_driver meson_reset_driver = { .of_match_table = meson_reset_dt_ids, }, }; -builtin_platform_driver(meson_reset_driver); +module_platform_driver(meson_reset_driver); + +MODULE_DESCRIPTION("Amlogic Meson Reset Controller driver"); +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index bdd984296196..2a72f861f798 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c @@ -44,7 +44,7 @@ static int a10_reset_init(struct device_node *np) data->membase = ioremap(res.start, size); if (!data->membase) { ret = -ENOMEM; - goto err_alloc; + goto release_region; } if (of_property_read_u32(np, "altr,modrst-offset", ®_offset)) @@ -59,7 +59,14 @@ static int a10_reset_init(struct device_node *np) data->rcdev.of_node = np; data->status_active_low = true; - return reset_controller_register(&data->rcdev); + ret = reset_controller_register(&data->rcdev); + if (ret) + pr_err("unable to register device\n"); + + return ret; + +release_region: + release_mem_region(res.start, size); err_alloc: kfree(data); diff --git a/drivers/reset/reset-ti-syscon.c b/drivers/reset/reset-ti-syscon.c index ef97c4dbbb4e..218370faf37b 100644 --- a/drivers/reset/reset-ti-syscon.c +++ b/drivers/reset/reset-ti-syscon.c @@ -89,7 +89,7 @@ static int ti_syscon_reset_assert(struct reset_controller_dev *rcdev, mask = BIT(control->assert_bit); value = (control->flags & ASSERT_SET) ? mask : 0x0; - return regmap_update_bits(data->regmap, control->assert_offset, mask, value); + return regmap_write_bits(data->regmap, control->assert_offset, mask, value); } /** @@ -120,7 +120,7 @@ static int ti_syscon_reset_deassert(struct reset_controller_dev *rcdev, mask = BIT(control->deassert_bit); value = (control->flags & DEASSERT_SET) ? mask : 0x0; - return regmap_update_bits(data->regmap, control->deassert_offset, mask, value); + return regmap_write_bits(data->regmap, control->deassert_offset, mask, value); } /** diff --git a/include/linux/reset.h b/include/linux/reset.h index 05aa9f440f48..439fec7112a9 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -13,6 +13,7 @@ struct reset_control; #ifdef CONFIG_RESET_CONTROLLER int reset_control_reset(struct reset_control *rstc); +int reset_control_rearm(struct reset_control *rstc); int reset_control_assert(struct reset_control *rstc); int reset_control_deassert(struct reset_control *rstc); int reset_control_status(struct reset_control *rstc); |