summaryrefslogtreecommitdiffstats
path: root/drivers/reset/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/reset/core.c')
-rw-r--r--drivers/reset/core.c196
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;