11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 211efe71fSSimon Guinot /* 311efe71fSSimon Guinot * leds-ns2.c - Driver for the Network Space v2 (and parents) dual-GPIO LED 411efe71fSSimon Guinot * 511efe71fSSimon Guinot * Copyright (C) 2010 LaCie 611efe71fSSimon Guinot * 711efe71fSSimon Guinot * Author: Simon Guinot <sguinot@lacie.com> 811efe71fSSimon Guinot * 911efe71fSSimon Guinot * Based on leds-gpio.c by Raphael Assenat <raph@8d.com> 1011efe71fSSimon Guinot */ 1111efe71fSSimon Guinot 1211efe71fSSimon Guinot #include <linux/kernel.h> 1311efe71fSSimon Guinot #include <linux/platform_device.h> 1411efe71fSSimon Guinot #include <linux/slab.h> 1511efe71fSSimon Guinot #include <linux/gpio.h> 1611efe71fSSimon Guinot #include <linux/leds.h> 1754f4dedbSPaul Gortmaker #include <linux/module.h> 18c02cecb9SArnd Bergmann #include <linux/platform_data/leds-kirkwood-ns2.h> 19c68f46ddSSachin Kamat #include <linux/of.h> 2072052fccSSimon Guinot #include <linux/of_gpio.h> 214b90432dSSimon Guinot #include "leds.h" 2211efe71fSSimon Guinot 2311efe71fSSimon Guinot /* 24f7fafd08SVincent Donnefort * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED 25f7fafd08SVincent Donnefort * modes are available: off, on and SATA activity blinking. The LED modes are 26f7fafd08SVincent Donnefort * controlled through two GPIOs (command and slow): each combination of values 27f7fafd08SVincent Donnefort * for the command/slow GPIOs corresponds to a LED mode. 2811efe71fSSimon Guinot */ 2911efe71fSSimon Guinot 3011efe71fSSimon Guinot struct ns2_led_data { 3111efe71fSSimon Guinot struct led_classdev cdev; 322224f2ffSKitone Elvis Peter unsigned int cmd; 332224f2ffSKitone Elvis Peter unsigned int slow; 344b90432dSSimon Guinot bool can_sleep; 3511efe71fSSimon Guinot unsigned char sata; /* True when SATA mode active. */ 3611efe71fSSimon Guinot rwlock_t rw_lock; /* Lock GPIOs. */ 37f7fafd08SVincent Donnefort int num_modes; 38f7fafd08SVincent Donnefort struct ns2_led_modval *modval; 3911efe71fSSimon Guinot }; 4011efe71fSSimon Guinot 4111efe71fSSimon Guinot static int ns2_led_get_mode(struct ns2_led_data *led_dat, 4211efe71fSSimon Guinot enum ns2_led_modes *mode) 4311efe71fSSimon Guinot { 4411efe71fSSimon Guinot int i; 4511efe71fSSimon Guinot int ret = -EINVAL; 4611efe71fSSimon Guinot int cmd_level; 4711efe71fSSimon Guinot int slow_level; 4811efe71fSSimon Guinot 494b90432dSSimon Guinot cmd_level = gpio_get_value_cansleep(led_dat->cmd); 504b90432dSSimon Guinot slow_level = gpio_get_value_cansleep(led_dat->slow); 5111efe71fSSimon Guinot 52f7fafd08SVincent Donnefort for (i = 0; i < led_dat->num_modes; i++) { 53f7fafd08SVincent Donnefort if (cmd_level == led_dat->modval[i].cmd_level && 54f7fafd08SVincent Donnefort slow_level == led_dat->modval[i].slow_level) { 55f7fafd08SVincent Donnefort *mode = led_dat->modval[i].mode; 5611efe71fSSimon Guinot ret = 0; 5711efe71fSSimon Guinot break; 5811efe71fSSimon Guinot } 5911efe71fSSimon Guinot } 6011efe71fSSimon Guinot 6111efe71fSSimon Guinot return ret; 6211efe71fSSimon Guinot } 6311efe71fSSimon Guinot 6411efe71fSSimon Guinot static void ns2_led_set_mode(struct ns2_led_data *led_dat, 6511efe71fSSimon Guinot enum ns2_led_modes mode) 6611efe71fSSimon Guinot { 6711efe71fSSimon Guinot int i; 684b90432dSSimon Guinot bool found = false; 69f539dfedSSimon Guinot unsigned long flags; 7011efe71fSSimon Guinot 714b90432dSSimon Guinot for (i = 0; i < led_dat->num_modes; i++) 724b90432dSSimon Guinot if (mode == led_dat->modval[i].mode) { 734b90432dSSimon Guinot found = true; 744b90432dSSimon Guinot break; 754b90432dSSimon Guinot } 764b90432dSSimon Guinot 774b90432dSSimon Guinot if (!found) 784b90432dSSimon Guinot return; 794b90432dSSimon Guinot 80f539dfedSSimon Guinot write_lock_irqsave(&led_dat->rw_lock, flags); 8111efe71fSSimon Guinot 824b90432dSSimon Guinot if (!led_dat->can_sleep) { 8311efe71fSSimon Guinot gpio_set_value(led_dat->cmd, 84f7fafd08SVincent Donnefort led_dat->modval[i].cmd_level); 8511efe71fSSimon Guinot gpio_set_value(led_dat->slow, 86f7fafd08SVincent Donnefort led_dat->modval[i].slow_level); 874b90432dSSimon Guinot goto exit_unlock; 8811efe71fSSimon Guinot } 8911efe71fSSimon Guinot 90c29e650bSJacek Anaszewski gpio_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level); 91c29e650bSJacek Anaszewski gpio_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level); 924b90432dSSimon Guinot 934b90432dSSimon Guinot exit_unlock: 94f539dfedSSimon Guinot write_unlock_irqrestore(&led_dat->rw_lock, flags); 9511efe71fSSimon Guinot } 9611efe71fSSimon Guinot 9711efe71fSSimon Guinot static void ns2_led_set(struct led_classdev *led_cdev, 9811efe71fSSimon Guinot enum led_brightness value) 9911efe71fSSimon Guinot { 10011efe71fSSimon Guinot struct ns2_led_data *led_dat = 10111efe71fSSimon Guinot container_of(led_cdev, struct ns2_led_data, cdev); 10211efe71fSSimon Guinot enum ns2_led_modes mode; 10311efe71fSSimon Guinot 10411efe71fSSimon Guinot if (value == LED_OFF) 10511efe71fSSimon Guinot mode = NS_V2_LED_OFF; 10611efe71fSSimon Guinot else if (led_dat->sata) 10711efe71fSSimon Guinot mode = NS_V2_LED_SATA; 10811efe71fSSimon Guinot else 10911efe71fSSimon Guinot mode = NS_V2_LED_ON; 11011efe71fSSimon Guinot 11111efe71fSSimon Guinot ns2_led_set_mode(led_dat, mode); 11211efe71fSSimon Guinot } 11311efe71fSSimon Guinot 114c29e650bSJacek Anaszewski static int ns2_led_set_blocking(struct led_classdev *led_cdev, 115c29e650bSJacek Anaszewski enum led_brightness value) 116c29e650bSJacek Anaszewski { 117c29e650bSJacek Anaszewski ns2_led_set(led_cdev, value); 118c29e650bSJacek Anaszewski return 0; 119c29e650bSJacek Anaszewski } 120c29e650bSJacek Anaszewski 12111efe71fSSimon Guinot static ssize_t ns2_led_sata_store(struct device *dev, 12211efe71fSSimon Guinot struct device_attribute *attr, 12311efe71fSSimon Guinot const char *buff, size_t count) 12411efe71fSSimon Guinot { 125e5971bbcSSimon Guinot struct led_classdev *led_cdev = dev_get_drvdata(dev); 126e5971bbcSSimon Guinot struct ns2_led_data *led_dat = 127e5971bbcSSimon Guinot container_of(led_cdev, struct ns2_led_data, cdev); 12811efe71fSSimon Guinot int ret; 12911efe71fSSimon Guinot unsigned long enable; 13011efe71fSSimon Guinot 1313874350cSJingoo Han ret = kstrtoul(buff, 10, &enable); 13211efe71fSSimon Guinot if (ret < 0) 13311efe71fSSimon Guinot return ret; 13411efe71fSSimon Guinot 13511efe71fSSimon Guinot enable = !!enable; 13611efe71fSSimon Guinot 13711efe71fSSimon Guinot if (led_dat->sata == enable) 1384b90432dSSimon Guinot goto exit; 13911efe71fSSimon Guinot 14011efe71fSSimon Guinot led_dat->sata = enable; 14111efe71fSSimon Guinot 1424b90432dSSimon Guinot if (!led_get_brightness(led_cdev)) 1434b90432dSSimon Guinot goto exit; 1444b90432dSSimon Guinot 1454b90432dSSimon Guinot if (enable) 1464b90432dSSimon Guinot ns2_led_set_mode(led_dat, NS_V2_LED_SATA); 1474b90432dSSimon Guinot else 1484b90432dSSimon Guinot ns2_led_set_mode(led_dat, NS_V2_LED_ON); 1494b90432dSSimon Guinot 1504b90432dSSimon Guinot exit: 15111efe71fSSimon Guinot return count; 15211efe71fSSimon Guinot } 15311efe71fSSimon Guinot 15411efe71fSSimon Guinot static ssize_t ns2_led_sata_show(struct device *dev, 15511efe71fSSimon Guinot struct device_attribute *attr, char *buf) 15611efe71fSSimon Guinot { 157e5971bbcSSimon Guinot struct led_classdev *led_cdev = dev_get_drvdata(dev); 158e5971bbcSSimon Guinot struct ns2_led_data *led_dat = 159e5971bbcSSimon Guinot container_of(led_cdev, struct ns2_led_data, cdev); 16011efe71fSSimon Guinot 16111efe71fSSimon Guinot return sprintf(buf, "%d\n", led_dat->sata); 16211efe71fSSimon Guinot } 16311efe71fSSimon Guinot 16411efe71fSSimon Guinot static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); 16511efe71fSSimon Guinot 166475f8548SJohan Hovold static struct attribute *ns2_led_attrs[] = { 167475f8548SJohan Hovold &dev_attr_sata.attr, 168475f8548SJohan Hovold NULL 169475f8548SJohan Hovold }; 170475f8548SJohan Hovold ATTRIBUTE_GROUPS(ns2_led); 171475f8548SJohan Hovold 17298ea1ea2SBill Pemberton static int 17311efe71fSSimon Guinot create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, 17411efe71fSSimon Guinot const struct ns2_led *template) 17511efe71fSSimon Guinot { 17611efe71fSSimon Guinot int ret; 17711efe71fSSimon Guinot enum ns2_led_modes mode; 17811efe71fSSimon Guinot 17904195823SSachin Kamat ret = devm_gpio_request_one(&pdev->dev, template->cmd, 1804b90432dSSimon Guinot gpio_get_value_cansleep(template->cmd) ? 1819d04cbaaSJingoo Han GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 18231c3dc74SJingoo Han template->name); 18311efe71fSSimon Guinot if (ret) { 18411efe71fSSimon Guinot dev_err(&pdev->dev, "%s: failed to setup command GPIO\n", 18511efe71fSSimon Guinot template->name); 18631c3dc74SJingoo Han return ret; 18711efe71fSSimon Guinot } 18811efe71fSSimon Guinot 18904195823SSachin Kamat ret = devm_gpio_request_one(&pdev->dev, template->slow, 1904b90432dSSimon Guinot gpio_get_value_cansleep(template->slow) ? 1919d04cbaaSJingoo Han GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 19231c3dc74SJingoo Han template->name); 19311efe71fSSimon Guinot if (ret) { 19411efe71fSSimon Guinot dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n", 19511efe71fSSimon Guinot template->name); 19604195823SSachin Kamat return ret; 19711efe71fSSimon Guinot } 19811efe71fSSimon Guinot 19911efe71fSSimon Guinot rwlock_init(&led_dat->rw_lock); 20011efe71fSSimon Guinot 20111efe71fSSimon Guinot led_dat->cdev.name = template->name; 20211efe71fSSimon Guinot led_dat->cdev.default_trigger = template->default_trigger; 20311efe71fSSimon Guinot led_dat->cdev.blink_set = NULL; 20411efe71fSSimon Guinot led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; 205475f8548SJohan Hovold led_dat->cdev.groups = ns2_led_groups; 20611efe71fSSimon Guinot led_dat->cmd = template->cmd; 20711efe71fSSimon Guinot led_dat->slow = template->slow; 2084b90432dSSimon Guinot led_dat->can_sleep = gpio_cansleep(led_dat->cmd) | 2094b90432dSSimon Guinot gpio_cansleep(led_dat->slow); 210c29e650bSJacek Anaszewski if (led_dat->can_sleep) 211c29e650bSJacek Anaszewski led_dat->cdev.brightness_set_blocking = ns2_led_set_blocking; 212c29e650bSJacek Anaszewski else 213c29e650bSJacek Anaszewski led_dat->cdev.brightness_set = ns2_led_set; 214f7fafd08SVincent Donnefort led_dat->modval = template->modval; 215f7fafd08SVincent Donnefort led_dat->num_modes = template->num_modes; 21611efe71fSSimon Guinot 21711efe71fSSimon Guinot ret = ns2_led_get_mode(led_dat, &mode); 21811efe71fSSimon Guinot if (ret < 0) 21904195823SSachin Kamat return ret; 22011efe71fSSimon Guinot 22111efe71fSSimon Guinot /* Set LED initial state. */ 22211efe71fSSimon Guinot led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0; 22311efe71fSSimon Guinot led_dat->cdev.brightness = 22411efe71fSSimon Guinot (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL; 22511efe71fSSimon Guinot 22611efe71fSSimon Guinot ret = led_classdev_register(&pdev->dev, &led_dat->cdev); 22711efe71fSSimon Guinot if (ret < 0) 22804195823SSachin Kamat return ret; 22911efe71fSSimon Guinot 23011efe71fSSimon Guinot return 0; 23111efe71fSSimon Guinot } 23211efe71fSSimon Guinot 233b8cd742aSArnd Bergmann static void delete_ns2_led(struct ns2_led_data *led_dat) 23411efe71fSSimon Guinot { 23511efe71fSSimon Guinot led_classdev_unregister(&led_dat->cdev); 23611efe71fSSimon Guinot } 23711efe71fSSimon Guinot 23872052fccSSimon Guinot #ifdef CONFIG_OF_GPIO 23972052fccSSimon Guinot /* 24072052fccSSimon Guinot * Translate OpenFirmware node properties into platform_data. 24172052fccSSimon Guinot */ 242cf4af012SLinus Torvalds static int 24372052fccSSimon Guinot ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) 24472052fccSSimon Guinot { 24572052fccSSimon Guinot struct device_node *np = dev->of_node; 24672052fccSSimon Guinot struct device_node *child; 247f7fafd08SVincent Donnefort struct ns2_led *led, *leds; 24879937a4bSNishka Dasgupta int ret, num_leds = 0; 24972052fccSSimon Guinot 25072052fccSSimon Guinot num_leds = of_get_child_count(np); 25172052fccSSimon Guinot if (!num_leds) 25272052fccSSimon Guinot return -ENODEV; 25372052fccSSimon Guinot 254a86854d0SKees Cook leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led), 25572052fccSSimon Guinot GFP_KERNEL); 25672052fccSSimon Guinot if (!leds) 25772052fccSSimon Guinot return -ENOMEM; 25872052fccSSimon Guinot 259f7fafd08SVincent Donnefort led = leds; 26072052fccSSimon Guinot for_each_child_of_node(np, child) { 26172052fccSSimon Guinot const char *string; 26279937a4bSNishka Dasgupta int i, num_modes; 263f7fafd08SVincent Donnefort struct ns2_led_modval *modval; 26472052fccSSimon Guinot 26572052fccSSimon Guinot ret = of_get_named_gpio(child, "cmd-gpio", 0); 26672052fccSSimon Guinot if (ret < 0) 26779937a4bSNishka Dasgupta goto err_node_put; 268f7fafd08SVincent Donnefort led->cmd = ret; 26972052fccSSimon Guinot ret = of_get_named_gpio(child, "slow-gpio", 0); 27072052fccSSimon Guinot if (ret < 0) 27179937a4bSNishka Dasgupta goto err_node_put; 272f7fafd08SVincent Donnefort led->slow = ret; 27372052fccSSimon Guinot ret = of_property_read_string(child, "label", &string); 274f7fafd08SVincent Donnefort led->name = (ret == 0) ? string : child->name; 27572052fccSSimon Guinot ret = of_property_read_string(child, "linux,default-trigger", 27672052fccSSimon Guinot &string); 27772052fccSSimon Guinot if (ret == 0) 278f7fafd08SVincent Donnefort led->default_trigger = string; 27972052fccSSimon Guinot 280f7fafd08SVincent Donnefort ret = of_property_count_u32_elems(child, "modes-map"); 281f7fafd08SVincent Donnefort if (ret < 0 || ret % 3) { 282f7fafd08SVincent Donnefort dev_err(dev, 283f7fafd08SVincent Donnefort "Missing or malformed modes-map property\n"); 28479937a4bSNishka Dasgupta ret = -EINVAL; 28579937a4bSNishka Dasgupta goto err_node_put; 286f7fafd08SVincent Donnefort } 287f7fafd08SVincent Donnefort 288f7fafd08SVincent Donnefort num_modes = ret / 3; 289a86854d0SKees Cook modval = devm_kcalloc(dev, 290a86854d0SKees Cook num_modes, 291a86854d0SKees Cook sizeof(struct ns2_led_modval), 292f7fafd08SVincent Donnefort GFP_KERNEL); 29379937a4bSNishka Dasgupta if (!modval) { 29479937a4bSNishka Dasgupta ret = -ENOMEM; 29579937a4bSNishka Dasgupta goto err_node_put; 29679937a4bSNishka Dasgupta } 297f7fafd08SVincent Donnefort 298f7fafd08SVincent Donnefort for (i = 0; i < num_modes; i++) { 299f7fafd08SVincent Donnefort of_property_read_u32_index(child, 300f7fafd08SVincent Donnefort "modes-map", 3 * i, 301f7fafd08SVincent Donnefort (u32 *) &modval[i].mode); 302f7fafd08SVincent Donnefort of_property_read_u32_index(child, 303f7fafd08SVincent Donnefort "modes-map", 3 * i + 1, 304f7fafd08SVincent Donnefort (u32 *) &modval[i].cmd_level); 305f7fafd08SVincent Donnefort of_property_read_u32_index(child, 306f7fafd08SVincent Donnefort "modes-map", 3 * i + 2, 307f7fafd08SVincent Donnefort (u32 *) &modval[i].slow_level); 308f7fafd08SVincent Donnefort } 309f7fafd08SVincent Donnefort 310f7fafd08SVincent Donnefort led->num_modes = num_modes; 311f7fafd08SVincent Donnefort led->modval = modval; 312f7fafd08SVincent Donnefort 313f7fafd08SVincent Donnefort led++; 31472052fccSSimon Guinot } 31572052fccSSimon Guinot 31672052fccSSimon Guinot pdata->leds = leds; 31772052fccSSimon Guinot pdata->num_leds = num_leds; 31872052fccSSimon Guinot 31972052fccSSimon Guinot return 0; 32079937a4bSNishka Dasgupta 32179937a4bSNishka Dasgupta err_node_put: 32279937a4bSNishka Dasgupta of_node_put(child); 32379937a4bSNishka Dasgupta return ret; 32472052fccSSimon Guinot } 32572052fccSSimon Guinot 32672052fccSSimon Guinot static const struct of_device_id of_ns2_leds_match[] = { 32772052fccSSimon Guinot { .compatible = "lacie,ns2-leds", }, 32872052fccSSimon Guinot {}, 32972052fccSSimon Guinot }; 33098f9cc7fSLuis de Bethencourt MODULE_DEVICE_TABLE(of, of_ns2_leds_match); 33172052fccSSimon Guinot #endif /* CONFIG_OF_GPIO */ 33272052fccSSimon Guinot 3333de1929bSSimon Guinot struct ns2_led_priv { 3343de1929bSSimon Guinot int num_leds; 3353de1929bSSimon Guinot struct ns2_led_data leds_data[]; 3363de1929bSSimon Guinot }; 3373de1929bSSimon Guinot 3383de1929bSSimon Guinot static inline int sizeof_ns2_led_priv(int num_leds) 3393de1929bSSimon Guinot { 3403de1929bSSimon Guinot return sizeof(struct ns2_led_priv) + 3413de1929bSSimon Guinot (sizeof(struct ns2_led_data) * num_leds); 3423de1929bSSimon Guinot } 3433de1929bSSimon Guinot 34498ea1ea2SBill Pemberton static int ns2_led_probe(struct platform_device *pdev) 34511efe71fSSimon Guinot { 34687aae1eaSJingoo Han struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev); 3473de1929bSSimon Guinot struct ns2_led_priv *priv; 34811efe71fSSimon Guinot int i; 34911efe71fSSimon Guinot int ret; 35011efe71fSSimon Guinot 35172052fccSSimon Guinot #ifdef CONFIG_OF_GPIO 35272052fccSSimon Guinot if (!pdata) { 35372052fccSSimon Guinot pdata = devm_kzalloc(&pdev->dev, 35472052fccSSimon Guinot sizeof(struct ns2_led_platform_data), 35572052fccSSimon Guinot GFP_KERNEL); 35672052fccSSimon Guinot if (!pdata) 35772052fccSSimon Guinot return -ENOMEM; 35872052fccSSimon Guinot 35972052fccSSimon Guinot ret = ns2_leds_get_of_pdata(&pdev->dev, pdata); 36072052fccSSimon Guinot if (ret) 36172052fccSSimon Guinot return ret; 36272052fccSSimon Guinot } 36372052fccSSimon Guinot #else 36411efe71fSSimon Guinot if (!pdata) 36511efe71fSSimon Guinot return -EINVAL; 36672052fccSSimon Guinot #endif /* CONFIG_OF_GPIO */ 36711efe71fSSimon Guinot 3683de1929bSSimon Guinot priv = devm_kzalloc(&pdev->dev, 3693de1929bSSimon Guinot sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL); 3703de1929bSSimon Guinot if (!priv) 37111efe71fSSimon Guinot return -ENOMEM; 3723de1929bSSimon Guinot priv->num_leds = pdata->num_leds; 37311efe71fSSimon Guinot 3743de1929bSSimon Guinot for (i = 0; i < priv->num_leds; i++) { 3753de1929bSSimon Guinot ret = create_ns2_led(pdev, &priv->leds_data[i], 3763de1929bSSimon Guinot &pdata->leds[i]); 377a209f766SBryan Wu if (ret < 0) { 378a209f766SBryan Wu for (i = i - 1; i >= 0; i--) 3793de1929bSSimon Guinot delete_ns2_led(&priv->leds_data[i]); 380a209f766SBryan Wu return ret; 381a209f766SBryan Wu } 38211efe71fSSimon Guinot } 38311efe71fSSimon Guinot 3843de1929bSSimon Guinot platform_set_drvdata(pdev, priv); 38511efe71fSSimon Guinot 38611efe71fSSimon Guinot return 0; 38711efe71fSSimon Guinot } 38811efe71fSSimon Guinot 389678e8a6bSBill Pemberton static int ns2_led_remove(struct platform_device *pdev) 39011efe71fSSimon Guinot { 39111efe71fSSimon Guinot int i; 3923de1929bSSimon Guinot struct ns2_led_priv *priv; 39311efe71fSSimon Guinot 3943de1929bSSimon Guinot priv = platform_get_drvdata(pdev); 39511efe71fSSimon Guinot 3963de1929bSSimon Guinot for (i = 0; i < priv->num_leds; i++) 3973de1929bSSimon Guinot delete_ns2_led(&priv->leds_data[i]); 39811efe71fSSimon Guinot 39911efe71fSSimon Guinot return 0; 40011efe71fSSimon Guinot } 40111efe71fSSimon Guinot 40211efe71fSSimon Guinot static struct platform_driver ns2_led_driver = { 40311efe71fSSimon Guinot .probe = ns2_led_probe, 404df07cf81SBill Pemberton .remove = ns2_led_remove, 40511efe71fSSimon Guinot .driver = { 40611efe71fSSimon Guinot .name = "leds-ns2", 40772052fccSSimon Guinot .of_match_table = of_match_ptr(of_ns2_leds_match), 40811efe71fSSimon Guinot }, 40911efe71fSSimon Guinot }; 41011efe71fSSimon Guinot 411892a8843SAxel Lin module_platform_driver(ns2_led_driver); 41211efe71fSSimon Guinot 41311efe71fSSimon Guinot MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); 41411efe71fSSimon Guinot MODULE_DESCRIPTION("Network Space v2 LED driver"); 41511efe71fSSimon Guinot MODULE_LICENSE("GPL"); 416892a8843SAxel Lin MODULE_ALIAS("platform:leds-ns2"); 417