summaryrefslogtreecommitdiff
path: root/drivers/reset/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/reset/core.c')
-rw-r--r--drivers/reset/core.c338
1 files changed, 292 insertions, 46 deletions
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 4d5a78d3c085..22f67fc77ae5 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -5,14 +5,19 @@
* Copyright 2013 Philipp Zabel, Pengutronix
*/
#include <linux/atomic.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/kref.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/machine.h>
+#include <linux/idr.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/acpi.h>
+#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
@@ -23,6 +28,11 @@ static LIST_HEAD(reset_controller_list);
static DEFINE_MUTEX(reset_lookup_mutex);
static LIST_HEAD(reset_lookup_list);
+/* Protects reset_gpio_lookup_list */
+static DEFINE_MUTEX(reset_gpio_lookup_mutex);
+static LIST_HEAD(reset_gpio_lookup_list);
+static DEFINE_IDA(reset_gpio_ida);
+
/**
* struct reset_control - a reset control
* @rcdev: a pointer to the reset controller device
@@ -63,6 +73,16 @@ struct reset_control_array {
struct reset_control *rstc[] __counted_by(num_rstcs);
};
+/**
+ * struct reset_gpio_lookup - lookup key for ad-hoc created reset-gpio devices
+ * @of_args: phandle to the reset controller with all the args like GPIO number
+ * @list: list entry for the reset_gpio_lookup_list
+ */
+struct reset_gpio_lookup {
+ struct of_phandle_args of_args;
+ struct list_head list;
+};
+
static const char *rcdev_name(struct reset_controller_dev *rcdev)
{
if (rcdev->dev)
@@ -71,6 +91,9 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev)
if (rcdev->of_node)
return rcdev->of_node->full_name;
+ if (rcdev->of_args)
+ return rcdev->of_args->np->full_name;
+
return NULL;
}
@@ -99,6 +122,9 @@ static int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
*/
int reset_controller_register(struct reset_controller_dev *rcdev)
{
+ if (rcdev->of_node && rcdev->of_args)
+ return -EINVAL;
+
if (!rcdev->of_xlate) {
rcdev->of_reset_n_cells = 1;
rcdev->of_xlate = of_reset_simple_xlate;
@@ -747,12 +773,19 @@ EXPORT_SYMBOL_GPL(reset_control_bulk_release);
static struct reset_control *
__reset_control_get_internal(struct reset_controller_dev *rcdev,
- unsigned int index, bool shared, bool acquired)
+ unsigned int index, enum reset_control_flags flags)
{
+ bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED;
+ bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED;
struct reset_control *rstc;
lockdep_assert_held(&reset_list_mutex);
+ /* Expect callers to filter out OPTIONAL and DEASSERTED bits */
+ if (WARN_ON(flags & ~(RESET_CONTROL_FLAGS_BIT_SHARED |
+ RESET_CONTROL_FLAGS_BIT_ACQUIRED)))
+ return ERR_PTR(-EINVAL);
+
list_for_each_entry(rstc, &rcdev->reset_control_head, list) {
if (rstc->id == index) {
/*
@@ -786,6 +819,7 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev,
kref_init(&rstc->refcnt);
rstc->acquired = acquired;
rstc->shared = shared;
+ get_device(rcdev->dev);
return rstc;
}
@@ -800,6 +834,7 @@ static void __reset_control_release(struct kref *kref)
module_put(rstc->rcdev->owner);
list_del(&rstc->list);
+ put_device(rstc->rcdev->dev);
kfree(rstc);
}
@@ -813,12 +848,165 @@ static void __reset_control_put_internal(struct reset_control *rstc)
kref_put(&rstc->refcnt, __reset_control_release);
}
+static int __reset_add_reset_gpio_lookup(int id, struct device_node *np,
+ unsigned int gpio,
+ unsigned int of_flags)
+{
+ const struct fwnode_handle *fwnode = of_fwnode_handle(np);
+ unsigned int lookup_flags;
+ const char *label_tmp;
+
+ /*
+ * Later we map GPIO flags between OF and Linux, however not all
+ * constants from include/dt-bindings/gpio/gpio.h and
+ * include/linux/gpio/machine.h match each other.
+ */
+ if (of_flags > GPIO_ACTIVE_LOW) {
+ pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n",
+ of_flags, gpio);
+ return -EINVAL;
+ }
+
+ struct gpio_device *gdev __free(gpio_device_put) = gpio_device_find_by_fwnode(fwnode);
+ if (!gdev)
+ return -EPROBE_DEFER;
+
+ label_tmp = gpio_device_get_label(gdev);
+ if (!label_tmp)
+ return -EINVAL;
+
+ char *label __free(kfree) = kstrdup(label_tmp, GFP_KERNEL);
+ if (!label)
+ return -ENOMEM;
+
+ /* Size: one lookup entry plus sentinel */
+ struct gpiod_lookup_table *lookup __free(kfree) = kzalloc(struct_size(lookup, table, 2),
+ GFP_KERNEL);
+ if (!lookup)
+ return -ENOMEM;
+
+ lookup->dev_id = kasprintf(GFP_KERNEL, "reset-gpio.%d", id);
+ if (!lookup->dev_id)
+ return -ENOMEM;
+
+ lookup_flags = GPIO_PERSISTENT;
+ lookup_flags |= of_flags & GPIO_ACTIVE_LOW;
+ lookup->table[0] = GPIO_LOOKUP(no_free_ptr(label), gpio, "reset",
+ lookup_flags);
+
+ /* Not freed on success, because it is persisent subsystem data. */
+ gpiod_add_lookup_table(no_free_ptr(lookup));
+
+ return 0;
+}
+
+/*
+ * @args: phandle to the GPIO provider with all the args like GPIO number
+ */
+static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
+{
+ struct reset_gpio_lookup *rgpio_dev;
+ struct platform_device *pdev;
+ int id, ret;
+
+ /*
+ * Currently only #gpio-cells=2 is supported with the meaning of:
+ * args[0]: GPIO number
+ * args[1]: GPIO flags
+ * TODO: Handle other cases.
+ */
+ if (args->args_count != 2)
+ return -ENOENT;
+
+ /*
+ * Registering reset-gpio device might cause immediate
+ * bind, resulting in its probe() registering new reset controller thus
+ * taking reset_list_mutex lock via reset_controller_register().
+ */
+ lockdep_assert_not_held(&reset_list_mutex);
+
+ guard(mutex)(&reset_gpio_lookup_mutex);
+
+ list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) {
+ if (args->np == rgpio_dev->of_args.np) {
+ if (of_phandle_args_equal(args, &rgpio_dev->of_args))
+ return 0; /* Already on the list, done */
+ }
+ }
+
+ id = ida_alloc(&reset_gpio_ida, GFP_KERNEL);
+ if (id < 0)
+ return id;
+
+ /* Not freed on success, because it is persisent subsystem data. */
+ rgpio_dev = kzalloc(sizeof(*rgpio_dev), GFP_KERNEL);
+ if (!rgpio_dev) {
+ ret = -ENOMEM;
+ goto err_ida_free;
+ }
+
+ ret = __reset_add_reset_gpio_lookup(id, args->np, args->args[0],
+ args->args[1]);
+ if (ret < 0)
+ goto err_kfree;
+
+ rgpio_dev->of_args = *args;
+ /*
+ * We keep the device_node reference, but of_args.np is put at the end
+ * of __of_reset_control_get(), so get it one more time.
+ * Hold reference as long as rgpio_dev memory is valid.
+ */
+ of_node_get(rgpio_dev->of_args.np);
+ pdev = platform_device_register_data(NULL, "reset-gpio", id,
+ &rgpio_dev->of_args,
+ sizeof(rgpio_dev->of_args));
+ ret = PTR_ERR_OR_ZERO(pdev);
+ if (ret)
+ goto err_put;
+
+ list_add(&rgpio_dev->list, &reset_gpio_lookup_list);
+
+ return 0;
+
+err_put:
+ of_node_put(rgpio_dev->of_args.np);
+err_kfree:
+ kfree(rgpio_dev);
+err_ida_free:
+ ida_free(&reset_gpio_ida, id);
+
+ return ret;
+}
+
+static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_args *args,
+ bool gpio_fallback)
+{
+ struct reset_controller_dev *rcdev;
+
+ lockdep_assert_held(&reset_list_mutex);
+
+ list_for_each_entry(rcdev, &reset_controller_list, list) {
+ if (gpio_fallback) {
+ if (rcdev->of_args && of_phandle_args_equal(args,
+ rcdev->of_args))
+ return rcdev;
+ } else {
+ if (args->np == rcdev->of_node)
+ return rcdev;
+ }
+ }
+
+ return NULL;
+}
+
struct reset_control *
__of_reset_control_get(struct device_node *node, const char *id, int index,
- bool shared, bool optional, bool acquired)
+ enum reset_control_flags flags)
{
+ bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
+ bool gpio_fallback = false;
struct reset_control *rstc;
- struct reset_controller_dev *r, *rcdev;
+ struct reset_controller_dev *rcdev;
struct of_phandle_args args;
int rstc_id;
int ret;
@@ -839,39 +1027,54 @@ __of_reset_control_get(struct device_node *node, const char *id, int index,
index, &args);
if (ret == -EINVAL)
return ERR_PTR(ret);
- if (ret)
- return optional ? NULL : ERR_PTR(ret);
+ if (ret) {
+ if (!IS_ENABLED(CONFIG_RESET_GPIO))
+ return optional ? NULL : ERR_PTR(ret);
- mutex_lock(&reset_list_mutex);
- rcdev = NULL;
- list_for_each_entry(r, &reset_controller_list, list) {
- if (args.np == r->of_node) {
- rcdev = r;
- break;
+ /*
+ * There can be only one reset-gpio for regular devices, so
+ * don't bother with the "reset-gpios" phandle index.
+ */
+ ret = of_parse_phandle_with_args(node, "reset-gpios", "#gpio-cells",
+ 0, &args);
+ if (ret)
+ return optional ? NULL : ERR_PTR(ret);
+
+ gpio_fallback = true;
+
+ ret = __reset_add_reset_gpio_device(&args);
+ if (ret) {
+ rstc = ERR_PTR(ret);
+ goto out_put;
}
}
+ mutex_lock(&reset_list_mutex);
+ rcdev = __reset_find_rcdev(&args, gpio_fallback);
if (!rcdev) {
rstc = ERR_PTR(-EPROBE_DEFER);
- goto out;
+ goto out_unlock;
}
if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) {
rstc = ERR_PTR(-EINVAL);
- goto out;
+ goto out_unlock;
}
rstc_id = rcdev->of_xlate(rcdev, &args);
if (rstc_id < 0) {
rstc = ERR_PTR(rstc_id);
- goto out;
+ goto out_unlock;
}
+ flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL;
+
/* reset_list_mutex also protects the rcdev's reset_control list */
- rstc = __reset_control_get_internal(rcdev, rstc_id, shared, acquired);
+ rstc = __reset_control_get_internal(rcdev, rstc_id, flags);
-out:
+out_unlock:
mutex_unlock(&reset_list_mutex);
+out_put:
of_node_put(args.np);
return rstc;
@@ -898,8 +1101,9 @@ __reset_controller_by_name(const char *name)
static struct reset_control *
__reset_control_get_from_lookup(struct device *dev, const char *con_id,
- bool shared, bool optional, bool acquired)
+ enum reset_control_flags flags)
{
+ bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
const struct reset_control_lookup *lookup;
struct reset_controller_dev *rcdev;
const char *dev_id = dev_name(dev);
@@ -923,9 +1127,11 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id,
return ERR_PTR(-EPROBE_DEFER);
}
+ flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL;
+
rstc = __reset_control_get_internal(rcdev,
lookup->index,
- shared, acquired);
+ flags);
mutex_unlock(&reset_list_mutex);
break;
}
@@ -940,30 +1146,29 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id,
}
struct reset_control *__reset_control_get(struct device *dev, const char *id,
- int index, bool shared, bool optional,
- bool acquired)
+ int index, enum reset_control_flags flags)
{
+ bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED;
+ bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED;
+
if (WARN_ON(shared && acquired))
return ERR_PTR(-EINVAL);
if (dev->of_node)
- return __of_reset_control_get(dev->of_node, id, index, shared,
- optional, acquired);
+ return __of_reset_control_get(dev->of_node, id, index, flags);
- return __reset_control_get_from_lookup(dev, id, shared, optional,
- acquired);
+ return __reset_control_get_from_lookup(dev, id, flags);
}
EXPORT_SYMBOL_GPL(__reset_control_get);
int __reset_control_bulk_get(struct device *dev, int num_rstcs,
struct reset_control_bulk_data *rstcs,
- bool shared, bool optional, bool acquired)
+ enum reset_control_flags flags)
{
int ret, i;
for (i = 0; i < num_rstcs; i++) {
- rstcs[i].rstc = __reset_control_get(dev, rstcs[i].id, 0,
- shared, optional, acquired);
+ rstcs[i].rstc = __reset_control_get(dev, rstcs[i].id, 0, flags);
if (IS_ERR(rstcs[i].rstc)) {
ret = PTR_ERR(rstcs[i].rstc);
goto err;
@@ -1031,23 +1236,46 @@ static void devm_reset_control_release(struct device *dev, void *res)
reset_control_put(*(struct reset_control **)res);
}
+static void devm_reset_control_release_deasserted(struct device *dev, void *res)
+{
+ struct reset_control *rstc = *(struct reset_control **)res;
+
+ reset_control_assert(rstc);
+ reset_control_put(rstc);
+}
+
struct reset_control *
__devm_reset_control_get(struct device *dev, const char *id, int index,
- bool shared, bool optional, bool acquired)
+ enum reset_control_flags flags)
{
struct reset_control **ptr, *rstc;
+ bool deasserted = flags & RESET_CONTROL_FLAGS_BIT_DEASSERTED;
- ptr = devres_alloc(devm_reset_control_release, sizeof(*ptr),
+ ptr = devres_alloc(deasserted ? devm_reset_control_release_deasserted :
+ devm_reset_control_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
- rstc = __reset_control_get(dev, id, index, shared, optional, acquired);
+ flags &= ~RESET_CONTROL_FLAGS_BIT_DEASSERTED;
+
+ rstc = __reset_control_get(dev, id, index, flags);
if (IS_ERR_OR_NULL(rstc)) {
devres_free(ptr);
return rstc;
}
+ if (deasserted) {
+ int ret;
+
+ ret = reset_control_deassert(rstc);
+ if (ret) {
+ reset_control_put(rstc);
+ devres_free(ptr);
+ return ERR_PTR(ret);
+ }
+ }
+
*ptr = rstc;
devres_add(dev, ptr);
@@ -1067,24 +1295,45 @@ static void devm_reset_control_bulk_release(struct device *dev, void *res)
reset_control_bulk_put(devres->num_rstcs, devres->rstcs);
}
+static void devm_reset_control_bulk_release_deasserted(struct device *dev, void *res)
+{
+ struct reset_control_bulk_devres *devres = res;
+
+ reset_control_bulk_assert(devres->num_rstcs, devres->rstcs);
+ reset_control_bulk_put(devres->num_rstcs, devres->rstcs);
+}
+
int __devm_reset_control_bulk_get(struct device *dev, int num_rstcs,
struct reset_control_bulk_data *rstcs,
- bool shared, bool optional, bool acquired)
+ enum reset_control_flags flags)
{
struct reset_control_bulk_devres *ptr;
+ bool deasserted = flags & RESET_CONTROL_FLAGS_BIT_DEASSERTED;
int ret;
- ptr = devres_alloc(devm_reset_control_bulk_release, sizeof(*ptr),
+ ptr = devres_alloc(deasserted ? devm_reset_control_bulk_release_deasserted :
+ devm_reset_control_bulk_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return -ENOMEM;
- ret = __reset_control_bulk_get(dev, num_rstcs, rstcs, shared, optional, acquired);
+ flags &= ~RESET_CONTROL_FLAGS_BIT_DEASSERTED;
+
+ ret = __reset_control_bulk_get(dev, num_rstcs, rstcs, flags);
if (ret < 0) {
devres_free(ptr);
return ret;
}
+ if (deasserted) {
+ ret = reset_control_bulk_deassert(num_rstcs, rstcs);
+ if (ret) {
+ reset_control_bulk_put(num_rstcs, rstcs);
+ devres_free(ptr);
+ return ret;
+ }
+ }
+
ptr->num_rstcs = num_rstcs;
ptr->rstcs = rstcs;
devres_add(dev, ptr);
@@ -1105,6 +1354,7 @@ EXPORT_SYMBOL_GPL(__devm_reset_control_bulk_get);
*/
int __device_reset(struct device *dev, bool optional)
{
+ enum reset_control_flags flags;
struct reset_control *rstc;
int ret;
@@ -1120,7 +1370,8 @@ int __device_reset(struct device *dev, bool optional)
}
#endif
- rstc = __reset_control_get(dev, NULL, 0, 0, optional, true);
+ flags = optional ? RESET_CONTROL_OPTIONAL_EXCLUSIVE : RESET_CONTROL_EXCLUSIVE;
+ rstc = __reset_control_get(dev, NULL, 0, flags);
if (IS_ERR(rstc))
return PTR_ERR(rstc);
@@ -1163,17 +1414,14 @@ static int of_reset_control_get_count(struct device_node *node)
* device node.
*
* @np: device node for the device that requests the reset controls array
- * @shared: whether reset controls are shared or not
- * @optional: whether it is optional to get the reset controls
- * @acquired: only one reset control may be acquired for a given controller
- * and ID
+ * @flags: whether reset controls are shared, optional, acquired
*
* Returns pointer to allocated reset_control on success or error on failure
*/
struct reset_control *
-of_reset_control_array_get(struct device_node *np, bool shared, bool optional,
- bool acquired)
+of_reset_control_array_get(struct device_node *np, enum reset_control_flags flags)
{
+ bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
struct reset_control_array *resets;
struct reset_control *rstc;
int num, i;
@@ -1188,8 +1436,7 @@ of_reset_control_array_get(struct device_node *np, bool shared, bool optional,
resets->num_rstcs = num;
for (i = 0; i < num; i++) {
- rstc = __of_reset_control_get(np, NULL, i, shared, optional,
- acquired);
+ rstc = __of_reset_control_get(np, NULL, i, flags);
if (IS_ERR(rstc))
goto err_rst;
resets->rstc[i] = rstc;
@@ -1214,8 +1461,7 @@ EXPORT_SYMBOL_GPL(of_reset_control_array_get);
* devm_reset_control_array_get - Resource managed reset control array get
*
* @dev: device that requests the list of reset controls
- * @shared: whether reset controls are shared or not
- * @optional: whether it is optional to get the reset controls
+ * @flags: whether reset controls are shared, optional, acquired
*
* The reset control array APIs are intended for a list of resets
* that just have to be asserted or deasserted, without any
@@ -1224,7 +1470,7 @@ EXPORT_SYMBOL_GPL(of_reset_control_array_get);
* Returns pointer to allocated reset_control on success or error on failure
*/
struct reset_control *
-devm_reset_control_array_get(struct device *dev, bool shared, bool optional)
+devm_reset_control_array_get(struct device *dev, enum reset_control_flags flags)
{
struct reset_control **ptr, *rstc;
@@ -1233,7 +1479,7 @@ devm_reset_control_array_get(struct device *dev, bool shared, bool optional)
if (!ptr)
return ERR_PTR(-ENOMEM);
- rstc = of_reset_control_array_get(dev->of_node, shared, optional, true);
+ rstc = of_reset_control_array_get(dev->of_node, flags);
if (IS_ERR_OR_NULL(rstc)) {
devres_free(ptr);
return rstc;