summaryrefslogtreecommitdiff
path: root/drivers/leds
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig30
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/led-class.c7
-rw-r--r--drivers/leds/leds-fsg.c52
-rw-r--r--drivers/leds/leds-lm3530.c1
-rw-r--r--drivers/leds/leds-lm355x.c1
-rw-r--r--drivers/leds/leds-lm3642.c1
-rw-r--r--drivers/leds/leds-lp5521.c11
-rw-r--r--drivers/leds/leds-lp5523.c11
-rw-r--r--drivers/leds/leds-lp5562.c11
-rw-r--r--drivers/leds/leds-lp55xx-common.c13
-rw-r--r--drivers/leds/leds-lp55xx-common.h4
-rw-r--r--drivers/leds/leds-lp8501.c11
-rw-r--r--drivers/leds/leds-lp8860.c4
-rw-r--r--drivers/leds/leds-max77693.c1
-rw-r--r--drivers/leds/leds-ns2.c169
-rw-r--r--drivers/leds/leds-pca955x.c1
-rw-r--r--drivers/leds/leds-pca963x.c2
-rw-r--r--drivers/leds/leds-powernv.c345
-rw-r--r--drivers/leds/leds-syscon.c4
-rw-r--r--drivers/leds/leds-tca6507.c2
-rw-r--r--drivers/leds/leds-tlc591xx.c4
-rw-r--r--drivers/leds/trigger/Kconfig2
23 files changed, 523 insertions, 165 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index b8d4b965ca2a..70f4255ff291 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -43,7 +43,7 @@ config LEDS_AAT1290
tristate "LED support for the AAT1290"
depends on LEDS_CLASS_FLASH
depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
depends on OF
depends on PINCTRL
help
@@ -419,7 +419,7 @@ config LEDS_INTEL_SS4200
config LEDS_LT3593
tristate "LED driver for LT3593 controllers"
depends on LEDS_CLASS
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
This option enables support for LEDs driven by a Linear Technology
LT3593 controller. This controller uses a special one-wire pulse
@@ -455,12 +455,16 @@ config LEDS_MC13783
config LEDS_NS2
tristate "LED support for Network Space v2 GPIO LEDs"
depends on LEDS_CLASS
- depends on MACH_KIRKWOOD
+ depends on MACH_KIRKWOOD || MACH_ARMADA_370
default y
help
- This option enable support for the dual-GPIO LED found on the
- Network Space v2 board (and parents). This include Internet Space v2,
- Network Space (Max) v2 and d2 Network v2 boards.
+ This option enables support for the dual-GPIO LEDs found on the
+ following LaCie/Seagate boards:
+
+ Network Space v2 (and parents: Max, Mini)
+ Internet Space v2
+ d2 Network v2
+ n090401 (Seagate NAS 4-Bay)
config LEDS_NETXBIG
tristate "LED support for Big Network series LEDs"
@@ -543,7 +547,8 @@ config LEDS_MENF21BMC
config LEDS_KTD2692
tristate "LED support for KTD2692 flash LED controller"
- depends on LEDS_CLASS_FLASH && GPIOLIB && OF
+ depends on LEDS_CLASS_FLASH && OF
+ depends on GPIOLIB || COMPILE_TEST
help
This option enables support for KTD2692 LED flash connected
through ExpressWire interface.
@@ -560,6 +565,17 @@ config LEDS_BLINKM
This option enables support for the BlinkM RGB LED connected
through I2C. Say Y to enable support for the BlinkM LED.
+config LEDS_POWERNV
+ tristate "LED support for PowerNV Platform"
+ depends on LEDS_CLASS
+ depends on PPC_POWERNV
+ depends on OF
+ help
+ This option enables support for the system LEDs present on
+ PowerNV platforms. Say 'y' to enable this support in kernel.
+ To compile this driver as a module, choose 'm' here: the module
+ will be called leds-powernv.
+
config LEDS_SYSCON
bool "LED support for LEDs on system controllers"
depends on LEDS_CLASS=y
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index abe96d960ebe..b503f92dc2c4 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o
+obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index beabfbc6f7cd..ca51d58bed24 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -228,12 +228,15 @@ static int led_classdev_next_name(const char *init_name, char *name,
{
unsigned int i = 0;
int ret = 0;
+ struct device *dev;
strlcpy(name, init_name, len);
- while (class_find_device(leds_class, NULL, name, match_name) &&
- (ret < len))
+ while ((ret < len) &&
+ (dev = class_find_device(leds_class, NULL, name, match_name))) {
+ put_device(dev);
ret = snprintf(name, len, "%s_%u", init_name, ++i);
+ }
if (ret >= len)
return -ENOMEM;
diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c
index 2b4dc738dcd6..257a813c73f3 100644
--- a/drivers/leds/leds-fsg.c
+++ b/drivers/leds/leds-fsg.c
@@ -156,63 +156,35 @@ static int fsg_led_probe(struct platform_device *pdev)
latch_value = 0xffff;
*latch_address = latch_value;
- ret = led_classdev_register(&pdev->dev, &fsg_wlan_led);
+ ret = devm_led_classdev_register(&pdev->dev, &fsg_wlan_led);
if (ret < 0)
- goto failwlan;
+ return ret;
- ret = led_classdev_register(&pdev->dev, &fsg_wan_led);
+ ret = devm_led_classdev_register(&pdev->dev, &fsg_wan_led);
if (ret < 0)
- goto failwan;
+ return ret;
- ret = led_classdev_register(&pdev->dev, &fsg_sata_led);
+ ret = devm_led_classdev_register(&pdev->dev, &fsg_sata_led);
if (ret < 0)
- goto failsata;
+ return ret;
- ret = led_classdev_register(&pdev->dev, &fsg_usb_led);
+ ret = devm_led_classdev_register(&pdev->dev, &fsg_usb_led);
if (ret < 0)
- goto failusb;
+ return ret;
- ret = led_classdev_register(&pdev->dev, &fsg_sync_led);
+ ret = devm_led_classdev_register(&pdev->dev, &fsg_sync_led);
if (ret < 0)
- goto failsync;
+ return ret;
- ret = led_classdev_register(&pdev->dev, &fsg_ring_led);
+ ret = devm_led_classdev_register(&pdev->dev, &fsg_ring_led);
if (ret < 0)
- goto failring;
-
- return ret;
-
- failring:
- led_classdev_unregister(&fsg_sync_led);
- failsync:
- led_classdev_unregister(&fsg_usb_led);
- failusb:
- led_classdev_unregister(&fsg_sata_led);
- failsata:
- led_classdev_unregister(&fsg_wan_led);
- failwan:
- led_classdev_unregister(&fsg_wlan_led);
- failwlan:
+ return ret;
return ret;
}
-static int fsg_led_remove(struct platform_device *pdev)
-{
- led_classdev_unregister(&fsg_wlan_led);
- led_classdev_unregister(&fsg_wan_led);
- led_classdev_unregister(&fsg_sata_led);
- led_classdev_unregister(&fsg_usb_led);
- led_classdev_unregister(&fsg_sync_led);
- led_classdev_unregister(&fsg_ring_led);
-
- return 0;
-}
-
-
static struct platform_driver fsg_led_driver = {
.probe = fsg_led_probe,
- .remove = fsg_led_remove,
.driver = {
.name = "fsg-led",
},
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
index 91325de3cd33..b38430cb10ad 100644
--- a/drivers/leds/leds-lm3530.c
+++ b/drivers/leds/leds-lm3530.c
@@ -492,7 +492,6 @@ static struct i2c_driver lm3530_i2c_driver = {
.id_table = lm3530_id,
.driver = {
.name = LM3530_NAME,
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c
index f5112cb2d991..48872997d6b4 100644
--- a/drivers/leds/leds-lm355x.c
+++ b/drivers/leds/leds-lm355x.c
@@ -555,7 +555,6 @@ MODULE_DEVICE_TABLE(i2c, lm355x_id);
static struct i2c_driver lm355x_i2c_driver = {
.driver = {
.name = LM355x_NAME,
- .owner = THIS_MODULE,
.pm = NULL,
},
.probe = lm355x_probe,
diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c
index d3dec0132769..02ebe342f5af 100644
--- a/drivers/leds/leds-lm3642.c
+++ b/drivers/leds/leds-lm3642.c
@@ -446,7 +446,6 @@ MODULE_DEVICE_TABLE(i2c, lm3642_id);
static struct i2c_driver lm3642_i2c_driver = {
.driver = {
.name = LM3642_NAME,
- .owner = THIS_MODULE,
.pm = NULL,
},
.probe = lm3642_probe,
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index 8ca197af2864..63a92542c8cb 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -514,20 +514,19 @@ static int lp5521_probe(struct i2c_client *client,
int ret;
struct lp55xx_chip *chip;
struct lp55xx_led *led;
- struct lp55xx_platform_data *pdata;
+ struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
struct device_node *np = client->dev.of_node;
- if (!dev_get_platdata(&client->dev)) {
+ if (!pdata) {
if (np) {
- ret = lp55xx_of_populate_pdata(&client->dev, np);
- if (ret < 0)
- return ret;
+ pdata = lp55xx_of_populate_pdata(&client->dev, np);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
} else {
dev_err(&client->dev, "no platform data\n");
return -EINVAL;
}
}
- pdata = dev_get_platdata(&client->dev);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c
index 584dbbcec659..1d0187f42941 100644
--- a/drivers/leds/leds-lp5523.c
+++ b/drivers/leds/leds-lp5523.c
@@ -880,20 +880,19 @@ static int lp5523_probe(struct i2c_client *client,
int ret;
struct lp55xx_chip *chip;
struct lp55xx_led *led;
- struct lp55xx_platform_data *pdata;
+ struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
struct device_node *np = client->dev.of_node;
- if (!dev_get_platdata(&client->dev)) {
+ if (!pdata) {
if (np) {
- ret = lp55xx_of_populate_pdata(&client->dev, np);
- if (ret < 0)
- return ret;
+ pdata = lp55xx_of_populate_pdata(&client->dev, np);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
} else {
dev_err(&client->dev, "no platform data\n");
return -EINVAL;
}
}
- pdata = dev_get_platdata(&client->dev);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c
index ca85724ab138..0360c59dbdc9 100644
--- a/drivers/leds/leds-lp5562.c
+++ b/drivers/leds/leds-lp5562.c
@@ -515,20 +515,19 @@ static int lp5562_probe(struct i2c_client *client,
int ret;
struct lp55xx_chip *chip;
struct lp55xx_led *led;
- struct lp55xx_platform_data *pdata;
+ struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
struct device_node *np = client->dev.of_node;
- if (!dev_get_platdata(&client->dev)) {
+ if (!pdata) {
if (np) {
- ret = lp55xx_of_populate_pdata(&client->dev, np);
- if (ret < 0)
- return ret;
+ pdata = lp55xx_of_populate_pdata(&client->dev, np);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
} else {
dev_err(&client->dev, "no platform data\n");
return -EINVAL;
}
}
- pdata = dev_get_platdata(&client->dev);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
index 96d51e9879c9..59b76833f0d3 100644
--- a/drivers/leds/leds-lp55xx-common.c
+++ b/drivers/leds/leds-lp55xx-common.c
@@ -543,7 +543,8 @@ void lp55xx_unregister_sysfs(struct lp55xx_chip *chip)
}
EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs);
-int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np)
+struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
+ struct device_node *np)
{
struct device_node *child;
struct lp55xx_platform_data *pdata;
@@ -553,17 +554,17 @@ int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np)
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
num_channels = of_get_child_count(np);
if (num_channels == 0) {
dev_err(dev, "no LED channels\n");
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
cfg = devm_kzalloc(dev, sizeof(*cfg) * num_channels, GFP_KERNEL);
if (!cfg)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
pdata->led_config = &cfg[0];
pdata->num_channels = num_channels;
@@ -588,9 +589,7 @@ int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np)
/* LP8501 specific */
of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel);
- dev->platform_data = pdata;
-
- return 0;
+ return pdata;
}
EXPORT_SYMBOL_GPL(lp55xx_of_populate_pdata);
diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h
index cceab483edd0..c7f1e6155001 100644
--- a/drivers/leds/leds-lp55xx-common.h
+++ b/drivers/leds/leds-lp55xx-common.h
@@ -202,7 +202,7 @@ extern int lp55xx_register_sysfs(struct lp55xx_chip *chip);
extern void lp55xx_unregister_sysfs(struct lp55xx_chip *chip);
/* common device tree population function */
-extern int lp55xx_of_populate_pdata(struct device *dev,
- struct device_node *np);
+extern struct lp55xx_platform_data
+*lp55xx_of_populate_pdata(struct device *dev, struct device_node *np);
#endif /* _LEDS_LP55XX_COMMON_H */
diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c
index d3098e395fff..3f54f6f2b821 100644
--- a/drivers/leds/leds-lp8501.c
+++ b/drivers/leds/leds-lp8501.c
@@ -308,20 +308,19 @@ static int lp8501_probe(struct i2c_client *client,
int ret;
struct lp55xx_chip *chip;
struct lp55xx_led *led;
- struct lp55xx_platform_data *pdata;
+ struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
struct device_node *np = client->dev.of_node;
- if (!dev_get_platdata(&client->dev)) {
+ if (!pdata) {
if (np) {
- ret = lp55xx_of_populate_pdata(&client->dev, np);
- if (ret < 0)
- return ret;
+ pdata = lp55xx_of_populate_pdata(&client->dev, np);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
} else {
dev_err(&client->dev, "no platform data\n");
return -EINVAL;
}
}
- pdata = dev_get_platdata(&client->dev);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
diff --git a/drivers/leds/leds-lp8860.c b/drivers/leds/leds-lp8860.c
index 8c2b7fbe2392..79f084354e67 100644
--- a/drivers/leds/leds-lp8860.c
+++ b/drivers/leds/leds-lp8860.c
@@ -302,7 +302,7 @@ out:
return ret;
}
-static struct reg_default lp8860_reg_defs[] = {
+static const struct reg_default lp8860_reg_defs[] = {
{ LP8860_DISP_CL1_BRT_MSB, 0x00},
{ LP8860_DISP_CL1_BRT_LSB, 0x00},
{ LP8860_DISP_CL1_CURR_MSB, 0x00},
@@ -332,7 +332,7 @@ static const struct regmap_config lp8860_regmap_config = {
.cache_type = REGCACHE_NONE,
};
-static struct reg_default lp8860_eeprom_defs[] = {
+static const struct reg_default lp8860_eeprom_defs[] = {
{ LP8860_EEPROM_REG_0, 0x00 },
{ LP8860_EEPROM_REG_1, 0x00 },
{ LP8860_EEPROM_REG_2, 0x00 },
diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c
index b8b0eec7b540..df348a06d8c7 100644
--- a/drivers/leds/leds-max77693.c
+++ b/drivers/leds/leds-max77693.c
@@ -13,6 +13,7 @@
#include <linux/led-class-flash.h>
#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-common.h>
#include <linux/mfd/max77693-private.h>
#include <linux/module.h>
#include <linux/mutex.h>
diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c
index 1fd6adbb43b7..b33514d9f427 100644
--- a/drivers/leds/leds-ns2.c
+++ b/drivers/leds/leds-ns2.c
@@ -31,50 +31,38 @@
#include <linux/platform_data/leds-kirkwood-ns2.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
+#include "leds.h"
/*
- * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in
- * relation with the SATA activity. This capability is exposed through the
- * "sata" sysfs attribute.
- *
- * The following array detail the different LED registers and the combination
- * of their possible values:
- *
- * cmd_led | slow_led | /SATA active | LED state
- * | | |
- * 1 | 0 | x | off
- * - | 1 | x | on
- * 0 | 0 | 1 | on
- * 0 | 0 | 0 | blink (rate 300ms)
+ * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED
+ * modes are available: off, on and SATA activity blinking. The LED modes are
+ * controlled through two GPIOs (command and slow): each combination of values
+ * for the command/slow GPIOs corresponds to a LED mode.
*/
-enum ns2_led_modes {
- NS_V2_LED_OFF,
- NS_V2_LED_ON,
- NS_V2_LED_SATA,
-};
-
-struct ns2_led_mode_value {
- enum ns2_led_modes mode;
- int cmd_level;
- int slow_level;
-};
-
-static struct ns2_led_mode_value ns2_led_modval[] = {
- { NS_V2_LED_OFF , 1, 0 },
- { NS_V2_LED_ON , 0, 1 },
- { NS_V2_LED_ON , 1, 1 },
- { NS_V2_LED_SATA, 0, 0 },
-};
-
struct ns2_led_data {
struct led_classdev cdev;
unsigned cmd;
unsigned slow;
+ bool can_sleep;
+ int mode_index;
unsigned char sata; /* True when SATA mode active. */
rwlock_t rw_lock; /* Lock GPIOs. */
+ struct work_struct work;
+ int num_modes;
+ struct ns2_led_modval *modval;
};
+static void ns2_led_work(struct work_struct *work)
+{
+ struct ns2_led_data *led_dat =
+ container_of(work, struct ns2_led_data, work);
+ int i = led_dat->mode_index;
+
+ gpio_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level);
+ gpio_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level);
+}
+
static int ns2_led_get_mode(struct ns2_led_data *led_dat,
enum ns2_led_modes *mode)
{
@@ -83,22 +71,18 @@ static int ns2_led_get_mode(struct ns2_led_data *led_dat,
int cmd_level;
int slow_level;
- read_lock_irq(&led_dat->rw_lock);
+ cmd_level = gpio_get_value_cansleep(led_dat->cmd);
+ slow_level = gpio_get_value_cansleep(led_dat->slow);
- cmd_level = gpio_get_value(led_dat->cmd);
- slow_level = gpio_get_value(led_dat->slow);
-
- for (i = 0; i < ARRAY_SIZE(ns2_led_modval); i++) {
- if (cmd_level == ns2_led_modval[i].cmd_level &&
- slow_level == ns2_led_modval[i].slow_level) {
- *mode = ns2_led_modval[i].mode;
+ for (i = 0; i < led_dat->num_modes; i++) {
+ if (cmd_level == led_dat->modval[i].cmd_level &&
+ slow_level == led_dat->modval[i].slow_level) {
+ *mode = led_dat->modval[i].mode;
ret = 0;
break;
}
}
- read_unlock_irq(&led_dat->rw_lock);
-
return ret;
}
@@ -106,19 +90,32 @@ static void ns2_led_set_mode(struct ns2_led_data *led_dat,
enum ns2_led_modes mode)
{
int i;
+ bool found = false;
unsigned long flags;
+ for (i = 0; i < led_dat->num_modes; i++)
+ if (mode == led_dat->modval[i].mode) {
+ found = true;
+ break;
+ }
+
+ if (!found)
+ return;
+
write_lock_irqsave(&led_dat->rw_lock, flags);
- for (i = 0; i < ARRAY_SIZE(ns2_led_modval); i++) {
- if (mode == ns2_led_modval[i].mode) {
- gpio_set_value(led_dat->cmd,
- ns2_led_modval[i].cmd_level);
- gpio_set_value(led_dat->slow,
- ns2_led_modval[i].slow_level);
- }
+ if (!led_dat->can_sleep) {
+ gpio_set_value(led_dat->cmd,
+ led_dat->modval[i].cmd_level);
+ gpio_set_value(led_dat->slow,
+ led_dat->modval[i].slow_level);
+ goto exit_unlock;
}
+ led_dat->mode_index = i;
+ schedule_work(&led_dat->work);
+
+exit_unlock:
write_unlock_irqrestore(&led_dat->rw_lock, flags);
}
@@ -148,7 +145,6 @@ static ssize_t ns2_led_sata_store(struct device *dev,
container_of(led_cdev, struct ns2_led_data, cdev);
int ret;
unsigned long enable;
- enum ns2_led_modes mode;
ret = kstrtoul(buff, 10, &enable);
if (ret < 0)
@@ -157,19 +153,19 @@ static ssize_t ns2_led_sata_store(struct device *dev,
enable = !!enable;
if (led_dat->sata == enable)
- return count;
+ goto exit;
- ret = ns2_led_get_mode(led_dat, &mode);
- if (ret < 0)
- return ret;
+ led_dat->sata = enable;
+
+ if (!led_get_brightness(led_cdev))
+ goto exit;
- if (enable && mode == NS_V2_LED_ON)
+ if (enable)
ns2_led_set_mode(led_dat, NS_V2_LED_SATA);
- if (!enable && mode == NS_V2_LED_SATA)
+ else
ns2_led_set_mode(led_dat, NS_V2_LED_ON);
- led_dat->sata = enable;
-
+exit:
return count;
}
@@ -199,7 +195,7 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
enum ns2_led_modes mode;
ret = devm_gpio_request_one(&pdev->dev, template->cmd,
- gpio_get_value(template->cmd) ?
+ gpio_get_value_cansleep(template->cmd) ?
GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
template->name);
if (ret) {
@@ -209,7 +205,7 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
}
ret = devm_gpio_request_one(&pdev->dev, template->slow,
- gpio_get_value(template->slow) ?
+ gpio_get_value_cansleep(template->slow) ?
GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
template->name);
if (ret) {
@@ -228,6 +224,10 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
led_dat->cdev.groups = ns2_led_groups;
led_dat->cmd = template->cmd;
led_dat->slow = template->slow;
+ led_dat->can_sleep = gpio_cansleep(led_dat->cmd) |
+ gpio_cansleep(led_dat->slow);
+ led_dat->modval = template->modval;
+ led_dat->num_modes = template->num_modes;
ret = ns2_led_get_mode(led_dat, &mode);
if (ret < 0)
@@ -238,6 +238,8 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
led_dat->cdev.brightness =
(mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
+ INIT_WORK(&led_dat->work, ns2_led_work);
+
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
if (ret < 0)
return ret;
@@ -248,6 +250,7 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
static void delete_ns2_led(struct ns2_led_data *led_dat)
{
led_classdev_unregister(&led_dat->cdev);
+ cancel_work_sync(&led_dat->work);
}
#ifdef CONFIG_OF_GPIO
@@ -259,9 +262,8 @@ ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata)
{
struct device_node *np = dev->of_node;
struct device_node *child;
- struct ns2_led *leds;
+ struct ns2_led *led, *leds;
int num_leds = 0;
- int i = 0;
num_leds = of_get_child_count(np);
if (!num_leds)
@@ -272,26 +274,57 @@ ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata)
if (!leds)
return -ENOMEM;
+ led = leds;
for_each_child_of_node(np, child) {
const char *string;
- int ret;
+ int ret, i, num_modes;
+ struct ns2_led_modval *modval;
ret = of_get_named_gpio(child, "cmd-gpio", 0);
if (ret < 0)
return ret;
- leds[i].cmd = ret;
+ led->cmd = ret;
ret = of_get_named_gpio(child, "slow-gpio", 0);
if (ret < 0)
return ret;
- leds[i].slow = ret;
+ led->slow = ret;
ret = of_property_read_string(child, "label", &string);
- leds[i].name = (ret == 0) ? string : child->name;
+ led->name = (ret == 0) ? string : child->name;
ret = of_property_read_string(child, "linux,default-trigger",
&string);
if (ret == 0)
- leds[i].default_trigger = string;
+ led->default_trigger = string;
+
+ ret = of_property_count_u32_elems(child, "modes-map");
+ if (ret < 0 || ret % 3) {
+ dev_err(dev,
+ "Missing or malformed modes-map property\n");
+ return -EINVAL;
+ }
+
+ num_modes = ret / 3;
+ modval = devm_kzalloc(dev,
+ num_modes * sizeof(struct ns2_led_modval),
+ GFP_KERNEL);
+ if (!modval)
+ return -ENOMEM;
+
+ for (i = 0; i < num_modes; i++) {
+ of_property_read_u32_index(child,
+ "modes-map", 3 * i,
+ (u32 *) &modval[i].mode);
+ of_property_read_u32_index(child,
+ "modes-map", 3 * i + 1,
+ (u32 *) &modval[i].cmd_level);
+ of_property_read_u32_index(child,
+ "modes-map", 3 * i + 2,
+ (u32 *) &modval[i].slow_level);
+ }
+
+ led->num_modes = num_modes;
+ led->modval = modval;
- i++;
+ led++;
}
pdata->leds = leds;
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index c3a08b60535b..b775e1efecd3 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -379,7 +379,6 @@ static int pca955x_remove(struct i2c_client *client)
static struct i2c_driver pca955x_driver = {
.driver = {
.name = "leds-pca955x",
- .owner = THIS_MODULE,
},
.probe = pca955x_probe,
.remove = pca955x_remove,
diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c
index bee3e1ab27fd..41f269fe0920 100644
--- a/drivers/leds/leds-pca963x.c
+++ b/drivers/leds/leds-pca963x.c
@@ -332,6 +332,7 @@ static const struct of_device_id of_pca963x_match[] = {
{ .compatible = "nxp,pca9635", },
{},
};
+MODULE_DEVICE_TABLE(of, of_pca963x_match);
#else
static struct pca963x_platform_data *
pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip)
@@ -458,7 +459,6 @@ static int pca963x_remove(struct i2c_client *client)
static struct i2c_driver pca963x_driver = {
.driver = {
.name = "leds-pca963x",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(of_pca963x_match),
},
.probe = pca963x_probe,
diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c
new file mode 100644
index 000000000000..2c5c5b12ab64
--- /dev/null
+++ b/drivers/leds/leds-powernv.c
@@ -0,0 +1,345 @@
+/*
+ * PowerNV LED Driver
+ *
+ * Copyright IBM Corp. 2015
+ *
+ * Author: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
+ * Author: Anshuman Khandual <khandual@linux.vnet.ibm.com>
+ *
+ * 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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <asm/opal.h>
+
+/* Map LED type to description. */
+struct led_type_map {
+ const int type;
+ const char *desc;
+};
+static const struct led_type_map led_type_map[] = {
+ {OPAL_SLOT_LED_TYPE_ID, "identify"},
+ {OPAL_SLOT_LED_TYPE_FAULT, "fault"},
+ {OPAL_SLOT_LED_TYPE_ATTN, "attention"},
+ {-1, NULL},
+};
+
+struct powernv_led_common {
+ /*
+ * By default unload path resets all the LEDs. But on PowerNV
+ * platform we want to retain LED state across reboot as these
+ * are controlled by firmware. Also service processor can modify
+ * the LEDs independent of OS. Hence avoid resetting LEDs in
+ * unload path.
+ */
+ bool led_disabled;
+
+ /* Max supported LED type */
+ __be64 max_led_type;
+
+ /* glabal lock */
+ struct mutex lock;
+};
+
+/* PowerNV LED data */
+struct powernv_led_data {
+ struct led_classdev cdev;
+ char *loc_code; /* LED location code */
+ int led_type; /* OPAL_SLOT_LED_TYPE_* */
+
+ struct powernv_led_common *common;
+};
+
+
+/* Returns OPAL_SLOT_LED_TYPE_* for given led type string */
+static int powernv_get_led_type(const char *led_type_desc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(led_type_map); i++)
+ if (!strcmp(led_type_map[i].desc, led_type_desc))
+ return led_type_map[i].type;
+
+ return -1;
+}
+
+/*
+ * This commits the state change of the requested LED through an OPAL call.
+ * This function is called from work queue task context when ever it gets
+ * scheduled. This function can sleep at opal_async_wait_response call.
+ */
+static void powernv_led_set(struct powernv_led_data *powernv_led,
+ enum led_brightness value)
+{
+ int rc, token;
+ u64 led_mask, led_value = 0;
+ __be64 max_type;
+ struct opal_msg msg;
+ struct device *dev = powernv_led->cdev.dev;
+ struct powernv_led_common *powernv_led_common = powernv_led->common;
+
+ /* Prepare for the OPAL call */
+ max_type = powernv_led_common->max_led_type;
+ led_mask = OPAL_SLOT_LED_STATE_ON << powernv_led->led_type;
+ if (value)
+ led_value = led_mask;
+
+ /* OPAL async call */
+ token = opal_async_get_token_interruptible();
+ if (token < 0) {
+ if (token != -ERESTARTSYS)
+ dev_err(dev, "%s: Couldn't get OPAL async token\n",
+ __func__);
+ return;
+ }
+
+ rc = opal_leds_set_ind(token, powernv_led->loc_code,
+ led_mask, led_value, &max_type);
+ if (rc != OPAL_ASYNC_COMPLETION) {
+ dev_err(dev, "%s: OPAL set LED call failed for %s [rc=%d]\n",
+ __func__, powernv_led->loc_code, rc);
+ goto out_token;
+ }
+
+ rc = opal_async_wait_response(token, &msg);
+ if (rc) {
+ dev_err(dev,
+ "%s: Failed to wait for the async response [rc=%d]\n",
+ __func__, rc);
+ goto out_token;
+ }
+
+ rc = be64_to_cpu(msg.params[1]);
+ if (rc != OPAL_SUCCESS)
+ dev_err(dev, "%s : OAPL async call returned failed [rc=%d]\n",
+ __func__, rc);
+
+out_token:
+ opal_async_release_token(token);
+}
+
+/*
+ * This function fetches the LED state for a given LED type for
+ * mentioned LED classdev structure.
+ */
+static enum led_brightness powernv_led_get(struct powernv_led_data *powernv_led)
+{
+ int rc;
+ __be64 mask, value, max_type;
+ u64 led_mask, led_value;
+ struct device *dev = powernv_led->cdev.dev;
+ struct powernv_led_common *powernv_led_common = powernv_led->common;
+
+ /* Fetch all LED status */
+ mask = cpu_to_be64(0);
+ value = cpu_to_be64(0);
+ max_type = powernv_led_common->max_led_type;
+
+ rc = opal_leds_get_ind(powernv_led->loc_code,
+ &mask, &value, &max_type);
+ if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL) {
+ dev_err(dev, "%s: OPAL get led call failed [rc=%d]\n",
+ __func__, rc);
+ return LED_OFF;
+ }
+
+ led_mask = be64_to_cpu(mask);
+ led_value = be64_to_cpu(value);
+
+ /* LED status available */
+ if (!((led_mask >> powernv_led->led_type) & OPAL_SLOT_LED_STATE_ON)) {
+ dev_err(dev, "%s: LED status not available for %s\n",
+ __func__, powernv_led->cdev.name);
+ return LED_OFF;
+ }
+
+ /* LED status value */
+ if ((led_value >> powernv_led->led_type) & OPAL_SLOT_LED_STATE_ON)
+ return LED_FULL;
+
+ return LED_OFF;
+}
+
+/*
+ * LED classdev 'brightness_get' function. This schedules work
+ * to update LED state.
+ */
+static void powernv_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct powernv_led_data *powernv_led =
+ container_of(led_cdev, struct powernv_led_data, cdev);
+ struct powernv_led_common *powernv_led_common = powernv_led->common;
+
+ /* Do not modify LED in unload path */
+ if (powernv_led_common->led_disabled)
+ return;
+
+ mutex_lock(&powernv_led_common->lock);
+ powernv_led_set(powernv_led, value);
+ mutex_unlock(&powernv_led_common->lock);
+}
+
+/* LED classdev 'brightness_get' function */
+static enum led_brightness powernv_brightness_get(struct led_classdev *led_cdev)
+{
+ struct powernv_led_data *powernv_led =
+ container_of(led_cdev, struct powernv_led_data, cdev);
+
+ return powernv_led_get(powernv_led);
+}
+
+/*
+ * This function registers classdev structure for any given type of LED on
+ * a given child LED device node.
+ */
+static int powernv_led_create(struct device *dev,
+ struct powernv_led_data *powernv_led,
+ const char *led_type_desc)
+{
+ int rc;
+
+ /* Make sure LED type is supported */
+ powernv_led->led_type = powernv_get_led_type(led_type_desc);
+ if (powernv_led->led_type == -1) {
+ dev_warn(dev, "%s: No support for led type : %s\n",
+ __func__, led_type_desc);
+ return -EINVAL;
+ }
+
+ /* Create the name for classdev */
+ powernv_led->cdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
+ powernv_led->loc_code,
+ led_type_desc);
+ if (!powernv_led->cdev.name) {
+ dev_err(dev,
+ "%s: Memory allocation failed for classdev name\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ powernv_led->cdev.brightness_set = powernv_brightness_set;
+ powernv_led->cdev.brightness_get = powernv_brightness_get;
+ powernv_led->cdev.brightness = LED_OFF;
+ powernv_led->cdev.max_brightness = LED_FULL;
+
+ /* Register the classdev */
+ rc = devm_led_classdev_register(dev, &powernv_led->cdev);
+ if (rc) {
+ dev_err(dev, "%s: Classdev registration failed for %s\n",
+ __func__, powernv_led->cdev.name);
+ }
+
+ return rc;
+}
+
+/* Go through LED device tree node and register LED classdev structure */
+static int powernv_led_classdev(struct platform_device *pdev,
+ struct device_node *led_node,
+ struct powernv_led_common *powernv_led_common)
+{
+ const char *cur = NULL;
+ int rc = -1;
+ struct property *p;
+ struct device_node *np;
+ struct powernv_led_data *powernv_led;
+ struct device *dev = &pdev->dev;
+
+ for_each_child_of_node(led_node, np) {
+ p = of_find_property(np, "led-types", NULL);
+ if (!p)
+ continue;
+
+ while ((cur = of_prop_next_string(p, cur)) != NULL) {
+ powernv_led = devm_kzalloc(dev, sizeof(*powernv_led),
+ GFP_KERNEL);
+ if (!powernv_led)
+ return -ENOMEM;
+
+ powernv_led->common = powernv_led_common;
+ powernv_led->loc_code = (char *)np->name;
+
+ rc = powernv_led_create(dev, powernv_led, cur);
+ if (rc)
+ return rc;
+ } /* while end */
+ }
+
+ return rc;
+}
+
+/* Platform driver probe */
+static int powernv_led_probe(struct platform_device *pdev)
+{
+ struct device_node *led_node;
+ struct powernv_led_common *powernv_led_common;
+ struct device *dev = &pdev->dev;
+
+ led_node = of_find_node_by_path("/ibm,opal/leds");
+ if (!led_node) {
+ dev_err(dev, "%s: LED parent device node not found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ powernv_led_common = devm_kzalloc(dev, sizeof(*powernv_led_common),
+ GFP_KERNEL);
+ if (!powernv_led_common)
+ return -ENOMEM;
+
+ mutex_init(&powernv_led_common->lock);
+ powernv_led_common->max_led_type = cpu_to_be64(OPAL_SLOT_LED_TYPE_MAX);
+
+ platform_set_drvdata(pdev, powernv_led_common);
+
+ return powernv_led_classdev(pdev, led_node, powernv_led_common);
+}
+
+/* Platform driver remove */
+static int powernv_led_remove(struct platform_device *pdev)
+{
+ struct powernv_led_common *powernv_led_common;
+
+ /* Disable LED operation */
+ powernv_led_common = platform_get_drvdata(pdev);
+ powernv_led_common->led_disabled = true;
+
+ /* Destroy lock */
+ mutex_destroy(&powernv_led_common->lock);
+
+ dev_info(&pdev->dev, "PowerNV led module unregistered\n");
+ return 0;
+}
+
+/* Platform driver property match */
+static const struct of_device_id powernv_led_match[] = {
+ {
+ .compatible = "ibm,opal-v3-led",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, powernv_led_match);
+
+static struct platform_driver powernv_led_driver = {
+ .probe = powernv_led_probe,
+ .remove = powernv_led_remove,
+ .driver = {
+ .name = "powernv-led-driver",
+ .of_match_table = powernv_led_match,
+ },
+};
+
+module_platform_driver(powernv_led_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PowerNV LED driver");
+MODULE_AUTHOR("Vasant Hegde <hegdevasant@linux.vnet.ibm.com>");
diff --git a/drivers/leds/leds-syscon.c b/drivers/leds/leds-syscon.c
index d1660b039812..b88900d721e4 100644
--- a/drivers/leds/leds-syscon.c
+++ b/drivers/leds/leds-syscon.c
@@ -83,9 +83,9 @@ static int syscon_led_probe(struct platform_device *pdev)
return -ENODEV;
}
map = syscon_node_to_regmap(parent->of_node);
- if (!map) {
+ if (IS_ERR(map)) {
dev_err(dev, "no regmap for syscon LED parent\n");
- return -ENODEV;
+ return PTR_ERR(map);
}
sled = devm_kzalloc(dev, sizeof(*sled), GFP_KERNEL);
diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c
index 20fa8e77f186..edbecc4ca2da 100644
--- a/drivers/leds/leds-tca6507.c
+++ b/drivers/leds/leds-tca6507.c
@@ -735,6 +735,7 @@ static const struct of_device_id of_tca6507_leds_match[] = {
{ .compatible = "ti,tca6507", },
{},
};
+MODULE_DEVICE_TABLE(of, of_tca6507_leds_match);
#else
static struct tca6507_platform_data *
@@ -830,7 +831,6 @@ static int tca6507_remove(struct i2c_client *client)
static struct i2c_driver tca6507_driver = {
.driver = {
.name = "leds-tca6507",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(of_tca6507_leds_match),
},
.probe = tca6507_probe,
diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c
index de16c29d7895..b806eca83d27 100644
--- a/drivers/leds/leds-tlc591xx.c
+++ b/drivers/leds/leds-tlc591xx.c
@@ -231,10 +231,6 @@ tlc591xx_probe(struct i2c_client *client,
if (!count || count > tlc591xx->max_leds)
return -EINVAL;
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE_DATA))
- return -EIO;
-
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
index 49794b47b51c..5bda6a9b56bb 100644
--- a/drivers/leds/trigger/Kconfig
+++ b/drivers/leds/trigger/Kconfig
@@ -72,7 +72,7 @@ config LEDS_TRIGGER_CPU
config LEDS_TRIGGER_GPIO
tristate "LED GPIO Trigger"
depends on LEDS_TRIGGERS
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
This allows LEDs to be controlled by gpio events. It's good
when using gpios as switches and triggering the needed LEDs