2 * LED driver for Marvell 88PM860x
4 * Copyright (C) 2009 Marvell International Ltd.
5 * Haojian Zhuang <haojian.zhuang@marvell.com>
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.
13 #include <linux/kernel.h>
14 #include <linux/init.h>
16 #include <linux/platform_device.h>
17 #include <linux/i2c.h>
18 #include <linux/leds.h>
19 #include <linux/slab.h>
20 #include <linux/workqueue.h>
21 #include <linux/mfd/88pm860x.h>
22 #include <linux/module.h>
24 #define LED_PWM_MASK (0x1F)
25 #define LED_CURRENT_MASK (0x07 << 5)
27 #define LED_BLINK_MASK (0x7F)
29 #define LED_ON_CONTINUOUS (0x0F << 3)
31 #define LED1_BLINK_EN (1 << 1)
32 #define LED2_BLINK_EN (1 << 2)
35 struct led_classdev cdev
;
36 struct i2c_client
*i2c
;
37 struct work_struct work
;
38 struct pm860x_chip
*chip
;
40 char name
[MFD_NAME_SIZE
];
44 unsigned char brightness
;
45 unsigned char current_brightness
;
52 static int led_power_set(struct pm860x_chip
*chip
, int port
, int on
)
60 ret
= on
? pm8606_osc_enable(chip
, RGB1_ENABLE
) :
61 pm8606_osc_disable(chip
, RGB1_ENABLE
);
66 ret
= on
? pm8606_osc_enable(chip
, RGB2_ENABLE
) :
67 pm8606_osc_disable(chip
, RGB2_ENABLE
);
73 static void pm860x_led_work(struct work_struct
*work
)
76 struct pm860x_led
*led
;
77 struct pm860x_chip
*chip
;
81 led
= container_of(work
, struct pm860x_led
, work
);
83 mutex_lock(&led
->lock
);
84 if ((led
->current_brightness
== 0) && led
->brightness
) {
85 led_power_set(chip
, led
->port
, 1);
87 pm860x_set_bits(led
->i2c
, led
->reg_control
,
88 LED_CURRENT_MASK
, led
->iset
);
90 pm860x_set_bits(led
->i2c
, led
->reg_blink
,
91 LED_BLINK_MASK
, LED_ON_CONTINUOUS
);
92 pm860x_set_bits(led
->i2c
, PM8606_WLED3B
, led
->blink_mask
,
95 pm860x_set_bits(led
->i2c
, led
->reg_control
, LED_PWM_MASK
,
98 if (led
->brightness
== 0) {
99 pm860x_bulk_read(led
->i2c
, led
->reg_control
, 3, buf
);
100 ret
= buf
[0] & LED_PWM_MASK
;
101 ret
|= buf
[1] & LED_PWM_MASK
;
102 ret
|= buf
[2] & LED_PWM_MASK
;
104 /* unset current since no led is lighting */
105 pm860x_set_bits(led
->i2c
, led
->reg_control
,
106 LED_CURRENT_MASK
, 0);
107 pm860x_set_bits(led
->i2c
, PM8606_WLED3B
,
109 led_power_set(chip
, led
->port
, 0);
112 led
->current_brightness
= led
->brightness
;
113 dev_dbg(chip
->dev
, "Update LED. (reg:%d, brightness:%d)\n",
114 led
->reg_control
, led
->brightness
);
115 mutex_unlock(&led
->lock
);
118 static void pm860x_led_set(struct led_classdev
*cdev
,
119 enum led_brightness value
)
121 struct pm860x_led
*data
= container_of(cdev
, struct pm860x_led
, cdev
);
123 data
->brightness
= value
>> 3;
124 schedule_work(&data
->work
);
128 static int pm860x_led_dt_init(struct platform_device
*pdev
,
129 struct pm860x_led
*data
)
131 struct device_node
*nproot
, *np
;
134 nproot
= of_node_get(pdev
->dev
.parent
->of_node
);
137 nproot
= of_find_node_by_name(nproot
, "leds");
139 dev_err(&pdev
->dev
, "failed to find leds node\n");
142 for_each_child_of_node(nproot
, np
) {
143 if (!of_node_cmp(np
->name
, data
->name
)) {
144 of_property_read_u32(np
, "marvell,88pm860x-iset",
146 data
->iset
= PM8606_LED_CURRENT(iset
);
154 #define pm860x_led_dt_init(x, y) (-1)
157 static int pm860x_led_probe(struct platform_device
*pdev
)
159 struct pm860x_chip
*chip
= dev_get_drvdata(pdev
->dev
.parent
);
160 struct pm860x_led_pdata
*pdata
= dev_get_platdata(&pdev
->dev
);
161 struct pm860x_led
*data
;
162 struct resource
*res
;
165 data
= devm_kzalloc(&pdev
->dev
, sizeof(struct pm860x_led
), GFP_KERNEL
);
168 res
= platform_get_resource_byname(pdev
, IORESOURCE_REG
, "control");
170 dev_err(&pdev
->dev
, "No REG resource for control\n");
173 data
->reg_control
= res
->start
;
174 res
= platform_get_resource_byname(pdev
, IORESOURCE_REG
, "blink");
176 dev_err(&pdev
->dev
, "No REG resource for blink\n");
179 data
->reg_blink
= res
->start
;
180 memset(data
->name
, 0, MFD_NAME_SIZE
);
183 data
->blink_mask
= LED1_BLINK_EN
;
184 sprintf(data
->name
, "led0-red");
187 data
->blink_mask
= LED1_BLINK_EN
;
188 sprintf(data
->name
, "led0-green");
191 data
->blink_mask
= LED1_BLINK_EN
;
192 sprintf(data
->name
, "led0-blue");
195 data
->blink_mask
= LED2_BLINK_EN
;
196 sprintf(data
->name
, "led1-red");
199 data
->blink_mask
= LED2_BLINK_EN
;
200 sprintf(data
->name
, "led1-green");
203 data
->blink_mask
= LED2_BLINK_EN
;
204 sprintf(data
->name
, "led1-blue");
207 platform_set_drvdata(pdev
, data
);
209 data
->i2c
= (chip
->id
== CHIP_PM8606
) ? chip
->client
: chip
->companion
;
210 data
->port
= pdev
->id
;
211 if (pm860x_led_dt_init(pdev
, data
))
213 data
->iset
= pdata
->iset
;
215 data
->current_brightness
= 0;
216 data
->cdev
.name
= data
->name
;
217 data
->cdev
.brightness_set
= pm860x_led_set
;
218 mutex_init(&data
->lock
);
219 INIT_WORK(&data
->work
, pm860x_led_work
);
221 ret
= led_classdev_register(chip
->dev
, &data
->cdev
);
223 dev_err(&pdev
->dev
, "Failed to register LED: %d\n", ret
);
226 pm860x_led_set(&data
->cdev
, 0);
230 static int pm860x_led_remove(struct platform_device
*pdev
)
232 struct pm860x_led
*data
= platform_get_drvdata(pdev
);
234 led_classdev_unregister(&data
->cdev
);
239 static struct platform_driver pm860x_led_driver
= {
241 .name
= "88pm860x-led",
242 .owner
= THIS_MODULE
,
244 .probe
= pm860x_led_probe
,
245 .remove
= pm860x_led_remove
,
248 module_platform_driver(pm860x_led_driver
);
250 MODULE_DESCRIPTION("LED driver for Marvell PM860x");
251 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
252 MODULE_LICENSE("GPL");
253 MODULE_ALIAS("platform:88pm860x-led");