基于IMX6ULL平台CW2015电量计驱动

cw2015_battery.c

/************************************************ 
    author: Cole
    date:   2024_8_12
    brief:  CW2015 Driver
 ************************************************/

#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>

/* CW2015寄存器定义 */
#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
/* 有很多人不知道这个电量模型信息是怎么来的,查规格书也查不到0x10之后的偏移地址是干什么的, 其实不用管,我们需要做的是主动联系CW2015厂家赛微电子的AE,去做电池建模才行 ,这里附上人家官网:http://www.cellwise-semi.com/Home/Index/*/

/*  cw_bat_config_info 该模型信息与电路原理图,具体PCB走线的长度,线阻都是有关系的,不可通用。*/
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
};

/*cw2015电源属性数组*/
static enum power_supply_property cw2015_charger_properties[] = {
    /*相关定义在power_supply.h*/
    POWER_SUPPLY_PROP_STATUS, //充电状态
    POWER_SUPPLY_PROP_CHARGE_TYPE, //充电类型
    POWER_SUPPLY_PROP_HEALTH,//电池健康状态
    POWER_SUPPLY_PROP_CAPACITY,//电量百分比
    POWER_SUPPLY_PROP_VOLTAGE_NOW, //当前电压应该是实际值的1000倍
    //POWER_SUPPLY_PROP_VOLTAGE_MAX, //电压最大值测量值
    //POWER_SUPPLY_PROP_VOLTAGE_MIN, //电压最小值测量值
    POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, //电压设计最大值
    POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, //电压设计最小值
    POWER_SUPPLY_PROP_CAPACITY_LEVEL, //电池容量
};

/* CW2015驱动结构体 */
struct cw2015_battery
{
    struct i2c_client *client;
    struct power_supply *psy; /*电源管理结构体*/

    int voltage;            //电压
    int charge_status;      //充电状态
    int capacity;           //电量百分比
    int charge_gpio;        //充电检测GPIO
};

 
/*************************** CW2015操作函数 ***********************************/

/**
 * @brief : 从cw2105读取多个寄存器数据
 * @param - bat : cw2015 设备
 * @param - reg : 要读取的寄存器首地址
 * @param - val : 读取到的数据
 * @param - len : 要读取的数据长度
 * @return      : 操作结果
 * 
 */
static int cw2015_read_regs(struct cw2015_battery *bat, u8 reg, void *val, int len)
{
    int ret;
    struct i2c_msg msg[2];

    /* msg[0]为发送要读取的首地址 */
    msg[0].addr = bat->client->addr;        /*cw2015地址*/
    msg[0].flags = 0;                       /*标记为发送数据*/
    msg[0].buf = &reg;                      /*读取的首地址*/
    msg[0].len = 1;                         /*reg的长度*/

    msg[1].addr = bat->client->addr;        /*cw2015地址*/
    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;
}

/*
 * @description : 向cw2015多个寄存器写入数据
* @param- bat: cw2015设备
* @param- reg: 要写入的寄存器首地址
* @param- val: 要写入的数据缓冲区
* @param- len: 要写入的数据长度
* @return
 : 操作结果
*/
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);
    /* 寄存器首地址 */
    /* 将要写入的数据拷贝到数组b里面 */
    msg.addr = bat->client->addr; /* cw2015 地址 */
    msg.flags = 0;
    /* 标记为写数据 */
    msg.buf = b;
    msg.len = len + 1;
    /* 要写入的数据缓冲区 */
    /* 要写入的数据长度 */
    return i2c_transfer(bat->client->adapter, &msg, 1);
 }

/*
 * @description : 读取cw2015指定寄存器值,读取一个寄存器
* @param- bat: cw2015设备
* @param- reg: 要读取的寄存器
* @return
 : 读取到的寄存器值
*/
 static unsigned char cw2015_read_reg(struct cw2015_battery *bat, u8 reg)
 {
    u8 data = 0;

    cw2015_read_regs(bat, reg, &data, 1);
    return data;
 }

 /*
 * @description : 向cw2015指定寄存器写入指定的值,写一个寄存器
* @param- bat: cw2015设备
* @param- reg: 要写的寄存器
* @param- data: 要写入的值
* @return : 无
*/
 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);
 }

/*
* @description : 读取cw2015的数据
* @param- bat: cw2015设备
* @return: 无
*/
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);
    /* 电压的分辨率为305uV */
    /* 读取电压 */
    BATVADC_VALUE_high = cw2015_read_reg(bat, REG_VCELL);
    BATVADC_VALUE_low = cw2015_read_reg(bat, REG_VCELL+1);
    /* 转换为14位ADC值 */
    bat->voltage = (BATVADC_VALUE_high<<8)+BATVADC_VALUE_low;
    /* 转换为电压值并且乘以系数1000供应用层使用 */
    /* 应用层读取到的就是以mv为单位的电压值 */
    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 {
        /*充电检测的GPIO为空,充电状态指定为UNKNOW*/
        bat->charge_status=POWER_SUPPLY_STATUS_UNKNOWN;
    }
 }

  /*
 *@description :CW2015初始化
*@return : 0-cw2015存在other-没有检测到cw2015
 */
 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) {
        /*未检测到cw2015*/
        return-1;
    }
    return 0;
 }

 /*cw2015属性值获取函数*/
 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;
            /*
            POWER_SUPPLY_STATUS_UNKNOWN
            POWER_SUPPLY_STATUS_CHARGING
            POWER_SUPPLY_STATUS_DISCHARGING
            POWER_SUPPLY_STATUS_NOT_CHARGING
            POWER_SUPPLY_STATUS_FULL
            */
            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,                    /*电源属性,用enum列出*/
    .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驱动设备的client数据 */
    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;
    /* 获取充电检测GPIO */
    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的驱动数据,指定后可以根据power_supply来获取到设备数据 */
    psy_cfg.drv_data = cw_bat;
    /* 检测cw2015是否存在 */
    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;
    }
    /*注册power_supply,注册成功后会在/sys/class/power_supply/下面生成设备目录*/
    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);
    }

    /* 往CW2015写入电池信息 */
	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;
}

/* 传统匹配方式ID列表 */
static const struct i2c_device_id cw2015_id[] = {
    {"cw2015_battery", 0},
    {}
};

 /* 设备树匹配列表 */
 static const struct of_device_id cw2015_of_match[] = {
    { .compatible = "cw2015_battery" }, 
    {}
 };

 /* i2c驱动结构体 */
 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";
		};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值