mirror of https://gitee.com/openkylin/linux.git
abx500-chargalg: Add charging current step interface
To prevent overheating, provide differnt steps of charging current interface to allow thermal mitigation. This will provide possibility to reduce gradually the charging current. Signed-off-by: Lee Jones <lee.jones@linaro.org>
This commit is contained in:
parent
9b7f50e3ea
commit
4d3b4aa58a
|
@ -47,6 +47,9 @@
|
||||||
/* Plus margin for the low battery threshold */
|
/* Plus margin for the low battery threshold */
|
||||||
#define BAT_PLUS_MARGIN (100)
|
#define BAT_PLUS_MARGIN (100)
|
||||||
|
|
||||||
|
#define CHARGALG_CURR_STEP_LOW 0
|
||||||
|
#define CHARGALG_CURR_STEP_HIGH 100
|
||||||
|
|
||||||
#define to_abx500_chargalg_device_info(x) container_of((x), \
|
#define to_abx500_chargalg_device_info(x) container_of((x), \
|
||||||
struct abx500_chargalg, chargalg_psy);
|
struct abx500_chargalg, chargalg_psy);
|
||||||
|
|
||||||
|
@ -80,6 +83,11 @@ struct abx500_chargalg_suspension_status {
|
||||||
bool usb_suspended;
|
bool usb_suspended;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct abx500_chargalg_current_step_status {
|
||||||
|
bool curr_step_change;
|
||||||
|
int curr_step;
|
||||||
|
};
|
||||||
|
|
||||||
struct abx500_chargalg_battery_data {
|
struct abx500_chargalg_battery_data {
|
||||||
int temp;
|
int temp;
|
||||||
int volt;
|
int volt;
|
||||||
|
@ -220,6 +228,7 @@ enum maxim_ret {
|
||||||
* @batt_data: data of the battery
|
* @batt_data: data of the battery
|
||||||
* @susp_status: current charger suspension status
|
* @susp_status: current charger suspension status
|
||||||
* @bm: Platform specific battery management information
|
* @bm: Platform specific battery management information
|
||||||
|
* @curr_status: Current step status for over-current protection
|
||||||
* @parent: pointer to the struct abx500
|
* @parent: pointer to the struct abx500
|
||||||
* @chargalg_psy: structure that holds the battery properties exposed by
|
* @chargalg_psy: structure that holds the battery properties exposed by
|
||||||
* the charging algorithm
|
* the charging algorithm
|
||||||
|
@ -245,6 +254,7 @@ struct abx500_chargalg {
|
||||||
struct abx500_chargalg_battery_data batt_data;
|
struct abx500_chargalg_battery_data batt_data;
|
||||||
struct abx500_chargalg_suspension_status susp_status;
|
struct abx500_chargalg_suspension_status susp_status;
|
||||||
struct ab8500 *parent;
|
struct ab8500 *parent;
|
||||||
|
struct abx500_chargalg_current_step_status curr_status;
|
||||||
struct abx500_bm_data *bm;
|
struct abx500_bm_data *bm;
|
||||||
struct power_supply chargalg_psy;
|
struct power_supply chargalg_psy;
|
||||||
struct ux500_charger *ac_chg;
|
struct ux500_charger *ac_chg;
|
||||||
|
@ -268,6 +278,12 @@ static enum power_supply_property abx500_chargalg_props[] = {
|
||||||
POWER_SUPPLY_PROP_HEALTH,
|
POWER_SUPPLY_PROP_HEALTH,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct abx500_chargalg_sysfs_entry {
|
||||||
|
struct attribute attr;
|
||||||
|
ssize_t (*show)(struct abx500_chargalg *, char *);
|
||||||
|
ssize_t (*store)(struct abx500_chargalg *, const char *, size_t);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
|
* abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
|
||||||
* @timer: pointer to the hrtimer structure
|
* @timer: pointer to the hrtimer structure
|
||||||
|
@ -401,6 +417,22 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
|
||||||
return di->chg_info.conn_chg;
|
return di->chg_info.conn_chg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* abx500_chargalg_check_current_step_status() - Check charging current
|
||||||
|
* step status.
|
||||||
|
* @di: pointer to the abx500_chargalg structure
|
||||||
|
*
|
||||||
|
* This function will check if there is a change in the charging current step
|
||||||
|
* and change charge state accordingly.
|
||||||
|
*/
|
||||||
|
static void abx500_chargalg_check_current_step_status
|
||||||
|
(struct abx500_chargalg *di)
|
||||||
|
{
|
||||||
|
if (di->curr_status.curr_step_change)
|
||||||
|
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
|
||||||
|
di->curr_status.curr_step_change = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* abx500_chargalg_start_safety_timer() - Start charging safety timer
|
* abx500_chargalg_start_safety_timer() - Start charging safety timer
|
||||||
* @di: pointer to the abx500_chargalg structure
|
* @di: pointer to the abx500_chargalg structure
|
||||||
|
@ -1300,6 +1332,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
||||||
{
|
{
|
||||||
int charger_status;
|
int charger_status;
|
||||||
int ret;
|
int ret;
|
||||||
|
int curr_step_lvl;
|
||||||
|
|
||||||
/* Collect data from all power_supply class devices */
|
/* Collect data from all power_supply class devices */
|
||||||
class_for_each_device(power_supply_class, NULL,
|
class_for_each_device(power_supply_class, NULL,
|
||||||
|
@ -1310,6 +1343,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
||||||
abx500_chargalg_check_charger_voltage(di);
|
abx500_chargalg_check_charger_voltage(di);
|
||||||
|
|
||||||
charger_status = abx500_chargalg_check_charger_connection(di);
|
charger_status = abx500_chargalg_check_charger_connection(di);
|
||||||
|
abx500_chargalg_check_current_step_status(di);
|
||||||
|
|
||||||
if (is_ab8500(di->parent)) {
|
if (is_ab8500(di->parent)) {
|
||||||
ret = abx500_chargalg_check_charger_enable(di);
|
ret = abx500_chargalg_check_charger_enable(di);
|
||||||
|
@ -1523,9 +1557,18 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abx500_chargalg_start_charging(di,
|
if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW)
|
||||||
di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
|
abx500_chargalg_stop_charging(di);
|
||||||
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
|
else {
|
||||||
|
curr_step_lvl = di->bm->bat_type[
|
||||||
|
di->bm->batt_id].normal_cur_lvl
|
||||||
|
* di->curr_status.curr_step
|
||||||
|
/ CHARGALG_CURR_STEP_HIGH;
|
||||||
|
abx500_chargalg_start_charging(di,
|
||||||
|
di->bm->bat_type[di->bm->batt_id]
|
||||||
|
.normal_vol_lvl, curr_step_lvl);
|
||||||
|
}
|
||||||
|
|
||||||
abx500_chargalg_state_to(di, STATE_NORMAL);
|
abx500_chargalg_state_to(di, STATE_NORMAL);
|
||||||
abx500_chargalg_start_safety_timer(di);
|
abx500_chargalg_start_safety_timer(di);
|
||||||
abx500_chargalg_stop_maintenance_timer(di);
|
abx500_chargalg_stop_maintenance_timer(di);
|
||||||
|
@ -1767,99 +1810,134 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
|
||||||
|
|
||||||
/* Exposure to the sysfs interface */
|
/* Exposure to the sysfs interface */
|
||||||
|
|
||||||
/**
|
static ssize_t abx500_chargalg_curr_step_show(struct abx500_chargalg *di,
|
||||||
* abx500_chargalg_sysfs_show() - sysfs show operations
|
char *buf)
|
||||||
* @kobj: pointer to the struct kobject
|
|
||||||
* @attr: pointer to the struct attribute
|
|
||||||
* @buf: buffer that holds the parameter to send to userspace
|
|
||||||
*
|
|
||||||
* Returns a buffer to be displayed in user space
|
|
||||||
*/
|
|
||||||
static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
|
|
||||||
struct attribute *attr, char *buf)
|
|
||||||
{
|
{
|
||||||
struct abx500_chargalg *di = container_of(kobj,
|
return sprintf(buf, "%d\n", di->curr_status.curr_step);
|
||||||
struct abx500_chargalg, chargalg_kobject);
|
}
|
||||||
|
|
||||||
|
static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di,
|
||||||
|
const char *buf, size_t length)
|
||||||
|
{
|
||||||
|
long int param;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtol(buf, 10, ¶m);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
di->curr_status.curr_step = param;
|
||||||
|
if (di->curr_status.curr_step >= CHARGALG_CURR_STEP_LOW &&
|
||||||
|
di->curr_status.curr_step <= CHARGALG_CURR_STEP_HIGH) {
|
||||||
|
di->curr_status.curr_step_change = true;
|
||||||
|
queue_work(di->chargalg_wq, &di->chargalg_work);
|
||||||
|
} else
|
||||||
|
dev_info(di->dev, "Wrong current step\n"
|
||||||
|
"Enter 0. Disable AC/USB Charging\n"
|
||||||
|
"1--100. Set AC/USB charging current step\n"
|
||||||
|
"100. Enable AC/USB Charging\n");
|
||||||
|
|
||||||
|
return strlen(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
return sprintf(buf, "%d\n",
|
return sprintf(buf, "%d\n",
|
||||||
di->susp_status.ac_suspended &&
|
di->susp_status.ac_suspended &&
|
||||||
di->susp_status.usb_suspended);
|
di->susp_status.usb_suspended);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
|
||||||
* abx500_chargalg_sysfs_charger() - sysfs store operations
|
const char *buf, size_t length)
|
||||||
* @kobj: pointer to the struct kobject
|
|
||||||
* @attr: pointer to the struct attribute
|
|
||||||
* @buf: buffer that holds the parameter passed from userspace
|
|
||||||
* @length: length of the parameter passed
|
|
||||||
*
|
|
||||||
* Returns length of the buffer(input taken from user space) on success
|
|
||||||
* else error code on failure
|
|
||||||
* The operation to be performed on passing the parameters from the user space.
|
|
||||||
*/
|
|
||||||
static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
|
|
||||||
struct attribute *attr, const char *buf, size_t length)
|
|
||||||
{
|
{
|
||||||
struct abx500_chargalg *di = container_of(kobj,
|
|
||||||
struct abx500_chargalg, chargalg_kobject);
|
|
||||||
long int param;
|
long int param;
|
||||||
int ac_usb;
|
int ac_usb;
|
||||||
int ret;
|
int ret;
|
||||||
char entry = *attr->name;
|
|
||||||
|
|
||||||
switch (entry) {
|
ret = kstrtol(buf, 10, ¶m);
|
||||||
case 'c':
|
if (ret < 0)
|
||||||
ret = strict_strtol(buf, 10, ¶m);
|
return ret;
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ac_usb = param;
|
ac_usb = param;
|
||||||
switch (ac_usb) {
|
switch (ac_usb) {
|
||||||
case 0:
|
case 0:
|
||||||
/* Disable charging */
|
/* Disable charging */
|
||||||
di->susp_status.ac_suspended = true;
|
di->susp_status.ac_suspended = true;
|
||||||
di->susp_status.usb_suspended = true;
|
di->susp_status.usb_suspended = true;
|
||||||
di->susp_status.suspended_change = true;
|
di->susp_status.suspended_change = true;
|
||||||
/* Trigger a state change */
|
/* Trigger a state change */
|
||||||
queue_work(di->chargalg_wq,
|
queue_work(di->chargalg_wq,
|
||||||
&di->chargalg_work);
|
&di->chargalg_work);
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/* Enable AC Charging */
|
|
||||||
di->susp_status.ac_suspended = false;
|
|
||||||
di->susp_status.suspended_change = true;
|
|
||||||
/* Trigger a state change */
|
|
||||||
queue_work(di->chargalg_wq,
|
|
||||||
&di->chargalg_work);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
/* Enable USB charging */
|
|
||||||
di->susp_status.usb_suspended = false;
|
|
||||||
di->susp_status.suspended_change = true;
|
|
||||||
/* Trigger a state change */
|
|
||||||
queue_work(di->chargalg_wq,
|
|
||||||
&di->chargalg_work);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dev_info(di->dev, "Wrong input\n"
|
|
||||||
"Enter 0. Disable AC/USB Charging\n"
|
|
||||||
"1. Enable AC charging\n"
|
|
||||||
"2. Enable USB Charging\n");
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
|
case 1:
|
||||||
|
/* Enable AC Charging */
|
||||||
|
di->susp_status.ac_suspended = false;
|
||||||
|
di->susp_status.suspended_change = true;
|
||||||
|
/* Trigger a state change */
|
||||||
|
queue_work(di->chargalg_wq,
|
||||||
|
&di->chargalg_work);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* Enable USB charging */
|
||||||
|
di->susp_status.usb_suspended = false;
|
||||||
|
di->susp_status.suspended_change = true;
|
||||||
|
/* Trigger a state change */
|
||||||
|
queue_work(di->chargalg_wq,
|
||||||
|
&di->chargalg_work);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_info(di->dev, "Wrong input\n"
|
||||||
|
"Enter 0. Disable AC/USB Charging\n"
|
||||||
|
"1. Enable AC charging\n"
|
||||||
|
"2. Enable USB Charging\n");
|
||||||
};
|
};
|
||||||
return strlen(buf);
|
return strlen(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct attribute abx500_chargalg_en_charger = \
|
static struct abx500_chargalg_sysfs_entry abx500_chargalg_en_charger =
|
||||||
|
__ATTR(chargalg, 0644, abx500_chargalg_en_show,
|
||||||
|
abx500_chargalg_en_store);
|
||||||
|
|
||||||
|
static struct abx500_chargalg_sysfs_entry abx500_chargalg_curr_step =
|
||||||
|
__ATTR(chargalg_curr_step, 0644, abx500_chargalg_curr_step_show,
|
||||||
|
abx500_chargalg_curr_step_store);
|
||||||
|
|
||||||
|
static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
|
||||||
|
struct attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
.name = "chargalg",
|
struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
|
||||||
.mode = S_IRUGO | S_IWUSR,
|
struct abx500_chargalg_sysfs_entry, attr);
|
||||||
};
|
|
||||||
|
struct abx500_chargalg *di = container_of(kobj,
|
||||||
|
struct abx500_chargalg, chargalg_kobject);
|
||||||
|
|
||||||
|
if (!entry->show)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
return entry->show(di, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
|
||||||
|
struct attribute *attr, const char *buf, size_t length)
|
||||||
|
{
|
||||||
|
struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
|
||||||
|
struct abx500_chargalg_sysfs_entry, attr);
|
||||||
|
|
||||||
|
struct abx500_chargalg *di = container_of(kobj,
|
||||||
|
struct abx500_chargalg, chargalg_kobject);
|
||||||
|
|
||||||
|
if (!entry->store)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
return entry->store(di, buf, length);
|
||||||
|
}
|
||||||
|
|
||||||
static struct attribute *abx500_chargalg_chg[] = {
|
static struct attribute *abx500_chargalg_chg[] = {
|
||||||
&abx500_chargalg_en_charger,
|
&abx500_chargalg_en_charger.attr,
|
||||||
NULL
|
&abx500_chargalg_curr_step.attr,
|
||||||
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
|
static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
|
||||||
|
@ -2052,6 +2130,7 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
|
||||||
dev_err(di->dev, "failed to create sysfs entry\n");
|
dev_err(di->dev, "failed to create sysfs entry\n");
|
||||||
goto free_psy;
|
goto free_psy;
|
||||||
}
|
}
|
||||||
|
di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH;
|
||||||
|
|
||||||
/* Run the charging algorithm */
|
/* Run the charging algorithm */
|
||||||
queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
|
queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
|
||||||
|
|
Loading…
Reference in New Issue