leds-cpld.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /* 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. #include <linux/module.h>
  18. #include <linux/init.h>
  19. #include <linux/slab.h>
  20. #include <linux/device.h>
  21. #include <linux/leds.h>
  22. #include <linux/spinlock.h>
  23. #include <linux/ctype.h>
  24. #include <linux/platform_device.h>
  25. #include <linux/io.h>
  26. #include <asm/mach-types.h>
  27. #define DEBUG_LED_CHANGE 0
  28. static int _g_cpld_led_addr;
  29. struct CPLD_LED_data {
  30. spinlock_t data_lock;
  31. struct led_classdev leds[4]; /* blue, green, red */
  32. };
  33. static ssize_t led_blink_solid_show(struct device *dev,
  34. struct device_attribute *attr, char *buf)
  35. {
  36. struct CPLD_LED_data *CPLD_LED;
  37. int idx = 2;
  38. uint8_t reg_val;
  39. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  40. ssize_t ret = 0;
  41. if (!strcmp(led_cdev->name, "red"))
  42. idx = 0;
  43. else if (!strcmp(led_cdev->name, "green"))
  44. idx = 1;
  45. else
  46. idx = 2;
  47. CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
  48. spin_lock(&CPLD_LED->data_lock);
  49. reg_val = readb(_g_cpld_led_addr);
  50. reg_val = reg_val >> (2 * idx + 1);
  51. reg_val &= 0x1;
  52. spin_unlock(&CPLD_LED->data_lock);
  53. /* no lock needed for this */
  54. sprintf(buf, "%u\n", reg_val);
  55. ret = strlen(buf) + 1;
  56. return ret;
  57. }
  58. static ssize_t led_blink_solid_store(struct device *dev,
  59. struct device_attribute *attr,
  60. const char *buf, size_t size)
  61. {
  62. struct CPLD_LED_data *CPLD_LED;
  63. int idx = 2;
  64. uint8_t reg_val;
  65. char *after;
  66. unsigned long state;
  67. ssize_t ret = -EINVAL;
  68. size_t count;
  69. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  70. if (!strcmp(led_cdev->name, "red"))
  71. idx = 0;
  72. else if (!strcmp(led_cdev->name, "green"))
  73. idx = 1;
  74. else
  75. idx = 2;
  76. CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
  77. state = simple_strtoul(buf, &after, 10);
  78. count = after - buf;
  79. if (*after && isspace(*after))
  80. count++;
  81. if (count == size) {
  82. ret = count;
  83. spin_lock(&CPLD_LED->data_lock);
  84. reg_val = readb(_g_cpld_led_addr);
  85. if (state)
  86. reg_val |= 1 << (2 * idx + 1);
  87. else
  88. reg_val &= ~(1 << (2 * idx + 1));
  89. writeb(reg_val, _g_cpld_led_addr);
  90. spin_unlock(&CPLD_LED->data_lock);
  91. }
  92. return ret;
  93. }
  94. static DEVICE_ATTR(blink, 0644, led_blink_solid_show, led_blink_solid_store);
  95. static ssize_t cpldled_blink_all_show(struct device *dev,
  96. struct device_attribute *attr, char *buf)
  97. {
  98. uint8_t reg_val;
  99. struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev);
  100. ssize_t ret = 0;
  101. spin_lock(&CPLD_LED->data_lock);
  102. reg_val = readb(_g_cpld_led_addr);
  103. reg_val &= 0x2A;
  104. if (reg_val == 0x2A)
  105. reg_val = 1;
  106. else
  107. reg_val = 0;
  108. spin_unlock(&CPLD_LED->data_lock);
  109. /* no lock needed for this */
  110. sprintf(buf, "%u\n", reg_val);
  111. ret = strlen(buf) + 1;
  112. return ret;
  113. }
  114. static ssize_t cpldled_blink_all_store(struct device *dev,
  115. struct device_attribute *attr,
  116. const char *buf, size_t size)
  117. {
  118. uint8_t reg_val;
  119. char *after;
  120. unsigned long state;
  121. ssize_t ret = -EINVAL;
  122. size_t count;
  123. struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev);
  124. state = simple_strtoul(buf, &after, 10);
  125. count = after - buf;
  126. if (*after && isspace(*after))
  127. count++;
  128. if (count == size) {
  129. ret = count;
  130. spin_lock(&CPLD_LED->data_lock);
  131. reg_val = readb(_g_cpld_led_addr);
  132. if (state)
  133. reg_val |= 0x2A;
  134. else
  135. reg_val &= ~0x2A;
  136. writeb(reg_val, _g_cpld_led_addr);
  137. spin_unlock(&CPLD_LED->data_lock);
  138. }
  139. return ret;
  140. }
  141. static struct device_attribute dev_attr_blink_all = {
  142. .attr = {
  143. .name = "blink",
  144. .mode = 0644,
  145. },
  146. .show = cpldled_blink_all_show,
  147. .store = cpldled_blink_all_store,
  148. };
  149. static void led_brightness_set(struct led_classdev *led_cdev,
  150. enum led_brightness brightness)
  151. {
  152. struct CPLD_LED_data *CPLD_LED;
  153. int idx = 2;
  154. struct led_classdev *led;
  155. uint8_t reg_val;
  156. if (!strcmp(led_cdev->name, "jogball-backlight")) {
  157. if (brightness > 7)
  158. reg_val = 1;
  159. else
  160. reg_val = brightness;
  161. writeb(0, _g_cpld_led_addr + 0x8);
  162. writeb(reg_val, _g_cpld_led_addr + 0x8);
  163. #if DEBUG_LED_CHANGE
  164. printk(KERN_INFO "LED change: jogball backlight = %d \n",
  165. reg_val);
  166. #endif
  167. return;
  168. } else if (!strcmp(led_cdev->name, "red")) {
  169. idx = 0;
  170. } else if (!strcmp(led_cdev->name, "green")) {
  171. idx = 1;
  172. } else {
  173. idx = 2;
  174. }
  175. CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
  176. spin_lock(&CPLD_LED->data_lock);
  177. reg_val = readb(_g_cpld_led_addr);
  178. led = &CPLD_LED->leds[idx];
  179. if (led->brightness > LED_OFF)
  180. reg_val |= 1 << (2 * idx);
  181. else
  182. reg_val &= ~(1 << (2 * idx));
  183. writeb(reg_val, _g_cpld_led_addr);
  184. #if DEBUG_LED_CHANGE
  185. printk(KERN_INFO "LED change: %s = %d \n", led_cdev->name, led->brightness);
  186. #endif
  187. spin_unlock(&CPLD_LED->data_lock);
  188. }
  189. static ssize_t cpldled_grpfreq_show(struct device *dev,
  190. struct device_attribute *attr, char *buf)
  191. {
  192. return sprintf(buf, "%u\n", 0);
  193. }
  194. static ssize_t cpldled_grpfreq_store(struct device *dev,
  195. struct device_attribute *attr,
  196. const char *buf, size_t count)
  197. {
  198. return 0;
  199. }
  200. static DEVICE_ATTR(grpfreq, 0644, cpldled_grpfreq_show, cpldled_grpfreq_store);
  201. static ssize_t cpldled_grppwm_show(struct device *dev,
  202. struct device_attribute *attr, char *buf)
  203. {
  204. return sprintf(buf, "%u\n", 0);
  205. }
  206. static ssize_t cpldled_grppwm_store(struct device *dev,
  207. struct device_attribute *attr,
  208. const char *buf, size_t count)
  209. {
  210. return 0;
  211. }
  212. static DEVICE_ATTR(grppwm, 0644, cpldled_grppwm_show, cpldled_grppwm_store);
  213. static int CPLD_LED_probe(struct platform_device *pdev)
  214. {
  215. int ret = 0;
  216. int i, j;
  217. struct resource *res;
  218. struct CPLD_LED_data *CPLD_LED;
  219. CPLD_LED = kzalloc(sizeof(struct CPLD_LED_data), GFP_KERNEL);
  220. if (CPLD_LED == NULL) {
  221. printk(KERN_ERR "CPLD_LED_probe: no memory for device\n");
  222. ret = -ENOMEM;
  223. goto err_alloc_failed;
  224. }
  225. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  226. if (!res) {
  227. ret = -ENOMEM;
  228. goto err_alloc_failed;
  229. }
  230. _g_cpld_led_addr = res->start;
  231. if (!_g_cpld_led_addr) {
  232. ret = -ENOMEM;
  233. goto err_alloc_failed;
  234. }
  235. memset(CPLD_LED, 0, sizeof(struct CPLD_LED_data));
  236. writeb(0x00, _g_cpld_led_addr);
  237. CPLD_LED->leds[0].name = "red";
  238. CPLD_LED->leds[0].brightness_set = led_brightness_set;
  239. CPLD_LED->leds[1].name = "green";
  240. CPLD_LED->leds[1].brightness_set = led_brightness_set;
  241. CPLD_LED->leds[2].name = "blue";
  242. CPLD_LED->leds[2].brightness_set = led_brightness_set;
  243. CPLD_LED->leds[3].name = "jogball-backlight";
  244. CPLD_LED->leds[3].brightness_set = led_brightness_set;
  245. spin_lock_init(&CPLD_LED->data_lock);
  246. for (i = 0; i < 4; i++) { /* red, green, blue jogball */
  247. ret = led_classdev_register(&pdev->dev, &CPLD_LED->leds[i]);
  248. if (ret) {
  249. printk(KERN_ERR
  250. "CPLD_LED: led_classdev_register failed\n");
  251. goto err_led_classdev_register_failed;
  252. }
  253. }
  254. for (i = 0; i < 3; i++) {
  255. ret =
  256. device_create_file(CPLD_LED->leds[i].dev, &dev_attr_blink);
  257. if (ret) {
  258. printk(KERN_ERR
  259. "CPLD_LED: device_create_file failed\n");
  260. goto err_out_attr_blink;
  261. }
  262. }
  263. dev_set_drvdata(&pdev->dev, CPLD_LED);
  264. ret = device_create_file(&pdev->dev, &dev_attr_blink_all);
  265. if (ret) {
  266. printk(KERN_ERR
  267. "CPLD_LED: create dev_attr_blink_all failed\n");
  268. goto err_out_attr_blink;
  269. }
  270. ret = device_create_file(&pdev->dev, &dev_attr_grppwm);
  271. if (ret) {
  272. printk(KERN_ERR
  273. "CPLD_LED: create dev_attr_grppwm failed\n");
  274. goto err_out_attr_grppwm;
  275. }
  276. ret = device_create_file(&pdev->dev, &dev_attr_grpfreq);
  277. if (ret) {
  278. printk(KERN_ERR
  279. "CPLD_LED: create dev_attr_grpfreq failed\n");
  280. goto err_out_attr_grpfreq;
  281. }
  282. return 0;
  283. err_out_attr_grpfreq:
  284. device_remove_file(&pdev->dev, &dev_attr_grppwm);
  285. err_out_attr_grppwm:
  286. device_remove_file(&pdev->dev, &dev_attr_blink_all);
  287. err_out_attr_blink:
  288. for (j = 0; j < i; j++)
  289. device_remove_file(CPLD_LED->leds[j].dev, &dev_attr_blink);
  290. i = 3;
  291. err_led_classdev_register_failed:
  292. for (j = 0; j < i; j++)
  293. led_classdev_unregister(&CPLD_LED->leds[j]);
  294. err_alloc_failed:
  295. kfree(CPLD_LED);
  296. return ret;
  297. }
  298. static int __devexit CPLD_LED_remove(struct platform_device *pdev)
  299. {
  300. struct CPLD_LED_data *CPLD_LED;
  301. int i;
  302. CPLD_LED = platform_get_drvdata(pdev);
  303. for (i = 0; i < 3; i++) {
  304. device_remove_file(CPLD_LED->leds[i].dev, &dev_attr_blink);
  305. led_classdev_unregister(&CPLD_LED->leds[i]);
  306. }
  307. device_remove_file(&pdev->dev, &dev_attr_blink_all);
  308. device_remove_file(&pdev->dev, &dev_attr_grppwm);
  309. device_remove_file(&pdev->dev, &dev_attr_grpfreq);
  310. kfree(CPLD_LED);
  311. return 0;
  312. }
  313. static struct platform_driver CPLD_LED_driver = {
  314. .probe = CPLD_LED_probe,
  315. .remove = __devexit_p(CPLD_LED_remove),
  316. .driver = {
  317. .name = "leds-cpld",
  318. .owner = THIS_MODULE,
  319. },
  320. };
  321. static int __init CPLD_LED_init(void)
  322. {
  323. return platform_driver_register(&CPLD_LED_driver);
  324. }
  325. static void __exit CPLD_LED_exit(void)
  326. {
  327. platform_driver_unregister(&CPLD_LED_driver);
  328. }
  329. MODULE_AUTHOR("Farmer Tseng");
  330. MODULE_DESCRIPTION("CPLD_LED driver");
  331. MODULE_LICENSE("GPL");
  332. module_init(CPLD_LED_init);
  333. module_exit(CPLD_LED_exit);