linux_dsm_epyc7002/include/linux/power/charger-manager.h
Krzysztof Kozlowski cdaf3e1538 power: charger-manager: Fix accessing invalidated power supply after charger unbind
The charger manager obtained in probe references to power supplies for
all chargers with power_supply_get_by_name() for later usage. However
if such charger driver was removed then this reference would point to
old power supply (from driver which was removed).

This lead to accessing invalid memory which could be observed with:
$ echo "max77693-charger" > /sys/bus/platform/drivers/max77693-charger/unbind
$ grep . /sys/devices/virtual/power_supply/battery/charger.0/*
$ grep . /sys/devices/virtual/power_supply/battery/*
[   15.339817] Unable to handle kernel paging request at virtual address 0001c12c
[   15.346187] pgd = edd08000
[   15.348814] [0001c12c] *pgd=6dce2831, *pte=00000000, *ppte=00000000
[   15.355075] Internal error: Oops: 80000007 [#1] PREEMPT SMP ARM
[   15.360967] Modules linked in:
[   15.364010] CPU: 2 PID: 1388 Comm: grep Not tainted 3.17.0-next-20141007-00027-ga95e761db1b0 #245
[   15.372859] task: ee03ad00 ti: edcf6000 task.ti: edcf6000
[   15.378241] PC is at 0x1c12c
[   15.381113] LR is at is_ext_pwr_online+0x30/0x6c
[   15.385706] pc : [<0001c12c>]    lr : [<c0339fc4>]    psr: a0000013
[   15.385706] sp : edcf7e88  ip : 00000000  fp : 00000000
[   15.397161] r10: eeb02c08  r9 : c04b1f84  r8 : eeb02c00
[   15.402369] r7 : edc69a10  r6 : eea6ac10  r5 : eea6ac10  r4 : 00000004
[   15.408878] r3 : 0001c12c  r2 : edcf7e8c  r1 : 00000004  r0 : ee914418
[   15.415390] Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
[   15.422506] Control: 10c5387d  Table: 6dd0804a  DAC: 00000015
[   15.428236] Process grep (pid: 1388, stack limit = 0xedcf6240)
[   15.434050] Stack: (0xedcf7e88 to 0xedcf8000)
[   15.438395] 7e80:                   ee03ad00 00000000 edcf7f80 eea6aca8 edcf7ec4 c033b7b0
[   15.446554] 7ea0: 00000001 ee1cc3f0 00000004 c06e1e44 eebdc000 c06e1e44 eeb02c00 c0337144
[   15.454713] 7ec0: ee2dac68 c005cffc ee1cc3c0 c06e1e44 00000fff 00001000 eebdc000 c0278ca8
[   15.462872] 7ee0: c0278c8c ee1cc3c0 eeb7ce00 c014422c edcf7f20 00008000 ee1cc3c0 ee9a48c0
[   15.471030] 7f00: 00000001 00000001 edcf7f80 c0142d94 c0142d70 c01060f4 00021000 ee1cc3f0
[   15.479190] 7f20: 00000000 00000000 c06a2150 eebdc000 2e7ec000 ee9a48c0 00008000 00021000
[   15.487349] 7f40: edcf7f80 00008000 edcf6000 00021000 00021000 c00e39a4 00000000 ee9a48c0
[   15.495508] 7f60: 00004000 00000000 00000000 ee9a48c0 ee9a48c0 00008000 00021000 c00e3aa0
[   15.503668] 7f80: 00000000 00000000 0001f2e0 0001f2e0 00021000 00001000 00000003 c000f364
[   15.511826] 7fa0: 00000000 c000f1a0 0001f2e0 00021000 00000003 00021000 00008000 00000000
[   15.519986] 7fc0: 0001f2e0 00021000 00001000 00000003 00000001 000205e8 00000000 00021000
[   15.528145] 7fe0: 00008000 bebbe910 0000a7ad b6edc49c 60000010 00000003 aaaaaaaa aaaaaaaa
[   15.536320] [<c0339fc4>] (is_ext_pwr_online) from [<c033b7b0>] (charger_get_property+0x170/0x314)
[   15.545164] [<c033b7b0>] (charger_get_property) from [<c0337144>] (power_supply_show_property+0x48/0x20c)
[   15.554719] [<c0337144>] (power_supply_show_property) from [<c0278ca8>] (dev_attr_show+0x1c/0x48)
[   15.563577] [<c0278ca8>] (dev_attr_show) from [<c014422c>] (sysfs_kf_seq_show+0x84/0x104)
[   15.571725] [<c014422c>] (sysfs_kf_seq_show) from [<c0142d94>] (kernfs_seq_show+0x24/0x28)
[   15.579973] [<c0142d94>] (kernfs_seq_show) from [<c01060f4>] (seq_read+0x1b0/0x484)
[   15.587614] [<c01060f4>] (seq_read) from [<c00e39a4>] (vfs_read+0x88/0x144)
[   15.594552] [<c00e39a4>] (vfs_read) from [<c00e3aa0>] (SyS_read+0x40/0x8c)
[   15.601417] [<c00e3aa0>] (SyS_read) from [<c000f1a0>] (ret_fast_syscall+0x0/0x48)
[   15.608877] Code: bad PC value
[   15.611991] ---[ end trace a88fcc95208db283 ]---

The charger-manager should get reference to charger power supply on
each use of get_property callback.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Cc: <stable@vger.kernel.org>
Fixes: 3bb3dbbd56 ("power_supply: Add initial Charger-Manager driver")
Signed-off-by: Sebastian Reichel <sre@kernel.org>
2014-10-28 03:30:21 +01:00

289 lines
8.6 KiB
C

/*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* MyungJoo.Ham <myungjoo.ham@samsung.com>
*
* Charger Manager.
* This framework enables to control and multiple chargers and to
* monitor charging even in the context of suspend-to-RAM with
* an interface combining the chargers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
**/
#ifndef _CHARGER_MANAGER_H
#define _CHARGER_MANAGER_H
#include <linux/power_supply.h>
#include <linux/extcon.h>
enum data_source {
CM_BATTERY_PRESENT,
CM_NO_BATTERY,
CM_FUEL_GAUGE,
CM_CHARGER_STAT,
};
enum polling_modes {
CM_POLL_DISABLE = 0,
CM_POLL_ALWAYS,
CM_POLL_EXTERNAL_POWER_ONLY,
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_BATT_OVERHEAT,
CM_EVENT_BATT_COLD,
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.
* @rtc_only_wakeup:
* If the system is woken up by waekup-sources other than the RTC or
* callbacks, Charger Manager should recognize with
* 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;
};
/**
* struct charger_cable
* @extcon_name: the name of extcon device.
* @name: the name of charger cable(external connector).
* @extcon_dev: the extcon device.
* @wq: the workqueue to control charger according to the state of
* charger cable. If charger cable is attached, enable charger.
* But if charger cable is detached, disable charger.
* @nb: the notifier block to receive changed state from EXTCON
* (External Connector) when charger cable is attached/detached.
* @attached: the state of charger cable.
* true: the charger cable is attached
* false: the charger cable is detached
* @charger: the instance of struct charger_regulator.
* @cm: the Charger Manager representing the battery.
*/
struct charger_cable {
const char *extcon_name;
const char *name;
/* The charger-manager use Exton framework*/
struct extcon_specific_cable_nb extcon_dev;
struct work_struct wq;
struct notifier_block nb;
/* The state of charger cable */
bool attached;
struct charger_regulator *charger;
/*
* Set min/max current of regulator to protect over-current issue
* according to a kind of charger cable when cable is attached.
*/
int min_uA;
int max_uA;
struct charger_manager *cm;
};
/**
* struct charger_regulator
* @regulator_name: the name of regulator for using charger.
* @consumer: the regulator consumer for the charger.
* @externally_control:
* Set if the charger-manager cannot control charger,
* the charger will be maintained with disabled state.
* @cables:
* the array of charger cables to enable/disable charger
* and set current limit according to constratint data of
* struct charger_cable if only charger cable included
* in the array of charger cables is attached/detached.
* @num_cables: the number of charger cables.
* @attr_g: Attribute group for the charger(regulator)
* @attr_name: "name" sysfs entry
* @attr_state: "state" sysfs entry
* @attr_externally_control: "externally_control" sysfs entry
* @attrs: Arrays pointing to attr_name/state/externally_control for attr_g
*/
struct charger_regulator {
/* The name of regulator for charging */
const char *regulator_name;
struct regulator *consumer;
/* charger never on when system is on */
int externally_control;
/*
* Store constraint information related to current limit,
* each cable have different condition for charging.
*/
struct charger_cable *cables;
int num_cables;
struct attribute_group attr_g;
struct device_attribute attr_name;
struct device_attribute attr_state;
struct device_attribute attr_externally_control;
struct attribute *attrs[4];
struct charger_manager *cm;
};
/**
* struct charger_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 VBATT >= fullbatt_uV, it is assumed to be full.
* @fullbatt_soc: state of Charge in %
* If state of Charge >= fullbatt_soc, it is assumed to be full.
* @fullbatt_full_capacity: full capacity measure
* If full capacity of battery >= fullbatt_full_capacity,
* it is assumed to be full.
* @polling_interval_ms: interval in millisecond at which
* charger manager will monitor battery health
* @battery_present:
* Specify where information for existance of battery can be obtained
* @psy_charger_stat: the names of power-supply for chargers
* @num_charger_regulator: the number of entries in charger_regulators
* @charger_regulators: array of charger regulators
* @psy_fuel_gauge: the name of power-supply for fuel gauge
* @thermal_zone : the name of thermal zone for battery
* @temp_min : Minimum battery temperature for charging.
* @temp_max : Maximum battery temperature for charging.
* @temp_diff : Temperature diffential to restart charging.
* @measure_battery_temp:
* true: measure battery temperature
* false: measure ambient temperature
* @charging_max_duration_ms: Maximum possible duration for charging
* If whole charging duration exceed 'charging_max_duration_ms',
* cm stop charging.
* @discharging_max_duration_ms:
* Maximum possible duration for discharging with charger cable
* after full-batt. If discharging duration exceed 'discharging
* max_duration_ms', cm start charging.
*/
struct charger_desc {
const char *psy_name;
enum polling_modes polling_mode;
unsigned int polling_interval_ms;
unsigned int fullbatt_vchkdrop_ms;
unsigned int fullbatt_vchkdrop_uV;
unsigned int fullbatt_uV;
unsigned int fullbatt_soc;
unsigned int fullbatt_full_capacity;
enum data_source battery_present;
const char **psy_charger_stat;
int num_charger_regulators;
struct charger_regulator *charger_regulators;
const char *psy_fuel_gauge;
const char *thermal_zone;
int temp_min;
int temp_max;
int temp_diff;
bool measure_battery_temp;
u32 charging_max_duration_ms;
u32 discharging_max_duration_ms;
};
#define PSY_NAME_MAX 30
/**
* struct charger_manager
* @entry: entry for list
* @dev: device pointer
* @desc: instance of charger_desc
* @fuel_gauge: power_supply for fuel gauge
* @charger_stat: array of power_supply for chargers
* @tzd_batt : thermal zone device for battery
* @charger_enabled: the state of charger
* @fullbatt_vchk_jiffies_at:
* jiffies at the time full battery check will occur.
* @fullbatt_vchk_work: work queue for full battery check
* @emergency_stop:
* When setting true, stop charging
* @psy_name_buf: the name of power-supply-class for charger manager
* @charger_psy: power_supply for charger manager
* @status_save_ext_pwr_inserted:
* saved status of external power before entering suspend-to-RAM
* @status_save_batt:
* saved status of battery before entering suspend-to-RAM
* @charging_start_time: saved start time of enabling charging
* @charging_end_time: saved end time of disabling charging
*/
struct charger_manager {
struct list_head entry;
struct device *dev;
struct charger_desc *desc;
#ifdef CONFIG_THERMAL
struct thermal_zone_device *tzd_batt;
#endif
bool charger_enabled;
unsigned long fullbatt_vchk_jiffies_at;
struct delayed_work fullbatt_vchk_work;
int emergency_stop;
char psy_name_buf[PSY_NAME_MAX + 1];
struct power_supply charger_psy;
bool status_save_ext_pwr_inserted;
bool status_save_batt;
u64 charging_start_time;
u64 charging_end_time;
};
#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 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 */