1/* 2 * LED driver for NU801 3 * 4 * Kevin Paul Herbert 5 * Copyright (c) 2012, Meraki, Inc. 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 */ 12 13#include <linux/module.h> 14#include <linux/kernel.h> 15#include <linux/init.h> 16#include <linux/slab.h> 17#include <linux/platform_device.h> 18#include <linux/leds.h> 19#include <linux/workqueue.h> 20#include <linux/delay.h> 21#include <linux/leds-nu801.h> 22 23#include <linux/gpio.h> 24#include <linux/of_gpio.h> 25 26#define MAX_NAME_LENGTH 24 27#define NUM_COLORS 3 28 29static const char * const led_nu801_colors[] = { "blue", "green", "red" }; 30 31struct led_nu801_led_data { 32 struct led_classdev cdev; 33 struct led_nu801_data *controller; 34 enum led_brightness level; 35 char name[MAX_NAME_LENGTH]; 36}; 37 38struct led_nu801_data { 39 unsigned cki; 40 unsigned sdi; 41 int lei; 42 struct delayed_work work; 43 struct led_nu801_led_data *led_chain; 44 int num_leds; 45 const char *device_name; 46 const char *name; 47 u32 ndelay; 48 atomic_t pending; 49}; 50 51static void led_nu801_work(struct work_struct *work) 52{ 53 struct led_nu801_data *controller = 54 container_of(work, struct led_nu801_data, work.work); 55 struct led_nu801_led_data *led; 56 u16 bit; 57 u16 brightness; 58 int index; 59 60 for (index = 0; index < controller->num_leds; index++) { 61 led = &controller->led_chain[index]; 62 brightness = led->level << 8; /* To do: gamma correction */ 63 for (bit = 0x8000; bit; bit = bit >> 1) { 64 gpio_set_value(controller->sdi, 65 (brightness & bit) != 0); 66 gpio_set_value(controller->cki, 1); 67 if (unlikely(((index == (controller->num_leds - 1)) && 68 (bit == 1) && 69 (controller->lei < 0)))) { 70 udelay(600); 71 } else { 72 ndelay(controller->ndelay); 73 } 74 gpio_set_value(controller->cki, 0); 75 ndelay(controller->ndelay); 76 } 77 } 78 if (controller->lei >= 0) { 79 gpio_set_value(controller->lei, 1); 80 ndelay(controller->ndelay); 81 gpio_set_value(controller->lei, 0); 82 } 83 atomic_set(&controller->pending, 1); 84} 85 86static void led_nu801_set(struct led_classdev *led_cdev, 87 enum led_brightness value) 88{ 89 struct led_nu801_led_data *led_dat = 90 container_of(led_cdev, struct led_nu801_led_data, cdev); 91 struct led_nu801_data *controller = led_dat->controller; 92 93 if (led_dat->level != value) { 94 led_dat->level = value; 95 if (atomic_dec_and_test(&controller->pending)) 96 schedule_delayed_work(&led_dat->controller->work, 97 (HZ/1000) + 1); 98 } 99} 100 101static int __init led_nu801_create(struct led_nu801_data *controller, 102 struct device *parent, 103 int index, 104 enum led_brightness brightness, 105#ifdef CONFIG_LEDS_TRIGGERS 106 const char *default_trigger, 107#endif 108 const char *color) 109{ 110 struct led_nu801_led_data *led = &controller->led_chain[index]; 111 int ret; 112 113 scnprintf(led->name, sizeof(led->name), "%s:%s:%s%d", 114 controller->device_name, color, controller->name, 115 (controller->num_leds - (index + 1)) / NUM_COLORS); 116 led->cdev.name = led->name; 117 led->cdev.brightness_set = led_nu801_set; 118#ifdef CONFIG_LEDS_TRIGGERS 119 led->cdev.default_trigger = default_trigger; 120#endif 121 led->level = brightness; 122 led->controller = controller; 123 ret = led_classdev_register(parent, &led->cdev); 124 if (ret < 0) 125 goto err; 126 127 return 0; 128 129err: 130 kfree(led); 131 return ret; 132} 133 134static int __init 135led_nu801_create_chain(const struct led_nu801_template *template, 136 struct led_nu801_data *controller, 137 struct device *parent) 138{ 139 int ret; 140 int index; 141 142 controller->cki = template->cki; 143 controller->sdi = template->sdi; 144 controller->lei = template->lei; 145 controller->num_leds = template->num_leds * 3; 146 controller->device_name = template->device_name; 147 controller->name = template->name; 148 controller->ndelay = template->ndelay; 149 atomic_set(&controller->pending, 1); 150 151 controller->led_chain = kzalloc(sizeof(struct led_nu801_led_data) * 152 controller->num_leds, GFP_KERNEL); 153 154 if (!controller->led_chain) 155 return -ENOMEM; 156 157 ret = gpio_request(controller->cki, template->name); 158 if (ret < 0) 159 goto err_free_chain; 160 161 ret = gpio_request(controller->sdi, template->name); 162 if (ret < 0) 163 goto err_ret_cki; 164 165 if (controller->lei >= 0) { 166 ret = gpio_request(controller->lei, template->name); 167 if (ret < 0) 168 goto err_ret_sdi; 169 ret = gpio_direction_output(controller->lei, 0); 170 if (ret < 0) 171 goto err_ret_lei; 172 } 173 174 ret = gpio_direction_output(controller->cki, 0); 175 if (ret < 0) 176 goto err_ret_lei; 177 178 ret = gpio_direction_output(controller->sdi, 0); 179 if (ret < 0) 180 goto err_ret_lei; 181 182 for (index = 0; index < controller->num_leds; index++) { 183 ret = led_nu801_create(controller, parent, index, 184 template->init_brightness 185 [index % NUM_COLORS], 186#ifdef CONFIG_LEDS_TRIGGERS 187 template->default_trigger, 188#endif 189 template->led_colors[index % NUM_COLORS] ? 190 template->led_colors[index % NUM_COLORS] : 191 led_nu801_colors[index % NUM_COLORS]); 192 if (ret < 0) 193 goto err_ret_sdi; 194 } 195 196 INIT_DELAYED_WORK(&controller->work, led_nu801_work); 197 schedule_delayed_work(&controller->work, 0); 198 199 return 0; 200 201err_ret_lei: 202 if (controller->lei >= 0) 203 gpio_free(controller->lei); 204err_ret_sdi: 205 gpio_free(controller->sdi); 206err_ret_cki: 207 gpio_free(controller->cki); 208err_free_chain: 209 kfree(controller->led_chain); 210 211 return ret; 212} 213 214static void led_nu801_delete_chain(struct led_nu801_data *controller) 215{ 216 struct led_nu801_led_data *led_chain; 217 struct led_nu801_led_data *led; 218 int index; 219 int num_leds; 220 221 led_chain = controller->led_chain; 222 controller->led_chain = 0; 223 num_leds = controller->num_leds; 224 controller->num_leds = 0; 225 cancel_delayed_work_sync(&controller->work); 226 227 for (index = 0; index < num_leds; index++) { 228 led = &led_chain[index]; 229 led_classdev_unregister(&led->cdev); 230 } 231 232 gpio_free(controller->cki); 233 gpio_free(controller->sdi); 234 if (controller->lei >= 0) 235 gpio_free(controller->lei); 236 237 kfree(led_chain); 238} 239 240static struct led_nu801_data * __init 241leds_nu801_create_of(struct platform_device *pdev) 242{ 243 struct device_node *np = pdev->dev.of_node, *child; 244 struct led_nu801_data *controllers; 245 int count = 0, ret; 246 int i = 0; 247 248 for_each_child_of_node(np, child) 249 count++; 250 if (!count) 251 return NULL; 252 253 controllers = kzalloc(sizeof(struct led_nu801_data) * count, 254 GFP_KERNEL); 255 if (!controllers) 256 return NULL; 257 258 for_each_child_of_node(np, child) { 259 const char *state; 260 struct led_nu801_template template = {}; 261 struct device_node *colors; 262 int jj; 263 264 template.cki = of_get_named_gpio_flags(child, "cki", 0, NULL); 265 template.sdi = of_get_named_gpio_flags(child, "sdi", 0, NULL); 266 if (of_find_property(child, "lei", NULL)) { 267 template.lei = of_get_named_gpio_flags(child, "lei", 268 0, NULL); 269 } else { 270 template.lei = -1; 271 } 272 of_property_read_u32(child, "ndelay", &template.ndelay); 273 of_property_read_u32(child, "num_leds", &template.num_leds); 274 template.name = of_get_property(child, "label", NULL) ? : 275 child->name; 276 template.default_trigger = of_get_property(child, 277 "default-trigger", NULL); 278 279 jj = 0; 280 for_each_child_of_node(child, colors) { 281 template.led_colors[jj] = of_get_property(colors, 282 "label", NULL); 283 state = of_get_property(colors, "state", NULL); 284 if (!strncmp(state, "off", 3)) 285 template.init_brightness[jj] = LED_OFF; 286 else if (!strncmp(state, "half", 4)) 287 template.init_brightness[jj] = LED_HALF; 288 else if (!strncmp(state, "full", 4)) 289 template.init_brightness[jj] = LED_FULL; 290 jj++; 291 } 292 293 ret = led_nu801_create_chain(&template, 294 &controllers[i], 295 &pdev->dev); 296 if (ret < 0) 297 goto err; 298 i++; 299 } 300 301 return controllers; 302 303err: 304 for (i = i - 1; i >= 0; i--) 305 led_nu801_delete_chain(&controllers[i]); 306 kfree(controllers); 307 return NULL; 308} 309 310static int __init led_nu801_probe(struct platform_device *pdev) 311{ 312 struct led_nu801_platform_data *pdata = pdev->dev.platform_data; 313 struct led_nu801_data *controllers; 314 int i, ret = 0; 315 316 if (!(pdata && pdata->num_controllers)) { 317 controllers = leds_nu801_create_of(pdev); 318 if (!controllers) 319 return -ENODEV; 320 } 321 322 controllers = kzalloc(sizeof(struct led_nu801_data) * 323 pdata->num_controllers, GFP_KERNEL); 324 if (!controllers) 325 return -ENOMEM; 326 327 for (i = 0; i < pdata->num_controllers; i++) { 328 ret = led_nu801_create_chain(&pdata->template[i], 329 &controllers[i], 330 &pdev->dev); 331 if (ret < 0) 332 goto err; 333 } 334 335 platform_set_drvdata(pdev, controllers); 336 337 return 0; 338 339err: 340 for (i = i - 1; i >= 0; i--) 341 led_nu801_delete_chain(&controllers[i]); 342 343 kfree(controllers); 344 345 return ret; 346} 347 348static int led_nu801_remove(struct platform_device *pdev) 349{ 350 int i; 351 struct led_nu801_platform_data *pdata = pdev->dev.platform_data; 352 struct led_nu801_data *controllers; 353 354 controllers = platform_get_drvdata(pdev); 355 356 for (i = 0; i < pdata->num_controllers; i++) 357 led_nu801_delete_chain(&controllers[i]); 358 359 kfree(controllers); 360 361 return 0; 362} 363 364static const struct of_device_id of_numen_leds_match[] = { 365 { .compatible = "numen,leds-nu801", }, 366 {}, 367}; 368MODULE_DEVICE_TABLE(of, of_pwm_leds_match); 369 370static struct platform_driver led_nu801_driver = { 371 .probe = led_nu801_probe, 372 .remove = led_nu801_remove, 373 .driver = { 374 .name = "leds-nu801", 375 .owner = THIS_MODULE, 376 .of_match_table = of_numen_leds_match, 377 }, 378}; 379 380static int __init led_nu801_init(void) 381{ 382 return platform_driver_register(&led_nu801_driver); 383} 384 385static void __exit led_nu801_exit(void) 386{ 387 platform_driver_unregister(&led_nu801_driver); 388} 389 390module_init(led_nu801_init); 391module_exit(led_nu801_exit); 392 393MODULE_AUTHOR("Kevin Paul Herbert <kph@meraki.net>"); 394MODULE_DESCRIPTION("NU801 LED driver"); 395MODULE_LICENSE("GPL v2"); 396MODULE_ALIAS("platform:leds-nu801"); 397