xref: /openbmc/linux/drivers/mfd/88pm860x-core.c (revision 5c42e8c4)
1 /*
2  * Base driver for Marvell 88PM8607
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/88pm860x.h>
19 
20 
21 #define PM8607_REG_RESOURCE(_start, _end)		\
22 {							\
23 	.start	= PM8607_##_start,			\
24 	.end	= PM8607_##_end,			\
25 	.flags	= IORESOURCE_IO,			\
26 }
27 
28 static struct resource pm8607_regulator_resources[] = {
29 	PM8607_REG_RESOURCE(BUCK1, BUCK1),
30 	PM8607_REG_RESOURCE(BUCK2, BUCK2),
31 	PM8607_REG_RESOURCE(BUCK3, BUCK3),
32 	PM8607_REG_RESOURCE(LDO1,  LDO1),
33 	PM8607_REG_RESOURCE(LDO2,  LDO2),
34 	PM8607_REG_RESOURCE(LDO3,  LDO3),
35 	PM8607_REG_RESOURCE(LDO4,  LDO4),
36 	PM8607_REG_RESOURCE(LDO5,  LDO5),
37 	PM8607_REG_RESOURCE(LDO6,  LDO6),
38 	PM8607_REG_RESOURCE(LDO7,  LDO7),
39 	PM8607_REG_RESOURCE(LDO8,  LDO8),
40 	PM8607_REG_RESOURCE(LDO9,  LDO9),
41 	PM8607_REG_RESOURCE(LDO10, LDO10),
42 	PM8607_REG_RESOURCE(LDO12, LDO12),
43 	PM8607_REG_RESOURCE(LDO14, LDO14),
44 };
45 
46 #define PM8607_REG_DEVS(_name, _id)					\
47 {									\
48 	.name		= "88pm8607-" #_name,				\
49 	.num_resources	= 1,						\
50 	.resources	= &pm8607_regulator_resources[PM8607_ID_##_id],	\
51 }
52 
53 static struct mfd_cell pm8607_devs[] = {
54 	PM8607_REG_DEVS(buck1, BUCK1),
55 	PM8607_REG_DEVS(buck2, BUCK2),
56 	PM8607_REG_DEVS(buck3, BUCK3),
57 	PM8607_REG_DEVS(ldo1,  LDO1),
58 	PM8607_REG_DEVS(ldo2,  LDO2),
59 	PM8607_REG_DEVS(ldo3,  LDO3),
60 	PM8607_REG_DEVS(ldo4,  LDO4),
61 	PM8607_REG_DEVS(ldo5,  LDO5),
62 	PM8607_REG_DEVS(ldo6,  LDO6),
63 	PM8607_REG_DEVS(ldo7,  LDO7),
64 	PM8607_REG_DEVS(ldo8,  LDO8),
65 	PM8607_REG_DEVS(ldo9,  LDO9),
66 	PM8607_REG_DEVS(ldo10, LDO10),
67 	PM8607_REG_DEVS(ldo12, LDO12),
68 	PM8607_REG_DEVS(ldo14, LDO14),
69 };
70 
71 #define CHECK_IRQ(irq)					\
72 do {							\
73 	if ((irq < 0) || (irq >= PM860X_NUM_IRQ))	\
74 		return -EINVAL;				\
75 } while (0)
76 
77 /* IRQs only occur on 88PM8607 */
78 int 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 }
92 EXPORT_SYMBOL(pm860x_mask_irq);
93 
94 int 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 }
108 EXPORT_SYMBOL(pm860x_unmask_irq);
109 
110 #define INT_STATUS_NUM		(3)
111 
112 static 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 	}
162 out:
163 	return IRQ_HANDLED;
164 }
165 
166 int pm860x_request_irq(struct pm860x_chip *chip, int irq,
167 		       irq_handler_t handler, void *data)
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;
179 }
180 EXPORT_SYMBOL(pm860x_request_irq);
181 
182 int pm860x_free_irq(struct pm860x_chip *chip, int irq)
183 {
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 }
193 EXPORT_SYMBOL(pm860x_free_irq);
194 
195 static 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;
253 out:
254 	return ret;
255 }
256 
257 static 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 
263 static void __devinit device_8606_init(struct pm860x_chip *chip,
264 				       struct i2c_client *i2c,
265 				       struct pm860x_platform_data *pdata)
266 {
267 }
268 
269 static 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;
274 	int ret;
275 
276 	ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
277 	if (ret < 0) {
278 		dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
279 		goto out;
280 	}
281 	if ((ret & PM8607_VERSION_MASK) == PM8607_VERSION)
282 		dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
283 			 ret);
284 	else {
285 		dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
286 			"Chip ID: %02x\n", ret);
287 		goto out;
288 	}
289 
290 	ret = pm860x_reg_read(i2c, PM8607_BUCK3);
291 	if (ret < 0) {
292 		dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
293 		goto out;
294 	}
295 	if (ret & PM8607_BUCK3_DOUBLE)
296 		chip->buck3_double = 1;
297 
298 	ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
299 	if (ret < 0) {
300 		dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
301 		goto out;
302 	}
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 
318 	count = ARRAY_SIZE(pm8607_devs);
319 	for (i = 0; i < count; i++) {
320 		ret = mfd_add_devices(chip->dev, i, &pm8607_devs[i],
321 				      1, NULL, 0);
322 		if (ret != 0) {
323 			dev_err(chip->dev, "Failed to add subdevs\n");
324 			goto out;
325 		}
326 	}
327 out:
328 	return;
329 }
330 
331 int pm860x_device_init(struct pm860x_chip *chip,
332 		       struct pm860x_platform_data *pdata)
333 {
334 	chip->chip_irq = -EINVAL;
335 
336 	switch (chip->id) {
337 	case CHIP_PM8606:
338 		device_8606_init(chip, chip->client, pdata);
339 		break;
340 	case CHIP_PM8607:
341 		device_8607_init(chip, chip->client, pdata);
342 		break;
343 	}
344 
345 	if (chip->companion) {
346 		switch (chip->id) {
347 		case CHIP_PM8607:
348 			device_8606_init(chip, chip->companion, pdata);
349 			break;
350 		case CHIP_PM8606:
351 			device_8607_init(chip, chip->companion, pdata);
352 			break;
353 		}
354 	}
355 
356 	return 0;
357 }
358 
359 void pm860x_device_exit(struct pm860x_chip *chip)
360 {
361 	device_irq_exit(chip);
362 	mfd_remove_devices(chip->dev);
363 }
364 
365 MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
366 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
367 MODULE_LICENSE("GPL");
368