aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVenu Byravarasu <vbyravarasu@nvidia.com>2012-03-16 01:40:19 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2012-03-20 11:56:33 -0400
commit3c33be06f9aa0949ad24e67dfcae1f2a3006f4e1 (patch)
treeb53b2e2c0257e2fac0a971fb94f334857da924bd
parentbcc2d6d6fcbee3c07515837b522f6c242f3f99e4 (diff)
mfd: Add support for TPS65090
TPS65090 is a Texas Instrument PMIC. It contains 3 Step-Down converters, 2 always on LDO's and 7 current limited load switches. Signed-off-by: Venu Byravarasu <vbyravarasu@nvidia.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r--drivers/mfd/Kconfig12
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/tps65090.c387
-rw-r--r--include/linux/mfd/tps65090.h46
4 files changed, 446 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index ef22292aa0cd..f67d20e8bc0f 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -827,6 +827,18 @@ config MFD_PM8XXX_IRQ
827config TPS65911_COMPARATOR 827config TPS65911_COMPARATOR
828 tristate 828 tristate
829 829
830config MFD_TPS65090
831 bool "TPS65090 Power Management chips"
832 depends on I2C=y && GENERIC_HARDIRQS
833 select MFD_CORE
834 select REGMAP_I2C
835 help
836 If you say yes here you get support for the TPS65090 series of
837 Power Management chips.
838 This driver provides common support for accessing the device,
839 additional drivers must be enabled in order to use the
840 functionality of the device.
841
830config MFD_AAT2870_CORE 842config MFD_AAT2870_CORE
831 bool "Support for the AnalogicTech AAT2870" 843 bool "Support for the AnalogicTech AAT2870"
832 select MFD_CORE 844 select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 67f7b0e34d37..05fa538c5efe 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -110,6 +110,7 @@ obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o
110obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o 110obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
111obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o 111obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
112obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o 112obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
113obj-$(CONFIG_MFD_TPS65090) += tps65090.o
113obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o 114obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
114obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o 115obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
115obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o 116obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c
new file mode 100644
index 000000000000..a66d4df51293
--- /dev/null
+++ b/drivers/mfd/tps65090.c
@@ -0,0 +1,387 @@
1/*
2 * Core driver for TI TPS65090 PMIC family
3 *
4 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
5
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/interrupt.h>
20#include <linux/irq.h>
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/mutex.h>
24#include <linux/slab.h>
25#include <linux/i2c.h>
26#include <linux/mfd/core.h>
27#include <linux/mfd/tps65090.h>
28#include <linux/regmap.h>
29#include <linux/err.h>
30
31#define NUM_INT_REG 2
32#define TOTAL_NUM_REG 0x18
33
34/* interrupt status registers */
35#define TPS65090_INT_STS 0x0
36#define TPS65090_INT_STS2 0x1
37
38/* interrupt mask registers */
39#define TPS65090_INT_MSK 0x2
40#define TPS65090_INT_MSK2 0x3
41
42struct tps65090_irq_data {
43 u8 mask_reg;
44 u8 mask_pos;
45};
46
47#define TPS65090_IRQ(_reg, _mask_pos) \
48 { \
49 .mask_reg = (_reg), \
50 .mask_pos = (_mask_pos), \
51 }
52
53static const struct tps65090_irq_data tps65090_irqs[] = {
54 [0] = TPS65090_IRQ(0, 0),
55 [1] = TPS65090_IRQ(0, 1),
56 [2] = TPS65090_IRQ(0, 2),
57 [3] = TPS65090_IRQ(0, 3),
58 [4] = TPS65090_IRQ(0, 4),
59 [5] = TPS65090_IRQ(0, 5),
60 [6] = TPS65090_IRQ(0, 6),
61 [7] = TPS65090_IRQ(0, 7),
62 [8] = TPS65090_IRQ(1, 0),
63 [9] = TPS65090_IRQ(1, 1),
64 [10] = TPS65090_IRQ(1, 2),
65 [11] = TPS65090_IRQ(1, 3),
66 [12] = TPS65090_IRQ(1, 4),
67 [13] = TPS65090_IRQ(1, 5),
68 [14] = TPS65090_IRQ(1, 6),
69 [15] = TPS65090_IRQ(1, 7),
70};
71
72static struct mfd_cell tps65090s[] = {
73 {
74 .name = "tps65910-pmic",
75 },
76 {
77 .name = "tps65910-regulator",
78 },
79};
80
81struct tps65090 {
82 struct mutex lock;
83 struct device *dev;
84 struct i2c_client *client;
85 struct regmap *rmap;
86 struct irq_chip irq_chip;
87 struct mutex irq_lock;
88 int irq_base;
89 unsigned int id;
90};
91
92int tps65090_write(struct device *dev, int reg, uint8_t val)
93{
94 struct tps65090 *tps = dev_get_drvdata(dev);
95 return regmap_write(tps->rmap, reg, val);
96}
97EXPORT_SYMBOL_GPL(tps65090_write);
98
99int tps65090_read(struct device *dev, int reg, uint8_t *val)
100{
101 struct tps65090 *tps = dev_get_drvdata(dev);
102 unsigned int temp_val;
103 int ret;
104 ret = regmap_read(tps->rmap, reg, &temp_val);
105 if (!ret)
106 *val = temp_val;
107 return ret;
108}
109EXPORT_SYMBOL_GPL(tps65090_read);
110
111int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num)
112{
113 struct tps65090 *tps = dev_get_drvdata(dev);
114 return regmap_update_bits(tps->rmap, reg, BIT(bit_num), ~0u);
115}
116EXPORT_SYMBOL_GPL(tps65090_set_bits);
117
118int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num)
119{
120 struct tps65090 *tps = dev_get_drvdata(dev);
121 return regmap_update_bits(tps->rmap, reg, BIT(bit_num), 0u);
122}
123EXPORT_SYMBOL_GPL(tps65090_clr_bits);
124
125static void tps65090_irq_lock(struct irq_data *data)
126{
127 struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data);
128
129 mutex_lock(&tps65090->irq_lock);
130}
131
132static void tps65090_irq_mask(struct irq_data *irq_data)
133{
134 struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data);
135 unsigned int __irq = irq_data->hwirq;
136 const struct tps65090_irq_data *data = &tps65090_irqs[__irq];
137
138 tps65090_set_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg),
139 data->mask_pos);
140}
141
142static void tps65090_irq_unmask(struct irq_data *irq_data)
143{
144 struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data);
145 unsigned int __irq = irq_data->irq - tps65090->irq_base;
146 const struct tps65090_irq_data *data = &tps65090_irqs[__irq];
147
148 tps65090_clr_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg),
149 data->mask_pos);
150}
151
152static void tps65090_irq_sync_unlock(struct irq_data *data)
153{
154 struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data);
155
156 mutex_unlock(&tps65090->irq_lock);
157}
158
159static irqreturn_t tps65090_irq(int irq, void *data)
160{
161 struct tps65090 *tps65090 = data;
162 int ret = 0;
163 u8 status, mask;
164 unsigned long int acks = 0;
165 int i;
166
167 for (i = 0; i < NUM_INT_REG; i++) {
168 ret = tps65090_read(tps65090->dev, TPS65090_INT_MSK + i, &mask);
169 if (ret < 0) {
170 dev_err(tps65090->dev,
171 "failed to read mask reg [addr:%d]\n",
172 TPS65090_INT_MSK + i);
173 return IRQ_NONE;
174 }
175 ret = tps65090_read(tps65090->dev, TPS65090_INT_STS + i,
176 &status);
177 if (ret < 0) {
178 dev_err(tps65090->dev,
179 "failed to read status reg [addr:%d]\n",
180 TPS65090_INT_STS + i);
181 return IRQ_NONE;
182 }
183 if (status) {
184 /* Ack only those interrupts which are not masked */
185 status &= (~mask);
186 ret = tps65090_write(tps65090->dev,
187 TPS65090_INT_STS + i, status);
188 if (ret < 0) {
189 dev_err(tps65090->dev,
190 "failed to write interrupt status\n");
191 return IRQ_NONE;
192 }
193 acks |= (status << (i * 8));
194 }
195 }
196
197 for_each_set_bit(i, &acks, ARRAY_SIZE(tps65090_irqs))
198 handle_nested_irq(tps65090->irq_base + i);
199 return acks ? IRQ_HANDLED : IRQ_NONE;
200}
201
202static int __devinit tps65090_irq_init(struct tps65090 *tps65090, int irq,
203 int irq_base)
204{
205 int i, ret;
206
207 if (!irq_base) {
208 dev_err(tps65090->dev, "IRQ base not set\n");
209 return -EINVAL;
210 }
211
212 mutex_init(&tps65090->irq_lock);
213
214 for (i = 0; i < NUM_INT_REG; i++)
215 tps65090_write(tps65090->dev, TPS65090_INT_MSK + i, 0xFF);
216
217 for (i = 0; i < NUM_INT_REG; i++)
218 tps65090_write(tps65090->dev, TPS65090_INT_STS + i, 0xff);
219
220 tps65090->irq_base = irq_base;
221 tps65090->irq_chip.name = "tps65090";
222 tps65090->irq_chip.irq_mask = tps65090_irq_mask;
223 tps65090->irq_chip.irq_unmask = tps65090_irq_unmask;
224 tps65090->irq_chip.irq_bus_lock = tps65090_irq_lock;
225 tps65090->irq_chip.irq_bus_sync_unlock = tps65090_irq_sync_unlock;
226
227 for (i = 0; i < ARRAY_SIZE(tps65090_irqs); i++) {
228 int __irq = i + tps65090->irq_base;
229 irq_set_chip_data(__irq, tps65090);
230 irq_set_chip_and_handler(__irq, &tps65090->irq_chip,
231 handle_simple_irq);
232 irq_set_nested_thread(__irq, 1);
233#ifdef CONFIG_ARM
234 set_irq_flags(__irq, IRQF_VALID);
235#endif
236 }
237
238 ret = request_threaded_irq(irq, NULL, tps65090_irq, IRQF_ONESHOT,
239 "tps65090", tps65090);
240 if (!ret) {
241 device_init_wakeup(tps65090->dev, 1);
242 enable_irq_wake(irq);
243 }
244
245 return ret;
246}
247
248static bool is_volatile_reg(struct device *dev, unsigned int reg)
249{
250 if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS))
251 return true;
252 else
253 return false;
254}
255
256static const struct regmap_config tps65090_regmap_config = {
257 .reg_bits = 8,
258 .val_bits = 8,
259 .max_register = TOTAL_NUM_REG,
260 .num_reg_defaults_raw = TOTAL_NUM_REG,
261 .cache_type = REGCACHE_RBTREE,
262 .volatile_reg = is_volatile_reg,
263};
264
265static int __devinit tps65090_i2c_probe(struct i2c_client *client,
266 const struct i2c_device_id *id)
267{
268 struct tps65090_platform_data *pdata = client->dev.platform_data;
269 struct tps65090 *tps65090;
270 int ret;
271
272 if (!pdata) {
273 dev_err(&client->dev, "tps65090 requires platform data\n");
274 return -EINVAL;
275 }
276
277 tps65090 = devm_kzalloc(&client->dev, sizeof(struct tps65090),
278 GFP_KERNEL);
279 if (tps65090 == NULL)
280 return -ENOMEM;
281
282 tps65090->client = client;
283 tps65090->dev = &client->dev;
284 i2c_set_clientdata(client, tps65090);
285
286 mutex_init(&tps65090->lock);
287
288 if (client->irq) {
289 ret = tps65090_irq_init(tps65090, client->irq, pdata->irq_base);
290 if (ret) {
291 dev_err(&client->dev, "IRQ init failed with err: %d\n",
292 ret);
293 goto err_exit;
294 }
295 }
296
297 tps65090->rmap = regmap_init_i2c(tps65090->client,
298 &tps65090_regmap_config);
299 if (IS_ERR(tps65090->rmap)) {
300 dev_err(&client->dev, "regmap_init failed with err: %ld\n",
301 PTR_ERR(tps65090->rmap));
302 goto err_irq_exit;
303 };
304
305 ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
306 ARRAY_SIZE(tps65090s), NULL, 0);
307 if (ret) {
308 dev_err(&client->dev, "add mfd devices failed with err: %d\n",
309 ret);
310 goto err_regmap_exit;
311 }
312
313 return 0;
314
315err_regmap_exit:
316 regmap_exit(tps65090->rmap);
317
318err_irq_exit:
319 if (client->irq)
320 free_irq(client->irq, tps65090);
321err_exit:
322 return ret;
323}
324
325static int __devexit tps65090_i2c_remove(struct i2c_client *client)
326{
327 struct tps65090 *tps65090 = i2c_get_clientdata(client);
328
329 mfd_remove_devices(tps65090->dev);
330 regmap_exit(tps65090->rmap);
331 if (client->irq)
332 free_irq(client->irq, tps65090);
333
334 return 0;
335}
336
337#ifdef CONFIG_PM
338static int tps65090_i2c_suspend(struct i2c_client *client, pm_message_t state)
339{
340 if (client->irq)
341 disable_irq(client->irq);
342 return 0;
343}
344
345static int tps65090_i2c_resume(struct i2c_client *client)
346{
347 if (client->irq)
348 enable_irq(client->irq);
349 return 0;
350}
351#endif
352
353static const struct i2c_device_id tps65090_id_table[] = {
354 { "tps65090", 0 },
355 { },
356};
357MODULE_DEVICE_TABLE(i2c, tps65090_id_table);
358
359static struct i2c_driver tps65090_driver = {
360 .driver = {
361 .name = "tps65090",
362 .owner = THIS_MODULE,
363 },
364 .probe = tps65090_i2c_probe,
365 .remove = __devexit_p(tps65090_i2c_remove),
366#ifdef CONFIG_PM
367 .suspend = tps65090_i2c_suspend,
368 .resume = tps65090_i2c_resume,
369#endif
370 .id_table = tps65090_id_table,
371};
372
373static int __init tps65090_init(void)
374{
375 return i2c_add_driver(&tps65090_driver);
376}
377subsys_initcall(tps65090_init);
378
379static void __exit tps65090_exit(void)
380{
381 i2c_del_driver(&tps65090_driver);
382}
383module_exit(tps65090_exit);
384
385MODULE_DESCRIPTION("TPS65090 core driver");
386MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
387MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h
new file mode 100644
index 000000000000..38e31c55adbb
--- /dev/null
+++ b/include/linux/mfd/tps65090.h
@@ -0,0 +1,46 @@
1/*
2 * Core driver interface for TI TPS65090 PMIC family
3 *
4 * Copyright (C) 2012 NVIDIA Corporation
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 */
21
22#ifndef __LINUX_MFD_TPS65090_H
23#define __LINUX_MFD_TPS65090_H
24
25struct tps65090_subdev_info {
26 int id;
27 const char *name;
28 void *platform_data;
29};
30
31struct tps65090_platform_data {
32 int irq_base;
33 int num_subdevs;
34 struct tps65090_subdev_info *subdevs;
35};
36
37/*
38 * NOTE: the functions below are not intended for use outside
39 * of the TPS65090 sub-device drivers
40 */
41extern int tps65090_write(struct device *dev, int reg, uint8_t val);
42extern int tps65090_read(struct device *dev, int reg, uint8_t *val);
43extern int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num);
44extern int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num);
45
46#endif /*__LINUX_MFD_TPS65090_H */