mirror of https://gitee.com/openkylin/linux.git
A bunch of fixes for v3.5, nothing extraordinary.
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.18 (GNU/Linux) iQIcBAABAgAGBQJPxr4VAAoJEGgI9fZJve1bmOkP/R+hVC0lHRzbevDiDXVzxx+M XNG3krM73Y6jC9sdIxUj5wU1/BpQ3z6wYNEKKPKeXJoHPW+UJaN+wjhm6+uYQPx/ 6QM7Fkraxcya98I7vKIsz+uVRd9vETMBvgrix6hZ/ec8xO9q62d5ozkXjfG3E4qO 3vUFSihGmeVVGES1BFehIMkLEHRqlEuiUsXwMw71cBaIYATXruzy46iRqS9e3fVS mLc5+Ylvsm9q65wY1djv2Kieq5AuZ1dOgH8du2FYWJED+vogkqRcZTWeJeuZ3HAc ql72WhN2ga2U+xuxypVt+mVl2Gb1pjfE1j802EDjZX6ir1E50iSWpcaKIM1M8th7 kZwCvzcMihtzBaKvZjXf1IVazZyBo8Chi02YNbG3UVWW/rSrYjV9GSSh5HPKfY3A Arw80C4t/I3kiCPr5uwdZZO/D5lsleMF677NEilTmsKZgPSOe9EqbZOTOGPKuALj A/y2SVCV0sPVb2ki8wYlQ5dpNRsz/Uya+I3cqRFW63YGeSyGHm8VysxWRjzzu6LN +n7I2q/3Un95aHGMMIoJ/3crHcdtKSqtXeBKbBSiRdRpyO4b4rVRkLJRARv36V7m eiaduSZJEf5ZrEb3z20FmM2SqjiFHz+d0Q1QH6MQmhwDQ44acKxx/iw6moxGtLTQ JsDneVe+4d3WlJFqXd3s =B6u0 -----END PGP SIGNATURE----- Merge tag 'for-v3.5' of git://git.infradead.org/battery-2.6 Pull battery updates from Anton Vorontsov: "A bunch of fixes for v3.5, nothing extraordinary." * tag 'for-v3.5' of git://git.infradead.org/battery-2.6: (27 commits) smb347-charger: Include missing <linux/err.h> smb347-charger: Clean up battery attributes max17042_battery: Add support for max17047/50 chip sbs-battery.c: Capacity attr = remaining relative capacity isp1704_charger: Use after free on probe error ds2781_battery: Use DS2781_PARAM_EEPROM_SIZE and DS2781_USER_EEPROM_SIZE power_supply: Fix a typo in BATTERY_DS2781 Kconfig entry charger-manager: Provide cm_notify_event function for in-kernel use charger-manager: Poll battery health in normal state smb347-charger: Convert to regmap API smb347-charger: Move IRQ enabling to the end of probe smb347-charger: Rename few functions to match better what they are doing smb347-charger: Convert to use module_i2c_driver() smb347_charger: Cleanup power supply registration code in probe ab8500: Clean up probe routines ab8500_fg: Harden platform data check ab8500_btemp: Harden platform data check ab8500_charger: Harden platform data check MAINTAINERS: Fix 'F' entry for the power supply class max17042_battery: Handle irq request failure case ...
This commit is contained in:
commit
76f901eb46
|
@ -44,6 +44,16 @@ Charger Manager supports the following:
|
|||
Normally, the platform will need to resume and suspend some devices
|
||||
that are used by Charger Manager.
|
||||
|
||||
* Support for premature full-battery event handling
|
||||
If the battery voltage drops by "fullbatt_vchkdrop_uV" after
|
||||
"fullbatt_vchkdrop_ms" from the full-battery event, the framework
|
||||
restarts charging. This check is also performed while suspended by
|
||||
setting wakeup time accordingly and using suspend_again.
|
||||
|
||||
* Support for uevent-notify
|
||||
With the charger-related events, the device sends
|
||||
notification to users with UEVENT.
|
||||
|
||||
2. Global Charger-Manager Data related with suspend_again
|
||||
========================================================
|
||||
In order to setup Charger Manager with suspend-again feature
|
||||
|
@ -55,7 +65,7 @@ if there are multiple batteries. If there are multiple batteries, the
|
|||
multiple instances of Charger Manager share the same charger_global_desc
|
||||
and it will manage in-suspend monitoring for all instances of Charger Manager.
|
||||
|
||||
The user needs to provide all the two entries properly in order to activate
|
||||
The user needs to provide all the three entries properly in order to activate
|
||||
in-suspend monitoring:
|
||||
|
||||
struct charger_global_desc {
|
||||
|
@ -74,6 +84,11 @@ bool (*rtc_only_wakeup)(void);
|
|||
same struct. If there is any other wakeup source triggered the
|
||||
wakeup, it should return false. If the "rtc" is the only wakeup
|
||||
reason, it should return true.
|
||||
|
||||
bool assume_timer_stops_in_suspend;
|
||||
: if true, Charger Manager assumes that
|
||||
the timer (CM uses jiffies as timer) stops during suspend. Then, CM
|
||||
assumes that the suspend-duration is same as the alarm length.
|
||||
};
|
||||
|
||||
3. How to setup suspend_again
|
||||
|
@ -111,6 +126,16 @@ enum polling_modes polling_mode;
|
|||
CM_POLL_CHARGING_ONLY: poll this battery if and only if the
|
||||
battery is being charged.
|
||||
|
||||
unsigned int fullbatt_vchkdrop_ms;
|
||||
unsigned int fullbatt_vchkdrop_uV;
|
||||
: If both have non-zero values, Charger Manager will check the
|
||||
battery voltage drop fullbatt_vchkdrop_ms after the battery is fully
|
||||
charged. If the voltage drop is over fullbatt_vchkdrop_uV, Charger
|
||||
Manager will try to recharge the battery by disabling and enabling
|
||||
chargers. Recharge with voltage drop condition only (without delay
|
||||
condition) is needed to be implemented with hardware interrupts from
|
||||
fuel gauges or charger devices/chips.
|
||||
|
||||
unsigned int fullbatt_uV;
|
||||
: If specified with a non-zero value, Charger Manager assumes
|
||||
that the battery is full (capacity = 100) if the battery is not being
|
||||
|
@ -122,6 +147,8 @@ unsigned int polling_interval_ms;
|
|||
this battery every polling_interval_ms or more frequently.
|
||||
|
||||
enum data_source battery_present;
|
||||
: CM_BATTERY_PRESENT: assume that the battery exists.
|
||||
CM_NO_BATTERY: assume that the battery does not exists.
|
||||
CM_FUEL_GAUGE: get battery presence information from fuel gauge.
|
||||
CM_CHARGER_STAT: get battery presence from chargers.
|
||||
|
||||
|
@ -151,7 +178,17 @@ bool measure_battery_temp;
|
|||
the value of measure_battery_temp.
|
||||
};
|
||||
|
||||
5. Other Considerations
|
||||
5. Notify Charger-Manager of charger events: cm_notify_event()
|
||||
=========================================================
|
||||
If there is an charger event is required to notify
|
||||
Charger Manager, a charger device driver that triggers the event can call
|
||||
cm_notify_event(psy, type, msg) to notify the corresponding Charger Manager.
|
||||
In the function, psy is the charger driver's power_supply pointer, which is
|
||||
associated with Charger-Manager. The parameter "type"
|
||||
is the same as irq's type (enum cm_event_types). The event message "msg" is
|
||||
optional and is effective only if the event type is "UNDESCRIBED" or "OTHERS".
|
||||
|
||||
6. Other Considerations
|
||||
=======================
|
||||
|
||||
At the charger/battery-related events such as battery-pulled-out,
|
||||
|
|
|
@ -84,6 +84,8 @@ are already charged or discharging, 'n/a' can be displayed (or
|
|||
HEALTH - represents health of the battery, values corresponds to
|
||||
POWER_SUPPLY_HEALTH_*, defined in battery.h.
|
||||
|
||||
VOLTAGE_OCV - open circuit voltage of the battery.
|
||||
|
||||
VOLTAGE_MAX_DESIGN, VOLTAGE_MIN_DESIGN - design values for maximal and
|
||||
minimal power supply voltages. Maximal/minimal means values of voltages
|
||||
when battery considered "full"/"empty" at normal conditions. Yes, there is
|
||||
|
|
|
@ -5337,7 +5337,7 @@ M: David Woodhouse <dwmw2@infradead.org>
|
|||
T: git git://git.infradead.org/battery-2.6.git
|
||||
S: Maintained
|
||||
F: include/linux/power_supply.h
|
||||
F: drivers/power/power_supply*
|
||||
F: drivers/power/
|
||||
|
||||
PNP SUPPORT
|
||||
M: Adam Belay <abelay@mit.edu>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
menuconfig POWER_SUPPLY
|
||||
tristate "Power supply class support"
|
||||
bool "Power supply class support"
|
||||
help
|
||||
Say Y here to enable power supply class support. This allows
|
||||
power supply (batteries, AC, USB) monitoring by userspace
|
||||
|
@ -77,7 +77,7 @@ config BATTERY_DS2780
|
|||
Say Y here to enable support for batteries with ds2780 chip.
|
||||
|
||||
config BATTERY_DS2781
|
||||
tristate "2781 battery driver"
|
||||
tristate "DS2781 battery driver"
|
||||
depends on HAS_IOMEM
|
||||
select W1
|
||||
select W1_SLAVE_DS2781
|
||||
|
@ -181,14 +181,15 @@ config BATTERY_MAX17040
|
|||
to operate with a single lithium cell
|
||||
|
||||
config BATTERY_MAX17042
|
||||
tristate "Maxim MAX17042/8997/8966 Fuel Gauge"
|
||||
tristate "Maxim MAX17042/17047/17050/8997/8966 Fuel Gauge"
|
||||
depends on I2C
|
||||
help
|
||||
MAX17042 is fuel-gauge systems for lithium-ion (Li+) batteries
|
||||
in handheld and portable equipment. The MAX17042 is configured
|
||||
to operate with a single lithium cell. MAX8997 and MAX8966 are
|
||||
multi-function devices that include fuel gauages that are compatible
|
||||
with MAX17042.
|
||||
with MAX17042. This driver also supports max17047/50 chips which are
|
||||
improved version of max17042.
|
||||
|
||||
config BATTERY_Z2
|
||||
tristate "Z2 battery driver"
|
||||
|
@ -291,6 +292,7 @@ config CHARGER_MAX8998
|
|||
config CHARGER_SMB347
|
||||
tristate "Summit Microelectronics SMB347 Battery Charger"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y to include support for Summit Microelectronics SMB347
|
||||
Battery Charger.
|
||||
|
|
|
@ -964,10 +964,15 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
|
|||
{
|
||||
int irq, i, ret = 0;
|
||||
u8 val;
|
||||
struct abx500_bm_plat_data *plat_data;
|
||||
struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
|
||||
struct ab8500_btemp *di;
|
||||
|
||||
struct ab8500_btemp *di =
|
||||
kzalloc(sizeof(struct ab8500_btemp), GFP_KERNEL);
|
||||
if (!plat_data) {
|
||||
dev_err(&pdev->dev, "No platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
di = kzalloc(sizeof(*di), GFP_KERNEL);
|
||||
if (!di)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -977,7 +982,6 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
|
|||
di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
|
||||
|
||||
/* get btemp specific platform data */
|
||||
plat_data = pdev->dev.platform_data;
|
||||
di->pdata = plat_data->btemp;
|
||||
if (!di->pdata) {
|
||||
dev_err(di->dev, "no btemp platform data supplied\n");
|
||||
|
|
|
@ -2534,10 +2534,15 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
|
|||
static int __devinit ab8500_charger_probe(struct platform_device *pdev)
|
||||
{
|
||||
int irq, i, charger_status, ret = 0;
|
||||
struct abx500_bm_plat_data *plat_data;
|
||||
struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
|
||||
struct ab8500_charger *di;
|
||||
|
||||
struct ab8500_charger *di =
|
||||
kzalloc(sizeof(struct ab8500_charger), GFP_KERNEL);
|
||||
if (!plat_data) {
|
||||
dev_err(&pdev->dev, "No platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
di = kzalloc(sizeof(*di), GFP_KERNEL);
|
||||
if (!di)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -2550,9 +2555,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
|
|||
spin_lock_init(&di->usb_state.usb_lock);
|
||||
|
||||
/* get charger specific platform data */
|
||||
plat_data = pdev->dev.platform_data;
|
||||
di->pdata = plat_data->charger;
|
||||
|
||||
if (!di->pdata) {
|
||||
dev_err(di->dev, "no charger platform data supplied\n");
|
||||
ret = -EINVAL;
|
||||
|
|
|
@ -2446,10 +2446,15 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
|
|||
{
|
||||
int i, irq;
|
||||
int ret = 0;
|
||||
struct abx500_bm_plat_data *plat_data;
|
||||
struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
|
||||
struct ab8500_fg *di;
|
||||
|
||||
struct ab8500_fg *di =
|
||||
kzalloc(sizeof(struct ab8500_fg), GFP_KERNEL);
|
||||
if (!plat_data) {
|
||||
dev_err(&pdev->dev, "No platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
di = kzalloc(sizeof(*di), GFP_KERNEL);
|
||||
if (!di)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -2461,7 +2466,6 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
|
|||
di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
|
||||
|
||||
/* get fg specific platform data */
|
||||
plat_data = pdev->dev.platform_data;
|
||||
di->pdata = plat_data->fg;
|
||||
if (!di->pdata) {
|
||||
dev_err(di->dev, "no fg platform data supplied\n");
|
||||
|
|
|
@ -23,6 +23,16 @@
|
|||
#include <linux/power/charger-manager.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
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_EXT_PWR_IN_OUT] = "External Power Attach/Detach",
|
||||
[CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",
|
||||
[CM_EVENT_OTHERS] = "Other battery events"
|
||||
};
|
||||
|
||||
/*
|
||||
* Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
|
||||
* delayed works so that we can run delayed works with CM_JIFFIES_SMALL
|
||||
|
@ -57,6 +67,12 @@ static bool cm_suspended;
|
|||
static bool cm_rtc_set;
|
||||
static unsigned long cm_suspend_duration_ms;
|
||||
|
||||
/* About normal (not suspended) monitoring */
|
||||
static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
|
||||
static unsigned long next_polling; /* Next appointed polling time */
|
||||
static struct workqueue_struct *cm_wq; /* init at driver add */
|
||||
static struct delayed_work cm_monitor_work; /* init at driver add */
|
||||
|
||||
/* Global charger-manager description */
|
||||
static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
|
||||
|
||||
|
@ -71,6 +87,11 @@ static bool is_batt_present(struct charger_manager *cm)
|
|||
int i, ret;
|
||||
|
||||
switch (cm->desc->battery_present) {
|
||||
case CM_BATTERY_PRESENT:
|
||||
present = true;
|
||||
break;
|
||||
case CM_NO_BATTERY:
|
||||
break;
|
||||
case CM_FUEL_GAUGE:
|
||||
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
|
||||
POWER_SUPPLY_PROP_PRESENT, &val);
|
||||
|
@ -278,6 +299,26 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
|
|||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* try_charger_restart - Restart charging.
|
||||
* @cm: the Charger Manager representing the battery.
|
||||
*
|
||||
* Restart charging by turning off and on the charger.
|
||||
*/
|
||||
static int try_charger_restart(struct charger_manager *cm)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (cm->emergency_stop)
|
||||
return -EAGAIN;
|
||||
|
||||
err = try_charger_enable(cm, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return try_charger_enable(cm, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* uevent_notify - Let users know something has changed.
|
||||
* @cm: the Charger Manager representing the battery.
|
||||
|
@ -333,6 +374,46 @@ static void uevent_notify(struct charger_manager *cm, const char *event)
|
|||
dev_info(cm->dev, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* fullbatt_vchk - Check voltage drop some times after "FULL" event.
|
||||
* @work: the work_struct appointing the function
|
||||
*
|
||||
* If a user has designated "fullbatt_vchkdrop_ms/uV" values with
|
||||
* charger_desc, Charger Manager checks voltage drop after the battery
|
||||
* "FULL" event. It checks whether the voltage has dropped more than
|
||||
* fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms.
|
||||
*/
|
||||
static void fullbatt_vchk(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
struct charger_manager *cm = container_of(dwork,
|
||||
struct charger_manager, fullbatt_vchk_work);
|
||||
struct charger_desc *desc = cm->desc;
|
||||
int batt_uV, err, diff;
|
||||
|
||||
/* remove the appointment for fullbatt_vchk */
|
||||
cm->fullbatt_vchk_jiffies_at = 0;
|
||||
|
||||
if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
|
||||
return;
|
||||
|
||||
err = get_batt_uV(cm, &batt_uV);
|
||||
if (err) {
|
||||
dev_err(cm->dev, "%s: get_batt_uV error(%d).\n", __func__, err);
|
||||
return;
|
||||
}
|
||||
|
||||
diff = cm->fullbatt_vchk_uV;
|
||||
diff -= batt_uV;
|
||||
|
||||
dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff);
|
||||
|
||||
if (diff > desc->fullbatt_vchkdrop_uV) {
|
||||
try_charger_restart(cm);
|
||||
uevent_notify(cm, "Recharge");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _cm_monitor - Monitor the temperature and return true for exceptions.
|
||||
* @cm: the Charger Manager representing the battery.
|
||||
|
@ -392,6 +473,131 @@ static bool cm_monitor(void)
|
|||
return stop;
|
||||
}
|
||||
|
||||
/**
|
||||
* _setup_polling - Setup the next instance of polling.
|
||||
* @work: work_struct of the function _setup_polling.
|
||||
*/
|
||||
static void _setup_polling(struct work_struct *work)
|
||||
{
|
||||
unsigned long min = ULONG_MAX;
|
||||
struct charger_manager *cm;
|
||||
bool keep_polling = false;
|
||||
unsigned long _next_polling;
|
||||
|
||||
mutex_lock(&cm_list_mtx);
|
||||
|
||||
list_for_each_entry(cm, &cm_list, entry) {
|
||||
if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
|
||||
keep_polling = true;
|
||||
|
||||
if (min > cm->desc->polling_interval_ms)
|
||||
min = cm->desc->polling_interval_ms;
|
||||
}
|
||||
}
|
||||
|
||||
polling_jiffy = msecs_to_jiffies(min);
|
||||
if (polling_jiffy <= CM_JIFFIES_SMALL)
|
||||
polling_jiffy = CM_JIFFIES_SMALL + 1;
|
||||
|
||||
if (!keep_polling)
|
||||
polling_jiffy = ULONG_MAX;
|
||||
if (polling_jiffy == ULONG_MAX)
|
||||
goto out;
|
||||
|
||||
WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
|
||||
". try it later. %s\n", __func__);
|
||||
|
||||
_next_polling = jiffies + polling_jiffy;
|
||||
|
||||
if (!delayed_work_pending(&cm_monitor_work) ||
|
||||
(delayed_work_pending(&cm_monitor_work) &&
|
||||
time_after(next_polling, _next_polling))) {
|
||||
cancel_delayed_work_sync(&cm_monitor_work);
|
||||
next_polling = jiffies + polling_jiffy;
|
||||
queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&cm_list_mtx);
|
||||
}
|
||||
static DECLARE_WORK(setup_polling, _setup_polling);
|
||||
|
||||
/**
|
||||
* cm_monitor_poller - The Monitor / Poller.
|
||||
* @work: work_struct of the function cm_monitor_poller
|
||||
*
|
||||
* During non-suspended state, cm_monitor_poller is used to poll and monitor
|
||||
* the batteries.
|
||||
*/
|
||||
static void cm_monitor_poller(struct work_struct *work)
|
||||
{
|
||||
cm_monitor();
|
||||
schedule_work(&setup_polling);
|
||||
}
|
||||
|
||||
/**
|
||||
* fullbatt_handler - Event handler for CM_EVENT_BATT_FULL
|
||||
* @cm: the Charger Manager representing the battery.
|
||||
*/
|
||||
static void fullbatt_handler(struct charger_manager *cm)
|
||||
{
|
||||
struct charger_desc *desc = cm->desc;
|
||||
|
||||
if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
|
||||
goto out;
|
||||
|
||||
if (cm_suspended)
|
||||
device_set_wakeup_capable(cm->dev, true);
|
||||
|
||||
if (delayed_work_pending(&cm->fullbatt_vchk_work))
|
||||
cancel_delayed_work(&cm->fullbatt_vchk_work);
|
||||
queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
|
||||
msecs_to_jiffies(desc->fullbatt_vchkdrop_ms));
|
||||
cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies(
|
||||
desc->fullbatt_vchkdrop_ms);
|
||||
|
||||
if (cm->fullbatt_vchk_jiffies_at == 0)
|
||||
cm->fullbatt_vchk_jiffies_at = 1;
|
||||
|
||||
out:
|
||||
dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n");
|
||||
uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* battout_handler - Event handler for CM_EVENT_BATT_OUT
|
||||
* @cm: the Charger Manager representing the battery.
|
||||
*/
|
||||
static void battout_handler(struct charger_manager *cm)
|
||||
{
|
||||
if (cm_suspended)
|
||||
device_set_wakeup_capable(cm->dev, true);
|
||||
|
||||
if (!is_batt_present(cm)) {
|
||||
dev_emerg(cm->dev, "Battery Pulled Out!\n");
|
||||
uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]);
|
||||
} else {
|
||||
uevent_notify(cm, "Battery Reinserted?");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* misc_event_handler - Handler for other evnets
|
||||
* @cm: the Charger Manager representing the battery.
|
||||
* @type: the Charger Manager representing the battery.
|
||||
*/
|
||||
static void misc_event_handler(struct charger_manager *cm,
|
||||
enum cm_event_types type)
|
||||
{
|
||||
if (cm_suspended)
|
||||
device_set_wakeup_capable(cm->dev, true);
|
||||
|
||||
if (!delayed_work_pending(&cm_monitor_work) &&
|
||||
is_polling_required(cm) && cm->desc->polling_interval_ms)
|
||||
schedule_work(&setup_polling);
|
||||
uevent_notify(cm, default_event_names[type]);
|
||||
}
|
||||
|
||||
static int charger_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
|
@ -613,6 +819,21 @@ static bool cm_setup_timer(void)
|
|||
mutex_lock(&cm_list_mtx);
|
||||
|
||||
list_for_each_entry(cm, &cm_list, entry) {
|
||||
unsigned int fbchk_ms = 0;
|
||||
|
||||
/* fullbatt_vchk is required. setup timer for that */
|
||||
if (cm->fullbatt_vchk_jiffies_at) {
|
||||
fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at
|
||||
- jiffies);
|
||||
if (time_is_before_eq_jiffies(
|
||||
cm->fullbatt_vchk_jiffies_at) ||
|
||||
msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) {
|
||||
fullbatt_vchk(&cm->fullbatt_vchk_work.work);
|
||||
fbchk_ms = 0;
|
||||
}
|
||||
}
|
||||
CM_MIN_VALID(wakeup_ms, fbchk_ms);
|
||||
|
||||
/* Skip if polling is not required for this CM */
|
||||
if (!is_polling_required(cm) && !cm->emergency_stop)
|
||||
continue;
|
||||
|
@ -672,6 +893,23 @@ static bool cm_setup_timer(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void _cm_fbchk_in_suspend(struct charger_manager *cm)
|
||||
{
|
||||
unsigned long jiffy_now = jiffies;
|
||||
|
||||
if (!cm->fullbatt_vchk_jiffies_at)
|
||||
return;
|
||||
|
||||
if (g_desc && g_desc->assume_timer_stops_in_suspend)
|
||||
jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms);
|
||||
|
||||
/* Execute now if it's going to be executed not too long after */
|
||||
jiffy_now += CM_JIFFIES_SMALL;
|
||||
|
||||
if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at))
|
||||
fullbatt_vchk(&cm->fullbatt_vchk_work.work);
|
||||
}
|
||||
|
||||
/**
|
||||
* cm_suspend_again - Determine whether suspend again or not
|
||||
*
|
||||
|
@ -693,6 +931,8 @@ bool cm_suspend_again(void)
|
|||
ret = true;
|
||||
mutex_lock(&cm_list_mtx);
|
||||
list_for_each_entry(cm, &cm_list, entry) {
|
||||
_cm_fbchk_in_suspend(cm);
|
||||
|
||||
if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
|
||||
cm->status_save_batt != is_batt_present(cm)) {
|
||||
ret = false;
|
||||
|
@ -796,6 +1036,21 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
memcpy(cm->desc, desc, sizeof(struct charger_desc));
|
||||
cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
|
||||
|
||||
/*
|
||||
* The following two do not need to be errors.
|
||||
* Users may intentionally ignore those two features.
|
||||
*/
|
||||
if (desc->fullbatt_uV == 0) {
|
||||
dev_info(&pdev->dev, "Ignoring full-battery voltage threshold"
|
||||
" as it is not supplied.");
|
||||
}
|
||||
if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) {
|
||||
dev_info(&pdev->dev, "Disabling full-battery voltage drop "
|
||||
"checking mechanism as it is not supplied.");
|
||||
desc->fullbatt_vchkdrop_ms = 0;
|
||||
desc->fullbatt_vchkdrop_uV = 0;
|
||||
}
|
||||
|
||||
if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
|
||||
ret = -EINVAL;
|
||||
dev_err(&pdev->dev, "charger_regulators undefined.\n");
|
||||
|
@ -903,6 +1158,8 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
cm->charger_psy.num_properties++;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
|
||||
|
||||
ret = power_supply_register(NULL, &cm->charger_psy);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot register charger-manager with"
|
||||
|
@ -928,6 +1185,15 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
list_add(&cm->entry, &cm_list);
|
||||
mutex_unlock(&cm_list_mtx);
|
||||
|
||||
/*
|
||||
* Charger-manager is capable of waking up the systme from sleep
|
||||
* when event is happend through cm_notify_event()
|
||||
*/
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
device_set_wakeup_capable(&pdev->dev, false);
|
||||
|
||||
schedule_work(&setup_polling);
|
||||
|
||||
return 0;
|
||||
|
||||
err_chg_enable:
|
||||
|
@ -958,9 +1224,17 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
|
|||
list_del(&cm->entry);
|
||||
mutex_unlock(&cm_list_mtx);
|
||||
|
||||
if (work_pending(&setup_polling))
|
||||
cancel_work_sync(&setup_polling);
|
||||
if (delayed_work_pending(&cm_monitor_work))
|
||||
cancel_delayed_work_sync(&cm_monitor_work);
|
||||
|
||||
regulator_bulk_free(desc->num_charger_regulators,
|
||||
desc->charger_regulators);
|
||||
power_supply_unregister(&cm->charger_psy);
|
||||
|
||||
try_charger_enable(cm, false);
|
||||
|
||||
kfree(cm->charger_psy.properties);
|
||||
kfree(cm->charger_stat);
|
||||
kfree(cm->desc);
|
||||
|
@ -975,6 +1249,18 @@ static const struct platform_device_id charger_manager_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(platform, charger_manager_id);
|
||||
|
||||
static int cm_suspend_noirq(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
device_set_wakeup_capable(dev, false);
|
||||
ret = -EAGAIN;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm_suspend_prepare(struct device *dev)
|
||||
{
|
||||
struct charger_manager *cm = dev_get_drvdata(dev);
|
||||
|
@ -1000,6 +1286,8 @@ static int cm_suspend_prepare(struct device *dev)
|
|||
cm_suspended = true;
|
||||
}
|
||||
|
||||
if (delayed_work_pending(&cm->fullbatt_vchk_work))
|
||||
cancel_delayed_work(&cm->fullbatt_vchk_work);
|
||||
cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
|
||||
cm->status_save_batt = is_batt_present(cm);
|
||||
|
||||
|
@ -1027,11 +1315,40 @@ static void cm_suspend_complete(struct device *dev)
|
|||
cm_rtc_set = false;
|
||||
}
|
||||
|
||||
/* Re-enqueue delayed work (fullbatt_vchk_work) */
|
||||
if (cm->fullbatt_vchk_jiffies_at) {
|
||||
unsigned long delay = 0;
|
||||
unsigned long now = jiffies + CM_JIFFIES_SMALL;
|
||||
|
||||
if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) {
|
||||
delay = (unsigned long)((long)now
|
||||
- (long)(cm->fullbatt_vchk_jiffies_at));
|
||||
delay = jiffies_to_msecs(delay);
|
||||
} else {
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Account for cm_suspend_duration_ms if
|
||||
* assume_timer_stops_in_suspend is active
|
||||
*/
|
||||
if (g_desc && g_desc->assume_timer_stops_in_suspend) {
|
||||
if (delay > cm_suspend_duration_ms)
|
||||
delay -= cm_suspend_duration_ms;
|
||||
else
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
|
||||
msecs_to_jiffies(delay));
|
||||
}
|
||||
device_set_wakeup_capable(cm->dev, false);
|
||||
uevent_notify(cm, NULL);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops charger_manager_pm = {
|
||||
.prepare = cm_suspend_prepare,
|
||||
.suspend_noirq = cm_suspend_noirq,
|
||||
.complete = cm_suspend_complete,
|
||||
};
|
||||
|
||||
|
@ -1048,16 +1365,91 @@ static struct platform_driver charger_manager_driver = {
|
|||
|
||||
static int __init charger_manager_init(void)
|
||||
{
|
||||
cm_wq = create_freezable_workqueue("charger_manager");
|
||||
INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
|
||||
|
||||
return platform_driver_register(&charger_manager_driver);
|
||||
}
|
||||
late_initcall(charger_manager_init);
|
||||
|
||||
static void __exit charger_manager_cleanup(void)
|
||||
{
|
||||
destroy_workqueue(cm_wq);
|
||||
cm_wq = NULL;
|
||||
|
||||
platform_driver_unregister(&charger_manager_driver);
|
||||
}
|
||||
module_exit(charger_manager_cleanup);
|
||||
|
||||
/**
|
||||
* find_power_supply - find the associated power_supply of charger
|
||||
* @cm: the Charger Manager representing the battery
|
||||
* @psy: pointer to instance of charger's power_supply
|
||||
*/
|
||||
static bool find_power_supply(struct charger_manager *cm,
|
||||
struct power_supply *psy)
|
||||
{
|
||||
int i;
|
||||
bool found = false;
|
||||
|
||||
for (i = 0; cm->charger_stat[i]; i++) {
|
||||
if (psy == cm->charger_stat[i]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* cm_notify_event - charger driver notify Charger Manager of charger event
|
||||
* @psy: pointer to instance of charger's power_supply
|
||||
* @type: type of charger event
|
||||
* @msg: optional message passed to uevent_notify fuction
|
||||
*/
|
||||
void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
|
||||
char *msg)
|
||||
{
|
||||
struct charger_manager *cm;
|
||||
bool found_power_supply = false;
|
||||
|
||||
if (psy == NULL)
|
||||
return;
|
||||
|
||||
mutex_lock(&cm_list_mtx);
|
||||
list_for_each_entry(cm, &cm_list, entry) {
|
||||
found_power_supply = find_power_supply(cm, psy);
|
||||
if (found_power_supply)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&cm_list_mtx);
|
||||
|
||||
if (!found_power_supply)
|
||||
return;
|
||||
|
||||
switch (type) {
|
||||
case CM_EVENT_BATT_FULL:
|
||||
fullbatt_handler(cm);
|
||||
break;
|
||||
case CM_EVENT_BATT_OUT:
|
||||
battout_handler(cm);
|
||||
break;
|
||||
case CM_EVENT_BATT_IN:
|
||||
case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP:
|
||||
misc_event_handler(cm, type);
|
||||
break;
|
||||
case CM_EVENT_UNKNOWN:
|
||||
case CM_EVENT_OTHERS:
|
||||
uevent_notify(cm, msg ? msg : default_event_names[type]);
|
||||
break;
|
||||
default:
|
||||
dev_err(cm->dev, "%s type not specified.\n", __func__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cm_notify_event);
|
||||
|
||||
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
|
||||
MODULE_DESCRIPTION("Charger Manager");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -643,9 +643,7 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
|
|||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
count = min_t(loff_t, count,
|
||||
DS2781_EEPROM_BLOCK1_END -
|
||||
DS2781_EEPROM_BLOCK1_START + 1 - off);
|
||||
count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off);
|
||||
|
||||
return ds2781_read_block(dev_info, buf,
|
||||
DS2781_EEPROM_BLOCK1_START + off, count);
|
||||
|
@ -661,9 +659,7 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
|
|||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
int ret;
|
||||
|
||||
count = min_t(loff_t, count,
|
||||
DS2781_EEPROM_BLOCK1_END -
|
||||
DS2781_EEPROM_BLOCK1_START + 1 - off);
|
||||
count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off);
|
||||
|
||||
ret = ds2781_write(dev_info, buf,
|
||||
DS2781_EEPROM_BLOCK1_START + off, count);
|
||||
|
@ -682,7 +678,7 @@ static struct bin_attribute ds2781_param_eeprom_bin_attr = {
|
|||
.name = "param_eeprom",
|
||||
.mode = S_IRUGO | S_IWUSR,
|
||||
},
|
||||
.size = DS2781_EEPROM_BLOCK1_END - DS2781_EEPROM_BLOCK1_START + 1,
|
||||
.size = DS2781_PARAM_EEPROM_SIZE,
|
||||
.read = ds2781_read_param_eeprom_bin,
|
||||
.write = ds2781_write_param_eeprom_bin,
|
||||
};
|
||||
|
@ -696,9 +692,7 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
|
|||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
count = min_t(loff_t, count,
|
||||
DS2781_EEPROM_BLOCK0_END -
|
||||
DS2781_EEPROM_BLOCK0_START + 1 - off);
|
||||
count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off);
|
||||
|
||||
return ds2781_read_block(dev_info, buf,
|
||||
DS2781_EEPROM_BLOCK0_START + off, count);
|
||||
|
@ -715,9 +709,7 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
|
|||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
int ret;
|
||||
|
||||
count = min_t(loff_t, count,
|
||||
DS2781_EEPROM_BLOCK0_END -
|
||||
DS2781_EEPROM_BLOCK0_START + 1 - off);
|
||||
count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off);
|
||||
|
||||
ret = ds2781_write(dev_info, buf,
|
||||
DS2781_EEPROM_BLOCK0_START + off, count);
|
||||
|
@ -736,7 +728,7 @@ static struct bin_attribute ds2781_user_eeprom_bin_attr = {
|
|||
.name = "user_eeprom",
|
||||
.mode = S_IRUGO | S_IWUSR,
|
||||
},
|
||||
.size = DS2781_EEPROM_BLOCK0_END - DS2781_EEPROM_BLOCK0_START + 1,
|
||||
.size = DS2781_USER_EEPROM_SIZE,
|
||||
.read = ds2781_read_user_eeprom_bin,
|
||||
.write = ds2781_write_user_eeprom_bin,
|
||||
};
|
||||
|
|
|
@ -474,13 +474,13 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
|
|||
fail2:
|
||||
power_supply_unregister(&isp->psy);
|
||||
fail1:
|
||||
isp1704_charger_set_power(isp, 0);
|
||||
usb_put_transceiver(isp->phy);
|
||||
fail0:
|
||||
kfree(isp);
|
||||
|
||||
dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
|
||||
|
||||
isp1704_charger_set_power(isp, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/power/max17042_battery.h>
|
||||
|
@ -61,9 +62,13 @@
|
|||
#define dP_ACC_100 0x1900
|
||||
#define dP_ACC_200 0x3200
|
||||
|
||||
#define MAX17042_IC_VERSION 0x0092
|
||||
#define MAX17047_IC_VERSION 0x00AC /* same for max17050 */
|
||||
|
||||
struct max17042_chip {
|
||||
struct i2c_client *client;
|
||||
struct power_supply battery;
|
||||
enum max170xx_chip_type chip_type;
|
||||
struct max17042_platform_data *pdata;
|
||||
struct work_struct work;
|
||||
int init_complete;
|
||||
|
@ -105,6 +110,7 @@ static enum power_supply_property max17042_battery_props[] = {
|
|||
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_AVG,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
|
@ -150,7 +156,10 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
val->intval *= 20000; /* Units of LSB = 20mV */
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_V_empty);
|
||||
if (chip->chip_type == MAX17042)
|
||||
ret = max17042_read_reg(chip->client, MAX17042_V_empty);
|
||||
else
|
||||
ret = max17042_read_reg(chip->client, MAX17047_V_empty);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -169,6 +178,13 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret * 625 / 8;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_OCVInternal);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret * 625 / 8;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
|
@ -325,11 +341,10 @@ static inline int max17042_model_data_compare(struct max17042_chip *chip,
|
|||
static int max17042_init_model(struct max17042_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
int table_size =
|
||||
sizeof(chip->pdata->config_data->cell_char_tbl)/sizeof(u16);
|
||||
int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
|
||||
u16 *temp_data;
|
||||
|
||||
temp_data = kzalloc(table_size, GFP_KERNEL);
|
||||
temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
|
||||
if (!temp_data)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -354,12 +369,11 @@ static int max17042_init_model(struct max17042_chip *chip)
|
|||
static int max17042_verify_model_lock(struct max17042_chip *chip)
|
||||
{
|
||||
int i;
|
||||
int table_size =
|
||||
sizeof(chip->pdata->config_data->cell_char_tbl);
|
||||
int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
|
||||
u16 *temp_data;
|
||||
int ret = 0;
|
||||
|
||||
temp_data = kzalloc(table_size, GFP_KERNEL);
|
||||
temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
|
||||
if (!temp_data)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -382,6 +396,9 @@ static void max17042_write_config_regs(struct max17042_chip *chip)
|
|||
max17042_write_reg(chip->client, MAX17042_FilterCFG,
|
||||
config->filter_cfg);
|
||||
max17042_write_reg(chip->client, MAX17042_RelaxCFG, config->relax_cfg);
|
||||
if (chip->chip_type == MAX17047)
|
||||
max17042_write_reg(chip->client, MAX17047_FullSOCThr,
|
||||
config->full_soc_thresh);
|
||||
}
|
||||
|
||||
static void max17042_write_custom_regs(struct max17042_chip *chip)
|
||||
|
@ -392,12 +409,23 @@ static void max17042_write_custom_regs(struct max17042_chip *chip)
|
|||
config->rcomp0);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_TempCo,
|
||||
config->tcompc0);
|
||||
max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
|
||||
config->empty_tempco);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
|
||||
config->kempty0);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_ICHGTerm,
|
||||
config->ichgt_term);
|
||||
if (chip->chip_type == MAX17042) {
|
||||
max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
|
||||
config->empty_tempco);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
|
||||
config->kempty0);
|
||||
} else {
|
||||
max17042_write_verify_reg(chip->client, MAX17047_QRTbl00,
|
||||
config->qrtbl00);
|
||||
max17042_write_verify_reg(chip->client, MAX17047_QRTbl10,
|
||||
config->qrtbl10);
|
||||
max17042_write_verify_reg(chip->client, MAX17047_QRTbl20,
|
||||
config->qrtbl20);
|
||||
max17042_write_verify_reg(chip->client, MAX17047_QRTbl30,
|
||||
config->qrtbl30);
|
||||
}
|
||||
}
|
||||
|
||||
static void max17042_update_capacity_regs(struct max17042_chip *chip)
|
||||
|
@ -453,6 +481,8 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip)
|
|||
config->design_cap);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
|
||||
config->fullcapnom);
|
||||
/* Update SOC register with new SOC */
|
||||
max17042_write_reg(chip->client, MAX17042_RepSOC, vfSoc);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -489,20 +519,28 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
|
|||
|
||||
max17042_override_por(client, MAX17042_FullCAP, config->fullcap);
|
||||
max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom);
|
||||
max17042_override_por(client, MAX17042_SOC_empty, config->socempty);
|
||||
if (chip->chip_type == MAX17042)
|
||||
max17042_override_por(client, MAX17042_SOC_empty,
|
||||
config->socempty);
|
||||
max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty);
|
||||
max17042_override_por(client, MAX17042_dQacc, config->dqacc);
|
||||
max17042_override_por(client, MAX17042_dPacc, config->dpacc);
|
||||
|
||||
max17042_override_por(client, MAX17042_V_empty, config->vempty);
|
||||
if (chip->chip_type == MAX17042)
|
||||
max17042_override_por(client, MAX17042_V_empty, config->vempty);
|
||||
else
|
||||
max17042_override_por(client, MAX17047_V_empty, config->vempty);
|
||||
max17042_override_por(client, MAX17042_TempNom, config->temp_nom);
|
||||
max17042_override_por(client, MAX17042_TempLim, config->temp_lim);
|
||||
max17042_override_por(client, MAX17042_FCTC, config->fctc);
|
||||
max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0);
|
||||
max17042_override_por(client, MAX17042_TempCo, config->tcompc0);
|
||||
max17042_override_por(client, MAX17042_EmptyTempCo,
|
||||
config->empty_tempco);
|
||||
max17042_override_por(client, MAX17042_K_empty0, config->kempty0);
|
||||
if (chip->chip_type) {
|
||||
max17042_override_por(client, MAX17042_EmptyTempCo,
|
||||
config->empty_tempco);
|
||||
max17042_override_por(client, MAX17042_K_empty0,
|
||||
config->kempty0);
|
||||
}
|
||||
}
|
||||
|
||||
static int max17042_init_chip(struct max17042_chip *chip)
|
||||
|
@ -659,7 +697,19 @@ static int __devinit max17042_probe(struct i2c_client *client,
|
|||
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
chip->battery.name = "max17042_battery";
|
||||
ret = max17042_read_reg(chip->client, MAX17042_DevName);
|
||||
if (ret == MAX17042_IC_VERSION) {
|
||||
dev_dbg(&client->dev, "chip type max17042 detected\n");
|
||||
chip->chip_type = MAX17042;
|
||||
} else if (ret == MAX17047_IC_VERSION) {
|
||||
dev_dbg(&client->dev, "chip type max17047/50 detected\n");
|
||||
chip->chip_type = MAX17047;
|
||||
} else {
|
||||
dev_err(&client->dev, "device version mismatch: %x\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
chip->battery.name = "max170xx_battery";
|
||||
chip->battery.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
chip->battery.get_property = max17042_get_property;
|
||||
chip->battery.properties = max17042_battery_props;
|
||||
|
@ -683,6 +733,12 @@ static int __devinit max17042_probe(struct i2c_client *client,
|
|||
max17042_write_reg(client, MAX17042_LearnCFG, 0x0007);
|
||||
}
|
||||
|
||||
ret = power_supply_register(&client->dev, &chip->battery);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed: power supply register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (client->irq) {
|
||||
ret = request_threaded_irq(client->irq, NULL,
|
||||
max17042_thread_handler,
|
||||
|
@ -693,13 +749,14 @@ static int __devinit max17042_probe(struct i2c_client *client,
|
|||
reg |= CONFIG_ALRT_BIT_ENBL;
|
||||
max17042_write_reg(client, MAX17042_CONFIG, reg);
|
||||
max17042_set_soc_threshold(chip, 1);
|
||||
} else
|
||||
} else {
|
||||
client->irq = 0;
|
||||
dev_err(&client->dev, "%s(): cannot get IRQ\n",
|
||||
__func__);
|
||||
}
|
||||
}
|
||||
|
||||
reg = max17042_read_reg(chip->client, MAX17042_STATUS);
|
||||
|
||||
if (reg & STATUS_POR_BIT) {
|
||||
INIT_WORK(&chip->work, max17042_init_worker);
|
||||
schedule_work(&chip->work);
|
||||
|
@ -707,23 +764,65 @@ static int __devinit max17042_probe(struct i2c_client *client,
|
|||
chip->init_complete = 1;
|
||||
}
|
||||
|
||||
ret = power_supply_register(&client->dev, &chip->battery);
|
||||
if (ret)
|
||||
dev_err(&client->dev, "failed: power supply register\n");
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit max17042_remove(struct i2c_client *client)
|
||||
{
|
||||
struct max17042_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
if (client->irq)
|
||||
free_irq(client->irq, chip);
|
||||
power_supply_unregister(&chip->battery);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int max17042_suspend(struct device *dev)
|
||||
{
|
||||
struct max17042_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
/*
|
||||
* disable the irq and enable irq_wake
|
||||
* capability to the interrupt line.
|
||||
*/
|
||||
if (chip->client->irq) {
|
||||
disable_irq(chip->client->irq);
|
||||
enable_irq_wake(chip->client->irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max17042_resume(struct device *dev)
|
||||
{
|
||||
struct max17042_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
if (chip->client->irq) {
|
||||
disable_irq_wake(chip->client->irq);
|
||||
enable_irq(chip->client->irq);
|
||||
/* re-program the SOC thresholds to 1% change */
|
||||
max17042_set_soc_threshold(chip, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops max17042_pm_ops = {
|
||||
.suspend = max17042_suspend,
|
||||
.resume = max17042_resume,
|
||||
};
|
||||
|
||||
#define MAX17042_PM_OPS (&max17042_pm_ops)
|
||||
#else
|
||||
#define MAX17042_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id max17042_dt_match[] = {
|
||||
{ .compatible = "maxim,max17042" },
|
||||
{ .compatible = "maxim,max17047" },
|
||||
{ .compatible = "maxim,max17050" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max17042_dt_match);
|
||||
|
@ -731,6 +830,8 @@ MODULE_DEVICE_TABLE(of, max17042_dt_match);
|
|||
|
||||
static const struct i2c_device_id max17042_id[] = {
|
||||
{ "max17042", 0 },
|
||||
{ "max17047", 1 },
|
||||
{ "max17050", 2 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max17042_id);
|
||||
|
@ -739,6 +840,7 @@ static struct i2c_driver max17042_i2c_driver = {
|
|||
.driver = {
|
||||
.name = "max17042",
|
||||
.of_match_table = of_match_ptr(max17042_dt_match),
|
||||
.pm = MAX17042_PM_OPS,
|
||||
},
|
||||
.probe = max17042_probe,
|
||||
.remove = __devexit_p(max17042_remove),
|
||||
|
|
|
@ -146,6 +146,7 @@ static struct device_attribute power_supply_attrs[] = {
|
|||
POWER_SUPPLY_ATTR(voltage_min_design),
|
||||
POWER_SUPPLY_ATTR(voltage_now),
|
||||
POWER_SUPPLY_ATTR(voltage_avg),
|
||||
POWER_SUPPLY_ATTR(voltage_ocv),
|
||||
POWER_SUPPLY_ATTR(current_max),
|
||||
POWER_SUPPLY_ATTR(current_now),
|
||||
POWER_SUPPLY_ATTR(current_avg),
|
||||
|
|
|
@ -89,7 +89,7 @@ static const struct chip_data {
|
|||
[REG_CURRENT] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767),
|
||||
[REG_CAPACITY] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100),
|
||||
SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0D, 0, 100),
|
||||
[REG_REMAINING_CAPACITY] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535),
|
||||
[REG_REMAINING_CAPACITY_CHARGE] =
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,6 +18,8 @@
|
|||
#include <linux/power_supply.h>
|
||||
|
||||
enum data_source {
|
||||
CM_BATTERY_PRESENT,
|
||||
CM_NO_BATTERY,
|
||||
CM_FUEL_GAUGE,
|
||||
CM_CHARGER_STAT,
|
||||
};
|
||||
|
@ -29,6 +31,16 @@ enum polling_modes {
|
|||
CM_POLL_CHARGING_ONLY,
|
||||
};
|
||||
|
||||
enum cm_event_types {
|
||||
CM_EVENT_UNKNOWN = 0,
|
||||
CM_EVENT_BATT_FULL,
|
||||
CM_EVENT_BATT_IN,
|
||||
CM_EVENT_BATT_OUT,
|
||||
CM_EVENT_EXT_PWR_IN_OUT,
|
||||
CM_EVENT_CHG_START_STOP,
|
||||
CM_EVENT_OTHERS,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct charger_global_desc
|
||||
* @rtc_name: the name of RTC used to wake up the system from suspend.
|
||||
|
@ -38,11 +50,18 @@ enum polling_modes {
|
|||
* rtc_only_wakeup() returning false.
|
||||
* If the RTC given to CM is the only wakeup reason,
|
||||
* rtc_only_wakeup should return true.
|
||||
* @assume_timer_stops_in_suspend:
|
||||
* Assume that the jiffy timer stops in suspend-to-RAM.
|
||||
* When enabled, CM does not rely on jiffies value in
|
||||
* suspend_again and assumes that jiffies value does not
|
||||
* change during suspend.
|
||||
*/
|
||||
struct charger_global_desc {
|
||||
char *rtc_name;
|
||||
|
||||
bool (*rtc_only_wakeup)(void);
|
||||
|
||||
bool assume_timer_stops_in_suspend;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -50,6 +69,11 @@ struct charger_global_desc {
|
|||
* @psy_name: the name of power-supply-class for charger manager
|
||||
* @polling_mode:
|
||||
* Determine which polling mode will be used
|
||||
* @fullbatt_vchkdrop_ms:
|
||||
* @fullbatt_vchkdrop_uV:
|
||||
* Check voltage drop after the battery is fully charged.
|
||||
* If it has dropped more than fullbatt_vchkdrop_uV after
|
||||
* fullbatt_vchkdrop_ms, CM will restart charging.
|
||||
* @fullbatt_uV: voltage in microvolt
|
||||
* If it is not being charged and VBATT >= fullbatt_uV,
|
||||
* it is assumed to be full.
|
||||
|
@ -76,6 +100,8 @@ struct charger_desc {
|
|||
enum polling_modes polling_mode;
|
||||
unsigned int polling_interval_ms;
|
||||
|
||||
unsigned int fullbatt_vchkdrop_ms;
|
||||
unsigned int fullbatt_vchkdrop_uV;
|
||||
unsigned int fullbatt_uV;
|
||||
|
||||
enum data_source battery_present;
|
||||
|
@ -101,6 +127,11 @@ struct charger_desc {
|
|||
* @fuel_gauge: power_supply for fuel gauge
|
||||
* @charger_stat: array of power_supply for chargers
|
||||
* @charger_enabled: the state of charger
|
||||
* @fullbatt_vchk_jiffies_at:
|
||||
* jiffies at the time full battery check will occur.
|
||||
* @fullbatt_vchk_uV: voltage in microvolt
|
||||
* criteria for full battery
|
||||
* @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
|
||||
|
@ -121,6 +152,10 @@ struct charger_manager {
|
|||
|
||||
bool charger_enabled;
|
||||
|
||||
unsigned long fullbatt_vchk_jiffies_at;
|
||||
unsigned int fullbatt_vchk_uV;
|
||||
struct delayed_work fullbatt_vchk_work;
|
||||
|
||||
int emergency_stop;
|
||||
int last_temp_mC;
|
||||
|
||||
|
@ -134,14 +169,13 @@ struct charger_manager {
|
|||
#ifdef CONFIG_CHARGER_MANAGER
|
||||
extern int setup_charger_manager(struct charger_global_desc *gd);
|
||||
extern bool cm_suspend_again(void);
|
||||
extern void cm_notify_event(struct power_supply *psy,
|
||||
enum cm_event_types type, char *msg);
|
||||
#else
|
||||
static void __maybe_unused setup_charger_manager(struct charger_global_desc *gd)
|
||||
{ }
|
||||
|
||||
static bool __maybe_unused cm_suspend_again(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline int setup_charger_manager(struct charger_global_desc *gd)
|
||||
{ return 0; }
|
||||
static inline bool cm_suspend_again(void) { return false; }
|
||||
static inline void cm_notify_event(struct power_supply *psy,
|
||||
enum cm_event_types type, char *msg) { }
|
||||
#endif
|
||||
|
||||
#endif /* _CHARGER_MANAGER_H */
|
||||
|
|
|
@ -116,6 +116,18 @@ enum max17042_register {
|
|||
MAX17042_VFSOC = 0xFF,
|
||||
};
|
||||
|
||||
/* Registers specific to max17047/50 */
|
||||
enum max17047_register {
|
||||
MAX17047_QRTbl00 = 0x12,
|
||||
MAX17047_FullSOCThr = 0x13,
|
||||
MAX17047_QRTbl10 = 0x22,
|
||||
MAX17047_QRTbl20 = 0x32,
|
||||
MAX17047_V_empty = 0x3A,
|
||||
MAX17047_QRTbl30 = 0x42,
|
||||
};
|
||||
|
||||
enum max170xx_chip_type {MAX17042, MAX17047};
|
||||
|
||||
/*
|
||||
* used for setting a register to a desired value
|
||||
* addr : address for a register
|
||||
|
@ -144,6 +156,7 @@ struct max17042_config_data {
|
|||
u16 shdntimer; /* 0x03F */
|
||||
|
||||
/* App data */
|
||||
u16 full_soc_thresh; /* 0x13 */
|
||||
u16 design_cap; /* 0x18 */
|
||||
u16 ichgt_term; /* 0x1E */
|
||||
|
||||
|
@ -162,6 +175,10 @@ struct max17042_config_data {
|
|||
u16 lavg_empty; /* 0x36 */
|
||||
u16 dqacc; /* 0x45 */
|
||||
u16 dpacc; /* 0x46 */
|
||||
u16 qrtbl00; /* 0x12 */
|
||||
u16 qrtbl10; /* 0x22 */
|
||||
u16 qrtbl20; /* 0x32 */
|
||||
u16 qrtbl30; /* 0x42 */
|
||||
|
||||
/* Cell technology from power_supply.h */
|
||||
u16 cell_technology;
|
||||
|
|
|
@ -96,6 +96,7 @@ enum power_supply_property {
|
|||
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_AVG,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
||||
POWER_SUPPLY_PROP_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_AVG,
|
||||
|
@ -211,7 +212,7 @@ 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);
|
||||
|
||||
#if defined(CONFIG_POWER_SUPPLY) || defined(CONFIG_POWER_SUPPLY_MODULE)
|
||||
#ifdef CONFIG_POWER_SUPPLY
|
||||
extern int power_supply_is_system_supplied(void);
|
||||
#else
|
||||
static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
|
||||
|
@ -261,6 +262,7 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp)
|
|||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
|
||||
case POWER_SUPPLY_PROP_POWER_NOW:
|
||||
return 1;
|
||||
default:
|
||||
|
|
Loading…
Reference in New Issue