88pm860x-core.c 13 KB

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