aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorHaojian Zhuang <haojian.zhuang@marvell.com>2010-01-08 06:29:23 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2010-03-07 16:17:06 -0500
commitd50f8f339f6901fccc9d4292b65ce8b69d7413d4 (patch)
treeda0256c7151f96177209c392c49e1a9b058f2f23 /drivers/mfd
parent34a4b2391e9fbd12de9817de4ae409528bd7d7b6 (diff)
mfd: Initial max8925 support
Basic Max8925 support, which is a power management IC from Maxim Semiconductor. Signed-off-by: Haojian Zhuang <haojian.zhuang@marvell.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig9
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/max8925-core.c262
-rw-r--r--drivers/mfd/max8925-i2c.c210
4 files changed, 483 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 815907eb70a4..ee416eefb8e9 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -194,6 +194,15 @@ config PMIC_ADP5520
194 individual components like LCD backlight, LEDs, GPIOs and Kepad 194 individual components like LCD backlight, LEDs, GPIOs and Kepad
195 under the corresponding menus. 195 under the corresponding menus.
196 196
197config MFD_MAX8925
198 tristate "Maxim Semiconductor MAX8925 PMIC Support"
199 depends on I2C
200 help
201 Say yes here to support for Maxim Semiconductor MAX8925. This is
202 a Power Management IC. This driver provies common support for
203 accessing the device, additional drivers must be enabled in order
204 to use the functionality of the device.
205
197config MFD_WM8400 206config MFD_WM8400
198 tristate "Support Wolfson Microelectronics WM8400" 207 tristate "Support Wolfson Microelectronics WM8400"
199 select MFD_CORE 208 select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 1e3ae062c1f6..261635700243 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -49,6 +49,8 @@ endif
49obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o 49obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o
50 50
51obj-$(CONFIG_PMIC_DA903X) += da903x.o 51obj-$(CONFIG_PMIC_DA903X) += da903x.o
52max8925-objs := max8925-core.o max8925-i2c.o
53obj-$(CONFIG_MFD_MAX8925) += max8925.o
52 54
53obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o 55obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o
54obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o 56obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
new file mode 100644
index 000000000000..3e26267960b1
--- /dev/null
+++ b/drivers/mfd/max8925-core.c
@@ -0,0 +1,262 @@
1/*
2 * Base driver for Maxim MAX8925
3 *
4 * Copyright (C) 2009 Marvell International Ltd.
5 * Haojian Zhuang <haojian.zhuang@marvell.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/i2c.h>
15#include <linux/interrupt.h>
16#include <linux/platform_device.h>
17#include <linux/mfd/core.h>
18#include <linux/mfd/max8925.h>
19
20#define IRQ_MODE_STATUS 0
21#define IRQ_MODE_MASK 1
22
23static int __get_irq_offset(struct max8925_chip *chip, int irq, int mode,
24 int *offset, int *bit)
25{
26 if (!offset || !bit)
27 return -EINVAL;
28
29 switch (chip->chip_id) {
30 case MAX8925_GPM:
31 *bit = irq % BITS_PER_BYTE;
32 if (irq < (BITS_PER_BYTE << 1)) { /* irq = [0,15] */
33 *offset = (mode) ? MAX8925_CHG_IRQ1_MASK
34 : MAX8925_CHG_IRQ1;
35 if (irq >= BITS_PER_BYTE)
36 (*offset)++;
37 } else { /* irq = [16,31] */
38 *offset = (mode) ? MAX8925_ON_OFF_IRQ1_MASK
39 : MAX8925_ON_OFF_IRQ1;
40 if (irq >= (BITS_PER_BYTE * 3))
41 (*offset)++;
42 }
43 break;
44 case MAX8925_ADC:
45 *bit = irq % BITS_PER_BYTE;
46 *offset = (mode) ? MAX8925_TSC_IRQ_MASK : MAX8925_TSC_IRQ;
47 break;
48 default:
49 goto out;
50 }
51 return 0;
52out:
53 dev_err(chip->dev, "Wrong irq #%d is assigned\n", irq);
54 return -EINVAL;
55}
56
57static int __check_irq(int irq)
58{
59 if ((irq < 0) || (irq >= MAX8925_NUM_IRQ))
60 return -EINVAL;
61 return 0;
62}
63
64int max8925_mask_irq(struct max8925_chip *chip, int irq)
65{
66 int offset, bit, ret;
67
68 ret = __get_irq_offset(chip, irq, IRQ_MODE_MASK, &offset, &bit);
69 if (ret < 0)
70 return ret;
71 ret = max8925_set_bits(chip->i2c, offset, 1 << bit, 1 << bit);
72 return ret;
73}
74
75int max8925_unmask_irq(struct max8925_chip *chip, int irq)
76{
77 int offset, bit, ret;
78
79 ret = __get_irq_offset(chip, irq, IRQ_MODE_MASK, &offset, &bit);
80 if (ret < 0)
81 return ret;
82 ret = max8925_set_bits(chip->i2c, offset, 1 << bit, 0);
83 return ret;
84}
85
86#define INT_STATUS_NUM (MAX8925_NUM_IRQ / BITS_PER_BYTE)
87
88static irqreturn_t max8925_irq_thread(int irq, void *data)
89{
90 struct max8925_chip *chip = data;
91 unsigned long irq_status[INT_STATUS_NUM];
92 unsigned char status_buf[INT_STATUS_NUM << 1];
93 int i, ret;
94
95 memset(irq_status, 0, sizeof(unsigned long) * INT_STATUS_NUM);
96
97 /* all these interrupt status registers are read-only */
98 switch (chip->chip_id) {
99 case MAX8925_GPM:
100 ret = max8925_bulk_read(chip->i2c, MAX8925_CHG_IRQ1,
101 4, status_buf);
102 if (ret < 0)
103 goto out;
104 ret = max8925_bulk_read(chip->i2c, MAX8925_ON_OFF_IRQ1,
105 2, &status_buf[4]);
106 if (ret < 0)
107 goto out;
108 ret = max8925_bulk_read(chip->i2c, MAX8925_ON_OFF_IRQ2,
109 2, &status_buf[6]);
110 if (ret < 0)
111 goto out;
112 /* clear masked interrupt status */
113 status_buf[0] &= (~status_buf[2] & CHG_IRQ1_MASK);
114 irq_status[0] |= status_buf[0];
115 status_buf[1] &= (~status_buf[3] & CHG_IRQ2_MASK);
116 irq_status[0] |= (status_buf[1] << BITS_PER_BYTE);
117 status_buf[4] &= (~status_buf[5] & ON_OFF_IRQ1_MASK);
118 irq_status[0] |= (status_buf[4] << (BITS_PER_BYTE * 2));
119 status_buf[6] &= (~status_buf[7] & ON_OFF_IRQ2_MASK);
120 irq_status[0] |= (status_buf[6] << (BITS_PER_BYTE * 3));
121 break;
122 case MAX8925_ADC:
123 ret = max8925_bulk_read(chip->i2c, MAX8925_TSC_IRQ,
124 2, status_buf);
125 if (ret < 0)
126 goto out;
127 /* clear masked interrupt status */
128 status_buf[0] &= (~status_buf[1] & TSC_IRQ_MASK);
129 irq_status[0] |= status_buf[0];
130 break;
131 default:
132 goto out;
133 }
134
135 for_each_bit(i, &irq_status[0], MAX8925_NUM_IRQ) {
136 clear_bit(i, irq_status);
137 dev_dbg(chip->dev, "Servicing IRQ #%d in %s\n", i, chip->name);
138
139 mutex_lock(&chip->irq_lock);
140 if (chip->irq[i].handler)
141 chip->irq[i].handler(i, chip->irq[i].data);
142 else {
143 max8925_mask_irq(chip, i);
144 dev_err(chip->dev, "Noboday cares IRQ #%d in %s. "
145 "Now mask it.\n", i, chip->name);
146 }
147 mutex_unlock(&chip->irq_lock);
148 }
149out:
150 return IRQ_HANDLED;
151}
152
153int max8925_request_irq(struct max8925_chip *chip, int irq,
154 irq_handler_t handler, void *data)
155{
156 if ((__check_irq(irq) < 0) || !handler)
157 return -EINVAL;
158
159 mutex_lock(&chip->irq_lock);
160 chip->irq[irq].handler = handler;
161 chip->irq[irq].data = data;
162 mutex_unlock(&chip->irq_lock);
163 return 0;
164}
165EXPORT_SYMBOL(max8925_request_irq);
166
167int max8925_free_irq(struct max8925_chip *chip, int irq)
168{
169 if (__check_irq(irq) < 0)
170 return -EINVAL;
171
172 mutex_lock(&chip->irq_lock);
173 chip->irq[irq].handler = NULL;
174 chip->irq[irq].data = NULL;
175 mutex_unlock(&chip->irq_lock);
176 return 0;
177}
178EXPORT_SYMBOL(max8925_free_irq);
179
180static int __devinit device_gpm_init(struct max8925_chip *chip,
181 struct i2c_client *i2c,
182 struct max8925_platform_data *pdata)
183{
184 int ret;
185
186 /* mask all IRQs */
187 ret = max8925_set_bits(i2c, MAX8925_CHG_IRQ1_MASK, 0x7, 0x7);
188 if (ret < 0)
189 goto out;
190 ret = max8925_set_bits(i2c, MAX8925_CHG_IRQ2_MASK, 0xff, 0xff);
191 if (ret < 0)
192 goto out;
193 ret = max8925_set_bits(i2c, MAX8925_ON_OFF_IRQ1_MASK, 0xff, 0xff);
194 if (ret < 0)
195 goto out;
196 ret = max8925_set_bits(i2c, MAX8925_ON_OFF_IRQ2_MASK, 0x3, 0x3);
197 if (ret < 0)
198 goto out;
199
200 chip->name = "GPM";
201 memset(chip->irq, 0, sizeof(struct max8925_irq) * MAX8925_NUM_IRQ);
202 ret = request_threaded_irq(i2c->irq, NULL, max8925_irq_thread,
203 IRQF_ONESHOT | IRQF_TRIGGER_LOW,
204 "max8925-gpm", chip);
205 if (ret < 0) {
206 dev_err(chip->dev, "Failed to request IRQ #%d.\n", i2c->irq);
207 goto out;
208 }
209 chip->chip_irq = i2c->irq;
210
211 /* enable hard-reset for ONKEY power-off */
212 max8925_set_bits(i2c, MAX8925_SYSENSEL, 0x80, 0x80);
213out:
214 return ret;
215}
216
217static int __devinit device_adc_init(struct max8925_chip *chip,
218 struct i2c_client *i2c,
219 struct max8925_platform_data *pdata)
220{
221 int ret;
222
223 /* mask all IRQs */
224 ret = max8925_set_bits(i2c, MAX8925_TSC_IRQ_MASK, 3, 3);
225
226 chip->name = "ADC";
227 memset(chip->irq, 0, sizeof(struct max8925_irq) * MAX8925_NUM_IRQ);
228 ret = request_threaded_irq(i2c->irq, NULL, max8925_irq_thread,
229 IRQF_ONESHOT | IRQF_TRIGGER_LOW,
230 "max8925-adc", chip);
231 if (ret < 0) {
232 dev_err(chip->dev, "Failed to request IRQ #%d.\n", i2c->irq);
233 goto out;
234 }
235 chip->chip_irq = i2c->irq;
236out:
237 return ret;
238}
239
240int __devinit max8925_device_init(struct max8925_chip *chip,
241 struct max8925_platform_data *pdata)
242{
243 switch (chip->chip_id) {
244 case MAX8925_GPM:
245 device_gpm_init(chip, chip->i2c, pdata);
246 break;
247 case MAX8925_ADC:
248 device_adc_init(chip, chip->i2c, pdata);
249 break;
250 }
251 return 0;
252}
253
254void max8925_device_exit(struct max8925_chip *chip)
255{
256 if (chip->chip_irq >= 0)
257 free_irq(chip->chip_irq, chip);
258}
259
260MODULE_DESCRIPTION("PMIC Driver for Maxim MAX8925");
261MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com");
262MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c
new file mode 100644
index 000000000000..942068e730f9
--- /dev/null
+++ b/drivers/mfd/max8925-i2c.c
@@ -0,0 +1,210 @@
1/*
2 * I2C driver for Maxim MAX8925
3 *
4 * Copyright (C) 2009 Marvell International Ltd.
5 * Haojian Zhuang <haojian.zhuang@marvell.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/platform_device.h>
14#include <linux/i2c.h>
15#include <linux/mfd/max8925.h>
16
17static inline int max8925_read_device(struct i2c_client *i2c,
18 int reg, int bytes, void *dest)
19{
20 unsigned char data;
21 unsigned char *buf;
22 int ret;
23
24 buf = kzalloc(bytes + 1, GFP_KERNEL);
25 if (!buf)
26 return -ENOMEM;
27
28 data = (unsigned char)reg;
29 ret = i2c_master_send(i2c, &data, 1);
30 if (ret < 0)
31 return ret;
32
33 ret = i2c_master_recv(i2c, buf, bytes + 1);
34 if (ret < 0)
35 return ret;
36 memcpy(dest, buf, bytes);
37 return 0;
38}
39
40static inline int max8925_write_device(struct i2c_client *i2c,
41 int reg, int bytes, void *src)
42{
43 unsigned char buf[bytes + 1];
44 int ret;
45
46 buf[0] = (unsigned char)reg;
47 memcpy(&buf[1], src, bytes);
48
49 ret = i2c_master_send(i2c, buf, bytes + 1);
50 if (ret < 0)
51 return ret;
52 return 0;
53}
54
55int max8925_reg_read(struct i2c_client *i2c, int reg)
56{
57 struct max8925_chip *chip = i2c_get_clientdata(i2c);
58 unsigned char data;
59 int ret;
60
61 mutex_lock(&chip->io_lock);
62 ret = max8925_read_device(i2c, reg, 1, &data);
63 mutex_unlock(&chip->io_lock);
64
65 if (ret < 0)
66 return ret;
67 else
68 return (int)data;
69}
70EXPORT_SYMBOL(max8925_reg_read);
71
72int max8925_reg_write(struct i2c_client *i2c, int reg,
73 unsigned char data)
74{
75 struct max8925_chip *chip = i2c_get_clientdata(i2c);
76 int ret;
77
78 mutex_lock(&chip->io_lock);
79 ret = max8925_write_device(i2c, reg, 1, &data);
80 mutex_unlock(&chip->io_lock);
81
82 return ret;
83}
84EXPORT_SYMBOL(max8925_reg_write);
85
86int max8925_bulk_read(struct i2c_client *i2c, int reg,
87 int count, unsigned char *buf)
88{
89 struct max8925_chip *chip = i2c_get_clientdata(i2c);
90 int ret;
91
92 mutex_lock(&chip->io_lock);
93 ret = max8925_read_device(i2c, reg, count, buf);
94 mutex_unlock(&chip->io_lock);
95
96 return ret;
97}
98EXPORT_SYMBOL(max8925_bulk_read);
99
100int max8925_bulk_write(struct i2c_client *i2c, int reg,
101 int count, unsigned char *buf)
102{
103 struct max8925_chip *chip = i2c_get_clientdata(i2c);
104 int ret;
105
106 mutex_lock(&chip->io_lock);
107 ret = max8925_write_device(i2c, reg, count, buf);
108 mutex_unlock(&chip->io_lock);
109
110 return ret;
111}
112EXPORT_SYMBOL(max8925_bulk_write);
113
114int max8925_set_bits(struct i2c_client *i2c, int reg,
115 unsigned char mask, unsigned char data)
116{
117 struct max8925_chip *chip = i2c_get_clientdata(i2c);
118 unsigned char value;
119 int ret;
120
121 mutex_lock(&chip->io_lock);
122 ret = max8925_read_device(i2c, reg, 1, &value);
123 if (ret < 0)
124 goto out;
125 value &= ~mask;
126 value |= data;
127 ret = max8925_write_device(i2c, reg, 1, &value);
128out:
129 mutex_unlock(&chip->io_lock);
130 return ret;
131}
132EXPORT_SYMBOL(max8925_set_bits);
133
134
135static const struct i2c_device_id max8925_id_table[] = {
136 { "max8925", 0 },
137 {}
138};
139MODULE_DEVICE_TABLE(i2c, max8925_id_table);
140
141static int __devinit max8925_probe(struct i2c_client *client,
142 const struct i2c_device_id *id)
143{
144 struct max8925_platform_data *pdata = client->dev.platform_data;
145 struct max8925_chip *chip;
146
147 if (!pdata) {
148 pr_info("%s: platform data is missing\n", __func__);
149 return -EINVAL;
150 }
151 if ((pdata->chip_id <= MAX8925_INVALID)
152 || (pdata->chip_id >= MAX8925_MAX)) {
153 pr_info("#%s: wrong chip identification\n", __func__);
154 return -EINVAL;
155 }
156
157 chip = kzalloc(sizeof(struct max8925_chip), GFP_KERNEL);
158 if (chip == NULL)
159 return -ENOMEM;
160 chip->i2c = client;
161 chip->chip_id = pdata->chip_id;
162 i2c_set_clientdata(client, chip);
163 chip->dev = &client->dev;
164 mutex_init(&chip->io_lock);
165 dev_set_drvdata(chip->dev, chip);
166 max8925_device_init(chip, pdata);
167
168 return 0;
169}
170
171static int __devexit max8925_remove(struct i2c_client *client)
172{
173 struct max8925_chip *chip = i2c_get_clientdata(client);
174
175 max8925_device_exit(chip);
176 i2c_set_clientdata(client, NULL);
177 kfree(chip);
178 return 0;
179}
180
181static struct i2c_driver max8925_driver = {
182 .driver = {
183 .name = "max8925",
184 .owner = THIS_MODULE,
185 },
186 .probe = max8925_probe,
187 .remove = __devexit_p(max8925_remove),
188 .id_table = max8925_id_table,
189};
190
191static int __init max8925_i2c_init(void)
192{
193 int ret;
194
195 ret = i2c_add_driver(&max8925_driver);
196 if (ret != 0)
197 pr_err("Failed to register MAX8925 I2C driver: %d\n", ret);
198 return ret;
199}
200subsys_initcall(max8925_i2c_init);
201
202static void __exit max8925_i2c_exit(void)
203{
204 i2c_del_driver(&max8925_driver);
205}
206module_exit(max8925_i2c_exit);
207
208MODULE_DESCRIPTION("I2C Driver for Maxim 8925");
209MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
210MODULE_LICENSE("GPL");