88pm860x-core.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  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/irq.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. #define INT_STATUS_NUM 3
  20. static struct resource bk_resources[] __initdata = {
  21. {PM8606_BACKLIGHT1, PM8606_BACKLIGHT1, "backlight-0", IORESOURCE_IO,},
  22. {PM8606_BACKLIGHT2, PM8606_BACKLIGHT2, "backlight-1", IORESOURCE_IO,},
  23. {PM8606_BACKLIGHT3, PM8606_BACKLIGHT3, "backlight-2", IORESOURCE_IO,},
  24. };
  25. static struct mfd_cell bk_devs[] __initdata = {
  26. {"88pm860x-backlight", 0,},
  27. {"88pm860x-backlight", 1,},
  28. {"88pm860x-backlight", 2,},
  29. };
  30. static struct pm860x_backlight_pdata bk_pdata[ARRAY_SIZE(bk_devs)];
  31. char pm860x_led_name[][MFD_NAME_SIZE] = {
  32. "led0-red",
  33. "led0-green",
  34. "led0-blue",
  35. "led1-red",
  36. "led1-green",
  37. "led1-blue",
  38. };
  39. EXPORT_SYMBOL(pm860x_led_name);
  40. #define PM8606_LED_RESOURCE(_i, _x) \
  41. { \
  42. .name = pm860x_led_name[_i], \
  43. .start = PM8606_##_x, \
  44. .end = PM8606_##_x, \
  45. .flags = IORESOURCE_IO, \
  46. }
  47. static struct resource led_resources[] = {
  48. PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB1B),
  49. PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB1C),
  50. PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB1D),
  51. PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB2B),
  52. PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB2C),
  53. PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB2D),
  54. };
  55. #define PM8606_LED_DEVS(_i) \
  56. { \
  57. .name = "88pm860x-led", \
  58. .num_resources = 1, \
  59. .resources = &led_resources[_i], \
  60. .id = _i, \
  61. }
  62. static struct mfd_cell led_devs[] = {
  63. PM8606_LED_DEVS(PM8606_LED1_RED),
  64. PM8606_LED_DEVS(PM8606_LED1_GREEN),
  65. PM8606_LED_DEVS(PM8606_LED1_BLUE),
  66. PM8606_LED_DEVS(PM8606_LED2_RED),
  67. PM8606_LED_DEVS(PM8606_LED2_GREEN),
  68. PM8606_LED_DEVS(PM8606_LED2_BLUE),
  69. };
  70. static struct resource touch_resources[] = {
  71. {
  72. .start = PM8607_IRQ_PEN,
  73. .end = PM8607_IRQ_PEN,
  74. .flags = IORESOURCE_IRQ,
  75. },
  76. };
  77. static struct mfd_cell touch_devs[] = {
  78. {
  79. .name = "88pm860x-touch",
  80. .num_resources = 1,
  81. .resources = &touch_resources[0],
  82. },
  83. };
  84. #define PM8607_REG_RESOURCE(_start, _end) \
  85. { \
  86. .start = PM8607_##_start, \
  87. .end = PM8607_##_end, \
  88. .flags = IORESOURCE_IO, \
  89. }
  90. static struct resource power_supply_resources[] = {
  91. {
  92. .name = "88pm860x-power",
  93. .start = PM8607_IRQ_CHG,
  94. .end = PM8607_IRQ_CHG,
  95. .flags = IORESOURCE_IRQ,
  96. },
  97. };
  98. static struct mfd_cell power_devs[] = {
  99. {
  100. .name = "88pm860x-power",
  101. .num_resources = 1,
  102. .resources = &power_supply_resources[0],
  103. .id = -1,
  104. },
  105. };
  106. static struct resource onkey_resources[] = {
  107. {
  108. .name = "88pm860x-onkey",
  109. .start = PM8607_IRQ_ONKEY,
  110. .end = PM8607_IRQ_ONKEY,
  111. .flags = IORESOURCE_IRQ,
  112. },
  113. };
  114. static struct mfd_cell onkey_devs[] = {
  115. {
  116. .name = "88pm860x-onkey",
  117. .num_resources = 1,
  118. .resources = &onkey_resources[0],
  119. .id = -1,
  120. },
  121. };
  122. static struct resource codec_resources[] = {
  123. {
  124. /* Headset microphone insertion or removal */
  125. .name = "micin",
  126. .start = PM8607_IRQ_MICIN,
  127. .end = PM8607_IRQ_MICIN,
  128. .flags = IORESOURCE_IRQ,
  129. }, {
  130. /* Hook-switch press or release */
  131. .name = "hook",
  132. .start = PM8607_IRQ_HOOK,
  133. .end = PM8607_IRQ_HOOK,
  134. .flags = IORESOURCE_IRQ,
  135. }, {
  136. /* Headset insertion or removal */
  137. .name = "headset",
  138. .start = PM8607_IRQ_HEADSET,
  139. .end = PM8607_IRQ_HEADSET,
  140. .flags = IORESOURCE_IRQ,
  141. }, {
  142. /* Audio short */
  143. .name = "audio-short",
  144. .start = PM8607_IRQ_AUDIO_SHORT,
  145. .end = PM8607_IRQ_AUDIO_SHORT,
  146. .flags = IORESOURCE_IRQ,
  147. },
  148. };
  149. static struct mfd_cell codec_devs[] = {
  150. {
  151. .name = "88pm860x-codec",
  152. .num_resources = ARRAY_SIZE(codec_resources),
  153. .resources = &codec_resources[0],
  154. .id = -1,
  155. },
  156. };
  157. static struct resource regulator_resources[] = {
  158. PM8607_REG_RESOURCE(BUCK1, BUCK1),
  159. PM8607_REG_RESOURCE(BUCK2, BUCK2),
  160. PM8607_REG_RESOURCE(BUCK3, BUCK3),
  161. PM8607_REG_RESOURCE(LDO1, LDO1),
  162. PM8607_REG_RESOURCE(LDO2, LDO2),
  163. PM8607_REG_RESOURCE(LDO3, LDO3),
  164. PM8607_REG_RESOURCE(LDO4, LDO4),
  165. PM8607_REG_RESOURCE(LDO5, LDO5),
  166. PM8607_REG_RESOURCE(LDO6, LDO6),
  167. PM8607_REG_RESOURCE(LDO7, LDO7),
  168. PM8607_REG_RESOURCE(LDO8, LDO8),
  169. PM8607_REG_RESOURCE(LDO9, LDO9),
  170. PM8607_REG_RESOURCE(LDO10, LDO10),
  171. PM8607_REG_RESOURCE(LDO12, LDO12),
  172. PM8607_REG_RESOURCE(VIBRATOR_SET, VIBRATOR_SET),
  173. PM8607_REG_RESOURCE(LDO14, LDO14),
  174. };
  175. #define PM8607_REG_DEVS(_id) \
  176. { \
  177. .name = "88pm860x-regulator", \
  178. .num_resources = 1, \
  179. .resources = &regulator_resources[PM8607_ID_##_id], \
  180. .id = PM8607_ID_##_id, \
  181. }
  182. static struct mfd_cell regulator_devs[] = {
  183. PM8607_REG_DEVS(BUCK1),
  184. PM8607_REG_DEVS(BUCK2),
  185. PM8607_REG_DEVS(BUCK3),
  186. PM8607_REG_DEVS(LDO1),
  187. PM8607_REG_DEVS(LDO2),
  188. PM8607_REG_DEVS(LDO3),
  189. PM8607_REG_DEVS(LDO4),
  190. PM8607_REG_DEVS(LDO5),
  191. PM8607_REG_DEVS(LDO6),
  192. PM8607_REG_DEVS(LDO7),
  193. PM8607_REG_DEVS(LDO8),
  194. PM8607_REG_DEVS(LDO9),
  195. PM8607_REG_DEVS(LDO10),
  196. PM8607_REG_DEVS(LDO12),
  197. PM8607_REG_DEVS(LDO13),
  198. PM8607_REG_DEVS(LDO14),
  199. };
  200. struct pm860x_irq_data {
  201. int reg;
  202. int mask_reg;
  203. int enable; /* enable or not */
  204. int offs; /* bit offset in mask register */
  205. };
  206. static struct pm860x_irq_data pm860x_irqs[] = {
  207. [PM8607_IRQ_ONKEY] = {
  208. .reg = PM8607_INT_STATUS1,
  209. .mask_reg = PM8607_INT_MASK_1,
  210. .offs = 1 << 0,
  211. },
  212. [PM8607_IRQ_EXTON] = {
  213. .reg = PM8607_INT_STATUS1,
  214. .mask_reg = PM8607_INT_MASK_1,
  215. .offs = 1 << 1,
  216. },
  217. [PM8607_IRQ_CHG] = {
  218. .reg = PM8607_INT_STATUS1,
  219. .mask_reg = PM8607_INT_MASK_1,
  220. .offs = 1 << 2,
  221. },
  222. [PM8607_IRQ_BAT] = {
  223. .reg = PM8607_INT_STATUS1,
  224. .mask_reg = PM8607_INT_MASK_1,
  225. .offs = 1 << 3,
  226. },
  227. [PM8607_IRQ_RTC] = {
  228. .reg = PM8607_INT_STATUS1,
  229. .mask_reg = PM8607_INT_MASK_1,
  230. .offs = 1 << 4,
  231. },
  232. [PM8607_IRQ_CC] = {
  233. .reg = PM8607_INT_STATUS1,
  234. .mask_reg = PM8607_INT_MASK_1,
  235. .offs = 1 << 5,
  236. },
  237. [PM8607_IRQ_VBAT] = {
  238. .reg = PM8607_INT_STATUS2,
  239. .mask_reg = PM8607_INT_MASK_2,
  240. .offs = 1 << 0,
  241. },
  242. [PM8607_IRQ_VCHG] = {
  243. .reg = PM8607_INT_STATUS2,
  244. .mask_reg = PM8607_INT_MASK_2,
  245. .offs = 1 << 1,
  246. },
  247. [PM8607_IRQ_VSYS] = {
  248. .reg = PM8607_INT_STATUS2,
  249. .mask_reg = PM8607_INT_MASK_2,
  250. .offs = 1 << 2,
  251. },
  252. [PM8607_IRQ_TINT] = {
  253. .reg = PM8607_INT_STATUS2,
  254. .mask_reg = PM8607_INT_MASK_2,
  255. .offs = 1 << 3,
  256. },
  257. [PM8607_IRQ_GPADC0] = {
  258. .reg = PM8607_INT_STATUS2,
  259. .mask_reg = PM8607_INT_MASK_2,
  260. .offs = 1 << 4,
  261. },
  262. [PM8607_IRQ_GPADC1] = {
  263. .reg = PM8607_INT_STATUS2,
  264. .mask_reg = PM8607_INT_MASK_2,
  265. .offs = 1 << 5,
  266. },
  267. [PM8607_IRQ_GPADC2] = {
  268. .reg = PM8607_INT_STATUS2,
  269. .mask_reg = PM8607_INT_MASK_2,
  270. .offs = 1 << 6,
  271. },
  272. [PM8607_IRQ_GPADC3] = {
  273. .reg = PM8607_INT_STATUS2,
  274. .mask_reg = PM8607_INT_MASK_2,
  275. .offs = 1 << 7,
  276. },
  277. [PM8607_IRQ_AUDIO_SHORT] = {
  278. .reg = PM8607_INT_STATUS3,
  279. .mask_reg = PM8607_INT_MASK_3,
  280. .offs = 1 << 0,
  281. },
  282. [PM8607_IRQ_PEN] = {
  283. .reg = PM8607_INT_STATUS3,
  284. .mask_reg = PM8607_INT_MASK_3,
  285. .offs = 1 << 1,
  286. },
  287. [PM8607_IRQ_HEADSET] = {
  288. .reg = PM8607_INT_STATUS3,
  289. .mask_reg = PM8607_INT_MASK_3,
  290. .offs = 1 << 2,
  291. },
  292. [PM8607_IRQ_HOOK] = {
  293. .reg = PM8607_INT_STATUS3,
  294. .mask_reg = PM8607_INT_MASK_3,
  295. .offs = 1 << 3,
  296. },
  297. [PM8607_IRQ_MICIN] = {
  298. .reg = PM8607_INT_STATUS3,
  299. .mask_reg = PM8607_INT_MASK_3,
  300. .offs = 1 << 4,
  301. },
  302. [PM8607_IRQ_CHG_FAIL] = {
  303. .reg = PM8607_INT_STATUS3,
  304. .mask_reg = PM8607_INT_MASK_3,
  305. .offs = 1 << 5,
  306. },
  307. [PM8607_IRQ_CHG_DONE] = {
  308. .reg = PM8607_INT_STATUS3,
  309. .mask_reg = PM8607_INT_MASK_3,
  310. .offs = 1 << 6,
  311. },
  312. [PM8607_IRQ_CHG_FAULT] = {
  313. .reg = PM8607_INT_STATUS3,
  314. .mask_reg = PM8607_INT_MASK_3,
  315. .offs = 1 << 7,
  316. },
  317. };
  318. static irqreturn_t pm860x_irq(int irq, void *data)
  319. {
  320. struct pm860x_chip *chip = data;
  321. struct pm860x_irq_data *irq_data;
  322. struct i2c_client *i2c;
  323. int read_reg = -1, value = 0;
  324. int i;
  325. i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
  326. for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
  327. irq_data = &pm860x_irqs[i];
  328. if (read_reg != irq_data->reg) {
  329. read_reg = irq_data->reg;
  330. value = pm860x_reg_read(i2c, irq_data->reg);
  331. }
  332. if (value & irq_data->enable)
  333. handle_nested_irq(chip->irq_base + i);
  334. }
  335. return IRQ_HANDLED;
  336. }
  337. static void pm860x_irq_lock(struct irq_data *data)
  338. {
  339. struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
  340. mutex_lock(&chip->irq_lock);
  341. }
  342. static void pm860x_irq_sync_unlock(struct irq_data *data)
  343. {
  344. struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
  345. struct pm860x_irq_data *irq_data;
  346. struct i2c_client *i2c;
  347. static unsigned char cached[3] = {0x0, 0x0, 0x0};
  348. unsigned char mask[3];
  349. int i;
  350. i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
  351. /* Load cached value. In initial, all IRQs are masked */
  352. for (i = 0; i < 3; i++)
  353. mask[i] = cached[i];
  354. for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
  355. irq_data = &pm860x_irqs[i];
  356. switch (irq_data->mask_reg) {
  357. case PM8607_INT_MASK_1:
  358. mask[0] &= ~irq_data->offs;
  359. mask[0] |= irq_data->enable;
  360. break;
  361. case PM8607_INT_MASK_2:
  362. mask[1] &= ~irq_data->offs;
  363. mask[1] |= irq_data->enable;
  364. break;
  365. case PM8607_INT_MASK_3:
  366. mask[2] &= ~irq_data->offs;
  367. mask[2] |= irq_data->enable;
  368. break;
  369. default:
  370. dev_err(chip->dev, "wrong IRQ\n");
  371. break;
  372. }
  373. }
  374. /* update mask into registers */
  375. for (i = 0; i < 3; i++) {
  376. if (mask[i] != cached[i]) {
  377. cached[i] = mask[i];
  378. pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
  379. }
  380. }
  381. mutex_unlock(&chip->irq_lock);
  382. }
  383. static void pm860x_irq_enable(struct irq_data *data)
  384. {
  385. struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
  386. pm860x_irqs[data->irq - chip->irq_base].enable
  387. = pm860x_irqs[data->irq - chip->irq_base].offs;
  388. }
  389. static void pm860x_irq_disable(struct irq_data *data)
  390. {
  391. struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
  392. pm860x_irqs[data->irq - chip->irq_base].enable = 0;
  393. }
  394. static struct irq_chip pm860x_irq_chip = {
  395. .name = "88pm860x",
  396. .irq_bus_lock = pm860x_irq_lock,
  397. .irq_bus_sync_unlock = pm860x_irq_sync_unlock,
  398. .irq_enable = pm860x_irq_enable,
  399. .irq_disable = pm860x_irq_disable,
  400. };
  401. static int __devinit device_gpadc_init(struct pm860x_chip *chip,
  402. struct pm860x_platform_data *pdata)
  403. {
  404. struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
  405. : chip->companion;
  406. int data;
  407. int ret;
  408. /* initialize GPADC without activating it */
  409. if (!pdata || !pdata->touch)
  410. return -EINVAL;
  411. /* set GPADC MISC1 register */
  412. data = 0;
  413. data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
  414. data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
  415. data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
  416. data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
  417. if (data) {
  418. ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
  419. if (ret < 0)
  420. goto out;
  421. }
  422. /* set tsi prebias time */
  423. if (pdata->touch->tsi_prebias) {
  424. data = pdata->touch->tsi_prebias;
  425. ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
  426. if (ret < 0)
  427. goto out;
  428. }
  429. /* set prebias & prechg time of pen detect */
  430. data = 0;
  431. data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
  432. data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
  433. if (data) {
  434. ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
  435. if (ret < 0)
  436. goto out;
  437. }
  438. ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
  439. PM8607_GPADC_EN, PM8607_GPADC_EN);
  440. out:
  441. return ret;
  442. }
  443. static int __devinit device_irq_init(struct pm860x_chip *chip,
  444. struct pm860x_platform_data *pdata)
  445. {
  446. struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
  447. : chip->companion;
  448. unsigned char status_buf[INT_STATUS_NUM];
  449. unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
  450. struct irq_desc *desc;
  451. int i, data, mask, ret = -EINVAL;
  452. int __irq;
  453. if (!pdata || !pdata->irq_base) {
  454. dev_warn(chip->dev, "No interrupt support on IRQ base\n");
  455. return -EINVAL;
  456. }
  457. mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
  458. | PM8607_B0_MISC1_INT_MASK;
  459. data = 0;
  460. chip->irq_mode = 0;
  461. if (pdata && pdata->irq_mode) {
  462. /*
  463. * irq_mode defines the way of clearing interrupt. If it's 1,
  464. * clear IRQ by write. Otherwise, clear it by read.
  465. * This control bit is valid from 88PM8607 B0 steping.
  466. */
  467. data |= PM8607_B0_MISC1_INT_CLEAR;
  468. chip->irq_mode = 1;
  469. }
  470. ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
  471. if (ret < 0)
  472. goto out;
  473. /* mask all IRQs */
  474. memset(status_buf, 0, INT_STATUS_NUM);
  475. ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
  476. INT_STATUS_NUM, status_buf);
  477. if (ret < 0)
  478. goto out;
  479. if (chip->irq_mode) {
  480. /* clear interrupt status by write */
  481. memset(status_buf, 0xFF, INT_STATUS_NUM);
  482. ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
  483. INT_STATUS_NUM, status_buf);
  484. } else {
  485. /* clear interrupt status by read */
  486. ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
  487. INT_STATUS_NUM, status_buf);
  488. }
  489. if (ret < 0)
  490. goto out;
  491. mutex_init(&chip->irq_lock);
  492. chip->irq_base = pdata->irq_base;
  493. chip->core_irq = i2c->irq;
  494. if (!chip->core_irq)
  495. goto out;
  496. desc = irq_to_desc(chip->core_irq);
  497. /* register IRQ by genirq */
  498. for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
  499. __irq = i + chip->irq_base;
  500. set_irq_chip_data(__irq, chip);
  501. set_irq_chip_and_handler(__irq, &pm860x_irq_chip,
  502. handle_edge_irq);
  503. set_irq_nested_thread(__irq, 1);
  504. #ifdef CONFIG_ARM
  505. set_irq_flags(__irq, IRQF_VALID);
  506. #else
  507. set_irq_noprobe(__irq);
  508. #endif
  509. }
  510. ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
  511. "88pm860x", chip);
  512. if (ret) {
  513. dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
  514. chip->core_irq = 0;
  515. }
  516. return 0;
  517. out:
  518. chip->core_irq = 0;
  519. return ret;
  520. }
  521. static void device_irq_exit(struct pm860x_chip *chip)
  522. {
  523. if (chip->core_irq)
  524. free_irq(chip->core_irq, chip);
  525. }
  526. static void __devinit device_bk_init(struct pm860x_chip *chip,
  527. struct i2c_client *i2c,
  528. struct pm860x_platform_data *pdata)
  529. {
  530. int ret;
  531. int i, j, id;
  532. if ((pdata == NULL) || (pdata->backlight == NULL))
  533. return;
  534. if (pdata->num_backlights > ARRAY_SIZE(bk_devs))
  535. pdata->num_backlights = ARRAY_SIZE(bk_devs);
  536. for (i = 0; i < pdata->num_backlights; i++) {
  537. memcpy(&bk_pdata[i], &pdata->backlight[i],
  538. sizeof(struct pm860x_backlight_pdata));
  539. bk_devs[i].mfd_data = &bk_pdata[i];
  540. for (j = 0; j < ARRAY_SIZE(bk_devs); j++) {
  541. id = bk_resources[j].start;
  542. if (bk_pdata[i].flags != id)
  543. continue;
  544. bk_devs[i].num_resources = 1;
  545. bk_devs[i].resources = &bk_resources[j];
  546. ret = mfd_add_devices(chip->dev, 0,
  547. &bk_devs[i], 1,
  548. &bk_resources[j], 0);
  549. if (ret < 0) {
  550. dev_err(chip->dev, "Failed to add "
  551. "backlight subdev\n");
  552. return;
  553. }
  554. }
  555. }
  556. }
  557. static void __devinit device_8606_init(struct pm860x_chip *chip,
  558. struct i2c_client *i2c,
  559. struct pm860x_platform_data *pdata)
  560. {
  561. int ret;
  562. if (pdata && pdata->led) {
  563. ret = mfd_add_devices(chip->dev, 0, &led_devs[0],
  564. ARRAY_SIZE(led_devs),
  565. &led_resources[0], 0);
  566. if (ret < 0) {
  567. dev_err(chip->dev, "Failed to add led "
  568. "subdev\n");
  569. goto out_dev;
  570. }
  571. }
  572. return;
  573. out_dev:
  574. device_irq_exit(chip);
  575. }
  576. static void __devinit device_8607_init(struct pm860x_chip *chip,
  577. struct i2c_client *i2c,
  578. struct pm860x_platform_data *pdata)
  579. {
  580. int data, ret;
  581. ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
  582. if (ret < 0) {
  583. dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
  584. goto out;
  585. }
  586. switch (ret & PM8607_VERSION_MASK) {
  587. case 0x40:
  588. case 0x50:
  589. dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
  590. ret);
  591. break;
  592. default:
  593. dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
  594. "Chip ID: %02x\n", ret);
  595. goto out;
  596. }
  597. ret = pm860x_reg_read(i2c, PM8607_BUCK3);
  598. if (ret < 0) {
  599. dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
  600. goto out;
  601. }
  602. if (ret & PM8607_BUCK3_DOUBLE)
  603. chip->buck3_double = 1;
  604. ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
  605. if (ret < 0) {
  606. dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
  607. goto out;
  608. }
  609. if (pdata && (pdata->i2c_port == PI2C_PORT))
  610. data = PM8607_B0_MISC1_PI2C;
  611. else
  612. data = 0;
  613. ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
  614. if (ret < 0) {
  615. dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
  616. goto out;
  617. }
  618. ret = device_gpadc_init(chip, pdata);
  619. if (ret < 0)
  620. goto out;
  621. ret = device_irq_init(chip, pdata);
  622. if (ret < 0)
  623. goto out;
  624. ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
  625. ARRAY_SIZE(regulator_devs),
  626. &regulator_resources[0], 0);
  627. if (ret < 0) {
  628. dev_err(chip->dev, "Failed to add regulator subdev\n");
  629. goto out_dev;
  630. }
  631. if (pdata && pdata->touch) {
  632. ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
  633. ARRAY_SIZE(touch_devs),
  634. &touch_resources[0], 0);
  635. if (ret < 0) {
  636. dev_err(chip->dev, "Failed to add touch "
  637. "subdev\n");
  638. goto out_dev;
  639. }
  640. }
  641. if (pdata && pdata->power) {
  642. ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
  643. ARRAY_SIZE(power_devs),
  644. &power_supply_resources[0], 0);
  645. if (ret < 0) {
  646. dev_err(chip->dev, "Failed to add power supply "
  647. "subdev\n");
  648. goto out_dev;
  649. }
  650. }
  651. ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
  652. ARRAY_SIZE(onkey_devs),
  653. &onkey_resources[0], 0);
  654. if (ret < 0) {
  655. dev_err(chip->dev, "Failed to add onkey subdev\n");
  656. goto out_dev;
  657. }
  658. ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
  659. ARRAY_SIZE(codec_devs),
  660. &codec_resources[0], 0);
  661. if (ret < 0) {
  662. dev_err(chip->dev, "Failed to add codec subdev\n");
  663. goto out_dev;
  664. }
  665. return;
  666. out_dev:
  667. mfd_remove_devices(chip->dev);
  668. device_irq_exit(chip);
  669. out:
  670. return;
  671. }
  672. int __devinit pm860x_device_init(struct pm860x_chip *chip,
  673. struct pm860x_platform_data *pdata)
  674. {
  675. chip->core_irq = 0;
  676. switch (chip->id) {
  677. case CHIP_PM8606:
  678. device_bk_init(chip, chip->client, pdata);
  679. device_8606_init(chip, chip->client, pdata);
  680. break;
  681. case CHIP_PM8607:
  682. device_8607_init(chip, chip->client, pdata);
  683. break;
  684. }
  685. if (chip->companion) {
  686. switch (chip->id) {
  687. case CHIP_PM8607:
  688. device_bk_init(chip, chip->companion, pdata);
  689. device_8606_init(chip, chip->companion, pdata);
  690. break;
  691. case CHIP_PM8606:
  692. device_8607_init(chip, chip->companion, pdata);
  693. break;
  694. }
  695. }
  696. return 0;
  697. }
  698. void __devexit pm860x_device_exit(struct pm860x_chip *chip)
  699. {
  700. device_irq_exit(chip);
  701. mfd_remove_devices(chip->dev);
  702. }
  703. MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
  704. MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
  705. MODULE_LICENSE("GPL");