sh366006.c
/*
谁愿压抑心中怒愤冲动
咒骂这虚与伪与假
从没信要屈膝面对生命
纵没有别人帮
一生只靠我双手
让我放声疯狂叫囔
今天的他 呼风可改雨
不可一世太嚣张
--《不可一世》Beyond
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/workqueue.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/sizes.h>
#define SHFG_ENABLE_LOG 1
#define SH_PROPERTIES "sh366006-bat"
#define SHFG_NAME "sh366006"
#define sh_printk(fmt, arg...) \
{ \
if (SHFG_ENABLE_LOG) \
printk(KERN_INFO "SH366006 : %s-%d : " fmt, __FUNCTION__ ,__LINE__,##arg); \
else {} \
}
#define queue_delayed_work_time 1000
#define queue_start_work_time 50
#define SH366006_SMBUS_ADDR 0x16
#define SH366006_MANUFACTURER_DATA 0x07
static const uint8_t crc8_table[256] =
{
0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d,
0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d,
0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd,
0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea,
0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a,
0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a,
0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4,
0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44,
0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63,
0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13,
0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83,
0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3
};
/* 完整的SBS Command Codes (0x00-0x3F) */
enum sh366006_commands {
MANUFACTURER_ACCESS = 0x00, // 制造商访问
DEVICE_TYPE = 0x01, // 设备类型
REMAINING_CAPACITY = 0x0F, // 剩余容量(mAh)
VOLTAGE = 0x09, // 电池电压(mV)
CURRENT = 0x0A, // 实时电流(mA)
AVERAGE_CURRENT = 0x0B, // 平均电流(mA)
RELATIVE_SOC = 0x0D, // 相对电量百分比(%)
ABSOLUTE_SOC = 0x0E, // 绝对电量百分比(%)
HEALTHY_SOC = 0x4F, // 电池健康度
TEMPERATURE = 0x08, // 温度(0.1K)
FULL_CHARGE_CAPACITY = 0x10, // 满电容量
BATTERY_STATUS = 0x16, // 电池状态字
CYCLE_COUNT = 0x17, // 循环次数
DESIGN_CAPACITY = 0x18, // 设计容量(mAh)
DESIGN_VOLTAGE = 0x19, // 标称电压(mV)
SPECIFICATION_INFO = 0x1A, // 规格信息
MANUFACTURE_DATE = 0x1B, // 生产日期
SERIAL_NUMBER = 0x1C, // 序列号
CELL_VOLTAGE_1 = 0x3C, // 第1节电压(mV)
CELL_VOLTAGE_2 = 0x3D, // 第2节电压(mV)
CELL_VOLTAGE_3 = 0x3E, // 第3节电压(mV)
CELL_VOLTAGE_4 = 0x3F, // 第4节电压(mV)
// 安全扩展命令(0x20-0x2F)
SAFETY_STATUS = 0x20, // 安全状态寄存器
SAFETY_ALERT = 0x21, // 安全警报阈值
SAFETY_STATUS_EXT = 0x22, // 扩展安全状态
CHG_VOLTAGE = 0x30, // 充电电压设置(mV)
CHG_CURRENT = 0x31, // 充电电流设置(mA)
DISCHG_CURRENT = 0x39, // 放电电流限制(mA)
// 制造商专用命令(0x40-0x5F)
QMAX_UPDATE = 0x41, // Qmax校准命令
BALANCE_CONTROL = 0x42, // 电池均衡控制
JEITA_CONTROL = 0x43, // JEITA温度补偿
SHA1_AUTH = 0x44, // SHA-1认证
CELL_TEMP_1 = 0x50, // 第1节温度
CELL_TEMP_2 = 0x51, // 第2节温度
// 时间预测命令
REMAINING_TIME = 0x11, // 剩余使用时间(min)
AVG_TIME_TO_EMPTY = 0x12, // 平均耗尽时间
AVG_TIME_TO_FULL = 0x13, // 平均充满时间
};
struct sh36606_chip {
struct i2c_client *client;
struct workqueue_struct *shfg_workqueue;
struct delayed_work monitor_work;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
struct power_supply sh_bat;
#else
struct power_supply *sh_bat;
#endif
int battery_temp;
int voltage;
int soc;
int battery_current;
int cycle_count;
int battery_healthy;
int status;
};
static int sh_read_reg(struct i2c_client *client, u8 reg, u8 *buf, int len)
{
struct i2c_msg msgs[2] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = ®,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = buf,
},
};
if (i2c_transfer(client->adapter, msgs, 2) != 2) {
dev_err(&client->dev, "I2C read error\n");
return -EIO;
}
return 0;
}
static int sh_write_reg(struct i2c_client *client, u8 reg, u8 val)
{
u8 buf[2] = {reg, val};
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0,
.len = 2,
.buf = buf,
};
if (i2c_transfer(client->adapter, &msg, 1) != 1) {
dev_err(&client->dev, "I2C write error\n");
return -EIO;
}
return 0;
}
uint8_t crc8_check(uint8_t *data , uint16_t len)
{
uint16_t i = 0;
uint8_t fcs = 0;
for(i=0 ; i<(len) ; i++)
{
fcs = crc8_table[(uint8_t)fcs ^ (*data)];
data++;
}
return fcs;
}
/*读SBS命令,返回两个字节数据
ID :SBS命令号,只能是返回两个字节的子命令
value:返回的两字节数据组成的16bit数据
*/
int SH366100_Read_SBS_Word(struct i2c_client *client, uint8_t reg, uint16_t* val)
{
uint8_t address = 0x16;
uint8_t recieve_data[6] = {0x00};
uint8_t crc_check = 0x00;
recieve_data[0] = address;
recieve_data[1] = reg;
recieve_data[2] = address | 0x01;
if(0 != sh_read_reg( client, reg, recieve_data+3, 3) ){
*val = 0;
return -1;
}
else{
crc_check = crc8_check(recieve_data , 5);
if(crc_check == recieve_data[5] ){
*val = recieve_data[3] + (((uint16_t)recieve_data[4])<<8);
return 0;
}
else{
*val = 0;
return -1;
}
}
}
static int sh366006_get_voltage(struct sh36606_chip *sh_chip)
{
int ret;
uint16_t reg_val[2] = {0, 0};
unsigned int voltage;
ret = SH366100_Read_SBS_Word(sh_chip->client, VOLTAGE, reg_val);
if(ret <0 ) {
sh_chip->voltage = 3000;
return ret;
}
voltage = reg_val[0];
sh_chip->voltage = voltage;
return 0;
}
static int sh366006_get_cycle_count(struct sh36606_chip *sh_chip)
{
int ret;
uint16_t reg_val[2] = {0, 0};
unsigned int cycle_count;
ret = SH366100_Read_SBS_Word(sh_chip->client, CYCLE_COUNT, reg_val);
if(ret <0 ) {
sh_chip->cycle_count = 1;
return ret;
}
cycle_count = reg_val[0];
sh_chip->cycle_count = cycle_count;
return 0;
}
static int sh366006_get_current(struct sh36606_chip *sh_chip)
{
int ret;
uint16_t reg_val[2] = {0, 0};
unsigned int battery_current;
ret = SH366100_Read_SBS_Word(sh_chip->client, AVERAGE_CURRENT, reg_val);
if(ret <0 ) {
sh_chip->battery_current = 65284;
return ret;
}
battery_current = reg_val[0];
sh_chip->battery_current = battery_current;
return 0;
}
static int sh366006_get_soc(struct sh36606_chip *sh_chip)
{
int ret;
uint16_t reg_val[2] = {0, 0};
unsigned int soc;
ret = SH366100_Read_SBS_Word(sh_chip->client, RELATIVE_SOC, reg_val);
if(ret <0 ) {
sh_chip->soc = 1;
return ret;
}
soc = reg_val[0] ;
sh_chip->soc = soc;
return 0;
}
static int sh366006_get_temperature(struct sh36606_chip *sh_chip)
{
int ret;
unsigned char reg_val[2] = {0, 0};
unsigned int temperature;
ret = SH366100_Read_SBS_Word(sh_chip->client, TEMPERATURE, reg_val);
if(ret <0 ) {
sh_chip->battery_temp = 26;
return ret;
}
temperature = reg_val[0] ;
sh_chip->battery_temp = temperature;
return 0;
}
static int sh366006_get_battery_healthy(struct sh36606_chip *sh_chip)
{
int ret;
unsigned char reg_val[2] = {0, 0};
unsigned int healthy;
ret = SH366100_Read_SBS_Word(sh_chip->client, HEALTHY_SOC, reg_val);
if(ret <0 ) {
sh_chip->battery_healthy = 2;
return ret;
}
healthy = reg_val[0] ;
sh_chip->battery_healthy = healthy;
return 0;
}
static int sh_init_data(struct sh36606_chip *sh_chip)
{
int ret = 0;
u16 device_type;
/* 验证芯片ID */
ret = SH366100_Read_SBS_Word(sh_chip->client, DEVICE_TYPE, &device_type);
if (ret != 0 || device_type != 410) {
dev_err(&sh_chip->client->dev, "Invalid chip ID: 0x%02X\n", device_type);
sh_chip->battery_temp = 26;
sh_chip->voltage = 3000;
sh_chip->soc = 50;
sh_chip->battery_current = 65284;
sh_chip->cycle_count = 1;
sh_chip->battery_healthy = 2;
sh_chip->status = POWER_SUPPLY_STATUS_CHARGING;
return -1;
} else {
sh_printk("check ok\r\n");
}
ret += sh366006_get_voltage(sh_chip);
ret += sh366006_get_current(sh_chip);
ret += sh366006_get_soc(sh_chip);
ret += sh366006_get_temperature(sh_chip);
ret += sh366006_get_cycle_count(sh_chip);
ret += sh366006_get_battery_healthy(sh_chip);
sh_chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
sh_printk("voltage : %d, current : %d, soc : %d, temperature : %d, cycle_count:%d, battery_healthy:%d ,ret : %d \n",\
sh_chip->voltage, sh_chip->battery_current, sh_chip->soc, sh_chip->battery_temp, sh_chip->cycle_count, sh_chip->battery_healthy, ret);
return ret;
}
/*
定期循环的工作队列
*/
static void sh366006_monitor_work(struct work_struct *work)
{
struct delayed_work *delay_work;
struct sh36606_chip *sh_chip;
int ret;
delay_work = container_of(work, struct delayed_work, work);
sh_chip = container_of(delay_work, struct sh36606_chip, monitor_work);
ret += sh366006_get_voltage(sh_chip);
ret += sh366006_get_current(sh_chip);
ret += sh366006_get_soc(sh_chip);
ret += sh366006_get_temperature(sh_chip);
ret += sh366006_get_cycle_count(sh_chip);
ret += sh366006_get_battery_healthy(sh_chip);
sh_printk("voltage : %d, current : %d, soc : %d, temperature : %d, cycle_count:%d, battery_healthy:%d ,ret : %d \n",\
sh_chip->voltage, sh_chip->battery_current, sh_chip->soc, sh_chip->battery_temp, sh_chip->cycle_count, sh_chip->battery_healthy, ret);
//向Android系统上报电池参数
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
power_supply_changed(&sh_chip->sh_bat);
#else
power_supply_changed(sh_chip->sh_bat);
#endif
queue_delayed_work(sh_chip->shfg_workqueue, &sh_chip->monitor_work, msecs_to_jiffies(queue_delayed_work_time));
}
static int sh_battery_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
int ret = 0;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
struct sh36606_chip *sh_bat;
sh_bat = container_of(psy, struct sh36606_chip, sh_bat);
#else
struct sh36606_chip *sh_bat = power_supply_get_drvdata(psy);
#endif
switch(psp) {
default:
ret = -EINVAL;
break;
}
return ret;
}
static int sh_get_capacity_level(struct sh36606_chip *sh_bat)
{
if (sh_bat->soc < 1)
return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
else if (sh_bat->soc <= 20)
return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
else if (sh_bat->soc <= 70)
return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
else if (sh_bat->soc <= 90)
return POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
else
return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
}
static int sh_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val)
{
int ret = 0;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
struct sh36606_chip *sh_bat;
sh_bat = container_of(psy, struct sh36606_chip, sh_bat);
#else
struct sh36606_chip *sh_bat = power_supply_get_drvdata(psy);
#endif
switch(psp) {
case POWER_SUPPLY_PROP_CYCLE_COUNT:
val->intval = sh_bat->cycle_count;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = sh_bat->soc;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = sh_bat->voltage <= 0 ? 0 : 1;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = sh_bat->voltage;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = sh_bat->battery_current;
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = 250;
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = POWER_SUPPLY_HEALTH_GOOD;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
val->intval = sh_get_capacity_level(sh_bat);
break;
case POWER_SUPPLY_PROP_STATUS:
val->intval = sh_bat->status;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static enum power_supply_property sh_battery_properties[] = {
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_STATUS,
};
static int sh366006_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
static struct sh36606_chip *sh_bat;
struct power_supply_desc *psy_desc;
struct power_supply_config psy_cfg = {0};
sh_printk("\n");
sh_bat = devm_kzalloc(&client->dev, sizeof(*sh_bat), GFP_KERNEL);
if (!sh_bat) {
sh_printk("%s : sh_bat create fail!\n", __func__);
return -ENOMEM;
}
// sh_printk("fuck 1\n");
i2c_set_clientdata(client, sh_bat);
sh_bat->client = client;
// sh_printk("fuck 2.5\n");
ret = sh_init_data(sh_bat);
if (ret < 0) {
sh_printk(" sh_init_data fail!\n");
// return ret;
}
#ifdef SH_PROPERTIES
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
sh_bat->sh_bat.name = SH_PROPERTIES;
sh_bat->sh_bat.type = POWER_SUPPLY_TYPE_BATTERY;
sh_bat->sh_bat.properties = sh_battery_properties;
sh_bat->sh_bat.num_properties = ARRAY_SIZE(sh_battery_properties);
sh_bat->sh_bat.get_property = sh_battery_get_property;
sh_bat->sh_bat.set_property = sh_battery_set_property;
ret = power_supply_register(&client->dev, &sh_bat->sh_bat);
if (ret < 0) {
power_supply_unregister(&sh_bat->sh_bat);
sh_printk("failed to register battery: %d\n", ret);
return ret;
}
// sh_printk("fuck 2.6\n");
#else
psy_desc = devm_kzalloc(&client->dev, sizeof(*psy_desc), GFP_KERNEL);
if (!psy_desc)
return -ENOMEM;
psy_cfg.drv_data = sh_bat;
psy_desc->name = SH_PROPERTIES;
psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
psy_desc->properties = sh_battery_properties;
psy_desc->num_properties = ARRAY_SIZE(sh_battery_properties);
psy_desc->get_property = sh_battery_get_property;
psy_desc->set_property = sh_battery_set_property;
sh_bat->sh_bat = power_supply_register(&client->dev, psy_desc, &psy_cfg);
if (IS_ERR(sh_bat->sh_bat)) {
ret = PTR_ERR(sh_bat->sh_bat);
sh_printk("failed to register battery: %d\n", ret);
return ret;
}
// sh_printk("fuck 2.7\n");
#endif
#endif
// sh_printk("fuck 3\n");
sh_bat->shfg_workqueue = create_singlethread_workqueue("shfg_gauge");
INIT_DELAYED_WORK(&sh_bat->monitor_work, sh366006_monitor_work);
queue_delayed_work(sh_bat->shfg_workqueue, &sh_bat->monitor_work , msecs_to_jiffies(queue_start_work_time));
// sh_printk("fuck 4\n");
sh_printk("sh366006 driver probe success!\n");
return 0;
}
static int sh366006_remove(struct i2c_client *client)
{
sh_printk("\n");
return 0;
}
static const struct i2c_device_id sh366006_id_table[] = {
{ SHFG_NAME, 0 },
{ }
};
static struct of_device_id sh366006_match_table[] = {
{ .compatible = "shunwei,sh366006", },
{ },
};
static struct i2c_driver sh366006_driver = {
.driver = {
.name = SHFG_NAME,
.owner = THIS_MODULE,
.of_match_table = sh366006_match_table,
},
.probe = sh366006_probe,
.remove = sh366006_remove,
.id_table = sh366006_id_table,
};
static int __init sh366006_init(void)
{
sh_printk("\n");
i2c_add_driver(&sh366006_driver);
return 0;
}
static void __exit sh366006_exit(void)
{
i2c_del_driver(&sh366006_driver);
}
module_init(sh366006_init);
module_exit(sh366006_exit);
MODULE_AUTHOR("SRED Cole");
MODULE_DESCRIPTION("SH366006 Device Driver V0.1");
MODULE_LICENSE("GPL");
设备树
sh366006@0b {
status = "okay";
compatible = "shunwei,sh366006";
reg = <0x0b>;
};