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 <X>ms configurable LED on/off values.
- General cleanup of code.

Change-Id: I1bcf2cc8295a66c5e45ebab88ef6846698f63f72
Signed-off-by: Brian Wood <brian.j.wood@intel.com>
Reviewed-on: https://android.intel.com/425245
diff --git a/drivers/leds/leds-edison-gpio-flash.c b/drivers/leds/leds-edison-gpio-flash.c
index 7371cdd..595bafe 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 <brian.j.wood@intel.com>
  *
+ * 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 <linux/workqueue.h>
 #include <linux/delay.h>
 #include <linux/sched.h>
+#include <linux/device.h>
 
 #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 @@
 				 * 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 void led_gpio_brightness_set(struct led_classdev *led_cdev,
-				    enum led_brightness value)
+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);
+		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)
+{
+	struct led_gpio_flash_data *flash_led =
+		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 @@
 				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 @@
 				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 @@
 		__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 @@
 			__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 @@
 	    (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__);