mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 11:21:26 -04:00
reset: gpio: use software nodes to setup the GPIO lookup
GPIO machine lookup is a nice mechanism for associating GPIOs with consumers if we don't know what kind of device the GPIO provider is or when it will become available. However in the case of the reset-gpio, we are already holding a reference to the device and so can reference its firmware node. Let's setup a software node that references the relevant GPIO and attach it to the auxiliary device we're creating. Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de> Acked-by: Linus Walleij <linus.walleij@linaro.org> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
This commit is contained in:
committed by
Philipp Zabel
parent
109ce747ac
commit
5fc4e4cf7a
@@ -14,6 +14,7 @@
|
|||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/gpio/driver.h>
|
#include <linux/gpio/driver.h>
|
||||||
#include <linux/gpio/machine.h>
|
#include <linux/gpio/machine.h>
|
||||||
|
#include <linux/gpio/property.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/kref.h>
|
#include <linux/kref.h>
|
||||||
@@ -77,10 +78,12 @@ struct reset_control_array {
|
|||||||
/**
|
/**
|
||||||
* struct reset_gpio_lookup - lookup key for ad-hoc created reset-gpio devices
|
* 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
|
* @of_args: phandle to the reset controller with all the args like GPIO number
|
||||||
|
* @swnode: Software node containing the reference to the GPIO provider
|
||||||
* @list: list entry for the reset_gpio_lookup_list
|
* @list: list entry for the reset_gpio_lookup_list
|
||||||
*/
|
*/
|
||||||
struct reset_gpio_lookup {
|
struct reset_gpio_lookup {
|
||||||
struct of_phandle_args of_args;
|
struct of_phandle_args of_args;
|
||||||
|
struct fwnode_handle *swnode;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -849,52 +852,45 @@ static void __reset_control_put_internal(struct reset_control *rstc)
|
|||||||
kref_put(&rstc->refcnt, __reset_control_release);
|
kref_put(&rstc->refcnt, __reset_control_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __reset_add_reset_gpio_lookup(struct gpio_device *gdev, int id,
|
static void reset_gpio_aux_device_release(struct device *dev)
|
||||||
struct device_node *np,
|
|
||||||
unsigned int gpio,
|
|
||||||
unsigned int of_flags)
|
|
||||||
{
|
{
|
||||||
unsigned int lookup_flags;
|
struct auxiliary_device *adev = to_auxiliary_dev(dev);
|
||||||
const char *label_tmp;
|
|
||||||
|
|
||||||
/*
|
kfree(adev);
|
||||||
* 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.
|
static int reset_add_gpio_aux_device(struct device *parent,
|
||||||
*/
|
struct fwnode_handle *swnode,
|
||||||
if (of_flags > GPIO_ACTIVE_LOW) {
|
int id, void *pdata)
|
||||||
pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n",
|
{
|
||||||
of_flags, gpio);
|
struct auxiliary_device *adev;
|
||||||
return -EINVAL;
|
int ret;
|
||||||
|
|
||||||
|
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
|
||||||
|
if (!adev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
adev->id = id;
|
||||||
|
adev->name = "gpio";
|
||||||
|
adev->dev.parent = parent;
|
||||||
|
adev->dev.platform_data = pdata;
|
||||||
|
adev->dev.release = reset_gpio_aux_device_release;
|
||||||
|
device_set_node(&adev->dev, swnode);
|
||||||
|
|
||||||
|
ret = auxiliary_device_init(adev);
|
||||||
|
if (ret) {
|
||||||
|
kfree(adev);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
label_tmp = gpio_device_get_label(gdev);
|
ret = __auxiliary_device_add(adev, "reset");
|
||||||
if (!label_tmp)
|
if (ret) {
|
||||||
return -EINVAL;
|
auxiliary_device_uninit(adev);
|
||||||
|
kfree(adev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
char *label __free(kfree) = kstrdup(label_tmp, GFP_KERNEL);
|
return ret;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -902,8 +898,10 @@ static int __reset_add_reset_gpio_lookup(struct gpio_device *gdev, int id,
|
|||||||
*/
|
*/
|
||||||
static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
|
static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
|
||||||
{
|
{
|
||||||
|
struct property_entry properties[2] = { };
|
||||||
|
unsigned int offset, of_flags, lflags;
|
||||||
struct reset_gpio_lookup *rgpio_dev;
|
struct reset_gpio_lookup *rgpio_dev;
|
||||||
struct auxiliary_device *adev;
|
struct device *parent;
|
||||||
int id, ret;
|
int id, ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -922,6 +920,23 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
|
|||||||
*/
|
*/
|
||||||
lockdep_assert_not_held(&reset_list_mutex);
|
lockdep_assert_not_held(&reset_list_mutex);
|
||||||
|
|
||||||
|
offset = args->args[0];
|
||||||
|
of_flags = args->args[1];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* FIXME: Find a better way of translating OF flags to GPIO lookup
|
||||||
|
* flags.
|
||||||
|
*/
|
||||||
|
if (of_flags > GPIO_ACTIVE_LOW) {
|
||||||
|
pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n",
|
||||||
|
of_flags, offset);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
struct gpio_device *gdev __free(gpio_device_put) =
|
struct gpio_device *gdev __free(gpio_device_put) =
|
||||||
gpio_device_find_by_fwnode(of_fwnode_handle(args->np));
|
gpio_device_find_by_fwnode(of_fwnode_handle(args->np));
|
||||||
if (!gdev)
|
if (!gdev)
|
||||||
@@ -936,6 +951,10 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lflags = GPIO_PERSISTENT | (of_flags & GPIO_ACTIVE_LOW);
|
||||||
|
parent = gpio_device_to_device(gdev);
|
||||||
|
properties[0] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags);
|
||||||
|
|
||||||
id = ida_alloc(&reset_gpio_ida, GFP_KERNEL);
|
id = ida_alloc(&reset_gpio_ida, GFP_KERNEL);
|
||||||
if (id < 0)
|
if (id < 0)
|
||||||
return id;
|
return id;
|
||||||
@@ -947,11 +966,6 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
|
|||||||
goto err_ida_free;
|
goto err_ida_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = __reset_add_reset_gpio_lookup(gdev, id, args->np, args->args[0],
|
|
||||||
args->args[1]);
|
|
||||||
if (ret < 0)
|
|
||||||
goto err_kfree;
|
|
||||||
|
|
||||||
rgpio_dev->of_args = *args;
|
rgpio_dev->of_args = *args;
|
||||||
/*
|
/*
|
||||||
* We keep the device_node reference, but of_args.np is put at the end
|
* We keep the device_node reference, but of_args.np is put at the end
|
||||||
@@ -959,19 +973,26 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
|
|||||||
* Hold reference as long as rgpio_dev memory is valid.
|
* Hold reference as long as rgpio_dev memory is valid.
|
||||||
*/
|
*/
|
||||||
of_node_get(rgpio_dev->of_args.np);
|
of_node_get(rgpio_dev->of_args.np);
|
||||||
adev = auxiliary_device_create(gpio_device_to_device(gdev), "reset",
|
|
||||||
"gpio", &rgpio_dev->of_args, id);
|
rgpio_dev->swnode = fwnode_create_software_node(properties, NULL);
|
||||||
ret = PTR_ERR_OR_ZERO(adev);
|
if (IS_ERR(rgpio_dev->swnode)) {
|
||||||
|
ret = PTR_ERR(rgpio_dev->swnode);
|
||||||
|
goto err_put_of_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = reset_add_gpio_aux_device(parent, rgpio_dev->swnode, id,
|
||||||
|
&rgpio_dev->of_args);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_put;
|
goto err_del_swnode;
|
||||||
|
|
||||||
list_add(&rgpio_dev->list, &reset_gpio_lookup_list);
|
list_add(&rgpio_dev->list, &reset_gpio_lookup_list);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_put:
|
err_del_swnode:
|
||||||
|
fwnode_remove_software_node(rgpio_dev->swnode);
|
||||||
|
err_put_of_node:
|
||||||
of_node_put(rgpio_dev->of_args.np);
|
of_node_put(rgpio_dev->of_args.np);
|
||||||
err_kfree:
|
|
||||||
kfree(rgpio_dev);
|
kfree(rgpio_dev);
|
||||||
err_ida_free:
|
err_ida_free:
|
||||||
ida_free(&reset_gpio_ida, id);
|
ida_free(&reset_gpio_ida, id);
|
||||||
|
|||||||
Reference in New Issue
Block a user