From 1ce4748a4d78acdc2c4b7cef35181306ed340ef9 Mon Sep 17 00:00:00 2001 From: Brian Wood Date: Wed, 14 Oct 2015 11:09:07 -0700 Subject: Adding new features to LED driver This commit is adding the following features: - Millisecond delay_on/delay_off sysfs device file entries added to support ms configurable LED on/off values. - General cleanup of code. Change-Id: I1bcf2cc8295a66c5e45ebab88ef6846698f63f72 Signed-off-by: Brian Wood Reviewed-on: https://android.intel.com/425245 --- drivers/leds/leds-edison-gpio-flash.c | 160 ++++++++++++++++++++++++++++------ 1 file changed, 131 insertions(+), 29 deletions(-) diff --git a/drivers/leds/leds-edison-gpio-flash.c b/drivers/leds/leds-edison-gpio-flash.c index 7371cdd1495..595bafeb5c1 100644 --- a/drivers/leds/leds-edison-gpio-flash.c +++ b/drivers/leds/leds-edison-gpio-flash.c @@ -1,8 +1,9 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. - * (C) Copyright 2015 Intel Corporation +/* (C) Copyright 2015 Intel Corporation * Author: Brian Wood * + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. @@ -24,9 +25,11 @@ #include #include #include +#include #define LED_GPIO_FLASH_DRIVER_NAME "leds-edison-gpio-flash" #define LED_TRIGGER_DEFAULT "none" +#define LED_MAX_BRIGHTNESS 1 #define GPIO_PIN_NUM 243 /* temporary hardcoded value for development */ #define GPIO_PIN_MUX_NUM 261 /* " " " " " " */ @@ -42,51 +45,131 @@ struct led_gpio_flash_data { * value for now (will change) */ int gpio_pin_mux_num; - int gpio_value; /* record single GPIO value on/off -> 1/0 */ struct work_struct led_flash_work; }; /* Workqueue handler function */ static void ledwq_work_handler(struct work_struct *ws) { - pr_debug("%s: Use the workqueue for flashing the LED\n", __func__); int ret = 0; struct led_gpio_flash_data *flash_led = container_of(ws, struct led_gpio_flash_data, led_flash_work); + + pr_debug("%s: Use the workqueue for flashing the LED\n", __func__); + while(1) { if (ret) { pr_debug("ON\n"); gpio_set_value_cansleep(flash_led->gpio_pin_num, 0); ret = 0; + msleep(flash_led->cdev.blink_delay_on); } else { /* ret == 0 */ pr_debug("OFF\n"); gpio_set_value_cansleep(flash_led->gpio_pin_num, 1); ret = 1; + msleep(flash_led->cdev.blink_delay_off); } - pr_debug("Delay...\n"); - /* Code below is an alternate timer that would use jiffies, - * it's more efficient than msleep but with HZ at 100 it - * can be unpredictable with values <10ms; this + /* Code below is an alternate timer code flow that would use + * jiffies, it's more efficient than msleep but with HZ at + * 100 it can be unpredictable with values <10ms; this * could lead to less predictable flash patterns with - * faster flashing (higher brightness values). + * faster flashing (lower ms values for delay_on/delay_off). set_current_state(TASK_INTERRUPTIBLE); schedule_timeout (1000/(flash_led->cdev.brightness+1)); */ - msleep(10000/(flash_led->cdev.brightness+1)); - if (flash_led->cdev.brightness == LED_OFF || - flash_led->cdev.brightness == LED_FULL) + if (!flash_led->cdev.blink_delay_on || + !flash_led->cdev.blink_delay_off) break; } } +static int led_gpio_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct led_gpio_flash_data *flash_led = + container_of(led_cdev, struct led_gpio_flash_data, cdev); + + flash_led->cdev.brightness = 0; + pr_debug("%s: Reset brightness=%d, setup flashing LED " + "delay_on/delay_off\n", + __func__, flash_led->cdev.brightness); + pr_debug("%s: delay_on=%lu, delay_off=%lu\n", + __func__, *delay_on, *delay_off); + + pr_debug("%s: Prepare and queue work\n", __func__); + schedule_work(&flash_led->led_flash_work); + + + return 0; +} + +static ssize_t led_delay_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + return sprintf(buf, "%lu\n", led_cdev->blink_delay_on); +} + +static ssize_t led_delay_on_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + unsigned long state; + ssize_t ret = -EINVAL; + + ret = kstrtoul(buf, 10, &state); + if (ret) + return ret; + + led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off); + led_cdev->blink_delay_on = state; + +return size; +} + +static ssize_t led_delay_off_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + return sprintf(buf, "%lu\n", led_cdev->blink_delay_off); +} + +static ssize_t led_delay_off_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + unsigned long state; + ssize_t ret = -EINVAL; + + ret = kstrtoul(buf, 10, &state); + if (ret) + return ret; + + led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state); + led_cdev->blink_delay_off = state; + + return size; +} + +static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store); +static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store); + + static void led_gpio_brightness_set(struct led_classdev *led_cdev, - enum led_brightness value) + enum led_brightness value) { struct led_gpio_flash_data *flash_led = - container_of(led_cdev, struct led_gpio_flash_data, cdev); + container_of(led_cdev, struct led_gpio_flash_data, cdev); int brightness = value; - if (brightness >= 255) { /* Turn on LED */ + /* Reset blink*/ + flash_led->cdev.blink_delay_on = + flash_led->cdev.blink_delay_off = LED_OFF; + + if (brightness >= 1) { /* Turn on LED */ pr_debug("%s: Turning on LED, brightness=%d\n", __func__, brightness); gpio_set_value_cansleep(flash_led->gpio_pin_num, 0); /* if 0 we failed to set */ @@ -95,9 +178,12 @@ static void led_gpio_brightness_set(struct led_classdev *led_cdev, flash_led->gpio_pin_num); goto err; } - flash_led->gpio_value = gpio_get_value_cansleep(flash_led->gpio_pin_num); - pr_debug("%s: GPIO value=%d\n", __func__, flash_led->gpio_value); - } else if (brightness <= 0) { /* Turn off LED */ + pr_debug("%s: GPIO value=%d\n", __func__, + gpio_get_value_cansleep(flash_led->gpio_pin_num)); + flash_led->cdev.brightness = 1; + pr_debug("%s: Stored new value for brightness=%d\n", __func__, + flash_led->cdev.brightness); + } else { /* brightness <= 0, Turn off LED */ pr_debug("%s: Turning off LED, brightness=%d\n", __func__, brightness); gpio_set_value_cansleep(flash_led->gpio_pin_num, 1); /* if nonzero we failed to set */ @@ -106,16 +192,13 @@ static void led_gpio_brightness_set(struct led_classdev *led_cdev, flash_led->gpio_pin_num); goto err; } - flash_led->gpio_value = gpio_get_value_cansleep(flash_led->gpio_pin_num); - pr_debug("%s: GPIO value=%d\n", __func__, flash_led->gpio_value); - } else { /* Use the workqueue for flashing the LED */ - pr_debug("%s: Prepare and queue work\n", __func__); - schedule_work(&flash_led->led_flash_work); + pr_debug("%s: GPIO value=%d\n", __func__, + gpio_get_value_cansleep(flash_led->gpio_pin_num)); + flash_led->cdev.brightness = 0; + pr_debug("%s: Stored new value for brightness=%d\n", __func__, + flash_led->cdev.brightness); } - flash_led->cdev.brightness = brightness; - pr_debug("%s: Stored new value for brightness=%d\n", __func__, - flash_led->cdev.brightness); err: return; } @@ -189,18 +272,18 @@ int led_gpio_flash_probe(struct platform_device *pdev) __func__, flash_led->gpio_pin_num, rc, 1); if (!rc) gpio_set_value_cansleep(flash_led->gpio_pin_num, 1); - flash_led->gpio_value = gpio_get_value_cansleep(flash_led->gpio_pin_num); dev_dbg(&pdev->dev, "%s: GPIO value=%d\n", - __func__, flash_led->gpio_value); + __func__, gpio_get_value_cansleep(flash_led->gpio_pin_num)); flash_led->cdev.name = "DS2_Green_LED"; dev_dbg(&pdev->dev, "%s: cdev name=%s\n", __func__, flash_led->cdev.name); platform_set_drvdata(pdev, flash_led); - flash_led->cdev.max_brightness = LED_FULL; + flash_led->cdev.max_brightness = LED_MAX_BRIGHTNESS; flash_led->cdev.brightness_set = led_gpio_brightness_set; flash_led->cdev.brightness_get = led_gpio_brightness_get; + flash_led->cdev.blink_set = led_gpio_blink_set; rc = led_classdev_register(&pdev->dev, &flash_led->cdev); if (rc) { @@ -208,11 +291,26 @@ int led_gpio_flash_probe(struct platform_device *pdev) __func__, rc); goto error; } + + rc = device_create_file(flash_led->cdev.dev, &dev_attr_delay_on); + if (rc) + dev_err(&pdev->dev, "%s: cannot create LED dev_attr delay_on, error=%d\n", + __func__, rc); + rc = device_create_file(flash_led->cdev.dev, &dev_attr_delay_off); + if (rc) + dev_err(&pdev->dev, "%s: cannot create LED dev_attr delay_off, error=%d\n", + __func__, rc); + dev_dbg(&pdev->dev, "%s:probe successfully!\n", __func__); return 0; error: + + device_remove_file(flash_led->cdev.dev, &dev_attr_delay_off); + device_remove_file(flash_led->cdev.dev, &dev_attr_delay_on); + devm_kfree(&pdev->dev, flash_led); + return rc; } @@ -222,6 +320,10 @@ int led_gpio_flash_remove(struct platform_device *pdev) (struct led_gpio_flash_data *)platform_get_drvdata(pdev); gpio_free(flash_led->gpio_pin_num); gpio_free(flash_led->gpio_pin_mux_num); + + device_remove_file(flash_led->cdev.dev, &dev_attr_delay_off); + device_remove_file(flash_led->cdev.dev, &dev_attr_delay_on); + led_classdev_unregister(&flash_led->cdev); devm_kfree(&pdev->dev, flash_led); pr_debug("%s: Unregistering Intel LED GPIO driver", __func__); -- cgit v1.2.3