aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Wood <brian.j.wood@intel.com>2015-10-08 11:13:10 -0700
committerLee Campbell <leecam@google.com>2015-10-12 13:27:05 -0700
commit2009e5703a0a50d212ccf61db3a266f81d667207 (patch)
tree66cc588b365e7981e34b66a86a7f55a0baf84127
parent285401dc5e241c8afd753302c52a9e6b9a824295 (diff)
downloadedison-v3.10-2009e5703a0a50d212ccf61db3a266f81d667207.tar.gz
Adding initial GPIO LED driver for Edison
This is an initial GPIO LED driver for the Edison Brillo project. It will likely be replaced with a far more functional driver, but this will unblock end customer wishing to test userspace applications and associated HAL. Change-Id: Ib7942b2f8df1f3b298a2a766d0b833168ac407c2 Signed-off-by: Brian Wood <brian.j.wood@intel.com>
-rw-r--r--arch/x86/configs/i386_brillo_edison_defconfig3
-rw-r--r--arch/x86/platform/intel-mid/device_libs/Makefile1
-rw-r--r--arch/x86/platform/intel-mid/device_libs/platform_gpio_leds.c30
-rw-r--r--drivers/leds/Kconfig9
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-edison-gpio-flash.c248
6 files changed, 291 insertions, 1 deletions
diff --git a/arch/x86/configs/i386_brillo_edison_defconfig b/arch/x86/configs/i386_brillo_edison_defconfig
index 0b129bded66..9add69cafd8 100644
--- a/arch/x86/configs/i386_brillo_edison_defconfig
+++ b/arch/x86/configs/i386_brillo_edison_defconfig
@@ -2828,7 +2828,7 @@ CONFIG_LEDS_CLASS=y
# CONFIG_LEDS_LM3530 is not set
# CONFIG_LEDS_LM3642 is not set
# CONFIG_LEDS_PCA9532 is not set
-# CONFIG_LEDS_GPIO is not set
+CONFIG_LEDS_GPIO=y
# CONFIG_LEDS_LP3944 is not set
# CONFIG_LEDS_LP5521 is not set
# CONFIG_LEDS_LP5523 is not set
@@ -2840,6 +2840,7 @@ CONFIG_LEDS_CLASS=y
# CONFIG_LEDS_REGULATOR is not set
# CONFIG_LEDS_BD2802 is not set
# CONFIG_LEDS_INTEL_SS4200 is not set
+CONFIG_LEDS_INTEL_GPIO_FLASH=y
# CONFIG_LEDS_LT3593 is not set
# CONFIG_LEDS_TCA6507 is not set
# CONFIG_LEDS_LM355x is not set
diff --git a/arch/x86/platform/intel-mid/device_libs/Makefile b/arch/x86/platform/intel-mid/device_libs/Makefile
index 20b06c5bed9..7881b06cadd 100644
--- a/arch/x86/platform/intel-mid/device_libs/Makefile
+++ b/arch/x86/platform/intel-mid/device_libs/Makefile
@@ -44,6 +44,7 @@ obj-$(subst m,y,$(CONFIG_SPI_SPIDEV)) += platform_spidev.o
obj-$(subst m,y,$(CONFIG_TI_ADS7955_ADC)) += platform_ads7955.o
# MISC Devices
obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
+obj-$(subst m,y,$(CONFIG_LEDS_INTEL_GPIO_FLASH)) += platform_gpio_leds.o
obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_wdt.o
# ADC
obj-$(subst m,y,$(CONFIG_MSIC_GPADC)) += platform_msic_adc.o
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_gpio_leds.c b/arch/x86/platform/intel-mid/device_libs/platform_gpio_leds.c
new file mode 100644
index 00000000000..00ea922bc19
--- /dev/null
+++ b/arch/x86/platform/intel-mid/device_libs/platform_gpio_leds.c
@@ -0,0 +1,30 @@
+/*
+ * platform_gpio_leds.c: gpio_leds platform data initilization file
+ *
+ * (C) Copyright 2015 Intel Corporation
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <asm/intel-mid.h>
+
+
+static struct platform_device led_dev = {
+ .name = "leds-edison-gpio-flash",
+ .id = -1,
+};
+
+static int __init leds_gpio_init(void)
+{
+ pr_info("%s: Registering Intel LED GPIO platform device\n", __func__);
+ return platform_device_register(&led_dev);
+}
+
+rootfs_initcall(leds_gpio_init);
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index ef992293598..d5de750a515 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -359,6 +359,15 @@ config LEDS_INTEL_SS4200
drive or power LEDs on the front panel. Using this driver
can stop the front LED from blinking after startup.
+config LEDS_INTEL_GPIO_FLASH
+ tristate "Support for GPIO Flash LEDs"
+ help
+ This driver supports the leds functionality of GPIO Flash LED. It
+ includes flash mode and torch mode.
+
+ To compile this driver as a module, choose M here: the module will
+ be called leds-gpio-flash.
+
config LEDS_LT3593
tristate "LED driver for LT3593 controllers"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index ac2897732b0..7bdf86f67da 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o
+obj-$(CONFIG_LEDS_INTEL_GPIO_FLASH) += leds-edison-gpio-flash.o
obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o
obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o
obj-$(CONFIG_LEDS_DELL_NETBOOKS) += dell-led.o
diff --git a/drivers/leds/leds-edison-gpio-flash.c b/drivers/leds/leds-edison-gpio-flash.c
new file mode 100644
index 00000000000..8363212c610
--- /dev/null
+++ b/drivers/leds/leds-edison-gpio-flash.c
@@ -0,0 +1,248 @@
+
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * (C) Copyright 2015 Intel Corporation
+ *
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/printk.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+
+/* #define CONFIG_GPIO_FLASH_DEBUG */
+#undef CDBG
+#ifdef CONFIG_GPIO_FLASH_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+#define LED_GPIO_FLASH_DRIVER_NAME "leds-edison-gpio-flash"
+#define LED_TRIGGER_DEFAULT "none"
+
+#define GPIO_PIN_NUM 243 /* temporary hardcoded value for development */
+#define GPIO_PIN_MUX_NUM 261 /* " " " " " " */
+#define GPIO_LABEL "DS2"
+#define GPIO_LABEL_MUX "DS2_MUX"
+
+#define GPIO_OUT_LOW (0 << 1)
+#define GPIO_OUT_HIGH (1 << 1)
+
+struct led_gpio_flash_data {
+ struct led_classdev cdev;
+ int gpio_pin_num; /* record single GPIO number, hard coded 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);
+ while(1) {
+ if (ret) {
+ pr_debug("ON\n");
+ gpio_set_value_cansleep(flash_led->gpio_pin_num, 0);
+ ret = 0;
+ } else { /* ret == 0 */
+ pr_debug("OFF\n");
+ gpio_set_value_cansleep(flash_led->gpio_pin_num, 1);
+ ret = 1;
+ }
+ pr_debug("Delay \n");
+ mdelay(10000/(flash_led->cdev.brightness+1));
+ if (flash_led->cdev.brightness == LED_OFF ||
+ flash_led->cdev.brightness == LED_FULL)
+ break;
+ }
+}
+
+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 */
+ pr_debug("%s: Turning on LED, brightness=%d\n", __func__, brightness);
+ gpio_set_value_cansleep(flash_led->gpio_pin_num, 0);
+ if (gpio_get_value_cansleep(flash_led->gpio_pin_num)) { /* if 0 we failed to set */
+ pr_err("%s: Failed to set gpio %d\n", __func__,
+ 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: Turning off LED, brightness=%d\n", __func__, brightness);
+ gpio_set_value_cansleep(flash_led->gpio_pin_num, 1);
+ if (gpio_get_value_cansleep(flash_led->gpio_pin_num)) { /* if nonzero we failed to set */
+ pr_err("%s: Failed to set gpio %d\n", __func__,
+ 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);
+ }
+
+ flash_led->cdev.brightness = brightness;
+ pr_debug("%s: Stored new value for brightness=%d\n", __func__, flash_led->cdev.brightness);
+err:
+ return;
+}
+
+static enum led_brightness led_gpio_brightness_get(struct led_classdev
+ *led_cdev)
+{
+ struct led_gpio_flash_data *flash_led =
+ container_of(led_cdev, struct led_gpio_flash_data, cdev);
+ pr_debug("%s: returning brightness=%d\n", __func__, flash_led->cdev.brightness);
+ return flash_led->cdev.brightness;
+}
+
+int led_gpio_flash_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct led_gpio_flash_data *flash_led = NULL;
+
+ dev_info(&pdev->dev, "%s: enter probe\n", __func__);
+ flash_led = devm_kzalloc(&pdev->dev, sizeof(struct led_gpio_flash_data),
+ GFP_KERNEL);
+ if (flash_led == NULL) {
+ dev_err(&pdev->dev, "%s:%d Unable to allocate memory\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ INIT_WORK(&flash_led->led_flash_work, ledwq_work_handler);
+ flash_led->gpio_pin_num = GPIO_PIN_NUM;
+ flash_led->gpio_pin_mux_num = GPIO_PIN_MUX_NUM;
+ flash_led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
+ dev_info(&pdev->dev, "%s: GPIO LED number=%d, GPIO LED pin mux number=%d, "
+ "default trigger=%s\n", __func__, flash_led->gpio_pin_num,
+ flash_led->gpio_pin_mux_num, flash_led->cdev.default_trigger);
+
+ /* Request the output enable pinmux for the DS2 LED GPIO */
+ rc = gpio_request(flash_led->gpio_pin_mux_num, GPIO_LABEL_MUX);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Failed to request gpio %d,rc = %d\n",
+ __func__, flash_led->gpio_pin_mux_num, rc);
+ goto error;
+ }
+ rc = gpio_direction_output(flash_led->gpio_pin_mux_num, 0); /* Make sure the direction is out */
+ if (rc)
+ dev_info(&pdev->dev, "%s: Setting GPIO %d direction to out failed!, rc=%d\n",
+ __func__, flash_led->gpio_pin_mux_num, rc);
+ else
+ dev_info(&pdev->dev, "%s: Setting GPIO %d direction to out succeeded, "
+ "rc=%d.\n", __func__, flash_led->gpio_pin_mux_num, rc);
+
+ /* Request the DS2 LED GPIO */
+ rc = gpio_request(flash_led->gpio_pin_num, GPIO_LABEL);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Failed to request gpio %d,rc = %d\n",
+ __func__, flash_led->gpio_pin_num, rc);
+ goto error;
+ }
+ rc = gpio_direction_output(flash_led->gpio_pin_num, 1); /* Make sure the direction is out */
+ if (rc)
+ dev_info(&pdev->dev, "%s: Setting GPIO %d direction to out succeeded, "
+ "rc=%d.\n", __func__, flash_led->gpio_pin_num, rc);
+ else
+ dev_info(&pdev->dev, "%s: Setting GPIO %d direction to out succeeded, "
+ "rc=%d.\n", __func__, flash_led->gpio_pin_num, rc);
+
+ /* If GPIO value is 0 set to 1 so light is off initially */
+ rc = gpio_get_value_cansleep(flash_led->gpio_pin_num);
+ dev_info(&pdev->dev, "%s: GPIO (%d) initial value=%d, "
+ "changing value to %d\n",
+ __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_info(&pdev->dev, "%s: GPIO value=%d\n", __func__, flash_led->gpio_value);
+
+ flash_led->cdev.name = "DS2_Green_LED";
+ dev_info(&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.brightness_set = led_gpio_brightness_set;
+ flash_led->cdev.brightness_get = led_gpio_brightness_get;
+
+ rc = led_classdev_register(&pdev->dev, &flash_led->cdev);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Failed to register led dev. rc = %d\n",
+ __func__, rc);
+ goto error;
+ }
+ dev_info(&pdev->dev, "%s:probe successfully!\n", __func__);
+ return 0;
+
+error:
+ devm_kfree(&pdev->dev, flash_led);
+ return rc;
+}
+
+int led_gpio_flash_remove(struct platform_device *pdev)
+{
+ struct led_gpio_flash_data *flash_led =
+ (struct led_gpio_flash_data *)platform_get_drvdata(pdev);
+ gpio_free(flash_led->gpio_pin_num);
+ gpio_free(flash_led->gpio_pin_mux_num);
+ led_classdev_unregister(&flash_led->cdev);
+ devm_kfree(&pdev->dev, flash_led);
+ pr_debug("%s: Unregistering Intel LED GPIO driver", __func__);
+ return 0;
+}
+
+static struct platform_driver led_gpio_flash_driver = {
+ .probe = led_gpio_flash_probe,
+ .remove = led_gpio_flash_remove,
+ .driver = {
+ .name = LED_GPIO_FLASH_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init led_gpio_flash_init(void)
+{
+ pr_debug("%s: Registering Intel LED GPIO driver\n", __func__);
+ return platform_driver_register(&led_gpio_flash_driver);
+}
+
+static void __exit led_gpio_flash_exit(void)
+{
+ pr_debug("%s: Unregistering Intel LED GPIO driver\n", __func__);
+ return platform_driver_unregister(&led_gpio_flash_driver);
+}
+
+late_initcall(led_gpio_flash_init);
+module_exit(led_gpio_flash_exit);
+
+MODULE_DESCRIPTION("Intel GPIO LEDs driver");
+MODULE_LICENSE("GPL v2");
+