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>
15 #include <linux/platform_device.h>
16 #include <linux/i2c.h>
17 #include <linux/leds.h>
18 #include <linux/slab.h>
19 #include <linux/workqueue.h>
20 #include <linux/mfd/88pm860x.h>
21 #include <linux/module.h>
23 #define LED_PWM_MASK (0x1F)
24 #define LED_CURRENT_MASK (0x07 << 5)
26 #define LED_BLINK_MASK (0x7F)
28 #define LED_ON_CONTINUOUS (0x0F << 3)
30 #define LED1_BLINK_EN (1 << 1)
31 #define LED2_BLINK_EN (1 << 2)
34 struct led_classdev cdev
;
35 struct i2c_client
*i2c
;
36 struct work_struct work
;
37 struct pm860x_chip
*chip
;
39 char name
[MFD_NAME_SIZE
];
43 unsigned char brightness
;
44 unsigned char current_brightness
;
51 static int led_power_set(struct pm860x_chip
*chip
, int port
, int on
)
59 ret
= on
? pm8606_osc_enable(chip
, RGB1_ENABLE
) :
60 pm8606_osc_disable(chip
, RGB1_ENABLE
);
65 ret
= on
? pm8606_osc_enable(chip
, RGB2_ENABLE
) :
66 pm8606_osc_disable(chip
, RGB2_ENABLE
);
72 static void pm860x_led_work(struct work_struct
*work
)
75 struct pm860x_led
*led
;
76 struct pm860x_chip
*chip
;
80 led
= container_of(work
, struct pm860x_led
, work
);
82 mutex_lock(&led
->lock
);
83 if ((led
->current_brightness
== 0) && led
->brightness
) {
84 led_power_set(chip
, led
->port
, 1);
86 pm860x_set_bits(led
->i2c
, led
->reg_control
,
87 LED_CURRENT_MASK
, led
->iset
);
89 pm860x_set_bits(led
->i2c
, led
->reg_blink
,
90 LED_BLINK_MASK
, LED_ON_CONTINUOUS
);
91 pm860x_set_bits(led
->i2c
, PM8606_WLED3B
, led
->blink_mask
,
94 pm860x_set_bits(led
->i2c
, led
->reg_control
, LED_PWM_MASK
,
97 if (led
->brightness
== 0) {
98 pm860x_bulk_read(led
->i2c
, led
->reg_control
, 3, buf
);
99 ret
= buf
[0] & LED_PWM_MASK
;
100 ret
|= buf
[1] & LED_PWM_MASK
;
101 ret
|= buf
[2] & LED_PWM_MASK
;
103 /* unset current since no led is lighting */
104 pm860x_set_bits(led
->i2c
, led
->reg_control
,
105 LED_CURRENT_MASK
, 0);
106 pm860x_set_bits(led
->i2c
, PM8606_WLED3B
,
108 led_power_set(chip
, led
->port
, 0);
111 led
->current_brightness
= led
->brightness
;
112 dev_dbg(chip
->dev
, "Update LED. (reg:%d, brightness:%d)\n",
113 led
->reg_control
, led
->brightness
);
114 mutex_unlock(&led
->lock
);
117 static void pm860x_led_set(struct led_classdev
*cdev
,
118 enum led_brightness value
)
120 struct pm860x_led
*data
= container_of(cdev
, struct pm860x_led
, cdev
);
122 data
->brightness
= value
>> 3;
123 schedule_work(&data
->work
);
127 static int pm860x_led_dt_init(struct platform_device
*pdev
,
128 struct pm860x_led
*data
)
130 struct device_node
*nproot
, *np
;
133 if (!pdev
->dev
.parent
->of_node
)
135 nproot
= of_get_child_by_name(pdev
->dev
.parent
->of_node
, "leds");
137 dev_err(&pdev
->dev
, "failed to find leds node\n");
140 for_each_child_of_node(nproot
, np
) {
141 if (!of_node_cmp(np
->name
, data
->name
)) {
142 of_property_read_u32(np
, "marvell,88pm860x-iset",
144 data
->iset
= PM8606_LED_CURRENT(iset
);
152 #define pm860x_led_dt_init(x, y) (-1)
155 static int pm860x_led_probe(struct platform_device
*pdev
)
157 struct pm860x_chip
*chip
= dev_get_drvdata(pdev
->dev
.parent
);
158 struct pm860x_led_pdata
*pdata
= dev_get_platdata(&pdev
->dev
);
159 struct pm860x_led
*data
;
160 struct resource
*res
;
163 data
= devm_kzalloc(&pdev
->dev
, sizeof(struct pm860x_led
), GFP_KERNEL
);
166 res
= platform_get_resource_byname(pdev
, IORESOURCE_REG
, "control");
168 dev_err(&pdev
->dev
, "No REG resource for control\n");
171 data
->reg_control
= res
->start
;
172 res
= platform_get_resource_byname(pdev
, IORESOURCE_REG
, "blink");
174 dev_err(&pdev
->dev
, "No REG resource for blink\n");
177 data
->reg_blink
= res
->start
;
178 memset(data
->name
, 0, MFD_NAME_SIZE
);
181 data
->blink_mask
= LED1_BLINK_EN
;
182 sprintf(data
->name
, "led0-red");
185 data
->blink_mask
= LED1_BLINK_EN
;
186 sprintf(data
->name
, "led0-green");
189 data
->blink_mask
= LED1_BLINK_EN
;
190 sprintf(data
->name
, "led0-blue");
193 data
->blink_mask
= LED2_BLINK_EN
;
194 sprintf(data
->name
, "led1-red");
197 data
->blink_mask
= LED2_BLINK_EN
;
198 sprintf(data
->name
, "led1-green");
201 data
->blink_mask
= LED2_BLINK_EN
;
202 sprintf(data
->name
, "led1-blue");
205 platform_set_drvdata(pdev
, data
);
207 data
->i2c
= (chip
->id
== CHIP_PM8606
) ? chip
->client
: chip
->companion
;
208 data
->port
= pdev
->id
;
209 if (pm860x_led_dt_init(pdev
, data
))
211 data
->iset
= pdata
->iset
;
213 data
->current_brightness
= 0;
214 data
->cdev
.name
= data
->name
;
215 data
->cdev
.brightness_set
= pm860x_led_set
;
216 mutex_init(&data
->lock
);
217 INIT_WORK(&data
->work
, pm860x_led_work
);
219 ret
= led_classdev_register(chip
->dev
, &data
->cdev
);
221 dev_err(&pdev
->dev
, "Failed to register LED: %d\n", ret
);
224 pm860x_led_set(&data
->cdev
, 0);
228 static int pm860x_led_remove(struct platform_device
*pdev
)
230 struct pm860x_led
*data
= platform_get_drvdata(pdev
);
232 led_classdev_unregister(&data
->cdev
);
237 static struct platform_driver pm860x_led_driver
= {
239 .name
= "88pm860x-led",
241 .probe
= pm860x_led_probe
,
242 .remove
= pm860x_led_remove
,
245 module_platform_driver(pm860x_led_driver
);
247 MODULE_DESCRIPTION("LED driver for Marvell PM860x");
248 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
249 MODULE_LICENSE("GPL");
250 MODULE_ALIAS("platform:88pm860x-led");