xref: /openbmc/linux/drivers/mfd/88pm860x-core.c (revision a16122bc)
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 char pm860x_backlight_name[][MFD_NAME_SIZE] = {
21 	"backlight-0",
22 	"backlight-1",
23 	"backlight-2",
24 };
25 EXPORT_SYMBOL(pm860x_backlight_name);
26 
27 char pm860x_led_name[][MFD_NAME_SIZE] = {
28 	"led0-red",
29 	"led0-green",
30 	"led0-blue",
31 	"led1-red",
32 	"led1-green",
33 	"led1-blue",
34 };
35 EXPORT_SYMBOL(pm860x_led_name);
36 
37 #define PM8606_BACKLIGHT_RESOURCE(_i, _x)		\
38 {							\
39 	.name	= pm860x_backlight_name[_i],		\
40 	.start	= PM8606_##_x,				\
41 	.end	= PM8606_##_x,				\
42 	.flags	= IORESOURCE_IO,			\
43 }
44 
45 static struct resource backlight_resources[] = {
46 	PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT1, WLED1A),
47 	PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT2, WLED2A),
48 	PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT3, WLED3A),
49 };
50 
51 #define PM8606_BACKLIGHT_DEVS(_i)			\
52 {							\
53 	.name		= "88pm860x-backlight",		\
54 	.num_resources	= 1,				\
55 	.resources	= &backlight_resources[_i],	\
56 	.id		= _i,				\
57 }
58 
59 static struct mfd_cell backlight_devs[] = {
60 	PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT1),
61 	PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT2),
62 	PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT3),
63 };
64 
65 #define PM8606_LED_RESOURCE(_i, _x)			\
66 {							\
67 	.name	= pm860x_led_name[_i],			\
68 	.start	= PM8606_##_x,				\
69 	.end	= PM8606_##_x,				\
70 	.flags	= IORESOURCE_IO,			\
71 }
72 
73 static struct resource led_resources[] = {
74 	PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB2B),
75 	PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB2C),
76 	PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB2D),
77 	PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB1B),
78 	PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB1C),
79 	PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB1D),
80 };
81 
82 #define PM8606_LED_DEVS(_i)				\
83 {							\
84 	.name		= "88pm860x-led",		\
85 	.num_resources	= 1,				\
86 	.resources	= &led_resources[_i],		\
87 	.id		= _i,				\
88 }
89 
90 static struct mfd_cell led_devs[] = {
91 	PM8606_LED_DEVS(PM8606_LED1_RED),
92 	PM8606_LED_DEVS(PM8606_LED1_GREEN),
93 	PM8606_LED_DEVS(PM8606_LED1_BLUE),
94 	PM8606_LED_DEVS(PM8606_LED2_RED),
95 	PM8606_LED_DEVS(PM8606_LED2_GREEN),
96 	PM8606_LED_DEVS(PM8606_LED2_BLUE),
97 };
98 
99 static struct resource touch_resources[] = {
100 	{
101 		.start	= PM8607_IRQ_PEN,
102 		.end	= PM8607_IRQ_PEN,
103 		.flags	= IORESOURCE_IRQ,
104 	},
105 };
106 
107 static struct mfd_cell touch_devs[] = {
108 	{
109 		.name		= "88pm860x-touch",
110 		.num_resources	= 1,
111 		.resources	= &touch_resources[0],
112 	},
113 };
114 
115 #define PM8607_REG_RESOURCE(_start, _end)		\
116 {							\
117 	.start	= PM8607_##_start,			\
118 	.end	= PM8607_##_end,			\
119 	.flags	= IORESOURCE_IO,			\
120 }
121 
122 static struct resource regulator_resources[] = {
123 	PM8607_REG_RESOURCE(BUCK1, BUCK1),
124 	PM8607_REG_RESOURCE(BUCK2, BUCK2),
125 	PM8607_REG_RESOURCE(BUCK3, BUCK3),
126 	PM8607_REG_RESOURCE(LDO1,  LDO1),
127 	PM8607_REG_RESOURCE(LDO2,  LDO2),
128 	PM8607_REG_RESOURCE(LDO3,  LDO3),
129 	PM8607_REG_RESOURCE(LDO4,  LDO4),
130 	PM8607_REG_RESOURCE(LDO5,  LDO5),
131 	PM8607_REG_RESOURCE(LDO6,  LDO6),
132 	PM8607_REG_RESOURCE(LDO7,  LDO7),
133 	PM8607_REG_RESOURCE(LDO8,  LDO8),
134 	PM8607_REG_RESOURCE(LDO9,  LDO9),
135 	PM8607_REG_RESOURCE(LDO10, LDO10),
136 	PM8607_REG_RESOURCE(LDO12, LDO12),
137 	PM8607_REG_RESOURCE(LDO14, LDO14),
138 };
139 
140 #define PM8607_REG_DEVS(_name, _id)					\
141 {									\
142 	.name		= "88pm8607-" #_name,				\
143 	.num_resources	= 1,						\
144 	.resources	= &regulator_resources[PM8607_ID_##_id],	\
145 	.id		= PM8607_ID_##_id,				\
146 }
147 
148 static struct mfd_cell regulator_devs[] = {
149 	PM8607_REG_DEVS(buck1, BUCK1),
150 	PM8607_REG_DEVS(buck2, BUCK2),
151 	PM8607_REG_DEVS(buck3, BUCK3),
152 	PM8607_REG_DEVS(ldo1,  LDO1),
153 	PM8607_REG_DEVS(ldo2,  LDO2),
154 	PM8607_REG_DEVS(ldo3,  LDO3),
155 	PM8607_REG_DEVS(ldo4,  LDO4),
156 	PM8607_REG_DEVS(ldo5,  LDO5),
157 	PM8607_REG_DEVS(ldo6,  LDO6),
158 	PM8607_REG_DEVS(ldo7,  LDO7),
159 	PM8607_REG_DEVS(ldo8,  LDO8),
160 	PM8607_REG_DEVS(ldo9,  LDO9),
161 	PM8607_REG_DEVS(ldo10, LDO10),
162 	PM8607_REG_DEVS(ldo12, LDO12),
163 	PM8607_REG_DEVS(ldo14, LDO14),
164 };
165 
166 #define CHECK_IRQ(irq)					\
167 do {							\
168 	if ((irq < 0) || (irq >= PM860X_NUM_IRQ))	\
169 		return -EINVAL;				\
170 } while (0)
171 
172 /* IRQs only occur on 88PM8607 */
173 int pm860x_mask_irq(struct pm860x_chip *chip, int irq)
174 {
175 	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
176 				: chip->companion;
177 	int offset, data, ret;
178 
179 	CHECK_IRQ(irq);
180 
181 	offset = (irq >> 3) + PM8607_INT_MASK_1;
182 	data = 1 << (irq % 8);
183 	ret = pm860x_set_bits(i2c, offset, data, 0);
184 
185 	return ret;
186 }
187 EXPORT_SYMBOL(pm860x_mask_irq);
188 
189 int pm860x_unmask_irq(struct pm860x_chip *chip, int irq)
190 {
191 	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
192 				: chip->companion;
193 	int offset, data, ret;
194 
195 	CHECK_IRQ(irq);
196 
197 	offset = (irq >> 3) + PM8607_INT_MASK_1;
198 	data = 1 << (irq % 8);
199 	ret = pm860x_set_bits(i2c, offset, data, data);
200 
201 	return ret;
202 }
203 EXPORT_SYMBOL(pm860x_unmask_irq);
204 
205 #define INT_STATUS_NUM		(3)
206 
207 static irqreturn_t pm8607_irq_thread(int irq, void *data)
208 {
209 	DECLARE_BITMAP(irq_status, PM860X_NUM_IRQ);
210 	struct pm860x_chip *chip = data;
211 	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
212 				: chip->companion;
213 	unsigned char status_buf[INT_STATUS_NUM << 1];
214 	unsigned long value;
215 	int i, ret;
216 
217 	irq_status[0] = 0;
218 
219 	/* read out status register */
220 	ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
221 				INT_STATUS_NUM << 1, status_buf);
222 	if (ret < 0)
223 		goto out;
224 	if (chip->irq_mode) {
225 		/* 0, clear by read. 1, clear by write */
226 		ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
227 					INT_STATUS_NUM, status_buf);
228 		if (ret < 0)
229 			goto out;
230 	}
231 
232 	/* clear masked interrupt status */
233 	for (i = 0, value = 0; i < INT_STATUS_NUM; i++) {
234 		status_buf[i] &= status_buf[i + INT_STATUS_NUM];
235 		irq_status[0] |= status_buf[i] << (i * 8);
236 	}
237 
238 	while (!bitmap_empty(irq_status, PM860X_NUM_IRQ)) {
239 		irq = find_first_bit(irq_status, PM860X_NUM_IRQ);
240 		clear_bit(irq, irq_status);
241 		dev_dbg(chip->dev, "Servicing IRQ #%d\n", irq);
242 
243 		mutex_lock(&chip->irq_lock);
244 		if (chip->irq[irq].handler)
245 			chip->irq[irq].handler(irq, chip->irq[irq].data);
246 		else {
247 			pm860x_mask_irq(chip, irq);
248 			dev_err(chip->dev, "Nobody cares IRQ %d. "
249 				"Now mask it.\n", irq);
250 			for (i = 0; i < (INT_STATUS_NUM << 1); i++) {
251 				dev_err(chip->dev, "status[%d]:%x\n", i,
252 					status_buf[i]);
253 			}
254 		}
255 		mutex_unlock(&chip->irq_lock);
256 	}
257 out:
258 	return IRQ_HANDLED;
259 }
260 
261 int pm860x_request_irq(struct pm860x_chip *chip, int irq,
262 		       irq_handler_t handler, void *data)
263 {
264 	CHECK_IRQ(irq);
265 	if (!handler)
266 		return -EINVAL;
267 
268 	mutex_lock(&chip->irq_lock);
269 	chip->irq[irq].handler = handler;
270 	chip->irq[irq].data = data;
271 	mutex_unlock(&chip->irq_lock);
272 
273 	return 0;
274 }
275 EXPORT_SYMBOL(pm860x_request_irq);
276 
277 int pm860x_free_irq(struct pm860x_chip *chip, int irq)
278 {
279 	CHECK_IRQ(irq);
280 
281 	mutex_lock(&chip->irq_lock);
282 	chip->irq[irq].handler = NULL;
283 	chip->irq[irq].data = NULL;
284 	mutex_unlock(&chip->irq_lock);
285 
286 	return 0;
287 }
288 EXPORT_SYMBOL(pm860x_free_irq);
289 
290 static int __devinit device_gpadc_init(struct pm860x_chip *chip,
291 				       struct pm860x_platform_data *pdata)
292 {
293 	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
294 				: chip->companion;
295 	int use_gpadc = 0, data, ret;
296 
297 	/* initialize GPADC without activating it */
298 
299 	if (pdata && pdata->touch) {
300 		/* set GPADC MISC1 register */
301 		data = 0;
302 		data |= (pdata->touch->gpadc_prebias << 1)
303 			& PM8607_GPADC_PREBIAS_MASK;
304 		data |= (pdata->touch->slot_cycle << 3)
305 			& PM8607_GPADC_SLOT_CYCLE_MASK;
306 		data |= (pdata->touch->off_scale << 5)
307 			& PM8607_GPADC_OFF_SCALE_MASK;
308 		data |= (pdata->touch->sw_cal << 7)
309 			& PM8607_GPADC_SW_CAL_MASK;
310 		if (data) {
311 			ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
312 			if (ret < 0)
313 				goto out;
314 		}
315 		/* set tsi prebias time */
316 		if (pdata->touch->tsi_prebias) {
317 			data = pdata->touch->tsi_prebias;
318 			ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
319 			if (ret < 0)
320 				goto out;
321 		}
322 		/* set prebias & prechg time of pen detect */
323 		data = 0;
324 		data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
325 		data |= (pdata->touch->pen_prechg << 5)
326 			& PM8607_PD_PRECHG_MASK;
327 		if (data) {
328 			ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
329 			if (ret < 0)
330 				goto out;
331 		}
332 
333 		use_gpadc = 1;
334 	}
335 
336 	/* turn on GPADC */
337 	if (use_gpadc) {
338 		ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
339 				      PM8607_GPADC_EN, PM8607_GPADC_EN);
340 	}
341 out:
342 	return ret;
343 }
344 
345 static int __devinit device_irq_init(struct pm860x_chip *chip,
346 				     struct pm860x_platform_data *pdata)
347 {
348 	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
349 				: chip->companion;
350 	unsigned char status_buf[INT_STATUS_NUM];
351 	int data, mask, ret = -EINVAL;
352 
353 	mutex_init(&chip->irq_lock);
354 
355 	mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
356 		| PM8607_B0_MISC1_INT_MASK;
357 	data = 0;
358 	chip->irq_mode = 0;
359 	if (pdata && pdata->irq_mode) {
360 		/*
361 		 * irq_mode defines the way of clearing interrupt. If it's 1,
362 		 * clear IRQ by write. Otherwise, clear it by read.
363 		 * This control bit is valid from 88PM8607 B0 steping.
364 		 */
365 		data |= PM8607_B0_MISC1_INT_CLEAR;
366 		chip->irq_mode = 1;
367 	}
368 	ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
369 	if (ret < 0)
370 		goto out;
371 
372 	/* mask all IRQs */
373 	memset(status_buf, 0, INT_STATUS_NUM);
374 	ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
375 				INT_STATUS_NUM, status_buf);
376 	if (ret < 0)
377 		goto out;
378 
379 	if (chip->irq_mode) {
380 		/* clear interrupt status by write */
381 		memset(status_buf, 0xFF, INT_STATUS_NUM);
382 		ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
383 					INT_STATUS_NUM, status_buf);
384 	} else {
385 		/* clear interrupt status by read */
386 		ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
387 					INT_STATUS_NUM, status_buf);
388 	}
389 	if (ret < 0)
390 		goto out;
391 
392 	memset(chip->irq, 0, sizeof(struct pm860x_irq) * PM860X_NUM_IRQ);
393 
394 	ret = request_threaded_irq(i2c->irq, NULL, pm8607_irq_thread,
395 				IRQF_ONESHOT | IRQF_TRIGGER_LOW,
396 				"88PM8607", chip);
397 	if (ret < 0) {
398 		dev_err(chip->dev, "Failed to request IRQ #%d.\n", i2c->irq);
399 		goto out;
400 	}
401 	chip->chip_irq = i2c->irq;
402 	return 0;
403 out:
404 	return ret;
405 }
406 
407 static void __devexit device_irq_exit(struct pm860x_chip *chip)
408 {
409 	if (chip->chip_irq >= 0)
410 		free_irq(chip->chip_irq, chip);
411 }
412 
413 static void __devinit device_8606_init(struct pm860x_chip *chip,
414 				       struct i2c_client *i2c,
415 				       struct pm860x_platform_data *pdata)
416 {
417 	int ret;
418 
419 	if (pdata && pdata->backlight) {
420 		ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0],
421 				      ARRAY_SIZE(backlight_devs),
422 				      &backlight_resources[0], 0);
423 		if (ret < 0) {
424 			dev_err(chip->dev, "Failed to add backlight "
425 				"subdev\n");
426 			goto out_dev;
427 		}
428 	}
429 
430 	if (pdata && pdata->led) {
431 		ret = mfd_add_devices(chip->dev, 0, &led_devs[0],
432 				      ARRAY_SIZE(led_devs),
433 				      &led_resources[0], 0);
434 		if (ret < 0) {
435 			dev_err(chip->dev, "Failed to add led "
436 				"subdev\n");
437 			goto out_dev;
438 		}
439 	}
440 	return;
441 out_dev:
442 	mfd_remove_devices(chip->dev);
443 	device_irq_exit(chip);
444 }
445 
446 static void __devinit device_8607_init(struct pm860x_chip *chip,
447 				       struct i2c_client *i2c,
448 				       struct pm860x_platform_data *pdata)
449 {
450 	int data, ret;
451 
452 	ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
453 	if (ret < 0) {
454 		dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
455 		goto out;
456 	}
457 	if ((ret & PM8607_VERSION_MASK) == PM8607_VERSION)
458 		dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
459 			 ret);
460 	else {
461 		dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
462 			"Chip ID: %02x\n", ret);
463 		goto out;
464 	}
465 
466 	ret = pm860x_reg_read(i2c, PM8607_BUCK3);
467 	if (ret < 0) {
468 		dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
469 		goto out;
470 	}
471 	if (ret & PM8607_BUCK3_DOUBLE)
472 		chip->buck3_double = 1;
473 
474 	ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
475 	if (ret < 0) {
476 		dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
477 		goto out;
478 	}
479 
480 	if (pdata && (pdata->i2c_port == PI2C_PORT))
481 		data = PM8607_B0_MISC1_PI2C;
482 	else
483 		data = 0;
484 	ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
485 	if (ret < 0) {
486 		dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
487 		goto out;
488 	}
489 
490 	ret = device_gpadc_init(chip, pdata);
491 	if (ret < 0)
492 		goto out;
493 
494 	ret = device_irq_init(chip, pdata);
495 	if (ret < 0)
496 		goto out;
497 
498 	ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
499 			      ARRAY_SIZE(regulator_devs),
500 			      &regulator_resources[0], 0);
501 	if (ret < 0) {
502 		dev_err(chip->dev, "Failed to add regulator subdev\n");
503 		goto out_dev;
504 	}
505 
506 	if (pdata && pdata->touch) {
507 		ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
508 				      ARRAY_SIZE(touch_devs),
509 				      &touch_resources[0], 0);
510 		if (ret < 0) {
511 			dev_err(chip->dev, "Failed to add touch "
512 				"subdev\n");
513 			goto out_dev;
514 		}
515 	}
516 	return;
517 out_dev:
518 	mfd_remove_devices(chip->dev);
519 	device_irq_exit(chip);
520 out:
521 	return;
522 }
523 
524 int pm860x_device_init(struct pm860x_chip *chip,
525 		       struct pm860x_platform_data *pdata)
526 {
527 	chip->chip_irq = -EINVAL;
528 
529 	switch (chip->id) {
530 	case CHIP_PM8606:
531 		device_8606_init(chip, chip->client, pdata);
532 		break;
533 	case CHIP_PM8607:
534 		device_8607_init(chip, chip->client, pdata);
535 		break;
536 	}
537 
538 	if (chip->companion) {
539 		switch (chip->id) {
540 		case CHIP_PM8607:
541 			device_8606_init(chip, chip->companion, pdata);
542 			break;
543 		case CHIP_PM8606:
544 			device_8607_init(chip, chip->companion, pdata);
545 			break;
546 		}
547 	}
548 
549 	return 0;
550 }
551 
552 void pm860x_device_exit(struct pm860x_chip *chip)
553 {
554 	device_irq_exit(chip);
555 	mfd_remove_devices(chip->dev);
556 }
557 
558 MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
559 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
560 MODULE_LICENSE("GPL");
561