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/mfd/88pm860x.h>
20 #include <linux/module.h>
22 #define LED_PWM_MASK (0x1F)
23 #define LED_CURRENT_MASK (0x07 << 5)
25 #define LED_BLINK_MASK (0x7F)
27 #define LED_ON_CONTINUOUS (0x0F << 3)
29 #define LED1_BLINK_EN (1 << 1)
30 #define LED2_BLINK_EN (1 << 2)
33 struct led_classdev cdev
;
34 struct i2c_client
*i2c
;
35 struct pm860x_chip
*chip
;
37 char name
[MFD_NAME_SIZE
];
41 unsigned char brightness
;
42 unsigned char current_brightness
;
49 static int led_power_set(struct pm860x_chip
*chip
, int port
, int on
)
57 ret
= on
? pm8606_osc_enable(chip
, RGB1_ENABLE
) :
58 pm8606_osc_disable(chip
, RGB1_ENABLE
);
63 ret
= on
? pm8606_osc_enable(chip
, RGB2_ENABLE
) :
64 pm8606_osc_disable(chip
, RGB2_ENABLE
);
70 static int pm860x_led_set(struct led_classdev
*cdev
,
71 enum led_brightness value
)
73 struct pm860x_led
*led
= container_of(cdev
, struct pm860x_led
, cdev
);
74 struct pm860x_chip
*chip
;
79 mutex_lock(&led
->lock
);
80 led
->brightness
= value
>> 3;
82 if ((led
->current_brightness
== 0) && led
->brightness
) {
83 led_power_set(chip
, led
->port
, 1);
85 pm860x_set_bits(led
->i2c
, led
->reg_control
,
86 LED_CURRENT_MASK
, led
->iset
);
88 pm860x_set_bits(led
->i2c
, led
->reg_blink
,
89 LED_BLINK_MASK
, LED_ON_CONTINUOUS
);
90 pm860x_set_bits(led
->i2c
, PM8606_WLED3B
, led
->blink_mask
,
93 pm860x_set_bits(led
->i2c
, led
->reg_control
, LED_PWM_MASK
,
96 if (led
->brightness
== 0) {
97 pm860x_bulk_read(led
->i2c
, led
->reg_control
, 3, buf
);
98 ret
= buf
[0] & LED_PWM_MASK
;
99 ret
|= buf
[1] & LED_PWM_MASK
;
100 ret
|= buf
[2] & LED_PWM_MASK
;
102 /* unset current since no led is lighting */
103 pm860x_set_bits(led
->i2c
, led
->reg_control
,
104 LED_CURRENT_MASK
, 0);
105 pm860x_set_bits(led
->i2c
, PM8606_WLED3B
,
107 led_power_set(chip
, led
->port
, 0);
110 led
->current_brightness
= led
->brightness
;
111 dev_dbg(chip
->dev
, "Update LED. (reg:%d, brightness:%d)\n",
112 led
->reg_control
, led
->brightness
);
113 mutex_unlock(&led
->lock
);
119 static int pm860x_led_dt_init(struct platform_device
*pdev
,
120 struct pm860x_led
*data
)
122 struct device_node
*nproot
, *np
;
125 if (!pdev
->dev
.parent
->of_node
)
127 nproot
= of_get_child_by_name(pdev
->dev
.parent
->of_node
, "leds");
129 dev_err(&pdev
->dev
, "failed to find leds node\n");
132 for_each_child_of_node(nproot
, np
) {
133 if (!of_node_cmp(np
->name
, data
->name
)) {
134 of_property_read_u32(np
, "marvell,88pm860x-iset",
136 data
->iset
= PM8606_LED_CURRENT(iset
);
145 #define pm860x_led_dt_init(x, y) (-1)
148 static int pm860x_led_probe(struct platform_device
*pdev
)
150 struct pm860x_chip
*chip
= dev_get_drvdata(pdev
->dev
.parent
);
151 struct pm860x_led_pdata
*pdata
= dev_get_platdata(&pdev
->dev
);
152 struct pm860x_led
*data
;
153 struct resource
*res
;
156 data
= devm_kzalloc(&pdev
->dev
, sizeof(struct pm860x_led
), GFP_KERNEL
);
159 res
= platform_get_resource_byname(pdev
, IORESOURCE_REG
, "control");
161 dev_err(&pdev
->dev
, "No REG resource for control\n");
164 data
->reg_control
= res
->start
;
165 res
= platform_get_resource_byname(pdev
, IORESOURCE_REG
, "blink");
167 dev_err(&pdev
->dev
, "No REG resource for blink\n");
170 data
->reg_blink
= res
->start
;
171 memset(data
->name
, 0, MFD_NAME_SIZE
);
174 data
->blink_mask
= LED1_BLINK_EN
;
175 sprintf(data
->name
, "led0-red");
178 data
->blink_mask
= LED1_BLINK_EN
;
179 sprintf(data
->name
, "led0-green");
182 data
->blink_mask
= LED1_BLINK_EN
;
183 sprintf(data
->name
, "led0-blue");
186 data
->blink_mask
= LED2_BLINK_EN
;
187 sprintf(data
->name
, "led1-red");
190 data
->blink_mask
= LED2_BLINK_EN
;
191 sprintf(data
->name
, "led1-green");
194 data
->blink_mask
= LED2_BLINK_EN
;
195 sprintf(data
->name
, "led1-blue");
199 data
->i2c
= (chip
->id
== CHIP_PM8606
) ? chip
->client
: chip
->companion
;
200 data
->port
= pdev
->id
;
201 if (pm860x_led_dt_init(pdev
, data
))
203 data
->iset
= pdata
->iset
;
205 data
->current_brightness
= 0;
206 data
->cdev
.name
= data
->name
;
207 data
->cdev
.brightness_set_blocking
= pm860x_led_set
;
208 mutex_init(&data
->lock
);
210 ret
= devm_led_classdev_register(chip
->dev
, &data
->cdev
);
212 dev_err(&pdev
->dev
, "Failed to register LED: %d\n", ret
);
215 pm860x_led_set(&data
->cdev
, 0);
220 static struct platform_driver pm860x_led_driver
= {
222 .name
= "88pm860x-led",
224 .probe
= pm860x_led_probe
,
227 module_platform_driver(pm860x_led_driver
);
229 MODULE_DESCRIPTION("LED driver for Marvell PM860x");
230 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
231 MODULE_LICENSE("GPL");
232 MODULE_ALIAS("platform:88pm860x-led");