]>
Commit | Line | Data |
---|---|---|
75cb2e1d PM |
1 | /* |
2 | * Copyright 2011 bct electronic GmbH | |
3 | * | |
4 | * Author: Peter Meerwald <p.meerwald@bct-electronic.com> | |
5 | * | |
6 | * Based on leds-pca955x.c | |
7 | * | |
8 | * This file is subject to the terms and conditions of version 2 of | |
9 | * the GNU General Public License. See the file COPYING in the main | |
10 | * directory of this archive for more details. | |
11 | * | |
12 | * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62) | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <linux/module.h> | |
17 | #include <linux/delay.h> | |
18 | #include <linux/string.h> | |
19 | #include <linux/ctype.h> | |
20 | #include <linux/leds.h> | |
21 | #include <linux/err.h> | |
22 | #include <linux/i2c.h> | |
23 | #include <linux/workqueue.h> | |
24 | #include <linux/slab.h> | |
81d22878 | 25 | #include <linux/of.h> |
2f73c392 | 26 | #include <linux/platform_data/leds-pca9633.h> |
75cb2e1d PM |
27 | |
28 | /* LED select registers determine the source that drives LED outputs */ | |
29 | #define PCA9633_LED_OFF 0x0 /* LED driver off */ | |
30 | #define PCA9633_LED_ON 0x1 /* LED driver on */ | |
31 | #define PCA9633_LED_PWM 0x2 /* Controlled through PWM */ | |
32 | #define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ | |
33 | ||
34 | #define PCA9633_MODE1 0x00 | |
35 | #define PCA9633_MODE2 0x01 | |
36 | #define PCA9633_PWM_BASE 0x02 | |
37 | #define PCA9633_LEDOUT 0x08 | |
38 | ||
39 | static const struct i2c_device_id pca9633_id[] = { | |
40 | { "pca9633", 0 }, | |
41 | { } | |
42 | }; | |
43 | MODULE_DEVICE_TABLE(i2c, pca9633_id); | |
44 | ||
45 | struct pca9633_led { | |
46 | struct i2c_client *client; | |
47 | struct work_struct work; | |
48 | enum led_brightness brightness; | |
49 | struct led_classdev led_cdev; | |
50 | int led_num; /* 0 .. 3 potentially */ | |
51 | char name[32]; | |
52 | }; | |
53 | ||
54 | static void pca9633_led_work(struct work_struct *work) | |
55 | { | |
56 | struct pca9633_led *pca9633 = container_of(work, | |
57 | struct pca9633_led, work); | |
58 | u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT); | |
59 | int shift = 2 * pca9633->led_num; | |
60 | u8 mask = 0x3 << shift; | |
61 | ||
62 | switch (pca9633->brightness) { | |
63 | case LED_FULL: | |
64 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, | |
65 | (ledout & ~mask) | (PCA9633_LED_ON << shift)); | |
66 | break; | |
67 | case LED_OFF: | |
68 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, | |
69 | ledout & ~mask); | |
70 | break; | |
71 | default: | |
72 | i2c_smbus_write_byte_data(pca9633->client, | |
73 | PCA9633_PWM_BASE + pca9633->led_num, | |
74 | pca9633->brightness); | |
75 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, | |
76 | (ledout & ~mask) | (PCA9633_LED_PWM << shift)); | |
77 | break; | |
78 | } | |
79 | } | |
80 | ||
81 | static void pca9633_led_set(struct led_classdev *led_cdev, | |
82 | enum led_brightness value) | |
83 | { | |
84 | struct pca9633_led *pca9633; | |
85 | ||
86 | pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); | |
87 | ||
88 | pca9633->brightness = value; | |
89 | ||
90 | /* | |
91 | * Must use workqueue for the actual I/O since I2C operations | |
92 | * can sleep. | |
93 | */ | |
94 | schedule_work(&pca9633->work); | |
95 | } | |
96 | ||
81d22878 TL |
97 | #if IS_ENABLED(CONFIG_OF) |
98 | static struct pca9633_platform_data * | |
99 | pca9633_dt_init(struct i2c_client *client) | |
100 | { | |
101 | struct device_node *np = client->dev.of_node, *child; | |
102 | struct pca9633_platform_data *pdata; | |
103 | struct led_info *pca9633_leds; | |
104 | int count; | |
105 | ||
106 | count = of_get_child_count(np); | |
107 | if (!count || count > 4) | |
108 | return ERR_PTR(-ENODEV); | |
109 | ||
110 | pca9633_leds = devm_kzalloc(&client->dev, | |
111 | sizeof(struct led_info) * count, GFP_KERNEL); | |
112 | if (!pca9633_leds) | |
113 | return ERR_PTR(-ENOMEM); | |
114 | ||
115 | for_each_child_of_node(np, child) { | |
116 | struct led_info led; | |
117 | u32 reg; | |
118 | int res; | |
119 | ||
120 | led.name = | |
121 | of_get_property(child, "label", NULL) ? : child->name; | |
122 | led.default_trigger = | |
123 | of_get_property(child, "linux,default-trigger", NULL); | |
124 | res = of_property_read_u32(child, "reg", ®); | |
125 | if (res != 0) | |
126 | continue; | |
127 | pca9633_leds[reg] = led; | |
128 | } | |
129 | pdata = devm_kzalloc(&client->dev, | |
130 | sizeof(struct pca9633_platform_data), GFP_KERNEL); | |
131 | if (!pdata) | |
132 | return ERR_PTR(-ENOMEM); | |
133 | ||
134 | pdata->leds.leds = pca9633_leds; | |
135 | pdata->leds.num_leds = count; | |
136 | ||
137 | /* default to open-drain unless totem pole (push-pull) is specified */ | |
138 | if (of_property_read_bool(np, "nxp,totem-pole")) | |
139 | pdata->outdrv = PCA9633_TOTEM_POLE; | |
140 | else | |
141 | pdata->outdrv = PCA9633_OPEN_DRAIN; | |
142 | ||
143 | return pdata; | |
144 | } | |
145 | ||
146 | static const struct of_device_id of_pca9633_match[] = { | |
147 | { .compatible = "nxp,pca963x", }, | |
148 | {}, | |
149 | }; | |
150 | #else | |
151 | static struct pca9633_platform_data * | |
152 | pca9633_dt_init(struct i2c_client *client) | |
153 | { | |
154 | return ERR_PTR(-ENODEV); | |
155 | } | |
156 | #endif | |
157 | ||
98ea1ea2 | 158 | static int pca9633_probe(struct i2c_client *client, |
75cb2e1d PM |
159 | const struct i2c_device_id *id) |
160 | { | |
161 | struct pca9633_led *pca9633; | |
2f73c392 | 162 | struct pca9633_platform_data *pdata; |
75cb2e1d PM |
163 | int i, err; |
164 | ||
75cb2e1d PM |
165 | pdata = client->dev.platform_data; |
166 | ||
81d22878 TL |
167 | if (!pdata) { |
168 | pdata = pca9633_dt_init(client); | |
169 | if (IS_ERR(pdata)) { | |
170 | dev_warn(&client->dev, "could not parse configuration\n"); | |
171 | pdata = NULL; | |
172 | } | |
173 | } | |
174 | ||
75cb2e1d | 175 | if (pdata) { |
2f73c392 | 176 | if (pdata->leds.num_leds <= 0 || pdata->leds.num_leds > 4) { |
75cb2e1d PM |
177 | dev_err(&client->dev, "board info must claim at most 4 LEDs"); |
178 | return -EINVAL; | |
179 | } | |
180 | } | |
181 | ||
983ce884 | 182 | pca9633 = devm_kzalloc(&client->dev, 4 * sizeof(*pca9633), GFP_KERNEL); |
75cb2e1d PM |
183 | if (!pca9633) |
184 | return -ENOMEM; | |
185 | ||
186 | i2c_set_clientdata(client, pca9633); | |
187 | ||
188 | for (i = 0; i < 4; i++) { | |
189 | pca9633[i].client = client; | |
190 | pca9633[i].led_num = i; | |
191 | ||
192 | /* Platform data can specify LED names and default triggers */ | |
2f73c392 PM |
193 | if (pdata && i < pdata->leds.num_leds) { |
194 | if (pdata->leds.leds[i].name) | |
75cb2e1d PM |
195 | snprintf(pca9633[i].name, |
196 | sizeof(pca9633[i].name), "pca9633:%s", | |
2f73c392 PM |
197 | pdata->leds.leds[i].name); |
198 | if (pdata->leds.leds[i].default_trigger) | |
75cb2e1d | 199 | pca9633[i].led_cdev.default_trigger = |
2f73c392 | 200 | pdata->leds.leds[i].default_trigger; |
75cb2e1d PM |
201 | } else { |
202 | snprintf(pca9633[i].name, sizeof(pca9633[i].name), | |
203 | "pca9633:%d", i); | |
204 | } | |
205 | ||
206 | pca9633[i].led_cdev.name = pca9633[i].name; | |
207 | pca9633[i].led_cdev.brightness_set = pca9633_led_set; | |
208 | ||
209 | INIT_WORK(&pca9633[i].work, pca9633_led_work); | |
210 | ||
211 | err = led_classdev_register(&client->dev, &pca9633[i].led_cdev); | |
212 | if (err < 0) | |
213 | goto exit; | |
214 | } | |
215 | ||
216 | /* Disable LED all-call address and set normal mode */ | |
217 | i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00); | |
218 | ||
2f73c392 PM |
219 | /* Configure output: open-drain or totem pole (push-pull) */ |
220 | if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN) | |
221 | i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01); | |
222 | ||
75cb2e1d PM |
223 | /* Turn off LEDs */ |
224 | i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00); | |
225 | ||
226 | return 0; | |
227 | ||
228 | exit: | |
229 | while (i--) { | |
230 | led_classdev_unregister(&pca9633[i].led_cdev); | |
231 | cancel_work_sync(&pca9633[i].work); | |
232 | } | |
233 | ||
75cb2e1d PM |
234 | return err; |
235 | } | |
236 | ||
678e8a6b | 237 | static int pca9633_remove(struct i2c_client *client) |
75cb2e1d PM |
238 | { |
239 | struct pca9633_led *pca9633 = i2c_get_clientdata(client); | |
240 | int i; | |
241 | ||
242 | for (i = 0; i < 4; i++) { | |
243 | led_classdev_unregister(&pca9633[i].led_cdev); | |
244 | cancel_work_sync(&pca9633[i].work); | |
245 | } | |
246 | ||
75cb2e1d PM |
247 | return 0; |
248 | } | |
249 | ||
250 | static struct i2c_driver pca9633_driver = { | |
251 | .driver = { | |
252 | .name = "leds-pca9633", | |
253 | .owner = THIS_MODULE, | |
81d22878 | 254 | .of_match_table = of_match_ptr(of_pca9633_match), |
75cb2e1d PM |
255 | }, |
256 | .probe = pca9633_probe, | |
df07cf81 | 257 | .remove = pca9633_remove, |
75cb2e1d PM |
258 | .id_table = pca9633_id, |
259 | }; | |
260 | ||
261 | module_i2c_driver(pca9633_driver); | |
262 | ||
263 | MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>"); | |
264 | MODULE_DESCRIPTION("PCA9633 LED driver"); | |
265 | MODULE_LICENSE("GPL v2"); |