leds: netxbig: Convert to use GPIO descriptors
This converts the NetXbig LED driver to use GPIO descriptors instead of using the legacy interfaces in <linux/of_gpio.h> and <linux/gpio.h> to iteratively parse the device tree for global GPIO numbers. Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Pavel Machek <pavel@ucw.cz> Tested-by: Simon Guinot <simon.guinot@sequanux.org>
This commit is contained in:
parent
cef8ec8cbd
commit
9af512e819
@ -12,16 +12,17 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
|
||||||
struct netxbig_gpio_ext {
|
struct netxbig_gpio_ext {
|
||||||
unsigned int *addr;
|
struct gpio_desc **addr;
|
||||||
int num_addr;
|
int num_addr;
|
||||||
unsigned int *data;
|
struct gpio_desc **data;
|
||||||
int num_data;
|
int num_data;
|
||||||
unsigned int enable;
|
struct gpio_desc *enable;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum netxbig_led_mode {
|
enum netxbig_led_mode {
|
||||||
@ -69,7 +70,7 @@ static void gpio_ext_set_addr(struct netxbig_gpio_ext *gpio_ext, int addr)
|
|||||||
int pin;
|
int pin;
|
||||||
|
|
||||||
for (pin = 0; pin < gpio_ext->num_addr; pin++)
|
for (pin = 0; pin < gpio_ext->num_addr; pin++)
|
||||||
gpio_set_value(gpio_ext->addr[pin], (addr >> pin) & 1);
|
gpiod_set_value(gpio_ext->addr[pin], (addr >> pin) & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpio_ext_set_data(struct netxbig_gpio_ext *gpio_ext, int data)
|
static void gpio_ext_set_data(struct netxbig_gpio_ext *gpio_ext, int data)
|
||||||
@ -77,14 +78,14 @@ static void gpio_ext_set_data(struct netxbig_gpio_ext *gpio_ext, int data)
|
|||||||
int pin;
|
int pin;
|
||||||
|
|
||||||
for (pin = 0; pin < gpio_ext->num_data; pin++)
|
for (pin = 0; pin < gpio_ext->num_data; pin++)
|
||||||
gpio_set_value(gpio_ext->data[pin], (data >> pin) & 1);
|
gpiod_set_value(gpio_ext->data[pin], (data >> pin) & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpio_ext_enable_select(struct netxbig_gpio_ext *gpio_ext)
|
static void gpio_ext_enable_select(struct netxbig_gpio_ext *gpio_ext)
|
||||||
{
|
{
|
||||||
/* Enable select is done on the raising edge. */
|
/* Enable select is done on the raising edge. */
|
||||||
gpio_set_value(gpio_ext->enable, 0);
|
gpiod_set_value(gpio_ext->enable, 0);
|
||||||
gpio_set_value(gpio_ext->enable, 1);
|
gpiod_set_value(gpio_ext->enable, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext,
|
static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext,
|
||||||
@ -99,41 +100,6 @@ static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext,
|
|||||||
spin_unlock_irqrestore(&gpio_ext_lock, flags);
|
spin_unlock_irqrestore(&gpio_ext_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gpio_ext_init(struct platform_device *pdev,
|
|
||||||
struct netxbig_gpio_ext *gpio_ext)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (unlikely(!gpio_ext))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/* Configure address GPIOs. */
|
|
||||||
for (i = 0; i < gpio_ext->num_addr; i++) {
|
|
||||||
err = devm_gpio_request_one(&pdev->dev, gpio_ext->addr[i],
|
|
||||||
GPIOF_OUT_INIT_LOW,
|
|
||||||
"GPIO extension addr");
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
/* Configure data GPIOs. */
|
|
||||||
for (i = 0; i < gpio_ext->num_data; i++) {
|
|
||||||
err = devm_gpio_request_one(&pdev->dev, gpio_ext->data[i],
|
|
||||||
GPIOF_OUT_INIT_LOW,
|
|
||||||
"GPIO extension data");
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
/* Configure "enable select" GPIO. */
|
|
||||||
err = devm_gpio_request_one(&pdev->dev, gpio_ext->enable,
|
|
||||||
GPIOF_OUT_INIT_LOW,
|
|
||||||
"GPIO extension enable");
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class LED driver.
|
* Class LED driver.
|
||||||
*/
|
*/
|
||||||
@ -347,15 +313,47 @@ static int create_netxbig_led(struct platform_device *pdev,
|
|||||||
return devm_led_classdev_register(&pdev->dev, &led_dat->cdev);
|
return devm_led_classdev_register(&pdev->dev, &led_dat->cdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np,
|
/**
|
||||||
struct netxbig_gpio_ext *gpio_ext)
|
* netxbig_gpio_ext_remove() - Clean up GPIO extension data
|
||||||
|
* @data: managed resource data to clean up
|
||||||
|
*
|
||||||
|
* Since we pick GPIO descriptors from another device than the device our
|
||||||
|
* driver is probing to, we need to register a specific callback to free
|
||||||
|
* these up using managed resources.
|
||||||
|
*/
|
||||||
|
static void netxbig_gpio_ext_remove(void *data)
|
||||||
{
|
{
|
||||||
int *addr, *data;
|
struct netxbig_gpio_ext *gpio_ext = data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < gpio_ext->num_addr; i++)
|
||||||
|
gpiod_put(gpio_ext->addr[i]);
|
||||||
|
for (i = 0; i < gpio_ext->num_data; i++)
|
||||||
|
gpiod_put(gpio_ext->data[i]);
|
||||||
|
gpiod_put(gpio_ext->enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* netxbig_gpio_ext_get() - Obtain GPIO extension device data
|
||||||
|
* @dev: main LED device
|
||||||
|
* @gpio_ext_dev: the GPIO extension device
|
||||||
|
* @gpio_ext: the data structure holding the GPIO extension data
|
||||||
|
*
|
||||||
|
* This function walks the subdevice that only contain GPIO line
|
||||||
|
* handles in the device tree and obtains the GPIO descriptors from that
|
||||||
|
* device.
|
||||||
|
*/
|
||||||
|
static int netxbig_gpio_ext_get(struct device *dev,
|
||||||
|
struct device *gpio_ext_dev,
|
||||||
|
struct netxbig_gpio_ext *gpio_ext)
|
||||||
|
{
|
||||||
|
struct gpio_desc **addr, **data;
|
||||||
int num_addr, num_data;
|
int num_addr, num_data;
|
||||||
|
struct gpio_desc *gpiod;
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ret = of_gpio_named_count(np, "addr-gpios");
|
ret = gpiod_count(gpio_ext_dev, "addr");
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev,
|
dev_err(dev,
|
||||||
"Failed to count GPIOs in DT property addr-gpios\n");
|
"Failed to count GPIOs in DT property addr-gpios\n");
|
||||||
@ -366,16 +364,25 @@ static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np,
|
|||||||
if (!addr)
|
if (!addr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We cannot use devm_ managed resources with these GPIO descriptors
|
||||||
|
* since they are associated with the "GPIO extension device" which
|
||||||
|
* does not probe any driver. The device tree parser will however
|
||||||
|
* populate a platform device for it so we can anyway obtain the
|
||||||
|
* GPIO descriptors from the device.
|
||||||
|
*/
|
||||||
for (i = 0; i < num_addr; i++) {
|
for (i = 0; i < num_addr; i++) {
|
||||||
ret = of_get_named_gpio(np, "addr-gpios", i);
|
gpiod = gpiod_get_index(gpio_ext_dev, "addr", i,
|
||||||
if (ret < 0)
|
GPIOD_OUT_LOW);
|
||||||
return ret;
|
if (IS_ERR(gpiod))
|
||||||
addr[i] = ret;
|
return PTR_ERR(gpiod);
|
||||||
|
gpiod_set_consumer_name(gpiod, "GPIO extension addr");
|
||||||
|
addr[i] = gpiod;
|
||||||
}
|
}
|
||||||
gpio_ext->addr = addr;
|
gpio_ext->addr = addr;
|
||||||
gpio_ext->num_addr = num_addr;
|
gpio_ext->num_addr = num_addr;
|
||||||
|
|
||||||
ret = of_gpio_named_count(np, "data-gpios");
|
ret = gpiod_count(gpio_ext_dev, "data");
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev,
|
dev_err(dev,
|
||||||
"Failed to count GPIOs in DT property data-gpios\n");
|
"Failed to count GPIOs in DT property data-gpios\n");
|
||||||
@ -387,23 +394,26 @@ static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
for (i = 0; i < num_data; i++) {
|
for (i = 0; i < num_data; i++) {
|
||||||
ret = of_get_named_gpio(np, "data-gpios", i);
|
gpiod = gpiod_get_index(gpio_ext_dev, "data", i,
|
||||||
if (ret < 0)
|
GPIOD_OUT_LOW);
|
||||||
return ret;
|
if (IS_ERR(gpiod))
|
||||||
data[i] = ret;
|
return PTR_ERR(gpiod);
|
||||||
|
gpiod_set_consumer_name(gpiod, "GPIO extension data");
|
||||||
|
data[i] = gpiod;
|
||||||
}
|
}
|
||||||
gpio_ext->data = data;
|
gpio_ext->data = data;
|
||||||
gpio_ext->num_data = num_data;
|
gpio_ext->num_data = num_data;
|
||||||
|
|
||||||
ret = of_get_named_gpio(np, "enable-gpio", 0);
|
gpiod = gpiod_get(gpio_ext_dev, "enable", GPIOD_OUT_LOW);
|
||||||
if (ret < 0) {
|
if (IS_ERR(gpiod)) {
|
||||||
dev_err(dev,
|
dev_err(dev,
|
||||||
"Failed to get GPIO from DT property enable-gpio\n");
|
"Failed to get GPIO from DT property enable-gpio\n");
|
||||||
return ret;
|
return PTR_ERR(gpiod);
|
||||||
}
|
}
|
||||||
gpio_ext->enable = ret;
|
gpiod_set_consumer_name(gpiod, "GPIO extension enable");
|
||||||
|
gpio_ext->enable = gpiod;
|
||||||
|
|
||||||
return 0;
|
return devm_add_action_or_reset(dev, netxbig_gpio_ext_remove, gpio_ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int netxbig_leds_get_of_pdata(struct device *dev,
|
static int netxbig_leds_get_of_pdata(struct device *dev,
|
||||||
@ -411,6 +421,8 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
|
|||||||
{
|
{
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
struct device_node *gpio_ext_np;
|
struct device_node *gpio_ext_np;
|
||||||
|
struct platform_device *gpio_ext_pdev;
|
||||||
|
struct device *gpio_ext_dev;
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
struct netxbig_gpio_ext *gpio_ext;
|
struct netxbig_gpio_ext *gpio_ext;
|
||||||
struct netxbig_led_timer *timers;
|
struct netxbig_led_timer *timers;
|
||||||
@ -426,13 +438,19 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
|
|||||||
dev_err(dev, "Failed to get DT handle gpio-ext\n");
|
dev_err(dev, "Failed to get DT handle gpio-ext\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
gpio_ext_pdev = of_find_device_by_node(gpio_ext_np);
|
||||||
|
if (!gpio_ext_pdev) {
|
||||||
|
dev_err(dev, "Failed to find platform device for gpio-ext\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
gpio_ext_dev = &gpio_ext_pdev->dev;
|
||||||
|
|
||||||
gpio_ext = devm_kzalloc(dev, sizeof(*gpio_ext), GFP_KERNEL);
|
gpio_ext = devm_kzalloc(dev, sizeof(*gpio_ext), GFP_KERNEL);
|
||||||
if (!gpio_ext) {
|
if (!gpio_ext) {
|
||||||
of_node_put(gpio_ext_np);
|
of_node_put(gpio_ext_np);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
ret = gpio_ext_get_of_pdata(dev, gpio_ext_np, gpio_ext);
|
ret = netxbig_gpio_ext_get(dev, gpio_ext_dev, gpio_ext);
|
||||||
of_node_put(gpio_ext_np);
|
of_node_put(gpio_ext_np);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@ -585,10 +603,6 @@ static int netxbig_led_probe(struct platform_device *pdev)
|
|||||||
if (!leds_data)
|
if (!leds_data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = gpio_ext_init(pdev, pdata->gpio_ext);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
for (i = 0; i < pdata->num_leds; i++) {
|
for (i = 0; i < pdata->num_leds; i++) {
|
||||||
ret = create_netxbig_led(pdev, pdata,
|
ret = create_netxbig_led(pdev, pdata,
|
||||||
&leds_data[i], &pdata->leds[i]);
|
&leds_data[i], &pdata->leds[i]);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user