diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-21 23:36:20 +0400 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-21 23:36:20 +0400 | 
| commit | 03d11a0e458d7008192585124e4c3313c2829046 (patch) | |
| tree | 8e9f5141e53d2d4bf435fbd56f8ae96790304b7f | |
| parent | ac26663572db5b64522b92f3941a58678a832a36 (diff) | |
| parent | 573189354b7c97cd2256b87cf083ee435584594e (diff) | |
| download | linux-03d11a0e458d7008192585124e4c3313c2829046.tar.xz | |
Merge tag 'for-v3.14' of git://git.infradead.org/battery-2.6
Pull battery updates from Dmitry Eremin-Solenikov:
 "I'm picking up power supply maintainership from Anton Vorontov.  Could
  you please pull battery-2.6 git tree changes prepared for the v3.14
  release.
  Highlights:
   - Power supply notifier
   - Several drivers gained DT support
   - Added Maxim 14577 driver
   - Change of maintainer"
* tag 'for-v3.14' of git://git.infradead.org/battery-2.6:
  MAINTAINERS: Pick up power supply maintainership
  max17042_battery: Add IRQF_ONESHOT flag to use default irq handler
  gpio-charger: Support wakeup events
  power_supply: Add charger support for Maxim 14577
  dt: Binding documentation for isp1704 charger
  isp1704_charger: Add DT support
  charger-manager: of_cm_parse_desc() should be static
  bq2415x_charger: Add DT support
  power_supply: Add power_supply_get_by_phandle
  bq2415x_charger: Use power_supply notifier for automode
  power: reset: Add as3722 power-off driver
  mfd: AS3722: Add dt node properties for system power controller
  charger-manager: Support deivce tree in charger manager driver
  charger-manager: Modify the way of checking battery's temperature
  power_supply: Add power_supply notifier
| -rw-r--r-- | Documentation/devicetree/bindings/mfd/as3722.txt | 11 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/power/isp1704.txt | 17 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/power_supply/charger-manager.txt | 81 | ||||
| -rw-r--r-- | MAINTAINERS | 2 | ||||
| -rw-r--r-- | drivers/power/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/power/Makefile | 1 | ||||
| -rw-r--r-- | drivers/power/bq2415x_charger.c | 121 | ||||
| -rw-r--r-- | drivers/power/charger-manager.c | 299 | ||||
| -rw-r--r-- | drivers/power/gpio-charger.c | 19 | ||||
| -rw-r--r-- | drivers/power/isp1704_charger.c | 54 | ||||
| -rw-r--r-- | drivers/power/max14577_charger.c | 311 | ||||
| -rw-r--r-- | drivers/power/max17042_battery.c | 6 | ||||
| -rw-r--r-- | drivers/power/power_supply_core.c | 44 | ||||
| -rw-r--r-- | drivers/power/reset/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/power/reset/Makefile | 1 | ||||
| -rw-r--r-- | drivers/power/reset/as3722-poweroff.c | 96 | ||||
| -rw-r--r-- | include/linux/power/bq2415x_charger.h | 48 | ||||
| -rw-r--r-- | include/linux/power/charger-manager.h | 34 | ||||
| -rw-r--r-- | include/linux/power/isp1704_charger.h | 1 | ||||
| -rw-r--r-- | include/linux/power_supply.h | 16 | 
20 files changed, 1043 insertions, 132 deletions
diff --git a/Documentation/devicetree/bindings/mfd/as3722.txt b/Documentation/devicetree/bindings/mfd/as3722.txt index fc2191ecfd6b..8edcb9bd873b 100644 --- a/Documentation/devicetree/bindings/mfd/as3722.txt +++ b/Documentation/devicetree/bindings/mfd/as3722.txt @@ -112,6 +112,15 @@ Following are properties of regulator subnode.  		ams,enable-tracking: Enable tracking with SD1, only supported  			by LDO3. +Power-off: +========= +AS3722 supports the system power off by turning off all its rail. This +is provided through pm_power_off. +The device node should have the following properties to enable this +functionality +ams,system-power-controller: Boolean, to enable the power off functionality +        through this device. +  Example:  --------  #include <dt-bindings/mfd/as3722.h> @@ -120,6 +129,8 @@ ams3722 {  	compatible = "ams,as3722";  	reg = <0x48>; +	ams,system-power-controller; +  	interrupt-parent = <&intc>;  	interrupt-controller;  	#interrupt-cells = <2>; diff --git a/Documentation/devicetree/bindings/power/isp1704.txt b/Documentation/devicetree/bindings/power/isp1704.txt new file mode 100644 index 000000000000..fa3596907967 --- /dev/null +++ b/Documentation/devicetree/bindings/power/isp1704.txt @@ -0,0 +1,17 @@ +Binding for NXP ISP1704 USB Charger Detection + +Required properties: +- compatible: Should contain one of the following: + * "nxp,isp1704" +- nxp,enable-gpio: Should contain a phandle + gpio-specifier +  to the GPIO pin connected to the chip's enable pin. +- usb-phy: Should contain a phandle to the USB PHY +  the ISP1704 is connected to. + +Example: + +isp1704 { +	compatible = "nxp,isp1704"; +	nxp,enable-gpio = <&gpio3 3 GPIO_ACTIVE_LOW>; +	usb-phy = <&usb2_phy>; +}; diff --git a/Documentation/devicetree/bindings/power_supply/charger-manager.txt b/Documentation/devicetree/bindings/power_supply/charger-manager.txt new file mode 100644 index 000000000000..2b33750e3db2 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/charger-manager.txt @@ -0,0 +1,81 @@ +charger-manager bindings +~~~~~~~~~~~~~~~~~~~~~~~~ + +Required properties : + - compatible : "charger-manager" + - <>-supply : for regulator consumer + - cm-num-chargers : number of chargers + - cm-chargers : name of chargers + - cm-fuel-gauge : name of battery fuel gauge + - subnode <regulator> : +	- cm-regulator-name : name of charger regulator +	- subnode <cable> : +		- cm-cable-name : name of charger cable +		- cm-cable-extcon : name of extcon dev +(optional)	- cm-cable-min : minimum current of cable +(optional)	- cm-cable-max : maximum current of cable + +Optional properties : + - cm-name : charger manager's name (default : "battery") + - cm-poll-mode : polling mode (enum polling_modes) + - cm-poll-interval : polling interval + - cm-battery-stat : battery status (enum data_source) + - cm-fullbatt-* : data for full battery checking + - cm-thermal-zone : name of external thermometer's thermal zone + - cm-battery-* : threshold battery temperature for charging +	-cold : critical cold temperature of battery for charging +	-cold-in-minus : flag that cold temerature is in minus degree +	-hot : critical hot temperature of battery for charging +	-temp-diff : temperature difference to allow recharging + - cm-dis/charging-max = limits of charging duration + +Example : +	charger-manager@0 { +		compatible = "charger-manager"; +		chg-reg-supply = <&charger_regulator>; + +		cm-name = "battery"; +		/* Always polling ON : 30s */ +		cm-poll-mode = <1>; +		cm-poll-interval = <30000>; + +		cm-fullbatt-vchkdrop-ms = <30000>; +		cm-fullbatt-vchkdrop-volt = <150000>; +		cm-fullbatt-soc = <100>; + +		cm-battery-stat = <3>; + +		cm-num-chargers = <3>; +		cm-chargers = "charger0", "charger1", "charger2"; + +		cm-fuel-gauge = "fuelgauge0"; + +		cm-thermal-zone = "thermal_zone.1" +		/* in deci centigrade */ +		cm-battery-cold = <50>; +		cm-battery-cold-in-minus; +		cm-battery-hot = <800>; +		cm-battery-temp-diff = <100>; + +		/* Allow charging for 5hr */ +		cm-charging-max = <18000000>; +		/* Allow discharging for 2hr */ +		cm-discharging-max = <7200000>; + +		regulator@0 { +			cm-regulator-name = "chg-reg"; +			cable@0 { +				cm-cable-name = "USB"; +				cm-cable-extcon = "extcon-dev.0"; +				cm-cable-min = <475000>; +				cm-cable-max = <500000>; +			}; +			cable@1 { +				cm-cable-name = "TA"; +				cm-cable-extcon = "extcon-dev.0"; +				cm-cable-min = <650000>; +				cm-cable-max = <675000>; +			}; +		}; + +	}; diff --git a/MAINTAINERS b/MAINTAINERS index 5c214024f60a..1088b70fbad7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6712,7 +6712,7 @@ F:	include/linux/timer*  F:	kernel/*timer*  POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS -M:	Anton Vorontsov <anton@enomsg.org> +M:	Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>  M:	David Woodhouse <dwmw2@infradead.org>  T:	git git://git.infradead.org/battery-2.6.git  S:	Maintained diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 85ad58c6da17..0196acf05231 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -317,6 +317,13 @@ config CHARGER_MANAGER            runtime and in suspend-to-RAM by waking up the system periodically            with help of suspend_again support. +config CHARGER_MAX14577 +	tristate "Maxim MAX14577 MUIC battery charger driver" +	depends on MFD_MAX14577 +	help +	  Say Y to enable support for the battery charger control sysfs and +	  platform data of MAX14577 MUICs. +  config CHARGER_MAX8997  	tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"  	depends on MFD_MAX8997 && REGULATOR_MAX8997 diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 372b4e8ab598..ee54a3e4c90a 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_CHARGER_LP8727)	+= lp8727_charger.o  obj-$(CONFIG_CHARGER_LP8788)	+= lp8788-charger.o  obj-$(CONFIG_CHARGER_GPIO)	+= gpio-charger.o  obj-$(CONFIG_CHARGER_MANAGER)	+= charger-manager.o +obj-$(CONFIG_CHARGER_MAX14577)	+= max14577_charger.o  obj-$(CONFIG_CHARGER_MAX8997)	+= max8997_charger.o  obj-$(CONFIG_CHARGER_MAX8998)	+= max8998_charger.o  obj-$(CONFIG_CHARGER_BQ2415X)	+= bq2415x_charger.o diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c index df893dd1447d..79a37f6d3307 100644 --- a/drivers/power/bq2415x_charger.c +++ b/drivers/power/bq2415x_charger.c @@ -1,7 +1,7 @@  /*   * bq2415x charger driver   * - * Copyright (C) 2011-2012  Pali Rohár <pali.rohar@gmail.com> + * Copyright (C) 2011-2013  Pali Rohár <pali.rohar@gmail.com>   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License as published by @@ -170,6 +170,8 @@ struct bq2415x_device {  	struct bq2415x_platform_data init_data;  	struct power_supply charger;  	struct delayed_work work; +	struct power_supply *notify_psy; +	struct notifier_block nb;  	enum bq2415x_mode reported_mode;/* mode reported by hook function */  	enum bq2415x_mode mode;		/* current configured mode */  	enum bq2415x_chip chip; @@ -795,24 +797,53 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)  } -/* hook function called by other driver which set reported mode */ -static void bq2415x_hook_function(enum bq2415x_mode mode, void *data) +static int bq2415x_notifier_call(struct notifier_block *nb, +		unsigned long val, void *v)  { -	struct bq2415x_device *bq = data; +	struct bq2415x_device *bq = +		container_of(nb, struct bq2415x_device, nb); +	struct power_supply *psy = v; +	enum bq2415x_mode mode; +	union power_supply_propval prop; +	int ret; +	int mA; -	if (!bq) -		return; +	if (val != PSY_EVENT_PROP_CHANGED) +		return NOTIFY_OK; + +	if (psy != bq->notify_psy) +		return NOTIFY_OK; + +	dev_dbg(bq->dev, "notifier call was called\n"); + +	ret = psy->get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, &prop); +	if (ret != 0) +		return NOTIFY_OK; + +	mA = prop.intval; + +	if (mA == 0) +		mode = BQ2415X_MODE_OFF; +	else if (mA < 500) +		mode = BQ2415X_MODE_NONE; +	else if (mA < 1800) +		mode = BQ2415X_MODE_HOST_CHARGER; +	else +		mode = BQ2415X_MODE_DEDICATED_CHARGER; + +	if (bq->reported_mode == mode) +		return NOTIFY_OK; -	dev_dbg(bq->dev, "hook function was called\n");  	bq->reported_mode = mode;  	/* if automode is not enabled do not tell about reported_mode */  	if (bq->automode < 1) -		return; +		return NOTIFY_OK;  	sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode");  	bq2415x_set_mode(bq, bq->reported_mode); +	return NOTIFY_OK;  }  /**** timer functions ****/ @@ -1512,9 +1543,11 @@ static int bq2415x_probe(struct i2c_client *client,  	int num;  	char *name;  	struct bq2415x_device *bq; +	struct device_node *np = client->dev.of_node; +	struct bq2415x_platform_data *pdata = client->dev.platform_data; -	if (!client->dev.platform_data) { -		dev_err(&client->dev, "platform data not set\n"); +	if (!np && !pdata) { +		dev_err(&client->dev, "platform data missing\n");  		return -ENODEV;  	} @@ -1539,6 +1572,17 @@ static int bq2415x_probe(struct i2c_client *client,  		goto error_2;  	} +	if (np) { +		bq->notify_psy = power_supply_get_by_phandle(np, "ti,usb-charger-detection"); + +		if (!bq->notify_psy) +			return -EPROBE_DEFER; +	} +	else if (pdata->notify_device) +		bq->notify_psy = power_supply_get_by_name(pdata->notify_device); +	else +		bq->notify_psy = NULL; +  	i2c_set_clientdata(client, bq);  	bq->id = num; @@ -1550,8 +1594,34 @@ static int bq2415x_probe(struct i2c_client *client,  	bq->autotimer = 0;  	bq->automode = 0; -	memcpy(&bq->init_data, client->dev.platform_data, -			sizeof(bq->init_data)); +	if (np) { +		ret = of_property_read_u32(np, "ti,current-limit", +				&bq->init_data.current_limit); +		if (ret) +			return ret; +		ret = of_property_read_u32(np, "ti,weak-battery-voltage", +				&bq->init_data.weak_battery_voltage); +		if (ret) +			return ret; +		ret = of_property_read_u32(np, "ti,battery-regulation-voltage", +				&bq->init_data.battery_regulation_voltage); +		if (ret) +			return ret; +		ret = of_property_read_u32(np, "ti,charge-current", +				&bq->init_data.charge_current); +		if (ret) +			return ret; +		ret = of_property_read_u32(np, "ti,termination-current", +				&bq->init_data.termination_current); +		if (ret) +			return ret; +		ret = of_property_read_u32(np, "ti,resistor-sense", +				&bq->init_data.resistor_sense); +		if (ret) +			return ret; +	} else { +		memcpy(&bq->init_data, pdata, sizeof(bq->init_data)); +	}  	bq2415x_reset_chip(bq); @@ -1573,16 +1643,20 @@ static int bq2415x_probe(struct i2c_client *client,  		goto error_4;  	} -	if (bq->init_data.set_mode_hook) { -		if (bq->init_data.set_mode_hook( -				bq2415x_hook_function, bq)) { -			bq->automode = 1; -			bq2415x_set_mode(bq, bq->reported_mode); -			dev_info(bq->dev, "automode enabled\n"); -		} else { -			bq->automode = -1; -			dev_info(bq->dev, "automode failed\n"); +	if (bq->notify_psy) { +		bq->nb.notifier_call = bq2415x_notifier_call; +		ret = power_supply_reg_notifier(&bq->nb); +		if (ret) { +			dev_err(bq->dev, "failed to reg notifier: %d\n", ret); +			goto error_5;  		} + +		/* Query for initial reported_mode and set it */ +		bq2415x_notifier_call(&bq->nb, PSY_EVENT_PROP_CHANGED, bq->notify_psy); +		bq2415x_set_mode(bq, bq->reported_mode); + +		bq->automode = 1; +		dev_info(bq->dev, "automode enabled\n");  	} else {  		bq->automode = -1;  		dev_info(bq->dev, "automode not supported\n"); @@ -1594,6 +1668,7 @@ static int bq2415x_probe(struct i2c_client *client,  	dev_info(bq->dev, "driver registered\n");  	return 0; +error_5:  error_4:  	bq2415x_sysfs_exit(bq);  error_3: @@ -1614,8 +1689,8 @@ static int bq2415x_remove(struct i2c_client *client)  {  	struct bq2415x_device *bq = i2c_get_clientdata(client); -	if (bq->init_data.set_mode_hook) -		bq->init_data.set_mode_hook(NULL, NULL); +	if (bq->notify_psy) +		power_supply_unreg_notifier(&bq->nb);  	bq2415x_sysfs_exit(bq);  	bq2415x_power_supply_exit(bq); diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 7287c0efd6bf..9e4dab46eefd 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -25,12 +25,23 @@  #include <linux/power/charger-manager.h>  #include <linux/regulator/consumer.h>  #include <linux/sysfs.h> +#include <linux/of.h> +#include <linux/thermal.h> + +/* + * Default termperature threshold for charging. + * Every temperature units are in tenth of centigrade. + */ +#define CM_DEFAULT_RECHARGE_TEMP_DIFF	50 +#define CM_DEFAULT_CHARGE_TEMP_MAX	500  static const char * const default_event_names[] = {  	[CM_EVENT_UNKNOWN] = "Unknown",  	[CM_EVENT_BATT_FULL] = "Battery Full",  	[CM_EVENT_BATT_IN] = "Battery Inserted",  	[CM_EVENT_BATT_OUT] = "Battery Pulled Out", +	[CM_EVENT_BATT_OVERHEAT] = "Battery Overheat", +	[CM_EVENT_BATT_COLD] = "Battery Cold",  	[CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach",  	[CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",  	[CM_EVENT_OTHERS] = "Other battery events" @@ -518,7 +529,7 @@ static int check_charging_duration(struct charger_manager *cm)  		duration = curr - cm->charging_start_time;  		if (duration > desc->charging_max_duration_ms) { -			dev_info(cm->dev, "Charging duration exceed %lldms\n", +			dev_info(cm->dev, "Charging duration exceed %ums\n",  				 desc->charging_max_duration_ms);  			uevent_notify(cm, "Discharging");  			try_charger_enable(cm, false); @@ -529,7 +540,7 @@ static int check_charging_duration(struct charger_manager *cm)  		if (duration > desc->charging_max_duration_ms &&  				is_ext_pwr_online(cm)) { -			dev_info(cm->dev, "Discharging duration exceed %lldms\n", +			dev_info(cm->dev, "Discharging duration exceed %ums\n",  				 desc->discharging_max_duration_ms);  			uevent_notify(cm, "Recharging");  			try_charger_enable(cm, true); @@ -540,6 +551,60 @@ static int check_charging_duration(struct charger_manager *cm)  	return ret;  } +static int cm_get_battery_temperature(struct charger_manager *cm, +					int *temp) +{ +	int ret; + +	if (!cm->desc->measure_battery_temp) +		return -ENODEV; + +#ifdef CONFIG_THERMAL +	ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp); +	if (!ret) +		/* Calibrate temperature unit */ +		*temp /= 100; +#else +	ret = cm->fuel_gauge->get_property(cm->fuel_gauge, +				POWER_SUPPLY_PROP_TEMP, +				(union power_supply_propval *)temp); +#endif +	return ret; +} + +static int cm_check_thermal_status(struct charger_manager *cm) +{ +	struct charger_desc *desc = cm->desc; +	int temp, upper_limit, lower_limit; +	int ret = 0; + +	ret = cm_get_battery_temperature(cm, &temp); +	if (ret) { +		/* FIXME: +		 * No information of battery temperature might +		 * occur hazadous result. We have to handle it +		 * depending on battery type. +		 */ +		dev_err(cm->dev, "Failed to get battery temperature\n"); +		return 0; +	} + +	upper_limit = desc->temp_max; +	lower_limit = desc->temp_min; + +	if (cm->emergency_stop) { +		upper_limit -= desc->temp_diff; +		lower_limit += desc->temp_diff; +	} + +	if (temp > upper_limit) +		ret = CM_EVENT_BATT_OVERHEAT; +	else if (temp < lower_limit) +		ret = CM_EVENT_BATT_COLD; + +	return ret; +} +  /**   * _cm_monitor - Monitor the temperature and return true for exceptions.   * @cm: the Charger Manager representing the battery. @@ -549,28 +614,22 @@ static int check_charging_duration(struct charger_manager *cm)   */  static bool _cm_monitor(struct charger_manager *cm)  { -	struct charger_desc *desc = cm->desc; -	int temp = desc->temperature_out_of_range(&cm->last_temp_mC); +	int temp_alrt; -	dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n", -		cm->last_temp_mC / 1000, cm->last_temp_mC % 1000); +	temp_alrt = cm_check_thermal_status(cm);  	/* It has been stopped already */ -	if (temp && cm->emergency_stop) +	if (temp_alrt && cm->emergency_stop)  		return false;  	/*  	 * Check temperature whether overheat or cold.  	 * If temperature is out of range normal state, stop charging.  	 */ -	if (temp) { -		cm->emergency_stop = temp; -		if (!try_charger_enable(cm, false)) { -			if (temp > 0) -				uevent_notify(cm, "OVERHEAT"); -			else -				uevent_notify(cm, "COLD"); -		} +	if (temp_alrt) { +		cm->emergency_stop = temp_alrt; +		if (!try_charger_enable(cm, false)) +			uevent_notify(cm, default_event_names[temp_alrt]);  	/*  	 * Check whole charging duration and discharing duration @@ -802,21 +861,8 @@ static int charger_get_property(struct power_supply *psy,  				POWER_SUPPLY_PROP_CURRENT_NOW, val);  		break;  	case POWER_SUPPLY_PROP_TEMP: -		/* in thenth of centigrade */ -		if (cm->last_temp_mC == INT_MIN) -			desc->temperature_out_of_range(&cm->last_temp_mC); -		val->intval = cm->last_temp_mC / 100; -		if (!desc->measure_battery_temp) -			ret = -ENODEV; -		break;  	case POWER_SUPPLY_PROP_TEMP_AMBIENT: -		/* in thenth of centigrade */ -		if (cm->last_temp_mC == INT_MIN) -			desc->temperature_out_of_range(&cm->last_temp_mC); -		val->intval = cm->last_temp_mC / 100; -		if (desc->measure_battery_temp) -			ret = -ENODEV; -		break; +		return cm_get_battery_temperature(cm, &val->intval);  	case POWER_SUPPLY_PROP_CAPACITY:  		if (!cm->fuel_gauge) {  			ret = -ENODEV; @@ -1439,9 +1485,183 @@ err:  	return ret;  } +static int cm_init_thermal_data(struct charger_manager *cm) +{ +	struct charger_desc *desc = cm->desc; +	union power_supply_propval val; +	int ret; + +	/* Verify whether fuel gauge provides battery temperature */ +	ret = cm->fuel_gauge->get_property(cm->fuel_gauge, +					POWER_SUPPLY_PROP_TEMP, &val); + +	if (!ret) { +		cm->charger_psy.properties[cm->charger_psy.num_properties] = +				POWER_SUPPLY_PROP_TEMP; +		cm->charger_psy.num_properties++; +		cm->desc->measure_battery_temp = true; +	} +#ifdef CONFIG_THERMAL +	cm->tzd_batt = cm->fuel_gauge->tzd; + +	if (ret && desc->thermal_zone) { +		cm->tzd_batt = +			thermal_zone_get_zone_by_name(desc->thermal_zone); +		if (IS_ERR(cm->tzd_batt)) +			return PTR_ERR(cm->tzd_batt); + +		/* Use external thermometer */ +		cm->charger_psy.properties[cm->charger_psy.num_properties] = +				POWER_SUPPLY_PROP_TEMP_AMBIENT; +		cm->charger_psy.num_properties++; +		cm->desc->measure_battery_temp = true; +		ret = 0; +	} +#endif +	if (cm->desc->measure_battery_temp) { +		/* NOTICE : Default allowable minimum charge temperature is 0 */ +		if (!desc->temp_max) +			desc->temp_max = CM_DEFAULT_CHARGE_TEMP_MAX; +		if (!desc->temp_diff) +			desc->temp_diff = CM_DEFAULT_RECHARGE_TEMP_DIFF; +	} + +	return ret; +} + +static struct of_device_id charger_manager_match[] = { +	{ +		.compatible = "charger-manager", +	}, +	{}, +}; + +static struct charger_desc *of_cm_parse_desc(struct device *dev) +{ +	struct charger_desc *desc; +	struct device_node *np = dev->of_node; +	u32 poll_mode = CM_POLL_DISABLE; +	u32 battery_stat = CM_NO_BATTERY; +	int num_chgs = 0; + +	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); +	if (!desc) +		return ERR_PTR(-ENOMEM); + +	of_property_read_string(np, "cm-name", &desc->psy_name); + +	of_property_read_u32(np, "cm-poll-mode", &poll_mode); +	desc->polling_mode = poll_mode; + +	of_property_read_u32(np, "cm-poll-interval", +				&desc->polling_interval_ms); + +	of_property_read_u32(np, "cm-fullbatt-vchkdrop-ms", +					&desc->fullbatt_vchkdrop_ms); +	of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt", +					&desc->fullbatt_vchkdrop_uV); +	of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV); +	of_property_read_u32(np, "cm-fullbatt-soc", &desc->fullbatt_soc); +	of_property_read_u32(np, "cm-fullbatt-capacity", +					&desc->fullbatt_full_capacity); + +	of_property_read_u32(np, "cm-battery-stat", &battery_stat); +	desc->battery_present = battery_stat; + +	/* chargers */ +	of_property_read_u32(np, "cm-num-chargers", &num_chgs); +	if (num_chgs) { +		/* Allocate empty bin at the tail of array */ +		desc->psy_charger_stat = devm_kzalloc(dev, sizeof(char *) +						* (num_chgs + 1), GFP_KERNEL); +		if (desc->psy_charger_stat) { +			int i; +			for (i = 0; i < num_chgs; i++) +				of_property_read_string_index(np, "cm-chargers", +						i, &desc->psy_charger_stat[i]); +		} else { +			return ERR_PTR(-ENOMEM); +		} +	} + +	of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge); + +	of_property_read_string(np, "cm-thermal-zone", &desc->thermal_zone); + +	of_property_read_u32(np, "cm-battery-cold", &desc->temp_min); +	if (of_get_property(np, "cm-battery-cold-in-minus", NULL)) +		desc->temp_min *= -1; +	of_property_read_u32(np, "cm-battery-hot", &desc->temp_max); +	of_property_read_u32(np, "cm-battery-temp-diff", &desc->temp_diff); + +	of_property_read_u32(np, "cm-charging-max", +				&desc->charging_max_duration_ms); +	of_property_read_u32(np, "cm-discharging-max", +				&desc->discharging_max_duration_ms); + +	/* battery charger regualtors */ +	desc->num_charger_regulators = of_get_child_count(np); +	if (desc->num_charger_regulators) { +		struct charger_regulator *chg_regs; +		struct device_node *child; + +		chg_regs = devm_kzalloc(dev, sizeof(*chg_regs) +					* desc->num_charger_regulators, +					GFP_KERNEL); +		if (!chg_regs) +			return ERR_PTR(-ENOMEM); + +		desc->charger_regulators = chg_regs; + +		for_each_child_of_node(np, child) { +			struct charger_cable *cables; +			struct device_node *_child; + +			of_property_read_string(child, "cm-regulator-name", +					&chg_regs->regulator_name); + +			/* charger cables */ +			chg_regs->num_cables = of_get_child_count(child); +			if (chg_regs->num_cables) { +				cables = devm_kzalloc(dev, sizeof(*cables) +						* chg_regs->num_cables, +						GFP_KERNEL); +				if (!cables) +					return ERR_PTR(-ENOMEM); + +				chg_regs->cables = cables; + +				for_each_child_of_node(child, _child) { +					of_property_read_string(_child, +					"cm-cable-name", &cables->name); +					of_property_read_string(_child, +					"cm-cable-extcon", +					&cables->extcon_name); +					of_property_read_u32(_child, +					"cm-cable-min", +					&cables->min_uA); +					of_property_read_u32(_child, +					"cm-cable-max", +					&cables->max_uA); +					cables++; +				} +			} +			chg_regs++; +		} +	} +	return desc; +} + +static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev) +{ +	if (pdev->dev.of_node) +		return of_cm_parse_desc(&pdev->dev); +	return (struct charger_desc *)dev_get_platdata(&pdev->dev); +} +  static int charger_manager_probe(struct platform_device *pdev)  { -	struct charger_desc *desc = dev_get_platdata(&pdev->dev); +	struct charger_desc *desc = cm_get_drv_data(pdev);  	struct charger_manager *cm;  	int ret = 0, i = 0;  	int j = 0; @@ -1470,7 +1690,6 @@ static int charger_manager_probe(struct platform_device *pdev)  	/* Basic Values. Unspecified are Null or 0 */  	cm->dev = &pdev->dev;  	cm->desc = desc; -	cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */  	/*  	 * The following two do not need to be errors. @@ -1533,11 +1752,6 @@ static int charger_manager_probe(struct platform_device *pdev)  		return -EINVAL;  	} -	if (!desc->temperature_out_of_range) { -		dev_err(&pdev->dev, "there is no temperature_out_of_range\n"); -		return -EINVAL; -	} -  	if (!desc->charging_max_duration_ms ||  			!desc->discharging_max_duration_ms) {  		dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration\n"); @@ -1583,14 +1797,10 @@ static int charger_manager_probe(struct platform_device *pdev)  		cm->charger_psy.num_properties++;  	} -	if (desc->measure_battery_temp) { -		cm->charger_psy.properties[cm->charger_psy.num_properties] = -				POWER_SUPPLY_PROP_TEMP; -		cm->charger_psy.num_properties++; -	} else { -		cm->charger_psy.properties[cm->charger_psy.num_properties] = -				POWER_SUPPLY_PROP_TEMP_AMBIENT; -		cm->charger_psy.num_properties++; +	ret = cm_init_thermal_data(cm); +	if (ret) { +		dev_err(&pdev->dev, "Failed to initialize thermal data\n"); +		cm->desc->measure_battery_temp = false;  	}  	INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk); @@ -1808,6 +2018,7 @@ static struct platform_driver charger_manager_driver = {  		.name = "charger-manager",  		.owner = THIS_MODULE,  		.pm = &charger_manager_pm, +		.of_match_table = charger_manager_match,  	},  	.probe = charger_manager_probe,  	.remove = charger_manager_remove, diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index 4e858a23568f..a0024b252197 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -28,6 +28,7 @@  struct gpio_charger {  	const struct gpio_charger_platform_data *pdata;  	unsigned int irq; +	bool wakeup_enabled;  	struct power_supply charger;  }; @@ -136,6 +137,8 @@ static int gpio_charger_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, gpio_charger); +	device_init_wakeup(&pdev->dev, 1); +  	return 0;  err_gpio_free: @@ -159,18 +162,32 @@ static int gpio_charger_remove(struct platform_device *pdev)  }  #ifdef CONFIG_PM_SLEEP +static int gpio_charger_suspend(struct device *dev) +{ +	struct gpio_charger *gpio_charger = dev_get_drvdata(dev); + +	if (device_may_wakeup(dev)) +		gpio_charger->wakeup_enabled = +			enable_irq_wake(gpio_charger->irq); + +	return 0; +} +  static int gpio_charger_resume(struct device *dev)  {  	struct platform_device *pdev = to_platform_device(dev);  	struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); +	if (gpio_charger->wakeup_enabled) +		disable_irq_wake(gpio_charger->irq);  	power_supply_changed(&gpio_charger->charger);  	return 0;  }  #endif -static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops, NULL, gpio_charger_resume); +static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops, +		gpio_charger_suspend, gpio_charger_resume);  static struct platform_driver gpio_charger_driver = {  	.probe = gpio_charger_probe, diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 1bb3a91b1acc..80edb7d8cb54 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -29,6 +29,8 @@  #include <linux/platform_device.h>  #include <linux/power_supply.h>  #include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_gpio.h>  #include <linux/usb/otg.h>  #include <linux/usb/ulpi.h> @@ -88,6 +90,8 @@ static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on)  	if (board && board->set_power)  		board->set_power(on); +	else if (board) +		gpio_set_value(board->enable_gpio, on);  }  /* @@ -400,12 +404,47 @@ static int isp1704_charger_probe(struct platform_device *pdev)  	struct isp1704_charger	*isp;  	int			ret = -ENODEV; +	struct isp1704_charger_data *pdata = dev_get_platdata(&pdev->dev); +	struct device_node *np = pdev->dev.of_node; + +	if (np) { +		int gpio = of_get_named_gpio(np, "nxp,enable-gpio", 0); + +		if (gpio < 0) +			return gpio; + +		pdata = devm_kzalloc(&pdev->dev, +			sizeof(struct isp1704_charger_data), GFP_KERNEL); +		pdata->enable_gpio = gpio; + +		dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio); + +		ret = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio, +					GPIOF_OUT_INIT_HIGH, "isp1704_reset"); +		if (ret) +			goto fail0; +	} + +	if (!pdata) { +		dev_err(&pdev->dev, "missing platform data!\n"); +		return -ENODEV; +	} + +  	isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);  	if (!isp)  		return -ENOMEM; -	isp->phy = usb_get_phy(USB_PHY_TYPE_USB2); -	if (IS_ERR_OR_NULL(isp->phy)) +	if (np) +		isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); +	else +		isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); + +	if (IS_ERR(isp->phy)) { +		ret = PTR_ERR(isp->phy); +		goto fail0; +	} +	if (!isp->phy)  		goto fail0;  	isp->dev = &pdev->dev; @@ -464,7 +503,6 @@ fail2:  	power_supply_unregister(&isp->psy);  fail1:  	isp1704_charger_set_power(isp, 0); -	usb_put_phy(isp->phy);  fail0:  	dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret); @@ -477,15 +515,23 @@ static int isp1704_charger_remove(struct platform_device *pdev)  	usb_unregister_notifier(isp->phy, &isp->nb);  	power_supply_unregister(&isp->psy); -	usb_put_phy(isp->phy);  	isp1704_charger_set_power(isp, 0);  	return 0;  } +#ifdef CONFIG_OF +static const struct of_device_id omap_isp1704_of_match[] = { +	{ .compatible = "nxp,isp1704", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, omap_isp1704_of_match); +#endif +  static struct platform_driver isp1704_charger_driver = {  	.driver = {  		.name = "isp1704_charger", +		.of_match_table = of_match_ptr(omap_isp1704_of_match),  	},  	.probe = isp1704_charger_probe,  	.remove = isp1704_charger_remove, diff --git a/drivers/power/max14577_charger.c b/drivers/power/max14577_charger.c new file mode 100644 index 000000000000..fad2a75b3604 --- /dev/null +++ b/drivers/power/max14577_charger.c @@ -0,0 +1,311 @@ +/* + * Battery charger driver for the Maxim 14577 + * + * Copyright (C) 2013 Samsung Electronics + * Krzysztof Kozlowski <k.kozlowski@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/mfd/max14577-private.h> + +struct max14577_charger { +	struct device *dev; +	struct max14577	*max14577; +	struct power_supply	charger; + +	unsigned int	charging_state; +	unsigned int	battery_state; +}; + +static int max14577_get_charger_state(struct max14577_charger *chg) +{ +	struct regmap *rmap = chg->max14577->regmap; +	int state = POWER_SUPPLY_STATUS_DISCHARGING; +	u8 reg_data; + +	/* +	 * Charging occurs only if: +	 *  - CHGCTRL2/MBCHOSTEN == 1 +	 *  - STATUS2/CGMBC == 1 +	 * +	 * TODO: +	 *  - handle FULL after Top-off timer (EOC register may be off +	 *    and the charger won't be charging although MBCHOSTEN is on) +	 *  - handle properly dead-battery charging (respect timer) +	 *  - handle timers (fast-charge and prequal) /MBCCHGERR/ +	 */ +	max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL2, ®_data); +	if ((reg_data & CHGCTRL2_MBCHOSTEN_MASK) == 0) +		goto state_set; + +	max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, ®_data); +	if (reg_data & STATUS3_CGMBC_MASK) { +		/* Charger or USB-cable is connected */ +		if (reg_data & STATUS3_EOC_MASK) +			state = POWER_SUPPLY_STATUS_FULL; +		else +			state = POWER_SUPPLY_STATUS_CHARGING; +		goto state_set; +	} + +state_set: +	chg->charging_state = state; +	return state; +} + +/* + * Supported charge types: + *  - POWER_SUPPLY_CHARGE_TYPE_NONE + *  - POWER_SUPPLY_CHARGE_TYPE_FAST + */ +static int max14577_get_charge_type(struct max14577_charger *chg) +{ +	/* +	 * TODO: CHARGE_TYPE_TRICKLE (VCHGR_RC or EOC)? +	 * As spec says: +	 * [after reaching EOC interrupt] +	 * "When the battery is fully charged, the 30-minute (typ) +	 *  top-off timer starts. The device continues to trickle +	 *  charge the battery until the top-off timer runs out." +	 */ +	if (max14577_get_charger_state(chg) == POWER_SUPPLY_STATUS_CHARGING) +		return POWER_SUPPLY_CHARGE_TYPE_FAST; +	return POWER_SUPPLY_CHARGE_TYPE_NONE; +} + +static int max14577_get_online(struct max14577_charger *chg) +{ +	struct regmap *rmap = chg->max14577->regmap; +	u8 reg_data; + +	max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data); +	reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT); +	switch (reg_data) { +	case MAX14577_CHARGER_TYPE_USB: +	case MAX14577_CHARGER_TYPE_DEDICATED_CHG: +	case MAX14577_CHARGER_TYPE_SPECIAL_500MA: +	case MAX14577_CHARGER_TYPE_SPECIAL_1A: +	case MAX14577_CHARGER_TYPE_DEAD_BATTERY: +		return 1; +	case MAX14577_CHARGER_TYPE_NONE: +	case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: +	case MAX14577_CHARGER_TYPE_RESERVED: +	default: +		return 0; +	} +} + +/* + * Supported health statuses: + *  - POWER_SUPPLY_HEALTH_DEAD + *  - POWER_SUPPLY_HEALTH_OVERVOLTAGE + *  - POWER_SUPPLY_HEALTH_GOOD + */ +static int max14577_get_battery_health(struct max14577_charger *chg) +{ +	struct regmap *rmap = chg->max14577->regmap; +	int state = POWER_SUPPLY_HEALTH_GOOD; +	u8 reg_data; + +	max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data); +	reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT); +	if (reg_data == MAX14577_CHARGER_TYPE_DEAD_BATTERY) { +		state = POWER_SUPPLY_HEALTH_DEAD; +		goto state_set; +	} + +	max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, ®_data); +	if (reg_data & STATUS3_OVP_MASK) { +		state = POWER_SUPPLY_HEALTH_OVERVOLTAGE; +		goto state_set; +	} + +state_set: +	chg->battery_state = state; +	return state; +} + +/* + * Always returns 1. + * The max14577 chip doesn't report any status of battery presence. + * Lets assume that it will always be used with some battery. + */ +static int max14577_get_present(struct max14577_charger *chg) +{ +	return 1; +} + +/* + * Sets charger registers to proper and safe default values. + * Some of these values are equal to defaults in MAX14577E + * data sheet but there are minor differences. + */ +static void max14577_charger_reg_init(struct max14577_charger *chg) +{ +	struct regmap *rmap = chg->max14577->regmap; +	u8 reg_data; + +	/* +	 * Charger-Type Manual Detection, default off (set CHGTYPMAN to 0) +	 * Charger-Detection Enable, default on (set CHGDETEN to 1) +	 * Combined mask of CHGDETEN and CHGTYPMAN will zero the CHGTYPMAN bit +	 */ +	reg_data = 0x1 << CDETCTRL1_CHGDETEN_SHIFT; +	max14577_update_reg(rmap, MAX14577_REG_CDETCTRL1, +			CDETCTRL1_CHGDETEN_MASK | CDETCTRL1_CHGTYPMAN_MASK, +			reg_data); + +	/* Battery Fast-Charge Timer, from SM-V700: 6hrs */ +	reg_data = 0x3 << CHGCTRL1_TCHW_SHIFT; +	max14577_write_reg(rmap, MAX14577_REG_CHGCTRL1, reg_data); + +	/* +	 * Wall-Adapter Rapid Charge, default on +	 * Battery-Charger, default on +	 */ +	reg_data = 0x1 << CHGCTRL2_VCHGR_RC_SHIFT; +	reg_data |= 0x1 << CHGCTRL2_MBCHOSTEN_SHIFT; +	max14577_write_reg(rmap, MAX14577_REG_CHGCTRL2, reg_data); + +	/* Battery-Charger Constant Voltage (CV) Mode, from SM-V700: 4.35V */ +	reg_data = 0xf << CHGCTRL3_MBCCVWRC_SHIFT; +	max14577_write_reg(rmap, MAX14577_REG_CHGCTRL3, reg_data); + +	/* +	 * Fast Battery-Charge Current Low, default 200-950mA +	 * Fast Battery-Charge Current High, from SM-V700: 450mA +	 */ +	reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT; +	reg_data |= 0x5 << CHGCTRL4_MBCICHWRCH_SHIFT; +	max14577_write_reg(rmap, MAX14577_REG_CHGCTRL4, reg_data); + +	/* End-of-Charge Current, from SM-V700: 50mA */ +	reg_data = 0x0 << CHGCTRL5_EOCS_SHIFT; +	max14577_write_reg(rmap, MAX14577_REG_CHGCTRL5, reg_data); + +	/* Auto Charging Stop, default off */ +	reg_data = 0x0 << CHGCTRL6_AUTOSTOP_SHIFT; +	max14577_write_reg(rmap, MAX14577_REG_CHGCTRL6, reg_data); + +	/* Overvoltage-Protection Threshold, from SM-V700: 6.5V */ +	reg_data = 0x2 << CHGCTRL7_OTPCGHCVS_SHIFT; +	max14577_write_reg(rmap, MAX14577_REG_CHGCTRL7, reg_data); +} + +/* Support property from charger */ +static enum power_supply_property max14577_charger_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_CHARGE_TYPE, +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_ONLINE, +	POWER_SUPPLY_PROP_MODEL_NAME, +	POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static const char *model_name = "MAX14577"; +static const char *manufacturer = "Maxim Integrated"; + +static int max14577_charger_get_property(struct power_supply *psy, +			    enum power_supply_property psp, +			    union power_supply_propval *val) +{ +	struct max14577_charger *chg = container_of(psy, +						  struct max14577_charger, +						  charger); +	int ret = 0; + +	switch (psp) { +	case POWER_SUPPLY_PROP_STATUS: +		val->intval = max14577_get_charger_state(chg); +		break; +	case POWER_SUPPLY_PROP_CHARGE_TYPE: +		val->intval = max14577_get_charge_type(chg); +		break; +	case POWER_SUPPLY_PROP_HEALTH: +		val->intval = max14577_get_battery_health(chg); +		break; +	case POWER_SUPPLY_PROP_PRESENT: +		val->intval = max14577_get_present(chg); +		break; +	case POWER_SUPPLY_PROP_ONLINE: +		val->intval = max14577_get_online(chg); +		break; +	case POWER_SUPPLY_PROP_MODEL_NAME: +		val->strval = model_name; +		break; +	case POWER_SUPPLY_PROP_MANUFACTURER: +		val->strval = manufacturer; +		break; +	default: +		return -EINVAL; +	} + +	return ret; +} + +static int max14577_charger_probe(struct platform_device *pdev) +{ +	struct max14577_charger *chg; +	struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent); +	int ret; + +	chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL); +	if (!chg) +		return -ENOMEM; + +	platform_set_drvdata(pdev, chg); +	chg->dev = &pdev->dev; +	chg->max14577 = max14577; + +	max14577_charger_reg_init(chg); + +	chg->charger.name = "max14577-charger", +	chg->charger.type = POWER_SUPPLY_TYPE_BATTERY, +	chg->charger.properties = max14577_charger_props, +	chg->charger.num_properties = ARRAY_SIZE(max14577_charger_props), +	chg->charger.get_property = max14577_charger_get_property, + +	ret = power_supply_register(&pdev->dev, &chg->charger); +	if (ret) { +		dev_err(&pdev->dev, "failed: power supply register\n"); +		return ret; +	} + +	return 0; +} + +static int max14577_charger_remove(struct platform_device *pdev) +{ +	struct max14577_charger *chg = platform_get_drvdata(pdev); + +	power_supply_unregister(&chg->charger); + +	return 0; +} + +static struct platform_driver max14577_charger_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "max14577-charger", +	}, +	.probe		= max14577_charger_probe, +	.remove		= max14577_charger_remove, +}; +module_platform_driver(max14577_charger_driver); + +MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>"); +MODULE_DESCRIPTION("MAXIM 14577 charger driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index e0b22f9b6fdd..66da691c41cf 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -741,9 +741,9 @@ static int max17042_probe(struct i2c_client *client,  	if (client->irq) {  		ret = request_threaded_irq(client->irq, NULL, -						max17042_thread_handler, -						IRQF_TRIGGER_FALLING, -						chip->battery.name, chip); +					max17042_thread_handler, +					IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +					chip->battery.name, chip);  		if (!ret) {  			regmap_read(chip->regmap, MAX17042_CONFIG, &val);  			val |= CONFIG_ALRT_BIT_ENBL; diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 557af943b2f5..26606641fe44 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -15,6 +15,7 @@  #include <linux/init.h>  #include <linux/slab.h>  #include <linux/device.h> +#include <linux/notifier.h>  #include <linux/err.h>  #include <linux/power_supply.h>  #include <linux/thermal.h> @@ -24,6 +25,9 @@  struct class *power_supply_class;  EXPORT_SYMBOL_GPL(power_supply_class); +ATOMIC_NOTIFIER_HEAD(power_supply_notifier); +EXPORT_SYMBOL_GPL(power_supply_notifier); +  static struct device_type power_supply_dev_type;  static bool __power_supply_is_supplied_by(struct power_supply *supplier, @@ -80,6 +84,8 @@ static void power_supply_changed_work(struct work_struct *work)  		class_for_each_device(power_supply_class, NULL, psy,  				      __power_supply_changed_work);  		power_supply_update_leds(psy); +		atomic_notifier_call_chain(&power_supply_notifier, +				PSY_EVENT_PROP_CHANGED, psy);  		kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);  		spin_lock_irqsave(&psy->changed_lock, flags);  	} @@ -335,6 +341,32 @@ struct power_supply *power_supply_get_by_name(const char *name)  }  EXPORT_SYMBOL_GPL(power_supply_get_by_name); +#ifdef CONFIG_OF +static int power_supply_match_device_node(struct device *dev, const void *data) +{ +	return dev->parent && dev->parent->of_node == data; +} + +struct power_supply *power_supply_get_by_phandle(struct device_node *np, +							const char *property) +{ +	struct device_node *power_supply_np; +	struct device *dev; + +	power_supply_np = of_parse_phandle(np, property, 0); +	if (!power_supply_np) +		return ERR_PTR(-ENODEV); + +	dev = class_find_device(power_supply_class, NULL, power_supply_np, +						power_supply_match_device_node); + +	of_node_put(power_supply_np); + +	return dev ? dev_get_drvdata(dev) : NULL; +} +EXPORT_SYMBOL_GPL(power_supply_get_by_phandle); +#endif /* CONFIG_OF */ +  int power_supply_powers(struct power_supply *psy, struct device *dev)  {  	return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers"); @@ -347,6 +379,18 @@ static void power_supply_dev_release(struct device *dev)  	kfree(dev);  } +int power_supply_reg_notifier(struct notifier_block *nb) +{ +	return atomic_notifier_chain_register(&power_supply_notifier, nb); +} +EXPORT_SYMBOL_GPL(power_supply_reg_notifier); + +void power_supply_unreg_notifier(struct notifier_block *nb) +{ +	atomic_notifier_chain_unregister(&power_supply_notifier, nb); +} +EXPORT_SYMBOL_GPL(power_supply_unreg_notifier); +  #ifdef CONFIG_THERMAL  static int power_supply_read_temp(struct thermal_zone_device *tzd,  		unsigned long *temp) diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 9b3ea535b472..6d452a78b19c 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -6,6 +6,12 @@ menuconfig POWER_RESET  	  Say Y here to enable board reset and power off +config POWER_RESET_AS3722 +	bool "ams AS3722 power-off driver" +	depends on MFD_AS3722 && POWER_RESET +	help +	  This driver supports turning off board via a ams AS3722 power-off. +  config POWER_RESET_GPIO  	bool "GPIO power-off driver"  	depends on OF_GPIO && POWER_RESET diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 3e6ed88725ac..a5b4a77d1a41 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o  obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o  obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o  obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o diff --git a/drivers/power/reset/as3722-poweroff.c b/drivers/power/reset/as3722-poweroff.c new file mode 100644 index 000000000000..684971199bd3 --- /dev/null +++ b/drivers/power/reset/as3722-poweroff.c @@ -0,0 +1,96 @@ +/* + * Power off driver for ams AS3722 device. + * + * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved. + * + * Author: Laxman Dewangan <ldewangan@nvidia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + */ + +#include <linux/mfd/as3722.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +struct as3722_poweroff { +	struct device *dev; +	struct as3722 *as3722; +}; + +static struct as3722_poweroff *as3722_pm_poweroff; + +static void as3722_pm_power_off(void) +{ +	int ret; + +	if (!as3722_pm_poweroff) { +		pr_err("AS3722 poweroff is not initialised\n"); +		return; +	} + +	ret = as3722_update_bits(as3722_pm_poweroff->as3722, +		AS3722_RESET_CONTROL_REG, AS3722_POWER_OFF, AS3722_POWER_OFF); +	if (ret < 0) +		dev_err(as3722_pm_poweroff->dev, +			"RESET_CONTROL_REG update failed, %d\n", ret); +} + +static int as3722_poweroff_probe(struct platform_device *pdev) +{ +	struct as3722_poweroff *as3722_poweroff; +	struct device_node *np = pdev->dev.parent->of_node; + +	if (!np) +		return -EINVAL; + +	if (!of_property_read_bool(np, "ams,system-power-controller")) +		return 0; + +	as3722_poweroff = devm_kzalloc(&pdev->dev, sizeof(*as3722_poweroff), +				GFP_KERNEL); +	if (!as3722_poweroff) +		return -ENOMEM; + +	as3722_poweroff->as3722 = dev_get_drvdata(pdev->dev.parent); +	as3722_poweroff->dev = &pdev->dev; +	as3722_pm_poweroff = as3722_poweroff; +	if (!pm_power_off) +		pm_power_off = as3722_pm_power_off; + +	return 0; +} + +static int as3722_poweroff_remove(struct platform_device *pdev) +{ +	if (pm_power_off == as3722_pm_power_off) +		pm_power_off = NULL; +	as3722_pm_poweroff = NULL; + +	return 0; +} + +static struct platform_driver as3722_poweroff_driver = { +	.driver = { +		.name = "as3722-power-off", +		.owner = THIS_MODULE, +	}, +	.probe = as3722_poweroff_probe, +	.remove = as3722_poweroff_remove, +}; + +module_platform_driver(as3722_poweroff_driver); + +MODULE_DESCRIPTION("Power off driver for ams AS3722 PMIC Device"); +MODULE_ALIAS("platform:as3722-power-off"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/power/bq2415x_charger.h b/include/linux/power/bq2415x_charger.h index 8dcc0f46fc0a..50762af8b834 100644 --- a/include/linux/power/bq2415x_charger.h +++ b/include/linux/power/bq2415x_charger.h @@ -1,7 +1,7 @@  /*   * bq2415x charger driver   * - * Copyright (C) 2011-2012  Pali Rohár <pali.rohar@gmail.com> + * Copyright (C) 2011-2013  Pali Rohár <pali.rohar@gmail.com>   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License as published by @@ -31,46 +31,9 @@   * termination current. It it is less or equal to zero, configuring charge   * and termination current will not be possible.   * - * Function set_mode_hook is needed for automode (setting correct current - * limit when charger is connected/disconnected or setting boost mode). - * When is NULL, automode function is disabled. When is not NULL, it must - * have this prototype: - * - *    int (*set_mode_hook)( - *      void (*hook)(enum bq2415x_mode mode, void *data), - *      void *data) - * - * hook is hook function (see below) and data is pointer to driver private - * data - * - * bq2415x driver will call it as: - * - *    platform_data->set_mode_hook(bq2415x_hook_function, bq2415x_device); - * - * Board/platform function set_mode_hook return non zero value when hook - * function was successful registered. Platform code should call that hook - * function (which get from pointer, with data) every time when charger - * was connected/disconnected or require to enable boost mode. bq2415x - * driver then will set correct current limit, enable/disable charger or - * boost mode. - * - * Hook function has this prototype: - * - *    void hook(enum bq2415x_mode mode, void *data); - * - * mode is bq2415x mode (charger or boost) - * data is pointer to driver private data (which get from - * set_charger_type_hook) - * - * When bq driver is being unloaded, it call function: - * - *    platform_data->set_mode_hook(NULL, NULL); - * - * (hook function and driver private data are NULL) - * - * After that board/platform code must not call driver hook function! It - * is possible that pointer to hook function will not be valid and calling - * will cause undefined result. + * For automode support is needed to provide name of power supply device + * in value notify_device. Device driver must immediately report property + * POWER_SUPPLY_PROP_CURRENT_MAX when current changed.   */  /* Supported modes with maximal current limit */ @@ -89,8 +52,7 @@ struct bq2415x_platform_data {  	int charge_current;		/* mA */  	int termination_current;	/* mA */  	int resistor_sense;		/* m ohm */ -	int (*set_mode_hook)(void (*hook)(enum bq2415x_mode mode, void *data), -			     void *data); +	const char *notify_device;	/* name */  };  #endif diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h index 0e86840eb603..07e7945a1ff2 100644 --- a/include/linux/power/charger-manager.h +++ b/include/linux/power/charger-manager.h @@ -37,6 +37,8 @@ enum cm_event_types {  	CM_EVENT_BATT_FULL,  	CM_EVENT_BATT_IN,  	CM_EVENT_BATT_OUT, +	CM_EVENT_BATT_OVERHEAT, +	CM_EVENT_BATT_COLD,  	CM_EVENT_EXT_PWR_IN_OUT,  	CM_EVENT_CHG_START_STOP,  	CM_EVENT_OTHERS, @@ -173,11 +175,10 @@ struct charger_regulator {   * @num_charger_regulator: the number of entries in charger_regulators   * @charger_regulators: array of charger regulators   * @psy_fuel_gauge: the name of power-supply for fuel gauge - * @temperature_out_of_range: - *	Determine whether the status is overheat or cold or normal. - *	return_value > 0: overheat - *	return_value == 0: normal - *	return_value < 0: cold + * @thermal_zone : the name of thermal zone for battery + * @temp_min : Minimum battery temperature for charging. + * @temp_max : Maximum battery temperature for charging. + * @temp_diff : Temperature diffential to restart charging.   * @measure_battery_temp:   *	true: measure battery temperature   *	false: measure ambient temperature @@ -190,7 +191,7 @@ struct charger_regulator {   *	max_duration_ms', cm start charging.   */  struct charger_desc { -	char *psy_name; +	const char *psy_name;  	enum polling_modes polling_mode;  	unsigned int polling_interval_ms; @@ -203,18 +204,23 @@ struct charger_desc {  	enum data_source battery_present; -	char **psy_charger_stat; +	const char **psy_charger_stat;  	int num_charger_regulators;  	struct charger_regulator *charger_regulators; -	char *psy_fuel_gauge; +	const char *psy_fuel_gauge; + +	const char *thermal_zone; + +	int temp_min; +	int temp_max; +	int temp_diff; -	int (*temperature_out_of_range)(int *mC);  	bool measure_battery_temp; -	u64 charging_max_duration_ms; -	u64 discharging_max_duration_ms; +	u32 charging_max_duration_ms; +	u32 discharging_max_duration_ms;  };  #define PSY_NAME_MAX	30 @@ -226,13 +232,13 @@ struct charger_desc {   * @desc: instance of charger_desc   * @fuel_gauge: power_supply for fuel gauge   * @charger_stat: array of power_supply for chargers + * @tzd_batt : thermal zone device for battery   * @charger_enabled: the state of charger   * @fullbatt_vchk_jiffies_at:   *	jiffies at the time full battery check will occur.   * @fullbatt_vchk_work: work queue for full battery check   * @emergency_stop:   *	When setting true, stop charging - * @last_temp_mC: the measured temperature in milli-Celsius   * @psy_name_buf: the name of power-supply-class for charger manager   * @charger_psy: power_supply for charger manager   * @status_save_ext_pwr_inserted: @@ -250,13 +256,15 @@ struct charger_manager {  	struct power_supply *fuel_gauge;  	struct power_supply **charger_stat; +#ifdef CONFIG_THERMAL +	struct thermal_zone_device *tzd_batt; +#endif  	bool charger_enabled;  	unsigned long fullbatt_vchk_jiffies_at;  	struct delayed_work fullbatt_vchk_work;  	int emergency_stop; -	int last_temp_mC;  	char psy_name_buf[PSY_NAME_MAX + 1];  	struct power_supply charger_psy; diff --git a/include/linux/power/isp1704_charger.h b/include/linux/power/isp1704_charger.h index 68096a6aa2d7..0105d9e7af85 100644 --- a/include/linux/power/isp1704_charger.h +++ b/include/linux/power/isp1704_charger.h @@ -24,6 +24,7 @@  struct isp1704_charger_data {  	void		(*set_power)(bool on); +	int		enable_gpio;  };  #endif diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 5c2600630dc9..c9dc4e09854c 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -16,6 +16,7 @@  #include <linux/workqueue.h>  #include <linux/leds.h>  #include <linux/spinlock.h> +#include <linux/notifier.h>  struct device; @@ -158,6 +159,10 @@ enum power_supply_type {  	POWER_SUPPLY_TYPE_USB_ACA,	/* Accessory Charger Adapters */  }; +enum power_supply_notifier_events { +	PSY_EVENT_PROP_CHANGED, +}; +  union power_supply_propval {  	int intval;  	const char *strval; @@ -235,7 +240,18 @@ struct power_supply_info {  	int use_for_apm;  }; +extern struct atomic_notifier_head power_supply_notifier; +extern int power_supply_reg_notifier(struct notifier_block *nb); +extern void power_supply_unreg_notifier(struct notifier_block *nb);  extern struct power_supply *power_supply_get_by_name(const char *name); +#ifdef CONFIG_OF +extern struct power_supply *power_supply_get_by_phandle(struct device_node *np, +							const char *property); +#else /* !CONFIG_OF */ +static inline struct power_supply * +power_supply_get_by_phandle(struct device_node *np, const char *property) +{ return NULL; } +#endif /* CONFIG_OF */  extern void power_supply_changed(struct power_supply *psy);  extern int power_supply_am_i_supplied(struct power_supply *psy);  extern int power_supply_set_battery_charged(struct power_supply *psy);  | 
