Commit 03d11a0e authored by Linus Torvalds's avatar Linus Torvalds

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
parents ac266635 57318935
...@@ -112,6 +112,15 @@ Following are properties of regulator subnode. ...@@ -112,6 +112,15 @@ Following are properties of regulator subnode.
ams,enable-tracking: Enable tracking with SD1, only supported ams,enable-tracking: Enable tracking with SD1, only supported
by LDO3. 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: Example:
-------- --------
#include <dt-bindings/mfd/as3722.h> #include <dt-bindings/mfd/as3722.h>
...@@ -120,6 +129,8 @@ ams3722 { ...@@ -120,6 +129,8 @@ ams3722 {
compatible = "ams,as3722"; compatible = "ams,as3722";
reg = <0x48>; reg = <0x48>;
ams,system-power-controller;
interrupt-parent = <&intc>; interrupt-parent = <&intc>;
interrupt-controller; interrupt-controller;
#interrupt-cells = <2>; #interrupt-cells = <2>;
......
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>;
};
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>;
};
};
};
...@@ -6712,7 +6712,7 @@ F: include/linux/timer* ...@@ -6712,7 +6712,7 @@ F: include/linux/timer*
F: kernel/*timer* F: kernel/*timer*
POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS 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> M: David Woodhouse <dwmw2@infradead.org>
T: git git://git.infradead.org/battery-2.6.git T: git git://git.infradead.org/battery-2.6.git
S: Maintained S: Maintained
......
...@@ -317,6 +317,13 @@ config CHARGER_MANAGER ...@@ -317,6 +317,13 @@ config CHARGER_MANAGER
runtime and in suspend-to-RAM by waking up the system periodically runtime and in suspend-to-RAM by waking up the system periodically
with help of suspend_again support. 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 config CHARGER_MAX8997
tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver" tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
depends on MFD_MAX8997 && REGULATOR_MAX8997 depends on MFD_MAX8997 && REGULATOR_MAX8997
......
...@@ -48,6 +48,7 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o ...@@ -48,6 +48,7 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o
obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.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_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
......
/* /*
* bq2415x charger driver * 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -170,6 +170,8 @@ struct bq2415x_device { ...@@ -170,6 +170,8 @@ struct bq2415x_device {
struct bq2415x_platform_data init_data; struct bq2415x_platform_data init_data;
struct power_supply charger; struct power_supply charger;
struct delayed_work work; 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 reported_mode;/* mode reported by hook function */
enum bq2415x_mode mode; /* current configured mode */ enum bq2415x_mode mode; /* current configured mode */
enum bq2415x_chip chip; enum bq2415x_chip chip;
...@@ -795,24 +797,53 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode) ...@@ -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 int bq2415x_notifier_call(struct notifier_block *nb,
static void bq2415x_hook_function(enum bq2415x_mode mode, void *data) 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) if (val != PSY_EVENT_PROP_CHANGED)
return; 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; bq->reported_mode = mode;
/* if automode is not enabled do not tell about reported_mode */ /* if automode is not enabled do not tell about reported_mode */
if (bq->automode < 1) if (bq->automode < 1)
return; return NOTIFY_OK;
sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode"); sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode");
bq2415x_set_mode(bq, bq->reported_mode); bq2415x_set_mode(bq, bq->reported_mode);
return NOTIFY_OK;
} }
/**** timer functions ****/ /**** timer functions ****/
...@@ -1512,9 +1543,11 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1512,9 +1543,11 @@ static int bq2415x_probe(struct i2c_client *client,
int num; int num;
char *name; char *name;
struct bq2415x_device *bq; 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) { if (!np && !pdata) {
dev_err(&client->dev, "platform data not set\n"); dev_err(&client->dev, "platform data missing\n");
return -ENODEV; return -ENODEV;
} }
...@@ -1539,6 +1572,17 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1539,6 +1572,17 @@ static int bq2415x_probe(struct i2c_client *client,
goto error_2; 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); i2c_set_clientdata(client, bq);
bq->id = num; bq->id = num;
...@@ -1550,8 +1594,34 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1550,8 +1594,34 @@ static int bq2415x_probe(struct i2c_client *client,
bq->autotimer = 0; bq->autotimer = 0;
bq->automode = 0; bq->automode = 0;
memcpy(&bq->init_data, client->dev.platform_data, if (np) {
sizeof(bq->init_data)); 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); bq2415x_reset_chip(bq);
...@@ -1573,16 +1643,20 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1573,16 +1643,20 @@ static int bq2415x_probe(struct i2c_client *client,
goto error_4; goto error_4;
} }
if (bq->init_data.set_mode_hook) { if (bq->notify_psy) {
if (bq->init_data.set_mode_hook( bq->nb.notifier_call = bq2415x_notifier_call;
bq2415x_hook_function, bq)) { ret = power_supply_reg_notifier(&bq->nb);
bq->automode = 1; 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); bq2415x_set_mode(bq, bq->reported_mode);
bq->automode = 1;
dev_info(bq->dev, "automode enabled\n"); dev_info(bq->dev, "automode enabled\n");
} else {
bq->automode = -1;
dev_info(bq->dev, "automode failed\n");
}
} else { } else {
bq->automode = -1; bq->automode = -1;
dev_info(bq->dev, "automode not supported\n"); dev_info(bq->dev, "automode not supported\n");
...@@ -1594,6 +1668,7 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1594,6 +1668,7 @@ static int bq2415x_probe(struct i2c_client *client,
dev_info(bq->dev, "driver registered\n"); dev_info(bq->dev, "driver registered\n");
return 0; return 0;
error_5:
error_4: error_4:
bq2415x_sysfs_exit(bq); bq2415x_sysfs_exit(bq);
error_3: error_3:
...@@ -1614,8 +1689,8 @@ static int bq2415x_remove(struct i2c_client *client) ...@@ -1614,8 +1689,8 @@ static int bq2415x_remove(struct i2c_client *client)
{ {
struct bq2415x_device *bq = i2c_get_clientdata(client); struct bq2415x_device *bq = i2c_get_clientdata(client);
if (bq->init_data.set_mode_hook) if (bq->notify_psy)
bq->init_data.set_mode_hook(NULL, NULL); power_supply_unreg_notifier(&bq->nb);
bq2415x_sysfs_exit(bq); bq2415x_sysfs_exit(bq);
bq2415x_power_supply_exit(bq); bq2415x_power_supply_exit(bq);
......
...@@ -25,12 +25,23 @@ ...@@ -25,12 +25,23 @@
#include <linux/power/charger-manager.h> #include <linux/power/charger-manager.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/sysfs.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[] = { static const char * const default_event_names[] = {
[CM_EVENT_UNKNOWN] = "Unknown", [CM_EVENT_UNKNOWN] = "Unknown",
[CM_EVENT_BATT_FULL] = "Battery Full", [CM_EVENT_BATT_FULL] = "Battery Full",
[CM_EVENT_BATT_IN] = "Battery Inserted", [CM_EVENT_BATT_IN] = "Battery Inserted",
[CM_EVENT_BATT_OUT] = "Battery Pulled Out", [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_EXT_PWR_IN_OUT] = "External Power Attach/Detach",
[CM_EVENT_CHG_START_STOP] = "Charging Start/Stop", [CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",
[CM_EVENT_OTHERS] = "Other battery events" [CM_EVENT_OTHERS] = "Other battery events"
...@@ -518,7 +529,7 @@ static int check_charging_duration(struct charger_manager *cm) ...@@ -518,7 +529,7 @@ static int check_charging_duration(struct charger_manager *cm)
duration = curr - cm->charging_start_time; duration = curr - cm->charging_start_time;
if (duration > desc->charging_max_duration_ms) { 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); desc->charging_max_duration_ms);
uevent_notify(cm, "Discharging"); uevent_notify(cm, "Discharging");
try_charger_enable(cm, false); try_charger_enable(cm, false);
...@@ -529,7 +540,7 @@ static int check_charging_duration(struct charger_manager *cm) ...@@ -529,7 +540,7 @@ static int check_charging_duration(struct charger_manager *cm)
if (duration > desc->charging_max_duration_ms && if (duration > desc->charging_max_duration_ms &&
is_ext_pwr_online(cm)) { 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); desc->discharging_max_duration_ms);
uevent_notify(cm, "Recharging"); uevent_notify(cm, "Recharging");
try_charger_enable(cm, true); try_charger_enable(cm, true);
...@@ -540,6 +551,60 @@ static int check_charging_duration(struct charger_manager *cm) ...@@ -540,6 +551,60 @@ static int check_charging_duration(struct charger_manager *cm)
return ret; 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_monitor - Monitor the temperature and return true for exceptions.
* @cm: the Charger Manager representing the battery. * @cm: the Charger Manager representing the battery.
...@@ -549,28 +614,22 @@ static int check_charging_duration(struct charger_manager *cm) ...@@ -549,28 +614,22 @@ static int check_charging_duration(struct charger_manager *cm)
*/ */
static bool _cm_monitor(struct charger_manager *cm) static bool _cm_monitor(struct charger_manager *cm)
{ {
struct charger_desc *desc = cm->desc; int temp_alrt;
int temp = desc->temperature_out_of_range(&cm->last_temp_mC);
dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n", temp_alrt = cm_check_thermal_status(cm);
cm->last_temp_mC / 1000, cm->last_temp_mC % 1000);
/* It has been stopped already */ /* It has been stopped already */
if (temp && cm->emergency_stop) if (temp_alrt && cm->emergency_stop)
return false; return false;
/* /*
* Check temperature whether overheat or cold. * Check temperature whether overheat or cold.
* If temperature is out of range normal state, stop charging. * If temperature is out of range normal state, stop charging.
*/ */
if (temp) { if (temp_alrt) {
cm->emergency_stop = temp; cm->emergency_stop = temp_alrt;
if (!try_charger_enable(cm, false)) { if (!try_charger_enable(cm, false))
if (temp > 0) uevent_notify(cm, default_event_names[temp_alrt]);
uevent_notify(cm, "OVERHEAT");
else
uevent_notify(cm, "COLD");
}
/* /*
* Check whole charging duration and discharing duration * Check whole charging duration and discharing duration
...@@ -802,21 +861,8 @@ static int charger_get_property(struct power_supply *psy, ...@@ -802,21 +861,8 @@ static int charger_get_property(struct power_supply *psy,
POWER_SUPPLY_PROP_CURRENT_NOW, val); POWER_SUPPLY_PROP_CURRENT_NOW, val);
break; break;
case POWER_SUPPLY_PROP_TEMP: 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: case POWER_SUPPLY_PROP_TEMP_AMBIENT:
/* in thenth of centigrade */ return cm_get_battery_temperature(cm, &val->intval);
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_CAPACITY: case POWER_SUPPLY_PROP_CAPACITY:
if (!cm->fuel_gauge) { if (!cm->fuel_gauge) {
ret = -ENODEV; ret = -ENODEV;
...@@ -1439,9 +1485,183 @@ static int charger_manager_register_sysfs(struct charger_manager *cm) ...@@ -1439,9 +1485,183 @@ static int charger_manager_register_sysfs(struct charger_manager *cm)
return ret; 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) 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; struct charger_manager *cm;
int ret = 0, i = 0; int ret = 0, i = 0;
int j = 0; int j = 0;
...@@ -1470,7 +1690,6 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1470,7 +1690,6 @@ static int charger_manager_probe(struct platform_device *pdev)
/* Basic Values. Unspecified are Null or 0 */ /* Basic Values. Unspecified are Null or 0 */
cm->dev = &pdev->dev; cm->dev = &pdev->dev;
cm->desc = desc; cm->desc = desc;
cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
/* /*
* The following two do not need to be errors. * The following two do not need to be errors.
...@@ -1533,11 +1752,6 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1533,11 +1752,6 @@ static int charger_manager_probe(struct platform_device *pdev)
return -EINVAL; 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 || if (!desc->charging_max_duration_ms ||
!desc->discharging_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"); 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) ...@@ -1583,14 +1797,10 @@ static int charger_manager_probe(struct platform_device *pdev)
cm->charger_psy.num_properties++; cm->charger_psy.num_properties++;
} }
if (desc->measure_battery_temp) { ret = cm_init_thermal_data(cm);
cm->charger_psy.properties[cm->charger_psy.num_properties] = if (ret) {
POWER_SUPPLY_PROP_TEMP; dev_err(&pdev->dev, "Failed to initialize thermal data\n");
cm->charger_psy.num_properties++; cm->desc->measure_battery_temp = false;
} else {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_TEMP_AMBIENT;
cm->charger_psy.num_properties++;
} }
INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk); INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
...@@ -1808,6 +2018,7 @@ static struct platform_driver charger_manager_driver = { ...@@ -1808,6 +2018,7 @@ static struct platform_driver charger_manager_driver = {
.name = "charger-manager", .name = "charger-manager",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &charger_manager_pm, .pm = &charger_manager_pm,
.of_match_table = charger_manager_match,
}, },
.probe = charger_manager_probe, .probe = charger_manager_probe,
.remove = charger_manager_remove, .remove = charger_manager_remove,
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
struct gpio_charger { struct gpio_charger {
const struct gpio_charger_platform_data *pdata; const struct gpio_charger_platform_data *pdata;
unsigned int irq; unsigned int irq;
bool wakeup_enabled;
struct power_supply charger; struct power_supply charger;
}; };
...@@ -136,6 +137,8 @@ static int gpio_charger_probe(struct platform_device *pdev) ...@@ -136,6 +137,8 @@ static int gpio_charger_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, gpio_charger); platform_set_drvdata(pdev, gpio_charger);
device_init_wakeup(&pdev->dev, 1);
return 0; return 0;
err_gpio_free: err_gpio_free:
...@@ -159,18 +162,32 @@ static int gpio_charger_remove(struct platform_device *pdev) ...@@ -159,18 +162,32 @@ static int gpio_charger_remove(struct platform_device *pdev)
} }
#ifdef CONFIG_PM_SLEEP #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) static int gpio_charger_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); 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); power_supply_changed(&gpio_charger->charger);
return 0; return 0;
} }
#endif #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 = { static struct platform_driver gpio_charger_driver = {
.probe = gpio_charger_probe, .probe = gpio_charger_probe,
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/usb/otg.h> #include <linux/usb/otg.h>
#include <linux/usb/ulpi.h> #include <linux/usb/ulpi.h>
...@@ -88,6 +90,8 @@ static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on) ...@@ -88,6 +90,8 @@ static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on)
if (board && board->set_power) if (board && board->set_power)
board->set_power(on); 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) ...@@ -400,12 +404,47 @@ static int isp1704_charger_probe(struct platform_device *pdev)
struct isp1704_charger *isp; struct isp1704_charger *isp;
int ret = -ENODEV; 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); isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
if (!isp) if (!isp)
return -ENOMEM; return -ENOMEM;
isp->phy = usb_get_phy(USB_PHY_TYPE_USB2); if (np)
if (IS_ERR_OR_NULL(isp->phy)) 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; goto fail0;
isp->dev = &pdev->dev; isp->dev = &pdev->dev;
...@@ -464,7 +503,6 @@ static int isp1704_charger_probe(struct platform_device *pdev) ...@@ -464,7 +503,6 @@ static int isp1704_charger_probe(struct platform_device *pdev)
power_supply_unregister(&isp->psy); power_supply_unregister(&isp->psy);
fail1: fail1:
isp1704_charger_set_power(isp, 0); isp1704_charger_set_power(isp, 0);
usb_put_phy(isp->phy);
fail0: fail0:
dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret); 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) ...@@ -477,15 +515,23 @@ static int isp1704_charger_remove(struct platform_device *pdev)
usb_unregister_notifier(isp->phy, &isp->nb); usb_unregister_notifier(isp->phy, &isp->nb);
power_supply_unregister(&isp->psy); power_supply_unregister(&isp->psy);
usb_put_phy(isp->phy);
isp1704_charger_set_power(isp, 0); isp1704_charger_set_power(isp, 0);
return 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 = { static struct platform_driver isp1704_charger_driver = {
.driver = { .driver = {
.name = "isp1704_charger", .name = "isp1704_charger",
.of_match_table = of_match_ptr(omap_isp1704_of_match),
}, },
.probe = isp1704_charger_probe, .probe = isp1704_charger_probe,
.remove = isp1704_charger_remove, .remove = isp1704_charger_remove,
......
/*
* 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, &reg_data);
if ((reg_data & CHGCTRL2_MBCHOSTEN_MASK) == 0)
goto state_set;
max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, &reg_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, &reg_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, &reg_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, &reg_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");
...@@ -742,7 +742,7 @@ static int max17042_probe(struct i2c_client *client, ...@@ -742,7 +742,7 @@ static int max17042_probe(struct i2c_client *client,
if (client->irq) { if (client->irq) {
ret = request_threaded_irq(client->irq, NULL, ret = request_threaded_irq(client->irq, NULL,
max17042_thread_handler, max17042_thread_handler,
IRQF_TRIGGER_FALLING, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
chip->battery.name, chip); chip->battery.name, chip);
if (!ret) { if (!ret) {
regmap_read(chip->regmap, MAX17042_CONFIG, &val); regmap_read(chip->regmap, MAX17042_CONFIG, &val);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/notifier.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/thermal.h> #include <linux/thermal.h>
...@@ -24,6 +25,9 @@ ...@@ -24,6 +25,9 @@
struct class *power_supply_class; struct class *power_supply_class;
EXPORT_SYMBOL_GPL(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 struct device_type power_supply_dev_type;
static bool __power_supply_is_supplied_by(struct power_supply *supplier, 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) ...@@ -80,6 +84,8 @@ static void power_supply_changed_work(struct work_struct *work)
class_for_each_device(power_supply_class, NULL, psy, class_for_each_device(power_supply_class, NULL, psy,
__power_supply_changed_work); __power_supply_changed_work);
power_supply_update_leds(psy); power_supply_update_leds(psy);
atomic_notifier_call_chain(&power_supply_notifier,
PSY_EVENT_PROP_CHANGED, psy);
kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
spin_lock_irqsave(&psy->changed_lock, flags); spin_lock_irqsave(&psy->changed_lock, flags);
} }
...@@ -335,6 +341,32 @@ struct power_supply *power_supply_get_by_name(const char *name) ...@@ -335,6 +341,32 @@ struct power_supply *power_supply_get_by_name(const char *name)
} }
EXPORT_SYMBOL_GPL(power_supply_get_by_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) int power_supply_powers(struct power_supply *psy, struct device *dev)
{ {
return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers"); return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers");
...@@ -347,6 +379,18 @@ static void power_supply_dev_release(struct device *dev) ...@@ -347,6 +379,18 @@ static void power_supply_dev_release(struct device *dev)
kfree(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 #ifdef CONFIG_THERMAL
static int power_supply_read_temp(struct thermal_zone_device *tzd, static int power_supply_read_temp(struct thermal_zone_device *tzd,
unsigned long *temp) unsigned long *temp)
......
...@@ -6,6 +6,12 @@ menuconfig POWER_RESET ...@@ -6,6 +6,12 @@ menuconfig POWER_RESET
Say Y here to enable board reset and power off 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 config POWER_RESET_GPIO
bool "GPIO power-off driver" bool "GPIO power-off driver"
depends on OF_GPIO && POWER_RESET depends on OF_GPIO && POWER_RESET
......
obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
......
/*
* 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");
/* /*
* bq2415x charger driver * 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -31,46 +31,9 @@ ...@@ -31,46 +31,9 @@
* termination current. It it is less or equal to zero, configuring charge * termination current. It it is less or equal to zero, configuring charge
* and termination current will not be possible. * and termination current will not be possible.
* *
* Function set_mode_hook is needed for automode (setting correct current * For automode support is needed to provide name of power supply device
* limit when charger is connected/disconnected or setting boost mode). * in value notify_device. Device driver must immediately report property
* When is NULL, automode function is disabled. When is not NULL, it must * POWER_SUPPLY_PROP_CURRENT_MAX when current changed.
* 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.
*/ */
/* Supported modes with maximal current limit */ /* Supported modes with maximal current limit */
...@@ -89,8 +52,7 @@ struct bq2415x_platform_data { ...@@ -89,8 +52,7 @@ struct bq2415x_platform_data {
int charge_current; /* mA */ int charge_current; /* mA */
int termination_current; /* mA */ int termination_current; /* mA */
int resistor_sense; /* m ohm */ int resistor_sense; /* m ohm */
int (*set_mode_hook)(void (*hook)(enum bq2415x_mode mode, void *data), const char *notify_device; /* name */
void *data);
}; };
#endif #endif
...@@ -37,6 +37,8 @@ enum cm_event_types { ...@@ -37,6 +37,8 @@ enum cm_event_types {
CM_EVENT_BATT_FULL, CM_EVENT_BATT_FULL,
CM_EVENT_BATT_IN, CM_EVENT_BATT_IN,
CM_EVENT_BATT_OUT, CM_EVENT_BATT_OUT,
CM_EVENT_BATT_OVERHEAT,
CM_EVENT_BATT_COLD,
CM_EVENT_EXT_PWR_IN_OUT, CM_EVENT_EXT_PWR_IN_OUT,
CM_EVENT_CHG_START_STOP, CM_EVENT_CHG_START_STOP,
CM_EVENT_OTHERS, CM_EVENT_OTHERS,
...@@ -173,11 +175,10 @@ struct charger_regulator { ...@@ -173,11 +175,10 @@ struct charger_regulator {
* @num_charger_regulator: the number of entries in charger_regulators * @num_charger_regulator: the number of entries in charger_regulators
* @charger_regulators: array of charger regulators * @charger_regulators: array of charger regulators
* @psy_fuel_gauge: the name of power-supply for fuel gauge * @psy_fuel_gauge: the name of power-supply for fuel gauge
* @temperature_out_of_range: * @thermal_zone : the name of thermal zone for battery
* Determine whether the status is overheat or cold or normal. * @temp_min : Minimum battery temperature for charging.
* return_value > 0: overheat * @temp_max : Maximum battery temperature for charging.
* return_value == 0: normal * @temp_diff : Temperature diffential to restart charging.
* return_value < 0: cold
* @measure_battery_temp: * @measure_battery_temp:
* true: measure battery temperature * true: measure battery temperature
* false: measure ambient temperature * false: measure ambient temperature
...@@ -190,7 +191,7 @@ struct charger_regulator { ...@@ -190,7 +191,7 @@ struct charger_regulator {
* max_duration_ms', cm start charging. * max_duration_ms', cm start charging.
*/ */
struct charger_desc { struct charger_desc {
char *psy_name; const char *psy_name;
enum polling_modes polling_mode; enum polling_modes polling_mode;
unsigned int polling_interval_ms; unsigned int polling_interval_ms;
...@@ -203,18 +204,23 @@ struct charger_desc { ...@@ -203,18 +204,23 @@ struct charger_desc {
enum data_source battery_present; enum data_source battery_present;
char **psy_charger_stat; const char **psy_charger_stat;
int num_charger_regulators; int num_charger_regulators;
struct charger_regulator *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; bool measure_battery_temp;
u64 charging_max_duration_ms; u32 charging_max_duration_ms;
u64 discharging_max_duration_ms; u32 discharging_max_duration_ms;
}; };
#define PSY_NAME_MAX 30 #define PSY_NAME_MAX 30
...@@ -226,13 +232,13 @@ struct charger_desc { ...@@ -226,13 +232,13 @@ struct charger_desc {
* @desc: instance of charger_desc * @desc: instance of charger_desc
* @fuel_gauge: power_supply for fuel gauge * @fuel_gauge: power_supply for fuel gauge
* @charger_stat: array of power_supply for chargers * @charger_stat: array of power_supply for chargers
* @tzd_batt : thermal zone device for battery
* @charger_enabled: the state of charger * @charger_enabled: the state of charger
* @fullbatt_vchk_jiffies_at: * @fullbatt_vchk_jiffies_at:
* jiffies at the time full battery check will occur. * jiffies at the time full battery check will occur.
* @fullbatt_vchk_work: work queue for full battery check * @fullbatt_vchk_work: work queue for full battery check
* @emergency_stop: * @emergency_stop:
* When setting true, stop charging * 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 * @psy_name_buf: the name of power-supply-class for charger manager
* @charger_psy: power_supply for charger manager * @charger_psy: power_supply for charger manager
* @status_save_ext_pwr_inserted: * @status_save_ext_pwr_inserted:
...@@ -250,13 +256,15 @@ struct charger_manager { ...@@ -250,13 +256,15 @@ struct charger_manager {
struct power_supply *fuel_gauge; struct power_supply *fuel_gauge;
struct power_supply **charger_stat; struct power_supply **charger_stat;
#ifdef CONFIG_THERMAL
struct thermal_zone_device *tzd_batt;
#endif
bool charger_enabled; bool charger_enabled;
unsigned long fullbatt_vchk_jiffies_at; unsigned long fullbatt_vchk_jiffies_at;
struct delayed_work fullbatt_vchk_work; struct delayed_work fullbatt_vchk_work;
int emergency_stop; int emergency_stop;
int last_temp_mC;
char psy_name_buf[PSY_NAME_MAX + 1]; char psy_name_buf[PSY_NAME_MAX + 1];
struct power_supply charger_psy; struct power_supply charger_psy;
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
struct isp1704_charger_data { struct isp1704_charger_data {
void (*set_power)(bool on); void (*set_power)(bool on);
int enable_gpio;
}; };
#endif #endif
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/notifier.h>
struct device; struct device;
...@@ -158,6 +159,10 @@ enum power_supply_type { ...@@ -158,6 +159,10 @@ enum power_supply_type {
POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */ POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */
}; };
enum power_supply_notifier_events {
PSY_EVENT_PROP_CHANGED,
};
union power_supply_propval { union power_supply_propval {
int intval; int intval;
const char *strval; const char *strval;
...@@ -235,7 +240,18 @@ struct power_supply_info { ...@@ -235,7 +240,18 @@ struct power_supply_info {
int use_for_apm; 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); 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 void power_supply_changed(struct power_supply *psy);
extern int power_supply_am_i_supplied(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); extern int power_supply_set_battery_charged(struct power_supply *psy);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment