lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20190222020832.83-1-elicec@foxmail.com>
Date:   Fri, 22 Feb 2019 10:08:32 +0800
From:   Hong Peng <elicec@...mail.com>
To:     elicec@...mail.com, sre@...nel.org, linux-kernel@...r.kernel.org,
        linux-pm@...r.kernel.org
Subject: [PATCH] power:supply:Add silergy sy6410 gas gauge support,

This patch adds the silergy sy6410 single cell Li+ battery gas
gauge ic support,which is used to calculate the battery capacity.

Signed-off-by: Hong Peng <elicec@...mail.com>

---
 drivers/power/supply/Kconfig          |   7 +
 drivers/power/supply/Makefile         |   1 +
 drivers/power/supply/sy6410_battery.c | 554 ++++++++++++++++++++++++++
 3 files changed, 562 insertions(+)
 create mode 100644 drivers/power/supply/sy6410_battery.c

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index e901b9879e7e..7e37598f68c3 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -660,4 +660,11 @@ config FUEL_GAUGE_SC27XX
 	 Say Y here to enable support for fuel gauge with SC27XX
 	 PMIC chips.
 
+config SY6410_BATTERY
+	tristate "Silergy sy6410 single cell Li+ battery fuel gauge driver"
+	depends on I2C
+	help
+	 Say Y here to enable support for fuel gauge with sy6410
+	 PMIC chips,which is used to calculate the battery capacity
+
 endif # POWER_SUPPLY
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index b731c2a9b695..330dbaa5319e 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -87,3 +87,4 @@ obj-$(CONFIG_AXP288_CHARGER)	+= axp288_charger.o
 obj-$(CONFIG_CHARGER_CROS_USBPD)	+= cros_usbpd-charger.o
 obj-$(CONFIG_CHARGER_SC2731)	+= sc2731_charger.o
 obj-$(CONFIG_FUEL_GAUGE_SC27XX)	+= sc27xx_fuel_gauge.o
+obj-$(CONFIG_SY6410_BATTERY)    += sy6410_battery.o
diff --git a/drivers/power/supply/sy6410_battery.c b/drivers/power/supply/sy6410_battery.c
index 000000000000..537dd1cf3e7f
--- /dev/null
+++ b/drivers/power/supply/sy6410_battery.c
@@ -0,0 +1,554 @@
+/**
+ * I2C client/driver for the Silergy sy6410 single Cell Li+ Battery Fuel Gauge
+ *
+ * Author: elicec
+ *
+ * Date: 2017-12-12 15:15:05
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/swab.h>
+#include <linux/debugfs.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/qpnp/qpnp-adc.h>
+
+
+#define SY6410_VBAT_REG	0x02        /* The Voltage of Battery */
+#define SY6410_SOC_REG		0x04	    /* The SOC of battery */
+#define SY6410_MODE_REG	0x06	    /* mode register */
+#define SY6410_VERSION_REG	0x08	    /* production version of this IC */
+#define SY6410_CONFIG_REG	0x0c
+#define SY6410_VRESET_REG	0x18        /* the reset voltage */
+#define SY6410_STATUS_REG	0x1a
+
+#define SY6410_STATUS_MASK	0x0001
+
+#define SY6410_DELAY		1000
+
+#define SY6410_DEBUG_REG(x)    {#x, x##_REG}
+
+struct debug_reg {
+	char *name;
+	u8 reg;
+};
+
+static struct debug_reg sy6410_debug_regs[] = {
+	SY6410_DEBUG_REG(SY6410_VBAT),
+	SY6410_DEBUG_REG(SY6410_SOC),
+	SY6410_DEBUG_REG(SY6410_MODE),
+	SY6410_DEBUG_REG(SY6410_VRESET),
+	SY6410_DEBUG_REG(SY6410_CONFIG),
+	SY6410_DEBUG_REG(SY6410_VERSION),
+	SY6410_DEBUG_REG(SY6410_STATUS),
+};
+
+struct sy6410_info;
+
+struct sy6410_battery_ops {
+	int (*get_battery_status)(struct sy6410_info *info, int *status);
+	int (*get_battery_voltage)(struct sy6410_info *info, int *voltage_uV);
+	int (*get_battery_capacity)(struct sy6410_info *info, int *capacity);
+	int (*get_battery_temp)(struct sy6410_info *info, int *temp);
+};
+
+#define to_sy6410_info(x) container_of(x, struct sy6410_info, battery)
+
+struct sy6410_info {
+	struct i2c_client	*client;
+	struct power_supply	battery;
+	struct sy6410_battery_ops  *ops;
+	struct delayed_work	bat_work;
+	struct dentry *debug_root;
+	struct qpnp_vadc_chip *vadc_dev;
+	s16			version;
+	int			capacity;
+	int			status;		/* State Of Charge */
+};
+
+static struct sy6410_info *the_chip;
+
+static inline int sy6410_read_reg(struct sy6410_info *info, int reg, u8 *val)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(info->client, reg);
+	if (ret < 0) {
+		dev_err(&info->client->dev, "register read failed\n");
+		return ret;
+	}
+
+	*val = ret;
+	return 0;
+}
+
+static inline int sy6410_write_reg16(struct sy6410_info *info, int reg_msb,
+				    u16 val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_word_data(info->client, reg_msb, val);
+	if (ret < 0)
+		dev_err(&info->client->dev, "register write failed\n");
+
+	return ret;
+}
+
+static inline int sy6410_read_reg16(struct sy6410_info *info, int reg_msb,
+				    s16 *val)
+{
+	int ret;
+
+	ret = i2c_smbus_read_word_data(info->client, reg_msb);
+	if (ret < 0) {
+		dev_err(&info->client->dev, "register read failed\n");
+		return ret;
+	}
+
+	*val = swab16(ret);
+	return 0;
+}
+
+//TODO get temp
+static int sy6410_get_temp(struct sy6410_info *info, int *temp)
+{
+	struct qpnp_vadc_result result;
+	int ret;
+	int batt_temp = 25;
+
+	if (PTR_ERR(info->vadc_dev) == -EPROBE_DEFER) {
+		pr_err("vadc not found try again\n");
+		info->vadc_dev = qpnp_get_vadc(&info->client->dev,
+									"batterytemp");
+	}
+	ret = qpnp_vadc_read(info->vadc_dev, LR_MUX1_BATT_THERM, &result);
+	if (ret)
+		pr_err("unable to read battery temp:ret=%d\n", ret);
+	else
+		batt_temp = (int)result.physical;
+
+	pr_info("read battery temp:raw=%lld\n", result.physical);
+	*temp = batt_temp;
+	*temp = 25;
+
+	return 0;
+}
+
+static int sy6410_get_voltage(struct sy6410_info *info, int *voltage_uV)
+{
+	s16 raw;
+	int err;
+
+	/*
+	 * Voltage is measured in units of 0.61mv,offset 2.5v. The voltage is
+	 * a 12-bit number plus sign, in the upper bits of a 16-bit register
+	 */
+	err = sy6410_read_reg16(info, SY6410_VBAT_REG, &raw);
+	if (err)
+		return err;
+	*voltage_uV = raw * 610 + 2500000;
+	dev_dbg(&info->client->dev, "vbat reg(%02x) = %02x\n", SY6410_VBAT_REG,
+		raw);
+
+	return 0;
+}
+
+static int sy6410_get_capacity(struct sy6410_info *info, int *capacity)
+{
+	int err;
+	u16 raw;
+
+	err = sy6410_read_reg16(info, SY6410_SOC_REG, &raw);
+	dev_dbg(&info->client->dev, "capacity reg(%02x) = %04x\n",
+		SY6410_SOC_REG, raw);
+	if (err)
+		return err;
+
+	*capacity = raw * 100/65535;
+
+	return 0;
+}
+
+#define SLEEP_REG_MASK 0x2000
+static int sy6410_sleep_enable(struct sy6410_info *info, bool enable)
+{
+	int err;
+	s16 raw;
+
+	err = sy6410_read_reg16(info, SY6410_MODE_REG, &raw);
+	if (err) {
+		dev_dbg(&info->client->dev, "sleep enable err! read reg(%02x)
+			error = %d\n", SY6410_MODE_REG, err);
+		return err;
+	}
+	/*the MSB 13bit is the sleep enable*/
+	raw &= ~SLEEP_REG_MASK;
+	raw |= (enable << 13) & SLEEP_REG_MASK;
+
+	err = sy6410_write_reg16(info, SY6410_MODE_REG, raw);
+	dev_dbg(&info->client->dev, "mode reg(%02x) = %02x\n", SY6410_MODE_REG,
+		raw);
+
+	return err;
+
+}
+
+static int sy6410_get_charge_status(struct sy6410_info *info, int *status)
+{
+	int err;
+	s16 raw;
+
+	err = sy6410_read_reg16(info, SY6410_STATUS_REG, &raw);
+	dev_dbg(&info->client->dev, "status reg(%02x) = %04x\n",
+		SY6410_STATUS_REG, raw);
+	if (err)
+		return err;
+	*status = raw & SY6410_STATUS_MASK;
+	return 0;
+}
+
+static int sy6410_get_status(struct sy6410_info *info, int *status)
+{
+	int err;
+	int state;
+	int capacity;
+
+	err = info->ops->get_battery_capacity(info, &capacity);
+	err = info->ops->get_battery_status(info, &state);
+	if (err)
+		return err;
+
+	info->capacity = capacity;
+	info->status = state;
+
+	if (capacity == 100)
+		*status = POWER_SUPPLY_STATUS_FULL;
+	else if (state == 1)
+		*status = POWER_SUPPLY_STATUS_CHARGING;
+	else if (state == 0)
+		*status = POWER_SUPPLY_STATUS_DISCHARGING;
+	else
+		*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+	return 0;
+}
+
+static int sy6410_battery_get_property(struct power_supply *psy,
+				       enum power_supply_property prop,
+				       union power_supply_propval *val)
+{
+	struct sy6410_info *info = to_sy6410_info(psy);
+	int ret;
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_CAPACITY:
+		ret = info->ops->get_battery_capacity(info, &val->intval);
+		break;
+
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		ret = info->ops->get_battery_voltage(info, &val->intval);
+		break;
+
+	case POWER_SUPPLY_PROP_TEMP:
+		ret = info->ops->get_battery_temp(info, &val->intval);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void sy6410_battery_update(struct sy6410_info *info)
+{
+	/*TODO add temp*/
+	int old_status = info->status;
+	int old_capacity = info->capacity;
+
+	sy6410_get_status(info, &info->status);
+
+	if ((old_status != info->status) || (old_capacity != info->capacity))
+		power_supply_changed(&info->battery);
+}
+
+static void sy6410_battery_work(struct work_struct *work)
+{
+	struct sy6410_info *info;
+
+	info = container_of(work, struct sy6410_info, bat_work.work);
+	sy6410_battery_update(info);
+
+	schedule_delayed_work(&info->bat_work, SY6410_DELAY);
+}
+
+static int show_config_regs(struct seq_file *m, void *data)
+{
+	struct sy6410_info *info = m->private;
+	int rc;
+	int n;
+	u16 reg;
+	u8 addrs[] = {0x02, 0x04, 0x06, 0x08, 0x0c, 0x18, 0x1a};

+	for (n = 0; n < ARRAY_SIZE(addrs); n++) {
+		rc = sy6410_read_reg16(info, addrs[n], &reg);
+		seq_printf(m, "0x%02x = 0x%04x\n", addrs[n], reg);
+	}
+	return 0;
+}
+
+static int cnfg_debugfs_open(struct inode *inode, struct file *file)
+{
+	struct sy6410_info *info = inode->i_private;

+	return single_open(file, show_config_regs, info);
+}
+
+static const struct file_operations cnfg_debugfs_ops = {
+	.owner = THIS_MODULE,
+	.open = cnfg_debugfs_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int sy6410_set_reg(void *data, u64 val)
+{
+	u32 addr = (long) data;
+	int ret;
+
+	ret = sy6410_write_reg16(the_chip, addr, (u16) val);
+
+	return ret;
+}
+
+static int sy6410_get_reg(void *data, u64 *val)
+{
+	u32 addr = (long) data;
+	int ret;
+	u16 raw;
+
+	ret = sy6410_read_reg16(the_chip, addr, &raw);
+	if (ret < 0)
+		return ret;
+
+	*val = raw;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, sy6410_get_reg, sy6410_set_reg,
+		"0x%02llx\n");
+
+static int sy6410_create_debugfs_entries(struct sy6410_info *info)
+{
+	int i;
+
+	info->debug_root = debugfs_create_dir("SY6410", NULL);
+	if (!info->debug_root)
+		dev_err(&info->client->dev, "Couldn't create debug dir\n");
+	if (info->debug_root) {
+		struct dentry *ent;
+
+		ent = debugfs_create_file("registers", 0444,
+				info->debug_root, info, &cnfg_debugfs_ops);
+		if (!ent)
+			dev_err(&info->client->dev, "Couldn't create debug file\n");
+	}
+
+	for (i = 0; i < ARRAY_SIZE(sy6410_debug_regs); i++) {
+		char *name = sy6410_debug_regs[i].name;
+		u32 reg = sy6410_debug_regs[i].reg;
+		struct dentry *file;
+
+		file = debugfs_create_file(name, 0644,
+				info->debug_root, (void *)(long)reg, &reg_fops);
+
+		if (IS_ERR(file)) {
+			pr_err("debugfs_create_file %s failed.\n", name);
+			return -EFAULT;
+		}
+	}
+
+	return 0;
+}
+
+static enum power_supply_property sy6410_battery_props[] = {
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_TEMP,
+};
+
+static void sy6410_power_supply_init(struct power_supply *battery)
+{
+	battery->name = "sy6410_battery";
+	battery->type			= POWER_SUPPLY_TYPE_BATTERY;
+	battery->properties		= sy6410_battery_props;
+	battery->num_properties		= ARRAY_SIZE(sy6410_battery_props);
+	battery->get_property		= sy6410_battery_get_property;
+	battery->external_power_changed	= NULL;
+}
+
+static int sy6410_battery_remove(struct i2c_client *client)
+{
+	struct sy6410_info *info = i2c_get_clientdata(client);
+
+	power_supply_unregister(&info->battery);
+	kfree(info->battery.name);
+
+	cancel_delayed_work(&info->bat_work);
+	the_chip = NULL;
+
+	kfree(info);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int sy6410_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct sy6410_info *info = i2c_get_clientdata(client);
+
+	cancel_delayed_work(&info->bat_work);
+	return 0;
+}
+
+static int sy6410_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct sy6410_info *info = i2c_get_clientdata(client);
+
+	schedule_delayed_work(&info->bat_work, SY6410_DELAY);
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(sy6410_battery_pm_ops, sy6410_suspend,
+		sy6410_resume);
+#define SY6410_BATTERY_PM_OPS (&sy6410_battery_pm_ops)
+
+#else
+#define SY6410_BATTERY_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+enum sy6410_num_id {
+	SY6410 = 0,
+	SY641X,
+};
+
+static struct sy6410_battery_ops sy6410_ops[] = {
+	[SY6410] = {
+		.get_battery_status  = sy6410_get_charge_status,
+		.get_battery_voltage  = sy6410_get_voltage,
+		.get_battery_capacity = sy6410_get_capacity,
+		.get_battery_temp = sy6410_get_temp,
+	},
+	[SY641X] = {
+		.get_battery_status  = sy6410_get_charge_status,
+		.get_battery_voltage  = sy6410_get_voltage,
+		.get_battery_capacity = sy6410_get_capacity,
+		.get_battery_temp = sy6410_get_temp,
+	}
+};
+
+static int sy6410_battery_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	struct sy6410_info *info;
+	int ret;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		ret = -ENOMEM;
+		goto fail_id;
+	}
+
+	info->client = client;
+	info->ops  = &sy6410_ops[0];
+	info->capacity = 75;
+	info->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	info->vadc_dev = qpnp_get_vadc(&client->dev, "batterytemp");
+	if (IS_ERR(info->vadc_dev)) {
+		ret = PTR_ERR(info->vadc_dev);
+		if (ret == -EPROBE_DEFER)
+			dev_err(&client->dev, "vadc not found rc=%d\n", ret);
+		else
+			dev_err(&client->dev, "vadc prop rc=%d\n", ret);
+	}
+	ret = sy6410_read_reg16(info, SY6410_VERSION_REG, &info->version);
+	if (ret) {
+		pr_err("unable to read sy6410 version, absent?ret=%d\n", ret);
+		return -ENODEV;
+	}
+
+	i2c_set_clientdata(client, info);
+	sy6410_power_supply_init(&info->battery);
+	INIT_DELAYED_WORK(&info->bat_work, sy6410_battery_work);
+
+	ret = power_supply_register(&client->dev, &info->battery);
+	if (ret) {
+		dev_err(&client->dev, "failed to register battery\n");
+		goto fail_register;
+	} else {
+		schedule_delayed_work(&info->bat_work, SY6410_DELAY);
+	}
+
+	sy6410_sleep_enable(info, false);
+	the_chip = info;
+
+	sy6410_create_debugfs_entries(info);
+
+	dev_info(&client->dev, "sy6410 HW version: 0x%X\n", info->version);
+
+	return 0;
+
+fail_register:
+	kfree(info->battery.name);
+fail_id:
+	return ret;
+}
+
+static const struct i2c_device_id sy6410_id[] = {
+	{"sy6410", SY6410},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, sy6410_id);
+
+static const struct of_device_id sy6410_match[] = {
+	{ .compatible = "silergy,sy6410-battery", },
+	{  },
+};
+
+static struct i2c_driver sy6410_battery_driver = {
+	.driver	= {
+		.name	= "sy6410-battery",
+		.pm	= SY6410_BATTERY_PM_OPS,
+		.of_match_table = of_match_ptr(sy6410_match),
+	},
+	.probe		= sy6410_battery_probe,
+	.remove		= sy6410_battery_remove,
+	.id_table	= sy6410_id,
+};
+module_i2c_driver(sy6410_battery_driver);
+
+static int __init sy6410_init(void)
+{
+	return i2c_add_driver(&sy6410_battery_driver);
+}
+module_init(sy6410_init);
+
+static void __exit sy6410_exit(void)
+{
+	return i2c_del_driver(&sy6410_battery_driver);
+}
+module_exit(sy6410_exit);
+
+MODULE_AUTHOR("elicec");
+MODULE_DESCRIPTION("Silergy SY6410 single cell Li+ Fuel Gauage IC driver");
+MODULE_LICENSE("GPL");
-- 
2.17.1

bÁC

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ