blob: eab004c3d75c5fb88580e193070bdc81f3fcaabc [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* include/asm/mach-msm/leds-cpld.c
2 *
3 * Copyright (C) 2008 HTC Corporation.
4 *
5 * Author: Farmer Tseng
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/module.h>
19#include <linux/init.h>
20#include <linux/slab.h>
21#include <linux/device.h>
22#include <linux/leds.h>
23#include <linux/spinlock.h>
24#include <linux/ctype.h>
25#include <linux/platform_device.h>
26#include <linux/io.h>
27#include <asm/mach-types.h>
28
29#define DEBUG_LED_CHANGE 0
30
31static int _g_cpld_led_addr;
32
33struct CPLD_LED_data {
34 spinlock_t data_lock;
35 struct led_classdev leds[4]; /* blue, green, red */
36};
37
38static ssize_t led_blink_solid_show(struct device *dev,
39 struct device_attribute *attr, char *buf)
40{
41 struct CPLD_LED_data *CPLD_LED;
42 int idx = 2;
43 uint8_t reg_val;
44 struct led_classdev *led_cdev = dev_get_drvdata(dev);
45 ssize_t ret = 0;
46
47 if (!strcmp(led_cdev->name, "red"))
48 idx = 0;
49 else if (!strcmp(led_cdev->name, "green"))
50 idx = 1;
51 else
52 idx = 2;
53
54 CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
55
56 spin_lock(&CPLD_LED->data_lock);
57 reg_val = readb(_g_cpld_led_addr);
58 reg_val = reg_val >> (2 * idx + 1);
59 reg_val &= 0x1;
60 spin_unlock(&CPLD_LED->data_lock);
61
62 /* no lock needed for this */
63 sprintf(buf, "%u\n", reg_val);
64 ret = strlen(buf) + 1;
65
66 return ret;
67}
68
69static ssize_t led_blink_solid_store(struct device *dev,
70 struct device_attribute *attr,
71 const char *buf, size_t size)
72{
73 struct CPLD_LED_data *CPLD_LED;
74 int idx = 2;
75 uint8_t reg_val;
76 char *after;
77 unsigned long state;
78 ssize_t ret = -EINVAL;
79 size_t count;
80
81 struct led_classdev *led_cdev = dev_get_drvdata(dev);
82
83 if (!strcmp(led_cdev->name, "red"))
84 idx = 0;
85 else if (!strcmp(led_cdev->name, "green"))
86 idx = 1;
87 else
88 idx = 2;
89
90 CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
91
92 state = simple_strtoul(buf, &after, 10);
93
94 count = after - buf;
95
96 if (*after && isspace(*after))
97 count++;
98
99 if (count == size) {
100 ret = count;
101 spin_lock(&CPLD_LED->data_lock);
102 reg_val = readb(_g_cpld_led_addr);
103 if (state)
104 reg_val |= 1 << (2 * idx + 1);
105 else
106 reg_val &= ~(1 << (2 * idx + 1));
107
108 writeb(reg_val, _g_cpld_led_addr);
109 spin_unlock(&CPLD_LED->data_lock);
110 }
111
112 return ret;
113}
114
115static DEVICE_ATTR(blink, 0644, led_blink_solid_show, led_blink_solid_store);
116
117static ssize_t cpldled_blink_all_show(struct device *dev,
118 struct device_attribute *attr, char *buf)
119{
120 uint8_t reg_val;
121 struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev);
122 ssize_t ret = 0;
123
124 spin_lock(&CPLD_LED->data_lock);
125 reg_val = readb(_g_cpld_led_addr);
126 reg_val &= 0x2A;
127 if (reg_val == 0x2A)
128 reg_val = 1;
129 else
130 reg_val = 0;
131 spin_unlock(&CPLD_LED->data_lock);
132
133 /* no lock needed for this */
134 sprintf(buf, "%u\n", reg_val);
135 ret = strlen(buf) + 1;
136
137 return ret;
138}
139
140static ssize_t cpldled_blink_all_store(struct device *dev,
141 struct device_attribute *attr,
142 const char *buf, size_t size)
143{
144 uint8_t reg_val;
145 char *after;
146 unsigned long state;
147 ssize_t ret = -EINVAL;
148 size_t count;
149 struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev);
150
151 state = simple_strtoul(buf, &after, 10);
152
153 count = after - buf;
154
155 if (*after && isspace(*after))
156 count++;
157
158 if (count == size) {
159 ret = count;
160 spin_lock(&CPLD_LED->data_lock);
161 reg_val = readb(_g_cpld_led_addr);
162 if (state)
163 reg_val |= 0x2A;
164 else
165 reg_val &= ~0x2A;
166
167 writeb(reg_val, _g_cpld_led_addr);
168 spin_unlock(&CPLD_LED->data_lock);
169 }
170
171 return ret;
172}
173
174static struct device_attribute dev_attr_blink_all = {
175 .attr = {
176 .name = "blink",
177 .mode = 0644,
178 },
179 .show = cpldled_blink_all_show,
180 .store = cpldled_blink_all_store,
181};
182
183static void led_brightness_set(struct led_classdev *led_cdev,
184 enum led_brightness brightness)
185{
186 struct CPLD_LED_data *CPLD_LED;
187 int idx = 2;
188 struct led_classdev *led;
189 uint8_t reg_val;
190
191 if (!strcmp(led_cdev->name, "jogball-backlight")) {
192 if (brightness > 7)
193 reg_val = 1;
194 else
195 reg_val = brightness;
196 writeb(0, _g_cpld_led_addr + 0x8);
197 writeb(reg_val, _g_cpld_led_addr + 0x8);
198#if DEBUG_LED_CHANGE
199 printk(KERN_INFO "LED change: jogball backlight = %d \n",
200 reg_val);
201#endif
202 return;
203 } else if (!strcmp(led_cdev->name, "red")) {
204 idx = 0;
205 } else if (!strcmp(led_cdev->name, "green")) {
206 idx = 1;
207 } else {
208 idx = 2;
209 }
210
211 CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
212 spin_lock(&CPLD_LED->data_lock);
213 reg_val = readb(_g_cpld_led_addr);
214 led = &CPLD_LED->leds[idx];
215
216 if (led->brightness > LED_OFF)
217 reg_val |= 1 << (2 * idx);
218 else
219 reg_val &= ~(1 << (2 * idx));
220
221 writeb(reg_val, _g_cpld_led_addr);
222#if DEBUG_LED_CHANGE
223 printk(KERN_INFO "LED change: %s = %d \n", led_cdev->name, led->brightness);
224#endif
225 spin_unlock(&CPLD_LED->data_lock);
226}
227
228static ssize_t cpldled_grpfreq_show(struct device *dev,
229 struct device_attribute *attr, char *buf)
230{
231 return sprintf(buf, "%u\n", 0);
232}
233
234static ssize_t cpldled_grpfreq_store(struct device *dev,
235 struct device_attribute *attr,
236 const char *buf, size_t count)
237{
238 return 0;
239}
240
241static DEVICE_ATTR(grpfreq, 0644, cpldled_grpfreq_show, cpldled_grpfreq_store);
242
243static ssize_t cpldled_grppwm_show(struct device *dev,
244 struct device_attribute *attr, char *buf)
245{
246 return sprintf(buf, "%u\n", 0);
247}
248
249static ssize_t cpldled_grppwm_store(struct device *dev,
250 struct device_attribute *attr,
251 const char *buf, size_t count)
252{
253 return 0;
254}
255
256static DEVICE_ATTR(grppwm, 0644, cpldled_grppwm_show, cpldled_grppwm_store);
257
258static int CPLD_LED_probe(struct platform_device *pdev)
259{
260 int ret = 0;
261 int i, j;
262 struct resource *res;
263 struct CPLD_LED_data *CPLD_LED;
264
265 CPLD_LED = kzalloc(sizeof(struct CPLD_LED_data), GFP_KERNEL);
266 if (CPLD_LED == NULL) {
267 printk(KERN_ERR "CPLD_LED_probe: no memory for device\n");
268 ret = -ENOMEM;
269 goto err_alloc_failed;
270 }
271
272 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
273 if (!res) {
274 ret = -ENOMEM;
275 goto err_alloc_failed;
276 }
277
278 _g_cpld_led_addr = res->start;
279 if (!_g_cpld_led_addr) {
280 ret = -ENOMEM;
281 goto err_alloc_failed;
282 }
283
284 memset(CPLD_LED, 0, sizeof(struct CPLD_LED_data));
285 writeb(0x00, _g_cpld_led_addr);
286
287 CPLD_LED->leds[0].name = "red";
288 CPLD_LED->leds[0].brightness_set = led_brightness_set;
289
290 CPLD_LED->leds[1].name = "green";
291 CPLD_LED->leds[1].brightness_set = led_brightness_set;
292
293 CPLD_LED->leds[2].name = "blue";
294 CPLD_LED->leds[2].brightness_set = led_brightness_set;
295
296 CPLD_LED->leds[3].name = "jogball-backlight";
297 CPLD_LED->leds[3].brightness_set = led_brightness_set;
298
299 spin_lock_init(&CPLD_LED->data_lock);
300
301 for (i = 0; i < 4; i++) { /* red, green, blue jogball */
302 ret = led_classdev_register(&pdev->dev, &CPLD_LED->leds[i]);
303 if (ret) {
304 printk(KERN_ERR
305 "CPLD_LED: led_classdev_register failed\n");
306 goto err_led_classdev_register_failed;
307 }
308 }
309
310 for (i = 0; i < 3; i++) {
311 ret =
312 device_create_file(CPLD_LED->leds[i].dev, &dev_attr_blink);
313 if (ret) {
314 printk(KERN_ERR
315 "CPLD_LED: device_create_file failed\n");
316 goto err_out_attr_blink;
317 }
318 }
319
320 dev_set_drvdata(&pdev->dev, CPLD_LED);
321 ret = device_create_file(&pdev->dev, &dev_attr_blink_all);
322 if (ret) {
323 printk(KERN_ERR
324 "CPLD_LED: create dev_attr_blink_all failed\n");
325 goto err_out_attr_blink;
326 }
327 ret = device_create_file(&pdev->dev, &dev_attr_grppwm);
328 if (ret) {
329 printk(KERN_ERR
330 "CPLD_LED: create dev_attr_grppwm failed\n");
331 goto err_out_attr_grppwm;
332 }
333 ret = device_create_file(&pdev->dev, &dev_attr_grpfreq);
334 if (ret) {
335 printk(KERN_ERR
336 "CPLD_LED: create dev_attr_grpfreq failed\n");
337 goto err_out_attr_grpfreq;
338 }
339
340 return 0;
341
342err_out_attr_grpfreq:
343 device_remove_file(&pdev->dev, &dev_attr_grppwm);
344err_out_attr_grppwm:
345 device_remove_file(&pdev->dev, &dev_attr_blink_all);
346err_out_attr_blink:
347 for (j = 0; j < i; j++)
348 device_remove_file(CPLD_LED->leds[j].dev, &dev_attr_blink);
349 i = 3;
350
351err_led_classdev_register_failed:
352 for (j = 0; j < i; j++)
353 led_classdev_unregister(&CPLD_LED->leds[j]);
354
355err_alloc_failed:
356 kfree(CPLD_LED);
357
358 return ret;
359}
360
361static int __devexit CPLD_LED_remove(struct platform_device *pdev)
362{
363 struct CPLD_LED_data *CPLD_LED;
364 int i;
365
366 CPLD_LED = platform_get_drvdata(pdev);
367
368 for (i = 0; i < 3; i++) {
369 device_remove_file(CPLD_LED->leds[i].dev, &dev_attr_blink);
370 led_classdev_unregister(&CPLD_LED->leds[i]);
371 }
372
373 device_remove_file(&pdev->dev, &dev_attr_blink_all);
374 device_remove_file(&pdev->dev, &dev_attr_grppwm);
375 device_remove_file(&pdev->dev, &dev_attr_grpfreq);
376
377 kfree(CPLD_LED);
378 return 0;
379}
380
381static struct platform_driver CPLD_LED_driver = {
382 .probe = CPLD_LED_probe,
383 .remove = __devexit_p(CPLD_LED_remove),
384 .driver = {
385 .name = "leds-cpld",
386 .owner = THIS_MODULE,
387 },
388};
389
390static int __init CPLD_LED_init(void)
391{
392 return platform_driver_register(&CPLD_LED_driver);
393}
394
395static void __exit CPLD_LED_exit(void)
396{
397 platform_driver_unregister(&CPLD_LED_driver);
398}
399
400MODULE_AUTHOR("Farmer Tseng");
401MODULE_DESCRIPTION("CPLD_LED driver");
402MODULE_LICENSE("GPL");
403
404module_init(CPLD_LED_init);
405module_exit(CPLD_LED_exit);