brintos

brintos / linux-shallow public Read only

0
0
Text · 15.9 KiB · 3f79ab6 Raw
591 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Battery driver for wm8350 PMIC4 *5 * Copyright 2007, 2008 Wolfson Microelectronics PLC.6 *7 * Based on OLPC Battery Driver8 *9 * Copyright 2006  David Woodhouse <dwmw2@infradead.org>10 */11 12#include <linux/module.h>13#include <linux/err.h>14#include <linux/platform_device.h>15#include <linux/power_supply.h>16#include <linux/mfd/wm8350/supply.h>17#include <linux/mfd/wm8350/core.h>18#include <linux/mfd/wm8350/comparator.h>19 20static int wm8350_read_battery_uvolts(struct wm8350 *wm8350)21{22	return wm8350_read_auxadc(wm8350, WM8350_AUXADC_BATT, 0, 0)23		* WM8350_AUX_COEFF;24}25 26static int wm8350_read_line_uvolts(struct wm8350 *wm8350)27{28	return wm8350_read_auxadc(wm8350, WM8350_AUXADC_LINE, 0, 0)29		* WM8350_AUX_COEFF;30}31 32static int wm8350_read_usb_uvolts(struct wm8350 *wm8350)33{34	return wm8350_read_auxadc(wm8350, WM8350_AUXADC_USB, 0, 0)35		* WM8350_AUX_COEFF;36}37 38#define WM8350_BATT_SUPPLY	139#define WM8350_USB_SUPPLY	240#define WM8350_LINE_SUPPLY	441 42static inline int wm8350_charge_time_min(struct wm8350 *wm8350, int min)43{44	if (!wm8350->power.rev_g_coeff)45		return (((min - 30) / 15) & 0xf) << 8;46	else47		return (((min - 30) / 30) & 0xf) << 8;48}49 50static int wm8350_get_supplies(struct wm8350 *wm8350)51{52	u16 sm, ov, co, chrg;53	int supplies = 0;54 55	sm = wm8350_reg_read(wm8350, WM8350_STATE_MACHINE_STATUS);56	ov = wm8350_reg_read(wm8350, WM8350_MISC_OVERRIDES);57	co = wm8350_reg_read(wm8350, WM8350_COMPARATOR_OVERRIDES);58	chrg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);59 60	/* USB_SM */61	sm = (sm & WM8350_USB_SM_MASK) >> WM8350_USB_SM_SHIFT;62 63	/* CHG_ISEL */64	chrg &= WM8350_CHG_ISEL_MASK;65 66	/* If the USB state machine is active then we're using that with or67	 * without battery, otherwise check for wall supply */68	if (((sm == WM8350_USB_SM_100_SLV) ||69	     (sm == WM8350_USB_SM_500_SLV) ||70	     (sm == WM8350_USB_SM_STDBY_SLV))71	    && !(ov & WM8350_USB_LIMIT_OVRDE))72		supplies = WM8350_USB_SUPPLY;73	else if (((sm == WM8350_USB_SM_100_SLV) ||74		  (sm == WM8350_USB_SM_500_SLV) ||75		  (sm == WM8350_USB_SM_STDBY_SLV))76		 && (ov & WM8350_USB_LIMIT_OVRDE) && (chrg == 0))77		supplies = WM8350_USB_SUPPLY | WM8350_BATT_SUPPLY;78	else if (co & WM8350_WALL_FB_OVRDE)79		supplies = WM8350_LINE_SUPPLY;80	else81		supplies = WM8350_BATT_SUPPLY;82 83	return supplies;84}85 86static int wm8350_charger_config(struct wm8350 *wm8350,87				 struct wm8350_charger_policy *policy)88{89	u16 reg, eoc_mA, fast_limit_mA;90 91	if (!policy) {92		dev_warn(wm8350->dev,93			 "No charger policy, charger not configured.\n");94		return -EINVAL;95	}96 97	/* make sure USB fast charge current is not > 500mA */98	if (policy->fast_limit_USB_mA > 500) {99		dev_err(wm8350->dev, "USB fast charge > 500mA\n");100		return -EINVAL;101	}102 103	eoc_mA = WM8350_CHG_EOC_mA(policy->eoc_mA);104 105	wm8350_reg_unlock(wm8350);106 107	reg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1)108		& WM8350_CHG_ENA_R168;109	wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,110			 reg | eoc_mA | policy->trickle_start_mV |111			 WM8350_CHG_TRICKLE_TEMP_CHOKE |112			 WM8350_CHG_TRICKLE_USB_CHOKE |113			 WM8350_CHG_FAST_USB_THROTTLE);114 115	if (wm8350_get_supplies(wm8350) & WM8350_USB_SUPPLY) {116		fast_limit_mA =117			WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_USB_mA);118		wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,119			    policy->charge_mV | policy->trickle_charge_USB_mA |120			    fast_limit_mA | wm8350_charge_time_min(wm8350,121						policy->charge_timeout));122 123	} else {124		fast_limit_mA =125			WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_mA);126		wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,127			    policy->charge_mV | policy->trickle_charge_mA |128			    fast_limit_mA | wm8350_charge_time_min(wm8350,129						policy->charge_timeout));130	}131 132	wm8350_reg_lock(wm8350);133	return 0;134}135 136static int wm8350_batt_status(struct wm8350 *wm8350)137{138	u16 state;139 140	state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);141	state &= WM8350_CHG_STS_MASK;142 143	switch (state) {144	case WM8350_CHG_STS_OFF:145		return POWER_SUPPLY_STATUS_DISCHARGING;146 147	case WM8350_CHG_STS_TRICKLE:148	case WM8350_CHG_STS_FAST:149		return POWER_SUPPLY_STATUS_CHARGING;150 151	default:152		return POWER_SUPPLY_STATUS_UNKNOWN;153	}154}155 156static ssize_t charger_state_show(struct device *dev,157				 struct device_attribute *attr, char *buf)158{159	struct wm8350 *wm8350 = dev_get_drvdata(dev);160	char *charge;161	int state;162 163	state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) &164	    WM8350_CHG_STS_MASK;165	switch (state) {166	case WM8350_CHG_STS_OFF:167		charge = "Charger Off";168		break;169	case WM8350_CHG_STS_TRICKLE:170		charge = "Trickle Charging";171		break;172	case WM8350_CHG_STS_FAST:173		charge = "Fast Charging";174		break;175	default:176		return 0;177	}178 179	return sysfs_emit(buf, "%s\n", charge);180}181 182static DEVICE_ATTR_RO(charger_state);183 184static irqreturn_t wm8350_charger_handler(int irq, void *data)185{186	struct wm8350 *wm8350 = data;187	struct wm8350_power *power = &wm8350->power;188	struct wm8350_charger_policy *policy = power->policy;189 190	switch (irq - wm8350->irq_base) {191	case WM8350_IRQ_CHG_BAT_FAIL:192		dev_err(wm8350->dev, "battery failed\n");193		break;194	case WM8350_IRQ_CHG_TO:195		dev_err(wm8350->dev, "charger timeout\n");196		power_supply_changed(power->battery);197		break;198 199	case WM8350_IRQ_CHG_BAT_HOT:200	case WM8350_IRQ_CHG_BAT_COLD:201	case WM8350_IRQ_CHG_START:202	case WM8350_IRQ_CHG_END:203		power_supply_changed(power->battery);204		break;205 206	case WM8350_IRQ_CHG_FAST_RDY:207		dev_dbg(wm8350->dev, "fast charger ready\n");208		wm8350_charger_config(wm8350, policy);209		wm8350_reg_unlock(wm8350);210		wm8350_set_bits(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,211				WM8350_CHG_FAST);212		wm8350_reg_lock(wm8350);213		break;214 215	case WM8350_IRQ_CHG_VBATT_LT_3P9:216		dev_warn(wm8350->dev, "battery < 3.9V\n");217		break;218	case WM8350_IRQ_CHG_VBATT_LT_3P1:219		dev_warn(wm8350->dev, "battery < 3.1V\n");220		break;221	case WM8350_IRQ_CHG_VBATT_LT_2P85:222		dev_warn(wm8350->dev, "battery < 2.85V\n");223		break;224 225		/* Supply change.  We will overnotify but it should do226		 * no harm. */227	case WM8350_IRQ_EXT_USB_FB:228	case WM8350_IRQ_EXT_WALL_FB:229		wm8350_charger_config(wm8350, policy);230		fallthrough;231	case WM8350_IRQ_EXT_BAT_FB:232		power_supply_changed(power->battery);233		power_supply_changed(power->usb);234		power_supply_changed(power->ac);235		break;236 237	default:238		dev_err(wm8350->dev, "Unknown interrupt %d\n", irq);239	}240 241	return IRQ_HANDLED;242}243 244/*********************************************************************245 *		AC Power246 *********************************************************************/247static int wm8350_ac_get_prop(struct power_supply *psy,248			      enum power_supply_property psp,249			      union power_supply_propval *val)250{251	struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);252	int ret = 0;253 254	switch (psp) {255	case POWER_SUPPLY_PROP_ONLINE:256		val->intval = !!(wm8350_get_supplies(wm8350) &257				 WM8350_LINE_SUPPLY);258		break;259	case POWER_SUPPLY_PROP_VOLTAGE_NOW:260		val->intval = wm8350_read_line_uvolts(wm8350);261		break;262	default:263		ret = -EINVAL;264		break;265	}266	return ret;267}268 269static enum power_supply_property wm8350_ac_props[] = {270	POWER_SUPPLY_PROP_ONLINE,271	POWER_SUPPLY_PROP_VOLTAGE_NOW,272};273 274/*********************************************************************275 *		USB Power276 *********************************************************************/277static int wm8350_usb_get_prop(struct power_supply *psy,278			       enum power_supply_property psp,279			       union power_supply_propval *val)280{281	struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);282	int ret = 0;283 284	switch (psp) {285	case POWER_SUPPLY_PROP_ONLINE:286		val->intval = !!(wm8350_get_supplies(wm8350) &287				 WM8350_USB_SUPPLY);288		break;289	case POWER_SUPPLY_PROP_VOLTAGE_NOW:290		val->intval = wm8350_read_usb_uvolts(wm8350);291		break;292	default:293		ret = -EINVAL;294		break;295	}296	return ret;297}298 299static enum power_supply_property wm8350_usb_props[] = {300	POWER_SUPPLY_PROP_ONLINE,301	POWER_SUPPLY_PROP_VOLTAGE_NOW,302};303 304/*********************************************************************305 *		Battery properties306 *********************************************************************/307 308static int wm8350_bat_check_health(struct wm8350 *wm8350)309{310	u16 reg;311 312	if (wm8350_read_battery_uvolts(wm8350) < 2850000)313		return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;314 315	reg = wm8350_reg_read(wm8350, WM8350_CHARGER_OVERRIDES);316	if (reg & WM8350_CHG_BATT_HOT_OVRDE)317		return POWER_SUPPLY_HEALTH_OVERHEAT;318 319	if (reg & WM8350_CHG_BATT_COLD_OVRDE)320		return POWER_SUPPLY_HEALTH_COLD;321 322	return POWER_SUPPLY_HEALTH_GOOD;323}324 325static int wm8350_bat_get_charge_type(struct wm8350 *wm8350)326{327	int state;328 329	state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) &330	    WM8350_CHG_STS_MASK;331	switch (state) {332	case WM8350_CHG_STS_OFF:333		return POWER_SUPPLY_CHARGE_TYPE_NONE;334	case WM8350_CHG_STS_TRICKLE:335		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;336	case WM8350_CHG_STS_FAST:337		return POWER_SUPPLY_CHARGE_TYPE_FAST;338	default:339		return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;340	}341}342 343static int wm8350_bat_get_property(struct power_supply *psy,344				   enum power_supply_property psp,345				   union power_supply_propval *val)346{347	struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);348	int ret = 0;349 350	switch (psp) {351	case POWER_SUPPLY_PROP_STATUS:352		val->intval = wm8350_batt_status(wm8350);353		break;354	case POWER_SUPPLY_PROP_ONLINE:355		val->intval = !!(wm8350_get_supplies(wm8350) &356				 WM8350_BATT_SUPPLY);357		break;358	case POWER_SUPPLY_PROP_VOLTAGE_NOW:359		val->intval = wm8350_read_battery_uvolts(wm8350);360		break;361	case POWER_SUPPLY_PROP_HEALTH:362		val->intval = wm8350_bat_check_health(wm8350);363		break;364	case POWER_SUPPLY_PROP_CHARGE_TYPE:365		val->intval = wm8350_bat_get_charge_type(wm8350);366		break;367	default:368		ret = -EINVAL;369		break;370	}371 372	return ret;373}374 375static enum power_supply_property wm8350_bat_props[] = {376	POWER_SUPPLY_PROP_STATUS,377	POWER_SUPPLY_PROP_ONLINE,378	POWER_SUPPLY_PROP_VOLTAGE_NOW,379	POWER_SUPPLY_PROP_HEALTH,380	POWER_SUPPLY_PROP_CHARGE_TYPE,381};382 383static const struct power_supply_desc wm8350_ac_desc = {384	.name		= "wm8350-ac",385	.type		= POWER_SUPPLY_TYPE_MAINS,386	.properties	= wm8350_ac_props,387	.num_properties	= ARRAY_SIZE(wm8350_ac_props),388	.get_property	= wm8350_ac_get_prop,389};390 391static const struct power_supply_desc wm8350_battery_desc = {392	.name		= "wm8350-battery",393	.properties	= wm8350_bat_props,394	.num_properties	= ARRAY_SIZE(wm8350_bat_props),395	.get_property	= wm8350_bat_get_property,396	.use_for_apm	= 1,397};398 399static const struct power_supply_desc wm8350_usb_desc = {400	.name		= "wm8350-usb",401	.type		= POWER_SUPPLY_TYPE_USB,402	.properties	= wm8350_usb_props,403	.num_properties	= ARRAY_SIZE(wm8350_usb_props),404	.get_property	= wm8350_usb_get_prop,405};406 407/*********************************************************************408 *		Initialisation409 *********************************************************************/410 411static int wm8350_init_charger(struct wm8350 *wm8350)412{413	int ret;414 415	/* register our interest in charger events */416	ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT,417			    wm8350_charger_handler, 0, "Battery hot", wm8350);418	if (ret)419		goto err;420 421	ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD,422			    wm8350_charger_handler, 0, "Battery cold", wm8350);423	if (ret)424		goto free_chg_bat_hot;425 426	ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL,427			    wm8350_charger_handler, 0, "Battery fail", wm8350);428	if (ret)429		goto free_chg_bat_cold;430 431	ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO,432			    wm8350_charger_handler, 0,433			    "Charger timeout", wm8350);434	if (ret)435		goto free_chg_bat_fail;436 437	ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END,438			    wm8350_charger_handler, 0,439			    "Charge end", wm8350);440	if (ret)441		goto free_chg_to;442 443	ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START,444			    wm8350_charger_handler, 0,445			    "Charge start", wm8350);446	if (ret)447		goto free_chg_end;448 449	ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY,450			    wm8350_charger_handler, 0,451			    "Fast charge ready", wm8350);452	if (ret)453		goto free_chg_start;454 455	ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9,456			    wm8350_charger_handler, 0,457			    "Battery <3.9V", wm8350);458	if (ret)459		goto free_chg_fast_rdy;460 461	ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1,462			    wm8350_charger_handler, 0,463			    "Battery <3.1V", wm8350);464	if (ret)465		goto free_chg_vbatt_lt_3p9;466 467	ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85,468			    wm8350_charger_handler, 0,469			    "Battery <2.85V", wm8350);470	if (ret)471		goto free_chg_vbatt_lt_3p1;472 473	/* and supply change events */474	ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB,475			    wm8350_charger_handler, 0, "USB", wm8350);476	if (ret)477		goto free_chg_vbatt_lt_2p85;478 479	ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB,480			    wm8350_charger_handler, 0, "Wall", wm8350);481	if (ret)482		goto free_ext_usb_fb;483 484	ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB,485			    wm8350_charger_handler, 0, "Battery", wm8350);486	if (ret)487		goto free_ext_wall_fb;488 489	return 0;490 491free_ext_wall_fb:492	wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, wm8350);493free_ext_usb_fb:494	wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, wm8350);495free_chg_vbatt_lt_2p85:496	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, wm8350);497free_chg_vbatt_lt_3p1:498	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, wm8350);499free_chg_vbatt_lt_3p9:500	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, wm8350);501free_chg_fast_rdy:502	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, wm8350);503free_chg_start:504	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, wm8350);505free_chg_end:506	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, wm8350);507free_chg_to:508	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, wm8350);509free_chg_bat_fail:510	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, wm8350);511free_chg_bat_cold:512	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, wm8350);513free_chg_bat_hot:514	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, wm8350);515err:516	return ret;517}518 519static void free_charger_irq(struct wm8350 *wm8350)520{521	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, wm8350);522	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, wm8350);523	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, wm8350);524	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, wm8350);525	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, wm8350);526	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, wm8350);527	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, wm8350);528	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, wm8350);529	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, wm8350);530	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, wm8350);531	wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, wm8350);532	wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, wm8350);533	wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, wm8350);534}535 536static int wm8350_power_probe(struct platform_device *pdev)537{538	struct wm8350 *wm8350 = platform_get_drvdata(pdev);539	struct wm8350_power *power = &wm8350->power;540	struct wm8350_charger_policy *policy = power->policy;541	int ret;542 543	power->ac = devm_power_supply_register(&pdev->dev, &wm8350_ac_desc, NULL);544	if (IS_ERR(power->ac))545		return PTR_ERR(power->ac);546 547	power->battery = devm_power_supply_register(&pdev->dev, &wm8350_battery_desc, NULL);548	if (IS_ERR(power->battery))549		return PTR_ERR(power->battery);550 551	power->usb = devm_power_supply_register(&pdev->dev, &wm8350_usb_desc, NULL);552	if (IS_ERR(power->usb))553		return PTR_ERR(power->usb);554 555	ret = device_create_file(&pdev->dev, &dev_attr_charger_state);556	if (ret < 0)557		dev_warn(wm8350->dev, "failed to add charge sysfs: %d\n", ret);558	ret = 0;559 560	wm8350_init_charger(wm8350);561	if (wm8350_charger_config(wm8350, policy) == 0) {562		wm8350_reg_unlock(wm8350);563		wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA);564		wm8350_reg_lock(wm8350);565	}566 567	return ret;568}569 570static void wm8350_power_remove(struct platform_device *pdev)571{572	struct wm8350 *wm8350 = platform_get_drvdata(pdev);573 574	free_charger_irq(wm8350);575	device_remove_file(&pdev->dev, &dev_attr_charger_state);576}577 578static struct platform_driver wm8350_power_driver = {579	.probe = wm8350_power_probe,580	.remove_new = wm8350_power_remove,581	.driver = {582		.name = "wm8350-power",583	},584};585 586module_platform_driver(wm8350_power_driver);587 588MODULE_LICENSE("GPL");589MODULE_DESCRIPTION("Power supply driver for WM8350");590MODULE_ALIAS("platform:wm8350-power");591