1 /* 2 * LEDs driver for Freescale MC13783/MC13892 3 * 4 * Copyright (C) 2010 Philippe Rétornaz 5 * 6 * Based on leds-da903x: 7 * Copyright (C) 2008 Compulab, Ltd. 8 * Mike Rapoport <mike@compulab.co.il> 9 * 10 * Copyright (C) 2006-2008 Marvell International Ltd. 11 * Eric Miao <eric.miao@marvell.com> 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License version 2 as 15 * published by the Free Software Foundation. 16 */ 17 18 #include <linux/module.h> 19 #include <linux/kernel.h> 20 #include <linux/init.h> 21 #include <linux/platform_device.h> 22 #include <linux/leds.h> 23 #include <linux/workqueue.h> 24 #include <linux/mfd/mc13xxx.h> 25 26 #define MC13XXX_REG_LED_CONTROL(x) (51 + (x)) 27 28 struct mc13xxx_led_devtype { 29 int led_min; 30 int led_max; 31 int num_regs; 32 }; 33 34 struct mc13xxx_led { 35 struct led_classdev cdev; 36 struct work_struct work; 37 struct mc13xxx *master; 38 enum led_brightness new_brightness; 39 int id; 40 }; 41 42 struct mc13xxx_leds { 43 struct mc13xxx_led_devtype *devtype; 44 int num_leds; 45 struct mc13xxx_led led[0]; 46 }; 47 48 static void mc13xxx_led_work(struct work_struct *work) 49 { 50 struct mc13xxx_led *led = container_of(work, struct mc13xxx_led, work); 51 int reg, mask, value, bank, off, shift; 52 53 switch (led->id) { 54 case MC13783_LED_MD: 55 reg = MC13XXX_REG_LED_CONTROL(2); 56 shift = 9; 57 mask = 0x0f; 58 value = led->new_brightness >> 4; 59 break; 60 case MC13783_LED_AD: 61 reg = MC13XXX_REG_LED_CONTROL(2); 62 shift = 13; 63 mask = 0x0f; 64 value = led->new_brightness >> 4; 65 break; 66 case MC13783_LED_KP: 67 reg = MC13XXX_REG_LED_CONTROL(2); 68 shift = 17; 69 mask = 0x0f; 70 value = led->new_brightness >> 4; 71 break; 72 case MC13783_LED_R1: 73 case MC13783_LED_G1: 74 case MC13783_LED_B1: 75 case MC13783_LED_R2: 76 case MC13783_LED_G2: 77 case MC13783_LED_B2: 78 case MC13783_LED_R3: 79 case MC13783_LED_G3: 80 case MC13783_LED_B3: 81 off = led->id - MC13783_LED_R1; 82 bank = off / 3; 83 reg = MC13XXX_REG_LED_CONTROL(3) + bank; 84 shift = (off - bank * 3) * 5 + 6; 85 value = led->new_brightness >> 3; 86 mask = 0x1f; 87 break; 88 case MC13892_LED_MD: 89 reg = MC13XXX_REG_LED_CONTROL(0); 90 shift = 3; 91 mask = 0x3f; 92 value = led->new_brightness >> 2; 93 break; 94 case MC13892_LED_AD: 95 reg = MC13XXX_REG_LED_CONTROL(0); 96 shift = 15; 97 mask = 0x3f; 98 value = led->new_brightness >> 2; 99 break; 100 case MC13892_LED_KP: 101 reg = MC13XXX_REG_LED_CONTROL(1); 102 shift = 3; 103 mask = 0x3f; 104 value = led->new_brightness >> 2; 105 break; 106 case MC13892_LED_R: 107 case MC13892_LED_G: 108 case MC13892_LED_B: 109 off = led->id - MC13892_LED_R; 110 bank = off / 2; 111 reg = MC13XXX_REG_LED_CONTROL(2) + bank; 112 shift = (off - bank * 2) * 12 + 3; 113 value = led->new_brightness >> 2; 114 mask = 0x3f; 115 break; 116 default: 117 BUG(); 118 } 119 120 mc13xxx_lock(led->master); 121 mc13xxx_reg_rmw(led->master, reg, mask << shift, value << shift); 122 mc13xxx_unlock(led->master); 123 } 124 125 static void mc13xxx_led_set(struct led_classdev *led_cdev, 126 enum led_brightness value) 127 { 128 struct mc13xxx_led *led = 129 container_of(led_cdev, struct mc13xxx_led, cdev); 130 131 led->new_brightness = value; 132 schedule_work(&led->work); 133 } 134 135 static int __init mc13xxx_led_setup(struct mc13xxx_led *led, int max_current) 136 { 137 int shift, mask, reg, ret, bank; 138 139 switch (led->id) { 140 case MC13783_LED_MD: 141 reg = MC13XXX_REG_LED_CONTROL(2); 142 shift = 0; 143 mask = 0x07; 144 break; 145 case MC13783_LED_AD: 146 reg = MC13XXX_REG_LED_CONTROL(2); 147 shift = 3; 148 mask = 0x07; 149 break; 150 case MC13783_LED_KP: 151 reg = MC13XXX_REG_LED_CONTROL(2); 152 shift = 6; 153 mask = 0x07; 154 break; 155 case MC13783_LED_R1: 156 case MC13783_LED_G1: 157 case MC13783_LED_B1: 158 case MC13783_LED_R2: 159 case MC13783_LED_G2: 160 case MC13783_LED_B2: 161 case MC13783_LED_R3: 162 case MC13783_LED_G3: 163 case MC13783_LED_B3: 164 bank = (led->id - MC13783_LED_R1) / 3; 165 reg = MC13XXX_REG_LED_CONTROL(3) + bank; 166 shift = ((led->id - MC13783_LED_R1) - bank * 3) * 2; 167 mask = 0x03; 168 break; 169 case MC13892_LED_MD: 170 reg = MC13XXX_REG_LED_CONTROL(0); 171 shift = 9; 172 mask = 0x07; 173 break; 174 case MC13892_LED_AD: 175 reg = MC13XXX_REG_LED_CONTROL(0); 176 shift = 21; 177 mask = 0x07; 178 break; 179 case MC13892_LED_KP: 180 reg = MC13XXX_REG_LED_CONTROL(1); 181 shift = 9; 182 mask = 0x07; 183 break; 184 case MC13892_LED_R: 185 case MC13892_LED_G: 186 case MC13892_LED_B: 187 bank = (led->id - MC13892_LED_R) / 2; 188 reg = MC13XXX_REG_LED_CONTROL(2) + bank; 189 shift = ((led->id - MC13892_LED_R) - bank * 2) * 12 + 9; 190 mask = 0x07; 191 break; 192 default: 193 BUG(); 194 } 195 196 mc13xxx_lock(led->master); 197 ret = mc13xxx_reg_rmw(led->master, reg, mask << shift, 198 max_current << shift); 199 mc13xxx_unlock(led->master); 200 201 return ret; 202 } 203 204 static int __init mc13xxx_led_probe(struct platform_device *pdev) 205 { 206 struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); 207 struct mc13xxx *mcdev = dev_get_drvdata(pdev->dev.parent); 208 struct mc13xxx_led_devtype *devtype = 209 (struct mc13xxx_led_devtype *)pdev->id_entry->driver_data; 210 struct mc13xxx_leds *leds; 211 int i, id, num_leds, ret = -ENODATA; 212 u32 reg, init_led = 0; 213 214 if (!pdata) { 215 dev_err(&pdev->dev, "Missing platform data\n"); 216 return -ENODEV; 217 } 218 219 num_leds = pdata->num_leds; 220 221 if ((num_leds < 1) || 222 (num_leds > (devtype->led_max - devtype->led_min + 1))) { 223 dev_err(&pdev->dev, "Invalid LED count %d\n", num_leds); 224 return -EINVAL; 225 } 226 227 leds = devm_kzalloc(&pdev->dev, num_leds * sizeof(struct mc13xxx_led) + 228 sizeof(struct mc13xxx_leds), GFP_KERNEL); 229 if (!leds) 230 return -ENOMEM; 231 232 leds->devtype = devtype; 233 leds->num_leds = num_leds; 234 platform_set_drvdata(pdev, leds); 235 236 mc13xxx_lock(mcdev); 237 for (i = 0; i < devtype->num_regs; i++) { 238 reg = pdata->led_control[i]; 239 WARN_ON(reg >= (1 << 24)); 240 ret = mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), reg); 241 if (ret) 242 break; 243 } 244 mc13xxx_unlock(mcdev); 245 246 if (ret) { 247 dev_err(&pdev->dev, "Unable to init LED driver\n"); 248 return ret; 249 } 250 251 for (i = 0; i < num_leds; i++) { 252 const char *name, *trig; 253 char max_current; 254 255 ret = -EINVAL; 256 257 id = pdata->led[i].id; 258 name = pdata->led[i].name; 259 trig = pdata->led[i].default_trigger; 260 max_current = pdata->led[i].max_current; 261 262 if ((id > devtype->led_max) || (id < devtype->led_min)) { 263 dev_err(&pdev->dev, "Invalid ID %i\n", id); 264 break; 265 } 266 267 if (init_led & (1 << id)) { 268 dev_warn(&pdev->dev, 269 "LED %i already initialized\n", id); 270 break; 271 } 272 273 init_led |= 1 << id; 274 leds->led[i].id = id; 275 leds->led[i].master = mcdev; 276 leds->led[i].cdev.name = name; 277 leds->led[i].cdev.default_trigger = trig; 278 leds->led[i].cdev.brightness_set = mc13xxx_led_set; 279 leds->led[i].cdev.brightness = LED_OFF; 280 281 INIT_WORK(&leds->led[i].work, mc13xxx_led_work); 282 283 ret = mc13xxx_led_setup(&leds->led[i], max_current); 284 if (ret) { 285 dev_err(&pdev->dev, "Unable to setup LED %i\n", id); 286 break; 287 } 288 ret = led_classdev_register(pdev->dev.parent, 289 &leds->led[i].cdev); 290 if (ret) { 291 dev_err(&pdev->dev, "Failed to register LED %i\n", id); 292 break; 293 } 294 } 295 296 if (ret) 297 while (--i >= 0) { 298 led_classdev_unregister(&leds->led[i].cdev); 299 cancel_work_sync(&leds->led[i].work); 300 } 301 302 return ret; 303 } 304 305 static int mc13xxx_led_remove(struct platform_device *pdev) 306 { 307 struct mc13xxx *mcdev = dev_get_drvdata(pdev->dev.parent); 308 struct mc13xxx_leds *leds = platform_get_drvdata(pdev); 309 int i; 310 311 for (i = 0; i < leds->num_leds; i++) { 312 led_classdev_unregister(&leds->led[i].cdev); 313 cancel_work_sync(&leds->led[i].work); 314 } 315 316 mc13xxx_lock(mcdev); 317 for (i = 0; i < leds->devtype->num_regs; i++) 318 mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), 0); 319 mc13xxx_unlock(mcdev); 320 321 return 0; 322 } 323 324 static const struct mc13xxx_led_devtype mc13783_led_devtype = { 325 .led_min = MC13783_LED_MD, 326 .led_max = MC13783_LED_B3, 327 .num_regs = 6, 328 }; 329 330 static const struct mc13xxx_led_devtype mc13892_led_devtype = { 331 .led_min = MC13892_LED_MD, 332 .led_max = MC13892_LED_B, 333 .num_regs = 4, 334 }; 335 336 static const struct platform_device_id mc13xxx_led_id_table[] = { 337 { "mc13783-led", (kernel_ulong_t)&mc13783_led_devtype, }, 338 { "mc13892-led", (kernel_ulong_t)&mc13892_led_devtype, }, 339 { } 340 }; 341 MODULE_DEVICE_TABLE(platform, mc13xxx_led_id_table); 342 343 static struct platform_driver mc13xxx_led_driver = { 344 .driver = { 345 .name = "mc13xxx-led", 346 .owner = THIS_MODULE, 347 }, 348 .remove = mc13xxx_led_remove, 349 .id_table = mc13xxx_led_id_table, 350 }; 351 module_platform_driver_probe(mc13xxx_led_driver, mc13xxx_led_probe); 352 353 MODULE_DESCRIPTION("LEDs driver for Freescale MC13XXX PMIC"); 354 MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>"); 355 MODULE_LICENSE("GPL"); 356