wm831x_power.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. /*
  2. * PMU driver for Wolfson Microelectronics wm831x PMICs
  3. *
  4. * Copyright 2009 Wolfson Microelectronics PLC.
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. */
  10. #include <linux/module.h>
  11. #include <linux/err.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/power_supply.h>
  14. #include <linux/slab.h>
  15. #include <linux/mfd/wm831x/core.h>
  16. #include <linux/mfd/wm831x/auxadc.h>
  17. #include <linux/mfd/wm831x/pmu.h>
  18. #include <linux/mfd/wm831x/pdata.h>
  19. struct wm831x_power {
  20. struct wm831x *wm831x;
  21. struct power_supply *wall;
  22. struct power_supply *usb;
  23. struct power_supply *battery;
  24. struct power_supply_desc wall_desc;
  25. struct power_supply_desc usb_desc;
  26. struct power_supply_desc battery_desc;
  27. char wall_name[20];
  28. char usb_name[20];
  29. char battery_name[20];
  30. bool have_battery;
  31. };
  32. static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
  33. union power_supply_propval *val)
  34. {
  35. int ret;
  36. ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
  37. if (ret < 0)
  38. return ret;
  39. if (ret & supply)
  40. val->intval = 1;
  41. else
  42. val->intval = 0;
  43. return 0;
  44. }
  45. static int wm831x_power_read_voltage(struct wm831x *wm831x,
  46. enum wm831x_auxadc src,
  47. union power_supply_propval *val)
  48. {
  49. int ret;
  50. ret = wm831x_auxadc_read_uv(wm831x, src);
  51. if (ret >= 0)
  52. val->intval = ret;
  53. return ret;
  54. }
  55. /*********************************************************************
  56. * WALL Power
  57. *********************************************************************/
  58. static int wm831x_wall_get_prop(struct power_supply *psy,
  59. enum power_supply_property psp,
  60. union power_supply_propval *val)
  61. {
  62. struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
  63. struct wm831x *wm831x = wm831x_power->wm831x;
  64. int ret = 0;
  65. switch (psp) {
  66. case POWER_SUPPLY_PROP_ONLINE:
  67. ret = wm831x_power_check_online(wm831x, WM831X_PWR_WALL, val);
  68. break;
  69. case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  70. ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_WALL, val);
  71. break;
  72. default:
  73. ret = -EINVAL;
  74. break;
  75. }
  76. return ret;
  77. }
  78. static enum power_supply_property wm831x_wall_props[] = {
  79. POWER_SUPPLY_PROP_ONLINE,
  80. POWER_SUPPLY_PROP_VOLTAGE_NOW,
  81. };
  82. /*********************************************************************
  83. * USB Power
  84. *********************************************************************/
  85. static int wm831x_usb_get_prop(struct power_supply *psy,
  86. enum power_supply_property psp,
  87. union power_supply_propval *val)
  88. {
  89. struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
  90. struct wm831x *wm831x = wm831x_power->wm831x;
  91. int ret = 0;
  92. switch (psp) {
  93. case POWER_SUPPLY_PROP_ONLINE:
  94. ret = wm831x_power_check_online(wm831x, WM831X_PWR_USB, val);
  95. break;
  96. case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  97. ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_USB, val);
  98. break;
  99. default:
  100. ret = -EINVAL;
  101. break;
  102. }
  103. return ret;
  104. }
  105. static enum power_supply_property wm831x_usb_props[] = {
  106. POWER_SUPPLY_PROP_ONLINE,
  107. POWER_SUPPLY_PROP_VOLTAGE_NOW,
  108. };
  109. /*********************************************************************
  110. * Battery properties
  111. *********************************************************************/
  112. struct chg_map {
  113. int val;
  114. int reg_val;
  115. };
  116. static struct chg_map trickle_ilims[] = {
  117. { 50, 0 << WM831X_CHG_TRKL_ILIM_SHIFT },
  118. { 100, 1 << WM831X_CHG_TRKL_ILIM_SHIFT },
  119. { 150, 2 << WM831X_CHG_TRKL_ILIM_SHIFT },
  120. { 200, 3 << WM831X_CHG_TRKL_ILIM_SHIFT },
  121. };
  122. static struct chg_map vsels[] = {
  123. { 4050, 0 << WM831X_CHG_VSEL_SHIFT },
  124. { 4100, 1 << WM831X_CHG_VSEL_SHIFT },
  125. { 4150, 2 << WM831X_CHG_VSEL_SHIFT },
  126. { 4200, 3 << WM831X_CHG_VSEL_SHIFT },
  127. };
  128. static struct chg_map fast_ilims[] = {
  129. { 0, 0 << WM831X_CHG_FAST_ILIM_SHIFT },
  130. { 50, 1 << WM831X_CHG_FAST_ILIM_SHIFT },
  131. { 100, 2 << WM831X_CHG_FAST_ILIM_SHIFT },
  132. { 150, 3 << WM831X_CHG_FAST_ILIM_SHIFT },
  133. { 200, 4 << WM831X_CHG_FAST_ILIM_SHIFT },
  134. { 250, 5 << WM831X_CHG_FAST_ILIM_SHIFT },
  135. { 300, 6 << WM831X_CHG_FAST_ILIM_SHIFT },
  136. { 350, 7 << WM831X_CHG_FAST_ILIM_SHIFT },
  137. { 400, 8 << WM831X_CHG_FAST_ILIM_SHIFT },
  138. { 450, 9 << WM831X_CHG_FAST_ILIM_SHIFT },
  139. { 500, 10 << WM831X_CHG_FAST_ILIM_SHIFT },
  140. { 600, 11 << WM831X_CHG_FAST_ILIM_SHIFT },
  141. { 700, 12 << WM831X_CHG_FAST_ILIM_SHIFT },
  142. { 800, 13 << WM831X_CHG_FAST_ILIM_SHIFT },
  143. { 900, 14 << WM831X_CHG_FAST_ILIM_SHIFT },
  144. { 1000, 15 << WM831X_CHG_FAST_ILIM_SHIFT },
  145. };
  146. static struct chg_map eoc_iterms[] = {
  147. { 20, 0 << WM831X_CHG_ITERM_SHIFT },
  148. { 30, 1 << WM831X_CHG_ITERM_SHIFT },
  149. { 40, 2 << WM831X_CHG_ITERM_SHIFT },
  150. { 50, 3 << WM831X_CHG_ITERM_SHIFT },
  151. { 60, 4 << WM831X_CHG_ITERM_SHIFT },
  152. { 70, 5 << WM831X_CHG_ITERM_SHIFT },
  153. { 80, 6 << WM831X_CHG_ITERM_SHIFT },
  154. { 90, 7 << WM831X_CHG_ITERM_SHIFT },
  155. };
  156. static struct chg_map chg_times[] = {
  157. { 60, 0 << WM831X_CHG_TIME_SHIFT },
  158. { 90, 1 << WM831X_CHG_TIME_SHIFT },
  159. { 120, 2 << WM831X_CHG_TIME_SHIFT },
  160. { 150, 3 << WM831X_CHG_TIME_SHIFT },
  161. { 180, 4 << WM831X_CHG_TIME_SHIFT },
  162. { 210, 5 << WM831X_CHG_TIME_SHIFT },
  163. { 240, 6 << WM831X_CHG_TIME_SHIFT },
  164. { 270, 7 << WM831X_CHG_TIME_SHIFT },
  165. { 300, 8 << WM831X_CHG_TIME_SHIFT },
  166. { 330, 9 << WM831X_CHG_TIME_SHIFT },
  167. { 360, 10 << WM831X_CHG_TIME_SHIFT },
  168. { 390, 11 << WM831X_CHG_TIME_SHIFT },
  169. { 420, 12 << WM831X_CHG_TIME_SHIFT },
  170. { 450, 13 << WM831X_CHG_TIME_SHIFT },
  171. { 480, 14 << WM831X_CHG_TIME_SHIFT },
  172. { 510, 15 << WM831X_CHG_TIME_SHIFT },
  173. };
  174. static void wm831x_battey_apply_config(struct wm831x *wm831x,
  175. struct chg_map *map, int count, int val,
  176. int *reg, const char *name,
  177. const char *units)
  178. {
  179. int i;
  180. for (i = 0; i < count; i++)
  181. if (val == map[i].val)
  182. break;
  183. if (i == count) {
  184. dev_err(wm831x->dev, "Invalid %s %d%s\n",
  185. name, val, units);
  186. } else {
  187. *reg |= map[i].reg_val;
  188. dev_dbg(wm831x->dev, "Set %s of %d%s\n", name, val, units);
  189. }
  190. }
  191. static void wm831x_config_battery(struct wm831x *wm831x)
  192. {
  193. struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
  194. struct wm831x_battery_pdata *pdata;
  195. int ret, reg1, reg2;
  196. if (!wm831x_pdata || !wm831x_pdata->battery) {
  197. dev_warn(wm831x->dev,
  198. "No battery charger configuration\n");
  199. return;
  200. }
  201. pdata = wm831x_pdata->battery;
  202. reg1 = 0;
  203. reg2 = 0;
  204. if (!pdata->enable) {
  205. dev_info(wm831x->dev, "Battery charger disabled\n");
  206. return;
  207. }
  208. reg1 |= WM831X_CHG_ENA;
  209. if (pdata->off_mask)
  210. reg2 |= WM831X_CHG_OFF_MSK;
  211. if (pdata->fast_enable)
  212. reg1 |= WM831X_CHG_FAST;
  213. wm831x_battey_apply_config(wm831x, trickle_ilims,
  214. ARRAY_SIZE(trickle_ilims),
  215. pdata->trickle_ilim, &reg2,
  216. "trickle charge current limit", "mA");
  217. wm831x_battey_apply_config(wm831x, vsels, ARRAY_SIZE(vsels),
  218. pdata->vsel, &reg2,
  219. "target voltage", "mV");
  220. wm831x_battey_apply_config(wm831x, fast_ilims, ARRAY_SIZE(fast_ilims),
  221. pdata->fast_ilim, &reg2,
  222. "fast charge current limit", "mA");
  223. wm831x_battey_apply_config(wm831x, eoc_iterms, ARRAY_SIZE(eoc_iterms),
  224. pdata->eoc_iterm, &reg1,
  225. "end of charge current threshold", "mA");
  226. wm831x_battey_apply_config(wm831x, chg_times, ARRAY_SIZE(chg_times),
  227. pdata->timeout, &reg2,
  228. "charger timeout", "min");
  229. ret = wm831x_reg_unlock(wm831x);
  230. if (ret != 0) {
  231. dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
  232. return;
  233. }
  234. ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1,
  235. WM831X_CHG_ENA_MASK |
  236. WM831X_CHG_FAST_MASK |
  237. WM831X_CHG_ITERM_MASK,
  238. reg1);
  239. if (ret != 0)
  240. dev_err(wm831x->dev, "Failed to set charger control 1: %d\n",
  241. ret);
  242. ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2,
  243. WM831X_CHG_OFF_MSK |
  244. WM831X_CHG_TIME_MASK |
  245. WM831X_CHG_FAST_ILIM_MASK |
  246. WM831X_CHG_TRKL_ILIM_MASK |
  247. WM831X_CHG_VSEL_MASK,
  248. reg2);
  249. if (ret != 0)
  250. dev_err(wm831x->dev, "Failed to set charger control 2: %d\n",
  251. ret);
  252. wm831x_reg_lock(wm831x);
  253. }
  254. static int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
  255. {
  256. int ret;
  257. ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
  258. if (ret < 0)
  259. return ret;
  260. if (ret & WM831X_PWR_SRC_BATT) {
  261. *status = POWER_SUPPLY_STATUS_DISCHARGING;
  262. return 0;
  263. }
  264. ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
  265. if (ret < 0)
  266. return ret;
  267. switch (ret & WM831X_CHG_STATE_MASK) {
  268. case WM831X_CHG_STATE_OFF:
  269. *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
  270. break;
  271. case WM831X_CHG_STATE_TRICKLE:
  272. case WM831X_CHG_STATE_FAST:
  273. *status = POWER_SUPPLY_STATUS_CHARGING;
  274. break;
  275. default:
  276. *status = POWER_SUPPLY_STATUS_UNKNOWN;
  277. break;
  278. }
  279. return 0;
  280. }
  281. static int wm831x_bat_check_type(struct wm831x *wm831x, int *type)
  282. {
  283. int ret;
  284. ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
  285. if (ret < 0)
  286. return ret;
  287. switch (ret & WM831X_CHG_STATE_MASK) {
  288. case WM831X_CHG_STATE_TRICKLE:
  289. case WM831X_CHG_STATE_TRICKLE_OT:
  290. *type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
  291. break;
  292. case WM831X_CHG_STATE_FAST:
  293. case WM831X_CHG_STATE_FAST_OT:
  294. *type = POWER_SUPPLY_CHARGE_TYPE_FAST;
  295. break;
  296. default:
  297. *type = POWER_SUPPLY_CHARGE_TYPE_NONE;
  298. break;
  299. }
  300. return 0;
  301. }
  302. static int wm831x_bat_check_health(struct wm831x *wm831x, int *health)
  303. {
  304. int ret;
  305. ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
  306. if (ret < 0)
  307. return ret;
  308. if (ret & WM831X_BATT_HOT_STS) {
  309. *health = POWER_SUPPLY_HEALTH_OVERHEAT;
  310. return 0;
  311. }
  312. if (ret & WM831X_BATT_COLD_STS) {
  313. *health = POWER_SUPPLY_HEALTH_COLD;
  314. return 0;
  315. }
  316. if (ret & WM831X_BATT_OV_STS) {
  317. *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
  318. return 0;
  319. }
  320. switch (ret & WM831X_CHG_STATE_MASK) {
  321. case WM831X_CHG_STATE_TRICKLE_OT:
  322. case WM831X_CHG_STATE_FAST_OT:
  323. *health = POWER_SUPPLY_HEALTH_OVERHEAT;
  324. break;
  325. case WM831X_CHG_STATE_DEFECTIVE:
  326. *health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
  327. break;
  328. default:
  329. *health = POWER_SUPPLY_HEALTH_GOOD;
  330. break;
  331. }
  332. return 0;
  333. }
  334. static int wm831x_bat_get_prop(struct power_supply *psy,
  335. enum power_supply_property psp,
  336. union power_supply_propval *val)
  337. {
  338. struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
  339. struct wm831x *wm831x = wm831x_power->wm831x;
  340. int ret = 0;
  341. switch (psp) {
  342. case POWER_SUPPLY_PROP_STATUS:
  343. ret = wm831x_bat_check_status(wm831x, &val->intval);
  344. break;
  345. case POWER_SUPPLY_PROP_ONLINE:
  346. ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT,
  347. val);
  348. break;
  349. case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  350. ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
  351. break;
  352. case POWER_SUPPLY_PROP_HEALTH:
  353. ret = wm831x_bat_check_health(wm831x, &val->intval);
  354. break;
  355. case POWER_SUPPLY_PROP_CHARGE_TYPE:
  356. ret = wm831x_bat_check_type(wm831x, &val->intval);
  357. break;
  358. default:
  359. ret = -EINVAL;
  360. break;
  361. }
  362. return ret;
  363. }
  364. static enum power_supply_property wm831x_bat_props[] = {
  365. POWER_SUPPLY_PROP_STATUS,
  366. POWER_SUPPLY_PROP_ONLINE,
  367. POWER_SUPPLY_PROP_VOLTAGE_NOW,
  368. POWER_SUPPLY_PROP_HEALTH,
  369. POWER_SUPPLY_PROP_CHARGE_TYPE,
  370. };
  371. static const char *wm831x_bat_irqs[] = {
  372. "BATT HOT",
  373. "BATT COLD",
  374. "BATT FAIL",
  375. "OV",
  376. "END",
  377. "TO",
  378. "MODE",
  379. "START",
  380. };
  381. static irqreturn_t wm831x_bat_irq(int irq, void *data)
  382. {
  383. struct wm831x_power *wm831x_power = data;
  384. struct wm831x *wm831x = wm831x_power->wm831x;
  385. dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq);
  386. /* The battery charger is autonomous so we don't need to do
  387. * anything except kick user space */
  388. if (wm831x_power->have_battery)
  389. power_supply_changed(wm831x_power->battery);
  390. return IRQ_HANDLED;
  391. }
  392. /*********************************************************************
  393. * Initialisation
  394. *********************************************************************/
  395. static irqreturn_t wm831x_syslo_irq(int irq, void *data)
  396. {
  397. struct wm831x_power *wm831x_power = data;
  398. struct wm831x *wm831x = wm831x_power->wm831x;
  399. /* Not much we can actually *do* but tell people for
  400. * posterity, we're probably about to run out of power. */
  401. dev_crit(wm831x->dev, "SYSVDD under voltage\n");
  402. return IRQ_HANDLED;
  403. }
  404. static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
  405. {
  406. struct wm831x_power *wm831x_power = data;
  407. struct wm831x *wm831x = wm831x_power->wm831x;
  408. dev_dbg(wm831x->dev, "Power source changed\n");
  409. /* Just notify for everything - little harm in overnotifying. */
  410. if (wm831x_power->have_battery)
  411. power_supply_changed(wm831x_power->battery);
  412. power_supply_changed(wm831x_power->usb);
  413. power_supply_changed(wm831x_power->wall);
  414. return IRQ_HANDLED;
  415. }
  416. static int wm831x_power_probe(struct platform_device *pdev)
  417. {
  418. struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
  419. struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
  420. struct wm831x_power *power;
  421. int ret, irq, i;
  422. power = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_power),
  423. GFP_KERNEL);
  424. if (power == NULL)
  425. return -ENOMEM;
  426. power->wm831x = wm831x;
  427. platform_set_drvdata(pdev, power);
  428. if (wm831x_pdata && wm831x_pdata->wm831x_num) {
  429. snprintf(power->wall_name, sizeof(power->wall_name),
  430. "wm831x-wall.%d", wm831x_pdata->wm831x_num);
  431. snprintf(power->battery_name, sizeof(power->wall_name),
  432. "wm831x-battery.%d", wm831x_pdata->wm831x_num);
  433. snprintf(power->usb_name, sizeof(power->wall_name),
  434. "wm831x-usb.%d", wm831x_pdata->wm831x_num);
  435. } else {
  436. snprintf(power->wall_name, sizeof(power->wall_name),
  437. "wm831x-wall");
  438. snprintf(power->battery_name, sizeof(power->wall_name),
  439. "wm831x-battery");
  440. snprintf(power->usb_name, sizeof(power->wall_name),
  441. "wm831x-usb");
  442. }
  443. /* We ignore configuration failures since we can still read back
  444. * the status without enabling the charger.
  445. */
  446. wm831x_config_battery(wm831x);
  447. power->wall_desc.name = power->wall_name;
  448. power->wall_desc.type = POWER_SUPPLY_TYPE_MAINS;
  449. power->wall_desc.properties = wm831x_wall_props;
  450. power->wall_desc.num_properties = ARRAY_SIZE(wm831x_wall_props);
  451. power->wall_desc.get_property = wm831x_wall_get_prop;
  452. power->wall = power_supply_register(&pdev->dev, &power->wall_desc,
  453. NULL);
  454. if (IS_ERR(power->wall)) {
  455. ret = PTR_ERR(power->wall);
  456. goto err;
  457. }
  458. power->usb_desc.name = power->usb_name,
  459. power->usb_desc.type = POWER_SUPPLY_TYPE_USB;
  460. power->usb_desc.properties = wm831x_usb_props;
  461. power->usb_desc.num_properties = ARRAY_SIZE(wm831x_usb_props);
  462. power->usb_desc.get_property = wm831x_usb_get_prop;
  463. power->usb = power_supply_register(&pdev->dev, &power->usb_desc, NULL);
  464. if (IS_ERR(power->usb)) {
  465. ret = PTR_ERR(power->usb);
  466. goto err_wall;
  467. }
  468. ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1);
  469. if (ret < 0)
  470. goto err_wall;
  471. power->have_battery = ret & WM831X_CHG_ENA;
  472. if (power->have_battery) {
  473. power->battery_desc.name = power->battery_name;
  474. power->battery_desc.properties = wm831x_bat_props;
  475. power->battery_desc.num_properties = ARRAY_SIZE(wm831x_bat_props);
  476. power->battery_desc.get_property = wm831x_bat_get_prop;
  477. power->battery_desc.use_for_apm = 1;
  478. power->battery = power_supply_register(&pdev->dev,
  479. &power->battery_desc,
  480. NULL);
  481. if (IS_ERR(power->battery)) {
  482. ret = PTR_ERR(power->battery);
  483. goto err_usb;
  484. }
  485. }
  486. irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
  487. ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
  488. IRQF_TRIGGER_RISING | IRQF_ONESHOT, "System power low",
  489. power);
  490. if (ret != 0) {
  491. dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
  492. irq, ret);
  493. goto err_battery;
  494. }
  495. irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
  496. ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
  497. IRQF_TRIGGER_RISING | IRQF_ONESHOT, "Power source",
  498. power);
  499. if (ret != 0) {
  500. dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n",
  501. irq, ret);
  502. goto err_syslo;
  503. }
  504. for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
  505. irq = wm831x_irq(wm831x,
  506. platform_get_irq_byname(pdev,
  507. wm831x_bat_irqs[i]));
  508. ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
  509. IRQF_TRIGGER_RISING | IRQF_ONESHOT,
  510. wm831x_bat_irqs[i],
  511. power);
  512. if (ret != 0) {
  513. dev_err(&pdev->dev,
  514. "Failed to request %s IRQ %d: %d\n",
  515. wm831x_bat_irqs[i], irq, ret);
  516. goto err_bat_irq;
  517. }
  518. }
  519. return ret;
  520. err_bat_irq:
  521. --i;
  522. for (; i >= 0; i--) {
  523. irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
  524. free_irq(irq, power);
  525. }
  526. irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
  527. free_irq(irq, power);
  528. err_syslo:
  529. irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
  530. free_irq(irq, power);
  531. err_battery:
  532. if (power->have_battery)
  533. power_supply_unregister(power->battery);
  534. err_usb:
  535. power_supply_unregister(power->usb);
  536. err_wall:
  537. power_supply_unregister(power->wall);
  538. err:
  539. return ret;
  540. }
  541. static int wm831x_power_remove(struct platform_device *pdev)
  542. {
  543. struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
  544. struct wm831x *wm831x = wm831x_power->wm831x;
  545. int irq, i;
  546. for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
  547. irq = wm831x_irq(wm831x,
  548. platform_get_irq_byname(pdev,
  549. wm831x_bat_irqs[i]));
  550. free_irq(irq, wm831x_power);
  551. }
  552. irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
  553. free_irq(irq, wm831x_power);
  554. irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
  555. free_irq(irq, wm831x_power);
  556. if (wm831x_power->have_battery)
  557. power_supply_unregister(wm831x_power->battery);
  558. power_supply_unregister(wm831x_power->wall);
  559. power_supply_unregister(wm831x_power->usb);
  560. return 0;
  561. }
  562. static struct platform_driver wm831x_power_driver = {
  563. .probe = wm831x_power_probe,
  564. .remove = wm831x_power_remove,
  565. .driver = {
  566. .name = "wm831x-power",
  567. },
  568. };
  569. module_platform_driver(wm831x_power_driver);
  570. MODULE_DESCRIPTION("Power supply driver for WM831x PMICs");
  571. MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
  572. MODULE_LICENSE("GPL");
  573. MODULE_ALIAS("platform:wm831x-power");