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