diff options
author | Brian Wood <brian.j.wood@intel.com> | 2015-10-08 11:13:10 -0700 |
---|---|---|
committer | Lee Campbell <leecam@google.com> | 2015-10-12 13:27:05 -0700 |
commit | 2009e5703a0a50d212ccf61db3a266f81d667207 (patch) | |
tree | 66cc588b365e7981e34b66a86a7f55a0baf84127 | |
parent | 285401dc5e241c8afd753302c52a9e6b9a824295 (diff) | |
download | edison-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_defconfig | 3 | ||||
-rw-r--r-- | arch/x86/platform/intel-mid/device_libs/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/platform/intel-mid/device_libs/platform_gpio_leds.c | 30 | ||||
-rw-r--r-- | drivers/leds/Kconfig | 9 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-edison-gpio-flash.c | 248 |
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"); + |