wm831x_power.c 19 KB

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