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