aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorHaojian Zhuang <haojian.zhuang@marvell.com>2009-12-15 16:01:47 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2010-03-07 16:17:03 -0500
commit5c42e8c4a9c86ea26ed4ecb732a842dea0dfb6b6 (patch)
treeffac1c091a0bedde01c802123e7a602945fd6f62 /drivers
parent2cc50bee9934deb6dfe32929a4c1742cf83d6db3 (diff)
mfd: Add irq support in 88pm860x
88PM860x is a complex PMIC device. It contains touch, charger, sound, rtc, backlight, led, and so on. Host communicates to 88PM860x by I2C bus. Use thread irq to support this usage case. Signed-off-by: Haojian Zhuang <haojian.zhuang@marvell.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/88pm860x-core.c226
1 files changed, 219 insertions, 7 deletions
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 72b00304dc3a..9185f0d945f4 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -11,6 +11,7 @@
11 11
12#include <linux/kernel.h> 12#include <linux/kernel.h>
13#include <linux/module.h> 13#include <linux/module.h>
14#include <linux/i2c.h>
14#include <linux/interrupt.h> 15#include <linux/interrupt.h>
15#include <linux/platform_device.h> 16#include <linux/platform_device.h>
16#include <linux/mfd/core.h> 17#include <linux/mfd/core.h>
@@ -67,15 +68,209 @@ static struct mfd_cell pm8607_devs[] = {
67 PM8607_REG_DEVS(ldo14, LDO14), 68 PM8607_REG_DEVS(ldo14, LDO14),
68}; 69};
69 70
70static void device_8606_init(struct pm860x_chip *chip, struct i2c_client *i2c, 71#define CHECK_IRQ(irq) \
71 struct pm860x_platform_data *pdata) 72do { \
73 if ((irq < 0) || (irq >= PM860X_NUM_IRQ)) \
74 return -EINVAL; \
75} while (0)
76
77/* IRQs only occur on 88PM8607 */
78int pm860x_mask_irq(struct pm860x_chip *chip, int irq)
79{
80 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
81 : chip->companion;
82 int offset, data, ret;
83
84 CHECK_IRQ(irq);
85
86 offset = (irq >> 3) + PM8607_INT_MASK_1;
87 data = 1 << (irq % 8);
88 ret = pm860x_set_bits(i2c, offset, data, 0);
89
90 return ret;
91}
92EXPORT_SYMBOL(pm860x_mask_irq);
93
94int pm860x_unmask_irq(struct pm860x_chip *chip, int irq)
95{
96 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
97 : chip->companion;
98 int offset, data, ret;
99
100 CHECK_IRQ(irq);
101
102 offset = (irq >> 3) + PM8607_INT_MASK_1;
103 data = 1 << (irq % 8);
104 ret = pm860x_set_bits(i2c, offset, data, data);
105
106 return ret;
107}
108EXPORT_SYMBOL(pm860x_unmask_irq);
109
110#define INT_STATUS_NUM (3)
111
112static irqreturn_t pm8607_irq_thread(int irq, void *data)
113{
114 DECLARE_BITMAP(irq_status, PM860X_NUM_IRQ);
115 struct pm860x_chip *chip = data;
116 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
117 : chip->companion;
118 unsigned char status_buf[INT_STATUS_NUM << 1];
119 unsigned long value;
120 int i, ret;
121
122 irq_status[0] = 0;
123
124 /* read out status register */
125 ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
126 INT_STATUS_NUM << 1, status_buf);
127 if (ret < 0)
128 goto out;
129 if (chip->irq_mode) {
130 /* 0, clear by read. 1, clear by write */
131 ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
132 INT_STATUS_NUM, status_buf);
133 if (ret < 0)
134 goto out;
135 }
136
137 /* clear masked interrupt status */
138 for (i = 0, value = 0; i < INT_STATUS_NUM; i++) {
139 status_buf[i] &= status_buf[i + INT_STATUS_NUM];
140 irq_status[0] |= status_buf[i] << (i * 8);
141 }
142
143 while (!bitmap_empty(irq_status, PM860X_NUM_IRQ)) {
144 irq = find_first_bit(irq_status, PM860X_NUM_IRQ);
145 clear_bit(irq, irq_status);
146 dev_dbg(chip->dev, "Servicing IRQ #%d\n", irq);
147
148 mutex_lock(&chip->irq_lock);
149 if (chip->irq[irq].handler)
150 chip->irq[irq].handler(irq, chip->irq[irq].data);
151 else {
152 pm860x_mask_irq(chip, irq);
153 dev_err(chip->dev, "Nobody cares IRQ %d. "
154 "Now mask it.\n", irq);
155 for (i = 0; i < (INT_STATUS_NUM << 1); i++) {
156 dev_err(chip->dev, "status[%d]:%x\n", i,
157 status_buf[i]);
158 }
159 }
160 mutex_unlock(&chip->irq_lock);
161 }
162out:
163 return IRQ_HANDLED;
164}
165
166int pm860x_request_irq(struct pm860x_chip *chip, int irq,
167 irq_handler_t handler, void *data)
72{ 168{
169 CHECK_IRQ(irq);
170 if (!handler)
171 return -EINVAL;
172
173 mutex_lock(&chip->irq_lock);
174 chip->irq[irq].handler = handler;
175 chip->irq[irq].data = data;
176 mutex_unlock(&chip->irq_lock);
177
178 return 0;
73} 179}
180EXPORT_SYMBOL(pm860x_request_irq);
74 181
75static void device_8607_init(struct pm860x_chip *chip, struct i2c_client *i2c, 182int pm860x_free_irq(struct pm860x_chip *chip, int irq)
76 struct pm860x_platform_data *pdata)
77{ 183{
78 int i, count; 184 CHECK_IRQ(irq);
185
186 mutex_lock(&chip->irq_lock);
187 chip->irq[irq].handler = NULL;
188 chip->irq[irq].data = NULL;
189 mutex_unlock(&chip->irq_lock);
190
191 return 0;
192}
193EXPORT_SYMBOL(pm860x_free_irq);
194
195static int __devinit device_irq_init(struct pm860x_chip *chip,
196 struct pm860x_platform_data *pdata)
197{
198 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
199 : chip->companion;
200 unsigned char status_buf[INT_STATUS_NUM];
201 int data, mask, ret = -EINVAL;
202
203 mutex_init(&chip->irq_lock);
204
205 mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
206 | PM8607_B0_MISC1_INT_MASK;
207 data = 0;
208 chip->irq_mode = 0;
209 if (pdata && pdata->irq_mode) {
210 /*
211 * irq_mode defines the way of clearing interrupt. If it's 1,
212 * clear IRQ by write. Otherwise, clear it by read.
213 * This control bit is valid from 88PM8607 B0 steping.
214 */
215 data |= PM8607_B0_MISC1_INT_CLEAR;
216 chip->irq_mode = 1;
217 }
218 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
219 if (ret < 0)
220 goto out;
221
222 /* mask all IRQs */
223 memset(status_buf, 0, INT_STATUS_NUM);
224 ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
225 INT_STATUS_NUM, status_buf);
226 if (ret < 0)
227 goto out;
228
229 if (chip->irq_mode) {
230 /* clear interrupt status by write */
231 memset(status_buf, 0xFF, INT_STATUS_NUM);
232 ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
233 INT_STATUS_NUM, status_buf);
234 } else {
235 /* clear interrupt status by read */
236 ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
237 INT_STATUS_NUM, status_buf);
238 }
239 if (ret < 0)
240 goto out;
241
242 memset(chip->irq, 0, sizeof(struct pm860x_irq) * PM860X_NUM_IRQ);
243
244 ret = request_threaded_irq(i2c->irq, NULL, pm8607_irq_thread,
245 IRQF_ONESHOT | IRQF_TRIGGER_LOW,
246 "88PM8607", chip);
247 if (ret < 0) {
248 dev_err(chip->dev, "Failed to request IRQ #%d.\n", i2c->irq);
249 goto out;
250 }
251 chip->chip_irq = i2c->irq;
252 return 0;
253out:
254 return ret;
255}
256
257static void __devexit device_irq_exit(struct pm860x_chip *chip)
258{
259 if (chip->chip_irq >= 0)
260 free_irq(chip->chip_irq, chip);
261}
262
263static void __devinit device_8606_init(struct pm860x_chip *chip,
264 struct i2c_client *i2c,
265 struct pm860x_platform_data *pdata)
266{
267}
268
269static void __devinit device_8607_init(struct pm860x_chip *chip,
270 struct i2c_client *i2c,
271 struct pm860x_platform_data *pdata)
272{
273 int i, count, data;
79 int ret; 274 int ret;
80 275
81 ret = pm860x_reg_read(i2c, PM8607_CHIP_ID); 276 ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
@@ -91,7 +286,6 @@ static void device_8607_init(struct pm860x_chip *chip, struct i2c_client *i2c,
91 "Chip ID: %02x\n", ret); 286 "Chip ID: %02x\n", ret);
92 goto out; 287 goto out;
93 } 288 }
94 chip->chip_version = ret;
95 289
96 ret = pm860x_reg_read(i2c, PM8607_BUCK3); 290 ret = pm860x_reg_read(i2c, PM8607_BUCK3);
97 if (ret < 0) { 291 if (ret < 0) {
@@ -101,12 +295,26 @@ static void device_8607_init(struct pm860x_chip *chip, struct i2c_client *i2c,
101 if (ret & PM8607_BUCK3_DOUBLE) 295 if (ret & PM8607_BUCK3_DOUBLE)
102 chip->buck3_double = 1; 296 chip->buck3_double = 1;
103 297
104 ret = pm860x_reg_read(i2c, PM8607_MISC1); 298 ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
105 if (ret < 0) { 299 if (ret < 0) {
106 dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret); 300 dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
107 goto out; 301 goto out;
108 } 302 }
109 303
304 if (pdata && (pdata->i2c_port == PI2C_PORT))
305 data = PM8607_B0_MISC1_PI2C;
306 else
307 data = 0;
308 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
309 if (ret < 0) {
310 dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
311 goto out;
312 }
313
314 ret = device_irq_init(chip, pdata);
315 if (ret < 0)
316 goto out;
317
110 count = ARRAY_SIZE(pm8607_devs); 318 count = ARRAY_SIZE(pm8607_devs);
111 for (i = 0; i < count; i++) { 319 for (i = 0; i < count; i++) {
112 ret = mfd_add_devices(chip->dev, i, &pm8607_devs[i], 320 ret = mfd_add_devices(chip->dev, i, &pm8607_devs[i],
@@ -123,6 +331,8 @@ out:
123int pm860x_device_init(struct pm860x_chip *chip, 331int pm860x_device_init(struct pm860x_chip *chip,
124 struct pm860x_platform_data *pdata) 332 struct pm860x_platform_data *pdata)
125{ 333{
334 chip->chip_irq = -EINVAL;
335
126 switch (chip->id) { 336 switch (chip->id) {
127 case CHIP_PM8606: 337 case CHIP_PM8606:
128 device_8606_init(chip, chip->client, pdata); 338 device_8606_init(chip, chip->client, pdata);
@@ -142,11 +352,13 @@ int pm860x_device_init(struct pm860x_chip *chip,
142 break; 352 break;
143 } 353 }
144 } 354 }
355
145 return 0; 356 return 0;
146} 357}
147 358
148void pm860x_device_exit(struct pm860x_chip *chip) 359void pm860x_device_exit(struct pm860x_chip *chip)
149{ 360{
361 device_irq_exit(chip);
150 mfd_remove_devices(chip->dev); 362 mfd_remove_devices(chip->dev);
151} 363}
152 364