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