Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/leds/leds-88pm860x.c
15109 views
1
/*
2
* LED driver for Marvell 88PM860x
3
*
4
* Copyright (C) 2009 Marvell International Ltd.
5
* Haojian Zhuang <[email protected]>
6
*
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.
10
*
11
*/
12
13
#include <linux/kernel.h>
14
#include <linux/init.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
22
#define LED_PWM_SHIFT (3)
23
#define LED_PWM_MASK (0x1F)
24
#define LED_CURRENT_MASK (0x07 << 5)
25
26
#define LED_BLINK_ON_MASK (0x07)
27
#define LED_BLINK_MASK (0x7F)
28
29
#define LED_BLINK_ON(x) ((x & 0x7) * 66 + 66)
30
#define LED_BLINK_ON_MIN LED_BLINK_ON(0)
31
#define LED_BLINK_ON_MAX LED_BLINK_ON(0x7)
32
#define LED_ON_CONTINUOUS (0x0F << 3)
33
#define LED_TO_ON(x) ((x - 66) / 66)
34
35
#define LED1_BLINK_EN (1 << 1)
36
#define LED2_BLINK_EN (1 << 2)
37
38
struct pm860x_led {
39
struct led_classdev cdev;
40
struct i2c_client *i2c;
41
struct work_struct work;
42
struct pm860x_chip *chip;
43
struct mutex lock;
44
char name[MFD_NAME_SIZE];
45
46
int port;
47
int iset;
48
unsigned char brightness;
49
unsigned char current_brightness;
50
51
int blink_data;
52
int blink_time;
53
int blink_on;
54
int blink_off;
55
};
56
57
/* return offset of color register */
58
static inline int __led_off(int port)
59
{
60
int ret = -EINVAL;
61
62
switch (port) {
63
case PM8606_LED1_RED:
64
case PM8606_LED1_GREEN:
65
case PM8606_LED1_BLUE:
66
ret = port - PM8606_LED1_RED + PM8606_RGB1B;
67
break;
68
case PM8606_LED2_RED:
69
case PM8606_LED2_GREEN:
70
case PM8606_LED2_BLUE:
71
ret = port - PM8606_LED2_RED + PM8606_RGB2B;
72
break;
73
}
74
return ret;
75
}
76
77
/* return offset of blink register */
78
static inline int __blink_off(int port)
79
{
80
int ret = -EINVAL;
81
82
switch (port) {
83
case PM8606_LED1_RED:
84
case PM8606_LED1_GREEN:
85
case PM8606_LED1_BLUE:
86
ret = PM8606_RGB1A;
87
break;
88
case PM8606_LED2_RED:
89
case PM8606_LED2_GREEN:
90
case PM8606_LED2_BLUE:
91
ret = PM8606_RGB2A;
92
break;
93
}
94
return ret;
95
}
96
97
static inline int __blink_ctl_mask(int port)
98
{
99
int ret = -EINVAL;
100
101
switch (port) {
102
case PM8606_LED1_RED:
103
case PM8606_LED1_GREEN:
104
case PM8606_LED1_BLUE:
105
ret = LED1_BLINK_EN;
106
break;
107
case PM8606_LED2_RED:
108
case PM8606_LED2_GREEN:
109
case PM8606_LED2_BLUE:
110
ret = LED2_BLINK_EN;
111
break;
112
}
113
return ret;
114
}
115
116
static void pm860x_led_work(struct work_struct *work)
117
{
118
119
struct pm860x_led *led;
120
struct pm860x_chip *chip;
121
unsigned char buf[3];
122
int mask, ret;
123
124
led = container_of(work, struct pm860x_led, work);
125
chip = led->chip;
126
mutex_lock(&led->lock);
127
if ((led->current_brightness == 0) && led->brightness) {
128
if (led->iset) {
129
pm860x_set_bits(led->i2c, __led_off(led->port),
130
LED_CURRENT_MASK, led->iset);
131
}
132
pm860x_set_bits(led->i2c, __blink_off(led->port),
133
LED_BLINK_MASK, LED_ON_CONTINUOUS);
134
mask = __blink_ctl_mask(led->port);
135
pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask);
136
}
137
pm860x_set_bits(led->i2c, __led_off(led->port), LED_PWM_MASK,
138
led->brightness);
139
140
if (led->brightness == 0) {
141
pm860x_bulk_read(led->i2c, __led_off(led->port), 3, buf);
142
ret = buf[0] & LED_PWM_MASK;
143
ret |= buf[1] & LED_PWM_MASK;
144
ret |= buf[2] & LED_PWM_MASK;
145
if (ret == 0) {
146
/* unset current since no led is lighting */
147
pm860x_set_bits(led->i2c, __led_off(led->port),
148
LED_CURRENT_MASK, 0);
149
mask = __blink_ctl_mask(led->port);
150
pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0);
151
}
152
}
153
led->current_brightness = led->brightness;
154
dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
155
__led_off(led->port), led->brightness);
156
mutex_unlock(&led->lock);
157
}
158
159
static void pm860x_led_set(struct led_classdev *cdev,
160
enum led_brightness value)
161
{
162
struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev);
163
164
data->brightness = value >> 3;
165
schedule_work(&data->work);
166
}
167
168
static int pm860x_led_probe(struct platform_device *pdev)
169
{
170
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
171
struct pm860x_led_pdata *pdata;
172
struct pm860x_led *data;
173
struct resource *res;
174
int ret;
175
176
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
177
if (res == NULL) {
178
dev_err(&pdev->dev, "No I/O resource!\n");
179
return -EINVAL;
180
}
181
182
pdata = pdev->dev.platform_data;
183
if (pdata == NULL) {
184
dev_err(&pdev->dev, "No platform data!\n");
185
return -EINVAL;
186
}
187
188
data = kzalloc(sizeof(struct pm860x_led), GFP_KERNEL);
189
if (data == NULL)
190
return -ENOMEM;
191
strncpy(data->name, res->name, MFD_NAME_SIZE - 1);
192
dev_set_drvdata(&pdev->dev, data);
193
data->chip = chip;
194
data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion;
195
data->iset = pdata->iset;
196
data->port = pdata->flags;
197
if (data->port < 0) {
198
dev_err(&pdev->dev, "check device failed\n");
199
kfree(data);
200
return -EINVAL;
201
}
202
203
data->current_brightness = 0;
204
data->cdev.name = data->name;
205
data->cdev.brightness_set = pm860x_led_set;
206
mutex_init(&data->lock);
207
INIT_WORK(&data->work, pm860x_led_work);
208
209
ret = led_classdev_register(chip->dev, &data->cdev);
210
if (ret < 0) {
211
dev_err(&pdev->dev, "Failed to register LED: %d\n", ret);
212
goto out;
213
}
214
pm860x_led_set(&data->cdev, 0);
215
return 0;
216
out:
217
kfree(data);
218
return ret;
219
}
220
221
static int pm860x_led_remove(struct platform_device *pdev)
222
{
223
struct pm860x_led *data = platform_get_drvdata(pdev);
224
225
led_classdev_unregister(&data->cdev);
226
kfree(data);
227
228
return 0;
229
}
230
231
static struct platform_driver pm860x_led_driver = {
232
.driver = {
233
.name = "88pm860x-led",
234
.owner = THIS_MODULE,
235
},
236
.probe = pm860x_led_probe,
237
.remove = pm860x_led_remove,
238
};
239
240
static int __devinit pm860x_led_init(void)
241
{
242
return platform_driver_register(&pm860x_led_driver);
243
}
244
module_init(pm860x_led_init);
245
246
static void __devexit pm860x_led_exit(void)
247
{
248
platform_driver_unregister(&pm860x_led_driver);
249
}
250
module_exit(pm860x_led_exit);
251
252
MODULE_DESCRIPTION("LED driver for Marvell PM860x");
253
MODULE_AUTHOR("Haojian Zhuang <[email protected]>");
254
MODULE_LICENSE("GPL");
255
MODULE_ALIAS("platform:88pm860x-led");
256
257