// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2011 Samsung Electronics Co., Ltd. * MyungJoo Ham <[email protected]> * * This driver enables to monitor battery health and control charger * during suspend-to-mem. * Charger manager depends on other devices. Register this later than * the depending devices. * **/ #define pr_fmt(fmt) … #include <linux/io.h> #include <linux/module.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/rtc.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/platform_device.h> #include <linux/power/charger-manager.h> #include <linux/regulator/consumer.h> #include <linux/sysfs.h> #include <linux/of.h> #include <linux/thermal.h> static struct { … } extcon_mapping[] = …; /* * Default temperature threshold for charging. * Every temperature units are in tenth of centigrade. */ #define CM_DEFAULT_RECHARGE_TEMP_DIFF … #define CM_DEFAULT_CHARGE_TEMP_MAX … /* * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for * delayed works so that we can run delayed works with CM_JIFFIES_SMALL * without any delays. */ #define CM_JIFFIES_SMALL … /* If y is valid (> 0) and smaller than x, do x = y */ #define CM_MIN_VALID(x, y) … /* * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking * rtc alarm. It should be 2 or larger */ #define CM_RTC_SMALL … static LIST_HEAD(cm_list); static DEFINE_MUTEX(cm_list_mtx); /* About in-suspend (suspend-again) monitoring */ static struct alarm *cm_timer; static bool cm_suspended; static bool cm_timer_set; static unsigned long cm_suspend_duration_ms; /* About normal (not suspended) monitoring */ static unsigned long polling_jiffy = …; /* 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 */ /** * is_batt_present - See if the battery presents in place. * @cm: the Charger Manager representing the battery. */ static bool is_batt_present(struct charger_manager *cm) { … } /** * is_ext_pwr_online - See if an external power source is attached to charge * @cm: the Charger Manager representing the battery. * * Returns true if at least one of the chargers of the battery has an external * power source attached to charge the battery regardless of whether it is * actually charging or not. */ static bool is_ext_pwr_online(struct charger_manager *cm) { … } /** * get_batt_uV - Get the voltage level of the battery * @cm: the Charger Manager representing the battery. * @uV: the voltage level returned. * * Returns 0 if there is no error. * Returns a negative value on error. */ static int get_batt_uV(struct charger_manager *cm, int *uV) { … } /** * is_charging - Returns true if the battery is being charged. * @cm: the Charger Manager representing the battery. */ static bool is_charging(struct charger_manager *cm) { … } /** * is_full_charged - Returns true if the battery is fully charged. * @cm: the Charger Manager representing the battery. */ static bool is_full_charged(struct charger_manager *cm) { … } /** * is_polling_required - Return true if need to continue polling for this CM. * @cm: the Charger Manager representing the battery. */ static bool is_polling_required(struct charger_manager *cm) { … } /** * try_charger_enable - Enable/Disable chargers altogether * @cm: the Charger Manager representing the battery. * @enable: true: enable / false: disable * * Note that Charger Manager keeps the charger enabled regardless whether * the charger is charging or not (because battery is full or no external * power source exists) except when CM needs to disable chargers forcibly * because of emergency causes; when the battery is overheated or too cold. */ static int try_charger_enable(struct charger_manager *cm, bool enable) { … } /** * check_charging_duration - Monitor charging/discharging duration * @cm: the Charger Manager representing the battery. * * If whole charging duration exceed 'charging_max_duration_ms', * cm stop charging to prevent overcharge/overheat. If discharging * duration exceed 'discharging _max_duration_ms', charger cable is * attached, after full-batt, cm start charging to maintain fully * charged state for battery. */ static int check_charging_duration(struct charger_manager *cm) { … } static int cm_get_battery_temperature_by_psy(struct charger_manager *cm, int *temp) { … } static int cm_get_battery_temperature(struct charger_manager *cm, int *temp) { … } static int cm_check_thermal_status(struct charger_manager *cm) { … } /** * cm_get_target_status - Check current status and get next target status. * @cm: the Charger Manager representing the battery. */ static int cm_get_target_status(struct charger_manager *cm) { … } /** * _cm_monitor - Monitor the temperature and return true for exceptions. * @cm: the Charger Manager representing the battery. * * Returns true if there is an event to notify for the battery. * (True if the status of "emergency_stop" changes) */ static bool _cm_monitor(struct charger_manager *cm) { … } /** * cm_monitor - Monitor every battery. * * Returns true if there is an event to notify from any of the batteries. * (True if the status of "emergency_stop" changes) */ static bool cm_monitor(void) { … } /** * _setup_polling - Setup the next instance of polling. * @work: work_struct of the function _setup_polling. */ static void _setup_polling(struct work_struct *work) { … } 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) { … } static int charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { … } #define NUM_CHARGER_PSY_OPTIONAL … static enum power_supply_property default_charger_props[] = …; static const struct power_supply_desc psy_default = …; /** * cm_setup_timer - For in-suspend monitoring setup wakeup alarm * for suspend_again. * * Returns true if the alarm is set for Charger Manager to use. * Returns false if * cm_setup_timer fails to set an alarm, * cm_setup_timer does not need to set an alarm for Charger Manager, * or an alarm previously configured is to be used. */ static bool cm_setup_timer(void) { … } /** * charger_extcon_work - enable/diable charger according to the state * of charger cable * * @work: work_struct of the function charger_extcon_work. */ static void charger_extcon_work(struct work_struct *work) { … } /** * charger_extcon_notifier - receive the state of charger cable * when registered cable is attached or detached. * * @self: the notifier block of the charger_extcon_notifier. * @event: the cable state. * @ptr: the data pointer of notifier block. */ static int charger_extcon_notifier(struct notifier_block *self, unsigned long event, void *ptr) { … } /** * charger_extcon_init - register external connector to use it * as the charger cable * * @cm: the Charger Manager representing the battery. * @cable: the Charger cable representing the external connector. */ static int charger_extcon_init(struct charger_manager *cm, struct charger_cable *cable) { … } /** * charger_manager_register_extcon - Register extcon device to receive state * of charger cable. * @cm: the Charger Manager representing the battery. * * This function support EXTCON(External Connector) subsystem to detect the * state of charger cables for enabling or disabling charger(regulator) and * select the charger cable for charging among a number of external cable * according to policy of H/W board. */ static int charger_manager_register_extcon(struct charger_manager *cm) { … } /* help function of sysfs node to control charger(regulator) */ static ssize_t charger_name_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t charger_state_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t charger_externally_control_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t charger_externally_control_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } /** * charger_manager_prepare_sysfs - Prepare sysfs entry for each charger * @cm: the Charger Manager representing the battery. * * This function add sysfs entry for charger(regulator) to control charger from * user-space. If some development board use one more chargers for charging * but only need one charger on specific case which is dependent on user * scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/ * class/power_supply/battery/charger.[index]/externally_control'. For example, * if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/ * externally_control, this charger isn't controlled from charger-manager and * always stay off state of regulator. */ static int charger_manager_prepare_sysfs(struct charger_manager *cm) { … } static int cm_init_thermal_data(struct charger_manager *cm, struct power_supply *fuel_gauge, enum power_supply_property *properties, size_t *num_properties) { … } static const struct of_device_id charger_manager_match[] = …; MODULE_DEVICE_TABLE(of, charger_manager_match); static struct charger_desc *of_cm_parse_desc(struct device *dev) { … } static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev) { … } static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now) { … } static int charger_manager_probe(struct platform_device *pdev) { … } static void charger_manager_remove(struct platform_device *pdev) { … } static const struct platform_device_id charger_manager_id[] = …; MODULE_DEVICE_TABLE(platform, charger_manager_id); static int cm_suspend_noirq(struct device *dev) { … } static bool cm_need_to_awake(void) { … } static int cm_suspend_prepare(struct device *dev) { … } static void cm_suspend_complete(struct device *dev) { … } static const struct dev_pm_ops charger_manager_pm = …; static struct platform_driver charger_manager_driver = …; static int __init charger_manager_init(void) { … } late_initcall(charger_manager_init); static void __exit charger_manager_cleanup(void) { … } module_exit(charger_manager_cleanup); MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;