1 // SPDX-License-Identifier: GPL-2.0-only
3 * LED driver for Marvell 88PM860x
5 * Copyright (C) 2009 Marvell International Ltd.
6 * Haojian Zhuang <haojian.zhuang@marvell.com>
9 #include <linux/kernel.h>
11 #include <linux/platform_device.h>
12 #include <linux/i2c.h>
13 #include <linux/leds.h>
14 #include <linux/slab.h>
15 #include <linux/mfd/88pm860x.h>
16 #include <linux/module.h>
18 #define LED_PWM_MASK (0x1F)
19 #define LED_CURRENT_MASK (0x07 << 5)
21 #define LED_BLINK_MASK (0x7F)
23 #define LED_ON_CONTINUOUS (0x0F << 3)
25 #define LED1_BLINK_EN (1 << 1)
26 #define LED2_BLINK_EN (1 << 2)
29 struct led_classdev cdev
;
30 struct i2c_client
*i2c
;
31 struct pm860x_chip
*chip
;
33 char name
[MFD_NAME_SIZE
];
37 unsigned char brightness
;
38 unsigned char current_brightness
;
45 static int led_power_set(struct pm860x_chip
*chip
, int port
, int on
)
53 ret
= on
? pm8606_osc_enable(chip
, RGB1_ENABLE
) :
54 pm8606_osc_disable(chip
, RGB1_ENABLE
);
59 ret
= on
? pm8606_osc_enable(chip
, RGB2_ENABLE
) :
60 pm8606_osc_disable(chip
, RGB2_ENABLE
);
66 static int pm860x_led_set(struct led_classdev
*cdev
,
67 enum led_brightness value
)
69 struct pm860x_led
*led
= container_of(cdev
, struct pm860x_led
, cdev
);
70 struct pm860x_chip
*chip
;
75 mutex_lock(&led
->lock
);
76 led
->brightness
= value
>> 3;
78 if ((led
->current_brightness
== 0) && led
->brightness
) {
79 led_power_set(chip
, led
->port
, 1);
81 pm860x_set_bits(led
->i2c
, led
->reg_control
,
82 LED_CURRENT_MASK
, led
->iset
);
84 pm860x_set_bits(led
->i2c
, led
->reg_blink
,
85 LED_BLINK_MASK
, LED_ON_CONTINUOUS
);
86 pm860x_set_bits(led
->i2c
, PM8606_WLED3B
, led
->blink_mask
,
89 pm860x_set_bits(led
->i2c
, led
->reg_control
, LED_PWM_MASK
,
92 if (led
->brightness
== 0) {
93 pm860x_bulk_read(led
->i2c
, led
->reg_control
, 3, buf
);
94 ret
= buf
[0] & LED_PWM_MASK
;
95 ret
|= buf
[1] & LED_PWM_MASK
;
96 ret
|= buf
[2] & LED_PWM_MASK
;
98 /* unset current since no led is lighting */
99 pm860x_set_bits(led
->i2c
, led
->reg_control
,
100 LED_CURRENT_MASK
, 0);
101 pm860x_set_bits(led
->i2c
, PM8606_WLED3B
,
103 led_power_set(chip
, led
->port
, 0);
106 led
->current_brightness
= led
->brightness
;
107 dev_dbg(chip
->dev
, "Update LED. (reg:%d, brightness:%d)\n",
108 led
->reg_control
, led
->brightness
);
109 mutex_unlock(&led
->lock
);
115 static int pm860x_led_dt_init(struct platform_device
*pdev
,
116 struct pm860x_led
*data
)
118 struct device_node
*nproot
, *np
;
121 if (!dev_of_node(pdev
->dev
.parent
))
123 nproot
= of_get_child_by_name(dev_of_node(pdev
->dev
.parent
), "leds");
125 dev_err(&pdev
->dev
, "failed to find leds node\n");
128 for_each_available_child_of_node(nproot
, np
) {
129 if (of_node_name_eq(np
, data
->name
)) {
130 of_property_read_u32(np
, "marvell,88pm860x-iset",
132 data
->iset
= PM8606_LED_CURRENT(iset
);
141 #define pm860x_led_dt_init(x, y) (-1)
144 static int pm860x_led_probe(struct platform_device
*pdev
)
146 struct pm860x_chip
*chip
= dev_get_drvdata(pdev
->dev
.parent
);
147 struct pm860x_led_pdata
*pdata
= dev_get_platdata(&pdev
->dev
);
148 struct pm860x_led
*data
;
149 struct resource
*res
;
152 data
= devm_kzalloc(&pdev
->dev
, sizeof(struct pm860x_led
), GFP_KERNEL
);
155 res
= platform_get_resource_byname(pdev
, IORESOURCE_REG
, "control");
157 dev_err(&pdev
->dev
, "No REG resource for control\n");
160 data
->reg_control
= res
->start
;
161 res
= platform_get_resource_byname(pdev
, IORESOURCE_REG
, "blink");
163 dev_err(&pdev
->dev
, "No REG resource for blink\n");
166 data
->reg_blink
= res
->start
;
167 memset(data
->name
, 0, MFD_NAME_SIZE
);
170 data
->blink_mask
= LED1_BLINK_EN
;
171 sprintf(data
->name
, "led0-red");
174 data
->blink_mask
= LED1_BLINK_EN
;
175 sprintf(data
->name
, "led0-green");
178 data
->blink_mask
= LED1_BLINK_EN
;
179 sprintf(data
->name
, "led0-blue");
182 data
->blink_mask
= LED2_BLINK_EN
;
183 sprintf(data
->name
, "led1-red");
186 data
->blink_mask
= LED2_BLINK_EN
;
187 sprintf(data
->name
, "led1-green");
190 data
->blink_mask
= LED2_BLINK_EN
;
191 sprintf(data
->name
, "led1-blue");
195 data
->i2c
= (chip
->id
== CHIP_PM8606
) ? chip
->client
: chip
->companion
;
196 data
->port
= pdev
->id
;
197 if (pm860x_led_dt_init(pdev
, data
))
199 data
->iset
= pdata
->iset
;
201 data
->current_brightness
= 0;
202 data
->cdev
.name
= data
->name
;
203 data
->cdev
.brightness_set_blocking
= pm860x_led_set
;
204 mutex_init(&data
->lock
);
206 ret
= led_classdev_register(chip
->dev
, &data
->cdev
);
208 dev_err(&pdev
->dev
, "Failed to register LED: %d\n", ret
);
211 pm860x_led_set(&data
->cdev
, 0);
213 platform_set_drvdata(pdev
, data
);
218 static int pm860x_led_remove(struct platform_device
*pdev
)
220 struct pm860x_led
*data
= platform_get_drvdata(pdev
);
222 led_classdev_unregister(&data
->cdev
);
227 static struct platform_driver pm860x_led_driver
= {
229 .name
= "88pm860x-led",
231 .probe
= pm860x_led_probe
,
232 .remove
= pm860x_led_remove
,
235 module_platform_driver(pm860x_led_driver
);
237 MODULE_DESCRIPTION("LED driver for Marvell PM860x");
238 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
239 MODULE_LICENSE("GPL");
240 MODULE_ALIAS("platform:88pm860x-led");