aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/lp8788.c
diff options
context:
space:
mode:
authorMilo Kim <Milo.Kim@ti.com>2012-09-19 12:53:33 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2012-09-23 14:51:34 -0400
commiteea6b7cc53aaecf868e1643058159807c744e04e (patch)
tree1922328c8edb85f2c106075c966baa07f15ea831 /drivers/mfd/lp8788.c
parent804971ec3793d30f40c1a74775dd3fe89deb461a (diff)
mfd: Add lp8788 mfd driver
TI LP8788 PMU provides regulators, battery charger, ADC, RTC, backlight driver and current sinks. This MFD patch supports the I2C communication using the regmap, the interrupt handling using the linear IRQ domain and configurable platform data structures for each driver module. (Driver Architecture) < mfd devices > LP8788 HW .......... mfd .......... regulator drivers I2C power supply driver IRQs iio adc driver rtc driver backlight driver current sink drivers o regulators : LDOs and BUCKs o power supply : Battery charger o iio adc : Battery voltage/temperature o rtc : RTC and alarm o backlight o current sink : LED and vibrator All MFD device modules are registered by LP8788 MFD core driver. For sharing information such like the virtual IRQ number, MFD core driver uses the resource structure. Then each module can retrieve the specific IRQ number and detect it in the IRQ thread. Configurable platform data is handled in each driver module. Signed-off-by: Milo(Woogyom) Kim <milo.kim@ti.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd/lp8788.c')
-rw-r--r--drivers/mfd/lp8788.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/drivers/mfd/lp8788.c b/drivers/mfd/lp8788.c
new file mode 100644
index 000000000000..3e94a699833c
--- /dev/null
+++ b/drivers/mfd/lp8788.c
@@ -0,0 +1,245 @@
1/*
2 * TI LP8788 MFD - core interface
3 *
4 * Copyright 2012 Texas Instruments
5 *
6 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 */
13
14#include <linux/err.h>
15#include <linux/i2c.h>
16#include <linux/mfd/core.h>
17#include <linux/mfd/lp8788.h>
18#include <linux/module.h>
19#include <linux/slab.h>
20
21#define MAX_LP8788_REGISTERS 0xA2
22
23#define MFD_DEV_SIMPLE(_name) \
24{ \
25 .name = LP8788_DEV_##_name, \
26}
27
28#define MFD_DEV_WITH_ID(_name, _id) \
29{ \
30 .name = LP8788_DEV_##_name, \
31 .id = _id, \
32}
33
34#define MFD_DEV_WITH_RESOURCE(_name, _resource, num_resource) \
35{ \
36 .name = LP8788_DEV_##_name, \
37 .resources = _resource, \
38 .num_resources = num_resource, \
39}
40
41static struct resource chg_irqs[] = {
42 /* Charger Interrupts */
43 {
44 .start = LP8788_INT_CHG_INPUT_STATE,
45 .end = LP8788_INT_PRECHG_TIMEOUT,
46 .name = LP8788_CHG_IRQ,
47 .flags = IORESOURCE_IRQ,
48 },
49 /* Power Routing Switch Interrupts */
50 {
51 .start = LP8788_INT_ENTER_SYS_SUPPORT,
52 .end = LP8788_INT_EXIT_SYS_SUPPORT,
53 .name = LP8788_PRSW_IRQ,
54 .flags = IORESOURCE_IRQ,
55 },
56 /* Battery Interrupts */
57 {
58 .start = LP8788_INT_BATT_LOW,
59 .end = LP8788_INT_NO_BATT,
60 .name = LP8788_BATT_IRQ,
61 .flags = IORESOURCE_IRQ,
62 },
63};
64
65static struct resource rtc_irqs[] = {
66 {
67 .start = LP8788_INT_RTC_ALARM1,
68 .end = LP8788_INT_RTC_ALARM2,
69 .name = LP8788_ALM_IRQ,
70 .flags = IORESOURCE_IRQ,
71 },
72};
73
74static struct mfd_cell lp8788_devs[] = {
75 /* 4 bucks */
76 MFD_DEV_WITH_ID(BUCK, 1),
77 MFD_DEV_WITH_ID(BUCK, 2),
78 MFD_DEV_WITH_ID(BUCK, 3),
79 MFD_DEV_WITH_ID(BUCK, 4),
80
81 /* 12 digital ldos */
82 MFD_DEV_WITH_ID(DLDO, 1),
83 MFD_DEV_WITH_ID(DLDO, 2),
84 MFD_DEV_WITH_ID(DLDO, 3),
85 MFD_DEV_WITH_ID(DLDO, 4),
86 MFD_DEV_WITH_ID(DLDO, 5),
87 MFD_DEV_WITH_ID(DLDO, 6),
88 MFD_DEV_WITH_ID(DLDO, 7),
89 MFD_DEV_WITH_ID(DLDO, 8),
90 MFD_DEV_WITH_ID(DLDO, 9),
91 MFD_DEV_WITH_ID(DLDO, 10),
92 MFD_DEV_WITH_ID(DLDO, 11),
93 MFD_DEV_WITH_ID(DLDO, 12),
94
95 /* 10 analog ldos */
96 MFD_DEV_WITH_ID(ALDO, 1),
97 MFD_DEV_WITH_ID(ALDO, 2),
98 MFD_DEV_WITH_ID(ALDO, 3),
99 MFD_DEV_WITH_ID(ALDO, 4),
100 MFD_DEV_WITH_ID(ALDO, 5),
101 MFD_DEV_WITH_ID(ALDO, 6),
102 MFD_DEV_WITH_ID(ALDO, 7),
103 MFD_DEV_WITH_ID(ALDO, 8),
104 MFD_DEV_WITH_ID(ALDO, 9),
105 MFD_DEV_WITH_ID(ALDO, 10),
106
107 /* ADC */
108 MFD_DEV_SIMPLE(ADC),
109
110 /* battery charger */
111 MFD_DEV_WITH_RESOURCE(CHARGER, chg_irqs, ARRAY_SIZE(chg_irqs)),
112
113 /* rtc */
114 MFD_DEV_WITH_RESOURCE(RTC, rtc_irqs, ARRAY_SIZE(rtc_irqs)),
115
116 /* backlight */
117 MFD_DEV_SIMPLE(BACKLIGHT),
118
119 /* current sink for vibrator */
120 MFD_DEV_SIMPLE(VIBRATOR),
121
122 /* current sink for keypad LED */
123 MFD_DEV_SIMPLE(KEYLED),
124};
125
126int lp8788_read_byte(struct lp8788 *lp, u8 reg, u8 *data)
127{
128 int ret;
129 unsigned int val;
130
131 ret = regmap_read(lp->regmap, reg, &val);
132 if (ret < 0) {
133 dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
134 return ret;
135 }
136
137 *data = (u8)val;
138 return 0;
139}
140EXPORT_SYMBOL_GPL(lp8788_read_byte);
141
142int lp8788_read_multi_bytes(struct lp8788 *lp, u8 reg, u8 *data, size_t count)
143{
144 return regmap_bulk_read(lp->regmap, reg, data, count);
145}
146EXPORT_SYMBOL_GPL(lp8788_read_multi_bytes);
147
148int lp8788_write_byte(struct lp8788 *lp, u8 reg, u8 data)
149{
150 return regmap_write(lp->regmap, reg, data);
151}
152EXPORT_SYMBOL_GPL(lp8788_write_byte);
153
154int lp8788_update_bits(struct lp8788 *lp, u8 reg, u8 mask, u8 data)
155{
156 return regmap_update_bits(lp->regmap, reg, mask, data);
157}
158EXPORT_SYMBOL_GPL(lp8788_update_bits);
159
160static int lp8788_platform_init(struct lp8788 *lp)
161{
162 struct lp8788_platform_data *pdata = lp->pdata;
163
164 return (pdata && pdata->init_func) ? pdata->init_func(lp) : 0;
165}
166
167static const struct regmap_config lp8788_regmap_config = {
168 .reg_bits = 8,
169 .val_bits = 8,
170 .max_register = MAX_LP8788_REGISTERS,
171};
172
173static int lp8788_probe(struct i2c_client *cl, const struct i2c_device_id *id)
174{
175 struct lp8788 *lp;
176 struct lp8788_platform_data *pdata = cl->dev.platform_data;
177 int ret;
178
179 lp = devm_kzalloc(&cl->dev, sizeof(struct lp8788), GFP_KERNEL);
180 if (!lp)
181 return -ENOMEM;
182
183 lp->regmap = devm_regmap_init_i2c(cl, &lp8788_regmap_config);
184 if (IS_ERR(lp->regmap)) {
185 ret = PTR_ERR(lp->regmap);
186 dev_err(&cl->dev, "regmap init i2c err: %d\n", ret);
187 return ret;
188 }
189
190 lp->pdata = pdata;
191 lp->dev = &cl->dev;
192 i2c_set_clientdata(cl, lp);
193
194 ret = lp8788_platform_init(lp);
195 if (ret)
196 return ret;
197
198 ret = lp8788_irq_init(lp, cl->irq);
199 if (ret)
200 return ret;
201
202 return mfd_add_devices(lp->dev, -1, lp8788_devs,
203 ARRAY_SIZE(lp8788_devs), NULL, 0, NULL);
204}
205
206static int __devexit lp8788_remove(struct i2c_client *cl)
207{
208 struct lp8788 *lp = i2c_get_clientdata(cl);
209
210 mfd_remove_devices(lp->dev);
211 lp8788_irq_exit(lp);
212 return 0;
213}
214
215static const struct i2c_device_id lp8788_ids[] = {
216 {"lp8788", 0},
217 { }
218};
219MODULE_DEVICE_TABLE(i2c, lp8788_ids);
220
221static struct i2c_driver lp8788_driver = {
222 .driver = {
223 .name = "lp8788",
224 .owner = THIS_MODULE,
225 },
226 .probe = lp8788_probe,
227 .remove = __devexit_p(lp8788_remove),
228 .id_table = lp8788_ids,
229};
230
231static int __init lp8788_init(void)
232{
233 return i2c_add_driver(&lp8788_driver);
234}
235subsys_initcall(lp8788_init);
236
237static void __exit lp8788_exit(void)
238{
239 i2c_del_driver(&lp8788_driver);
240}
241module_exit(lp8788_exit);
242
243MODULE_DESCRIPTION("TI LP8788 MFD Driver");
244MODULE_AUTHOR("Milo Kim");
245MODULE_LICENSE("GPL");