diff options
Diffstat (limited to 'drivers/leds/leds-netxbig.c')
| -rw-r--r-- | drivers/leds/leds-netxbig.c | 336 |
1 files changed, 198 insertions, 138 deletions
diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index 62fa0de526ee..99df46f2d9f5 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * leds-netxbig.c - Driver for the 2Big and 5Big Network series LEDs * * Copyright (C) 2010 LaCie * * Author: Simon Guinot <sguinot@lacie.com> - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/module.h> @@ -25,10 +12,52 @@ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/platform_device.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/leds.h> -#include <linux/platform_data/leds-kirkwood-netxbig.h> +#include <linux/of.h> +#include <linux/of_platform.h> + +struct netxbig_gpio_ext { + struct gpio_desc **addr; + int num_addr; + struct gpio_desc **data; + int num_data; + struct gpio_desc *enable; +}; + +enum netxbig_led_mode { + NETXBIG_LED_OFF, + NETXBIG_LED_ON, + NETXBIG_LED_SATA, + NETXBIG_LED_TIMER1, + NETXBIG_LED_TIMER2, + NETXBIG_LED_MODE_NUM, +}; + +#define NETXBIG_LED_INVALID_MODE NETXBIG_LED_MODE_NUM + +struct netxbig_led_timer { + unsigned long delay_on; + unsigned long delay_off; + enum netxbig_led_mode mode; +}; + +struct netxbig_led { + const char *name; + const char *default_trigger; + int mode_addr; + int *mode_val; + int bright_addr; + int bright_max; +}; + +struct netxbig_led_platform_data { + struct netxbig_gpio_ext *gpio_ext; + struct netxbig_led_timer *timer; + int num_timer; + struct netxbig_led *leds; + int num_leds; +}; /* * GPIO extension bus. @@ -41,7 +70,7 @@ static void gpio_ext_set_addr(struct netxbig_gpio_ext *gpio_ext, int addr) int 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) @@ -49,14 +78,14 @@ static void gpio_ext_set_data(struct netxbig_gpio_ext *gpio_ext, int data) int 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) { /* Enable select is done on the raising edge. */ - gpio_set_value(gpio_ext->enable, 0); - gpio_set_value(gpio_ext->enable, 1); + gpiod_set_value(gpio_ext->enable, 0); + gpiod_set_value(gpio_ext->enable, 1); } static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext, @@ -71,41 +100,6 @@ static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext, 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. */ @@ -210,9 +204,9 @@ static void netxbig_led_set(struct led_classdev *led_cdev, spin_unlock_irqrestore(&led_dat->lock, flags); } -static ssize_t netxbig_led_sata_store(struct device *dev, - struct device_attribute *attr, - const char *buff, size_t count) +static ssize_t sata_store(struct device *dev, + struct device_attribute *attr, + const char *buff, size_t count) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct netxbig_led_data *led_dat = @@ -261,8 +255,8 @@ exit_unlock: return ret; } -static ssize_t netxbig_led_sata_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t sata_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct netxbig_led_data *led_dat = @@ -271,7 +265,7 @@ static ssize_t netxbig_led_sata_show(struct device *dev, return sprintf(buf, "%d\n", led_dat->sata); } -static DEVICE_ATTR(sata, 0644, netxbig_led_sata_show, netxbig_led_sata_store); +static DEVICE_ATTR_RW(sata); static struct attribute *netxbig_led_attrs[] = { &dev_attr_sata.attr, @@ -319,16 +313,47 @@ static int create_netxbig_led(struct platform_device *pdev, return devm_led_classdev_register(&pdev->dev, &led_dat->cdev); } -#ifdef CONFIG_OF_GPIO -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; + struct gpio_desc *gpiod; int ret; int i; - ret = of_gpio_named_count(np, "addr-gpios"); + ret = gpiod_count(gpio_ext_dev, "addr"); if (ret < 0) { dev_err(dev, "Failed to count GPIOs in DT property addr-gpios\n"); @@ -339,52 +364,81 @@ static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np, if (!addr) return -ENOMEM; + gpio_ext->addr = addr; + gpio_ext->num_addr = 0; + + /* + * 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++) { - ret = of_get_named_gpio(np, "addr-gpios", i); - if (ret < 0) - return ret; - addr[i] = ret; + gpiod = gpiod_get_index(gpio_ext_dev, "addr", i, + GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) + goto err_set_code; + gpiod_set_consumer_name(gpiod, "GPIO extension addr"); + addr[i] = gpiod; + gpio_ext->num_addr++; } - gpio_ext->addr = 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) { dev_err(dev, "Failed to count GPIOs in DT property data-gpios\n"); - return ret; + goto err_free_addr; } num_data = ret; data = devm_kcalloc(dev, num_data, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; + if (!data) { + ret = -ENOMEM; + goto err_free_addr; + } + + gpio_ext->data = data; + gpio_ext->num_data = 0; for (i = 0; i < num_data; i++) { - ret = of_get_named_gpio(np, "data-gpios", i); - if (ret < 0) - return ret; - data[i] = ret; + gpiod = gpiod_get_index(gpio_ext_dev, "data", i, + GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) + goto err_free_data; + gpiod_set_consumer_name(gpiod, "GPIO extension data"); + data[i] = gpiod; + gpio_ext->num_data++; } - gpio_ext->data = data; - gpio_ext->num_data = num_data; - ret = of_get_named_gpio(np, "enable-gpio", 0); - if (ret < 0) { + gpiod = gpiod_get(gpio_ext_dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) { dev_err(dev, "Failed to get GPIO from DT property enable-gpio\n"); - return ret; + goto err_free_data; } - gpio_ext->enable = ret; - - return 0; + gpiod_set_consumer_name(gpiod, "GPIO extension enable"); + gpio_ext->enable = gpiod; + + return devm_add_action_or_reset(dev, netxbig_gpio_ext_remove, gpio_ext); + +err_free_data: + for (i = 0; i < gpio_ext->num_data; i++) + gpiod_put(gpio_ext->data[i]); +err_set_code: + ret = PTR_ERR(gpiod); +err_free_addr: + for (i = 0; i < gpio_ext->num_addr; i++) + gpiod_put(gpio_ext->addr[i]); + return ret; } static int netxbig_leds_get_of_pdata(struct device *dev, struct netxbig_led_platform_data *pdata) { - struct device_node *np = dev->of_node; + struct device_node *np = dev_of_node(dev); struct device_node *gpio_ext_np; - struct device_node *child; + struct platform_device *gpio_ext_pdev; + struct device *gpio_ext_dev; struct netxbig_gpio_ext *gpio_ext; struct netxbig_led_timer *timers; struct netxbig_led *leds, *led; @@ -399,33 +453,50 @@ static int netxbig_leds_get_of_pdata(struct device *dev, dev_err(dev, "Failed to get DT handle gpio-ext\n"); return -EINVAL; } + gpio_ext_pdev = of_find_device_by_node(gpio_ext_np); + if (!gpio_ext_pdev) { + of_node_put(gpio_ext_np); + 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); - if (!gpio_ext) - return -ENOMEM; - ret = gpio_ext_get_of_pdata(dev, gpio_ext_np, gpio_ext); - if (ret) - return ret; + if (!gpio_ext) { + of_node_put(gpio_ext_np); + ret = -ENOMEM; + goto put_device; + } + ret = netxbig_gpio_ext_get(dev, gpio_ext_dev, gpio_ext); of_node_put(gpio_ext_np); + if (ret) + goto put_device; pdata->gpio_ext = gpio_ext; /* Timers (optional) */ ret = of_property_count_u32_elems(np, "timers"); if (ret > 0) { - if (ret % 3) - return -EINVAL; + if (ret % 3) { + ret = -EINVAL; + goto put_device; + } + num_timers = ret / 3; timers = devm_kcalloc(dev, num_timers, sizeof(*timers), GFP_KERNEL); - if (!timers) - return -ENOMEM; + if (!timers) { + ret = -ENOMEM; + goto put_device; + } for (i = 0; i < num_timers; i++) { u32 tmp; of_property_read_u32_index(np, "timers", 3 * i, &timers[i].mode); - if (timers[i].mode >= NETXBIG_LED_MODE_NUM) - return -EINVAL; + if (timers[i].mode >= NETXBIG_LED_MODE_NUM) { + ret = -EINVAL; + goto put_device; + } of_property_read_u32_index(np, "timers", 3 * i + 1, &tmp); timers[i].delay_on = tmp; @@ -438,18 +509,21 @@ static int netxbig_leds_get_of_pdata(struct device *dev, } /* LEDs */ - num_leds = of_get_child_count(np); + num_leds = of_get_available_child_count(np); if (!num_leds) { dev_err(dev, "No LED subnodes found in DT\n"); - return -ENODEV; + ret = -ENODEV; + goto put_device; } leds = devm_kcalloc(dev, num_leds, sizeof(*leds), GFP_KERNEL); - if (!leds) - return -ENOMEM; + if (!leds) { + ret = -ENOMEM; + goto put_device; + } led = leds; - for_each_child_of_node(np, child) { + for_each_available_child_of_node_scoped(np, child) { const char *string; int *mode_val; int num_modes; @@ -457,17 +531,17 @@ static int netxbig_leds_get_of_pdata(struct device *dev, ret = of_property_read_u32(child, "mode-addr", &led->mode_addr); if (ret) - goto err_node_put; + goto put_device; ret = of_property_read_u32(child, "bright-addr", &led->bright_addr); if (ret) - goto err_node_put; + goto put_device; ret = of_property_read_u32(child, "max-brightness", &led->bright_max); if (ret) - goto err_node_put; + goto put_device; mode_val = devm_kcalloc(dev, @@ -475,7 +549,7 @@ static int netxbig_leds_get_of_pdata(struct device *dev, GFP_KERNEL); if (!mode_val) { ret = -ENOMEM; - goto err_node_put; + goto put_device; } for (i = 0; i < NETXBIG_LED_MODE_NUM; i++) @@ -484,12 +558,12 @@ static int netxbig_leds_get_of_pdata(struct device *dev, ret = of_property_count_u32_elems(child, "mode-val"); if (ret < 0 || ret % 2) { ret = -EINVAL; - goto err_node_put; + goto put_device; } num_modes = ret / 2; if (num_modes > NETXBIG_LED_MODE_NUM) { ret = -EINVAL; - goto err_node_put; + goto put_device; } for (i = 0; i < num_modes; i++) { @@ -502,7 +576,7 @@ static int netxbig_leds_get_of_pdata(struct device *dev, "mode-val", 2 * i + 1, &val); if (mode >= NETXBIG_LED_MODE_NUM) { ret = -EINVAL; - goto err_node_put; + goto put_device; } mode_val[mode] = val; } @@ -525,8 +599,8 @@ static int netxbig_leds_get_of_pdata(struct device *dev, return 0; -err_node_put: - of_node_put(child); +put_device: + put_device(gpio_ext_dev); return ret; } @@ -535,30 +609,20 @@ static const struct of_device_id of_netxbig_leds_match[] = { {}, }; MODULE_DEVICE_TABLE(of, of_netxbig_leds_match); -#else -static inline int -netxbig_leds_get_of_pdata(struct device *dev, - struct netxbig_led_platform_data *pdata) -{ - return -ENODEV; -} -#endif /* CONFIG_OF_GPIO */ static int netxbig_led_probe(struct platform_device *pdev) { - struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct netxbig_led_platform_data *pdata; struct netxbig_led_data *leds_data; int i; int ret; - if (!pdata) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - ret = netxbig_leds_get_of_pdata(&pdev->dev, pdata); - if (ret) - return ret; - } + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + ret = netxbig_leds_get_of_pdata(&pdev->dev, pdata); + if (ret) + return ret; leds_data = devm_kcalloc(&pdev->dev, pdata->num_leds, sizeof(*leds_data), @@ -566,10 +630,6 @@ static int netxbig_led_probe(struct platform_device *pdev) if (!leds_data) return -ENOMEM; - ret = gpio_ext_init(pdev, pdata->gpio_ext); - if (ret < 0) - return ret; - for (i = 0; i < pdata->num_leds; i++) { ret = create_netxbig_led(pdev, pdata, &leds_data[i], &pdata->leds[i]); @@ -584,7 +644,7 @@ static struct platform_driver netxbig_led_driver = { .probe = netxbig_led_probe, .driver = { .name = "leds-netxbig", - .of_match_table = of_match_ptr(of_netxbig_leds_match), + .of_match_table = of_netxbig_leds_match, }, }; |
