ab8500-fg: Add power cut feature for ab8505 and ab8540
Add support for a power cut feature which allows user to configure when ab8505 and ab8540 based platforms should shut down system due to low battery. Signed-off-by: Lee Jones <lee.jones@linaro.org>
This commit is contained in:
Родитель
330b7ebfa5
Коммит
93ff722e88
|
@ -113,6 +113,7 @@
|
||||||
#define AB8500_SWITCH_OFF_STATUS 0x00
|
#define AB8500_SWITCH_OFF_STATUS 0x00
|
||||||
|
|
||||||
#define AB8500_TURN_ON_STATUS 0x00
|
#define AB8500_TURN_ON_STATUS 0x00
|
||||||
|
#define AB8505_TURN_ON_STATUS_2 0x04
|
||||||
|
|
||||||
#define AB8500_CH_USBCH_STAT1_REG 0x02
|
#define AB8500_CH_USBCH_STAT1_REG 0x02
|
||||||
#define VBUS_DET_DBNC100 0x02
|
#define VBUS_DET_DBNC100 0x02
|
||||||
|
@ -1401,6 +1402,21 @@ static ssize_t show_turn_on_status(struct device *dev,
|
||||||
return sprintf(buf, "%#x\n", value);
|
return sprintf(buf, "%#x\n", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t show_turn_on_status_2(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 value;
|
||||||
|
struct ab8500 *ab8500;
|
||||||
|
|
||||||
|
ab8500 = dev_get_drvdata(dev);
|
||||||
|
ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
|
||||||
|
AB8505_TURN_ON_STATUS_2, &value);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
return sprintf(buf, "%#x\n", (value & 0x1));
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t show_ab9540_dbbrstn(struct device *dev,
|
static ssize_t show_ab9540_dbbrstn(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
|
@ -1457,6 +1473,7 @@ exit:
|
||||||
static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
|
static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
|
||||||
static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
|
static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
|
||||||
static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL);
|
static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL);
|
||||||
|
static DEVICE_ATTR(turn_on_status_2, S_IRUGO, show_turn_on_status_2, NULL);
|
||||||
static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR,
|
static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR,
|
||||||
show_ab9540_dbbrstn, store_ab9540_dbbrstn);
|
show_ab9540_dbbrstn, store_ab9540_dbbrstn);
|
||||||
|
|
||||||
|
@ -1467,6 +1484,11 @@ static struct attribute *ab8500_sysfs_entries[] = {
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct attribute *ab8505_sysfs_entries[] = {
|
||||||
|
&dev_attr_turn_on_status_2.attr,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
static struct attribute *ab9540_sysfs_entries[] = {
|
static struct attribute *ab9540_sysfs_entries[] = {
|
||||||
&dev_attr_chip_id.attr,
|
&dev_attr_chip_id.attr,
|
||||||
&dev_attr_switch_off_status.attr,
|
&dev_attr_switch_off_status.attr,
|
||||||
|
@ -1479,6 +1501,10 @@ static struct attribute_group ab8500_attr_group = {
|
||||||
.attrs = ab8500_sysfs_entries,
|
.attrs = ab8500_sysfs_entries,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct attribute_group ab8505_attr_group = {
|
||||||
|
.attrs = ab8505_sysfs_entries,
|
||||||
|
};
|
||||||
|
|
||||||
static struct attribute_group ab9540_attr_group = {
|
static struct attribute_group ab9540_attr_group = {
|
||||||
.attrs = ab9540_sysfs_entries,
|
.attrs = ab9540_sysfs_entries,
|
||||||
};
|
};
|
||||||
|
@ -1719,6 +1745,12 @@ static int ab8500_probe(struct platform_device *pdev)
|
||||||
else
|
else
|
||||||
ret = sysfs_create_group(&ab8500->dev->kobj,
|
ret = sysfs_create_group(&ab8500->dev->kobj,
|
||||||
&ab8500_attr_group);
|
&ab8500_attr_group);
|
||||||
|
|
||||||
|
if ((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
|
||||||
|
ab8500->chip_id >= AB8500_CUT2P0)
|
||||||
|
ret = sysfs_create_group(&ab8500->dev->kobj,
|
||||||
|
&ab8505_attr_group);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(ab8500->dev, "error creating sysfs entries\n");
|
dev_err(ab8500->dev, "error creating sysfs entries\n");
|
||||||
|
|
||||||
|
@ -1735,6 +1767,10 @@ static int ab8500_remove(struct platform_device *pdev)
|
||||||
else
|
else
|
||||||
sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
|
sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
|
||||||
|
|
||||||
|
if ((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
|
||||||
|
ab8500->chip_id >= AB8500_CUT2P0)
|
||||||
|
sysfs_remove_group(&ab8500->dev->kobj, &ab8505_attr_group);
|
||||||
|
|
||||||
mfd_remove_devices(ab8500->dev);
|
mfd_remove_devices(ab8500->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -407,6 +407,11 @@ static const struct abx500_fg_parameters fg = {
|
||||||
.battok_raising_th_sel1 = 2860,
|
.battok_raising_th_sel1 = 2860,
|
||||||
.maint_thres = 95,
|
.maint_thres = 95,
|
||||||
.user_cap_limit = 15,
|
.user_cap_limit = 15,
|
||||||
|
.pcut_enable = 1,
|
||||||
|
.pcut_max_time = 127,
|
||||||
|
.pcut_flag_time = 112,
|
||||||
|
.pcut_max_restart = 15,
|
||||||
|
.pcut_debounce_time = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct abx500_maxim_parameters maxi_params = {
|
static const struct abx500_maxim_parameters maxi_params = {
|
||||||
|
|
|
@ -2344,6 +2344,50 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
|
||||||
dev_err(di->dev, "BattOk init write failed.\n");
|
dev_err(di->dev, "BattOk init write failed.\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
|
||||||
|
abx500_get_chip_id(di->dev) >= AB8500_CUT2P0)
|
||||||
|
|| is_ab8540(di->parent)) {
|
||||||
|
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_MAX_TIME_REG, di->bm->fg_params->pcut_max_time);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__);
|
||||||
|
goto out;
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__);
|
||||||
|
goto out;
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__);
|
||||||
|
goto out;
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__);
|
||||||
|
goto out;
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__);
|
||||||
|
goto out;
|
||||||
|
};
|
||||||
|
}
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -2546,6 +2590,428 @@ static int ab8500_fg_sysfs_init(struct ab8500_fg *di)
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_flagtime_read(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_FLAG_TIME_REG, ®_value);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_flagtime_write(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
long unsigned reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
reg_value = simple_strtoul(buf, NULL, 10);
|
||||||
|
|
||||||
|
if (reg_value > 0x7F) {
|
||||||
|
dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n");
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_maxtime_read(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_MAX_TIME_REG, ®_value);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to read AB8505_RTC_PCUT_MAX_TIME_REG\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_maxtime_write(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
reg_value = simple_strtoul(buf, NULL, 10);
|
||||||
|
if (reg_value > 0x7F) {
|
||||||
|
dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_MAX_TIME_REG, (u8)reg_value);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(dev, "Failed to set AB8505_RTC_PCUT_MAX_TIME_REG\n");
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_restart_read(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_RESTART_REG, ®_value);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF));
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_restart_write(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
reg_value = simple_strtoul(buf, NULL, 10);
|
||||||
|
if (reg_value > 0xF) {
|
||||||
|
dev_err(dev, "Incorrect parameter, echo 0 - 15 for number of restart\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_RESTART_REG, (u8)reg_value);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(dev, "Failed to set AB8505_RTC_PCUT_RESTART_REG\n");
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return count;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_timer_read(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_TIME_REG, ®_value);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to read AB8505_RTC_PCUT_TIME_REG\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_restart_counter_read(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_RESTART_REG, ®_value);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF0) >> 4);
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_read(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x1));
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_write(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
reg_value = simple_strtoul(buf, NULL, 10);
|
||||||
|
if (reg_value > 0x1) {
|
||||||
|
dev_err(dev, "Incorrect parameter, echo 0/1 to disable/enable Pcut feature\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_CTL_STATUS_REG, (u8)reg_value);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(dev, "Failed to set AB8505_RTC_PCUT_CTL_STATUS_REG\n");
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_flag_read(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
u8 reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x10) >> 4));
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_debounce_read(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_DEBOUNCE_REG, ®_value);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to read AB8505_RTC_PCUT_DEBOUNCE_REG\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7));
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_debounce_write(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
reg_value = simple_strtoul(buf, NULL, 10);
|
||||||
|
if (reg_value > 0x7) {
|
||||||
|
dev_err(dev, "Incorrect parameter, echo 0 to 7 for debounce setting\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_DEBOUNCE_REG, (u8)reg_value);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(dev, "Failed to set AB8505_RTC_PCUT_DEBOUNCE_REG\n");
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ab8505_powercut_enable_status_read(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 reg_value;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||||
|
AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x20) >> 5));
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
|
||||||
|
__ATTR(powercut_flagtime, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||||
|
ab8505_powercut_flagtime_read, ab8505_powercut_flagtime_write),
|
||||||
|
__ATTR(powercut_maxtime, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||||
|
ab8505_powercut_maxtime_read, ab8505_powercut_maxtime_write),
|
||||||
|
__ATTR(powercut_restart_max, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||||
|
ab8505_powercut_restart_read, ab8505_powercut_restart_write),
|
||||||
|
__ATTR(powercut_timer, S_IRUGO, ab8505_powercut_timer_read, NULL),
|
||||||
|
__ATTR(powercut_restart_counter, S_IRUGO,
|
||||||
|
ab8505_powercut_restart_counter_read, NULL),
|
||||||
|
__ATTR(powercut_enable, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||||
|
ab8505_powercut_read, ab8505_powercut_write),
|
||||||
|
__ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL),
|
||||||
|
__ATTR(powercut_debounce_time, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||||
|
ab8505_powercut_debounce_read, ab8505_powercut_debounce_write),
|
||||||
|
__ATTR(powercut_enable_status, S_IRUGO,
|
||||||
|
ab8505_powercut_enable_status_read, NULL),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev)
|
||||||
|
{
|
||||||
|
unsigned int i, j;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
|
||||||
|
abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0)
|
||||||
|
|| is_ab8540(di->parent)) {
|
||||||
|
for (j = 0; j < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); j++)
|
||||||
|
if (device_create_file(dev, &ab8505_fg_sysfs_psy_attrs[j]))
|
||||||
|
goto sysfs_psy_create_attrs_failed_ab8505;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
sysfs_psy_create_attrs_failed_ab8505:
|
||||||
|
dev_err(dev, "Failed creating sysfs psy attrs for ab8505.\n");
|
||||||
|
while (j--)
|
||||||
|
device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]);
|
||||||
|
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ab8500_fg_sysfs_psy_remove_attrs(struct device *dev)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
struct ab8500_fg *di;
|
||||||
|
|
||||||
|
di = to_ab8500_fg_device_info(psy);
|
||||||
|
|
||||||
|
if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
|
||||||
|
abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0)
|
||||||
|
|| is_ab8540(di->parent)) {
|
||||||
|
for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++)
|
||||||
|
(void)device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Exposure to the sysfs interface <<END>> */
|
/* Exposure to the sysfs interface <<END>> */
|
||||||
|
|
||||||
#if defined(CONFIG_PM)
|
#if defined(CONFIG_PM)
|
||||||
|
@ -2607,6 +3073,7 @@ static int ab8500_fg_remove(struct platform_device *pdev)
|
||||||
ab8500_fg_sysfs_exit(di);
|
ab8500_fg_sysfs_exit(di);
|
||||||
|
|
||||||
flush_scheduled_work();
|
flush_scheduled_work();
|
||||||
|
ab8500_fg_sysfs_psy_remove_attrs(di->fg_psy.dev);
|
||||||
power_supply_unregister(&di->fg_psy);
|
power_supply_unregister(&di->fg_psy);
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2772,6 +3239,13 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||||
goto free_irq;
|
goto free_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = ab8500_fg_sysfs_psy_create_attrs(di->fg_psy.dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(di->dev, "failed to create FG psy\n");
|
||||||
|
ab8500_fg_sysfs_exit(di);
|
||||||
|
goto free_irq;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calibrate the fg first time */
|
/* Calibrate the fg first time */
|
||||||
di->flags.calibrate = true;
|
di->flags.calibrate = true;
|
||||||
di->calib_state = AB8500_FG_CALIB_INIT;
|
di->calib_state = AB8500_FG_CALIB_INIT;
|
||||||
|
|
|
@ -89,6 +89,11 @@ struct abx500_fg;
|
||||||
* points.
|
* points.
|
||||||
* @maint_thres This is the threshold where we stop reporting
|
* @maint_thres This is the threshold where we stop reporting
|
||||||
* battery full while in maintenance, in per cent
|
* battery full while in maintenance, in per cent
|
||||||
|
* @pcut_enable: Enable power cut feature in ab8505
|
||||||
|
* @pcut_max_time: Max time threshold
|
||||||
|
* @pcut_flag_time: Flagtime threshold
|
||||||
|
* @pcut_max_restart: Max number of restarts
|
||||||
|
* @pcut_debounce_time: Sets battery debounce time
|
||||||
*/
|
*/
|
||||||
struct abx500_fg_parameters {
|
struct abx500_fg_parameters {
|
||||||
int recovery_sleep_timer;
|
int recovery_sleep_timer;
|
||||||
|
@ -106,6 +111,11 @@ struct abx500_fg_parameters {
|
||||||
int battok_raising_th_sel1;
|
int battok_raising_th_sel1;
|
||||||
int user_cap_limit;
|
int user_cap_limit;
|
||||||
int maint_thres;
|
int maint_thres;
|
||||||
|
bool pcut_enable;
|
||||||
|
u8 pcut_max_time;
|
||||||
|
u8 pcut_flag_time;
|
||||||
|
u8 pcut_max_restart;
|
||||||
|
u8 pcut_debounce_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -235,6 +235,14 @@
|
||||||
/* Battery type */
|
/* Battery type */
|
||||||
#define BATTERY_UNKNOWN 00
|
#define BATTERY_UNKNOWN 00
|
||||||
|
|
||||||
|
/* Registers for pcut feature in ab8505 and ab9540 */
|
||||||
|
#define AB8505_RTC_PCUT_CTL_STATUS_REG 0x12
|
||||||
|
#define AB8505_RTC_PCUT_TIME_REG 0x13
|
||||||
|
#define AB8505_RTC_PCUT_MAX_TIME_REG 0x14
|
||||||
|
#define AB8505_RTC_PCUT_FLAG_TIME_REG 0x15
|
||||||
|
#define AB8505_RTC_PCUT_RESTART_REG 0x16
|
||||||
|
#define AB8505_RTC_PCUT_DEBOUNCE_REG 0x17
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct res_to_temp - defines one point in a temp to res curve. To
|
* struct res_to_temp - defines one point in a temp to res curve. To
|
||||||
* be used in battery packs that combines the identification resistor with a
|
* be used in battery packs that combines the identification resistor with a
|
||||||
|
@ -283,6 +291,11 @@ struct ab8500_fg;
|
||||||
* points.
|
* points.
|
||||||
* @maint_thres This is the threshold where we stop reporting
|
* @maint_thres This is the threshold where we stop reporting
|
||||||
* battery full while in maintenance, in per cent
|
* battery full while in maintenance, in per cent
|
||||||
|
* @pcut_enable: Enable power cut feature in ab8505
|
||||||
|
* @pcut_max_time: Max time threshold
|
||||||
|
* @pcut_flag_time: Flagtime threshold
|
||||||
|
* @pcut_max_restart: Max number of restarts
|
||||||
|
* @pcut_debunce_time: Sets battery debounce time
|
||||||
*/
|
*/
|
||||||
struct ab8500_fg_parameters {
|
struct ab8500_fg_parameters {
|
||||||
int recovery_sleep_timer;
|
int recovery_sleep_timer;
|
||||||
|
@ -299,6 +312,11 @@ struct ab8500_fg_parameters {
|
||||||
int battok_raising_th_sel1;
|
int battok_raising_th_sel1;
|
||||||
int user_cap_limit;
|
int user_cap_limit;
|
||||||
int maint_thres;
|
int maint_thres;
|
||||||
|
bool pcut_enable;
|
||||||
|
u8 pcut_max_time;
|
||||||
|
u8 pcut_flag_time;
|
||||||
|
u8 pcut_max_restart;
|
||||||
|
u8 pcut_debunce_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Загрузка…
Ссылка в новой задаче