diff options
Diffstat (limited to 'drivers/reset/core.c')
-rw-r--r-- | drivers/reset/core.c | 196 |
1 files changed, 172 insertions, 24 deletions
diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 9f5a642539..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,14 +238,15 @@ int reset_control_deassert(struct reset_control *rstc) EXPORT_SYMBOL_GPL(reset_control_deassert); /** - * of_reset_control_count - Count reset lines - * @node: device node + * reset_control_get_count - Count reset lines + * @dev: device * * Returns number of resets, 0 if none specified */ -static int of_reset_control_count(struct device_node *node) +int reset_control_get_count(struct device *dev) { - return of_count_phandle_with_args(node, "resets", "#reset-cells"); + return of_count_phandle_with_args(dev->of_node, "resets", + "#reset-cells"); } /** @@ -173,9 +273,8 @@ static struct reset_control *of_reset_control_get_by_index(struct device_node *n if (ret) return ERR_PTR(ret); - ret = of_device_ensure_probed(args.np); - 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) { @@ -218,14 +317,17 @@ struct reset_control *of_reset_control_get(struct device_node *node, { int index = 0; - if (id) + 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; @@ -234,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); @@ -257,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); @@ -310,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; @@ -331,15 +433,61 @@ int device_reset(struct device_d *dev) } EXPORT_SYMBOL_GPL(device_reset); -int device_reset_all(struct device_d *dev) +/* + * 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 < of_reset_control_count(dev->device_node); i++) { + for (i = 0; i < reset_control_get_count(dev); i++) { int ret; - rstc = of_reset_control_get_by_index(dev->device_node, i); + rstc = of_reset_control_get_by_index(dev->of_node, i); if (IS_ERR(rstc)) return PTR_ERR(rstc); @@ -364,7 +512,7 @@ int device_reset_all(struct device_d *dev) } EXPORT_SYMBOL_GPL(device_reset_all); -int device_reset_us(struct device_d *dev, int us) +int device_reset_us(struct device *dev, int us) { struct reset_control *rstc; int ret; |