aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Wood <brian.j.wood@intel.com>2015-10-14 11:09:07 -0700
committerBeare, Bruce J <bruce.j.beare@intel.com>2015-10-20 18:29:04 +0100
commit1ce4748a4d78acdc2c4b7cef35181306ed340ef9 (patch)
tree5eba37c3d2674f0731a48d39b0e1b243ac9b4a6a
parentca5f01c23b0959e43063f8fa68123535af5c2b91 (diff)
downloadedison-v3.10-1ce4748a4d78acdc2c4b7cef35181306ed340ef9.tar.gz
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
-rw-r--r--drivers/leds/leds-edison-gpio-flash.c160
1 files 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 <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 @@ 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__);