aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/mfd/88pm860x-core.c226
-rw-r--r--include/linux/mfd/88pm860x.h54
2 files changed, 247 insertions, 33 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
diff --git a/include/linux/mfd/88pm860x.h b/include/linux/mfd/88pm860x.h
index 5845ae47df30..b4d6018ba0d6 100644
--- a/include/linux/mfd/88pm860x.h
+++ b/include/linux/mfd/88pm860x.h
@@ -12,6 +12,8 @@
12#ifndef __LINUX_MFD_88PM860X_H 12#ifndef __LINUX_MFD_88PM860X_H
13#define __LINUX_MFD_88PM860X_H 13#define __LINUX_MFD_88PM860X_H
14 14
15#include <linux/interrupt.h>
16
15enum { 17enum {
16 CHIP_INVALID = 0, 18 CHIP_INVALID = 0,
17 CHIP_PM8606, 19 CHIP_PM8606,
@@ -109,33 +111,10 @@ enum {
109 111
110/* Misc Registers */ 112/* Misc Registers */
111#define PM8607_CHIP_ID (0x00) 113#define PM8607_CHIP_ID (0x00)
114#define PM8607_B0_MISC1 (0x0C)
112#define PM8607_LDO1 (0x10) 115#define PM8607_LDO1 (0x10)
113#define PM8607_DVC3 (0x26) 116#define PM8607_DVC3 (0x26)
114#define PM8607_MISC1 (0x40) 117#define PM8607_A1_MISC1 (0x40)
115
116/* bit definitions for PM8607 events */
117#define PM8607_EVENT_ONKEY (1 << 0)
118#define PM8607_EVENT_EXTON (1 << 1)
119#define PM8607_EVENT_CHG (1 << 2)
120#define PM8607_EVENT_BAT (1 << 3)
121#define PM8607_EVENT_RTC (1 << 4)
122#define PM8607_EVENT_CC (1 << 5)
123#define PM8607_EVENT_VBAT (1 << 8)
124#define PM8607_EVENT_VCHG (1 << 9)
125#define PM8607_EVENT_VSYS (1 << 10)
126#define PM8607_EVENT_TINT (1 << 11)
127#define PM8607_EVENT_GPADC0 (1 << 12)
128#define PM8607_EVENT_GPADC1 (1 << 13)
129#define PM8607_EVENT_GPADC2 (1 << 14)
130#define PM8607_EVENT_GPADC3 (1 << 15)
131#define PM8607_EVENT_AUDIO_SHORT (1 << 16)
132#define PM8607_EVENT_PEN (1 << 17)
133#define PM8607_EVENT_HEADSET (1 << 18)
134#define PM8607_EVENT_HOOK (1 << 19)
135#define PM8607_EVENT_MICIN (1 << 20)
136#define PM8607_EVENT_CHG_TIMEOUT (1 << 21)
137#define PM8607_EVENT_CHG_DONE (1 << 22)
138#define PM8607_EVENT_CHG_FAULT (1 << 23)
139 118
140/* bit definitions of Status Query Interface */ 119/* bit definitions of Status Query Interface */
141#define PM8607_STATUS_CC (1 << 3) 120#define PM8607_STATUS_CC (1 << 3)
@@ -154,7 +133,12 @@ enum {
154#define PM8607_BUCK3_DOUBLE (1 << 6) 133#define PM8607_BUCK3_DOUBLE (1 << 6)
155 134
156/* bit definitions of Misc1 */ 135/* bit definitions of Misc1 */
157#define PM8607_MISC1_PI2C (1 << 0) 136#define PM8607_A1_MISC1_PI2C (1 << 0)
137#define PM8607_B0_MISC1_INV_INT (1 << 0)
138#define PM8607_B0_MISC1_INT_CLEAR (1 << 1)
139#define PM8607_B0_MISC1_INT_MASK (1 << 2)
140#define PM8607_B0_MISC1_PI2C (1 << 3)
141#define PM8607_B0_MISC1_RESET (1 << 6)
158 142
159/* Interrupt Number in 88PM8607 */ 143/* Interrupt Number in 88PM8607 */
160enum { 144enum {
@@ -187,15 +171,26 @@ enum {
187 PM8607_CHIP_B0 = 0x48, 171 PM8607_CHIP_B0 = 0x48,
188}; 172};
189 173
174#define PM860X_NUM_IRQ 24
175
176struct pm860x_irq {
177 irq_handler_t handler;
178 void *data;
179};
180
190struct pm860x_chip { 181struct pm860x_chip {
191 struct device *dev; 182 struct device *dev;
192 struct mutex io_lock; 183 struct mutex io_lock;
184 struct mutex irq_lock;
193 struct i2c_client *client; 185 struct i2c_client *client;
194 struct i2c_client *companion; /* companion chip client */ 186 struct i2c_client *companion; /* companion chip client */
187 struct pm860x_irq irq[PM860X_NUM_IRQ];
195 188
196 int buck3_double; /* DVC ramp slope double */ 189 int buck3_double; /* DVC ramp slope double */
197 unsigned short companion_addr; 190 unsigned short companion_addr;
198 int id; 191 int id;
192 int irq_mode;
193 int chip_irq;
199 unsigned char chip_version; 194 unsigned char chip_version;
200 195
201}; 196};
@@ -210,6 +205,7 @@ enum {
210struct pm860x_platform_data { 205struct pm860x_platform_data {
211 unsigned short companion_addr; /* I2C address of companion chip */ 206 unsigned short companion_addr; /* I2C address of companion chip */
212 int i2c_port; /* Controlled by GI2C or PI2C */ 207 int i2c_port; /* Controlled by GI2C or PI2C */
208 int irq_mode; /* Clear interrupt by read/write(0/1) */
213 struct regulator_init_data *regulator[PM8607_MAX_REGULATOR]; 209 struct regulator_init_data *regulator[PM8607_MAX_REGULATOR];
214}; 210};
215 211
@@ -220,6 +216,12 @@ extern int pm860x_bulk_write(struct i2c_client *, int, int, unsigned char *);
220extern int pm860x_set_bits(struct i2c_client *, int, unsigned char, 216extern int pm860x_set_bits(struct i2c_client *, int, unsigned char,
221 unsigned char); 217 unsigned char);
222 218
219extern int pm860x_mask_irq(struct pm860x_chip *, int);
220extern int pm860x_unmask_irq(struct pm860x_chip *, int);
221extern int pm860x_request_irq(struct pm860x_chip *, int,
222 irq_handler_t handler, void *);
223extern int pm860x_free_irq(struct pm860x_chip *, int);
224
223extern int pm860x_device_init(struct pm860x_chip *chip, 225extern int pm860x_device_init(struct pm860x_chip *chip,
224 struct pm860x_platform_data *pdata); 226 struct pm860x_platform_data *pdata);
225extern void pm860x_device_exit(struct pm860x_chip *chip); 227extern void pm860x_device_exit(struct pm860x_chip *chip);