cw2015_battery.c
#include<linux/types.h>
#include<linux/kernel.h>
#include<linux/delay.h>
#include<linux/ide.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/errno.h>
#include<linux/gpio.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of_gpio.h>
#include<linux/semaphore.h>
#include<linux/timer.h>
#include<linux/i2c.h>
#include<asm/mach/map.h>
#include<asm/uaccess.h>
#include<asm/io.h>
#include<linux/power_supply.h>
#define REG_VERSION 0x00
#define REG_VCELL 0x02
#define REG_SOC 0x04
#define REG_RRT_ALERT 0x06
#define REG_CONFIG 0x08
#define REG_MODE 0x0A
#define REG_BATINFO 0x10
#define SIZE_BATINFO 64
static unsigned char cw_bat_config_info[SIZE_BATINFO] = {
0X15,0X7E,0X7C,0X5C,0X64,0X6A,0X65,0X5C,0X55,0X53,0X56,0X61,0X6F,0X66,0X50,0X48,
0X43,0X42,0X40,0X43,0X4B,0X5F,0X75,0X7D,0X52,0X44,0X07,0XAE,0X11,0X22,0X40,0X56,
0X6C,0X7C,0X85,0X86,0X3D,0X19,0X8D,0X1B,0X06,0X34,0X46,0X79,0X8D,0X90,0X90,0X46,
0X67,0X80,0X97,0XAF,0X80,0X9F,0XAE,0XCB,0X2F,0X00,0X64,0XA5,0XB5,0X11,0XD0,0X11
};
static enum power_supply_property cw2015_charger_properties[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
};
struct cw2015_battery
{
struct i2c_client *client;
struct power_supply *psy;
int voltage;
int charge_status;
int capacity;
int charge_gpio;
};
static int cw2015_read_regs(struct cw2015_battery *bat, u8 reg, void *val, int len)
{
int ret;
struct i2c_msg msg[2];
msg[0].addr = bat->client->addr;
msg[0].flags = 0;
msg[0].buf = ®
msg[0].len = 1;
msg[1].addr = bat->client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = val;
msg[1].len = len;
ret = i2c_transfer(bat->client->adapter, msg, 2);
if (ret == 2) {
ret = 0;
} else {
printk("i2c rd failed = %d reg = %06x len = %d \n", ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
}
static s32 cw2015_write_regs(struct cw2015_battery *bat, u8 reg, u8 *buf, u8 len)
{
u8 b[256];
struct i2c_msg msg;
b[0] = reg;
memcpy(&b[1],buf,len);
msg.addr = bat->client->addr;
msg.flags = 0;
msg.buf = b;
msg.len = len + 1;
return i2c_transfer(bat->client->adapter, &msg, 1);
}
static unsigned char cw2015_read_reg(struct cw2015_battery *bat, u8 reg)
{
u8 data = 0;
cw2015_read_regs(bat, reg, &data, 1);
return data;
}
static void cw2015_write_reg(struct cw2015_battery *bat, u8 reg, u8 data)
{
u8 buf = 0;
buf = data;
cw2015_write_regs(bat, reg, &buf, 1);
}
static void cw2015_read_data(struct cw2015_battery *bat)
{
u8 BATVADC_VALUE_low,BATVADC_VALUE_high;
u8 value;
cw2015_write_reg(bat, REG_MODE, 0x00);
BATVADC_VALUE_high = cw2015_read_reg(bat, REG_VCELL);
BATVADC_VALUE_low = cw2015_read_reg(bat, REG_VCELL+1);
bat->voltage = (BATVADC_VALUE_high<<8)+BATVADC_VALUE_low;
bat->voltage = bat->voltage * 305 / 1000;
bat->capacity = cw2015_read_reg(bat, REG_SOC);
if (bat->charge_gpio !=-1) {
value = gpio_get_value(bat->charge_gpio);
if (value == 1) {
bat->charge_status = POWER_SUPPLY_STATUS_CHARGING;
} else {
bat->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
}
} else {
bat->charge_status=POWER_SUPPLY_STATUS_UNKNOWN;
}
}
static int cw2015_bat_init(struct cw2015_battery *bat)
{
int ret = 0;
u8 data = 0;
ret = cw2015_write_regs(bat, REG_MODE, &data, 1);
if(ret < 0) {
return-1;
}
return 0;
}
static int cw2015_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val)
{
struct cw2015_battery *cw_bat = power_supply_get_drvdata(psy);
switch(psp) {
case POWER_SUPPLY_PROP_STATUS:
cw2015_read_data(cw_bat);
val->intval = cw_bat->charge_status;
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = POWER_SUPPLY_HEALTH_GOOD;
break;
case POWER_SUPPLY_PROP_CAPACITY:
cw2015_read_data(cw_bat);
val->intval = cw_bat->capacity;
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
val->intval = 4000;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
cw2015_read_data(cw_bat);
val->intval = cw_bat->voltage;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = 4300;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
val->intval = 3300;
break;
default:
return-EINVAL;
}
return 0;
}
static const struct power_supply_desc cw2015_psy_desc = {
.name = "cw2015-battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.get_property = cw2015_get_property,
.properties = cw2015_charger_properties,
.num_properties = ARRAY_SIZE(cw2015_charger_properties),
};
static int cw2015_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
int loop=0;
int i;
struct cw2015_battery *cw_bat;
struct power_supply_config psy_cfg={};
cw_bat = devm_kzalloc(&client->dev, sizeof(*cw_bat), GFP_KERNEL);
if(!cw_bat) {
return -ENOMEM;
}
i2c_set_clientdata(client, cw_bat);
cw_bat->client = client;
cw_bat->voltage = 0;
cw_bat->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
cw_bat->capacity = 0;
cw_bat->charge_gpio =-1;
if (of_get_property(client->dev.of_node, "cw2015_charge_gpio", NULL)) {
cw_bat->charge_gpio = of_get_named_gpio(client->dev.of_node, "cw2015_charge_gpio", 0);
} else {
pr_warning("cw2015 gpio get failed\n");
cw_bat->charge_gpio = -1;
}
if (gpio_is_valid(cw_bat->charge_gpio)) {
devm_gpio_request_one(&client->dev, cw_bat->charge_gpio, GPIOF_IN, "cw2015-charge");
} else {
cw_bat->charge_gpio = -1;
}
psy_cfg.drv_data = cw_bat;
ret = cw2015_bat_init(cw_bat);
while ((loop++ < 200) && (ret != 0)) {
msleep(200);
ret = cw2015_bat_init(cw_bat);
}
if (ret) {
pr_warning("cw2015 is not found\n");
return ret;
}
cw_bat->psy = power_supply_register(&client->dev, &cw2015_psy_desc, &psy_cfg);
if(IS_ERR(cw_bat->psy)){
dev_err(&client->dev, "failed : power supply register\n");
return PTR_ERR(cw_bat->psy);
}
for (i = 0; i < SIZE_BATINFO; i++) {
cw2015_write_reg(cw_bat, REG_BATINFO + i, cw_bat_config_info[i]);
}
pr_info("cw2015 is found\n");
return 0;
}
static int cw2015_remove(struct i2c_client *client)
{
return 0;
}
static const struct i2c_device_id cw2015_id[] = {
{"cw2015_battery", 0},
{}
};
static const struct of_device_id cw2015_of_match[] = {
{ .compatible = "cw2015_battery" },
{}
};
static struct i2c_driver cw2015_driver = {
.probe = cw2015_probe,
.remove = cw2015_remove,
.driver = {
.owner = THIS_MODULE,
.name = "cw2015_battery",
.of_match_table = cw2015_of_match,
},
.id_table = cw2015_id,
};
static int __init cw2015_init(void)
{
int ret = 0;
ret = i2c_add_driver(&cw2015_driver);
return ret;
}
static void __exit cw2015_exit(void)
{
i2c_del_driver(&cw2015_driver);
}
module_init(cw2015_init);
module_exit(cw2015_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CW2015 Battery Driver");
MODULE_AUTHOR("Cole");
设备树
cw2015: cw2015@62{
compatible = "cw2015_battery";
cw2015_charge_gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;
reg = <0x62>;
status = "okay";
};