From 244b2c5ca8b14627e4a17755e5922221e121c771 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Wed, 09 Oct 2024 06:15:07 +0000
Subject: [PATCH] change system file
---
kernel/drivers/power/supply/cw2015_battery.c | 1118 ++++++++++++++++++++++++----------------------------------
1 files changed, 468 insertions(+), 650 deletions(-)
diff --git a/kernel/drivers/power/supply/cw2015_battery.c b/kernel/drivers/power/supply/cw2015_battery.c
index 068e9a2..7b8268b 100644
--- a/kernel/drivers/power/supply/cw2015_battery.c
+++ b/kernel/drivers/power/supply/cw2015_battery.c
@@ -1,554 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Fuel gauge driver for CellWise 2013 / 2015
*
* Copyright (C) 2012, RockChip
+ * Copyright (C) 2020, Tobias Schramm
*
* Authors: xuhuicong <xhc@rock-chips.com>
- *
- * 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.
- *
+ * Authors: Tobias Schramm <t.schramm@manjaro.org>
*/
+#include <linux/bits.h>
#include <linux/delay.h>
-#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/gfp.h>
+#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
-#include <linux/platform_device.h>
#include <linux/power_supply.h>
-#include <linux/slab.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
#include <linux/workqueue.h>
-#include <linux/power/cw2015_battery.h>
+#define CW2015_SIZE_BATINFO 64
-static int dbg_enable;
-module_param_named(dbg_level, dbg_enable, int, 0644);
+#define CW2015_RESET_TRIES 5
-#define cw_printk(args...) \
- do { \
- if (dbg_enable) { \
- pr_info(args); \
- } \
- } while (0)
+#define CW2015_REG_VERSION 0x00
+#define CW2015_REG_VCELL 0x02
+#define CW2015_REG_SOC 0x04
+#define CW2015_REG_RRT_ALERT 0x06
+#define CW2015_REG_CONFIG 0x08
+#define CW2015_REG_MODE 0x0A
+#define CW2015_REG_BATINFO 0x10
-static int cw_read(struct i2c_client *client, u8 reg, u8 buf[])
+#define CW2015_MODE_SLEEP_MASK GENMASK(7, 6)
+#define CW2015_MODE_SLEEP (0x03 << 6)
+#define CW2015_MODE_NORMAL (0x00 << 6)
+#define CW2015_MODE_QUICK_START (0x03 << 4)
+#define CW2015_MODE_RESTART (0x0f << 0)
+
+#define CW2015_CONFIG_UPDATE_FLG (0x01 << 1)
+#define CW2015_ATHD(x) ((x) << 3)
+#define CW2015_MASK_ATHD GENMASK(7, 3)
+#define CW2015_MASK_SOC GENMASK(12, 0)
+
+/* reset gauge of no valid state of charge could be polled for 40s */
+#define CW2015_BAT_SOC_ERROR_MS (40 * MSEC_PER_SEC)
+/* reset gauge if state of charge stuck for half an hour during charging */
+#define CW2015_BAT_CHARGING_STUCK_MS (1800 * MSEC_PER_SEC)
+
+/* poll interval from CellWise GPL Android driver example */
+#define CW2015_DEFAULT_POLL_INTERVAL_MS 8000
+
+#define CW2015_AVERAGING_SAMPLES 3
+
+struct cw_battery {
+ struct device *dev;
+ struct workqueue_struct *battery_workqueue;
+ struct delayed_work battery_delay_work;
+ struct regmap *regmap;
+ struct power_supply *rk_bat;
+ struct power_supply_battery_info battery;
+ u8 *bat_profile;
+
+ bool charger_attached;
+ bool battery_changed;
+
+ int soc;
+ int voltage_mv;
+ int status;
+ int time_to_empty;
+ int charge_count;
+
+ u32 poll_interval_ms;
+ u8 alert_level;
+
+ bool dual_cell;
+
+ unsigned int read_errors;
+ unsigned int charge_stuck_cnt;
+};
+
+static int cw_read_word(struct cw_battery *cw_bat, u8 reg, u16 *val)
{
- return i2c_smbus_read_i2c_block_data(client, reg, 1, buf);
+ __be16 value;
+ int ret;
+
+ ret = regmap_bulk_read(cw_bat->regmap, reg, &value, sizeof(value));
+ if (ret)
+ return ret;
+
+ *val = be16_to_cpu(value);
+ return 0;
}
-static int cw_write(struct i2c_client *client, u8 reg, u8 const buf[])
-{
- return i2c_smbus_write_i2c_block_data(client, reg, 1, &buf[0]);
-}
-
-static int cw_read_word(struct i2c_client *client, u8 reg, u8 buf[])
-{
- return i2c_smbus_read_i2c_block_data(client, reg, 2, buf);
-}
-
-int cw_update_config_info(struct cw_battery *cw_bat)
+static int cw_update_profile(struct cw_battery *cw_bat)
{
int ret;
- u8 reg_val;
- u8 i;
+ unsigned int reg_val;
u8 reset_val;
- cw_printk("[FGADC] test config_info = 0x%x\n",
- cw_bat->plat_data.cw_bat_config_info[0]);
-
- /* make sure no in sleep mode */
- ret = cw_read(cw_bat->client, REG_MODE, ®_val);
- if (ret < 0)
+ /* make sure gauge is not in sleep mode */
+ ret = regmap_read(cw_bat->regmap, CW2015_REG_MODE, ®_val);
+ if (ret)
return ret;
reset_val = reg_val;
- if ((reg_val & MODE_SLEEP_MASK) == MODE_SLEEP) {
- dev_err(&cw_bat->client->dev,
- "device in sleep mode, cannot update battery info\n");
- return -1;
+ if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) {
+ dev_err(cw_bat->dev,
+ "Gauge is in sleep mode, can't update battery info\n");
+ return -EINVAL;
}
- /* update new battery info */
- for (i = 0; i < SIZE_BATINFO; i++) {
- ret =
- cw_write(cw_bat->client, REG_BATINFO + i,
- (u8 *)&cw_bat->plat_data.cw_bat_config_info[i]);
-
- if (ret < 0)
- return ret;
- }
-
- reg_val |= CONFIG_UPDATE_FLG; /* set UPDATE_FLAG */
- reg_val &= 0x07; /* clear ATHD */
- reg_val |= ATHD; /* set ATHD */
- ret = cw_write(cw_bat->client, REG_CONFIG, ®_val);
- if (ret < 0)
+ /* write new battery info */
+ ret = regmap_raw_write(cw_bat->regmap, CW2015_REG_BATINFO,
+ cw_bat->bat_profile,
+ CW2015_SIZE_BATINFO);
+ if (ret)
return ret;
- /* check 2015/cw2013 for ATHD & update_flag */
- ret = cw_read(cw_bat->client, REG_CONFIG, ®_val);
- if (ret < 0)
+ /* set config update flag */
+ reg_val |= CW2015_CONFIG_UPDATE_FLG;
+ reg_val &= ~CW2015_MASK_ATHD;
+ reg_val |= CW2015_ATHD(cw_bat->alert_level);
+ ret = regmap_write(cw_bat->regmap, CW2015_REG_CONFIG, reg_val);
+ if (ret)
return ret;
- if (!(reg_val & CONFIG_UPDATE_FLG)) {
- dev_info(&cw_bat->client->dev,
- "update flag for new battery info have not set..\n");
- }
-
- if ((reg_val & 0xf8) != ATHD)
- dev_info(&cw_bat->client->dev, "the new ATHD have not set..\n");
-
- /* reset */
- reset_val &= ~(MODE_RESTART);
- reg_val = reset_val | MODE_RESTART;
- ret = cw_write(cw_bat->client, REG_MODE, ®_val);
- if (ret < 0)
+ /* reset gauge to apply new battery profile */
+ reset_val &= ~CW2015_MODE_RESTART;
+ reg_val = reset_val | CW2015_MODE_RESTART;
+ ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reg_val);
+ if (ret)
return ret;
- msleep(10);
- ret = cw_write(cw_bat->client, REG_MODE, &reset_val);
- if (ret < 0)
+ /* wait for gauge to reset */
+ msleep(20);
+
+ /* clear reset flag */
+ ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reset_val);
+ if (ret)
return ret;
- cw_printk("cw2015 update config success!\n");
+ /* wait for gauge to become ready */
+ ret = regmap_read_poll_timeout(cw_bat->regmap, CW2015_REG_SOC,
+ reg_val, reg_val <= 100,
+ 10 * USEC_PER_MSEC, 10 * USEC_PER_SEC);
+ if (ret)
+ dev_err(cw_bat->dev,
+ "Gauge did not become ready after profile upload\n");
+ else
+ dev_dbg(cw_bat->dev, "Battery profile updated\n");
- return 0;
+ return ret;
}
static int cw_init(struct cw_battery *cw_bat)
{
int ret;
- int i;
- u8 reg_val = MODE_SLEEP;
+ unsigned int reg_val = CW2015_MODE_SLEEP;
- if ((reg_val & MODE_SLEEP_MASK) == MODE_SLEEP) {
- reg_val = MODE_NORMAL;
- ret = cw_write(cw_bat->client, REG_MODE, ®_val);
- if (ret < 0)
+ if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) {
+ reg_val = CW2015_MODE_NORMAL;
+ ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reg_val);
+ if (ret)
return ret;
}
- ret = cw_read(cw_bat->client, REG_CONFIG, ®_val);
- if (ret < 0)
+ ret = regmap_read(cw_bat->regmap, CW2015_REG_CONFIG, ®_val);
+ if (ret)
return ret;
- if ((reg_val & 0xf8) != ATHD) {
- dev_info(&cw_bat->client->dev, "the new ATHD have not set\n");
- reg_val &= 0x07; /* clear ATHD */
- reg_val |= ATHD; /* set ATHD */
- ret = cw_write(cw_bat->client, REG_CONFIG, ®_val);
- if (ret < 0)
+ if ((reg_val & CW2015_MASK_ATHD) != CW2015_ATHD(cw_bat->alert_level)) {
+ dev_dbg(cw_bat->dev, "Setting new alert level\n");
+ reg_val &= ~CW2015_MASK_ATHD;
+ reg_val |= ~CW2015_ATHD(cw_bat->alert_level);
+ ret = regmap_write(cw_bat->regmap, CW2015_REG_CONFIG, reg_val);
+ if (ret)
return ret;
}
- ret = cw_read(cw_bat->client, REG_CONFIG, ®_val);
- if (ret < 0)
+ ret = regmap_read(cw_bat->regmap, CW2015_REG_CONFIG, ®_val);
+ if (ret)
return ret;
- if (!(reg_val & CONFIG_UPDATE_FLG)) {
- cw_printk("update config flg is true, need update config\n");
- ret = cw_update_config_info(cw_bat);
- if (ret < 0) {
- dev_info(&cw_bat->client->dev,
- "update flag for new battery info have not set\n");
+ if (!(reg_val & CW2015_CONFIG_UPDATE_FLG)) {
+ dev_dbg(cw_bat->dev,
+ "Battery profile not present, uploading battery profile\n");
+ if (cw_bat->bat_profile) {
+ ret = cw_update_profile(cw_bat);
+ if (ret) {
+ dev_err(cw_bat->dev,
+ "Failed to upload battery profile\n");
+ return ret;
+ }
+ } else {
+ dev_warn(cw_bat->dev,
+ "No profile specified, continuing without profile\n");
+ }
+ } else if (cw_bat->bat_profile) {
+ u8 bat_info[CW2015_SIZE_BATINFO];
+
+ ret = regmap_raw_read(cw_bat->regmap, CW2015_REG_BATINFO,
+ bat_info, CW2015_SIZE_BATINFO);
+ if (ret) {
+ dev_err(cw_bat->dev,
+ "Failed to read stored battery profile\n");
return ret;
+ }
+
+ if (memcmp(bat_info, cw_bat->bat_profile, CW2015_SIZE_BATINFO)) {
+ dev_warn(cw_bat->dev, "Replacing stored battery profile\n");
+ ret = cw_update_profile(cw_bat);
+ if (ret)
+ return ret;
}
} else {
- for (i = 0; i < SIZE_BATINFO; i++) {
- ret = cw_read(cw_bat->client, (REG_BATINFO + i),
- ®_val);
- if (ret < 0)
- return ret;
-
- if (cw_bat->plat_data.cw_bat_config_info[i] != reg_val)
- break;
- }
-
- if (i != SIZE_BATINFO) {
- dev_info(&cw_bat->client->dev,
- "update flag for new battery info have not set\n");
- ret = cw_update_config_info(cw_bat);
- if (ret < 0)
- return ret;
- }
+ dev_warn(cw_bat->dev,
+ "Can't check current battery profile, no profile provided\n");
}
- for (i = 0; i < 30; i++) {
- ret = cw_read(cw_bat->client, REG_SOC, ®_val);
- if (ret < 0)
- return ret;
- else if (reg_val <= 0x64)
- break;
- msleep(120);
- }
-
- if (i >= 30) {
- reg_val = MODE_SLEEP;
- ret = cw_write(cw_bat->client, REG_MODE, ®_val);
- dev_info(&cw_bat->client->dev, "report battery capacity error");
- return -1;
- }
-
- cw_printk("cw2015 init success!\n");
+ dev_dbg(cw_bat->dev, "Battery profile configured\n");
return 0;
}
-static int check_chrg_usb_psy(struct device *dev, void *data)
-{
- struct power_supply *psy = dev_get_drvdata(dev);
- struct cw_battery *cw_bat = (struct cw_battery *)data;
-
- if (psy->desc->type == POWER_SUPPLY_TYPE_USB) {
- cw_bat->chrg_usb_psy = psy;
- return 1;
- }
- return 0;
-}
-
-static int check_chrg_ac_psy(struct device *dev, void *data)
-{
- struct power_supply *psy = dev_get_drvdata(dev);
- struct cw_battery *cw_bat = (struct cw_battery *)data;
-
- if (psy->desc->type == POWER_SUPPLY_TYPE_MAINS) {
- cw_bat->chrg_ac_psy = psy;
- return 1;
- }
- return 0;
-}
-
-static void get_chrg_psy(struct cw_battery *cw_bat)
-{
- if (!cw_bat->chrg_usb_psy)
- class_for_each_device(power_supply_class, NULL, cw_bat,
- check_chrg_usb_psy);
- if (!cw_bat->chrg_ac_psy)
- class_for_each_device(power_supply_class, NULL, cw_bat,
- check_chrg_ac_psy);
-}
-
-static int get_charge_state(struct cw_battery *cw_bat)
-{
- union power_supply_propval val;
- int ret = -ENODEV;
- int usb_online = 0;
- int ac_online = 0;
- struct power_supply *chrg_usb_psy;
- struct power_supply *chrg_ac_psy;
-
- if (!cw_bat->chrg_usb_psy || !cw_bat->chrg_ac_psy)
- get_chrg_psy(cw_bat);
-
- chrg_usb_psy = cw_bat->chrg_usb_psy;
- chrg_ac_psy = cw_bat->chrg_ac_psy;
- if (chrg_usb_psy) {
- ret = chrg_usb_psy->desc->get_property(chrg_usb_psy,
- POWER_SUPPLY_PROP_ONLINE,
- &val);
- if (!ret)
- usb_online = val.intval;
- }
- if (chrg_ac_psy) {
- ret = chrg_ac_psy->desc->get_property(chrg_ac_psy,
- POWER_SUPPLY_PROP_ONLINE,
- &val);
- if (!ret)
- ac_online = val.intval;
- }
- if (!chrg_usb_psy)
- cw_printk("Usb online didn't find\n");
- if (!chrg_ac_psy)
- cw_printk("Ac online didn't find\n");
-
- cw_printk("ac_online = %d, usb_online = %d\n", ac_online, usb_online);
- if (ac_online || usb_online)
- return 1;
-
- return 0;
-}
-
-static int cw_por(struct cw_battery *cw_bat)
+static int cw_power_on_reset(struct cw_battery *cw_bat)
{
int ret;
unsigned char reset_val;
- reset_val = MODE_SLEEP;
- ret = cw_write(cw_bat->client, REG_MODE, &reset_val);
- if (ret < 0)
+ reset_val = CW2015_MODE_SLEEP;
+ ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reset_val);
+ if (ret)
return ret;
- reset_val = MODE_NORMAL;
+
+ /* wait for gauge to enter sleep */
msleep(20);
- ret = cw_write(cw_bat->client, REG_MODE, &reset_val);
- if (ret < 0)
+
+ reset_val = CW2015_MODE_NORMAL;
+ ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reset_val);
+ if (ret)
return ret;
+
ret = cw_init(cw_bat);
if (ret)
return ret;
return 0;
}
-static int cw_get_capacity(struct cw_battery *cw_bat)
+#define HYSTERESIS(current, previous, up, down) \
+ (((current) < (previous) + (up)) && ((current) > (previous) - (down)))
+
+static int cw_get_soc(struct cw_battery *cw_bat)
{
- int cw_capacity;
+ unsigned int soc;
int ret;
- unsigned char reg_val[2];
- static int reset_loop;
- static int charging_loop;
- static int discharging_loop;
- static int jump_flag;
- static int charging_5_loop;
- int sleep_cap;
-
- ret = cw_read_word(cw_bat->client, REG_SOC, reg_val);
- if (ret < 0)
+ ret = regmap_read(cw_bat->regmap, CW2015_REG_SOC, &soc);
+ if (ret)
return ret;
- cw_capacity = reg_val[0];
+ if (soc > 100) {
+ int max_error_cycles =
+ CW2015_BAT_SOC_ERROR_MS / cw_bat->poll_interval_ms;
- if ((cw_capacity < 0) || (cw_capacity > 100)) {
- cw_printk("Error: cw_capacity = %d\n", cw_capacity);
- reset_loop++;
- if (reset_loop >
- (BATTERY_CAPACITY_ERROR / cw_bat->monitor_sec)) {
- cw_por(cw_bat);
- reset_loop = 0;
+ dev_err(cw_bat->dev, "Invalid SoC %d%%\n", soc);
+ cw_bat->read_errors++;
+ if (cw_bat->read_errors > max_error_cycles) {
+ dev_warn(cw_bat->dev,
+ "Too many invalid SoC reports, resetting gauge\n");
+ cw_power_on_reset(cw_bat);
+ cw_bat->read_errors = 0;
}
- return cw_bat->capacity;
+ return cw_bat->soc;
+ }
+ cw_bat->read_errors = 0;
+
+ /* Reset gauge if stuck while charging */
+ if (cw_bat->status == POWER_SUPPLY_STATUS_CHARGING && soc == cw_bat->soc) {
+ int max_stuck_cycles =
+ CW2015_BAT_CHARGING_STUCK_MS / cw_bat->poll_interval_ms;
+
+ cw_bat->charge_stuck_cnt++;
+ if (cw_bat->charge_stuck_cnt > max_stuck_cycles) {
+ dev_warn(cw_bat->dev,
+ "SoC stuck @%u%%, resetting gauge\n", soc);
+ cw_power_on_reset(cw_bat);
+ cw_bat->charge_stuck_cnt = 0;
+ }
} else {
- reset_loop = 0;
+ cw_bat->charge_stuck_cnt = 0;
}
- /* case 1 : aviod swing */
- if (((cw_bat->charger_mode > 0) &&
- (cw_capacity <= cw_bat->capacity - 1) &&
- (cw_capacity > cw_bat->capacity - 9)) ||
- ((cw_bat->charger_mode == 0) &&
- (cw_capacity == (cw_bat->capacity + 1)))) {
- if (!(cw_capacity == 0 && cw_bat->capacity <= 2))
- cw_capacity = cw_bat->capacity;
- }
+ /* Ignore voltage dips during charge */
+ if (cw_bat->charger_attached && HYSTERESIS(soc, cw_bat->soc, 0, 3))
+ soc = cw_bat->soc;
- /* case 2 : aviod no charge full */
- if ((cw_bat->charger_mode > 0) &&
- (cw_capacity >= 95) && (cw_capacity <= cw_bat->capacity)) {
- cw_printk("Chaman join no charge full\n");
- charging_loop++;
- if (charging_loop >
- (BATTERY_UP_MAX_CHANGE / cw_bat->monitor_sec)) {
- cw_capacity = (cw_bat->capacity + 1) <= 100 ?
- (cw_bat->capacity + 1) : 100;
- charging_loop = 0;
- jump_flag = 1;
- } else {
- cw_capacity = cw_bat->capacity;
- }
- }
+ /* Ignore voltage spikes during discharge */
+ if (!cw_bat->charger_attached && HYSTERESIS(soc, cw_bat->soc, 3, 0))
+ soc = cw_bat->soc;
- /* case 3 : avoid battery level jump to CW_BAT */
- if ((cw_bat->charger_mode == 0) &&
- (cw_capacity <= cw_bat->capacity) &&
- (cw_capacity >= 90) && (jump_flag == 1)) {
- cw_printk("Chaman join no charge full discharging\n");
-#ifdef CONFIG_PM
- if (cw_bat->suspend_resume_mark == 1) {
- cw_bat->suspend_resume_mark = 0;
- sleep_cap = (cw_bat->after.tv_sec +
- discharging_loop *
- (cw_bat->monitor_sec / 1000)) /
- (BATTERY_DOWN_MAX_CHANGE / 1000);
- cw_printk("sleep_cap = %d\n", sleep_cap);
-
- if (cw_capacity >= cw_bat->capacity - sleep_cap) {
- return cw_capacity;
- } else {
- if (!sleep_cap)
- discharging_loop = discharging_loop +
- 1 + cw_bat->after.tv_sec /
- (cw_bat->monitor_sec / 1000);
- else
- discharging_loop = 0;
- cw_printk("discharging_loop = %d\n",
- discharging_loop);
- return cw_bat->capacity - sleep_cap;
- }
- }
-#endif
- discharging_loop++;
- if (discharging_loop >
- (BATTERY_DOWN_MAX_CHANGE / cw_bat->monitor_sec)) {
- if (cw_capacity >= cw_bat->capacity - 1)
- jump_flag = 0;
- else
- cw_capacity = cw_bat->capacity - 1;
-
- discharging_loop = 0;
- } else {
- cw_capacity = cw_bat->capacity;
- }
- }
-
- /* case 4 : avoid battery level is 0% when long time charging */
- if ((cw_bat->charger_mode > 0) && (cw_capacity == 0)) {
- charging_5_loop++;
- if (charging_5_loop >
- BATTERY_CHARGING_ZERO / cw_bat->monitor_sec) {
- cw_por(cw_bat);
- charging_5_loop = 0;
- }
- } else if (charging_5_loop != 0) {
- charging_5_loop = 0;
- }
-#ifdef CONFIG_PM
- if (cw_bat->suspend_resume_mark == 1)
- cw_bat->suspend_resume_mark = 0;
-#endif
- return cw_capacity;
+ return soc;
}
static int cw_get_voltage(struct cw_battery *cw_bat)
{
- int ret;
- u8 reg_val[2];
- u16 value16, value16_1, value16_2, value16_3;
- int voltage;
- int res1, res2;
+ int ret, i, voltage_mv;
+ u16 reg_val;
+ u32 avg = 0;
- ret = cw_read_word(cw_bat->client, REG_VCELL, reg_val);
- if (ret < 0)
- return ret;
- value16 = (reg_val[0] << 8) + reg_val[1];
+ for (i = 0; i < CW2015_AVERAGING_SAMPLES; i++) {
+ ret = cw_read_word(cw_bat, CW2015_REG_VCELL, ®_val);
+ if (ret)
+ return ret;
- ret = cw_read_word(cw_bat->client, REG_VCELL, reg_val);
- if (ret < 0)
- return ret;
- value16_1 = (reg_val[0] << 8) + reg_val[1];
-
- ret = cw_read_word(cw_bat->client, REG_VCELL, reg_val);
- if (ret < 0)
- return ret;
- value16_2 = (reg_val[0] << 8) + reg_val[1];
-
- if (value16 > value16_1) {
- value16_3 = value16;
- value16 = value16_1;
- value16_1 = value16_3;
+ avg += reg_val;
}
+ avg /= CW2015_AVERAGING_SAMPLES;
- if (value16_1 > value16_2) {
- value16_3 = value16_1;
- value16_1 = value16_2;
- value16_2 = value16_3;
- }
+ /*
+ * 305 uV per ADC step
+ * Use 312 / 1024 as efficient approximation of 305 / 1000
+ * Negligible error of 0.1%
+ */
+ voltage_mv = avg * 312 / 1024;
+ if (cw_bat->dual_cell)
+ voltage_mv *= 2;
- if (value16 > value16_1) {
- value16_3 = value16;
- value16 = value16_1;
- value16_1 = value16_3;
- }
-
- voltage = value16_1 * 312 / 1024;
-
- if (cw_bat->plat_data.divider_res1 &&
- cw_bat->plat_data.divider_res2) {
- res1 = cw_bat->plat_data.divider_res1;
- res2 = cw_bat->plat_data.divider_res2;
- voltage = voltage * (res1 + res2) / res2;
- } else if (cw_bat->dual_battery) {
- voltage = voltage * 2;
- }
-
- dev_dbg(&cw_bat->client->dev, "the cw201x voltage=%d,reg_val=%x %x\n",
- voltage, reg_val[0], reg_val[1]);
- return voltage;
+ dev_dbg(cw_bat->dev, "Read voltage: %d mV, raw=0x%04x\n",
+ voltage_mv, reg_val);
+ return voltage_mv;
}
-/*This function called when get RRT from cw2015*/
static int cw_get_time_to_empty(struct cw_battery *cw_bat)
{
int ret;
- u8 reg_val;
u16 value16;
- ret = cw_read(cw_bat->client, REG_RRT_ALERT, ®_val);
- if (ret < 0)
+ ret = cw_read_word(cw_bat, CW2015_REG_RRT_ALERT, &value16);
+ if (ret)
return ret;
- value16 = reg_val;
-
- ret = cw_read(cw_bat->client, REG_RRT_ALERT + 1, ®_val);
- if (ret < 0)
- return ret;
-
- value16 = ((value16 << 8) + reg_val) & 0x1fff;
- return value16;
+ return value16 & CW2015_MASK_SOC;
}
static void cw_update_charge_status(struct cw_battery *cw_bat)
{
- int cw_charger_mode;
-
- cw_charger_mode = get_charge_state(cw_bat);
- if (cw_bat->charger_mode != cw_charger_mode) {
- cw_bat->charger_mode = cw_charger_mode;
- cw_bat->bat_change = 1;
- if (cw_charger_mode)
- cw_bat->charge_count++;
- }
-}
-
-static void cw_update_capacity(struct cw_battery *cw_bat)
-{
- int cw_capacity;
-
- cw_capacity = cw_get_capacity(cw_bat);
- if ((cw_capacity >= 0) && (cw_capacity <= 100) &&
- (cw_bat->capacity != cw_capacity)) {
- cw_bat->capacity = cw_capacity;
- cw_bat->bat_change = 1;
- }
-}
-
-static void cw_update_vol(struct cw_battery *cw_bat)
-{
int ret;
- ret = cw_get_voltage(cw_bat);
- if ((ret >= 0) && (cw_bat->voltage != ret))
- cw_bat->voltage = ret;
+ ret = power_supply_am_i_supplied(cw_bat->rk_bat);
+ if (ret < 0) {
+ dev_warn(cw_bat->dev, "Failed to get supply state: %d\n", ret);
+ } else {
+ bool charger_attached;
+
+ charger_attached = !!ret;
+ if (cw_bat->charger_attached != charger_attached) {
+ cw_bat->battery_changed = true;
+ if (charger_attached)
+ cw_bat->charge_count++;
+ }
+ cw_bat->charger_attached = charger_attached;
+ }
+}
+
+static void cw_update_soc(struct cw_battery *cw_bat)
+{
+ int soc;
+
+ soc = cw_get_soc(cw_bat);
+ if (soc < 0)
+ dev_err(cw_bat->dev, "Failed to get SoC from gauge: %d\n", soc);
+ else if (cw_bat->soc != soc) {
+ cw_bat->soc = soc;
+ cw_bat->battery_changed = true;
+ }
+}
+
+static void cw_update_voltage(struct cw_battery *cw_bat)
+{
+ int voltage_mv;
+
+ voltage_mv = cw_get_voltage(cw_bat);
+ if (voltage_mv < 0)
+ dev_err(cw_bat->dev, "Failed to get voltage from gauge: %d\n",
+ voltage_mv);
+ else
+ cw_bat->voltage_mv = voltage_mv;
}
static void cw_update_status(struct cw_battery *cw_bat)
{
- int status;
+ int status = POWER_SUPPLY_STATUS_DISCHARGING;
- if (cw_bat->charger_mode > 0) {
- if (cw_bat->capacity >= 100)
+ if (cw_bat->charger_attached) {
+ if (cw_bat->soc >= 100)
status = POWER_SUPPLY_STATUS_FULL;
else
status = POWER_SUPPLY_STATUS_CHARGING;
- } else {
- status = POWER_SUPPLY_STATUS_DISCHARGING;
}
- if (cw_bat->status != status) {
- cw_bat->status = status;
- cw_bat->bat_change = 1;
- }
+ if (cw_bat->status != status)
+ cw_bat->battery_changed = true;
+ cw_bat->status = status;
}
static void cw_update_time_to_empty(struct cw_battery *cw_bat)
{
- int ret;
+ int time_to_empty;
- ret = cw_get_time_to_empty(cw_bat);
- if ((ret >= 0) && (cw_bat->time_to_empty != ret)) {
- cw_bat->time_to_empty = ret;
- cw_bat->bat_change = 1;
+ time_to_empty = cw_get_time_to_empty(cw_bat);
+ if (time_to_empty < 0) {
+ dev_err(cw_bat->dev, "Failed to get time to empty from gauge: %d\n",
+ time_to_empty);
+ return;
}
+ cw_bat->time_to_empty = time_to_empty;
}
static void cw_bat_work(struct work_struct *work)
@@ -556,63 +424,58 @@
struct delayed_work *delay_work;
struct cw_battery *cw_bat;
int ret;
- u8 reg_val;
- int i = 0;
+ unsigned int reg_val;
- delay_work = container_of(work, struct delayed_work, work);
- cw_bat =
- container_of(delay_work, struct cw_battery, battery_delay_work);
-
- /* Add for battery swap start */
- ret = cw_read(cw_bat->client, REG_MODE, ®_val);
- if (ret < 0) {
- cw_bat->bat_mode = MODE_VIRTUAL;
- cw_bat->bat_change = 1;
+ delay_work = to_delayed_work(work);
+ cw_bat = container_of(delay_work, struct cw_battery, battery_delay_work);
+ ret = regmap_read(cw_bat->regmap, CW2015_REG_MODE, ®_val);
+ if (ret) {
+ dev_err(cw_bat->dev, "Failed to read mode from gauge: %d\n", ret);
} else {
- if ((reg_val & MODE_SLEEP_MASK) == MODE_SLEEP) {
- for (i = 0; i < 5; i++) {
- if (cw_por(cw_bat) == 0)
+ if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) {
+ int i;
+
+ for (i = 0; i < CW2015_RESET_TRIES; i++) {
+ if (!cw_power_on_reset(cw_bat))
break;
}
}
- cw_update_capacity(cw_bat);
- cw_update_vol(cw_bat);
+ cw_update_soc(cw_bat);
+ cw_update_voltage(cw_bat);
cw_update_charge_status(cw_bat);
cw_update_status(cw_bat);
cw_update_time_to_empty(cw_bat);
}
- /* Add for battery swap end */
- cw_printk("charger_mod = %d\n", cw_bat->charger_mode);
- cw_printk("status = %d\n", cw_bat->status);
- cw_printk("capacity = %d\n", cw_bat->capacity);
- cw_printk("voltage = %d\n", cw_bat->voltage);
+ dev_dbg(cw_bat->dev, "charger_attached = %d\n", cw_bat->charger_attached);
+ dev_dbg(cw_bat->dev, "status = %d\n", cw_bat->status);
+ dev_dbg(cw_bat->dev, "soc = %d%%\n", cw_bat->soc);
+ dev_dbg(cw_bat->dev, "voltage = %dmV\n", cw_bat->voltage_mv);
-#ifdef CONFIG_PM
- if (cw_bat->suspend_resume_mark == 1)
- cw_bat->suspend_resume_mark = 0;
-#endif
-
- if (cw_bat->bat_change == 1) {
+ if (cw_bat->battery_changed)
power_supply_changed(cw_bat->rk_bat);
- cw_bat->bat_change = 0;
- }
+ cw_bat->battery_changed = false;
+
queue_delayed_work(cw_bat->battery_workqueue,
&cw_bat->battery_delay_work,
- msecs_to_jiffies(cw_bat->monitor_sec));
+ msecs_to_jiffies(cw_bat->poll_interval_ms));
+}
+
+static bool cw_battery_valid_time_to_empty(struct cw_battery *cw_bat)
+{
+ return cw_bat->time_to_empty > 0 &&
+ cw_bat->time_to_empty < CW2015_MASK_SOC &&
+ cw_bat->status == POWER_SUPPLY_STATUS_DISCHARGING;
}
static int cw_get_capacity_leve(struct cw_battery *cw_bat)
{
- if (cw_bat->bat_mode == MODE_VIRTUAL)
- return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-
- if (cw_bat->capacity < 1)
+ if (cw_bat->soc < 1)
return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
- else if (cw_bat->capacity <= 20)
+ else if (cw_bat->soc <= 20)
return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
- else if (cw_bat->capacity <= 70)
+ else if (cw_bat->soc <= 70)
return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
- else if (cw_bat->capacity <= 90)
+ else if (cw_bat->soc <= 90)
return POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
else
return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
@@ -622,44 +485,35 @@
enum power_supply_property psp,
union power_supply_propval *val)
{
- int ret = 0;
struct cw_battery *cw_bat;
cw_bat = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = cw_bat->capacity;
- if (cw_bat->bat_mode == MODE_VIRTUAL)
- val->intval = VIRTUAL_SOC;
+ val->intval = cw_bat->soc;
break;
+
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
val->intval = cw_get_capacity_leve(cw_bat);
break;
+
case POWER_SUPPLY_PROP_STATUS:
val->intval = cw_bat->status;
- if (cw_bat->bat_mode == MODE_VIRTUAL)
- val->intval = VIRTUAL_STATUS;
break;
- case POWER_SUPPLY_PROP_HEALTH:
- val->intval = POWER_SUPPLY_HEALTH_GOOD;
- break;
case POWER_SUPPLY_PROP_PRESENT:
- val->intval = cw_bat->voltage <= 0 ? 0 : 1;
- if (cw_bat->bat_mode == MODE_VIRTUAL)
- val->intval = VIRTUAL_PRESET;
+ val->intval = !!cw_bat->voltage_mv;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = cw_bat->voltage * 1000;
- if (cw_bat->bat_mode == MODE_VIRTUAL)
- val->intval = VIRTUAL_VOLTAGE * 1000;
+ val->intval = cw_bat->voltage_mv * 1000;
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
- val->intval = cw_bat->time_to_empty;
- if (cw_bat->bat_mode == MODE_VIRTUAL)
- val->intval = VIRTUAL_TIME2EMPTY;
+ if (cw_battery_valid_time_to_empty(cw_bat))
+ val->intval = cw_bat->time_to_empty;
+ else
+ val->intval = 0;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
@@ -672,28 +526,37 @@
case POWER_SUPPLY_PROP_CHARGE_FULL:
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
- val->intval = cw_bat->plat_data.design_capacity * 1000;
+ if (cw_bat->battery.charge_full_design_uah > 0)
+ val->intval = cw_bat->battery.charge_full_design_uah;
+ else
+ val->intval = 0;
break;
- case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
- val->intval = 3600;
- break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ if (cw_battery_valid_time_to_empty(cw_bat) &&
+ cw_bat->battery.charge_full_design_uah > 0) {
+ /* calculate remaining capacity */
+ val->intval = cw_bat->battery.charge_full_design_uah;
+ val->intval = val->intval * cw_bat->soc / 100;
- case POWER_SUPPLY_PROP_TEMP:
- val->intval = VIRTUAL_TEMPERATURE;
+ /* estimate current based on time to empty */
+ val->intval = 60 * val->intval / cw_bat->time_to_empty;
+ } else {
+ val->intval = 0;
+ }
+
break;
default:
break;
}
- return ret;
+ return 0;
}
static enum power_supply_property cw_battery_properties[] = {
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_STATUS,
- POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
@@ -701,258 +564,213 @@
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
- POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
- POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
};
static const struct power_supply_desc cw2015_bat_desc = {
- .name = "rk-bat",
+ .name = "cw2015-battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = cw_battery_properties,
.num_properties = ARRAY_SIZE(cw_battery_properties),
.get_property = cw_battery_get_property,
};
-#ifdef CONFIG_OF
-static int cw2015_parse_dt(struct cw_battery *cw_bat)
+static int cw2015_parse_properties(struct cw_battery *cw_bat)
{
- struct device *dev = &cw_bat->client->dev;
- struct device_node *node = dev->of_node;
- struct property *prop;
+ struct device *dev = cw_bat->dev;
int length;
- u32 value;
int ret;
- struct cw_bat_platform_data *data = &cw_bat->plat_data;
- struct gpio_desc *hw_id0_io;
- struct gpio_desc *hw_id1_io;
- int hw_id0_val;
- int hw_id1_val;
- if (!node)
- return -ENODEV;
-
- memset(data, 0, sizeof(*data));
-
- ret = of_property_read_u32(node, "hw_id_check", &value);
- if (!ret && value) {
- hw_id0_io = gpiod_get_optional(dev, "hw-id0", GPIOD_IN);
- if (!hw_id0_io)
- return -EINVAL;
- if (IS_ERR(hw_id0_io))
- return PTR_ERR(hw_id0_io);
-
- hw_id0_val = gpiod_get_value(hw_id0_io);
- gpiod_put(hw_id0_io);
-
- hw_id1_io = gpiod_get_optional(dev, "hw-id1", GPIOD_IN);
- if (!hw_id1_io)
- return -EINVAL;
- if (IS_ERR(hw_id1_io))
- return PTR_ERR(hw_id1_io);
-
- hw_id1_val = gpiod_get_value(hw_id1_io);
- gpiod_put(hw_id1_io);
-
- /*
- * ID1 = 0, ID0 = 1 : Battery
- * ID1 = 1, ID0 = 0 : Dual Battery
- * ID1 = 0, ID0 = 0 : Adapter
- */
- if (hw_id0_val == 1 && hw_id1_val == 0)
- cw_bat->dual_battery = false;
- else if (hw_id0_val == 0 && hw_id1_val == 1)
- cw_bat->dual_battery = true;
- else
- return -EINVAL;
- }
-
- /* determine the number of config info */
- prop = of_find_property(node, "bat_config_info", &length);
- if (!prop)
+ length = device_property_count_u8(dev, "cellwise,battery-profile");
+ if (length < 0) {
+ dev_warn(cw_bat->dev,
+ "No battery-profile found, using current flash contents\n");
+ } else if (length != CW2015_SIZE_BATINFO) {
+ dev_err(cw_bat->dev, "battery-profile must be %d bytes\n",
+ CW2015_SIZE_BATINFO);
return -EINVAL;
-
- length /= sizeof(u32);
-
- if (length > 0) {
- size_t size = sizeof(*data->cw_bat_config_info) * length;
-
- data->cw_bat_config_info = devm_kzalloc(dev, size, GFP_KERNEL);
- if (!data->cw_bat_config_info)
+ } else {
+ cw_bat->bat_profile = devm_kzalloc(dev, length, GFP_KERNEL);
+ if (!cw_bat->bat_profile)
return -ENOMEM;
- ret = of_property_read_u32_array(node, "bat_config_info",
- data->cw_bat_config_info,
- length);
- if (ret < 0)
+ ret = device_property_read_u8_array(dev,
+ "cellwise,battery-profile",
+ cw_bat->bat_profile,
+ length);
+ if (ret)
return ret;
}
- cw_bat->bat_mode = MODE_BATTARY;
- cw_bat->monitor_sec = DEFAULT_MONITOR_SEC * TIMER_MS_COUNTS;
+ cw_bat->dual_cell = device_property_read_bool(dev, "cellwise,dual-cell");
- ret = of_property_read_u32(node, "divider_res1", &value);
- if (ret < 0)
- value = 0;
- data->divider_res1 = value;
-
- ret = of_property_read_u32(node, "divider_res2", &value);
- if (ret < 0)
- value = 0;
- data->divider_res2 = value;
-
- ret = of_property_read_u32(node, "virtual_power", &value);
- if (ret < 0)
- value = 0;
- cw_bat->bat_mode = value;
-
- ret = of_property_read_u32(node, "monitor_sec", &value);
- if (ret < 0)
- dev_err(dev, "monitor_sec missing!\n");
- else
- cw_bat->monitor_sec = value * TIMER_MS_COUNTS;
-
- ret = of_property_read_u32(node, "design_capacity", &value);
- if (ret < 0) {
- dev_err(dev, "design_capacity missing!\n");
- data->design_capacity = 2000;
- } else {
- data->design_capacity = value;
+ ret = device_property_read_u32(dev, "cellwise,monitor-interval-ms",
+ &cw_bat->poll_interval_ms);
+ if (ret) {
+ dev_dbg(cw_bat->dev, "Using default poll interval\n");
+ cw_bat->poll_interval_ms = CW2015_DEFAULT_POLL_INTERVAL_MS;
}
return 0;
}
-#else
-static int cw2015_parse_dt(struct cw_battery *cw_bat)
-{
- return -ENODEV;
-}
-#endif
-static int cw_bat_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static const struct regmap_range regmap_ranges_rd_yes[] = {
+ regmap_reg_range(CW2015_REG_VERSION, CW2015_REG_VERSION),
+ regmap_reg_range(CW2015_REG_VCELL, CW2015_REG_CONFIG),
+ regmap_reg_range(CW2015_REG_MODE, CW2015_REG_MODE),
+ regmap_reg_range(CW2015_REG_BATINFO,
+ CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1),
+};
+
+static const struct regmap_access_table regmap_rd_table = {
+ .yes_ranges = regmap_ranges_rd_yes,
+ .n_yes_ranges = 4,
+};
+
+static const struct regmap_range regmap_ranges_wr_yes[] = {
+ regmap_reg_range(CW2015_REG_RRT_ALERT, CW2015_REG_CONFIG),
+ regmap_reg_range(CW2015_REG_MODE, CW2015_REG_MODE),
+ regmap_reg_range(CW2015_REG_BATINFO,
+ CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1),
+};
+
+static const struct regmap_access_table regmap_wr_table = {
+ .yes_ranges = regmap_ranges_wr_yes,
+ .n_yes_ranges = 3,
+};
+
+static const struct regmap_range regmap_ranges_vol_yes[] = {
+ regmap_reg_range(CW2015_REG_VCELL, CW2015_REG_SOC + 1),
+};
+
+static const struct regmap_access_table regmap_vol_table = {
+ .yes_ranges = regmap_ranges_vol_yes,
+ .n_yes_ranges = 1,
+};
+
+static const struct regmap_config cw2015_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = ®map_rd_table,
+ .wr_table = ®map_wr_table,
+ .volatile_table = ®map_vol_table,
+ .max_register = CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1,
+};
+
+static int cw_bat_probe(struct i2c_client *client)
{
int ret;
struct cw_battery *cw_bat;
- struct power_supply_config psy_cfg = {0};
+ struct power_supply_config psy_cfg = { 0 };
cw_bat = devm_kzalloc(&client->dev, sizeof(*cw_bat), GFP_KERNEL);
- if (!cw_bat) {
- dev_err(&client->dev,
- "fail to allocate memory for cw2015\n");
+ if (!cw_bat)
return -ENOMEM;
- }
i2c_set_clientdata(client, cw_bat);
- cw_bat->client = client;
+ cw_bat->dev = &client->dev;
+ cw_bat->soc = 1;
- ret = cw2015_parse_dt(cw_bat);
- if (ret < 0) {
- dev_err(&client->dev,
- "failed to find cw2015 platform data\n");
- return -1;
+ ret = cw2015_parse_properties(cw_bat);
+ if (ret) {
+ dev_err(cw_bat->dev, "Failed to parse cw2015 properties\n");
+ return ret;
}
- cw_bat->capacity = 1;
- cw_bat->voltage = 0;
- cw_bat->status = 0;
- cw_bat->suspend_resume_mark = 0;
- cw_bat->charger_mode = NO_CHARGING;
- cw_bat->bat_change = 0;
+ cw_bat->regmap = devm_regmap_init_i2c(client, &cw2015_regmap_config);
+ if (IS_ERR(cw_bat->regmap)) {
+ dev_err(cw_bat->dev, "Failed to allocate regmap: %ld\n",
+ PTR_ERR(cw_bat->regmap));
+ return PTR_ERR(cw_bat->regmap);
+ }
ret = cw_init(cw_bat);
if (ret) {
- pr_err("%s cw_init error\n", __func__);
+ dev_err(cw_bat->dev, "Init failed: %d\n", ret);
return ret;
}
psy_cfg.drv_data = cw_bat;
+ psy_cfg.fwnode = dev_fwnode(cw_bat->dev);
cw_bat->rk_bat = devm_power_supply_register(&client->dev,
- &cw2015_bat_desc, &psy_cfg);
+ &cw2015_bat_desc,
+ &psy_cfg);
if (IS_ERR(cw_bat->rk_bat)) {
- dev_err(&cw_bat->client->dev,
- "power supply register rk_bat error\n");
- return -1;
+ /* try again if this happens */
+ dev_err_probe(&client->dev, PTR_ERR(cw_bat->rk_bat),
+ "Failed to register power supply\n");
+ return PTR_ERR(cw_bat->rk_bat);
+ }
+
+ ret = power_supply_get_battery_info(cw_bat->rk_bat, &cw_bat->battery);
+ if (ret) {
+ dev_warn(cw_bat->dev,
+ "No monitored battery, some properties will be missing\n");
}
cw_bat->battery_workqueue = create_singlethread_workqueue("rk_battery");
INIT_DELAYED_WORK(&cw_bat->battery_delay_work, cw_bat_work);
queue_delayed_work(cw_bat->battery_workqueue,
&cw_bat->battery_delay_work, msecs_to_jiffies(10));
-
- dev_info(&cw_bat->client->dev,
- "cw2015/cw2013 driver v1.2 probe sucess\n");
return 0;
}
-#ifdef CONFIG_PM
-static int cw_bat_suspend(struct device *dev)
+static int __maybe_unused cw_bat_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct cw_battery *cw_bat = i2c_get_clientdata(client);
- read_persistent_clock(&cw_bat->suspend_time_before);
- cancel_delayed_work(&cw_bat->battery_delay_work);
+
+ cancel_delayed_work_sync(&cw_bat->battery_delay_work);
return 0;
}
-static int cw_bat_resume(struct device *dev)
+static int __maybe_unused cw_bat_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct cw_battery *cw_bat = i2c_get_clientdata(client);
- cw_bat->suspend_resume_mark = 1;
- read_persistent_clock(&cw_bat->after);
- cw_bat->after = timespec_sub(cw_bat->after,
- cw_bat->suspend_time_before);
+
queue_delayed_work(cw_bat->battery_workqueue,
- &cw_bat->battery_delay_work, msecs_to_jiffies(2));
+ &cw_bat->battery_delay_work, 0);
return 0;
}
-static const struct dev_pm_ops cw_bat_pm_ops = {
- .suspend = cw_bat_suspend,
- .resume = cw_bat_resume,
-};
-#endif
+static SIMPLE_DEV_PM_OPS(cw_bat_pm_ops, cw_bat_suspend, cw_bat_resume);
static int cw_bat_remove(struct i2c_client *client)
{
struct cw_battery *cw_bat = i2c_get_clientdata(client);
- dev_dbg(&cw_bat->client->dev, "%s\n", __func__);
- cancel_delayed_work(&cw_bat->battery_delay_work);
+ cancel_delayed_work_sync(&cw_bat->battery_delay_work);
+ power_supply_put_battery_info(cw_bat->rk_bat, &cw_bat->battery);
return 0;
}
static const struct i2c_device_id cw_bat_id_table[] = {
- {"cw201x", 0},
- {}
+ { "cw2015", 0 },
+ { }
};
+
+static const struct of_device_id cw2015_of_match[] = {
+ { .compatible = "cellwise,cw2015" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cw2015_of_match);
static struct i2c_driver cw_bat_driver = {
.driver = {
- .name = "cw201x",
-#ifdef CONFIG_PM
+ .name = "cw2015",
+ .of_match_table = cw2015_of_match,
.pm = &cw_bat_pm_ops,
-#endif
},
- .probe = cw_bat_probe,
+ .probe_new = cw_bat_probe,
.remove = cw_bat_remove,
.id_table = cw_bat_id_table,
};
-static int __init cw_bat_init(void)
-{
- return i2c_add_driver(&cw_bat_driver);
-}
-
-static void __exit cw_bat_exit(void)
-{
- i2c_del_driver(&cw_bat_driver);
-}
-
-module_init(cw_bat_init);
-module_exit(cw_bat_exit);
+module_i2c_driver(cw_bat_driver);
MODULE_AUTHOR("xhc<xhc@rock-chips.com>");
+MODULE_AUTHOR("Tobias Schramm <t.schramm@manjaro.org>");
MODULE_DESCRIPTION("cw2015/cw2013 battery driver");
MODULE_LICENSE("GPL");
--
Gitblit v1.6.2