17fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz/* 27fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * LEDs driver for Freescale MC13783 37fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * 47fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * Copyright (C) 2010 Philippe Rétornaz 57fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * 67fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * Based on leds-da903x: 77fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * Copyright (C) 2008 Compulab, Ltd. 87fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * Mike Rapoport <mike@compulab.co.il> 97fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * 107fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * Copyright (C) 2006-2008 Marvell International Ltd. 117fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * Eric Miao <eric.miao@marvell.com> 127fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * 137fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * This program is free software; you can redistribute it and/or modify 147fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * it under the terms of the GNU General Public License version 2 as 157fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz * published by the Free Software Foundation. 167fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz */ 177fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 187fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/module.h> 197fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/kernel.h> 207fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/init.h> 217fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/platform_device.h> 227fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/leds.h> 237fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/workqueue.h> 24f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander#include <linux/mfd/mc13xxx.h> 257fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#include <linux/slab.h> 267fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 277fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstruct mc13783_led { 287fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz struct led_classdev cdev; 297fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz struct work_struct work; 30f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander struct mc13xxx *master; 317fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz enum led_brightness new_brightness; 327fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int id; 337fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz}; 347fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 357fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_REG_LED_CONTROL_0 51 367fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C0_ENABLE_BIT (1 << 0) 377fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C0_TRIODE_MD_BIT (1 << 7) 387fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C0_TRIODE_AD_BIT (1 << 8) 397fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C0_TRIODE_KP_BIT (1 << 9) 407fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C0_BOOST_BIT (1 << 10) 417fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C0_ABMODE_MASK 0x7 427fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C0_ABMODE 11 437fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C0_ABREF_MASK 0x3 447fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C0_ABREF 14 457fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 467fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_REG_LED_CONTROL_1 52 477fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C1_TC1HALF_BIT (1 << 18) 487fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 497fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_REG_LED_CONTROL_2 53 507fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C2_BL_P_MASK 0xf 517fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C2_MD_P 9 527fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C2_AD_P 13 537fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C2_KP_P 17 547fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C2_BL_C_MASK 0x7 557fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C2_MD_C 0 567fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C2_AD_C 3 577fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C2_KP_C 6 587fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 597fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_REG_LED_CONTROL_3 54 607fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C3_TC_P 6 617fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_C3_TC_P_MASK 0x1f 627fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 637fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_REG_LED_CONTROL_4 55 647fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_REG_LED_CONTROL_5 56 657fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 667fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_Cx_PERIOD 21 677fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_Cx_PERIOD_MASK 0x3 687fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_Cx_SLEWLIM_BIT (1 << 23) 697fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_Cx_TRIODE_TC_BIT (1 << 23) 707fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz#define MC13783_LED_Cx_TC_C_MASK 0x3 717fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 727fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic void mc13783_led_work(struct work_struct *work) 737fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 747fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz struct mc13783_led *led = container_of(work, struct mc13783_led, work); 757fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int reg = 0; 767fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int mask = 0; 777fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int value = 0; 787fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int bank, off, shift; 797fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 807fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz switch (led->id) { 817fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_MD: 827fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = MC13783_REG_LED_CONTROL_2; 837fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_MD_P; 847fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz value = (led->new_brightness >> 4) << MC13783_LED_C2_MD_P; 857fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz break; 867fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_AD: 877fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = MC13783_REG_LED_CONTROL_2; 887fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_AD_P; 897fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz value = (led->new_brightness >> 4) << MC13783_LED_C2_AD_P; 907fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz break; 917fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_KP: 927fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = MC13783_REG_LED_CONTROL_2; 937fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_KP_P; 947fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz value = (led->new_brightness >> 4) << MC13783_LED_C2_KP_P; 957fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz break; 967fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_R1: 977fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_G1: 987fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_B1: 997fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_R2: 1007fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_G2: 1017fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_B2: 1027fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_R3: 1037fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_G3: 1047fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_B3: 1057fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz off = led->id - MC13783_LED_R1; 1067fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz bank = off/3; 1077fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = MC13783_REG_LED_CONTROL_3 + off/3; 1087fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz shift = (off - bank * 3) * 5 + MC13783_LED_C3_TC_P; 1097fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz value = (led->new_brightness >> 3) << shift; 1107fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz mask = MC13783_LED_C3_TC_P_MASK << shift; 1117fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz break; 1127fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 1137fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 114f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_lock(led->master); 1157fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 116f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_reg_rmw(led->master, reg, mask, value); 1177fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 118f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_unlock(led->master); 1197fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 1207fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1217fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic void mc13783_led_set(struct led_classdev *led_cdev, 1227fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz enum led_brightness value) 1237fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 1247fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz struct mc13783_led *led; 1257fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1267fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led = container_of(led_cdev, struct mc13783_led, cdev); 1277fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led->new_brightness = value; 1287fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz schedule_work(&led->work); 1297fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 1307fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1317fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current) 1327fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 1337fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int shift = 0; 1347fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int mask = 0; 1357fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int value = 0; 1367fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int reg = 0; 1377fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int ret, bank; 1387fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1397fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz switch (led->id) { 1407fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_MD: 1417fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz shift = MC13783_LED_C2_MD_C; 1427fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz mask = MC13783_LED_C2_BL_C_MASK; 1437fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz value = max_current & MC13783_LED_C2_BL_C_MASK; 1447fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = MC13783_REG_LED_CONTROL_2; 1457fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz break; 1467fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_AD: 1477fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz shift = MC13783_LED_C2_AD_C; 1487fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz mask = MC13783_LED_C2_BL_C_MASK; 1497fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz value = max_current & MC13783_LED_C2_BL_C_MASK; 1507fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = MC13783_REG_LED_CONTROL_2; 1517fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz break; 1527fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_KP: 1537fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz shift = MC13783_LED_C2_KP_C; 1547fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz mask = MC13783_LED_C2_BL_C_MASK; 1557fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz value = max_current & MC13783_LED_C2_BL_C_MASK; 1567fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = MC13783_REG_LED_CONTROL_2; 1577fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz break; 1587fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_R1: 1597fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_G1: 1607fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_B1: 1617fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_R2: 1627fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_G2: 1637fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_B2: 1647fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_R3: 1657fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_G3: 1667fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz case MC13783_LED_B3: 1677fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz bank = (led->id - MC13783_LED_R1)/3; 1687fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = MC13783_REG_LED_CONTROL_3 + bank; 1697fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz shift = ((led->id - MC13783_LED_R1) - bank * 3) * 2; 1707fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz mask = MC13783_LED_Cx_TC_C_MASK; 1717fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz value = max_current & MC13783_LED_Cx_TC_C_MASK; 1727fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz break; 1737fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 1747fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 175f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_lock(led->master); 1767fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 177f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander ret = mc13xxx_reg_rmw(led->master, reg, mask << shift, 1787fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz value << shift); 1797fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 180f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_unlock(led->master); 1817fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return ret; 1827fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 1837fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1847fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic int __devinit mc13783_leds_prepare(struct platform_device *pdev) 1857fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 18652b7ad3a63a42b76f4f07cba876479a3c416f1e8Samuel Ortiz struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); 187f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent); 1887fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int ret = 0; 1897fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int reg = 0; 1907fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 191f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_lock(dev); 1927fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1937fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (pdata->flags & MC13783_LED_TC1HALF) 1947fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg |= MC13783_LED_C1_TC1HALF_BIT; 1957fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 1967fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (pdata->flags & MC13783_LED_SLEWLIMTC) 1977fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg |= MC13783_LED_Cx_SLEWLIM_BIT; 1987fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 199f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_1, reg); 2007fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (ret) 2017fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto out; 2027fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2037fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = (pdata->bl_period & MC13783_LED_Cx_PERIOD_MASK) << 2047fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz MC13783_LED_Cx_PERIOD; 2057fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2067fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (pdata->flags & MC13783_LED_SLEWLIMBL) 2077fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg |= MC13783_LED_Cx_SLEWLIM_BIT; 2087fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 209f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_2, reg); 2107fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (ret) 2117fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto out; 2127fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2137fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = (pdata->tc1_period & MC13783_LED_Cx_PERIOD_MASK) << 2147fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz MC13783_LED_Cx_PERIOD; 2157fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2167fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (pdata->flags & MC13783_LED_TRIODE_TC1) 2177fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg |= MC13783_LED_Cx_TRIODE_TC_BIT; 2187fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 219f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_3, reg); 2207fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (ret) 2217fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto out; 2227fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2237fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = (pdata->tc2_period & MC13783_LED_Cx_PERIOD_MASK) << 2247fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz MC13783_LED_Cx_PERIOD; 2257fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2267fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (pdata->flags & MC13783_LED_TRIODE_TC2) 2277fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg |= MC13783_LED_Cx_TRIODE_TC_BIT; 2287fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 229f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_4, reg); 2307fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (ret) 2317fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto out; 2327fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2337fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = (pdata->tc3_period & MC13783_LED_Cx_PERIOD_MASK) << 2347fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz MC13783_LED_Cx_PERIOD; 2357fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2367fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (pdata->flags & MC13783_LED_TRIODE_TC3) 2376eab04a87677a37cf15b52e2b4b4fd57917102adJustin P. Mattock reg |= MC13783_LED_Cx_TRIODE_TC_BIT; 2387fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 239f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_5, reg); 2407fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (ret) 2417fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto out; 2427fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2437fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg = MC13783_LED_C0_ENABLE_BIT; 2447fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (pdata->flags & MC13783_LED_TRIODE_MD) 2457fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg |= MC13783_LED_C0_TRIODE_MD_BIT; 2467fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (pdata->flags & MC13783_LED_TRIODE_AD) 2477fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg |= MC13783_LED_C0_TRIODE_AD_BIT; 2487fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (pdata->flags & MC13783_LED_TRIODE_KP) 2497fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg |= MC13783_LED_C0_TRIODE_KP_BIT; 2507fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (pdata->flags & MC13783_LED_BOOST_EN) 2517fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg |= MC13783_LED_C0_BOOST_BIT; 2527fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2537fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg |= (pdata->abmode & MC13783_LED_C0_ABMODE_MASK) << 2547fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz MC13783_LED_C0_ABMODE; 2557fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz reg |= (pdata->abref & MC13783_LED_C0_ABREF_MASK) << 2567fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz MC13783_LED_C0_ABREF; 2577fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 258f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg); 2597fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2607fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazout: 261f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_unlock(dev); 2627fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return ret; 2637fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 2647fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2657fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic int __devinit mc13783_led_probe(struct platform_device *pdev) 2667fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 26752b7ad3a63a42b76f4f07cba876479a3c416f1e8Samuel Ortiz struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); 26852b7ad3a63a42b76f4f07cba876479a3c416f1e8Samuel Ortiz struct mc13xxx_led_platform_data *led_cur; 2697fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz struct mc13783_led *led, *led_dat; 2707fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int ret, i; 2717fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int init_led = 0; 2727fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2737fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (pdata == NULL) { 2747fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz dev_err(&pdev->dev, "missing platform data\n"); 2757fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return -ENODEV; 2767fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 2777fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2783b080945aa7670354364c8f9e1a3a07cbb97beb3Axel Lin if (pdata->num_leds < 1 || pdata->num_leds > (MC13783_LED_MAX + 1)) { 2797fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz dev_err(&pdev->dev, "Invalid led count %d\n", pdata->num_leds); 2807fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return -EINVAL; 2817fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 2827fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2837fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL); 2847fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (led == NULL) { 2857fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz dev_err(&pdev->dev, "failed to alloc memory\n"); 2867fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return -ENOMEM; 2877fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 2887fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2897fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz ret = mc13783_leds_prepare(pdev); 2907fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (ret) { 2917fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz dev_err(&pdev->dev, "unable to init led driver\n"); 2927fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto err_free; 2937fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 2947fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2957fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz for (i = 0; i < pdata->num_leds; i++) { 2967fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_dat = &led[i]; 2977fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_cur = &pdata->led[i]; 2987fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 2997fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (led_cur->id > MC13783_LED_MAX || led_cur->id < 0) { 3007fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz dev_err(&pdev->dev, "invalid id %d\n", led_cur->id); 3017fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz ret = -EINVAL; 3027fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto err_register; 3037fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 3047fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3057fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (init_led & (1 << led_cur->id)) { 3067fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz dev_err(&pdev->dev, "led %d already initialized\n", 3077fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_cur->id); 3087fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz ret = -EINVAL; 3097fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto err_register; 3107fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 3117fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3127fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz init_led |= 1 << led_cur->id; 3137fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_dat->cdev.name = led_cur->name; 3147fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_dat->cdev.default_trigger = led_cur->default_trigger; 3157fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_dat->cdev.brightness_set = mc13783_led_set; 3167fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_dat->cdev.brightness = LED_OFF; 3177fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_dat->id = led_cur->id; 3187fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_dat->master = dev_get_drvdata(pdev->dev.parent); 3197fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3207fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz INIT_WORK(&led_dat->work, mc13783_led_work); 3217fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3227fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz ret = led_classdev_register(pdev->dev.parent, &led_dat->cdev); 3237fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (ret) { 3247fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz dev_err(&pdev->dev, "failed to register led %d\n", 3257fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_dat->id); 3267fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto err_register; 3277fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 3287fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3297fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz ret = mc13783_led_setup(led_dat, led_cur->max_current); 3307fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz if (ret) { 3317fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz dev_err(&pdev->dev, "unable to init led %d\n", 3327fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_dat->id); 3337fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz i++; 3347fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz goto err_register; 3357fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 3367fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 3377fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3387fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz platform_set_drvdata(pdev, led); 3397fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return 0; 3407fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3417fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazerr_register: 3427fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz for (i = i - 1; i >= 0; i--) { 3437fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_classdev_unregister(&led[i].cdev); 3447fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz cancel_work_sync(&led[i].work); 3457fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 3467fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3477fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazerr_free: 3487fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz kfree(led); 3497fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return ret; 3507fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 3517fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3527fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic int __devexit mc13783_led_remove(struct platform_device *pdev) 3537fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz{ 35452b7ad3a63a42b76f4f07cba876479a3c416f1e8Samuel Ortiz struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); 3557fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz struct mc13783_led *led = platform_get_drvdata(pdev); 356f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent); 3577fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz int i; 3587fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3597fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz for (i = 0; i < pdata->num_leds; i++) { 3607fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz led_classdev_unregister(&led[i].cdev); 3617fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz cancel_work_sync(&led[i].work); 3627fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz } 3637fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 364f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_lock(dev); 3657fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 366f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0); 367f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0); 368f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0); 369f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0); 370f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0); 371f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0); 3727fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 373f3ca07824f309474b308d859c9a2cc871c6c5ab8David Jander mc13xxx_unlock(dev); 3747fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3757fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz kfree(led); 3767fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz return 0; 3777fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz} 3787fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3797fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornazstatic struct platform_driver mc13783_led_driver = { 3807fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .driver = { 3817fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .name = "mc13783-led", 3827fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .owner = THIS_MODULE, 3837fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz }, 3847fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .probe = mc13783_led_probe, 3857fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz .remove = __devexit_p(mc13783_led_remove), 3867fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz}; 3877fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 388892a8843fbef07a7f2ab62d5f7ff5c16ea0903b0Axel Linmodule_platform_driver(mc13783_led_driver); 3897fdcef8a414eaeb367b3696005b25283d62d195dPhilippe Rétornaz 3907fdcef8a414eaeb367b3696005b25283d62d195dPhilippe RétornazMODULE_DESCRIPTION("LEDs driver for Freescale MC13783 PMIC"); 3917fdcef8a414eaeb367b3696005b25283d62d195dPhilippe RétornazMODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>"); 3927fdcef8a414eaeb367b3696005b25283d62d195dPhilippe RétornazMODULE_LICENSE("GPL"); 3937fdcef8a414eaeb367b3696005b25283d62d195dPhilippe RétornazMODULE_ALIAS("platform:mc13783-led"); 394