// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Reset Controller framework
*
* Copyright 2013 Philipp Zabel, Pengutronix
*/
#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/reset.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
static DEFINE_MUTEX(reset_list_mutex);
static LIST_HEAD(reset_controller_list);
static DEFINE_MUTEX(reset_lookup_mutex);
static LIST_HEAD(reset_lookup_list);
/**
* struct reset_control - a reset control
* @rcdev: a pointer to the reset controller device
* this reset control belongs to
* @list: list entry for the rcdev's reset controller list
* @id: ID of the reset controller in the reset
* controller device
* @refcnt: Number of gets of this reset_control
* @acquired: Only one reset_control may be acquired for a given rcdev and id.
* @shared: Is this a shared (1), or an exclusive (0) reset_control?
* @array: Is this an array of reset controls (1)?
* @deassert_count: Number of times this reset line has been deasserted
* @triggered_count: Number of times this reset line has been reset. Currently
* only used for shared resets, which means that the value
* will be either 0 or 1.
*/
struct reset_control {
struct reset_controller_dev *rcdev;
struct list_head list;
unsigned int id;
struct kref refcnt;
bool acquired;
bool shared;
bool array;
atomic_t deassert_count;
atomic_t triggered_count;
};
/**
* 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[];
};
static const char *rcdev_name(struct reset_controller_dev *rcdev)
{
if (rcdev->dev)
return dev_name(rcdev->dev);
if (rcdev->of_node)
return rcdev->of_node->full_name;
return NULL;
}
/**
* of_reset_simple_xlate - translate reset_spec to the reset line number
* @rcdev: a pointer to the reset controller device
* @reset_spec: reset line specifier as found in the device tree
*
* This static translation function is used by default if of_xlate in
* :c:type:`reset_controller_dev` is not set. It is useful for all reset
* controllers with 1:1 mapping, where reset lines can be indexed by number
* without gaps.
*/
static int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec)
{
if (reset_spec->args[0] >= rcdev->nr_resets)
return -EINVAL;
return reset_spec->args[0];
}
/**
* reset_controller_register - register a reset controller device
* @rcdev: a pointer to the initialized reset controller device
*/
int reset_controller_register(struct reset_controller_dev *rcdev)
{
if (!rcdev->of_xlate) {
rcdev->of_reset_n_cells = 1;
rcdev->of_xlate = of_reset_simple_xlate;
}
INIT_LIST_HEAD(&rcdev->reset_control_head);
mutex_lock(&reset_list_mutex);
list_add(&rcdev->list, &reset_controller_list);
mutex_unlock(&reset_list_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(reset_controller_register);
/**
* reset_controller_unregister - unregister a reset controller device
* @rcdev: a pointer to the reset controller device
*/
void reset_controller_unregister(struct reset_controller_dev *rcdev)
{
mutex_lock(&reset_list_mutex);
list_del(&rcdev->list);
mutex_unlock(&reset_list_mutex);
}
EXPORT_SYMBOL_GPL(reset_controller_unregister);
static void devm_reset_controller_release(struct device *dev, void *res)
{
reset_controller_unregister(*(struct reset_controller_dev **)res);
}
/**
* devm_reset_controller_register - resource managed reset_controller_register()
* @dev: device that is registering this reset controller
* @rcdev: a pointer to the initialized reset controller device
*
* Managed reset_controller_register(). For reset controllers registered by
* this function, reset_controller_unregister() is automatically called on
* driver detach. See reset_controller_register() for more information.
*/
int devm_reset_controller_register(struct device *dev,
struct reset_controller_dev *rcdev)
{
struct reset_controller_dev **rcdevp;
int ret;
rcdevp = devres_alloc(devm_reset_controller_release, sizeof(*rcdevp),
GFP_KERNEL);
if (!rcdevp)
return -ENOMEM;
ret = reset_controller_register(rcdev);
if (ret) {
devres_free(rcdevp);
return ret;
}
*rcdevp = rcdev;
devres_add(dev, rcdevp);
return ret;
}
EXPORT_SYMBOL_GPL(devm_reset_controller_register);
/**
* reset_controller_add_lookup - register a set of lookup entries
* @lookup: array of reset lookup entries
* @num_entries: number of entries in the lookup array
*/
void reset_controller_add_lookup(struct reset_control_lookup *lookup,
unsigned int num_entries)
{
struct reset_control_lookup *entry;
unsigned int i;
mutex_lock(&reset_lookup_mutex);
for (i = 0; i < num_entries; i++) {
entry = &lookup[i];
if (!entry->