88pm860x-core.c 20 KB

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