外部电量计驱动代码,直接上代码了,懒,不做细节分析。。。。。
/*
* Fuelgauge battery driver
*
* This package 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.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#define pr_fmt(fmt) "[hy4145] %s: " fmt, __func__
#include <linux/module.h>
#include <linux/param.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/gpio/consumer.h>
#define sh_info pr_info
#define sh_dbg pr_debug
#define sh_err pr_err
#define sh_log pr_err
#define INVALID_REG_ADDR 0xFF
#define SHFS_UPDATE_KEY 0x8F91
#define SH_FG_I2C_DEV_ADDR (0x0b >> 1)
#define hy4145_ADDR 0x0b
#define FG_FLAGS_FD BIT(4)
#define FG_FLAGS_FC BIT(5)
#define FG_FLAGS_DSG BIT(6)
#define FG_FLAGS_RCA BIT(9)
#define DEBUG_INFO 0
#if DEBUG_INFO
#define debug(fmt,x...) printk("[hy4145]:-------------->%s(), line = %d " fmt,__FUNCTION__, __LINE__, ##x);
#else
#define debug(fmt, x...)
#endif
struct sh_fg_chip* sh;
int charge_temp_value = 1000;
int charge_vlot_value = 0;
static int value_temp = 0;
enum sh_fg_device {
HY4145,
};
static const unsigned char *device2str[] = {
"hy4145",
};
struct sh_fg_chip {
struct device *dev;
struct i2c_client *client;
struct mutex update_lock;
struct mutex irq_complete;
bool resume_completed;
u8 chip;
struct power_supply *fg_psy;
struct power_supply_desc fg_psy_d;
};
static int standard_command(struct i2c_client *client,unsigned char addr, unsigned char* buf)
{
int ret = i2c_master_send(client, &addr, 1);
if (ret < 0){
pr_err("i2c_master_send error ret=%d\n", ret);
return -1;
}
ret = i2c_master_recv(client, buf, 2);
if (ret < 0){
pr_err("i2c_master_recv error ret=%d\n", ret);
return -1;
}
return 0;
}
static int get_prop_status(struct sh_fg_chip *sh)
{
unsigned char buf[2];
standard_command(sh->client,0x00,buf);
return buf[1]<<8|buf[0];
}
static int get_prop_flags(struct sh_fg_chip *sh)
{
unsigned char buf[2];
standard_command(sh->client,0x0a,buf);
return buf[1]<<8|buf[0];
}
static int get_prop_present(struct sh_fg_chip *sh){
unsigned char buf[2];
standard_command(sh->client,0x0a,buf);
// BAT_DET
if (buf[1] & (1<<3))
return 1;
else
return 0;
}
static int get_prop_temp(struct sh_fg_chip *sh)
{
unsigned char buf[2];
standard_command(sh->client,0x06,buf);
return buf[1]<<8|buf[0];
}
static int get_prop_averge_volt(struct sh_fg_chip *sh)
{
unsigned char buf[2];
standard_command(sh->client,0x08,buf);
return buf[1]<<8|buf[0];
}
static int get_prop_full_volt(struct sh_fg_chip *sh)
{
unsigned char buf[2];
standard_command(sh->client,0x76,buf);
return buf[1]<<8|buf[0];
}
static int get_prop_averge_current(struct sh_fg_chip *sh)
{
unsigned char buf[2];
standard_command(sh->client,0x30,buf);
return buf[1]<<8|buf[0];
}
static int get_prop_full_design(struct sh_fg_chip *sh)
{
unsigned char buf[2];
standard_command(sh->client,0x12,buf);
return buf[1]<<8|buf[0];
}
static int get_prop_cycle_count(struct sh_fg_chip *sh)
{
unsigned char buf[2];
standard_command(sh->client,0x2a,buf);
return buf[1]<<8|buf[0];
}
static int get_prop_health(struct sh_fg_chip *sh)
{
unsigned char buf[2];
standard_command(sh->client,0x2e,buf);
return buf[1]<<8|buf[0];
}
static int get_prop_capacity(struct sh_fg_chip *sh)
{
unsigned char buf[2];
standard_command(sh->client,0x0e,buf);
return buf[1]<<8|buf[0];
}
static int get_prop_capacity_level(struct sh_fg_chip *sh)
{
unsigned char buf[2];
standard_command(sh->client,0x2c,buf);
return buf[1]<<8|buf[0];
}
void update_info(struct sh_fg_chip *sh)
{
printk("==rrd== update_info:status=0x%x \n", get_prop_status(sh));
printk("==rrd== update_info:flags=0x%x \n",get_prop_flags(sh));
printk("==rrd== update_info:present=%d \n",get_prop_present(sh));
printk("==rrd== update_info:voltage=%d \n",get_prop_averge_volt(sh));
printk("==rrd== update_info:current=%d \n", get_prop_averge_current(sh));
printk("==rrd== update_info:health=%d \n", get_prop_health(sh));
printk("==rrd== update_info:capacity=%d \n", get_prop_capacity(sh));
printk("==rrd== update_info:level=%d \n", get_prop_capacity_level(sh));
printk("==rrd== update_info:temp=%d \n", get_prop_temp(sh));
printk("==rrd== update_info:counter=%d \n", get_prop_cycle_count(sh));
printk("==rrd== update_info:full=%d \n", get_prop_full_volt(sh));
printk("==rrd== update_info:design=%d \n", get_prop_full_design(sh));
printk("==rrd== update_info:count=%d\n", get_prop_cycle_count(sh));
}
static enum power_supply_property fg_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_HEALTH,/*implement it in battery power_supply*/
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
};
static int fg_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct sh_fg_chip *sh = power_supply_get_drvdata(psy);
mutex_lock(&sh->update_lock);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = get_prop_status(sh);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
val->intval = get_prop_averge_volt(sh);
charge_vlot_value = val->intval;
printk("hy4145 %s %d value=%d \n",__func__,__LINE__,val->intval);
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 1;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_CURRENT_AVG:
val->intval = get_prop_averge_current(sh);
debug("hy4145 %s %d value=%d \n",__func__,__LINE__,val->intval);
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = get_prop_capacity_level(sh);
debug("hy4145 %s %d value=%d \n",__func__,__LINE__,val->intval);
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
val->intval = get_prop_capacity_level(sh);
debug("hy4145 %s %d value=%d \n",__func__,__LINE__,val->intval);
break;
case POWER_SUPPLY_PROP_TEMP:
if(value_temp>20){
val->intval = value_temp;
}else{
val->intval = get_prop_temp(sh);
charge_temp_value = val->intval;
}
debug("hy4145 %s %d value=%d \n",__func__,__LINE__,val->intval);
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
//debug("hy4145 %s %d value=%d \n",__func__,__LINE__,val->intval);
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = POWER_SUPPLY_HEALTH_GOOD;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
val->intval = get_prop_full_volt(sh);
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = get_prop_full_design(sh);
break;
case POWER_SUPPLY_PROP_CYCLE_COUNT:
val->intval = get_prop_cycle_count(sh);
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = 870000;
break;
case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
val->intval = 6600*1000;
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
val->intval = 1;
break;
case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
val->intval = 60 * 4 * 60 ;
break;
default:
mutex_unlock(&sh->update_lock);
return -EINVAL;
}
mutex_unlock(&sh->update_lock);
return 0;
}
static int fg_set_property(struct power_supply *psy,
enum power_supply_property prop,
const union power_supply_propval *val)
{
struct sh_fg_chip *sh = power_supply_get_drvdata(psy);
mutex_lock(&sh->update_lock);
switch (prop) {
case POWER_SUPPLY_PROP_TEMP:
break;
case POWER_SUPPLY_PROP_CAPACITY:
break;
default:
mutex_unlock(&sh->update_lock);
return -EINVAL;
}
mutex_unlock(&sh->update_lock);
return 0;
}
static int fg_prop_is_writeable(struct power_supply *psy,
enum power_supply_property prop)
{
int ret;
switch (prop) {
case POWER_SUPPLY_PROP_TEMP:
case POWER_SUPPLY_PROP_CAPACITY:
// case POWER_SUPPLY_PROP_UPDATE_NOW:
ret = 1;
break;
default:
ret = 0;
break;
}
return ret;
}
static int fg_psy_register(struct sh_fg_chip *sh)
{
struct power_supply_config fg_psy_cfg = {};
sh->fg_psy_d.name = "hy4145-fgu";
sh->fg_psy_d.type = POWER_SUPPLY_TYPE_BATTERY;
sh->fg_psy_d.properties = fg_props;
sh->fg_psy_d.num_properties = ARRAY_SIZE(fg_props);
sh->fg_psy_d.get_property = fg_get_property;
sh->fg_psy_d.set_property = fg_set_property;
sh->fg_psy_d.property_is_writeable = fg_prop_is_writeable;
fg_psy_cfg.drv_data = sh;
fg_psy_cfg.num_supplicants = 0;
sh->fg_psy = devm_power_supply_register(sh->dev,
&sh->fg_psy_d,
&fg_psy_cfg);
if (IS_ERR(sh->fg_psy)) {
pr_err("Failed to register fg_psy");
return PTR_ERR(sh->fg_psy);
}
return 0;
}
static ssize_t fg_attr_show_Rtest(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct sh_fg_chip *sh = i2c_get_clientdata(client);
mutex_lock(&sh->update_lock);
update_info(sh);
mutex_unlock(&sh->update_lock);
return 0;
}
static ssize_t fg_attr_store_Rtest(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
char *end;
value_temp = simple_strtoul(buf, &end, 10);
debug("%s:%d: Entry %s value= %d", __FILE__, __LINE__, __func__, value_temp);
return count;
}
//static DEVICE_ATTR(Qmax, S_IRUGO, fg_attr_show_Qmax, NULL);
static DEVICE_ATTR(Rtest, 0664, fg_attr_show_Rtest, fg_attr_store_Rtest);
static struct attribute *fg_attributes[] = {
// &dev_attr_RaTable.attr,
// &dev_attr_Qmax.attr,
&dev_attr_Rtest.attr,
NULL,
};
static const struct attribute_group fg_attr_group = {
.attrs = fg_attributes,
};
static inline bool is_device_suspended(struct sh_fg_chip *sh)
{
return !sh->resume_completed;
}
static int sh_fg_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
sh = devm_kzalloc(&client->dev, sizeof(*sh), GFP_KERNEL);
if (sh == NULL) {
return -ENOMEM;
}
if (!sh)
return -ENOMEM;
sh->dev = &client->dev;
sh->client = client;
sh->chip = id->driver_data;
i2c_set_clientdata(client, sh);
mutex_init(&sh->update_lock);
mutex_init(&sh->irq_complete);
sh->resume_completed = true;
device_init_wakeup(sh->dev, 1);
fg_psy_register(sh);
ret = sysfs_create_group(&sh->dev->kobj, &fg_attr_group);
if (ret)
pr_err("Failed to register sysfs, err:%d\n", ret);
sh_log("sh fuel gauge probe successfully, %s\n",
device2str[sh->chip]);
return 0;
}
static s32 sh_fg_remove(struct i2c_client* client)
{
//struct sh_fg_chip* sh = i2c_get_clientdata(client);
debug("hy4145_remove %s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static void sh_fg_shutdown(struct i2c_client *client)
{
pr_info("sh fuel gauge driver shutdown!\n");
}
static const struct of_device_id sh_fg_match_table[] = {
{
.compatible = "sh,hy4145",
},
{},
};
MODULE_DEVICE_TABLE(of, sh_fg_match_table);
static const struct i2c_device_id sh_fg_id[] = {
{"hy4145", HY4145},
{},
};
MODULE_DEVICE_TABLE(i2c, sh_fg_id);
static struct i2c_driver sh_fg_driver = {
.driver = {
.name = "hy4145",
.owner = THIS_MODULE,
.of_match_table = sh_fg_match_table,
//.pm = &sh_fg_pm_ops,
},
.id_table = sh_fg_id,
.probe = sh_fg_probe,
.remove = sh_fg_remove,
.shutdown = sh_fg_shutdown,
};
static int __init sh_fg_driver_init(void)
{
u8 ret;
ret= i2c_add_driver(&sh_fg_driver);
pr_err("i2c_drivr_init,return=%d\n",ret);
return ret;
}
static void __exit sh_fg_driver_exit(void)
{
i2c_del_driver(&sh_fg_driver);
}
module_init(sh_fg_driver_init);
module_exit(sh_fg_driver_exit);
EXPORT_SYMBOL(charge_vlot_value);
MODULE_DESCRIPTION("SH HY4145 Driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("ruidong.ren@163.com");