--- zzzz-none-000/linux-3.10.107/drivers/leds/leds-ns2.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/leds/leds-ns2.c 2021-02-04 17:41:59.000000000 +0000 @@ -23,58 +23,46 @@ */ #include -#include #include #include #include #include #include #include +#include #include +#include "leds.h" /* - * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in - * relation with the SATA activity. This capability is exposed through the - * "sata" sysfs attribute. - * - * The following array detail the different LED registers and the combination - * of their possible values: - * - * cmd_led | slow_led | /SATA active | LED state - * | | | - * 1 | 0 | x | off - * - | 1 | x | on - * 0 | 0 | 1 | on - * 0 | 0 | 0 | blink (rate 300ms) + * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED + * modes are available: off, on and SATA activity blinking. The LED modes are + * controlled through two GPIOs (command and slow): each combination of values + * for the command/slow GPIOs corresponds to a LED mode. */ -enum ns2_led_modes { - NS_V2_LED_OFF, - NS_V2_LED_ON, - NS_V2_LED_SATA, -}; - -struct ns2_led_mode_value { - enum ns2_led_modes mode; - int cmd_level; - int slow_level; -}; - -static struct ns2_led_mode_value ns2_led_modval[] = { - { NS_V2_LED_OFF , 1, 0 }, - { NS_V2_LED_ON , 0, 1 }, - { NS_V2_LED_ON , 1, 1 }, - { NS_V2_LED_SATA, 0, 0 }, -}; - struct ns2_led_data { struct led_classdev cdev; unsigned cmd; unsigned slow; + bool can_sleep; + int mode_index; unsigned char sata; /* True when SATA mode active. */ rwlock_t rw_lock; /* Lock GPIOs. */ + struct work_struct work; + int num_modes; + struct ns2_led_modval *modval; }; +static void ns2_led_work(struct work_struct *work) +{ + struct ns2_led_data *led_dat = + container_of(work, struct ns2_led_data, work); + int i = led_dat->mode_index; + + gpio_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level); + gpio_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level); +} + static int ns2_led_get_mode(struct ns2_led_data *led_dat, enum ns2_led_modes *mode) { @@ -83,22 +71,18 @@ int cmd_level; int slow_level; - read_lock_irq(&led_dat->rw_lock); + cmd_level = gpio_get_value_cansleep(led_dat->cmd); + slow_level = gpio_get_value_cansleep(led_dat->slow); - cmd_level = gpio_get_value(led_dat->cmd); - slow_level = gpio_get_value(led_dat->slow); - - for (i = 0; i < ARRAY_SIZE(ns2_led_modval); i++) { - if (cmd_level == ns2_led_modval[i].cmd_level && - slow_level == ns2_led_modval[i].slow_level) { - *mode = ns2_led_modval[i].mode; + for (i = 0; i < led_dat->num_modes; i++) { + if (cmd_level == led_dat->modval[i].cmd_level && + slow_level == led_dat->modval[i].slow_level) { + *mode = led_dat->modval[i].mode; ret = 0; break; } } - read_unlock_irq(&led_dat->rw_lock); - return ret; } @@ -106,19 +90,32 @@ enum ns2_led_modes mode) { int i; + bool found = false; unsigned long flags; + for (i = 0; i < led_dat->num_modes; i++) + if (mode == led_dat->modval[i].mode) { + found = true; + break; + } + + if (!found) + return; + write_lock_irqsave(&led_dat->rw_lock, flags); - for (i = 0; i < ARRAY_SIZE(ns2_led_modval); i++) { - if (mode == ns2_led_modval[i].mode) { - gpio_set_value(led_dat->cmd, - ns2_led_modval[i].cmd_level); - gpio_set_value(led_dat->slow, - ns2_led_modval[i].slow_level); - } + if (!led_dat->can_sleep) { + gpio_set_value(led_dat->cmd, + led_dat->modval[i].cmd_level); + gpio_set_value(led_dat->slow, + led_dat->modval[i].slow_level); + goto exit_unlock; } + led_dat->mode_index = i; + schedule_work(&led_dat->work); + +exit_unlock: write_unlock_irqrestore(&led_dat->rw_lock, flags); } @@ -148,7 +145,6 @@ container_of(led_cdev, struct ns2_led_data, cdev); int ret; unsigned long enable; - enum ns2_led_modes mode; ret = kstrtoul(buff, 10, &enable); if (ret < 0) @@ -157,19 +153,19 @@ enable = !!enable; if (led_dat->sata == enable) - return count; + goto exit; - ret = ns2_led_get_mode(led_dat, &mode); - if (ret < 0) - return ret; + led_dat->sata = enable; + + if (!led_get_brightness(led_cdev)) + goto exit; - if (enable && mode == NS_V2_LED_ON) + if (enable) ns2_led_set_mode(led_dat, NS_V2_LED_SATA); - if (!enable && mode == NS_V2_LED_SATA) + else ns2_led_set_mode(led_dat, NS_V2_LED_ON); - led_dat->sata = enable; - +exit: return count; } @@ -185,6 +181,12 @@ static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); +static struct attribute *ns2_led_attrs[] = { + &dev_attr_sata.attr, + NULL +}; +ATTRIBUTE_GROUPS(ns2_led); + static int create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, const struct ns2_led *template) @@ -193,7 +195,7 @@ enum ns2_led_modes mode; ret = devm_gpio_request_one(&pdev->dev, template->cmd, - gpio_get_value(template->cmd) ? + gpio_get_value_cansleep(template->cmd) ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, template->name); if (ret) { @@ -203,7 +205,7 @@ } ret = devm_gpio_request_one(&pdev->dev, template->slow, - gpio_get_value(template->slow) ? + gpio_get_value_cansleep(template->slow) ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, template->name); if (ret) { @@ -219,8 +221,13 @@ led_dat->cdev.blink_set = NULL; led_dat->cdev.brightness_set = ns2_led_set; led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; + led_dat->cdev.groups = ns2_led_groups; led_dat->cmd = template->cmd; led_dat->slow = template->slow; + led_dat->can_sleep = gpio_cansleep(led_dat->cmd) | + gpio_cansleep(led_dat->slow); + led_dat->modval = template->modval; + led_dat->num_modes = template->num_modes; ret = ns2_led_get_mode(led_dat, &mode); if (ret < 0) @@ -231,25 +238,19 @@ led_dat->cdev.brightness = (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL; + INIT_WORK(&led_dat->work, ns2_led_work); + ret = led_classdev_register(&pdev->dev, &led_dat->cdev); if (ret < 0) return ret; - ret = device_create_file(led_dat->cdev.dev, &dev_attr_sata); - if (ret < 0) - goto err_free_cdev; - return 0; - -err_free_cdev: - led_classdev_unregister(&led_dat->cdev); - return ret; } 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); + cancel_work_sync(&led_dat->work); } #ifdef CONFIG_OF_GPIO @@ -261,9 +262,8 @@ { struct device_node *np = dev->of_node; struct device_node *child; - struct ns2_led *leds; + struct ns2_led *led, *leds; int num_leds = 0; - int i = 0; num_leds = of_get_child_count(np); if (!num_leds) @@ -274,26 +274,57 @@ if (!leds) return -ENOMEM; + led = leds; for_each_child_of_node(np, child) { const char *string; - int ret; + int ret, i, num_modes; + struct ns2_led_modval *modval; ret = of_get_named_gpio(child, "cmd-gpio", 0); if (ret < 0) return ret; - leds[i].cmd = ret; + led->cmd = ret; ret = of_get_named_gpio(child, "slow-gpio", 0); if (ret < 0) return ret; - leds[i].slow = ret; + led->slow = ret; ret = of_property_read_string(child, "label", &string); - leds[i].name = (ret == 0) ? string : child->name; + led->name = (ret == 0) ? string : child->name; ret = of_property_read_string(child, "linux,default-trigger", &string); if (ret == 0) - leds[i].default_trigger = string; + led->default_trigger = string; - i++; + ret = of_property_count_u32_elems(child, "modes-map"); + if (ret < 0 || ret % 3) { + dev_err(dev, + "Missing or malformed modes-map property\n"); + return -EINVAL; + } + + num_modes = ret / 3; + modval = devm_kzalloc(dev, + num_modes * sizeof(struct ns2_led_modval), + GFP_KERNEL); + if (!modval) + return -ENOMEM; + + for (i = 0; i < num_modes; i++) { + of_property_read_u32_index(child, + "modes-map", 3 * i, + (u32 *) &modval[i].mode); + of_property_read_u32_index(child, + "modes-map", 3 * i + 1, + (u32 *) &modval[i].cmd_level); + of_property_read_u32_index(child, + "modes-map", 3 * i + 2, + (u32 *) &modval[i].slow_level); + } + + led->num_modes = num_modes; + led->modval = modval; + + led++; } pdata->leds = leds; @@ -306,6 +337,7 @@ { .compatible = "lacie,ns2-leds", }, {}, }; +MODULE_DEVICE_TABLE(of, of_ns2_leds_match); #endif /* CONFIG_OF_GPIO */ struct ns2_led_priv { @@ -321,7 +353,7 @@ static int ns2_led_probe(struct platform_device *pdev) { - struct ns2_led_platform_data *pdata = pdev->dev.platform_data; + struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev); struct ns2_led_priv *priv; int i; int ret; @@ -374,8 +406,6 @@ for (i = 0; i < priv->num_leds; i++) delete_ns2_led(&priv->leds_data[i]); - platform_set_drvdata(pdev, NULL); - return 0; } @@ -384,7 +414,6 @@ .remove = ns2_led_remove, .driver = { .name = "leds-ns2", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(of_ns2_leds_match), }, };