Commit | Line | Data |
---|---|---|
b4ecd326 RV |
1 | /* |
2 | * Copyright (C) ST-Ericsson SA 2010 | |
3 | * | |
4 | * License Terms: GNU General Public License, version 2 | |
5 | * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson | |
6 | * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson | |
7 | */ | |
8 | ||
9 | #include <linux/module.h> | |
10 | #include <linux/interrupt.h> | |
11 | #include <linux/irq.h> | |
15e27b10 | 12 | #include <linux/irqdomain.h> |
b4ecd326 RV |
13 | #include <linux/slab.h> |
14 | #include <linux/i2c.h> | |
a435ae1d | 15 | #include <linux/of.h> |
b4ecd326 | 16 | #include <linux/mfd/core.h> |
c6eda6c5 | 17 | #include <linux/mfd/tc3589x.h> |
b4ecd326 | 18 | |
e64c1eb4 LW |
19 | /** |
20 | * enum tc3589x_version - indicates the TC3589x version | |
21 | */ | |
22 | enum tc3589x_version { | |
23 | TC3589X_TC35890, | |
24 | TC3589X_TC35892, | |
25 | TC3589X_TC35893, | |
26 | TC3589X_TC35894, | |
27 | TC3589X_TC35895, | |
28 | TC3589X_TC35896, | |
29 | TC3589X_UNKNOWN, | |
30 | }; | |
31 | ||
593e9d70 SI |
32 | #define TC3589x_CLKMODE_MODCTL_SLEEP 0x0 |
33 | #define TC3589x_CLKMODE_MODCTL_OPERATION (1 << 0) | |
34 | ||
b4ecd326 | 35 | /** |
20406ebf SI |
36 | * tc3589x_reg_read() - read a single TC3589x register |
37 | * @tc3589x: Device to read from | |
b4ecd326 RV |
38 | * @reg: Register to read |
39 | */ | |
20406ebf | 40 | int tc3589x_reg_read(struct tc3589x *tc3589x, u8 reg) |
b4ecd326 RV |
41 | { |
42 | int ret; | |
43 | ||
20406ebf | 44 | ret = i2c_smbus_read_byte_data(tc3589x->i2c, reg); |
b4ecd326 | 45 | if (ret < 0) |
20406ebf | 46 | dev_err(tc3589x->dev, "failed to read reg %#x: %d\n", |
b4ecd326 RV |
47 | reg, ret); |
48 | ||
49 | return ret; | |
50 | } | |
20406ebf | 51 | EXPORT_SYMBOL_GPL(tc3589x_reg_read); |
b4ecd326 RV |
52 | |
53 | /** | |
20406ebf SI |
54 | * tc3589x_reg_read() - write a single TC3589x register |
55 | * @tc3589x: Device to write to | |
b4ecd326 RV |
56 | * @reg: Register to read |
57 | * @data: Value to write | |
58 | */ | |
20406ebf | 59 | int tc3589x_reg_write(struct tc3589x *tc3589x, u8 reg, u8 data) |
b4ecd326 RV |
60 | { |
61 | int ret; | |
62 | ||
20406ebf | 63 | ret = i2c_smbus_write_byte_data(tc3589x->i2c, reg, data); |
b4ecd326 | 64 | if (ret < 0) |
20406ebf | 65 | dev_err(tc3589x->dev, "failed to write reg %#x: %d\n", |
b4ecd326 RV |
66 | reg, ret); |
67 | ||
68 | return ret; | |
69 | } | |
20406ebf | 70 | EXPORT_SYMBOL_GPL(tc3589x_reg_write); |
b4ecd326 RV |
71 | |
72 | /** | |
20406ebf SI |
73 | * tc3589x_block_read() - read multiple TC3589x registers |
74 | * @tc3589x: Device to read from | |
b4ecd326 RV |
75 | * @reg: First register |
76 | * @length: Number of registers | |
77 | * @values: Buffer to write to | |
78 | */ | |
20406ebf | 79 | int tc3589x_block_read(struct tc3589x *tc3589x, u8 reg, u8 length, u8 *values) |
b4ecd326 RV |
80 | { |
81 | int ret; | |
82 | ||
20406ebf | 83 | ret = i2c_smbus_read_i2c_block_data(tc3589x->i2c, reg, length, values); |
b4ecd326 | 84 | if (ret < 0) |
20406ebf | 85 | dev_err(tc3589x->dev, "failed to read regs %#x: %d\n", |
b4ecd326 RV |
86 | reg, ret); |
87 | ||
88 | return ret; | |
89 | } | |
20406ebf | 90 | EXPORT_SYMBOL_GPL(tc3589x_block_read); |
b4ecd326 RV |
91 | |
92 | /** | |
20406ebf SI |
93 | * tc3589x_block_write() - write multiple TC3589x registers |
94 | * @tc3589x: Device to write to | |
b4ecd326 RV |
95 | * @reg: First register |
96 | * @length: Number of registers | |
97 | * @values: Values to write | |
98 | */ | |
20406ebf | 99 | int tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length, |
b4ecd326 RV |
100 | const u8 *values) |
101 | { | |
102 | int ret; | |
103 | ||
20406ebf | 104 | ret = i2c_smbus_write_i2c_block_data(tc3589x->i2c, reg, length, |
b4ecd326 RV |
105 | values); |
106 | if (ret < 0) | |
20406ebf | 107 | dev_err(tc3589x->dev, "failed to write regs %#x: %d\n", |
b4ecd326 RV |
108 | reg, ret); |
109 | ||
110 | return ret; | |
111 | } | |
20406ebf | 112 | EXPORT_SYMBOL_GPL(tc3589x_block_write); |
b4ecd326 RV |
113 | |
114 | /** | |
20406ebf SI |
115 | * tc3589x_set_bits() - set the value of a bitfield in a TC3589x register |
116 | * @tc3589x: Device to write to | |
b4ecd326 RV |
117 | * @reg: Register to write |
118 | * @mask: Mask of bits to set | |
119 | * @values: Value to set | |
120 | */ | |
20406ebf | 121 | int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val) |
b4ecd326 RV |
122 | { |
123 | int ret; | |
124 | ||
20406ebf | 125 | mutex_lock(&tc3589x->lock); |
b4ecd326 | 126 | |
20406ebf | 127 | ret = tc3589x_reg_read(tc3589x, reg); |
b4ecd326 RV |
128 | if (ret < 0) |
129 | goto out; | |
130 | ||
131 | ret &= ~mask; | |
132 | ret |= val; | |
133 | ||
20406ebf | 134 | ret = tc3589x_reg_write(tc3589x, reg, ret); |
b4ecd326 RV |
135 | |
136 | out: | |
20406ebf | 137 | mutex_unlock(&tc3589x->lock); |
b4ecd326 RV |
138 | return ret; |
139 | } | |
20406ebf | 140 | EXPORT_SYMBOL_GPL(tc3589x_set_bits); |
b4ecd326 RV |
141 | |
142 | static struct resource gpio_resources[] = { | |
143 | { | |
20406ebf SI |
144 | .start = TC3589x_INT_GPIIRQ, |
145 | .end = TC3589x_INT_GPIIRQ, | |
b4ecd326 RV |
146 | .flags = IORESOURCE_IRQ, |
147 | }, | |
148 | }; | |
149 | ||
09c730a4 SI |
150 | static struct resource keypad_resources[] = { |
151 | { | |
152 | .start = TC3589x_INT_KBDIRQ, | |
153 | .end = TC3589x_INT_KBDIRQ, | |
154 | .flags = IORESOURCE_IRQ, | |
155 | }, | |
156 | }; | |
157 | ||
611b7590 | 158 | static struct mfd_cell tc3589x_dev_gpio[] = { |
b4ecd326 | 159 | { |
20406ebf | 160 | .name = "tc3589x-gpio", |
b4ecd326 RV |
161 | .num_resources = ARRAY_SIZE(gpio_resources), |
162 | .resources = &gpio_resources[0], | |
a435ae1d | 163 | .of_compatible = "tc3589x-gpio", |
b4ecd326 RV |
164 | }, |
165 | }; | |
166 | ||
09c730a4 SI |
167 | static struct mfd_cell tc3589x_dev_keypad[] = { |
168 | { | |
169 | .name = "tc3589x-keypad", | |
170 | .num_resources = ARRAY_SIZE(keypad_resources), | |
171 | .resources = &keypad_resources[0], | |
a435ae1d | 172 | .of_compatible = "tc3589x-keypad", |
09c730a4 SI |
173 | }, |
174 | }; | |
175 | ||
20406ebf | 176 | static irqreturn_t tc3589x_irq(int irq, void *data) |
b4ecd326 | 177 | { |
20406ebf | 178 | struct tc3589x *tc3589x = data; |
b4ecd326 RV |
179 | int status; |
180 | ||
bd77efd0 | 181 | again: |
20406ebf | 182 | status = tc3589x_reg_read(tc3589x, TC3589x_IRQST); |
b4ecd326 RV |
183 | if (status < 0) |
184 | return IRQ_NONE; | |
185 | ||
186 | while (status) { | |
187 | int bit = __ffs(status); | |
15e27b10 | 188 | int virq = irq_create_mapping(tc3589x->domain, bit); |
b4ecd326 | 189 | |
15e27b10 | 190 | handle_nested_irq(virq); |
b4ecd326 RV |
191 | status &= ~(1 << bit); |
192 | } | |
193 | ||
194 | /* | |
195 | * A dummy read or write (to any register) appears to be necessary to | |
196 | * have the last interrupt clear (for example, GPIO IC write) take | |
bd77efd0 SI |
197 | * effect. In such a case, recheck for any interrupt which is still |
198 | * pending. | |
b4ecd326 | 199 | */ |
bd77efd0 SI |
200 | status = tc3589x_reg_read(tc3589x, TC3589x_IRQST); |
201 | if (status) | |
202 | goto again; | |
b4ecd326 RV |
203 | |
204 | return IRQ_HANDLED; | |
205 | } | |
206 | ||
15e27b10 LJ |
207 | static int tc3589x_irq_map(struct irq_domain *d, unsigned int virq, |
208 | irq_hw_number_t hwirq) | |
b4ecd326 | 209 | { |
15e27b10 | 210 | struct tc3589x *tc3589x = d->host_data; |
b4ecd326 | 211 | |
15e27b10 LJ |
212 | irq_set_chip_data(virq, tc3589x); |
213 | irq_set_chip_and_handler(virq, &dummy_irq_chip, | |
214 | handle_edge_irq); | |
215 | irq_set_nested_thread(virq, 1); | |
b4ecd326 | 216 | #ifdef CONFIG_ARM |
15e27b10 | 217 | set_irq_flags(virq, IRQF_VALID); |
b4ecd326 | 218 | #else |
15e27b10 | 219 | irq_set_noprobe(virq); |
b4ecd326 | 220 | #endif |
b4ecd326 RV |
221 | |
222 | return 0; | |
223 | } | |
224 | ||
15e27b10 | 225 | static void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq) |
b4ecd326 | 226 | { |
b4ecd326 | 227 | #ifdef CONFIG_ARM |
15e27b10 | 228 | set_irq_flags(virq, 0); |
b4ecd326 | 229 | #endif |
15e27b10 LJ |
230 | irq_set_chip_and_handler(virq, NULL, NULL); |
231 | irq_set_chip_data(virq, NULL); | |
232 | } | |
233 | ||
234 | static struct irq_domain_ops tc3589x_irq_ops = { | |
1f0529b4 | 235 | .map = tc3589x_irq_map, |
15e27b10 | 236 | .unmap = tc3589x_irq_unmap, |
1f0529b4 | 237 | .xlate = irq_domain_xlate_twocell, |
15e27b10 LJ |
238 | }; |
239 | ||
a435ae1d | 240 | static int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np) |
15e27b10 LJ |
241 | { |
242 | int base = tc3589x->irq_base; | |
243 | ||
1f0529b4 LW |
244 | tc3589x->domain = irq_domain_add_simple( |
245 | np, TC3589x_NR_INTERNAL_IRQS, base, | |
246 | &tc3589x_irq_ops, tc3589x); | |
15e27b10 LJ |
247 | |
248 | if (!tc3589x->domain) { | |
249 | dev_err(tc3589x->dev, "Failed to create irqdomain\n"); | |
250 | return -ENOSYS; | |
251 | } | |
252 | ||
253 | return 0; | |
b4ecd326 RV |
254 | } |
255 | ||
20406ebf | 256 | static int tc3589x_chip_init(struct tc3589x *tc3589x) |
b4ecd326 RV |
257 | { |
258 | int manf, ver, ret; | |
259 | ||
20406ebf | 260 | manf = tc3589x_reg_read(tc3589x, TC3589x_MANFCODE); |
b4ecd326 RV |
261 | if (manf < 0) |
262 | return manf; | |
263 | ||
20406ebf | 264 | ver = tc3589x_reg_read(tc3589x, TC3589x_VERSION); |
b4ecd326 RV |
265 | if (ver < 0) |
266 | return ver; | |
267 | ||
20406ebf SI |
268 | if (manf != TC3589x_MANFCODE_MAGIC) { |
269 | dev_err(tc3589x->dev, "unknown manufacturer: %#x\n", manf); | |
b4ecd326 RV |
270 | return -EINVAL; |
271 | } | |
272 | ||
20406ebf | 273 | dev_info(tc3589x->dev, "manufacturer: %#x, version: %#x\n", manf, ver); |
b4ecd326 | 274 | |
523bc382 SI |
275 | /* |
276 | * Put everything except the IRQ module into reset; | |
277 | * also spare the GPIO module for any pin initialization | |
278 | * done during pre-kernel boot | |
279 | */ | |
20406ebf SI |
280 | ret = tc3589x_reg_write(tc3589x, TC3589x_RSTCTRL, |
281 | TC3589x_RSTCTRL_TIMRST | |
282 | | TC3589x_RSTCTRL_ROTRST | |
523bc382 | 283 | | TC3589x_RSTCTRL_KBDRST); |
b4ecd326 RV |
284 | if (ret < 0) |
285 | return ret; | |
286 | ||
287 | /* Clear the reset interrupt. */ | |
20406ebf | 288 | return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1); |
b4ecd326 RV |
289 | } |
290 | ||
f791be49 | 291 | static int tc3589x_device_init(struct tc3589x *tc3589x) |
611b7590 SI |
292 | { |
293 | int ret = 0; | |
294 | unsigned int blocks = tc3589x->pdata->block; | |
295 | ||
296 | if (blocks & TC3589x_BLOCK_GPIO) { | |
297 | ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio, | |
55692af5 | 298 | ARRAY_SIZE(tc3589x_dev_gpio), NULL, |
15e27b10 | 299 | tc3589x->irq_base, tc3589x->domain); |
611b7590 SI |
300 | if (ret) { |
301 | dev_err(tc3589x->dev, "failed to add gpio child\n"); | |
302 | return ret; | |
303 | } | |
304 | dev_info(tc3589x->dev, "added gpio block\n"); | |
305 | } | |
306 | ||
09c730a4 SI |
307 | if (blocks & TC3589x_BLOCK_KEYPAD) { |
308 | ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad, | |
55692af5 | 309 | ARRAY_SIZE(tc3589x_dev_keypad), NULL, |
15e27b10 | 310 | tc3589x->irq_base, tc3589x->domain); |
09c730a4 SI |
311 | if (ret) { |
312 | dev_err(tc3589x->dev, "failed to keypad child\n"); | |
313 | return ret; | |
314 | } | |
315 | dev_info(tc3589x->dev, "added keypad block\n"); | |
316 | } | |
611b7590 | 317 | |
09c730a4 | 318 | return ret; |
611b7590 SI |
319 | } |
320 | ||
a435ae1d LJ |
321 | static int tc3589x_of_probe(struct device_node *np, |
322 | struct tc3589x_platform_data *pdata) | |
323 | { | |
324 | struct device_node *child; | |
325 | ||
326 | for_each_child_of_node(np, child) { | |
327 | if (!strcmp(child->name, "tc3589x_gpio")) { | |
328 | pdata->block |= TC3589x_BLOCK_GPIO; | |
329 | } | |
330 | if (!strcmp(child->name, "tc3589x_keypad")) { | |
331 | pdata->block |= TC3589x_BLOCK_KEYPAD; | |
332 | } | |
333 | } | |
334 | ||
335 | return 0; | |
336 | } | |
337 | ||
f791be49 | 338 | static int tc3589x_probe(struct i2c_client *i2c, |
b4ecd326 RV |
339 | const struct i2c_device_id *id) |
340 | { | |
334a41ce | 341 | struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev); |
a435ae1d | 342 | struct device_node *np = i2c->dev.of_node; |
20406ebf | 343 | struct tc3589x *tc3589x; |
b4ecd326 RV |
344 | int ret; |
345 | ||
a435ae1d LJ |
346 | if (!pdata) { |
347 | if (np) { | |
348 | pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL); | |
349 | if (!pdata) | |
350 | return -ENOMEM; | |
351 | ||
352 | ret = tc3589x_of_probe(np, pdata); | |
353 | if (ret) | |
354 | return ret; | |
355 | } | |
356 | else { | |
357 | dev_err(&i2c->dev, "No platform data or DT found\n"); | |
358 | return -EINVAL; | |
359 | } | |
360 | } | |
361 | ||
b4ecd326 RV |
362 | if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
363 | | I2C_FUNC_SMBUS_I2C_BLOCK)) | |
364 | return -EIO; | |
365 | ||
1383e00f JH |
366 | tc3589x = devm_kzalloc(&i2c->dev, sizeof(struct tc3589x), |
367 | GFP_KERNEL); | |
20406ebf | 368 | if (!tc3589x) |
b4ecd326 RV |
369 | return -ENOMEM; |
370 | ||
20406ebf | 371 | mutex_init(&tc3589x->lock); |
b4ecd326 | 372 | |
20406ebf SI |
373 | tc3589x->dev = &i2c->dev; |
374 | tc3589x->i2c = i2c; | |
375 | tc3589x->pdata = pdata; | |
376 | tc3589x->irq_base = pdata->irq_base; | |
e64c1eb4 LW |
377 | |
378 | switch (id->driver_data) { | |
379 | case TC3589X_TC35893: | |
380 | case TC3589X_TC35895: | |
381 | case TC3589X_TC35896: | |
382 | tc3589x->num_gpio = 20; | |
383 | break; | |
384 | case TC3589X_TC35890: | |
385 | case TC3589X_TC35892: | |
386 | case TC3589X_TC35894: | |
387 | case TC3589X_UNKNOWN: | |
388 | default: | |
389 | tc3589x->num_gpio = 24; | |
390 | break; | |
391 | } | |
b4ecd326 | 392 | |
20406ebf | 393 | i2c_set_clientdata(i2c, tc3589x); |
b4ecd326 | 394 | |
20406ebf | 395 | ret = tc3589x_chip_init(tc3589x); |
b4ecd326 | 396 | if (ret) |
1383e00f | 397 | return ret; |
b4ecd326 | 398 | |
a435ae1d | 399 | ret = tc3589x_irq_init(tc3589x, np); |
b4ecd326 | 400 | if (ret) |
1383e00f | 401 | return ret; |
b4ecd326 | 402 | |
20406ebf | 403 | ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq, |
b4ecd326 | 404 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
20406ebf | 405 | "tc3589x", tc3589x); |
b4ecd326 | 406 | if (ret) { |
20406ebf | 407 | dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret); |
1383e00f | 408 | return ret; |
b4ecd326 RV |
409 | } |
410 | ||
611b7590 | 411 | ret = tc3589x_device_init(tc3589x); |
b4ecd326 | 412 | if (ret) { |
611b7590 | 413 | dev_err(tc3589x->dev, "failed to add child devices\n"); |
1383e00f | 414 | return ret; |
b4ecd326 RV |
415 | } |
416 | ||
417 | return 0; | |
b4ecd326 RV |
418 | } |
419 | ||
4740f73f | 420 | static int tc3589x_remove(struct i2c_client *client) |
b4ecd326 | 421 | { |
20406ebf | 422 | struct tc3589x *tc3589x = i2c_get_clientdata(client); |
b4ecd326 | 423 | |
20406ebf | 424 | mfd_remove_devices(tc3589x->dev); |
b4ecd326 | 425 | |
b4ecd326 RV |
426 | return 0; |
427 | } | |
428 | ||
930bf022 | 429 | #ifdef CONFIG_PM_SLEEP |
593e9d70 SI |
430 | static int tc3589x_suspend(struct device *dev) |
431 | { | |
432 | struct tc3589x *tc3589x = dev_get_drvdata(dev); | |
433 | struct i2c_client *client = tc3589x->i2c; | |
434 | int ret = 0; | |
435 | ||
436 | /* put the system to sleep mode */ | |
437 | if (!device_may_wakeup(&client->dev)) | |
438 | ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE, | |
439 | TC3589x_CLKMODE_MODCTL_SLEEP); | |
440 | ||
441 | return ret; | |
442 | } | |
443 | ||
444 | static int tc3589x_resume(struct device *dev) | |
445 | { | |
446 | struct tc3589x *tc3589x = dev_get_drvdata(dev); | |
447 | struct i2c_client *client = tc3589x->i2c; | |
448 | int ret = 0; | |
449 | ||
450 | /* enable the system into operation */ | |
451 | if (!device_may_wakeup(&client->dev)) | |
452 | ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE, | |
453 | TC3589x_CLKMODE_MODCTL_OPERATION); | |
454 | ||
455 | return ret; | |
456 | } | |
54d8e2c3 | 457 | #endif |
593e9d70 | 458 | |
930bf022 AL |
459 | static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume); |
460 | ||
20406ebf | 461 | static const struct i2c_device_id tc3589x_id[] = { |
e64c1eb4 LW |
462 | { "tc35890", TC3589X_TC35890 }, |
463 | { "tc35892", TC3589X_TC35892 }, | |
464 | { "tc35893", TC3589X_TC35893 }, | |
465 | { "tc35894", TC3589X_TC35894 }, | |
466 | { "tc35895", TC3589X_TC35895 }, | |
467 | { "tc35896", TC3589X_TC35896 }, | |
468 | { "tc3589x", TC3589X_UNKNOWN }, | |
b4ecd326 RV |
469 | { } |
470 | }; | |
20406ebf | 471 | MODULE_DEVICE_TABLE(i2c, tc3589x_id); |
b4ecd326 | 472 | |
20406ebf SI |
473 | static struct i2c_driver tc3589x_driver = { |
474 | .driver.name = "tc3589x", | |
b4ecd326 | 475 | .driver.owner = THIS_MODULE, |
593e9d70 | 476 | .driver.pm = &tc3589x_dev_pm_ops, |
20406ebf | 477 | .probe = tc3589x_probe, |
84449216 | 478 | .remove = tc3589x_remove, |
20406ebf | 479 | .id_table = tc3589x_id, |
b4ecd326 RV |
480 | }; |
481 | ||
20406ebf | 482 | static int __init tc3589x_init(void) |
b4ecd326 | 483 | { |
20406ebf | 484 | return i2c_add_driver(&tc3589x_driver); |
b4ecd326 | 485 | } |
20406ebf | 486 | subsys_initcall(tc3589x_init); |
b4ecd326 | 487 | |
20406ebf | 488 | static void __exit tc3589x_exit(void) |
b4ecd326 | 489 | { |
20406ebf | 490 | i2c_del_driver(&tc3589x_driver); |
b4ecd326 | 491 | } |
20406ebf | 492 | module_exit(tc3589x_exit); |
b4ecd326 RV |
493 | |
494 | MODULE_LICENSE("GPL v2"); | |
20406ebf | 495 | MODULE_DESCRIPTION("TC3589x MFD core driver"); |
b4ecd326 | 496 | MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); |