diff options
Diffstat (limited to 'drivers/reset')
-rw-r--r-- | drivers/reset/Kconfig | 39 | ||||
-rw-r--r-- | drivers/reset/Makefile | 5 | ||||
-rw-r--r-- | drivers/reset/core.c | 258 | ||||
-rw-r--r-- | drivers/reset/reset-imx7.c | 33 | ||||
-rw-r--r-- | drivers/reset/reset-scmi.c | 130 | ||||
-rw-r--r-- | drivers/reset/reset-simple.c | 191 | ||||
-rw-r--r-- | drivers/reset/reset-socfpga.c | 24 | ||||
-rw-r--r-- | drivers/reset/reset-starfive-vic.c | 235 | ||||
-rw-r--r-- | drivers/reset/reset-stm32.c | 109 |
9 files changed, 851 insertions, 173 deletions
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 048f2081f8..16c05d50f0 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config ARCH_HAS_RESET_CONTROLLER bool @@ -14,16 +15,46 @@ 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 SOC_IMX7D + default ARCH_IMX7 select MFD_SYSCON help This enables the reset controller driver for i.MX7 SoCs. -config RESET_STM32 - bool "STM32 Reset Driver" +config RESET_STARFIVE + bool "StarFive Controller Driver" if COMPILE_TEST + depends on COMMON_CLK + default SOC_STARFIVE help - This enables the reset controller driver for STM32MP and STM32 MCUs. + This enables the reset controller driver for the StarFive JH7100. + +config RESET_SCMI + tristate "Reset driver controlled via ARM SCMI interface" + depends on ARM_SCMI_PROTOCOL || COMPILE_TEST + default ARM_SCMI_PROTOCOL + help + This driver provides support for reset signal/domains that are + controlled by firmware that implements the SCMI interface. + + This driver uses SCMI Message Protocol to interact with the + firmware controlling all the reset signals. endif diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 8460c4b154..b1668433d7 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,4 +1,7 @@ +# 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 +obj-$(CONFIG_RESET_STARFIVE) += reset-starfive-vic.o +obj-$(CONFIG_RESET_SCMI) += reset-scmi.o diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 99b9c80655..94bfad2067 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Reset Controller framework * * Copyright 2013 Philipp Zabel, Pengutronix - * - * 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. */ + #include <common.h> #include <gpio.h> #include <malloc.h> @@ -32,8 +29,21 @@ struct reset_control { int gpio; int gpio_active_high; - struct device_d *dev; + struct device *dev; unsigned int id; + bool array; +}; + +/** + * struct reset_control_array - an array of reset controls + * @base: reset control for compatibility with reset control API functions + * @num_rstcs: number of reset controls + * @rstc: array of reset controls + */ +struct reset_control_array { + struct reset_control base; + unsigned int num_rstcs; + struct reset_control *rstc[]; }; /** @@ -85,6 +95,86 @@ void reset_controller_unregister(struct reset_controller_dev *rcdev) EXPORT_SYMBOL_GPL(reset_controller_unregister); /** + * reset_control_status - returns a negative errno if not supported, a + * positive value if the reset line is asserted, or zero if the reset + * line is not asserted or if the desc is NULL (optional reset). + * @rstc: reset controller + */ +int reset_control_status(struct reset_control *rstc) +{ + if (!rstc) + return 0; + + if (WARN_ON(IS_ERR(rstc))) + return -EINVAL; + + if (rstc->rcdev->ops->status) + return rstc->rcdev->ops->status(rstc->rcdev, rstc->id); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(reset_control_status); + +static inline struct reset_control_array * +rstc_to_array(struct reset_control *rstc) { + return container_of(rstc, struct reset_control_array, base); +} + +static int reset_control_array_reset(struct reset_control_array *resets) +{ + int ret, i; + + for (i = 0; i < resets->num_rstcs; i++) { + ret = reset_control_reset(resets->rstc[i]); + if (ret) + return ret; + } + + return 0; +} + +static int reset_control_array_assert(struct reset_control_array *resets) +{ + int ret, i; + + for (i = 0; i < resets->num_rstcs; i++) { + ret = reset_control_assert(resets->rstc[i]); + if (ret) + goto err; + } + + return 0; + +err: + while (i--) + reset_control_deassert(resets->rstc[i]); + return ret; +} + +static int reset_control_array_deassert(struct reset_control_array *resets) +{ + int ret, i; + + for (i = 0; i < resets->num_rstcs; i++) { + ret = reset_control_deassert(resets->rstc[i]); + if (ret) + goto err; + } + + return 0; + +err: + while (i--) + reset_control_assert(resets->rstc[i]); + return ret; +} + +static inline bool reset_control_is_array(struct reset_control *rstc) +{ + return rstc->array; +} + +/** * reset_control_reset - reset the controlled device * @rstc: reset controller */ @@ -93,6 +183,9 @@ int reset_control_reset(struct reset_control *rstc) if (!rstc) return 0; + if (reset_control_is_array(rstc)) + return reset_control_array_reset(rstc_to_array(rstc)); + if (rstc->rcdev->ops->reset) return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); @@ -109,6 +202,9 @@ int reset_control_assert(struct reset_control *rstc) if (!rstc) return 0; + if (reset_control_is_array(rstc)) + return reset_control_array_assert(rstc_to_array(rstc)); + if (rstc->gpio >= 0) return gpio_direction_output(rstc->gpio, rstc->gpio_active_high); @@ -128,6 +224,9 @@ int reset_control_deassert(struct reset_control *rstc) if (!rstc) return 0; + if (reset_control_is_array(rstc)) + return reset_control_array_deassert(rstc_to_array(rstc)); + if (rstc->gpio >= 0) return gpio_direction_output(rstc->gpio, !rstc->gpio_active_high); @@ -139,35 +238,44 @@ int reset_control_deassert(struct reset_control *rstc) EXPORT_SYMBOL_GPL(reset_control_deassert); /** - * of_reset_control_get - Lookup and obtain a reference to a reset controller. + * reset_control_get_count - Count reset lines + * @dev: device + * + * Returns number of resets, 0 if none specified + */ +int reset_control_get_count(struct device *dev) +{ + return of_count_phandle_with_args(dev->of_node, "resets", + "#reset-cells"); +} + +/** + * of_reset_control_get_by_index - Lookup and obtain a reference to a reset controller. * @node: device to be reset by the controller - * @id: reset line name + * @index: reset line index * * Returns a struct reset_control or IS_ERR() condition containing errno. - * - * Use of id names is optional. */ -static struct reset_control *of_reset_control_get(struct device_node *node, - const char *id) +static struct reset_control *of_reset_control_get_by_index(struct device_node *node, + int index) { - struct reset_control *rstc = ERR_PTR(-ENODEV); + struct reset_control *rstc; struct reset_controller_dev *r, *rcdev; struct of_phandle_args args; - int index = 0; int rstc_id; int ret; if (!of_get_property(node, "resets", NULL)) return NULL; - if (id) - index = of_property_match_string(node, - "reset-names", id); ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", index, &args); if (ret) return ERR_PTR(ret); + /* Ignore error, as CLK_OF_DECLARE resets have no proper driver. */ + of_device_ensure_probed(args.np); + rcdev = NULL; list_for_each_entry(r, &reset_controller_list, list) { if (args.np == r->of_node) { @@ -195,8 +303,31 @@ static struct reset_control *of_reset_control_get(struct device_node *node, return rstc; } +/** + * of_reset_control_get - Lookup and obtain a reference to a reset controller. + * @node: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * + * Use of id names is optional. + */ +struct reset_control *of_reset_control_get(struct device_node *node, + const char *id) +{ + int index = 0; + + if (id) { + index = of_property_match_string(node, "reset-names", id); + if (index < 0) + return ERR_PTR(-ENOENT); + } + + return of_reset_control_get_by_index(node, index); +} + static struct reset_control * -gpio_reset_control_get(struct device_d *dev, const char *id) +gpio_reset_control_get(struct device *dev, const char *id) { struct reset_control *rc; int gpio; @@ -205,10 +336,10 @@ gpio_reset_control_get(struct device_d *dev, const char *id) if (id) return ERR_PTR(-EINVAL); - if (!of_get_property(dev->device_node, "reset-gpios", NULL)) + if (!of_get_property(dev->of_node, "reset-gpios", NULL)) return NULL; - gpio = of_get_named_gpio_flags(dev->device_node, "reset-gpios", 0, &flags); + gpio = of_get_named_gpio_flags(dev->of_node, "reset-gpios", 0, &flags); if (gpio < 0) return ERR_PTR(gpio); @@ -228,14 +359,14 @@ gpio_reset_control_get(struct device_d *dev, const char *id) * * Use of id names is optional. */ -struct reset_control *reset_control_get(struct device_d *dev, const char *id) +struct reset_control *reset_control_get(struct device *dev, const char *id) { struct reset_control *rstc; if (!dev) return ERR_PTR(-EINVAL); - rstc = of_reset_control_get(dev->device_node, id); + rstc = of_reset_control_get(dev->of_node, id); if (IS_ERR(rstc)) return ERR_CAST(rstc); @@ -281,7 +412,7 @@ EXPORT_SYMBOL_GPL(reset_control_put); * This is useful for the common case of devices with single, dedicated reset * lines. */ -int device_reset(struct device_d *dev) +int device_reset(struct device *dev) { struct reset_control *rstc; int ret; @@ -302,7 +433,86 @@ int device_reset(struct device_d *dev) } EXPORT_SYMBOL_GPL(device_reset); -int device_reset_us(struct device_d *dev, int us) +/* + * APIs to manage an array of reset controls. + */ + +/** + * reset_control_array_get - Get a list of reset controls + * + * @dev: device that requests the reset controls array + * + * Returns pointer to allocated reset_control on success or error on failure + */ +struct reset_control *reset_control_array_get(struct device *dev) +{ + struct reset_control_array *resets; + struct reset_control *rstc; + struct device_node *np = dev->of_node; + int num, i; + + num = reset_control_get_count(dev); + if (num < 0) + return ERR_PTR(num); + + resets = kzalloc(struct_size(resets, rstc, num), GFP_KERNEL); + if (!resets) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < num; i++) { + rstc = of_reset_control_get_by_index(np, i); + if (IS_ERR(rstc)) + goto err_rst; + resets->rstc[i] = rstc; + } + resets->num_rstcs = num; + resets->base.array = true; + + return &resets->base; + +err_rst: + while (--i >= 0) + reset_control_put(resets->rstc[i]); + + kfree(resets); + + return rstc; +} + +int device_reset_all(struct device *dev) +{ + struct reset_control *rstc; + int ret, i; + + for (i = 0; i < reset_control_get_count(dev); i++) { + int ret; + + rstc = of_reset_control_get_by_index(dev->of_node, i); + if (IS_ERR(rstc)) + return PTR_ERR(rstc); + + ret = reset_control_reset(rstc); + if (ret) + return ret; + + reset_control_put(rstc); + } + + if (i == 0) { + rstc = gpio_reset_control_get(dev, NULL); + + ret = reset_control_reset(rstc); + if (ret) + return ret; + + reset_control_put(rstc); + } + + return 0; +} +EXPORT_SYMBOL_GPL(device_reset_all); + +int device_reset_us(struct device *dev, int us) { struct reset_control *rstc; int ret; diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c index 9d4344a94c..c6c38f48a8 100644 --- a/drivers/reset/reset-imx7.c +++ b/drivers/reset/reset-imx7.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017, Impinj, Inc. * * i.MX7 System Reset Controller (SRC) driver * * Author: Andrey Smirnov <andrew.smirnov@gmail.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; 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 <common.h> @@ -22,7 +14,7 @@ #include <linux/err.h> #include <linux/reset-controller.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include <of_device.h> struct imx7_src_signal { @@ -177,9 +169,9 @@ static const struct imx7_src_signal imx8mq_src_signals[IMX8MQ_RESET_NUM] = { [IMX8MQ_RESET_OTG2_PHY_RESET] = { SRC_USBOPHY2_RCR, BIT(0) }, [IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N] = { SRC_MIPIPHY_RCR, BIT(1) }, [IMX8MQ_RESET_MIPI_DSI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(2) }, - [IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(3) }, - [IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N] = { SRC_MIPIPHY_RCR, BIT(4) }, - [IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N] = { SRC_MIPIPHY_RCR, BIT(5) }, + [IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(3) }, + [IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N] = { SRC_MIPIPHY_RCR, BIT(4) }, + [IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N] = { SRC_MIPIPHY_RCR, BIT(5) }, [IMX8MQ_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR, BIT(2) | BIT(1) }, [IMX8MQ_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) }, @@ -228,9 +220,9 @@ static int imx8mq_reset_set(struct reset_controller_dev *rcdev, case IMX8MQ_RESET_PCIE_CTRL_APPS_EN: case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN: /* fallthrough */ - case IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N: /* fallthrough */ - case IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N: /* fallthrough */ - case IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N: /* fallthrough */ case IMX8MQ_RESET_MIPI_DSI_RESET_N: /* fallthrough */ case IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N: /* fallthrough */ value = assert ? 0 : bit; @@ -261,14 +253,14 @@ static const struct imx7_src_variant variant_imx8mq = { }, }; -static int imx7_reset_probe(struct device_d *dev) +static int imx7_reset_probe(struct device *dev) { struct imx7_src *imx7src; const struct imx7_src_variant *variant = of_device_get_match_data(dev); imx7src = xzalloc(sizeof(*imx7src)); imx7src->signals = variant->signals; - imx7src->regmap = syscon_node_to_regmap(dev->device_node); + imx7src->regmap = syscon_node_to_regmap(dev->of_node); if (IS_ERR(imx7src->regmap)) { dev_err(dev, "Unable to get imx7-src regmap"); return PTR_ERR(imx7src->regmap); @@ -276,7 +268,7 @@ static int imx7_reset_probe(struct device_d *dev) imx7src->rcdev.nr_resets = variant->signals_num; imx7src->rcdev.ops = &variant->ops; - imx7src->rcdev.of_node = dev->device_node; + imx7src->rcdev.of_node = dev->of_node; return reset_controller_register(&imx7src->rcdev); } @@ -286,8 +278,9 @@ static const struct of_device_id imx7_reset_dt_ids[] = { { .compatible = "fsl,imx8mq-src", .data = &variant_imx8mq }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, imx7_reset_dt_ids); -static struct driver_d imx7_reset_driver = { +static struct driver imx7_reset_driver = { .name = "imx7d-src", .probe = imx7_reset_probe, .of_compatible = DRV_OF_COMPAT(imx7_reset_dt_ids), diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c new file mode 100644 index 0000000000..d8c4734f1b --- /dev/null +++ b/drivers/reset/reset-scmi.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM System Control and Management Interface (ARM SCMI) reset driver + * + * Copyright (C) 2019-2021 ARM Ltd. + */ + +#include <common.h> +#include <of.h> +#include <driver.h> +#include <linux/reset-controller.h> +#include <linux/scmi_protocol.h> + +static const struct scmi_reset_proto_ops *reset_ops; + +/** + * struct scmi_reset_data - reset controller information structure + * @rcdev: reset controller entity + * @ph: ARM SCMI protocol handle used for communication with system controller + */ +struct scmi_reset_data { + struct reset_controller_dev rcdev; + const struct scmi_protocol_handle *ph; +}; + +#define to_scmi_reset_data(p) container_of((p), struct scmi_reset_data, rcdev) +#define to_scmi_handle(p) (to_scmi_reset_data(p)->ph) + +/** + * scmi_reset_assert() - assert device reset + * @rcdev: reset controller entity + * @id: ID of the reset to be asserted + * + * This function implements the reset driver op to assert a device's reset + * using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev); + + return reset_ops->assert(ph, id); +} + +/** + * scmi_reset_deassert() - deassert device reset + * @rcdev: reset controller entity + * @id: ID of the reset to be deasserted + * + * This function implements the reset driver op to deassert a device's reset + * using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev); + + return reset_ops->deassert(ph, id); +} + +/** + * scmi_reset_reset() - reset the device + * @rcdev: reset controller entity + * @id: ID of the reset signal to be reset(assert + deassert) + * + * This function implements the reset driver op to trigger a device's + * reset signal using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev); + + return reset_ops->reset(ph, id); +} + +static const struct reset_control_ops scmi_reset_ops = { + .assert = scmi_reset_assert, + .deassert = scmi_reset_deassert, + .reset = scmi_reset_reset, +}; + +static int scmi_reset_probe(struct scmi_device *sdev) +{ + struct scmi_reset_data *data; + struct device *dev = &sdev->dev; + struct device_node *np = dev->of_node; + const struct scmi_handle *handle = sdev->handle; + struct scmi_protocol_handle *ph; + + if (!handle) + return -ENODEV; + + reset_ops = handle->dev_protocol_get(sdev, SCMI_PROTOCOL_RESET, &ph); + if (IS_ERR(reset_ops)) + return PTR_ERR(reset_ops); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->rcdev.ops = &scmi_reset_ops; + data->rcdev.of_node = np; + data->rcdev.nr_resets = reset_ops->num_domains_get(ph); + data->ph = ph; + + return reset_controller_register(&data->rcdev); +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_RESET, "reset" }, + { }, +}; + +static struct scmi_driver scmi_reset_driver = { + .name = "scmi-reset", + .probe = scmi_reset_probe, + .id_table = scmi_id_table, +}; +core_scmi_driver(scmi_reset_driver); + +MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>"); +MODULE_DESCRIPTION("ARM SCMI reset controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c new file mode 100644 index 0000000000..20f3df18f4 --- /dev/null +++ b/drivers/reset/reset-simple.c @@ -0,0 +1,191 @@ +// 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 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, + .status = reset_simple_status, +}; +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 */ }, +}; +MODULE_DEVICE_TABLE(of, reset_simple_dt_ids); + +static int reset_simple_probe(struct device *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->of_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 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-socfpga.c b/drivers/reset/reset-socfpga.c index 9b499f23c5..d214ce503e 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright 2014 Steffen Trumtrar <s.trumtrar@pengutronix.de> * @@ -7,11 +8,6 @@ * Copyright 2013 Maxime Ripard * * Maxime Ripard <maxime.ripard@free-electrons.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. */ #include <common.h> @@ -78,19 +74,20 @@ static const struct reset_control_ops socfpga_reset_ops = { .deassert = socfpga_reset_deassert, }; -static int socfpga_reset_probe(struct device_d *dev) +static int socfpga_reset_probe(struct device *dev) { struct socfpga_reset_data *data; struct resource *res; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; u32 modrst_offset; data = xzalloc(sizeof(*data)); res = dev_request_mem_resource(dev, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + data->membase = IOMEM(res->start); - if (IS_ERR(data->membase)) - return PTR_ERR(data->membase); if (of_property_read_u32(np, "altr,modrst-offset", &modrst_offset)) { dev_warn(dev, "missing altr,modrst-offset property, assuming 0x10!\n"); @@ -111,15 +108,12 @@ static const struct of_device_id socfpga_reset_dt_ids[] = { { .compatible = "altr,rst-mgr", }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, socfpga_reset_dt_ids); -static struct driver_d socfpga_reset_driver = { +static struct driver socfpga_reset_driver = { .name = "socfpga_reset", .probe = socfpga_reset_probe, .of_compatible = DRV_OF_COMPAT(socfpga_reset_dt_ids), }; -static int socfpga_reset_init(void) -{ - return platform_driver_register(&socfpga_reset_driver); -} -postcore_initcall(socfpga_reset_init); +postcore_platform_driver(socfpga_reset_driver); diff --git a/drivers/reset/reset-starfive-vic.c b/drivers/reset/reset-starfive-vic.c new file mode 100644 index 0000000000..3e9e367215 --- /dev/null +++ b/drivers/reset/reset-starfive-vic.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Ahmad Fatoum, Pengutronix + * + * StarFive Reset Controller driver + */ +#define pr_fmt(fmt) "reset-starfive: " fmt + +#include <common.h> +#include <init.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/reset-controller.h> +#include <soc/starfive/rstgen.h> +#include <dt-bindings/reset-controller/starfive-jh7100.h> +#include <dt-bindings/clock/starfive-jh7100.h> + +struct starfive_rstgen { + void __iomem *base; + struct reset_controller_dev rcdev; + const struct starfive_rstgen_ops *ops; + struct device_node *clknp; + const int *sync_resets; +}; + +static struct starfive_rstgen *to_starfive_rstgen(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct starfive_rstgen, rcdev); +} + +static const int jh7110_rstgen_sync_resets[RSTN_END] = { + [RSTN_SGDMA2P_AHB] = CLK_SGDMA2P_AHB, + [RSTN_SGDMA2P_AXI] = CLK_SGDMA2P_AXI, + [RSTN_DMA2PNOC_AXI] = CLK_DMA2PNOC_AXI, + [RSTN_DLA_AXI] = CLK_DLA_AXI, + [RSTN_DLANOC_AXI] = CLK_DLANOC_AXI, + [RSTN_DLA_APB] = CLK_DLA_APB, + [RSTN_VDECBRG_MAIN] = CLK_VDECBRG_MAIN, + [RSTN_VDEC_AXI] = CLK_VDEC_AXI, + [RSTN_VDEC_BCLK] = CLK_VDEC_BCLK, + [RSTN_VDEC_CCLK] = CLK_VDEC_CCLK, + [RSTN_VDEC_APB] = CLK_VDEC_APB, + [RSTN_JPEG_AXI] = CLK_JPEG_AXI, + [RSTN_JPEG_CCLK] = CLK_JPEG_CCLK, + [RSTN_JPEG_APB] = CLK_JPEG_APB, + [RSTN_JPCGC300_MAIN] = CLK_JPCGC300_MAIN, + [RSTN_GC300_2X] = CLK_GC300_2X, + [RSTN_GC300_AXI] = CLK_GC300_AXI, + [RSTN_GC300_AHB] = CLK_GC300_AHB, + [RSTN_VENC_AXI] = CLK_VENC_AXI, + [RSTN_VENCBRG_MAIN] = CLK_VENCBRG_MAIN, + [RSTN_VENC_BCLK] = CLK_VENC_BCLK, + [RSTN_VENC_CCLK] = CLK_VENC_CCLK, + [RSTN_VENC_APB] = CLK_VENC_APB, + [RSTN_DDRPHY_APB] = CLK_DDRPHY_APB, + [RSTN_USB_AXI] = CLK_USB_AXI, + [RSTN_SGDMA1P_AXI] = CLK_SGDMA1P_AXI, + [RSTN_DMA1P_AXI] = CLK_DMA1P_AXI, + [RSTN_NNE_AHB] = CLK_NNE_AHB, + [RSTN_NNE_AXI] = CLK_NNE_AXI, + [RSTN_NNENOC_AXI] = CLK_NNENOC_AXI, + [RSTN_DLASLV_AXI] = CLK_DLASLV_AXI, + [RSTN_VOUT_SRC] = CLK_VOUT_SRC, + [RSTN_DISP_AXI] = CLK_DISP_AXI, + [RSTN_DISPNOC_AXI] = CLK_DISPNOC_AXI, + [RSTN_SDIO0_AHB] = CLK_SDIO0_AHB, + [RSTN_SDIO1_AHB] = CLK_SDIO1_AHB, + [RSTN_GMAC_AHB] = CLK_GMAC_AHB, + [RSTN_SPI2AHB_AHB] = CLK_SPI2AHB_AHB, + [RSTN_SPI2AHB_CORE] = CLK_SPI2AHB_CORE, + [RSTN_EZMASTER_AHB] = CLK_EZMASTER_AHB, + [RSTN_SEC_AHB] = CLK_SEC_AHB, + [RSTN_AES] = CLK_AES, + [RSTN_PKA] = CLK_PKA, + [RSTN_SHA] = CLK_SHA, + [RSTN_TRNG_APB] = CLK_TRNG_APB, + [RSTN_OTP_APB] = CLK_OTP_APB, + [RSTN_UART0_APB] = CLK_UART0_APB, + [RSTN_UART0_CORE] = CLK_UART0_CORE, + [RSTN_UART1_APB] = CLK_UART1_APB, + [RSTN_UART1_CORE] = CLK_UART1_CORE, + [RSTN_SPI0_APB] = CLK_SPI0_APB, + [RSTN_SPI0_CORE] = CLK_SPI0_CORE, + [RSTN_SPI1_APB] = CLK_SPI1_APB, + [RSTN_SPI1_CORE] = CLK_SPI1_CORE, + [RSTN_I2C0_APB] = CLK_I2C0_APB, + [RSTN_I2C0_CORE] = CLK_I2C0_CORE, + [RSTN_I2C1_APB] = CLK_I2C1_APB, + [RSTN_I2C1_CORE] = CLK_I2C1_CORE, + [RSTN_GPIO_APB] = CLK_GPIO_APB, + [RSTN_UART2_APB] = CLK_UART2_APB, + [RSTN_UART2_CORE] = CLK_UART2_CORE, + [RSTN_UART3_APB] = CLK_UART3_APB, + [RSTN_UART3_CORE] = CLK_UART3_CORE, + [RSTN_SPI2_APB] = CLK_SPI2_APB, + [RSTN_SPI2_CORE] = CLK_SPI2_CORE, + [RSTN_SPI3_APB] = CLK_SPI3_APB, + [RSTN_SPI3_CORE] = CLK_SPI3_CORE, + [RSTN_I2C2_APB] = CLK_I2C2_APB, + [RSTN_I2C2_CORE] = CLK_I2C2_CORE, + [RSTN_I2C3_APB] = CLK_I2C3_APB, + [RSTN_I2C3_CORE] = CLK_I2C3_CORE, + [RSTN_WDTIMER_APB] = CLK_WDTIMER_APB, + [RSTN_WDT] = CLK_WDT_CORE, + [RSTN_VP6INTC_APB] = CLK_VP6INTC_APB, + [RSTN_TEMP_APB] = CLK_TEMP_APB, + [RSTN_TEMP_SENSE] = CLK_TEMP_SENSE, +}; + +static struct clk *starfive_reset_clk_get(struct starfive_rstgen *priv, unsigned id) +{ + struct of_phandle_args clkspec = { + .np = priv->clknp, + .args_count = 1, + }; + + if (!priv->sync_resets || !priv->sync_resets[id]) + return 0; + + clkspec.args[0] = priv->sync_resets[id]; + + pr_debug("synchronous reset=%u clk=%u\n", id, priv->sync_resets[id]); + + return of_clk_get_from_provider(&clkspec); +} + +static int starfive_reset_clk_enable(struct starfive_rstgen *priv, unsigned id) +{ + return clk_enable(starfive_reset_clk_get(priv, id)); +} + +static void starfive_reset_clk_disable(struct starfive_rstgen *priv, unsigned id) +{ + clk_disable(starfive_reset_clk_get(priv, id)); +} + +static int starfive_rstgen(struct starfive_rstgen *priv, unsigned id, bool assert) +{ + void __iomem *base = priv->base; + + __starfive_rstgen(base, id, assert); + + return wait_on_timeout(NSEC_PER_MSEC, __starfive_rstgen_asserted(base, id) == assert); +} + +static int starfive_rstgen_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct starfive_rstgen *priv = to_starfive_rstgen(rcdev); + int ret; + + starfive_reset_clk_enable(priv, id); + ret = starfive_rstgen(priv, id, true); + starfive_reset_clk_disable(priv, id); + + return ret; +} + +static int starfive_rstgen_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct starfive_rstgen *priv = to_starfive_rstgen(rcdev); + int ret; + + starfive_reset_clk_enable(priv, id); + ret = starfive_rstgen(priv, id, false); + starfive_reset_clk_disable(priv, id); + + return ret; +} + +static int starfive_reset(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct starfive_rstgen *priv = to_starfive_rstgen(rcdev); + int ret; + + starfive_reset_clk_enable(priv, id); + + ret = starfive_rstgen(priv, id, true); + if (ret) + goto out; + + udelay(2); + + ret = starfive_rstgen(priv, id, false); + +out: + starfive_reset_clk_disable(priv, id); + + return ret; +} + +static const struct reset_control_ops starfive_rstgen_ops = { + .assert = starfive_rstgen_assert, + .deassert = starfive_rstgen_deassert, + .reset = starfive_reset, +}; + +static int starfive_rstgen_probe(struct device *dev) +{ + struct starfive_rstgen *priv; + struct resource *iores; + + priv = xzalloc(sizeof(*priv)); + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + if ((priv->sync_resets = device_get_match_data(dev))) { + priv->clknp = of_find_compatible_node(NULL, NULL, "starfive,jh7100-clkgen"); + if (!priv->clknp) + return -ENODEV; + } + + priv->base = IOMEM(iores->start); + priv->rcdev.nr_resets = RSTN_END; + priv->rcdev.ops = &starfive_rstgen_ops; + priv->rcdev.of_node = dev->of_node; + + return reset_controller_register(&priv->rcdev); +} + +static const struct of_device_id starfive_rstgen_reset_dt_ids[] = { + { .compatible = "starfive,jh7100-rstgen", .data = jh7110_rstgen_sync_resets }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, starfive_rstgen_reset_dt_ids); + +static struct driver starfive_rstgen_reset_driver = { + .name = "starfive_rstgen", + .probe = starfive_rstgen_probe, + .of_compatible = starfive_rstgen_reset_dt_ids, +}; +core_platform_driver(starfive_rstgen_reset_driver); diff --git a/drivers/reset/reset-stm32.c b/drivers/reset/reset-stm32.c deleted file mode 100644 index 5689dfd488..0000000000 --- a/drivers/reset/reset-stm32.c +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2017, STMicroelectronics - All Rights Reserved - * Copyright (C) 2019, Ahmad Fatoum, Pengutronix - * Author(s): Patrice Chotard, <patrice.chotard@st.com> for STMicroelectronics. - */ - -#include <common.h> -#include <init.h> -#include <linux/err.h> -#include <linux/reset-controller.h> -#include <asm/io.h> - -#define RCC_CL 0x4 - -struct stm32_reset { - void __iomem *base; - struct reset_controller_dev rcdev; - void (*reset)(void __iomem *reg, unsigned offset, bool assert); -}; - -static struct stm32_reset *to_stm32_reset(struct reset_controller_dev *rcdev) -{ - return container_of(rcdev, struct stm32_reset, rcdev); -} - -static void stm32mp_reset(void __iomem *reg, unsigned offset, bool assert) -{ - if (!assert) - reg += RCC_CL; - - 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 void stm32_reset(struct stm32_reset *priv, unsigned long id, bool assert) -{ - int bank = (id / BITS_PER_LONG) * 4; - int offset = id % BITS_PER_LONG; - - priv->reset(priv->base + bank, offset, assert); -} - -static int stm32_reset_assert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - stm32_reset(to_stm32_reset(rcdev), id, true); - return 0; -} - -static int stm32_reset_deassert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - stm32_reset(to_stm32_reset(rcdev), id, false); - return 0; -} - -static const struct reset_control_ops stm32_reset_ops = { - .assert = stm32_reset_assert, - .deassert = stm32_reset_deassert, -}; - -static int stm32_reset_probe(struct device_d *dev) -{ - struct stm32_reset *priv; - struct resource *iores; - int ret; - - priv = xzalloc(sizeof(*priv)); - ret = dev_get_drvdata(dev, (const void **)&priv->reset); - if (ret) - return ret; - - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - - priv->base = IOMEM(iores->start); - priv->rcdev.nr_resets = (iores->end - iores->start) * BITS_PER_BYTE; - priv->rcdev.ops = &stm32_reset_ops; - priv->rcdev.of_node = dev->device_node; - - return reset_controller_register(&priv->rcdev); -} - -static const struct of_device_id stm32_rcc_reset_dt_ids[] = { - { .compatible = "st,stm32mp1-rcc", .data = stm32mp_reset }, - { .compatible = "st,stm32-rcc", .data = stm32mcu_reset }, - { /* sentinel */ }, -}; - -static struct driver_d stm32_rcc_reset_driver = { - .name = "stm32_rcc_reset", - .probe = stm32_reset_probe, - .of_compatible = DRV_OF_COMPAT(stm32_rcc_reset_dt_ids), -}; - -static int stm32_rcc_reset_init(void) -{ - return platform_driver_register(&stm32_rcc_reset_driver); -} -postcore_initcall(stm32_rcc_reset_init); |