aboutsummaryrefslogtreecommitdiff
path: root/drivers/leds/leds-edison-gpio-flash.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds/leds-edison-gpio-flash.c')
-rw-r--r--drivers/leds/leds-edison-gpio-flash.c337
1 files changed, 242 insertions, 95 deletions
diff --git a/drivers/leds/leds-edison-gpio-flash.c b/drivers/leds/leds-edison-gpio-flash.c
index 595bafeb5c1..9ca19b13598 100644
--- a/drivers/leds/leds-edison-gpio-flash.c
+++ b/drivers/leds/leds-edison-gpio-flash.c
@@ -31,42 +31,204 @@
#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 /* " " " " " " */
-#define GPIO_LABEL "DS2"
-#define GPIO_LABEL_MUX "DS2_MUX"
+#define GPIO_TRI_STATE_ALL 214 /* hardcoded value for development */
+#define GPIO_PIN_NUM 243 /* " " " " */
+#define GPIO_PIN_MUX_NUM 261 /* " " " " */
+#define GPIO_LABEL "DS2"
+#define GPIO_LABEL_MUX "DS2_MUX"
+#define GPIO_LABEL_TRI_STATE "DS2_TRI_STATE"
#define GPIO_OUT_LOW (0 << 1)
#define GPIO_OUT_HIGH (1 << 1)
+#undef LED_DBG
+#ifdef CONFIG_LEDS_INTEL_GPIO_FLASH_DEBUG
+#define LED_DBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define LED_DBG(fmt, args...)
+#endif
+
struct led_gpio_flash_data {
struct led_classdev cdev;
int gpio_pin_num; /* record single GPIO number, hard coded
- * value for now (will change)
+ * value for now (may change to dynamic)
*/
+ int gpio_pin_state; /* current GPIO state, 1 locked/0 free */
int gpio_pin_mux_num;
+ int gpio_pin_mux_state;
+ int gpio_pin_tri_state_num;
+ int gpio_pin_tri_state_state;
+ int blinking; /* blinking state, 1 enabled/0 disabled */
struct work_struct led_flash_work;
};
-/* Workqueue handler function */
+/* GPIO request/free */
+static int led_gpio_request(struct led_classdev *led_cdev)
+{
+
+ int rc;
+ struct led_gpio_flash_data *flash_led =
+ container_of(led_cdev, struct led_gpio_flash_data, cdev);
+
+ /* Reserve the GPIO's for LED */
+
+ /* Test if GPIO_TRI_STATE_ALL is available, if not we know MRAA
+ * has exported for SPI use
+ */
+ flash_led->gpio_pin_tri_state_state =
+ gpio_request(flash_led->gpio_pin_tri_state_num,
+ GPIO_LABEL_TRI_STATE);
+ if (flash_led->gpio_pin_tri_state_state == -EBUSY) {
+ /* GPIO is exported by MRAA */
+ pr_info("%s: GPIO_TRI_STATE_ALL (%d) is busy/exported "
+ "by someone, we need to set direction to input\n",
+ __func__, flash_led->gpio_pin_tri_state_num);
+ /* Set direction for pin */
+ rc = gpio_direction_input(flash_led->gpio_pin_tri_state_num);
+ if (rc)
+ pr_err("%s: Setting GPIO %d direction to input failed! "
+ ",rc=%d\n", __func__,
+ flash_led->gpio_pin_tri_state_num, rc);
+ else
+ pr_info("%s: Setting GPIO %d direction to input "
+ "succeeded, rc=%d.\n", __func__,
+ flash_led->gpio_pin_tri_state_num, rc);
+
+ } /* else: GPIO is unchanged from defaults, no need to adjust */
+
+ /* Request the output enable pinmux for the DS2 LED GPIO */
+ rc = gpio_request(flash_led->gpio_pin_mux_num, GPIO_LABEL_MUX);
+ if (rc) {
+ pr_err("%s: Failed to request gpio %d,rc = %d\n",
+ __func__, flash_led->gpio_pin_mux_num, rc);
+ /* Reset blink values to 0 since we've failed */
+ flash_led->cdev.blink_delay_on =
+ flash_led->cdev.blink_delay_off =
+ flash_led->blinking = LED_OFF;
+ goto error;
+ }
+ /* Make sure the direction is out */
+ rc = gpio_direction_output(flash_led->gpio_pin_mux_num, 0);
+ if (rc)
+ pr_err("%s: Setting GPIO %d direction to out failed!, rc=%d\n",
+ __func__, flash_led->gpio_pin_mux_num, rc);
+ else
+ LED_DBG("%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) {
+ pr_err("%s: Failed to request gpio %d,rc = %d\n",
+ __func__, flash_led->gpio_pin_num, rc);
+ goto error2;
+ }
+ /* Make sure the direction is out */
+ rc = gpio_direction_output(flash_led->gpio_pin_num, 1);
+ if (rc)
+ pr_err("%s: Setting GPIO %d direction to out failed!, "
+ "rc=%d.\n", __func__, flash_led->gpio_pin_num, rc);
+ else
+ LED_DBG("%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);
+ LED_DBG("%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);
+ LED_DBG("%s: GPIO value=%d\n",
+ __func__, gpio_get_value_cansleep(flash_led->gpio_pin_num));
+
+ /* GPIO's reserved for use by driver */
+ flash_led->gpio_pin_state = flash_led->gpio_pin_mux_state = 1;
+
+ return 0;
+
+error:
+ pr_err("%s: Failed to request pinmux GPIO=%d\n",
+ __func__, GPIO_PIN_MUX_NUM);
+ return rc;
+
+error2:
+ pr_err("%s: Failed to request GPIO=%d\n",
+ __func__, GPIO_PIN_NUM);
+ gpio_free(flash_led->gpio_pin_mux_num);
+ return rc;
+}
+
+static int led_gpio_free(struct led_classdev *led_cdev)
+{
+ int rc;
+ struct led_gpio_flash_data *flash_led =
+ container_of(led_cdev, struct led_gpio_flash_data, cdev);
+
+ /* Free the GPIO's for use outside driver */
+ gpio_free(flash_led->gpio_pin_num);
+ gpio_free(flash_led->gpio_pin_mux_num);
+ if (!flash_led->gpio_pin_tri_state_state) {
+ LED_DBG("%s: Freeing GPIO_TRI_STATE_ALL=%d\n",
+ __func__, GPIO_TRI_STATE_ALL);
+ gpio_free(flash_led->gpio_pin_tri_state_num);
+ } else {
+ pr_info("%s: GPIO_TRI_STATE_ALL (%d) is being reset "
+ "back to output direction\n",
+ __func__, flash_led->gpio_pin_tri_state_num);
+ /* Set direction for pin */
+ rc = gpio_direction_output(flash_led->gpio_pin_tri_state_num, 0);
+ if (rc)
+ pr_err("%s: Setting GPIO %d direction to output failed! "
+ ",rc=%d\n", __func__,
+ flash_led->gpio_pin_tri_state_num, rc);
+ else
+ pr_info("%s: Setting GPIO %d direction to output "
+ "succeeded, rc=%d.\n", __func__,
+ flash_led->gpio_pin_tri_state_num, rc);
+
+ }
+ LED_DBG("%s: Freeing GPIO=%d and GPIO_PIN_MUX=%d\n",
+ __func__, GPIO_PIN_NUM, GPIO_PIN_MUX_NUM);
+
+ flash_led->gpio_pin_state = flash_led->gpio_pin_mux_state =
+ flash_led->gpio_pin_tri_state_state = 0;
+
+ return 0;
+}
+
+/* Workqueue handler */
static void ledwq_work_handler(struct work_struct *ws)
{
- int ret = 0;
+ int ret = 0, rc = 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__);
+ LED_DBG("%s: Use the workqueue for flashing the LED\n", __func__);
+
+ if (!flash_led->gpio_pin_state && !flash_led->gpio_pin_mux_state) {
+ rc = led_gpio_request(&flash_led->cdev);
+ if (rc) {
+ pr_err("%s: GPIO request failed LED blinking "
+ "disabled, rc=%d\n", __func__, rc);
+ return;
+ }
+ }
- while(1) {
+ flash_led->blinking = 1;
+
+ while (1) {
if (ret) {
- pr_debug("ON\n");
+ LED_DBG("ON\n");
gpio_set_value_cansleep(flash_led->gpio_pin_num, 0);
ret = 0;
+ set_current_state(TASK_INTERRUPTIBLE);
msleep(flash_led->cdev.blink_delay_on);
} else { /* ret == 0 */
- pr_debug("OFF\n");
+ LED_DBG("OFF\n");
gpio_set_value_cansleep(flash_led->gpio_pin_num, 1);
ret = 1;
+ set_current_state(TASK_INTERRUPTIBLE);
msleep(flash_led->cdev.blink_delay_off);
}
/* Code below is an alternate timer code flow that would use
@@ -78,11 +240,15 @@ static void ledwq_work_handler(struct work_struct *ws)
schedule_timeout (1000/(flash_led->cdev.brightness+1));
*/
if (!flash_led->cdev.blink_delay_on ||
- !flash_led->cdev.blink_delay_off)
- break;
+ !flash_led->cdev.blink_delay_off ||
+ !flash_led->blinking) {
+ led_gpio_free(&flash_led->cdev);
+ return;
+ }
}
}
+/* LED blink setup */
static int led_gpio_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
@@ -91,40 +257,42 @@ static int led_gpio_blink_set(struct led_classdev *led_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 "
+ LED_DBG("%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",
+ LED_DBG("%s: delay_on=%lu, delay_off=%lu\n",
__func__, *delay_on, *delay_off);
- pr_debug("%s: Prepare and queue work\n", __func__);
+ LED_DBG("%s: Cancel any pending queued LED blinking work\n", __func__);
+ flash_led->blinking = LED_OFF;
+ cancel_work_sync(&flash_led->led_flash_work);
+ LED_DBG("%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);
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
- return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
+ 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;
+ 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;
+ 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;
+ led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);
+ led_cdev->blink_delay_on = state;
return size;
}
@@ -132,26 +300,26 @@ 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);
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
- return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
+ 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;
+ 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;
+ 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;
+ led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);
+ led_cdev->blink_delay_off = state;
- return size;
+ return size;
}
static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
@@ -163,14 +331,26 @@ static void led_gpio_brightness_set(struct led_classdev *led_cdev,
{
struct led_gpio_flash_data *flash_led =
container_of(led_cdev, struct led_gpio_flash_data, cdev);
+ int rc;
int brightness = value;
/* Reset blink*/
flash_led->cdev.blink_delay_on =
- flash_led->cdev.blink_delay_off = LED_OFF;
+ flash_led->cdev.blink_delay_off =
+ flash_led->blinking = LED_OFF;
if (brightness >= 1) { /* Turn on LED */
- pr_debug("%s: Turning on LED, brightness=%d\n", __func__, brightness);
+ if (!flash_led->gpio_pin_state &&
+ !flash_led->gpio_pin_mux_state) {
+ rc = led_gpio_request(&flash_led->cdev);
+ if (rc) {
+ pr_err("%s: GPIO request failed LED disabled, "
+ "rc=%d\n", __func__, rc);
+ goto err;
+ }
+ }
+ LED_DBG("%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 */
if (gpio_get_value_cansleep(flash_led->gpio_pin_num)) {
@@ -178,13 +358,14 @@ static void led_gpio_brightness_set(struct led_classdev *led_cdev,
flash_led->gpio_pin_num);
goto err;
}
- pr_debug("%s: GPIO value=%d\n", __func__,
+ LED_DBG("%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__,
+ LED_DBG("%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);
+ LED_DBG("%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 */
if (gpio_get_value_cansleep(flash_led->gpio_pin_num)) {
@@ -192,11 +373,13 @@ static void led_gpio_brightness_set(struct led_classdev *led_cdev,
flash_led->gpio_pin_num);
goto err;
}
- pr_debug("%s: GPIO value=%d\n", __func__,
+ LED_DBG("%s: GPIO value=%d\n", __func__,
gpio_get_value_cansleep(flash_led->gpio_pin_num));
+ if (flash_led->gpio_pin_state && flash_led->gpio_pin_mux_state)
+ led_gpio_free(&flash_led->cdev);
flash_led->cdev.brightness = 0;
- pr_debug("%s: Stored new value for brightness=%d\n", __func__,
- flash_led->cdev.brightness);
+ LED_DBG("%s: Stored new value for brightness=%d\n",
+ __func__, flash_led->cdev.brightness);
}
err:
@@ -208,7 +391,8 @@ static enum led_brightness led_gpio_brightness_get(struct led_classdev
{
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);
+ LED_DBG("%s: returning brightness=%d\n",
+ __func__, flash_led->cdev.brightness);
return flash_led->cdev.brightness;
}
@@ -216,7 +400,7 @@ int led_gpio_flash_probe(struct platform_device *pdev)
{
int rc = 0;
struct led_gpio_flash_data *flash_led = NULL;
-
+
flash_led = devm_kzalloc(&pdev->dev, sizeof(struct led_gpio_flash_data),
GFP_KERNEL);
if (flash_led == NULL) {
@@ -228,53 +412,15 @@ int led_gpio_flash_probe(struct platform_device *pdev)
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->gpio_pin_tri_state_num = GPIO_TRI_STATE_ALL;
+ flash_led->gpio_pin_state = flash_led->gpio_pin_mux_state =
+ flash_led->gpio_pin_tri_state_state = 0;
+ flash_led->blinking = LED_OFF;
flash_led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
dev_dbg(&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;
- }
- /* Make sure the direction is out */
- rc = gpio_direction_output(flash_led->gpio_pin_mux_num, 0);
- if (rc)
- dev_err(&pdev->dev, "%s: Setting GPIO %d direction to out failed!, rc=%d\n",
- __func__, flash_led->gpio_pin_mux_num, rc);
- else
- dev_dbg(&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;
- }
- /* Make sure the direction is out */
- rc = gpio_direction_output(flash_led->gpio_pin_num, 1);
- if (rc)
- dev_err(&pdev->dev, "%s: Setting GPIO %d direction to out failed!, "
- "rc=%d.\n", __func__, flash_led->gpio_pin_num, rc);
- else
- dev_dbg(&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_dbg(&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);
- dev_dbg(&pdev->dev, "%s: GPIO value=%d\n",
- __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);
@@ -318,15 +464,16 @@ 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);
+
+ if (flash_led->gpio_pin_state && flash_led->gpio_pin_mux_state)
+ led_gpio_free(&flash_led->cdev);
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__);
+ LED_DBG("%s: Unregistering Intel LED GPIO driver", __func__);
return 0;
}
@@ -341,13 +488,13 @@ static struct platform_driver led_gpio_flash_driver = {
static int __init led_gpio_flash_init(void)
{
- pr_debug("%s: Registering Intel LED GPIO driver\n", __func__);
+ LED_DBG("%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__);
+ LED_DBG("%s: Unregistering Intel LED GPIO driver\n", __func__);
return platform_driver_unregister(&led_gpio_flash_driver);
}