aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/leds/Kconfig15
-rw-r--r--drivers/leds/Makefile2
-rw-r--r--drivers/leds/led-class.c24
-rw-r--r--drivers/leds/leds-alix2.c181
-rw-r--r--drivers/leds/leds-ams-delta.c33
-rw-r--r--drivers/leds/leds-clevo-mail.c21
-rw-r--r--drivers/leds/leds-fsg.c37
-rw-r--r--drivers/leds/leds-gpio.c36
-rw-r--r--drivers/leds/leds-hp-disk.c20
-rw-r--r--drivers/leds/leds-hp6xx.c22
-rw-r--r--drivers/leds/leds-net48xx.c21
-rw-r--r--drivers/leds/leds-pca9532.c77
-rw-r--r--drivers/leds/leds-s3c24xx.c25
-rw-r--r--drivers/leds/leds-wm8350.c311
-rw-r--r--drivers/leds/leds-wrap.c27
-rw-r--r--drivers/leds/ledtrig-timer.c5
-rw-r--r--drivers/mfd/wm8350-core.c3
-rw-r--r--drivers/regulator/wm8350-regulator.c91
-rw-r--r--include/linux/leds-pca9532.h2
-rw-r--r--include/linux/leds.h5
-rw-r--r--include/linux/mfd/wm8350/pmic.h36
21 files changed, 749 insertions, 245 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index e7fb7d2fcbfc..a4a1ae214630 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -63,6 +63,12 @@ config LEDS_WRAP
63 help 63 help
64 This option enables support for the PCEngines WRAP programmable LEDs. 64 This option enables support for the PCEngines WRAP programmable LEDs.
65 65
66config LEDS_ALIX2
67 tristate "LED Support for ALIX.2 and ALIX.3 series"
68 depends on LEDS_CLASS && X86 && EXPERIMENTAL
69 help
70 This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs.
71
66config LEDS_H1940 72config LEDS_H1940
67 tristate "LED Support for iPAQ H1940 device" 73 tristate "LED Support for iPAQ H1940 device"
68 depends on LEDS_CLASS && ARCH_H1940 74 depends on LEDS_CLASS && ARCH_H1940
@@ -77,7 +83,7 @@ config LEDS_COBALT_QUBE
77 83
78config LEDS_COBALT_RAQ 84config LEDS_COBALT_RAQ
79 bool "LED Support for the Cobalt Raq series" 85 bool "LED Support for the Cobalt Raq series"
80 depends on LEDS_CLASS && MIPS_COBALT 86 depends on LEDS_CLASS=y && MIPS_COBALT
81 select LEDS_TRIGGERS 87 select LEDS_TRIGGERS
82 help 88 help
83 This option enables support for the Cobalt Raq series LEDs. 89 This option enables support for the Cobalt Raq series LEDs.
@@ -158,6 +164,13 @@ config LEDS_PCA955X
158 LED driver chips accessed via the I2C bus. Supported 164 LED driver chips accessed via the I2C bus. Supported
159 devices include PCA9550, PCA9551, PCA9552, and PCA9553. 165 devices include PCA9550, PCA9551, PCA9552, and PCA9553.
160 166
167config LEDS_WM8350
168 tristate "LED Support for WM8350 AudioPlus PMIC"
169 depends on LEDS_CLASS && MFD_WM8350
170 help
171 This option enables support for LEDs driven by the Wolfson
172 Microelectronics WM8350 AudioPlus PMIC.
173
161config LEDS_DA903X 174config LEDS_DA903X
162 tristate "LED Support for DA9030/DA9034 PMIC" 175 tristate "LED Support for DA9030/DA9034 PMIC"
163 depends on LEDS_CLASS && PMIC_DA903X 176 depends on LEDS_CLASS && PMIC_DA903X
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index e1967a29850e..bc247cb02e82 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
11obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o 11obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
12obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o 12obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
13obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o 13obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
14obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.o
14obj-$(CONFIG_LEDS_H1940) += leds-h1940.o 15obj-$(CONFIG_LEDS_H1940) += leds-h1940.o
15obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o 16obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
16obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o 17obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
@@ -23,6 +24,7 @@ obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
23obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o 24obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
24obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o 25obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
25obj-$(CONFIG_LEDS_HP_DISK) += leds-hp-disk.o 26obj-$(CONFIG_LEDS_HP_DISK) += leds-hp-disk.o
27obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
26 28
27# LED Triggers 29# LED Triggers
28obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o 30obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 6c4a326176d7..52f82e3ea13a 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -91,9 +91,29 @@ void led_classdev_resume(struct led_classdev *led_cdev)
91} 91}
92EXPORT_SYMBOL_GPL(led_classdev_resume); 92EXPORT_SYMBOL_GPL(led_classdev_resume);
93 93
94static int led_suspend(struct device *dev, pm_message_t state)
95{
96 struct led_classdev *led_cdev = dev_get_drvdata(dev);
97
98 if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
99 led_classdev_suspend(led_cdev);
100
101 return 0;
102}
103
104static int led_resume(struct device *dev)
105{
106 struct led_classdev *led_cdev = dev_get_drvdata(dev);
107
108 if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
109 led_classdev_resume(led_cdev);
110
111 return 0;
112}
113
94/** 114/**
95 * led_classdev_register - register a new object of led_classdev class. 115 * led_classdev_register - register a new object of led_classdev class.
96 * @dev: The device to register. 116 * @parent: The device to register.
97 * @led_cdev: the led_classdev structure for this device. 117 * @led_cdev: the led_classdev structure for this device.
98 */ 118 */
99int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) 119int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
@@ -174,6 +194,8 @@ static int __init leds_init(void)
174 leds_class = class_create(THIS_MODULE, "leds"); 194 leds_class = class_create(THIS_MODULE, "leds");
175 if (IS_ERR(leds_class)) 195 if (IS_ERR(leds_class))
176 return PTR_ERR(leds_class); 196 return PTR_ERR(leds_class);
197 leds_class->suspend = led_suspend;
198 leds_class->resume = led_resume;
177 return 0; 199 return 0;
178} 200}
179 201
diff --git a/drivers/leds/leds-alix2.c b/drivers/leds/leds-alix2.c
new file mode 100644
index 000000000000..ddbd7730dfc8
--- /dev/null
+++ b/drivers/leds/leds-alix2.c
@@ -0,0 +1,181 @@
1/*
2 * LEDs driver for PCEngines ALIX.2 and ALIX.3
3 *
4 * Copyright (C) 2008 Constantin Baranov <const@mimas.ru>
5 */
6
7#include <linux/err.h>
8#include <linux/io.h>
9#include <linux/kernel.h>
10#include <linux/leds.h>
11#include <linux/module.h>
12#include <linux/platform_device.h>
13#include <linux/string.h>
14
15static int force = 0;
16module_param(force, bool, 0444);
17MODULE_PARM_DESC(force, "Assume system has ALIX.2 style LEDs");
18
19struct alix_led {
20 struct led_classdev cdev;
21 unsigned short port;
22 unsigned int on_value;
23 unsigned int off_value;
24};
25
26static void alix_led_set(struct led_classdev *led_cdev,
27 enum led_brightness brightness)
28{
29 struct alix_led *led_dev =
30 container_of(led_cdev, struct alix_led, cdev);
31
32 if (brightness)
33 outl(led_dev->on_value, led_dev->port);
34 else
35 outl(led_dev->off_value, led_dev->port);
36}
37
38static struct alix_led alix_leds[] = {
39 {
40 .cdev = {
41 .name = "alix:1",
42 .brightness_set = alix_led_set,
43 },
44 .port = 0x6100,
45 .on_value = 1 << 22,
46 .off_value = 1 << 6,
47 },
48 {
49 .cdev = {
50 .name = "alix:2",
51 .brightness_set = alix_led_set,
52 },
53 .port = 0x6180,
54 .on_value = 1 << 25,
55 .off_value = 1 << 9,
56 },
57 {
58 .cdev = {
59 .name = "alix:3",
60 .brightness_set = alix_led_set,
61 },
62 .port = 0x6180,
63 .on_value = 1 << 27,
64 .off_value = 1 << 11,
65 },
66};
67
68static int __init alix_led_probe(struct platform_device *pdev)
69{
70 int i;
71 int ret;
72
73 for (i = 0; i < ARRAY_SIZE(alix_leds); i++) {
74 alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
75 ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev);
76 if (ret < 0)
77 goto fail;
78 }
79 return 0;
80
81fail:
82 while (--i >= 0)
83 led_classdev_unregister(&alix_leds[i].cdev);
84 return ret;
85}
86
87static int alix_led_remove(struct platform_device *pdev)
88{
89 int i;
90
91 for (i = 0; i < ARRAY_SIZE(alix_leds); i++)
92 led_classdev_unregister(&alix_leds[i].cdev);
93 return 0;
94}
95
96static struct platform_driver alix_led_driver = {
97 .remove = alix_led_remove,
98 .driver = {
99 .name = KBUILD_MODNAME,
100 .owner = THIS_MODULE,
101 },
102};
103
104static int __init alix_present(void)
105{
106 const unsigned long bios_phys = 0x000f0000;
107 const size_t bios_len = 0x00010000;
108 const char alix_sig[] = "PC Engines ALIX.";
109 const size_t alix_sig_len = sizeof(alix_sig) - 1;
110
111 const char *bios_virt;
112 const char *scan_end;
113 const char *p;
114 int ret = 0;
115
116 if (force) {
117 printk(KERN_NOTICE "%s: forced to skip BIOS test, "
118 "assume system has ALIX.2 style LEDs\n",
119 KBUILD_MODNAME);
120 ret = 1;
121 goto out;
122 }
123
124 bios_virt = phys_to_virt(bios_phys);
125 scan_end = bios_virt + bios_len - (alix_sig_len + 2);
126 for (p = bios_virt; p < scan_end; p++) {
127 const char *tail;
128
129 if (memcmp(p, alix_sig, alix_sig_len) != 0) {
130 continue;
131 }
132
133 tail = p + alix_sig_len;
134 if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') {
135 printk(KERN_INFO
136 "%s: system is recognized as \"%s\"\n",
137 KBUILD_MODNAME, p);
138 ret = 1;
139 break;
140 }
141 }
142
143out:
144 return ret;
145}
146
147static struct platform_device *pdev;
148
149static int __init alix_led_init(void)
150{
151 int ret;
152
153 if (!alix_present()) {
154 ret = -ENODEV;
155 goto out;
156 }
157
158 pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
159 if (!IS_ERR(pdev)) {
160 ret = platform_driver_probe(&alix_led_driver, alix_led_probe);
161 if (ret)
162 platform_device_unregister(pdev);
163 } else
164 ret = PTR_ERR(pdev);
165
166out:
167 return ret;
168}
169
170static void __exit alix_led_exit(void)
171{
172 platform_device_unregister(pdev);
173 platform_driver_unregister(&alix_led_driver);
174}
175
176module_init(alix_led_init);
177module_exit(alix_led_exit);
178
179MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>");
180MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver");
181MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c
index 1bd590bb3a6e..446050759b4d 100644
--- a/drivers/leds/leds-ams-delta.c
+++ b/drivers/leds/leds-ams-delta.c
@@ -79,37 +79,12 @@ static struct ams_delta_led ams_delta_leds[] = {
79 }, 79 },
80}; 80};
81 81
82#ifdef CONFIG_PM
83static int ams_delta_led_suspend(struct platform_device *dev,
84 pm_message_t state)
85{
86 int i;
87
88 for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++)
89 led_classdev_suspend(&ams_delta_leds[i].cdev);
90
91 return 0;
92}
93
94static int ams_delta_led_resume(struct platform_device *dev)
95{
96 int i;
97
98 for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++)
99 led_classdev_resume(&ams_delta_leds[i].cdev);
100
101 return 0;
102}
103#else
104#define ams_delta_led_suspend NULL
105#define ams_delta_led_resume NULL
106#endif
107
108static int ams_delta_led_probe(struct platform_device *pdev) 82static int ams_delta_led_probe(struct platform_device *pdev)
109{ 83{
110 int i, ret; 84 int i, ret;
111 85
112 for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) { 86 for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) {
87 ams_delta_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
113 ret = led_classdev_register(&pdev->dev, 88 ret = led_classdev_register(&pdev->dev,
114 &ams_delta_leds[i].cdev); 89 &ams_delta_leds[i].cdev);
115 if (ret < 0) 90 if (ret < 0)
@@ -127,7 +102,7 @@ static int ams_delta_led_remove(struct platform_device *pdev)
127{ 102{
128 int i; 103 int i;
129 104
130 for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i--) 105 for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++)
131 led_classdev_unregister(&ams_delta_leds[i].cdev); 106 led_classdev_unregister(&ams_delta_leds[i].cdev);
132 107
133 return 0; 108 return 0;
@@ -136,8 +111,6 @@ static int ams_delta_led_remove(struct platform_device *pdev)
136static struct platform_driver ams_delta_led_driver = { 111static struct platform_driver ams_delta_led_driver = {
137 .probe = ams_delta_led_probe, 112 .probe = ams_delta_led_probe,
138 .remove = ams_delta_led_remove, 113 .remove = ams_delta_led_remove,
139 .suspend = ams_delta_led_suspend,
140 .resume = ams_delta_led_resume,
141 .driver = { 114 .driver = {
142 .name = "ams-delta-led", 115 .name = "ams-delta-led",
143 .owner = THIS_MODULE, 116 .owner = THIS_MODULE,
@@ -151,7 +124,7 @@ static int __init ams_delta_led_init(void)
151 124
152static void __exit ams_delta_led_exit(void) 125static void __exit ams_delta_led_exit(void)
153{ 126{
154 return platform_driver_unregister(&ams_delta_led_driver); 127 platform_driver_unregister(&ams_delta_led_driver);
155} 128}
156 129
157module_init(ams_delta_led_init); 130module_init(ams_delta_led_init);
diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c
index eb3415e88f43..1813c84ea5fc 100644
--- a/drivers/leds/leds-clevo-mail.c
+++ b/drivers/leds/leds-clevo-mail.c
@@ -142,6 +142,7 @@ static struct led_classdev clevo_mail_led = {
142 .name = "clevo::mail", 142 .name = "clevo::mail",
143 .brightness_set = clevo_mail_led_set, 143 .brightness_set = clevo_mail_led_set,
144 .blink_set = clevo_mail_led_blink, 144 .blink_set = clevo_mail_led_blink,
145 .flags = LED_CORE_SUSPENDRESUME,
145}; 146};
146 147
147static int __init clevo_mail_led_probe(struct platform_device *pdev) 148static int __init clevo_mail_led_probe(struct platform_device *pdev)
@@ -155,29 +156,9 @@ static int clevo_mail_led_remove(struct platform_device *pdev)
155 return 0; 156 return 0;
156} 157}
157 158
158#ifdef CONFIG_PM
159static int clevo_mail_led_suspend(struct platform_device *dev,
160 pm_message_t state)
161{
162 led_classdev_suspend(&clevo_mail_led);
163 return 0;
164}
165
166static int clevo_mail_led_resume(struct platform_device *dev)
167{
168 led_classdev_resume(&clevo_mail_led);
169 return 0;
170}
171#else
172#define clevo_mail_led_suspend NULL
173#define clevo_mail_led_resume NULL
174#endif
175
176static struct platform_driver clevo_mail_led_driver = { 159static struct platform_driver clevo_mail_led_driver = {
177 .probe = clevo_mail_led_probe, 160 .probe = clevo_mail_led_probe,
178 .remove = clevo_mail_led_remove, 161 .remove = clevo_mail_led_remove,
179 .suspend = clevo_mail_led_suspend,
180 .resume = clevo_mail_led_resume,
181 .driver = { 162 .driver = {
182 .name = KBUILD_MODNAME, 163 .name = KBUILD_MODNAME,
183 .owner = THIS_MODULE, 164 .owner = THIS_MODULE,
diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c
index 34935155c1c0..5f7c9c5c09b1 100644
--- a/drivers/leds/leds-fsg.c
+++ b/drivers/leds/leds-fsg.c
@@ -99,64 +99,43 @@ static void fsg_led_ring_set(struct led_classdev *led_cdev,
99} 99}
100 100
101 101
102
103static struct led_classdev fsg_wlan_led = { 102static struct led_classdev fsg_wlan_led = {
104 .name = "fsg:blue:wlan", 103 .name = "fsg:blue:wlan",
105 .brightness_set = fsg_led_wlan_set, 104 .brightness_set = fsg_led_wlan_set,
105 .flags = LED_CORE_SUSPENDRESUME,
106}; 106};
107 107
108static struct led_classdev fsg_wan_led = { 108static struct led_classdev fsg_wan_led = {
109 .name = "fsg:blue:wan", 109 .name = "fsg:blue:wan",
110 .brightness_set = fsg_led_wan_set, 110 .brightness_set = fsg_led_wan_set,
111 .flags = LED_CORE_SUSPENDRESUME,
111}; 112};
112 113
113static struct led_classdev fsg_sata_led = { 114static struct led_classdev fsg_sata_led = {
114 .name = "fsg:blue:sata", 115 .name = "fsg:blue:sata",
115 .brightness_set = fsg_led_sata_set, 116 .brightness_set = fsg_led_sata_set,
117 .flags = LED_CORE_SUSPENDRESUME,
116}; 118};
117 119
118static struct led_classdev fsg_usb_led = { 120static struct led_classdev fsg_usb_led = {
119 .name = "fsg:blue:usb", 121 .name = "fsg:blue:usb",
120 .brightness_set = fsg_led_usb_set, 122 .brightness_set = fsg_led_usb_set,
123 .flags = LED_CORE_SUSPENDRESUME,
121}; 124};
122 125
123static struct led_classdev fsg_sync_led = { 126static struct led_classdev fsg_sync_led = {
124 .name = "fsg:blue:sync", 127 .name = "fsg:blue:sync",
125 .brightness_set = fsg_led_sync_set, 128 .brightness_set = fsg_led_sync_set,
129 .flags = LED_CORE_SUSPENDRESUME,
126}; 130};
127 131
128static struct led_classdev fsg_ring_led = { 132static struct led_classdev fsg_ring_led = {
129 .name = "fsg:blue:ring", 133 .name = "fsg:blue:ring",
130 .brightness_set = fsg_led_ring_set, 134 .brightness_set = fsg_led_ring_set,
135 .flags = LED_CORE_SUSPENDRESUME,
131}; 136};
132 137
133 138
134
135#ifdef CONFIG_PM
136static int fsg_led_suspend(struct platform_device *dev, pm_message_t state)
137{
138 led_classdev_suspend(&fsg_wlan_led);
139 led_classdev_suspend(&fsg_wan_led);
140 led_classdev_suspend(&fsg_sata_led);
141 led_classdev_suspend(&fsg_usb_led);
142 led_classdev_suspend(&fsg_sync_led);
143 led_classdev_suspend(&fsg_ring_led);
144 return 0;
145}
146
147static int fsg_led_resume(struct platform_device *dev)
148{
149 led_classdev_resume(&fsg_wlan_led);
150 led_classdev_resume(&fsg_wan_led);
151 led_classdev_resume(&fsg_sata_led);
152 led_classdev_resume(&fsg_usb_led);
153 led_classdev_resume(&fsg_sync_led);
154 led_classdev_resume(&fsg_ring_led);
155 return 0;
156}
157#endif
158
159
160static int fsg_led_probe(struct platform_device *pdev) 139static int fsg_led_probe(struct platform_device *pdev)
161{ 140{
162 int ret; 141 int ret;
@@ -232,10 +211,6 @@ static int fsg_led_remove(struct platform_device *pdev)
232static struct platform_driver fsg_led_driver = { 211static struct platform_driver fsg_led_driver = {
233 .probe = fsg_led_probe, 212 .probe = fsg_led_probe,
234 .remove = fsg_led_remove, 213 .remove = fsg_led_remove,
235#ifdef CONFIG_PM
236 .suspend = fsg_led_suspend,
237 .resume = fsg_led_resume,
238#endif
239 .driver = { 214 .driver = {
240 .name = "fsg-led", 215 .name = "fsg-led",
241 }, 216 },
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index b13bd2950e95..2e3df08b649b 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -105,6 +105,7 @@ static int gpio_led_probe(struct platform_device *pdev)
105 } 105 }
106 led_dat->cdev.brightness_set = gpio_led_set; 106 led_dat->cdev.brightness_set = gpio_led_set;
107 led_dat->cdev.brightness = LED_OFF; 107 led_dat->cdev.brightness = LED_OFF;
108 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
108 109
109 gpio_direction_output(led_dat->gpio, led_dat->active_low); 110 gpio_direction_output(led_dat->gpio, led_dat->active_low);
110 111
@@ -154,44 +155,9 @@ static int __devexit gpio_led_remove(struct platform_device *pdev)
154 return 0; 155 return 0;
155} 156}
156 157
157#ifdef CONFIG_PM
158static int gpio_led_suspend(struct platform_device *pdev, pm_message_t state)
159{
160 struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
161 struct gpio_led_data *leds_data;
162 int i;
163
164 leds_data = platform_get_drvdata(pdev);
165
166 for (i = 0; i < pdata->num_leds; i++)
167 led_classdev_suspend(&leds_data[i].cdev);
168
169 return 0;
170}
171
172static int gpio_led_resume(struct platform_device *pdev)
173{
174 struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
175 struct gpio_led_data *leds_data;
176 int i;
177
178 leds_data = platform_get_drvdata(pdev);
179
180 for (i = 0; i < pdata->num_leds; i++)
181 led_classdev_resume(&leds_data[i].cdev);
182
183 return 0;
184}
185#else
186#define gpio_led_suspend NULL
187#define gpio_led_resume NULL
188#endif
189
190static struct platform_driver gpio_led_driver = { 158static struct platform_driver gpio_led_driver = {
191 .probe = gpio_led_probe, 159 .probe = gpio_led_probe,
192 .remove = __devexit_p(gpio_led_remove), 160 .remove = __devexit_p(gpio_led_remove),
193 .suspend = gpio_led_suspend,
194 .resume = gpio_led_resume,
195 .driver = { 161 .driver = {
196 .name = "leds-gpio", 162 .name = "leds-gpio",
197 .owner = THIS_MODULE, 163 .owner = THIS_MODULE,
diff --git a/drivers/leds/leds-hp-disk.c b/drivers/leds/leds-hp-disk.c
index 44fa757d8254..d786adc8c5e3 100644
--- a/drivers/leds/leds-hp-disk.c
+++ b/drivers/leds/leds-hp-disk.c
@@ -68,25 +68,9 @@ static struct led_classdev hpled_led = {
68 .name = "hp:red:hddprotection", 68 .name = "hp:red:hddprotection",
69 .default_trigger = "heartbeat", 69 .default_trigger = "heartbeat",
70 .brightness_set = hpled_set, 70 .brightness_set = hpled_set,
71 .flags = LED_CORE_SUSPENDRESUME,
71}; 72};
72 73
73#ifdef CONFIG_PM
74static int hpled_suspend(struct acpi_device *dev, pm_message_t state)
75{
76 led_classdev_suspend(&hpled_led);
77 return 0;
78}
79
80static int hpled_resume(struct acpi_device *dev)
81{
82 led_classdev_resume(&hpled_led);
83 return 0;
84}
85#else
86#define hpled_suspend NULL
87#define hpled_resume NULL
88#endif
89
90static int hpled_add(struct acpi_device *device) 74static int hpled_add(struct acpi_device *device)
91{ 75{
92 int ret; 76 int ret;
@@ -121,8 +105,6 @@ static struct acpi_driver leds_hp_driver = {
121 .ops = { 105 .ops = {
122 .add = hpled_add, 106 .add = hpled_add,
123 .remove = hpled_remove, 107 .remove = hpled_remove,
124 .suspend = hpled_suspend,
125 .resume = hpled_resume,
126 } 108 }
127}; 109};
128 110
diff --git a/drivers/leds/leds-hp6xx.c b/drivers/leds/leds-hp6xx.c
index e8fb1baf8a50..e4ce1fd46338 100644
--- a/drivers/leds/leds-hp6xx.c
+++ b/drivers/leds/leds-hp6xx.c
@@ -45,30 +45,16 @@ static struct led_classdev hp6xx_red_led = {
45 .name = "hp6xx:red", 45 .name = "hp6xx:red",
46 .default_trigger = "hp6xx-charge", 46 .default_trigger = "hp6xx-charge",
47 .brightness_set = hp6xxled_red_set, 47 .brightness_set = hp6xxled_red_set,
48 .flags = LED_CORE_SUSPENDRESUME,
48}; 49};
49 50
50static struct led_classdev hp6xx_green_led = { 51static struct led_classdev hp6xx_green_led = {
51 .name = "hp6xx:green", 52 .name = "hp6xx:green",
52 .default_trigger = "ide-disk", 53 .default_trigger = "ide-disk",
53 .brightness_set = hp6xxled_green_set, 54 .brightness_set = hp6xxled_green_set,
55 .flags = LED_CORE_SUSPENDRESUME,
54}; 56};
55 57
56#ifdef CONFIG_PM
57static int hp6xxled_suspend(struct platform_device *dev, pm_message_t state)
58{
59 led_classdev_suspend(&hp6xx_red_led);
60 led_classdev_suspend(&hp6xx_green_led);
61 return 0;
62}
63
64static int hp6xxled_resume(struct platform_device *dev)
65{
66 led_classdev_resume(&hp6xx_red_led);
67 led_classdev_resume(&hp6xx_green_led);
68 return 0;
69}
70#endif
71
72static int hp6xxled_probe(struct platform_device *pdev) 58static int hp6xxled_probe(struct platform_device *pdev)
73{ 59{
74 int ret; 60 int ret;
@@ -98,10 +84,6 @@ MODULE_ALIAS("platform:hp6xx-led");
98static struct platform_driver hp6xxled_driver = { 84static struct platform_driver hp6xxled_driver = {
99 .probe = hp6xxled_probe, 85 .probe = hp6xxled_probe,
100 .remove = hp6xxled_remove, 86 .remove = hp6xxled_remove,
101#ifdef CONFIG_PM
102 .suspend = hp6xxled_suspend,
103 .resume = hp6xxled_resume,
104#endif
105 .driver = { 87 .driver = {
106 .name = "hp6xx-led", 88 .name = "hp6xx-led",
107 .owner = THIS_MODULE, 89 .owner = THIS_MODULE,
diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c
index 054360473c94..93987a12da49 100644
--- a/drivers/leds/leds-net48xx.c
+++ b/drivers/leds/leds-net48xx.c
@@ -33,26 +33,9 @@ static void net48xx_error_led_set(struct led_classdev *led_cdev,
33static struct led_classdev net48xx_error_led = { 33static struct led_classdev net48xx_error_led = {
34 .name = "net48xx::error", 34 .name = "net48xx::error",
35 .brightness_set = net48xx_error_led_set, 35 .brightness_set = net48xx_error_led_set,
36 .flags = LED_CORE_SUSPENDRESUME,
36}; 37};
37 38
38#ifdef CONFIG_PM
39static int net48xx_led_suspend(struct platform_device *dev,
40 pm_message_t state)
41{
42 led_classdev_suspend(&net48xx_error_led);
43 return 0;
44}
45
46static int net48xx_led_resume(struct platform_device *dev)
47{
48 led_classdev_resume(&net48xx_error_led);
49 return 0;
50}
51#else
52#define net48xx_led_suspend NULL
53#define net48xx_led_resume NULL
54#endif
55
56static int net48xx_led_probe(struct platform_device *pdev) 39static int net48xx_led_probe(struct platform_device *pdev)
57{ 40{
58 return led_classdev_register(&pdev->dev, &net48xx_error_led); 41 return led_classdev_register(&pdev->dev, &net48xx_error_led);
@@ -67,8 +50,6 @@ static int net48xx_led_remove(struct platform_device *pdev)
67static struct platform_driver net48xx_led_driver = { 50static struct platform_driver net48xx_led_driver = {
68 .probe = net48xx_led_probe, 51 .probe = net48xx_led_probe,
69 .remove = net48xx_led_remove, 52 .remove = net48xx_led_remove,
70 .suspend = net48xx_led_suspend,
71 .resume = net48xx_led_resume,
72 .driver = { 53 .driver = {
73 .name = DRVNAME, 54 .name = DRVNAME,
74 .owner = THIS_MODULE, 55 .owner = THIS_MODULE,
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index 4064d4f6b33b..76ec7498e2d5 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -16,6 +16,7 @@
16#include <linux/leds.h> 16#include <linux/leds.h>
17#include <linux/input.h> 17#include <linux/input.h>
18#include <linux/mutex.h> 18#include <linux/mutex.h>
19#include <linux/workqueue.h>
19#include <linux/leds-pca9532.h> 20#include <linux/leds-pca9532.h>
20 21
21static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; 22static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END};
@@ -34,6 +35,7 @@ struct pca9532_data {
34 struct pca9532_led leds[16]; 35 struct pca9532_led leds[16];
35 struct mutex update_lock; 36 struct mutex update_lock;
36 struct input_dev *idev; 37 struct input_dev *idev;
38 struct work_struct work;
37 u8 pwm[2]; 39 u8 pwm[2];
38 u8 psc[2]; 40 u8 psc[2];
39}; 41};
@@ -63,7 +65,7 @@ static struct i2c_driver pca9532_driver = {
63 * as a compromise we average one pwm to the values requested by all 65 * as a compromise we average one pwm to the values requested by all
64 * leds that are not ON/OFF. 66 * leds that are not ON/OFF.
65 * */ 67 * */
66static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, 68static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink,
67 enum led_brightness value) 69 enum led_brightness value)
68{ 70{
69 int a = 0, b = 0, i = 0; 71 int a = 0, b = 0, i = 0;
@@ -84,11 +86,17 @@ static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink,
84 b = b/a; 86 b = b/a;
85 if (b > 0xFF) 87 if (b > 0xFF)
86 return -EINVAL; 88 return -EINVAL;
87 mutex_lock(&data->update_lock);
88 data->pwm[pwm] = b; 89 data->pwm[pwm] = b;
90 data->psc[pwm] = blink;
91 return 0;
92}
93
94static int pca9532_setpwm(struct i2c_client *client, int pwm)
95{
96 struct pca9532_data *data = i2c_get_clientdata(client);
97 mutex_lock(&data->update_lock);
89 i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), 98 i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm),
90 data->pwm[pwm]); 99 data->pwm[pwm]);
91 data->psc[pwm] = blink;
92 i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), 100 i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm),
93 data->psc[pwm]); 101 data->psc[pwm]);
94 mutex_unlock(&data->update_lock); 102 mutex_unlock(&data->update_lock);
@@ -124,11 +132,11 @@ static void pca9532_set_brightness(struct led_classdev *led_cdev,
124 led->state = PCA9532_ON; 132 led->state = PCA9532_ON;
125 else { 133 else {
126 led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ 134 led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */
127 err = pca9532_setpwm(led->client, 0, 0, value); 135 err = pca9532_calcpwm(led->client, 0, 0, value);
128 if (err) 136 if (err)
129 return; /* XXX: led api doesn't allow error code? */ 137 return; /* XXX: led api doesn't allow error code? */
130 } 138 }
131 pca9532_setled(led); 139 schedule_work(&led->work);
132} 140}
133 141
134static int pca9532_set_blink(struct led_classdev *led_cdev, 142static int pca9532_set_blink(struct led_classdev *led_cdev,
@@ -137,6 +145,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
137 struct pca9532_led *led = ldev_to_led(led_cdev); 145 struct pca9532_led *led = ldev_to_led(led_cdev);
138 struct i2c_client *client = led->client; 146 struct i2c_client *client = led->client;
139 int psc; 147 int psc;
148 int err = 0;
140 149
141 if (*delay_on == 0 && *delay_off == 0) { 150 if (*delay_on == 0 && *delay_off == 0) {
142 /* led subsystem ask us for a blink rate */ 151 /* led subsystem ask us for a blink rate */
@@ -148,11 +157,15 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
148 157
149 /* Thecus specific: only use PSC/PWM 0 */ 158 /* Thecus specific: only use PSC/PWM 0 */
150 psc = (*delay_on * 152-1)/1000; 159 psc = (*delay_on * 152-1)/1000;
151 return pca9532_setpwm(client, 0, psc, led_cdev->brightness); 160 err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness);
161 if (err)
162 return err;
163 schedule_work(&led->work);
164 return 0;
152} 165}
153 166
154int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, 167static int pca9532_event(struct input_dev *dev, unsigned int type,
155 int value) 168 unsigned int code, int value)
156{ 169{
157 struct pca9532_data *data = input_get_drvdata(dev); 170 struct pca9532_data *data = input_get_drvdata(dev);
158 171
@@ -165,13 +178,28 @@ int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code,
165 else 178 else
166 data->pwm[1] = 0; 179 data->pwm[1] = 0;
167 180
168 dev_info(&dev->dev, "setting beep to %d \n", data->pwm[1]); 181 schedule_work(&data->work);
182
183 return 0;
184}
185
186static void pca9532_input_work(struct work_struct *work)
187{
188 struct pca9532_data *data;
189 data = container_of(work, struct pca9532_data, work);
169 mutex_lock(&data->update_lock); 190 mutex_lock(&data->update_lock);
170 i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), 191 i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1),
171 data->pwm[1]); 192 data->pwm[1]);
172 mutex_unlock(&data->update_lock); 193 mutex_unlock(&data->update_lock);
194}
173 195
174 return 0; 196static void pca9532_led_work(struct work_struct *work)
197{
198 struct pca9532_led *led;
199 led = container_of(work, struct pca9532_led, work);
200 if (led->state == PCA9532_PWM0)
201 pca9532_setpwm(led->client, 0);
202 pca9532_setled(led);
175} 203}
176 204
177static int pca9532_configure(struct i2c_client *client, 205static int pca9532_configure(struct i2c_client *client,
@@ -204,8 +232,9 @@ static int pca9532_configure(struct i2c_client *client,
204 led->ldev.brightness = LED_OFF; 232 led->ldev.brightness = LED_OFF;
205 led->ldev.brightness_set = pca9532_set_brightness; 233 led->ldev.brightness_set = pca9532_set_brightness;
206 led->ldev.blink_set = pca9532_set_blink; 234 led->ldev.blink_set = pca9532_set_blink;
207 if (led_classdev_register(&client->dev, 235 INIT_WORK(&led->work, pca9532_led_work);
208 &led->ldev) < 0) { 236 err = led_classdev_register(&client->dev, &led->ldev);
237 if (err < 0) {
209 dev_err(&client->dev, 238 dev_err(&client->dev,
210 "couldn't register LED %s\n", 239 "couldn't register LED %s\n",
211 led->name); 240 led->name);
@@ -233,9 +262,11 @@ static int pca9532_configure(struct i2c_client *client,
233 BIT_MASK(SND_TONE); 262 BIT_MASK(SND_TONE);
234 data->idev->event = pca9532_event; 263 data->idev->event = pca9532_event;
235 input_set_drvdata(data->idev, data); 264 input_set_drvdata(data->idev, data);
265 INIT_WORK(&data->work, pca9532_input_work);
236 err = input_register_device(data->idev); 266 err = input_register_device(data->idev);
237 if (err) { 267 if (err) {
238 input_free_device(data->idev); 268 input_free_device(data->idev);
269 cancel_work_sync(&data->work);
239 data->idev = NULL; 270 data->idev = NULL;
240 goto exit; 271 goto exit;
241 } 272 }
@@ -252,18 +283,19 @@ exit:
252 break; 283 break;
253 case PCA9532_TYPE_LED: 284 case PCA9532_TYPE_LED:
254 led_classdev_unregister(&data->leds[i].ldev); 285 led_classdev_unregister(&data->leds[i].ldev);
286 cancel_work_sync(&data->leds[i].work);
255 break; 287 break;
256 case PCA9532_TYPE_N2100_BEEP: 288 case PCA9532_TYPE_N2100_BEEP:
257 if (data->idev != NULL) { 289 if (data->idev != NULL) {
258 input_unregister_device(data->idev); 290 input_unregister_device(data->idev);
259 input_free_device(data->idev); 291 input_free_device(data->idev);
292 cancel_work_sync(&data->work);
260 data->idev = NULL; 293 data->idev = NULL;
261 } 294 }
262 break; 295 break;
263 } 296 }
264 297
265 return err; 298 return err;
266
267} 299}
268 300
269static int pca9532_probe(struct i2c_client *client, 301static int pca9532_probe(struct i2c_client *client,
@@ -271,12 +303,16 @@ static int pca9532_probe(struct i2c_client *client,
271{ 303{
272 struct pca9532_data *data = i2c_get_clientdata(client); 304 struct pca9532_data *data = i2c_get_clientdata(client);
273 struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data; 305 struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data;
306 int err;
307
308 if (!pca9532_pdata)
309 return -EIO;
274 310
275 if (!i2c_check_functionality(client->adapter, 311 if (!i2c_check_functionality(client->adapter,
276 I2C_FUNC_SMBUS_BYTE_DATA)) 312 I2C_FUNC_SMBUS_BYTE_DATA))
277 return -EIO; 313 return -EIO;
278 314
279 data = kzalloc(sizeof(struct pca9532_data), GFP_KERNEL); 315 data = kzalloc(sizeof(*data), GFP_KERNEL);
280 if (!data) 316 if (!data)
281 return -ENOMEM; 317 return -ENOMEM;
282 318
@@ -285,12 +321,13 @@ static int pca9532_probe(struct i2c_client *client,
285 data->client = client; 321 data->client = client;
286 mutex_init(&data->update_lock); 322 mutex_init(&data->update_lock);
287 323
288 if (pca9532_pdata == NULL) 324 err = pca9532_configure(client, data, pca9532_pdata);
289 return -EIO; 325 if (err) {
290 326 kfree(data);
291 pca9532_configure(client, data, pca9532_pdata); 327 i2c_set_clientdata(client, NULL);
292 return 0; 328 }
293 329
330 return err;
294} 331}
295 332
296static int pca9532_remove(struct i2c_client *client) 333static int pca9532_remove(struct i2c_client *client)
@@ -303,11 +340,13 @@ static int pca9532_remove(struct i2c_client *client)
303 break; 340 break;
304 case PCA9532_TYPE_LED: 341 case PCA9532_TYPE_LED:
305 led_classdev_unregister(&data->leds[i].ldev); 342 led_classdev_unregister(&data->leds[i].ldev);
343 cancel_work_sync(&data->leds[i].work);
306 break; 344 break;
307 case PCA9532_TYPE_N2100_BEEP: 345 case PCA9532_TYPE_N2100_BEEP:
308 if (data->idev != NULL) { 346 if (data->idev != NULL) {
309 input_unregister_device(data->idev); 347 input_unregister_device(data->idev);
310 input_free_device(data->idev); 348 input_free_device(data->idev);
349 cancel_work_sync(&data->work);
311 data->idev = NULL; 350 data->idev = NULL;
312 } 351 }
313 break; 352 break;
diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c
index 25a07f2643ad..4d81131542ae 100644
--- a/drivers/leds/leds-s3c24xx.c
+++ b/drivers/leds/leds-s3c24xx.c
@@ -82,6 +82,7 @@ static int s3c24xx_led_probe(struct platform_device *dev)
82 led->cdev.brightness_set = s3c24xx_led_set; 82 led->cdev.brightness_set = s3c24xx_led_set;
83 led->cdev.default_trigger = pdata->def_trigger; 83 led->cdev.default_trigger = pdata->def_trigger;
84 led->cdev.name = pdata->name; 84 led->cdev.name = pdata->name;
85 led->cdev.flags |= LED_CORE_SUSPENDRESUME;
85 86
86 led->pdata = pdata; 87 led->pdata = pdata;
87 88
@@ -111,33 +112,9 @@ static int s3c24xx_led_probe(struct platform_device *dev)
111 return ret; 112 return ret;
112} 113}
113 114
114
115#ifdef CONFIG_PM
116static int s3c24xx_led_suspend(struct platform_device *dev, pm_message_t state)
117{
118 struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);
119
120 led_classdev_suspend(&led->cdev);
121 return 0;
122}
123
124static int s3c24xx_led_resume(struct platform_device *dev)
125{
126 struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);
127
128 led_classdev_resume(&led->cdev);
129 return 0;
130}
131#else
132#define s3c24xx_led_suspend NULL
133#define s3c24xx_led_resume NULL
134#endif
135
136static struct platform_driver s3c24xx_led_driver = { 115static struct platform_driver s3c24xx_led_driver = {
137 .probe = s3c24xx_led_probe, 116 .probe = s3c24xx_led_probe,
138 .remove = s3c24xx_led_remove, 117 .remove = s3c24xx_led_remove,
139 .suspend = s3c24xx_led_suspend,
140 .resume = s3c24xx_led_resume,
141 .driver = { 118 .driver = {
142 .name = "s3c24xx_led", 119 .name = "s3c24xx_led",
143 .owner = THIS_MODULE, 120 .owner = THIS_MODULE,
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c
new file mode 100644
index 000000000000..38c6bcb07e6c
--- /dev/null
+++ b/drivers/leds/leds-wm8350.c
@@ -0,0 +1,311 @@
1/*
2 * LED driver for WM8350 driven LEDS.
3 *
4 * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 */
11
12#include <linux/kernel.h>
13#include <linux/init.h>
14#include <linux/platform_device.h>
15#include <linux/leds.h>
16#include <linux/err.h>
17#include <linux/mfd/wm8350/pmic.h>
18#include <linux/regulator/consumer.h>
19
20/* Microamps */
21static const int isink_cur[] = {
22 4,
23 5,
24 6,
25 7,
26 8,
27 10,
28 11,
29 14,
30 16,
31 19,
32 23,
33 27,
34 32,
35 39,
36 46,
37 54,
38 65,
39 77,
40 92,
41 109,
42 130,
43 154,
44 183,
45 218,
46 259,
47 308,
48 367,
49 436,
50 518,
51 616,
52 733,
53 872,
54 1037,
55 1233,
56 1466,
57 1744,
58 2073,
59 2466,
60 2933,
61 3487,
62 4147,
63 4932,
64 5865,
65 6975,
66 8294,
67 9864,
68 11730,
69 13949,
70 16589,
71 19728,
72 23460,
73 27899,
74 33178,
75 39455,
76 46920,
77 55798,
78 66355,
79 78910,
80 93840,
81 111596,
82 132710,
83 157820,
84 187681,
85 223191
86};
87
88#define to_wm8350_led(led_cdev) \
89 container_of(led_cdev, struct wm8350_led, cdev)
90
91static void wm8350_led_enable(struct wm8350_led *led)
92{
93 int ret;
94
95 if (led->enabled)
96 return;
97
98 ret = regulator_enable(led->isink);
99 if (ret != 0) {
100 dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
101 return;
102 }
103
104 ret = regulator_enable(led->dcdc);
105 if (ret != 0) {
106 dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
107 regulator_disable(led->isink);
108 return;
109 }
110
111 led->enabled = 1;
112}
113
114static void wm8350_led_disable(struct wm8350_led *led)
115{
116 int ret;
117
118 if (!led->enabled)
119 return;
120
121 ret = regulator_disable(led->dcdc);
122 if (ret != 0) {
123 dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
124 return;
125 }
126
127 ret = regulator_disable(led->isink);
128 if (ret != 0) {
129 dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
130 regulator_enable(led->dcdc);
131 return;
132 }
133
134 led->enabled = 0;
135}
136
137static void led_work(struct work_struct *work)
138{
139 struct wm8350_led *led = container_of(work, struct wm8350_led, work);
140 int ret;
141 int uA;
142 unsigned long flags;
143
144 mutex_lock(&led->mutex);
145
146 spin_lock_irqsave(&led->value_lock, flags);
147
148 if (led->value == LED_OFF) {
149 spin_unlock_irqrestore(&led->value_lock, flags);
150 wm8350_led_disable(led);
151 goto out;
152 }
153
154 /* This scales linearly into the index of valid current
155 * settings which results in a linear scaling of perceived
156 * brightness due to the non-linear current settings provided
157 * by the hardware.
158 */
159 uA = (led->max_uA_index * led->value) / LED_FULL;
160 spin_unlock_irqrestore(&led->value_lock, flags);
161 BUG_ON(uA >= ARRAY_SIZE(isink_cur));
162
163 ret = regulator_set_current_limit(led->isink, isink_cur[uA],
164 isink_cur[uA]);
165 if (ret != 0)
166 dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
167 isink_cur[uA], ret);
168
169 wm8350_led_enable(led);
170
171out:
172 mutex_unlock(&led->mutex);
173}
174
175static void wm8350_led_set(struct led_classdev *led_cdev,
176 enum led_brightness value)
177{
178 struct wm8350_led *led = to_wm8350_led(led_cdev);
179 unsigned long flags;
180
181 spin_lock_irqsave(&led->value_lock, flags);
182 led->value = value;
183 schedule_work(&led->work);
184 spin_unlock_irqrestore(&led->value_lock, flags);
185}
186
187static void wm8350_led_shutdown(struct platform_device *pdev)
188{
189 struct wm8350_led *led = platform_get_drvdata(pdev);
190
191 mutex_lock(&led->mutex);
192 led->value = LED_OFF;
193 wm8350_led_disable(led);
194 mutex_unlock(&led->mutex);
195}
196
197static int wm8350_led_probe(struct platform_device *pdev)
198{
199 struct regulator *isink, *dcdc;
200 struct wm8350_led *led;
201 struct wm8350_led_platform_data *pdata = pdev->dev.platform_data;
202 int ret, i;
203
204 if (pdata == NULL) {
205 dev_err(&pdev->dev, "no platform data\n");
206 return -ENODEV;
207 }
208
209 if (pdata->max_uA < isink_cur[0]) {
210 dev_err(&pdev->dev, "Invalid maximum current %duA\n",
211 pdata->max_uA);
212 return -EINVAL;
213 }
214
215 isink = regulator_get(&pdev->dev, "led_isink");
216 if (IS_ERR(isink)) {
217 printk(KERN_ERR "%s: cant get ISINK\n", __func__);
218 return PTR_ERR(isink);
219 }
220
221 dcdc = regulator_get(&pdev->dev, "led_vcc");
222 if (IS_ERR(dcdc)) {
223 printk(KERN_ERR "%s: cant get DCDC\n", __func__);
224 ret = PTR_ERR(dcdc);
225 goto err_isink;
226 }
227
228 led = kzalloc(sizeof(*led), GFP_KERNEL);
229 if (led == NULL) {
230 ret = -ENOMEM;
231 goto err_dcdc;
232 }
233
234 led->cdev.brightness_set = wm8350_led_set;
235 led->cdev.default_trigger = pdata->default_trigger;
236 led->cdev.name = pdata->name;
237 led->cdev.flags |= LED_CORE_SUSPENDRESUME;
238 led->enabled = regulator_is_enabled(isink);
239 led->isink = isink;
240 led->dcdc = dcdc;
241
242 for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++)
243 if (isink_cur[i] >= pdata->max_uA)
244 break;
245 led->max_uA_index = i;
246 if (pdata->max_uA != isink_cur[i])
247 dev_warn(&pdev->dev,
248 "Maximum current %duA is not directly supported,"
249 " check platform data\n",
250 pdata->max_uA);
251
252 spin_lock_init(&led->value_lock);
253 mutex_init(&led->mutex);
254 INIT_WORK(&led->work, led_work);
255 led->value = LED_OFF;
256 platform_set_drvdata(pdev, led);
257
258 ret = led_classdev_register(&pdev->dev, &led->cdev);
259 if (ret < 0)
260 goto err_led;
261
262 return 0;
263
264 err_led:
265 kfree(led);
266 err_dcdc:
267 regulator_put(dcdc);
268 err_isink:
269 regulator_put(isink);
270 return ret;
271}
272
273static int wm8350_led_remove(struct platform_device *pdev)
274{
275 struct wm8350_led *led = platform_get_drvdata(pdev);
276
277 led_classdev_unregister(&led->cdev);
278 flush_scheduled_work();
279 wm8350_led_disable(led);
280 regulator_put(led->dcdc);
281 regulator_put(led->isink);
282 kfree(led);
283 return 0;
284}
285
286static struct platform_driver wm8350_led_driver = {
287 .driver = {
288 .name = "wm8350-led",
289 .owner = THIS_MODULE,
290 },
291 .probe = wm8350_led_probe,
292 .remove = wm8350_led_remove,
293 .shutdown = wm8350_led_shutdown,
294};
295
296static int __devinit wm8350_led_init(void)
297{
298 return platform_driver_register(&wm8350_led_driver);
299}
300module_init(wm8350_led_init);
301
302static void wm8350_led_exit(void)
303{
304 platform_driver_unregister(&wm8350_led_driver);
305}
306module_exit(wm8350_led_exit);
307
308MODULE_AUTHOR("Mark Brown");
309MODULE_DESCRIPTION("WM8350 LED driver");
310MODULE_LICENSE("GPL");
311MODULE_ALIAS("platform:wm8350-led");
diff --git a/drivers/leds/leds-wrap.c b/drivers/leds/leds-wrap.c
index 2f3aa87f2a1f..2982c86ac4cf 100644
--- a/drivers/leds/leds-wrap.c
+++ b/drivers/leds/leds-wrap.c
@@ -56,40 +56,21 @@ static struct led_classdev wrap_power_led = {
56 .name = "wrap::power", 56 .name = "wrap::power",
57 .brightness_set = wrap_power_led_set, 57 .brightness_set = wrap_power_led_set,
58 .default_trigger = "default-on", 58 .default_trigger = "default-on",
59 .flags = LED_CORE_SUSPENDRESUME,
59}; 60};
60 61
61static struct led_classdev wrap_error_led = { 62static struct led_classdev wrap_error_led = {
62 .name = "wrap::error", 63 .name = "wrap::error",
63 .brightness_set = wrap_error_led_set, 64 .brightness_set = wrap_error_led_set,
65 .flags = LED_CORE_SUSPENDRESUME,
64}; 66};
65 67
66static struct led_classdev wrap_extra_led = { 68static struct led_classdev wrap_extra_led = {
67 .name = "wrap::extra", 69 .name = "wrap::extra",
68 .brightness_set = wrap_extra_led_set, 70 .brightness_set = wrap_extra_led_set,
71 .flags = LED_CORE_SUSPENDRESUME,
69}; 72};
70 73
71#ifdef CONFIG_PM
72static int wrap_led_suspend(struct platform_device *dev,
73 pm_message_t state)
74{
75 led_classdev_suspend(&wrap_power_led);
76 led_classdev_suspend(&wrap_error_led);
77 led_classdev_suspend(&wrap_extra_led);
78 return 0;
79}
80
81static int wrap_led_resume(struct platform_device *dev)
82{
83 led_classdev_resume(&wrap_power_led);
84 led_classdev_resume(&wrap_error_led);
85 led_classdev_resume(&wrap_extra_led);
86 return 0;
87}
88#else
89#define wrap_led_suspend NULL
90#define wrap_led_resume NULL
91#endif
92
93static int wrap_led_probe(struct platform_device *pdev) 74static int wrap_led_probe(struct platform_device *pdev)
94{ 75{
95 int ret; 76 int ret;
@@ -127,8 +108,6 @@ static int wrap_led_remove(struct platform_device *pdev)
127static struct platform_driver wrap_led_driver = { 108static struct platform_driver wrap_led_driver = {
128 .probe = wrap_led_probe, 109 .probe = wrap_led_probe,
129 .remove = wrap_led_remove, 110 .remove = wrap_led_remove,
130 .suspend = wrap_led_suspend,
131 .resume = wrap_led_resume,
132 .driver = { 111 .driver = {
133 .name = DRVNAME, 112 .name = DRVNAME,
134 .owner = THIS_MODULE, 113 .owner = THIS_MODULE,
diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c
index db681962d7bb..3d6531396dda 100644
--- a/drivers/leds/ledtrig-timer.c
+++ b/drivers/leds/ledtrig-timer.c
@@ -199,6 +199,7 @@ err_out:
199static void timer_trig_deactivate(struct led_classdev *led_cdev) 199static void timer_trig_deactivate(struct led_classdev *led_cdev)
200{ 200{
201 struct timer_trig_data *timer_data = led_cdev->trigger_data; 201 struct timer_trig_data *timer_data = led_cdev->trigger_data;
202 unsigned long on = 0, off = 0;
202 203
203 if (timer_data) { 204 if (timer_data) {
204 device_remove_file(led_cdev->dev, &dev_attr_delay_on); 205 device_remove_file(led_cdev->dev, &dev_attr_delay_on);
@@ -206,6 +207,10 @@ static void timer_trig_deactivate(struct led_classdev *led_cdev)
206 del_timer_sync(&timer_data->timer); 207 del_timer_sync(&timer_data->timer);
207 kfree(timer_data); 208 kfree(timer_data);
208 } 209 }
210
211 /* If there is hardware support for blinking, stop it */
212 if (led_cdev->blink_set)
213 led_cdev->blink_set(led_cdev, &on, &off);
209} 214}
210 215
211static struct led_trigger timer_led_trigger = { 216static struct led_trigger timer_led_trigger = {
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index 3a273ccef3f2..f92595c8f165 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -1453,6 +1453,9 @@ void wm8350_device_exit(struct wm8350 *wm8350)
1453{ 1453{
1454 int i; 1454 int i;
1455 1455
1456 for (i = 0; i < ARRAY_SIZE(wm8350->pmic.led); i++)
1457 platform_device_unregister(wm8350->pmic.led[i].pdev);
1458
1456 for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++) 1459 for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++)
1457 platform_device_unregister(wm8350->pmic.pdev[i]); 1460 platform_device_unregister(wm8350->pmic.pdev[i]);
1458 1461
diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c
index c68c496b2c49..7aa35248181b 100644
--- a/drivers/regulator/wm8350-regulator.c
+++ b/drivers/regulator/wm8350-regulator.c
@@ -1412,6 +1412,97 @@ int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
1412} 1412}
1413EXPORT_SYMBOL_GPL(wm8350_register_regulator); 1413EXPORT_SYMBOL_GPL(wm8350_register_regulator);
1414 1414
1415/**
1416 * wm8350_register_led - Register a WM8350 LED output
1417 *
1418 * @param wm8350 The WM8350 device to configure.
1419 * @param lednum LED device index to create.
1420 * @param dcdc The DCDC to use for the LED.
1421 * @param isink The ISINK to use for the LED.
1422 * @param pdata Configuration for the LED.
1423 *
1424 * The WM8350 supports the use of an ISINK together with a DCDC to
1425 * provide a power-efficient LED driver. This function registers the
1426 * regulators and instantiates the platform device for a LED. The
1427 * operating modes for the LED regulators must be configured using
1428 * wm8350_isink_set_flash(), wm8350_dcdc25_set_mode() and
1429 * wm8350_dcdc_set_slot() prior to calling this function.
1430 */
1431int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink,
1432 struct wm8350_led_platform_data *pdata)
1433{
1434 struct wm8350_led *led;
1435 struct platform_device *pdev;
1436 int ret;
1437
1438 if (lednum > ARRAY_SIZE(wm8350->pmic.led) || lednum < 0) {
1439 dev_err(wm8350->dev, "Invalid LED index %d\n", lednum);
1440 return -ENODEV;
1441 }
1442
1443 led = &wm8350->pmic.led[lednum];
1444
1445 if (led->pdev) {
1446 dev_err(wm8350->dev, "LED %d already allocated\n", lednum);
1447 return -EINVAL;
1448 }
1449
1450 pdev = platform_device_alloc("wm8350-led", lednum);
1451 if (pdev == NULL) {
1452 dev_err(wm8350->dev, "Failed to allocate LED %d\n", lednum);
1453 return -ENOMEM;
1454 }
1455
1456 led->isink_consumer.dev = &pdev->dev;
1457 led->isink_consumer.supply = "led_isink";
1458 led->isink_init.num_consumer_supplies = 1;
1459 led->isink_init.consumer_supplies = &led->isink_consumer;
1460 led->isink_init.constraints.min_uA = 0;
1461 led->isink_init.constraints.max_uA = pdata->max_uA;
1462 led->isink_init.constraints.valid_ops_mask = REGULATOR_CHANGE_CURRENT;
1463 led->isink_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL;
1464 ret = wm8350_register_regulator(wm8350, isink, &led->isink_init);
1465 if (ret != 0) {
1466 platform_device_put(pdev);
1467 return ret;
1468 }
1469
1470 led->dcdc_consumer.dev = &pdev->dev;
1471 led->dcdc_consumer.supply = "led_vcc";
1472 led->dcdc_init.num_consumer_supplies = 1;
1473 led->dcdc_init.consumer_supplies = &led->dcdc_consumer;
1474 led->dcdc_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL;
1475 ret = wm8350_register_regulator(wm8350, dcdc, &led->dcdc_init);
1476 if (ret != 0) {
1477 platform_device_put(pdev);
1478 return ret;
1479 }
1480
1481 switch (isink) {
1482 case WM8350_ISINK_A:
1483 wm8350->pmic.isink_A_dcdc = dcdc;
1484 break;
1485 case WM8350_ISINK_B:
1486 wm8350->pmic.isink_B_dcdc = dcdc;
1487 break;
1488 }
1489
1490 pdev->dev.platform_data = pdata;
1491 pdev->dev.parent = wm8350->dev;
1492 ret = platform_device_add(pdev);
1493 if (ret != 0) {
1494 dev_err(wm8350->dev, "Failed to register LED %d: %d\n",
1495 lednum, ret);
1496 platform_device_put(pdev);
1497 return ret;
1498 }
1499
1500 led->pdev = pdev;
1501
1502 return 0;
1503}
1504EXPORT_SYMBOL_GPL(wm8350_register_led);
1505
1415static struct platform_driver wm8350_regulator_driver = { 1506static struct platform_driver wm8350_regulator_driver = {
1416 .probe = wm8350_regulator_probe, 1507 .probe = wm8350_regulator_probe,
1417 .remove = wm8350_regulator_remove, 1508 .remove = wm8350_regulator_remove,
diff --git a/include/linux/leds-pca9532.h b/include/linux/leds-pca9532.h
index 81b4207deb95..96eea90f01a8 100644
--- a/include/linux/leds-pca9532.h
+++ b/include/linux/leds-pca9532.h
@@ -15,6 +15,7 @@
15#define __LINUX_PCA9532_H 15#define __LINUX_PCA9532_H
16 16
17#include <linux/leds.h> 17#include <linux/leds.h>
18#include <linux/workqueue.h>
18 19
19enum pca9532_state { 20enum pca9532_state {
20 PCA9532_OFF = 0x0, 21 PCA9532_OFF = 0x0,
@@ -31,6 +32,7 @@ struct pca9532_led {
31 struct i2c_client *client; 32 struct i2c_client *client;
32 char *name; 33 char *name;
33 struct led_classdev ldev; 34 struct led_classdev ldev;
35 struct work_struct work;
34 enum pca9532_type type; 36 enum pca9532_type type;
35 enum pca9532_state state; 37 enum pca9532_state state;
36}; 38};
diff --git a/include/linux/leds.h b/include/linux/leds.h
index d3a73f5a48c3..24489da701e3 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -32,7 +32,10 @@ struct led_classdev {
32 int brightness; 32 int brightness;
33 int flags; 33 int flags;
34 34
35 /* Lower 16 bits reflect status */
35#define LED_SUSPENDED (1 << 0) 36#define LED_SUSPENDED (1 << 0)
37 /* Upper 16 bits reflect control information */
38#define LED_CORE_SUSPENDRESUME (1 << 16)
36 39
37 /* Set LED brightness level */ 40 /* Set LED brightness level */
38 /* Must not sleep, use a workqueue if needed */ 41 /* Must not sleep, use a workqueue if needed */
@@ -62,7 +65,7 @@ struct led_classdev {
62 65
63extern int led_classdev_register(struct device *parent, 66extern int led_classdev_register(struct device *parent,
64 struct led_classdev *led_cdev); 67 struct led_classdev *led_cdev);
65extern void led_classdev_unregister(struct led_classdev *lcd); 68extern void led_classdev_unregister(struct led_classdev *led_cdev);
66extern void led_classdev_suspend(struct led_classdev *led_cdev); 69extern void led_classdev_suspend(struct led_classdev *led_cdev);
67extern void led_classdev_resume(struct led_classdev *led_cdev); 70extern void led_classdev_resume(struct led_classdev *led_cdev);
68 71
diff --git a/include/linux/mfd/wm8350/pmic.h b/include/linux/mfd/wm8350/pmic.h
index 96acbfc8aa12..be3264e286e0 100644
--- a/include/linux/mfd/wm8350/pmic.h
+++ b/include/linux/mfd/wm8350/pmic.h
@@ -13,6 +13,10 @@
13#ifndef __LINUX_MFD_WM8350_PMIC_H 13#ifndef __LINUX_MFD_WM8350_PMIC_H
14#define __LINUX_MFD_WM8350_PMIC_H 14#define __LINUX_MFD_WM8350_PMIC_H
15 15
16#include <linux/platform_device.h>
17#include <linux/leds.h>
18#include <linux/regulator/machine.h>
19
16/* 20/*
17 * Register values. 21 * Register values.
18 */ 22 */
@@ -700,6 +704,33 @@ struct wm8350;
700struct platform_device; 704struct platform_device;
701struct regulator_init_data; 705struct regulator_init_data;
702 706
707/*
708 * WM8350 LED platform data
709 */
710struct wm8350_led_platform_data {
711 const char *name;
712 const char *default_trigger;
713 int max_uA;
714};
715
716struct wm8350_led {
717 struct platform_device *pdev;
718 struct mutex mutex;
719 struct work_struct work;
720 spinlock_t value_lock;
721 enum led_brightness value;
722 struct led_classdev cdev;
723 int max_uA_index;
724 int enabled;
725
726 struct regulator *isink;
727 struct regulator_consumer_supply isink_consumer;
728 struct regulator_init_data isink_init;
729 struct regulator *dcdc;
730 struct regulator_consumer_supply dcdc_consumer;
731 struct regulator_init_data dcdc_init;
732};
733
703struct wm8350_pmic { 734struct wm8350_pmic {
704 /* Number of regulators of each type on this device */ 735 /* Number of regulators of each type on this device */
705 int max_dcdc; 736 int max_dcdc;
@@ -717,10 +748,15 @@ struct wm8350_pmic {
717 748
718 /* regulator devices */ 749 /* regulator devices */
719 struct platform_device *pdev[NUM_WM8350_REGULATORS]; 750 struct platform_device *pdev[NUM_WM8350_REGULATORS];
751
752 /* LED devices */
753 struct wm8350_led led[2];
720}; 754};
721 755
722int wm8350_register_regulator(struct wm8350 *wm8350, int reg, 756int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
723 struct regulator_init_data *initdata); 757 struct regulator_init_data *initdata);
758int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink,
759 struct wm8350_led_platform_data *pdata);
724 760
725/* 761/*
726 * Additional DCDC control not supported via regulator API 762 * Additional DCDC control not supported via regulator API