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