From 4e11f848c65b1c87782cb232a6e3b47a9d4c1f98 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 23 Oct 2012 15:45:56 -0600 Subject: dt: describe base reset signal binding This binding is intended to represent the hardware reset signals present internally in most IC (SoC, FPGA, ...) designs. It consists of a binding for a reset controller device (provider), and a pair of properties, "resets" and "reset-names", to link a device node (consumer) to its reset controller via phandle, similarly to the clock and interrupt bindings. The reset controller has all information necessary to reset the consumer device. That could be provided via device tree, or it could be implemented in hardware. The aim is to enable device drivers to request a framework API to issue a reset simply by providing their struct device pointer as the most common case. Signed-off-by: Stephen Warren Signed-off-by: Philipp Zabel Reviewed-by: Shawn Guo Reviewed-by: Marek Vasut Reviewed-by: Pavel Machek Acked-by: Rob Herring diff --git a/Documentation/devicetree/bindings/reset/reset.txt b/Documentation/devicetree/bindings/reset/reset.txt new file mode 100644 index 0000000..31db6ff --- /dev/null +++ b/Documentation/devicetree/bindings/reset/reset.txt @@ -0,0 +1,75 @@ += Reset Signal Device Tree Bindings = + +This binding is intended to represent the hardware reset signals present +internally in most IC (SoC, FPGA, ...) designs. Reset signals for whole +standalone chips are most likely better represented as GPIOs, although there +are likely to be exceptions to this rule. + +Hardware blocks typically receive a reset signal. This signal is generated by +a reset provider (e.g. power management or clock module) and received by a +reset consumer (the module being reset, or a module managing when a sub- +ordinate module is reset). This binding exists to represent the provider and +consumer, and provide a way to couple the two together. + +A reset signal is represented by the phandle of the provider, plus a reset +specifier - a list of DT cells that represents the reset signal within the +provider. The length (number of cells) and semantics of the reset specifier +are dictated by the binding of the reset provider, although common schemes +are described below. + +A word on where to place reset signal consumers in device tree: It is possible +in hardware for a reset signal to affect multiple logically separate HW blocks +at once. In this case, it would be unwise to represent this reset signal in +the DT node of each affected HW block, since if activated, an unrelated block +may be reset. Instead, reset signals should be represented in the DT node +where it makes most sense to control it; this may be a bus node if all +children of the bus are affected by the reset signal, or an individual HW +block node for dedicated reset signals. The intent of this binding is to give +appropriate software access to the reset signals in order to manage the HW, +rather than to slavishly enumerate the reset signal that affects each HW +block. + += Reset providers = + +Required properties: +#reset-cells: Number of cells in a reset specifier; Typically 0 for nodes + with a single reset output and 1 for nodes with multiple + reset outputs. + +For example: + + rst: reset-controller { + #reset-cells = <1>; + }; + += Reset consumers = + +Required properties: +resets: List of phandle and reset specifier pairs, one pair + for each reset signal that affects the device, or that the + device manages. Note: if the reset provider specifies '0' for + #reset-cells, then only the phandle portion of the pair will + appear. + +Optional properties: +reset-names: List of reset signal name strings sorted in the same order as + the resets property. Consumers drivers will use reset-names to + match reset signal names with reset specifiers. + +For example: + + device { + resets = <&rst 20>; + reset-names = "reset"; + }; + +This represents a device with a single reset signal named "reset". + + bus { + resets = <&rst 10> <&rst 11> <&rst 12> <&rst 11>; + reset-names = "i2s1", "i2s2", "dma", "mixer"; + }; + +This represents a bus that controls the reset signal of each of four sub- +ordinate devices. Consider for example a bus that fails to operate unless no +child device has reset asserted. -- cgit v0.10.2 From 61fc41317666be400802ac793f47de816ef7bd57 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 19 Nov 2012 17:23:13 +0100 Subject: reset: Add reset controller API This adds a simple API for devices to request being reset by separate reset controller hardware and implements the reset signal device tree binding. Signed-off-by: Philipp Zabel Reviewed-by: Stephen Warren Reviewed-by: Shawn Guo Reviewed-by: Marek Vasut Reviewed-by: Pavel Machek diff --git a/drivers/Kconfig b/drivers/Kconfig index 202fa6d..847f8e3 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -162,4 +162,6 @@ source "drivers/irqchip/Kconfig" source "drivers/ipack/Kconfig" +source "drivers/reset/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index dce39a9..1a64c4c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -37,6 +37,9 @@ obj-$(CONFIG_XEN) += xen/ # regulators early, since some subsystems rely on them to initialize obj-$(CONFIG_REGULATOR) += regulator/ +# reset controllers early, since gpu drivers might rely on them to initialize +obj-$(CONFIG_RESET_CONTROLLER) += reset/ + # tty/ comes before char/ so that the VT console is the boot-time # default. obj-y += tty/ diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig new file mode 100644 index 0000000..c9d04f7 --- /dev/null +++ b/drivers/reset/Kconfig @@ -0,0 +1,13 @@ +config ARCH_HAS_RESET_CONTROLLER + bool + +menuconfig RESET_CONTROLLER + bool "Reset Controller Support" + default y if ARCH_HAS_RESET_CONTROLLER + help + Generic Reset Controller support. + + This framework is designed to abstract reset handling of devices + via GPIOs or SoC-internal reset controller modules. + + If unsure, say no. diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile new file mode 100644 index 0000000..1e2d83f --- /dev/null +++ b/drivers/reset/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_RESET_CONTROLLER) += core.o diff --git a/drivers/reset/core.c b/drivers/reset/core.c new file mode 100644 index 0000000..a258277 --- /dev/null +++ b/drivers/reset/core.c @@ -0,0 +1,297 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(reset_controller_list_mutex); +static LIST_HEAD(reset_controller_list); + +/** + * struct reset_control - a reset control + * @rcdev: a pointer to the reset controller device + * this reset control belongs to + * @id: ID of the reset controller in the reset + * controller device + */ +struct reset_control { + struct reset_controller_dev *rcdev; + struct device *dev; + unsigned int id; +}; + +/** + * 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 + * @flags: a flags pointer to fill in (optional) + * + * This simple translation function should be used for reset controllers + * with 1:1 mapping, where reset lines can be indexed by number without gaps. + */ +int of_reset_simple_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells)) + return -EINVAL; + + if (reset_spec->args[0] >= rcdev->nr_resets) + return -EINVAL; + + return reset_spec->args[0]; +} +EXPORT_SYMBOL_GPL(of_reset_simple_xlate); + +/** + * 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; + } + + mutex_lock(&reset_controller_list_mutex); + list_add(&rcdev->list, &reset_controller_list); + mutex_unlock(&reset_controller_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_controller_list_mutex); + list_del(&rcdev->list); + mutex_unlock(&reset_controller_list_mutex); +} +EXPORT_SYMBOL_GPL(reset_controller_unregister); + +/** + * reset_control_reset - reset the controlled device + * @rstc: reset controller + */ +int reset_control_reset(struct reset_control *rstc) +{ + if (rstc->rcdev->ops->reset) + return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); + + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(reset_control_reset); + +/** + * reset_control_assert - asserts the reset line + * @rstc: reset controller + */ +int reset_control_assert(struct reset_control *rstc) +{ + if (rstc->rcdev->ops->assert) + return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); + + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(reset_control_assert); + +/** + * reset_control_deassert - deasserts the reset line + * @rstc: reset controller + */ +int reset_control_deassert(struct reset_control *rstc) +{ + if (rstc->rcdev->ops->deassert) + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); + + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(reset_control_deassert); + +/** + * reset_control_get - Lookup and obtain a reference to a reset controller. + * @dev: 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 *reset_control_get(struct device *dev, const char *id) +{ + struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER); + struct reset_controller_dev *r, *rcdev; + struct of_phandle_args args; + int index = 0; + int rstc_id; + int ret; + + if (!dev) + return ERR_PTR(-EINVAL); + + if (id) + index = of_property_match_string(dev->of_node, + "reset-names", id); + ret = of_parse_phandle_with_args(dev->of_node, "resets", "#reset-cells", + index, &args); + if (ret) + return ERR_PTR(ret); + + mutex_lock(&reset_controller_list_mutex); + rcdev = NULL; + list_for_each_entry(r, &reset_controller_list, list) { + if (args.np == r->of_node) { + rcdev = r; + break; + } + } + of_node_put(args.np); + + if (!rcdev) { + mutex_unlock(&reset_controller_list_mutex); + return ERR_PTR(-ENODEV); + } + + rstc_id = rcdev->of_xlate(rcdev, &args); + if (rstc_id < 0) { + mutex_unlock(&reset_controller_list_mutex); + return ERR_PTR(rstc_id); + } + + try_module_get(rcdev->owner); + mutex_unlock(&reset_controller_list_mutex); + + rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); + if (!rstc) { + module_put(rstc->rcdev->owner); + return ERR_PTR(-ENOMEM); + } + + rstc->dev = dev; + rstc->rcdev = rcdev; + rstc->id = rstc_id; + + return rstc; +} +EXPORT_SYMBOL_GPL(reset_control_get); + +/** + * reset_control_put - free the reset controller + * @rstc: reset controller + */ + +void reset_control_put(struct reset_control *rstc) +{ + if (IS_ERR(rstc)) + return; + + module_put(rstc->rcdev->owner); + kfree(rstc); +} +EXPORT_SYMBOL_GPL(reset_control_put); + +static void devm_reset_control_release(struct device *dev, void *res) +{ + reset_control_put(*(struct reset_control **)res); +} + +/** + * devm_reset_control_get - resource managed reset_control_get() + * @dev: device to be reset by the controller + * @id: reset line name + * + * Managed reset_control_get(). For reset controllers returned from this + * function, reset_control_put() is called automatically on driver detach. + * See reset_control_get() for more information. + */ +struct reset_control *devm_reset_control_get(struct device *dev, const char *id) +{ + struct reset_control **ptr, *rstc; + + ptr = devres_alloc(devm_reset_control_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + rstc = reset_control_get(dev, id); + if (!IS_ERR(rstc)) { + *ptr = rstc; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return rstc; +} +EXPORT_SYMBOL_GPL(devm_reset_control_get); + +static int devm_reset_control_match(struct device *dev, void *res, void *data) +{ + struct reset_control **rstc = res; + if (WARN_ON(!rstc || !*rstc)) + return 0; + return *rstc == data; +} + +/** + * devm_reset_control_put - resource managed reset_control_put() + * @rstc: reset controller to free + * + * Deallocate a reset control allocated withd devm_reset_control_get(). + * This function will not need to be called normally, as devres will take + * care of freeing the resource. + */ +void devm_reset_control_put(struct reset_control *rstc) +{ + int ret; + + ret = devres_release(rstc->dev, devm_reset_control_release, + devm_reset_control_match, rstc); + if (ret) + WARN_ON(ret); +} +EXPORT_SYMBOL_GPL(devm_reset_control_put); + +/** + * device_reset - find reset controller associated with the device + * and perform reset + * @dev: device to be reset by the controller + * + * Convenience wrapper for reset_control_get() and reset_control_reset(). + * This is useful for the common case of devices with single, dedicated reset + * lines. + */ +int device_reset(struct device *dev) +{ + struct reset_control *rstc; + int ret; + + rstc = reset_control_get(dev, NULL); + if (IS_ERR(rstc)) + return PTR_ERR(rstc); + + ret = reset_control_reset(rstc); + + reset_control_put(rstc); + + return ret; +} +EXPORT_SYMBOL_GPL(device_reset); diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h new file mode 100644 index 0000000..2f61311 --- /dev/null +++ b/include/linux/reset-controller.h @@ -0,0 +1,51 @@ +#ifndef _LINUX_RESET_CONTROLLER_H_ +#define _LINUX_RESET_CONTROLLER_H_ + +#include + +struct reset_controller_dev; + +/** + * struct reset_control_ops + * + * @reset: for self-deasserting resets, does all necessary + * things to reset the device + * @assert: manually assert the reset line, if supported + * @deassert: manually deassert the reset line, if supported + */ +struct reset_control_ops { + int (*reset)(struct reset_controller_dev *rcdev, unsigned long id); + int (*assert)(struct reset_controller_dev *rcdev, unsigned long id); + int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id); +}; + +struct module; +struct device_node; + +/** + * struct reset_controller_dev - reset controller entity that might + * provide multiple reset controls + * @ops: a pointer to device specific struct reset_control_ops + * @owner: kernel module of the reset controller driver + * @list: internal list of reset controller devices + * @of_node: corresponding device tree node as phandle target + * @of_reset_n_cells: number of cells in reset line specifiers + * @of_xlate: translation function to translate from specifier as found in the + * device tree to id as given to the reset control ops + * @nr_resets: number of reset controls in this reset controller device + */ +struct reset_controller_dev { + struct reset_control_ops *ops; + struct module *owner; + struct list_head list; + struct device_node *of_node; + int of_reset_n_cells; + int (*of_xlate)(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec); + unsigned int nr_resets; +}; + +int reset_controller_register(struct reset_controller_dev *rcdev); +void reset_controller_unregister(struct reset_controller_dev *rcdev); + +#endif diff --git a/include/linux/reset.h b/include/linux/reset.h new file mode 100644 index 0000000..6082247 --- /dev/null +++ b/include/linux/reset.h @@ -0,0 +1,17 @@ +#ifndef _LINUX_RESET_H_ +#define _LINUX_RESET_H_ + +struct device; +struct reset_control; + +int reset_control_reset(struct reset_control *rstc); +int reset_control_assert(struct reset_control *rstc); +int reset_control_deassert(struct reset_control *rstc); + +struct reset_control *reset_control_get(struct device *dev, const char *id); +void reset_control_put(struct reset_control *rstc); +struct reset_control *devm_reset_control_get(struct device *dev, const char *id); + +int device_reset(struct device *dev); + +#endif -- cgit v0.10.2 From 6034bb22d8387708075c083385e5d2e1072a4f33 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 3 Apr 2013 08:02:53 +0300 Subject: reset: NULL deref on allocation failure "rstc" is NULL here and we should use "rcdev" instead of "rstc->rcdev". Signed-off-by: Dan Carpenter Signed-off-by: Philipp Zabel diff --git a/drivers/reset/core.c b/drivers/reset/core.c index a258277..d1b6089 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -181,7 +181,7 @@ struct reset_control *reset_control_get(struct device *dev, const char *id) rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); if (!rstc) { - module_put(rstc->rcdev->owner); + module_put(rcdev->owner); return ERR_PTR(-ENOMEM); } -- cgit v0.10.2