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 = ®ulator_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, ®ulator_devs[0], 499 ARRAY_SIZE(regulator_devs), 500 ®ulator_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