diff options
Diffstat (limited to 'drivers/leds/leds-ns2.c')
| -rw-r--r-- | drivers/leds/leds-ns2.c | 203 |
1 files changed, 128 insertions, 75 deletions
diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index f77d48d0b3e..efa625883c8 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -23,12 +23,14 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/gpio.h> #include <linux/leds.h> -#include <mach/leds-ns2.h> +#include <linux/module.h> +#include <linux/platform_data/leds-kirkwood-ns2.h> +#include <linux/of.h> +#include <linux/of_gpio.h> /* * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in @@ -148,7 +150,7 @@ static ssize_t ns2_led_sata_store(struct device *dev, unsigned long enable; enum ns2_led_modes mode; - ret = strict_strtoul(buff, 10, &enable); + ret = kstrtoul(buff, 10, &enable); if (ret < 0) return ret; @@ -183,36 +185,31 @@ static ssize_t ns2_led_sata_show(struct device *dev, static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); -static int __devinit +static int create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, const struct ns2_led *template) { int ret; enum ns2_led_modes mode; - ret = gpio_request(template->cmd, template->name); - if (ret == 0) { - ret = gpio_direction_output(template->cmd, - gpio_get_value(template->cmd)); - if (ret) - gpio_free(template->cmd); - } + ret = devm_gpio_request_one(&pdev->dev, template->cmd, + gpio_get_value(template->cmd) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, + template->name); if (ret) { dev_err(&pdev->dev, "%s: failed to setup command GPIO\n", template->name); + return ret; } - ret = gpio_request(template->slow, template->name); - if (ret == 0) { - ret = gpio_direction_output(template->slow, - gpio_get_value(template->slow)); - if (ret) - gpio_free(template->slow); - } + ret = devm_gpio_request_one(&pdev->dev, template->slow, + gpio_get_value(template->slow) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, + template->name); if (ret) { dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n", template->name); - goto err_free_cmd; + return ret; } rwlock_init(&led_dat->rw_lock); @@ -227,7 +224,7 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, ret = ns2_led_get_mode(led_dat, &mode); if (ret < 0) - goto err_free_slow; + return ret; /* Set LED initial state. */ led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0; @@ -236,7 +233,7 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, ret = led_classdev_register(&pdev->dev, &led_dat->cdev); if (ret < 0) - goto err_free_slow; + return ret; ret = device_create_file(led_dat->cdev.dev, &dev_attr_sata); if (ret < 0) @@ -246,97 +243,153 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, err_free_cdev: led_classdev_unregister(&led_dat->cdev); -err_free_slow: - gpio_free(led_dat->slow); -err_free_cmd: - gpio_free(led_dat->cmd); - return ret; } -static void __devexit delete_ns2_led(struct ns2_led_data *led_dat) +static void delete_ns2_led(struct ns2_led_data *led_dat) { device_remove_file(led_dat->cdev.dev, &dev_attr_sata); led_classdev_unregister(&led_dat->cdev); - gpio_free(led_dat->cmd); - gpio_free(led_dat->slow); } -static int __devinit ns2_led_probe(struct platform_device *pdev) +#ifdef CONFIG_OF_GPIO +/* + * Translate OpenFirmware node properties into platform_data. + */ +static int +ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) { - struct ns2_led_platform_data *pdata = pdev->dev.platform_data; - struct ns2_led_data *leds_data; - int i; - int ret; - - if (!pdata) - return -EINVAL; - - leds_data = kzalloc(sizeof(struct ns2_led_data) * - pdata->num_leds, GFP_KERNEL); - if (!leds_data) + struct device_node *np = dev->of_node; + struct device_node *child; + struct ns2_led *leds; + int num_leds = 0; + int i = 0; + + num_leds = of_get_child_count(np); + if (!num_leds) + return -ENODEV; + + leds = devm_kzalloc(dev, num_leds * sizeof(struct ns2_led), + GFP_KERNEL); + if (!leds) return -ENOMEM; - for (i = 0; i < pdata->num_leds; i++) { - ret = create_ns2_led(pdev, &leds_data[i], &pdata->leds[i]); - if (ret < 0) - goto err; + for_each_child_of_node(np, child) { + const char *string; + int ret; + ret = of_get_named_gpio(child, "cmd-gpio", 0); + if (ret < 0) + return ret; + leds[i].cmd = ret; + ret = of_get_named_gpio(child, "slow-gpio", 0); + if (ret < 0) + return ret; + leds[i].slow = ret; + ret = of_property_read_string(child, "label", &string); + leds[i].name = (ret == 0) ? string : child->name; + ret = of_property_read_string(child, "linux,default-trigger", + &string); + if (ret == 0) + leds[i].default_trigger = string; + + i++; } - platform_set_drvdata(pdev, leds_data); + pdata->leds = leds; + pdata->num_leds = num_leds; return 0; +} -err: - for (i = i - 1; i >= 0; i--) - delete_ns2_led(&leds_data[i]); +static const struct of_device_id of_ns2_leds_match[] = { + { .compatible = "lacie,ns2-leds", }, + {}, +}; +#endif /* CONFIG_OF_GPIO */ - kfree(leds_data); +struct ns2_led_priv { + int num_leds; + struct ns2_led_data leds_data[]; +}; - return ret; +static inline int sizeof_ns2_led_priv(int num_leds) +{ + return sizeof(struct ns2_led_priv) + + (sizeof(struct ns2_led_data) * num_leds); } -static int __devexit ns2_led_remove(struct platform_device *pdev) +static int ns2_led_probe(struct platform_device *pdev) { + struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct ns2_led_priv *priv; int i; - struct ns2_led_platform_data *pdata = pdev->dev.platform_data; - struct ns2_led_data *leds_data; + int ret; + +#ifdef CONFIG_OF_GPIO + if (!pdata) { + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct ns2_led_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = ns2_leds_get_of_pdata(&pdev->dev, pdata); + if (ret) + return ret; + } +#else + if (!pdata) + return -EINVAL; +#endif /* CONFIG_OF_GPIO */ + + priv = devm_kzalloc(&pdev->dev, + sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->num_leds = pdata->num_leds; + + for (i = 0; i < priv->num_leds; i++) { + ret = create_ns2_led(pdev, &priv->leds_data[i], + &pdata->leds[i]); + if (ret < 0) { + for (i = i - 1; i >= 0; i--) + delete_ns2_led(&priv->leds_data[i]); + return ret; + } + } + + platform_set_drvdata(pdev, priv); - leds_data = platform_get_drvdata(pdev); + return 0; +} - for (i = 0; i < pdata->num_leds; i++) - delete_ns2_led(&leds_data[i]); +static int ns2_led_remove(struct platform_device *pdev) +{ + int i; + struct ns2_led_priv *priv; - kfree(leds_data); - platform_set_drvdata(pdev, NULL); + priv = platform_get_drvdata(pdev); + + for (i = 0; i < priv->num_leds; i++) + delete_ns2_led(&priv->leds_data[i]); return 0; } static struct platform_driver ns2_led_driver = { .probe = ns2_led_probe, - .remove = __devexit_p(ns2_led_remove), + .remove = ns2_led_remove, .driver = { - .name = "leds-ns2", - .owner = THIS_MODULE, + .name = "leds-ns2", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_ns2_leds_match), }, }; -MODULE_ALIAS("platform:leds-ns2"); -static int __init ns2_led_init(void) -{ - return platform_driver_register(&ns2_led_driver); -} - -static void __exit ns2_led_exit(void) -{ - platform_driver_unregister(&ns2_led_driver); -} - -module_init(ns2_led_init); -module_exit(ns2_led_exit); +module_platform_driver(ns2_led_driver); MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); MODULE_DESCRIPTION("Network Space v2 LED driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:leds-ns2"); |
