aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Rapoport <mike@compulab.co.il>2010-08-10 19:11:04 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2010-08-12 05:28:12 -0400
commitc6c193326384aecfd668c8f271799a44dbc74c1a (patch)
tree58df734fad40389f64641a6f757d245b6274a93e
parent1c888e2e3824a3f7565b4d96ede423cb9a9a28b7 (diff)
mfd: Add TPS6586x driver
Add mfd core driver for TPS6586x PMICs family. The driver provides I/O access for the sub-device drivers and performs regstration of the sub-devices based on the platform requirements. In addition it implements GPIOlib interface for the chip GPIOs. TODO: - add interrupt support - add platform data for PWM, backlight leds and charger Signed-off-by: Mike Rapoport <mike@compulab.co.il> Signed-off-by: Mike Rapoport <mike.rapoport@gmail.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r--drivers/mfd/Kconfig14
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/tps6586x.c375
-rw-r--r--include/linux/mfd/tps6586x.h47
4 files changed, 437 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 23a891f396e4..d75909e7cf2f 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -531,6 +531,20 @@ config MFD_JZ4740_ADC
531 Say yes here if you want support for the ADC unit in the JZ4740 SoC. 531 Say yes here if you want support for the ADC unit in the JZ4740 SoC.
532 This driver is necessary for jz4740-battery and jz4740-hwmon driver. 532 This driver is necessary for jz4740-battery and jz4740-hwmon driver.
533 533
534config MFD_TPS6586X
535 tristate "TPS6586x Power Management chips"
536 depends on I2C && GPIOLIB
537 select MFD_CORE
538 help
539 If you say yes here you get support for the TPS6586X series of
540 Power Management chips.
541 This driver provides common support for accessing the device,
542 additional drivers must be enabled in order to use the
543 functionality of the device.
544
545 This driver can also be built as a module. If so, the module
546 will be called tps6586x.
547
534endif # MFD_SUPPORT 548endif # MFD_SUPPORT
535 549
536menu "Multimedia Capabilities Port drivers" 550menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index cc7cce0976f3..1e48d7e3e884 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -74,3 +74,4 @@ obj-$(CONFIG_LPC_SCH) += lpc_sch.o
74obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o 74obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
75obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o 75obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
76obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o 76obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o
77obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
new file mode 100644
index 000000000000..4cde31e6a252
--- /dev/null
+++ b/drivers/mfd/tps6586x.c
@@ -0,0 +1,375 @@
1/*
2 * Core driver for TI TPS6586x PMIC family
3 *
4 * Copyright (c) 2010 CompuLab Ltd.
5 * Mike Rapoport <mike@compulab.co.il>
6 *
7 * Based on da903x.c.
8 * Copyright (C) 2008 Compulab, Ltd.
9 * Mike Rapoport <mike@compulab.co.il>
10 * Copyright (C) 2006-2008 Marvell International Ltd.
11 * Eric Miao <eric.miao@marvell.com>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
16 */
17
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/mutex.h>
21#include <linux/slab.h>
22#include <linux/gpio.h>
23#include <linux/i2c.h>
24
25#include <linux/mfd/core.h>
26#include <linux/mfd/tps6586x.h>
27
28/* GPIO control registers */
29#define TPS6586X_GPIOSET1 0x5d
30#define TPS6586X_GPIOSET2 0x5e
31
32/* device id */
33#define TPS6586X_VERSIONCRC 0xcd
34#define TPS658621A_VERSIONCRC 0x15
35
36struct tps6586x {
37 struct mutex lock;
38 struct device *dev;
39 struct i2c_client *client;
40
41 struct gpio_chip gpio;
42};
43
44static inline int __tps6586x_read(struct i2c_client *client,
45 int reg, uint8_t *val)
46{
47 int ret;
48
49 ret = i2c_smbus_read_byte_data(client, reg);
50 if (ret < 0) {
51 dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
52 return ret;
53 }
54
55 *val = (uint8_t)ret;
56
57 return 0;
58}
59
60static inline int __tps6586x_reads(struct i2c_client *client, int reg,
61 int len, uint8_t *val)
62{
63 int ret;
64
65 ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
66 if (ret < 0) {
67 dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
68 return ret;
69 }
70
71 return 0;
72}
73
74static inline int __tps6586x_write(struct i2c_client *client,
75 int reg, uint8_t val)
76{
77 int ret;
78
79 ret = i2c_smbus_write_byte_data(client, reg, val);
80 if (ret < 0) {
81 dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
82 val, reg);
83 return ret;
84 }
85
86 return 0;
87}
88
89static inline int __tps6586x_writes(struct i2c_client *client, int reg,
90 int len, uint8_t *val)
91{
92 int ret;
93
94 ret = i2c_smbus_write_i2c_block_data(client, reg, len, val);
95 if (ret < 0) {
96 dev_err(&client->dev, "failed writings to 0x%02x\n", reg);
97 return ret;
98 }
99
100 return 0;
101}
102
103int tps6586x_write(struct device *dev, int reg, uint8_t val)
104{
105 return __tps6586x_write(to_i2c_client(dev), reg, val);
106}
107EXPORT_SYMBOL_GPL(tps6586x_write);
108
109int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val)
110{
111 return __tps6586x_writes(to_i2c_client(dev), reg, len, val);
112}
113EXPORT_SYMBOL_GPL(tps6586x_writes);
114
115int tps6586x_read(struct device *dev, int reg, uint8_t *val)
116{
117 return __tps6586x_read(to_i2c_client(dev), reg, val);
118}
119EXPORT_SYMBOL_GPL(tps6586x_read);
120
121int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val)
122{
123 return __tps6586x_reads(to_i2c_client(dev), reg, len, val);
124}
125EXPORT_SYMBOL_GPL(tps6586x_reads);
126
127int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
128{
129 struct tps6586x *tps6586x = dev_get_drvdata(dev);
130 uint8_t reg_val;
131 int ret = 0;
132
133 mutex_lock(&tps6586x->lock);
134
135 ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
136 if (ret)
137 goto out;
138
139 if ((reg_val & bit_mask) == 0) {
140 reg_val |= bit_mask;
141 ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
142 }
143out:
144 mutex_unlock(&tps6586x->lock);
145 return ret;
146}
147EXPORT_SYMBOL_GPL(tps6586x_set_bits);
148
149int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
150{
151 struct tps6586x *tps6586x = dev_get_drvdata(dev);
152 uint8_t reg_val;
153 int ret = 0;
154
155 mutex_lock(&tps6586x->lock);
156
157 ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
158 if (ret)
159 goto out;
160
161 if (reg_val & bit_mask) {
162 reg_val &= ~bit_mask;
163 ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
164 }
165out:
166 mutex_unlock(&tps6586x->lock);
167 return ret;
168}
169EXPORT_SYMBOL_GPL(tps6586x_clr_bits);
170
171int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
172{
173 struct tps6586x *tps6586x = dev_get_drvdata(dev);
174 uint8_t reg_val;
175 int ret = 0;
176
177 mutex_lock(&tps6586x->lock);
178
179 ret = __tps6586x_read(tps6586x->client, reg, &reg_val);
180 if (ret)
181 goto out;
182
183 if ((reg_val & mask) != val) {
184 reg_val = (reg_val & ~mask) | val;
185 ret = __tps6586x_write(tps6586x->client, reg, reg_val);
186 }
187out:
188 mutex_unlock(&tps6586x->lock);
189 return ret;
190}
191EXPORT_SYMBOL_GPL(tps6586x_update);
192
193static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
194{
195 struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
196 uint8_t val;
197 int ret;
198
199 ret = __tps6586x_read(tps6586x->client, TPS6586X_GPIOSET2, &val);
200 if (ret)
201 return ret;
202
203 return !!(val & (1 << offset));
204}
205
206
207static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset,
208 int value)
209{
210 struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio);
211
212 __tps6586x_write(tps6586x->client, TPS6586X_GPIOSET2,
213 value << offset);
214}
215
216static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
217 int value)
218{
219 struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
220 uint8_t val, mask;
221
222 tps6586x_gpio_set(gc, offset, value);
223
224 val = 0x1 << (offset * 2);
225 mask = 0x3 << (offset * 2);
226
227 return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask);
228}
229
230static void tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base)
231{
232 int ret;
233
234 if (!gpio_base)
235 return;
236
237 tps6586x->gpio.owner = THIS_MODULE;
238 tps6586x->gpio.label = tps6586x->client->name;
239 tps6586x->gpio.dev = tps6586x->dev;
240 tps6586x->gpio.base = gpio_base;
241 tps6586x->gpio.ngpio = 4;
242 tps6586x->gpio.can_sleep = 1;
243
244 /* FIXME: add handling of GPIOs as dedicated inputs */
245 tps6586x->gpio.direction_output = tps6586x_gpio_output;
246 tps6586x->gpio.set = tps6586x_gpio_set;
247 tps6586x->gpio.get = tps6586x_gpio_get;
248
249 ret = gpiochip_add(&tps6586x->gpio);
250 if (ret)
251 dev_warn(tps6586x->dev, "GPIO registration failed: %d\n", ret);
252}
253
254static int __remove_subdev(struct device *dev, void *unused)
255{
256 platform_device_unregister(to_platform_device(dev));
257 return 0;
258}
259
260static int tps6586x_remove_subdevs(struct tps6586x *tps6586x)
261{
262 return device_for_each_child(tps6586x->dev, NULL, __remove_subdev);
263}
264
265static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x,
266 struct tps6586x_platform_data *pdata)
267{
268 struct tps6586x_subdev_info *subdev;
269 struct platform_device *pdev;
270 int i, ret = 0;
271
272 for (i = 0; i < pdata->num_subdevs; i++) {
273 subdev = &pdata->subdevs[i];
274
275 pdev = platform_device_alloc(subdev->name, subdev->id);
276
277 pdev->dev.parent = tps6586x->dev;
278 pdev->dev.platform_data = subdev->platform_data;
279
280 ret = platform_device_add(pdev);
281 if (ret)
282 goto failed;
283 }
284 return 0;
285
286failed:
287 tps6586x_remove_subdevs(tps6586x);
288 return ret;
289}
290
291static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
292 const struct i2c_device_id *id)
293{
294 struct tps6586x_platform_data *pdata = client->dev.platform_data;
295 struct tps6586x *tps6586x;
296 int ret;
297
298 if (!pdata) {
299 dev_err(&client->dev, "tps6586x requires platform data\n");
300 return -ENOTSUPP;
301 }
302
303 ret = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC);
304 if (ret < 0) {
305 dev_err(&client->dev, "Chip ID read failed: %d\n", ret);
306 return -EIO;
307 }
308
309 if (ret != TPS658621A_VERSIONCRC) {
310 dev_err(&client->dev, "Unsupported chip ID: %x\n", ret);
311 return -ENODEV;
312 }
313
314 tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL);
315 if (tps6586x == NULL)
316 return -ENOMEM;
317
318 tps6586x->client = client;
319 tps6586x->dev = &client->dev;
320 i2c_set_clientdata(client, tps6586x);
321
322 mutex_init(&tps6586x->lock);
323
324 ret = tps6586x_add_subdevs(tps6586x, pdata);
325 if (ret) {
326 dev_err(&client->dev, "add devices failed: %d\n", ret);
327 goto err_add_devs;
328 }
329
330 tps6586x_gpio_init(tps6586x, pdata->gpio_base);
331
332 return 0;
333
334err_add_devs:
335 kfree(tps6586x);
336 return ret;
337}
338
339static int __devexit tps6586x_i2c_remove(struct i2c_client *client)
340{
341 return 0;
342}
343
344static const struct i2c_device_id tps6586x_id_table[] = {
345 { "tps6586x", 0 },
346 { },
347};
348MODULE_DEVICE_TABLE(i2c, tps6586x_id_table);
349
350static struct i2c_driver tps6586x_driver = {
351 .driver = {
352 .name = "tps6586x",
353 .owner = THIS_MODULE,
354 },
355 .probe = tps6586x_i2c_probe,
356 .remove = __devexit_p(tps6586x_i2c_remove),
357 .id_table = tps6586x_id_table,
358};
359
360static int __init tps6586x_init(void)
361{
362 return i2c_add_driver(&tps6586x_driver);
363}
364subsys_initcall(tps6586x_init);
365
366static void __exit tps6586x_exit(void)
367{
368 i2c_del_driver(&tps6586x_driver);
369}
370module_exit(tps6586x_exit);
371
372MODULE_DESCRIPTION("TPS6586X core driver");
373MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
374MODULE_LICENSE("GPL");
375
diff --git a/include/linux/mfd/tps6586x.h b/include/linux/mfd/tps6586x.h
new file mode 100644
index 000000000000..772b3ae640af
--- /dev/null
+++ b/include/linux/mfd/tps6586x.h
@@ -0,0 +1,47 @@
1#ifndef __LINUX_MFD_TPS6586X_H
2#define __LINUX_MFD_TPS6586X_H
3
4enum {
5 TPS6586X_ID_SM_0,
6 TPS6586X_ID_SM_1,
7 TPS6586X_ID_SM_2,
8 TPS6586X_ID_LDO_0,
9 TPS6586X_ID_LDO_1,
10 TPS6586X_ID_LDO_2,
11 TPS6586X_ID_LDO_3,
12 TPS6586X_ID_LDO_4,
13 TPS6586X_ID_LDO_5,
14 TPS6586X_ID_LDO_6,
15 TPS6586X_ID_LDO_7,
16 TPS6586X_ID_LDO_8,
17 TPS6586X_ID_LDO_9,
18 TPS6586X_ID_LDO_RTC,
19};
20
21struct tps6586x_subdev_info {
22 int id;
23 const char *name;
24 void *platform_data;
25};
26
27struct tps6586x_platform_data {
28 int num_subdevs;
29 struct tps6586x_subdev_info *subdevs;
30
31 int gpio_base;
32};
33
34/*
35 * NOTE: the functions below are not intended for use outside
36 * of the TPS6586X sub-device drivers
37 */
38extern int tps6586x_write(struct device *dev, int reg, uint8_t val);
39extern int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val);
40extern int tps6586x_read(struct device *dev, int reg, uint8_t *val);
41extern int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val);
42extern int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask);
43extern int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask);
44extern int tps6586x_update(struct device *dev, int reg, uint8_t val,
45 uint8_t mask);
46
47#endif /*__LINUX_MFD_TPS6586X_H */