13961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown/*
23961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown * PMU driver for Wolfson Microelectronics wm831x PMICs
33961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown *
43961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown * Copyright 2009 Wolfson Microelectronics PLC.
53961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown *
63961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown * This program is free software; you can redistribute it and/or modify
73961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown * it under the terms of the GNU General Public License version 2 as
83961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown * published by the Free Software Foundation.
93961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown */
103961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
113961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown#include <linux/module.h>
123961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown#include <linux/err.h>
133961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown#include <linux/platform_device.h>
143961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown#include <linux/power_supply.h>
155a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
163961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
173961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown#include <linux/mfd/wm831x/core.h>
183961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown#include <linux/mfd/wm831x/auxadc.h>
193961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown#include <linux/mfd/wm831x/pmu.h>
203961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown#include <linux/mfd/wm831x/pdata.h>
213961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
223961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstruct wm831x_power {
233961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x *wm831x;
243961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct power_supply wall;
253961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct power_supply usb;
263961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct power_supply battery;
27ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown	char wall_name[20];
28ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown	char usb_name[20];
29ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown	char battery_name[20];
3035c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown	bool have_battery;
313961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
323961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
333961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic int wm831x_power_check_online(struct wm831x *wm831x, int supply,
343961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				     union power_supply_propval *val)
353961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
363961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int ret;
373961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
383961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
393961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret < 0)
403961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return ret;
413961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
423961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret & supply)
433961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		val->intval = 1;
443961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	else
453961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		val->intval = 0;
463961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
473961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return 0;
483961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
493961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
503961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic int wm831x_power_read_voltage(struct wm831x *wm831x,
513961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				     enum wm831x_auxadc src,
523961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				     union power_supply_propval *val)
533961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
543961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int ret;
553961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
563961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	ret = wm831x_auxadc_read_uv(wm831x, src);
573961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret >= 0)
583961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		val->intval = ret;
593961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
603961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return ret;
613961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
623961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
633961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown/*********************************************************************
643961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown *		WALL Power
653961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown *********************************************************************/
663961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic int wm831x_wall_get_prop(struct power_supply *psy,
673961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				enum power_supply_property psp,
683961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				union power_supply_propval *val)
693961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
703961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
713961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x *wm831x = wm831x_power->wm831x;
723961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int ret = 0;
733961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
743961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	switch (psp) {
753961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case POWER_SUPPLY_PROP_ONLINE:
763961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = wm831x_power_check_online(wm831x, WM831X_PWR_WALL, val);
773961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
783961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
793961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_WALL, val);
803961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
813961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	default:
823961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = -EINVAL;
833961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
843961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
853961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
863961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return ret;
873961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
883961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
893961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic enum power_supply_property wm831x_wall_props[] = {
903961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	POWER_SUPPLY_PROP_ONLINE,
913961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	POWER_SUPPLY_PROP_VOLTAGE_NOW,
923961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
933961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
943961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown/*********************************************************************
953961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown *		USB Power
963961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown *********************************************************************/
973961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic int wm831x_usb_get_prop(struct power_supply *psy,
983961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			       enum power_supply_property psp,
993961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			       union power_supply_propval *val)
1003961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
1013961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
1023961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x *wm831x = wm831x_power->wm831x;
1033961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int ret = 0;
1043961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
1053961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	switch (psp) {
1063961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case POWER_SUPPLY_PROP_ONLINE:
1073961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = wm831x_power_check_online(wm831x, WM831X_PWR_USB, val);
1083961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
1093961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
1103961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_USB, val);
1113961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
1123961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	default:
1133961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = -EINVAL;
1143961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
1153961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
1163961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
1173961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return ret;
1183961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
1193961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
1203961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic enum power_supply_property wm831x_usb_props[] = {
1213961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	POWER_SUPPLY_PROP_ONLINE,
1223961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	POWER_SUPPLY_PROP_VOLTAGE_NOW,
1233961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
1243961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
1253961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown/*********************************************************************
1263961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown *		Battery properties
1273961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown *********************************************************************/
1283961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
1293961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstruct chg_map {
1303961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int val;
1313961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int reg_val;
1323961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
1333961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
1343961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic struct chg_map trickle_ilims[] = {
1353961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  50, 0 << WM831X_CHG_TRKL_ILIM_SHIFT },
1363961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 100, 1 << WM831X_CHG_TRKL_ILIM_SHIFT },
1373961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 150, 2 << WM831X_CHG_TRKL_ILIM_SHIFT },
1383961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 200, 3 << WM831X_CHG_TRKL_ILIM_SHIFT },
1393961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
1403961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
1413961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic struct chg_map vsels[] = {
1423961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 4050, 0 << WM831X_CHG_VSEL_SHIFT },
1433961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 4100, 1 << WM831X_CHG_VSEL_SHIFT },
1443961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 4150, 2 << WM831X_CHG_VSEL_SHIFT },
1453961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 4200, 3 << WM831X_CHG_VSEL_SHIFT },
1463961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
1473961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
1483961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic struct chg_map fast_ilims[] = {
1493961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{    0,  0 << WM831X_CHG_FAST_ILIM_SHIFT },
1503961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{   50,  1 << WM831X_CHG_FAST_ILIM_SHIFT },
1513961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  100,  2 << WM831X_CHG_FAST_ILIM_SHIFT },
1523961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  150,  3 << WM831X_CHG_FAST_ILIM_SHIFT },
1533961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  200,  4 << WM831X_CHG_FAST_ILIM_SHIFT },
1543961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  250,  5 << WM831X_CHG_FAST_ILIM_SHIFT },
1553961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  300,  6 << WM831X_CHG_FAST_ILIM_SHIFT },
1563961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  350,  7 << WM831X_CHG_FAST_ILIM_SHIFT },
1573961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  400,  8 << WM831X_CHG_FAST_ILIM_SHIFT },
1583961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  450,  9 << WM831X_CHG_FAST_ILIM_SHIFT },
1593961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  500, 10 << WM831X_CHG_FAST_ILIM_SHIFT },
1603961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  600, 11 << WM831X_CHG_FAST_ILIM_SHIFT },
1613961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  700, 12 << WM831X_CHG_FAST_ILIM_SHIFT },
1623961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  800, 13 << WM831X_CHG_FAST_ILIM_SHIFT },
1633961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  900, 14 << WM831X_CHG_FAST_ILIM_SHIFT },
1643961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 1000, 15 << WM831X_CHG_FAST_ILIM_SHIFT },
1653961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
1663961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
1673961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic struct chg_map eoc_iterms[] = {
1683961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 20, 0 << WM831X_CHG_ITERM_SHIFT },
1693961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 30, 1 << WM831X_CHG_ITERM_SHIFT },
1703961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 40, 2 << WM831X_CHG_ITERM_SHIFT },
1713961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 50, 3 << WM831X_CHG_ITERM_SHIFT },
1723961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 60, 4 << WM831X_CHG_ITERM_SHIFT },
1733961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 70, 5 << WM831X_CHG_ITERM_SHIFT },
1743961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 80, 6 << WM831X_CHG_ITERM_SHIFT },
1753961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 90, 7 << WM831X_CHG_ITERM_SHIFT },
1763961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
1773961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
1783961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic struct chg_map chg_times[] = {
1793961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  60,  0 << WM831X_CHG_TIME_SHIFT },
1803961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{  90,  1 << WM831X_CHG_TIME_SHIFT },
1813961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 120,  2 << WM831X_CHG_TIME_SHIFT },
1823961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 150,  3 << WM831X_CHG_TIME_SHIFT },
1833961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 180,  4 << WM831X_CHG_TIME_SHIFT },
1843961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 210,  5 << WM831X_CHG_TIME_SHIFT },
1853961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 240,  6 << WM831X_CHG_TIME_SHIFT },
1863961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 270,  7 << WM831X_CHG_TIME_SHIFT },
1873961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 300,  8 << WM831X_CHG_TIME_SHIFT },
1883961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 330,  9 << WM831X_CHG_TIME_SHIFT },
1893961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 360, 10 << WM831X_CHG_TIME_SHIFT },
1903961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 390, 11 << WM831X_CHG_TIME_SHIFT },
1913961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 420, 12 << WM831X_CHG_TIME_SHIFT },
1923961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 450, 13 << WM831X_CHG_TIME_SHIFT },
1933961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 480, 14 << WM831X_CHG_TIME_SHIFT },
1943961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	{ 510, 15 << WM831X_CHG_TIME_SHIFT },
1953961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
1963961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
1973961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic void wm831x_battey_apply_config(struct wm831x *wm831x,
1983961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				       struct chg_map *map, int count, int val,
1993961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				       int *reg, const char *name,
2003961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				       const char *units)
2013961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
2023961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int i;
2033961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2043961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	for (i = 0; i < count; i++)
2053961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		if (val == map[i].val)
2063961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			break;
2073961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (i == count) {
2083961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		dev_err(wm831x->dev, "Invalid %s %d%s\n",
2093961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			name, val, units);
2103961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	} else {
2113961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*reg |= map[i].reg_val;
2123961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		dev_dbg(wm831x->dev, "Set %s of %d%s\n", name, val, units);
2133961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
2143961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
2153961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2163961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic void wm831x_config_battery(struct wm831x *wm831x)
2173961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
2183961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
2193961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x_battery_pdata *pdata;
2203961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int ret, reg1, reg2;
2213961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2223961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (!wm831x_pdata || !wm831x_pdata->battery) {
2233961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		dev_warn(wm831x->dev,
2243961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			 "No battery charger configuration\n");
2253961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return;
2263961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
2273961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2283961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	pdata = wm831x_pdata->battery;
2293961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2303961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	reg1 = 0;
2313961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	reg2 = 0;
2323961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2333961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (!pdata->enable) {
2343961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		dev_info(wm831x->dev, "Battery charger disabled\n");
2353961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return;
2363961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
2373961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2383961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	reg1 |= WM831X_CHG_ENA;
2393961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (pdata->off_mask)
2403961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		reg2 |= WM831X_CHG_OFF_MSK;
2413961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (pdata->fast_enable)
2423961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		reg1 |= WM831X_CHG_FAST;
2433961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2443961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wm831x_battey_apply_config(wm831x, trickle_ilims,
2453961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				   ARRAY_SIZE(trickle_ilims),
2463961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				   pdata->trickle_ilim, &reg2,
2473961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				   "trickle charge current limit", "mA");
2483961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2493961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wm831x_battey_apply_config(wm831x, vsels, ARRAY_SIZE(vsels),
2503961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				   pdata->vsel, &reg2,
2513961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				   "target voltage", "mV");
2523961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2533961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wm831x_battey_apply_config(wm831x, fast_ilims, ARRAY_SIZE(fast_ilims),
2543961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				   pdata->fast_ilim, &reg2,
2553961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				   "fast charge current limit", "mA");
2563961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2573961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wm831x_battey_apply_config(wm831x, eoc_iterms, ARRAY_SIZE(eoc_iterms),
2583961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				   pdata->eoc_iterm, &reg1,
2593961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				   "end of charge current threshold", "mA");
2603961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2613961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wm831x_battey_apply_config(wm831x, chg_times, ARRAY_SIZE(chg_times),
2623961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				   pdata->timeout, &reg2,
2633961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				   "charger timeout", "min");
2643961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2653961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	ret = wm831x_reg_unlock(wm831x);
2663961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret != 0) {
2673961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
2683961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return;
2693961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
2703961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2713961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1,
2723961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			      WM831X_CHG_ENA_MASK |
2733961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			      WM831X_CHG_FAST_MASK |
2743961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			      WM831X_CHG_ITERM_MASK,
2753961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			      reg1);
2763961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret != 0)
2773961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		dev_err(wm831x->dev, "Failed to set charger control 1: %d\n",
2783961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			ret);
2793961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2803961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2,
2813961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			      WM831X_CHG_OFF_MSK |
2823961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			      WM831X_CHG_TIME_MASK |
2833961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			      WM831X_CHG_FAST_ILIM_MASK |
2843961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			      WM831X_CHG_TRKL_ILIM_MASK |
2853961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			      WM831X_CHG_VSEL_MASK,
2863961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			      reg2);
2873961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret != 0)
2883961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		dev_err(wm831x->dev, "Failed to set charger control 2: %d\n",
2893961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			ret);
2903961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2913961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wm831x_reg_lock(wm831x);
2923961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
2933961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2943961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
2953961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
2963961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int ret;
2973961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
2983961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
2993961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret < 0)
3003961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return ret;
3013961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3023961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret & WM831X_PWR_SRC_BATT) {
3033961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*status = POWER_SUPPLY_STATUS_DISCHARGING;
3043961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return 0;
3053961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
3063961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3073961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
3083961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret < 0)
3093961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return ret;
3103961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3113961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	switch (ret & WM831X_CHG_STATE_MASK) {
3123961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case WM831X_CHG_STATE_OFF:
3133961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
3143961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
3153961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case WM831X_CHG_STATE_TRICKLE:
3163961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case WM831X_CHG_STATE_FAST:
3173961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*status = POWER_SUPPLY_STATUS_CHARGING;
3183961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
3193961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3203961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	default:
3213961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*status = POWER_SUPPLY_STATUS_UNKNOWN;
3223961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
3233961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
3243961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3253961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return 0;
3263961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
3273961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3283961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic int wm831x_bat_check_type(struct wm831x *wm831x, int *type)
3293961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
3303961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int ret;
3313961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3323961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
3333961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret < 0)
3343961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return ret;
3353961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3363961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	switch (ret & WM831X_CHG_STATE_MASK) {
3373961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case WM831X_CHG_STATE_TRICKLE:
3383961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case WM831X_CHG_STATE_TRICKLE_OT:
3393961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
3403961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
3413961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case WM831X_CHG_STATE_FAST:
3423961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case WM831X_CHG_STATE_FAST_OT:
3433961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*type = POWER_SUPPLY_CHARGE_TYPE_FAST;
3443961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
3453961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	default:
3463961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*type = POWER_SUPPLY_CHARGE_TYPE_NONE;
3473961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
3483961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
3493961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3503961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return 0;
3513961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
3523961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3533961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic int wm831x_bat_check_health(struct wm831x *wm831x, int *health)
3543961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
3553961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int ret;
3563961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3573961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
3583961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret < 0)
3593961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return ret;
3603961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3613961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret & WM831X_BATT_HOT_STS) {
3623961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*health = POWER_SUPPLY_HEALTH_OVERHEAT;
3633961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return 0;
3643961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
3653961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3663961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret & WM831X_BATT_COLD_STS) {
3673961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*health = POWER_SUPPLY_HEALTH_COLD;
3683961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return 0;
3693961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
3703961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3713961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret & WM831X_BATT_OV_STS) {
3723961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
3733961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return 0;
3743961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
3753961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3763961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	switch (ret & WM831X_CHG_STATE_MASK) {
3773961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case WM831X_CHG_STATE_TRICKLE_OT:
3783961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case WM831X_CHG_STATE_FAST_OT:
3793961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*health = POWER_SUPPLY_HEALTH_OVERHEAT;
3803961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
3813961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case WM831X_CHG_STATE_DEFECTIVE:
3823961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
3833961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
3843961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	default:
3853961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		*health = POWER_SUPPLY_HEALTH_GOOD;
3863961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
3873961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
3883961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3893961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return 0;
3903961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
3913961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
3923961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic int wm831x_bat_get_prop(struct power_supply *psy,
3933961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			       enum power_supply_property psp,
3943961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			       union power_supply_propval *val)
3953961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
3963961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
3973961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x *wm831x = wm831x_power->wm831x;
3983961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int ret = 0;
3993961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4003961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	switch (psp) {
4013961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case POWER_SUPPLY_PROP_STATUS:
4023961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = wm831x_bat_check_status(wm831x, &val->intval);
4033961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
4043961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case POWER_SUPPLY_PROP_ONLINE:
4053961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT,
4063961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown						val);
4073961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
4083961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
4093961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
4103961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
4113961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case POWER_SUPPLY_PROP_HEALTH:
4123961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = wm831x_bat_check_health(wm831x, &val->intval);
4133961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
4143961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	case POWER_SUPPLY_PROP_CHARGE_TYPE:
4153961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = wm831x_bat_check_type(wm831x, &val->intval);
4163961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
4173961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	default:
4183961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		ret = -EINVAL;
4193961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		break;
4203961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
4213961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4223961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return ret;
4233961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
4243961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4253961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic enum power_supply_property wm831x_bat_props[] = {
4263961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	POWER_SUPPLY_PROP_STATUS,
4273961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	POWER_SUPPLY_PROP_ONLINE,
4283961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	POWER_SUPPLY_PROP_VOLTAGE_NOW,
4293961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	POWER_SUPPLY_PROP_HEALTH,
4303961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	POWER_SUPPLY_PROP_CHARGE_TYPE,
4313961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
4323961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4333961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic const char *wm831x_bat_irqs[] = {
4343961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	"BATT HOT",
4353961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	"BATT COLD",
4363961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	"BATT FAIL",
4373961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	"OV",
4383961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	"END",
4393961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	"TO",
4403961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	"MODE",
4413961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	"START",
4423961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
4433961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4443961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic irqreturn_t wm831x_bat_irq(int irq, void *data)
4453961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
4463961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x_power *wm831x_power = data;
4473961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x *wm831x = wm831x_power->wm831x;
4483961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4493961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq);
4503961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4513961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	/* The battery charger is autonomous so we don't need to do
4523961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	 * anything except kick user space */
45335c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown	if (wm831x_power->have_battery)
45435c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		power_supply_changed(&wm831x_power->battery);
4553961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4563961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return IRQ_HANDLED;
4573961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
4583961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4593961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4603961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown/*********************************************************************
4613961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown *		Initialisation
4623961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown *********************************************************************/
4633961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4643961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic irqreturn_t wm831x_syslo_irq(int irq, void *data)
4653961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
4663961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x_power *wm831x_power = data;
4673961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x *wm831x = wm831x_power->wm831x;
4683961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4693961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	/* Not much we can actually *do* but tell people for
4703961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	 * posterity, we're probably about to run out of power. */
4713961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	dev_crit(wm831x->dev, "SYSVDD under voltage\n");
4723961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4733961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return IRQ_HANDLED;
4743961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
4753961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4763961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
4773961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
4783961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x_power *wm831x_power = data;
4793961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x *wm831x = wm831x_power->wm831x;
4803961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4813961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	dev_dbg(wm831x->dev, "Power source changed\n");
4823961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
483c26964ead57f0aa1dff4926aae2982b174798e7bMark Brown	/* Just notify for everything - little harm in overnotifying. */
48435c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown	if (wm831x_power->have_battery)
48535c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		power_supply_changed(&wm831x_power->battery);
4863961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	power_supply_changed(&wm831x_power->usb);
4873961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	power_supply_changed(&wm831x_power->wall);
4883961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
4893961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return IRQ_HANDLED;
4903961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
4913961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
492c8afa6406e60aec6ff90033e5ffe41a206609296Bill Pembertonstatic int wm831x_power_probe(struct platform_device *pdev)
4933961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
4943961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
495ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown	struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
4963961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x_power *power;
4973961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct power_supply *usb;
4983961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct power_supply *battery;
4993961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct power_supply *wall;
5003961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int ret, irq, i;
5013961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
5023961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL);
5033961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (power == NULL)
5043961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		return -ENOMEM;
5053961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
5063961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	power->wm831x = wm831x;
5073961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	platform_set_drvdata(pdev, power);
5083961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
5093961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	usb = &power->usb;
5103961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	battery = &power->battery;
5113961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wall = &power->wall;
5123961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
513ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown	if (wm831x_pdata && wm831x_pdata->wm831x_num) {
514ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown		snprintf(power->wall_name, sizeof(power->wall_name),
515ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown			 "wm831x-wall.%d", wm831x_pdata->wm831x_num);
516ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown		snprintf(power->battery_name, sizeof(power->wall_name),
517ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown			 "wm831x-battery.%d", wm831x_pdata->wm831x_num);
518ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown		snprintf(power->usb_name, sizeof(power->wall_name),
519ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown			 "wm831x-usb.%d", wm831x_pdata->wm831x_num);
520ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown	} else {
521ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown		snprintf(power->wall_name, sizeof(power->wall_name),
522ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown			 "wm831x-wall");
523ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown		snprintf(power->battery_name, sizeof(power->wall_name),
524ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown			 "wm831x-battery");
525ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown		snprintf(power->usb_name, sizeof(power->wall_name),
526ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown			 "wm831x-usb");
527ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown	}
528ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown
5293961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	/* We ignore configuration failures since we can still read back
530c26964ead57f0aa1dff4926aae2982b174798e7bMark Brown	 * the status without enabling the charger.
5313961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	 */
5323961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wm831x_config_battery(wm831x);
5333961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
534ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown	wall->name = power->wall_name;
5353961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wall->type = POWER_SUPPLY_TYPE_MAINS;
5363961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wall->properties = wm831x_wall_props;
5373961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wall->num_properties = ARRAY_SIZE(wm831x_wall_props);
5383961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	wall->get_property = wm831x_wall_get_prop;
5393961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	ret = power_supply_register(&pdev->dev, wall);
5403961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret)
5413961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		goto err_kmalloc;
5423961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
543ccf8fa2d1b4dd8660aadc830f22645781628b894Mark Brown	usb->name = power->usb_name,
5443961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	usb->type = POWER_SUPPLY_TYPE_USB;
5453961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	usb->properties = wm831x_usb_props;
5463961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	usb->num_properties = ARRAY_SIZE(wm831x_usb_props);
5473961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	usb->get_property = wm831x_usb_get_prop;
5483961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	ret = power_supply_register(&pdev->dev, usb);
5493961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret)
55035c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		goto err_wall;
55135c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown
55235c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1);
55335c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown	if (ret < 0)
55435c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		goto err_wall;
55535c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown	power->have_battery = ret & WM831X_CHG_ENA;
55635c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown
55735c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown	if (power->have_battery) {
55835c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		    battery->name = power->battery_name;
55935c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		    battery->properties = wm831x_bat_props;
56035c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		    battery->num_properties = ARRAY_SIZE(wm831x_bat_props);
56135c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		    battery->get_property = wm831x_bat_get_prop;
56235c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		    battery->use_for_apm = 1;
56335c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		    ret = power_supply_register(&pdev->dev, battery);
56435c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		    if (ret)
56535c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown			    goto err_usb;
56635c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown	}
5673961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
568cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
569b5874f33bbaf00586d05de37706491ee37057e11Mark Brown	ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
570b5874f33bbaf00586d05de37706491ee37057e11Mark Brown				   IRQF_TRIGGER_RISING, "System power low",
571b5874f33bbaf00586d05de37706491ee37057e11Mark Brown				   power);
5723961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret != 0) {
5733961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
5743961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			irq, ret);
57535c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		goto err_battery;
5763961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
5773961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
578cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
579b5874f33bbaf00586d05de37706491ee37057e11Mark Brown	ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
580b5874f33bbaf00586d05de37706491ee37057e11Mark Brown				   IRQF_TRIGGER_RISING, "Power source",
581b5874f33bbaf00586d05de37706491ee37057e11Mark Brown				   power);
5823961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	if (ret != 0) {
5833961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n",
5843961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			irq, ret);
5853961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		goto err_syslo;
5863961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
5873961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
5883961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
589cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown		irq = wm831x_irq(wm831x,
590cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown				 platform_get_irq_byname(pdev,
591cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown							 wm831x_bat_irqs[i]));
592b5874f33bbaf00586d05de37706491ee37057e11Mark Brown		ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
593b5874f33bbaf00586d05de37706491ee37057e11Mark Brown					   IRQF_TRIGGER_RISING,
594b5874f33bbaf00586d05de37706491ee37057e11Mark Brown					   wm831x_bat_irqs[i],
595b5874f33bbaf00586d05de37706491ee37057e11Mark Brown					   power);
5963961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		if (ret != 0) {
5973961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			dev_err(&pdev->dev,
5983961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				"Failed to request %s IRQ %d: %d\n",
5993961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown				wm831x_bat_irqs[i], irq, ret);
6003961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown			goto err_bat_irq;
6013961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		}
6023961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
6033961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
6043961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return ret;
6053961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
6063961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownerr_bat_irq:
6073961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	for (; i >= 0; i--) {
6083961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
609b5874f33bbaf00586d05de37706491ee37057e11Mark Brown		free_irq(irq, power);
6103961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
611cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
612b5874f33bbaf00586d05de37706491ee37057e11Mark Brown	free_irq(irq, power);
6133961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownerr_syslo:
614cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
615b5874f33bbaf00586d05de37706491ee37057e11Mark Brown	free_irq(irq, power);
61635c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brownerr_battery:
61735c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown	if (power->have_battery)
61835c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		power_supply_unregister(battery);
6193961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownerr_usb:
6203961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	power_supply_unregister(usb);
6213961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownerr_wall:
6223961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	power_supply_unregister(wall);
6233961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownerr_kmalloc:
6243961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	kfree(power);
6253961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return ret;
6263961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
6273961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
628415ec69fb1861fc377c65cb30ddc76999891b8e1Bill Pembertonstatic int wm831x_power_remove(struct platform_device *pdev)
6293961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown{
6303961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
631cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown	struct wm831x *wm831x = wm831x_power->wm831x;
6323961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	int irq, i;
6333961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
6343961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
635cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown		irq = wm831x_irq(wm831x,
636cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown				 platform_get_irq_byname(pdev,
637cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown							 wm831x_bat_irqs[i]));
638b5874f33bbaf00586d05de37706491ee37057e11Mark Brown		free_irq(irq, wm831x_power);
6393961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	}
6403961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
641cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
642b5874f33bbaf00586d05de37706491ee37057e11Mark Brown	free_irq(irq, wm831x_power);
6433961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
644cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
645b5874f33bbaf00586d05de37706491ee37057e11Mark Brown	free_irq(irq, wm831x_power);
6463961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
64735c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown	if (wm831x_power->have_battery)
64835c3ae5eef3fc641c75c5e1e4307835c8efa5f6bMark Brown		power_supply_unregister(&wm831x_power->battery);
6493961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	power_supply_unregister(&wm831x_power->wall);
6503961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	power_supply_unregister(&wm831x_power->usb);
6519c99f08991b38bf6ab62ffe9013ee46ff41d66d0Axel Lin	kfree(wm831x_power);
6523961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	return 0;
6533961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown}
6543961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
6553961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brownstatic struct platform_driver wm831x_power_driver = {
6563961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	.probe = wm831x_power_probe,
65728ea73f4c67cb3dd8c972b21d9fdf84ea78d6daaBill Pemberton	.remove = wm831x_power_remove,
6583961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	.driver = {
6593961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown		.name = "wm831x-power",
6603961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown	},
6613961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown};
6623961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
663300bac7fb85a20b2704dc3645419057992f78565Axel Linmodule_platform_driver(wm831x_power_driver);
6643961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark Brown
6653961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark BrownMODULE_DESCRIPTION("Power supply driver for WM831x PMICs");
6663961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark BrownMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
6673961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark BrownMODULE_LICENSE("GPL");
6683961f7c3cf247eee5df7fabadc7a40f2deeb98f3Mark BrownMODULE_ALIAS("platform:wm831x-power");
669