📄 ds2786_battery.c
字号:
#include <linux/module.h>#include <linux/param.h>#include <linux/jiffies.h>#include <linux/workqueue.h>#include <linux/pm.h>#include <linux/platform_device.h>#include <linux/power_supply.h>#include <linux/i2c.h>#include <linux/device.h>#include <linux/delay.h>#include <linux/mutex.h>#include <linux/interrupt.h>#include <asm/io.h>#include <asm/arch/regs-irq.h>#include <asm/plat-s3c24xx/irq.h>#include <asm/hardware.h>#include "ds2786_battery.h"#define DBG(format, arg...) do { \printk(KERN_ALERT"%s: " format "\n" , __FUNCTION__ , ## arg); \} while (0)#ifdef DEBUG#define dprintk (format,arg...) printk(format,arg...)#else#define DBG(format, arg...) do {} while (0)#define dprintk(format,arg...) do {} while (0)#endifstruct ds2786_device_info { struct device *dev; /* DS2760 data, valid after calling ds2786_battery_read_status() */ unsigned long update_time; /* jiffies when data read */ char raw[DS2786_DATA_SIZE]; /* raw DS2786 data */ int voltage_raw; /* units of 1.22 mV */ int voltage_uV; /* units of µV */ int current_raw; /* units of 0.625 mA */ int current_uA; /* units of µA */ int accum_current_uAh; /* units of µAh */ int temp_raw; /* units of 0.125 °C */ int temp_C; /* units of 0.1 °C */ int rated_capacity; /* units of µAh */ int rem_capacity; /* percentage */ int full_active_uAh; /* units of µAh */ int empty_uAh; /* units of µAh */ int life_sec; /* units of seconds */ int charge_status; /* POWER_SUPPLY_STATUS_* */ int full_counter; struct power_supply bat; struct i2c_client *client; struct i2c_driver ds2786_driver; struct workqueue_struct *monitor_wqueue; struct delayed_work monitor_work;};static unsigned int cache_time = 1000;module_param(cache_time, uint, 0644);MODULE_PARM_DESC(cache_time, "cache time in milliseconds");//i2c partstatic unsigned short normal_i2c[] = { 0x36, I2C_CLIENT_END };static struct i2c_client_address_data addr_data = { .normal_i2c = normal_i2c, .probe = &normal_i2c[1], .ignore = &normal_i2c[1], };struct ds2786_device_info *di;static int ds2786_attach_adapter(struct i2c_adapter *adapter);static int ds2786_detect(struct i2c_adapter *adapter, int address, int kind);static int ds2786_detach_client(struct i2c_client *client);int i2c_ds2786_write(struct i2c_client *client,char *buf ,int count){ int ret=0; struct i2c_adapter *adap=client->adapter; struct i2c_msg msg; if (client->adapter->algo->master_xfer) { msg.addr = client->addr; msg.flags = client->flags & I2C_M_TEN; msg.len = count; msg.buf = buf; dev_dbg(&client->adapter->dev, "master_send: writing %d bytes.\n",count); mutex_lock_nested(&adap->bus_lock, adap->level); msg.len=count+1; ret = adap->algo->master_xfer(adap,&msg,1); mutex_unlock(&adap->bus_lock); return (ret==count )? count : -EBUSY; } dev_err(&client->adapter->dev, "I2C level transfers not supported\n"); return -ENOSYS;}int i2c_ds2786_read(struct i2c_client *client, char *buf ,int pos,int count){ struct i2c_adapter *adap=client->adapter; struct i2c_msg msg[2]; int ret; char addr=pos; //return count; if (client->adapter->algo->master_xfer) { msg[0].addr = client->addr; msg[0].flags = client->flags & I2C_M_TEN; msg[0].len = 1; msg[0].buf = &addr; msg[1].addr = client->addr; msg[1].flags = client->flags & I2C_M_TEN; msg[1].flags |= I2C_M_RD; msg[1].len = count; msg[1].buf = buf; dev_dbg(&client->adapter->dev, "master_recv: reading %d bytes.\n",count); mutex_lock_nested(&adap->bus_lock, adap->level); ret = adap->algo->master_xfer(adap,msg,2); mutex_unlock(&adap->bus_lock); dev_dbg(&client->adapter->dev, "master_recv: return:%d (count:%d, addr:0x%02x)\n", ret, count, client->addr); return (ret == 2 )? count : ret; } dev_err(&client->adapter->dev, "I2C level transfers not supported\n"); return -ENOSYS;}static int ds2786_attach_adapter(struct i2c_adapter *adapter){ DBG("\n"); printk(KERN_ALERT"%s: \n" , __FUNCTION__ ); return i2c_probe(adapter, &addr_data, ds2786_detect);}/* This function is called by i2c_probe */static int ds2786_detect(struct i2c_adapter *adapter, int address, int kind){ int err = 0; DBG("\n"); printk(KERN_ALERT"%s: \n" , __FUNCTION__ ); if (!(di->client= kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { err = -ENOMEM; goto exit; } i2c_set_clientdata(di->client,di->client); di->client->addr = address; di->client->adapter = adapter; di->client->driver = &di->ds2786_driver; di->client->flags = 0; /* Fill in the remaining client fields */ strlcpy(di->client->name, "ds2786", strlen("ds2786")+1); /* Detect the ds2786.*/ err=i2c_ds2786_read(di->client,di->raw,0,1); printk(KERN_ALERT"err:%d\n",err); if (err<0) goto exit_kfree; /* Tell the I2C layer a new client has arrived */ if ((err = i2c_attach_client(di->client))) goto exit_kfree; return 0;exit_kfree: kfree(di->client);exit: return err;}static int ds2786_detach_client (struct i2c_client *client){ int err; err = i2c_detach_client(client); if (err) return err; kfree(i2c_get_clientdata(client)); return 0;}//end of i2c part//end of apm partstatic int ds2786_battery_read_status(struct ds2786_device_info *di){ int ret, start, count; if (di->update_time && time_before(jiffies, di->update_time + msecs_to_jiffies(cache_time))) return 0; DBG("\n"); /* The first time we read the entire contents of SRAM/EEPROM, * but after that we just read the interesting bits that change. */ //return -1; if (di->update_time == 0) { start = 0; count = DS2786_DATA_SIZE; dprintk(KERN_ALERT"first\n"); } else { start = DS2786_RELATIVE_CAP; count = DS2786_LEARNED_CAPACITY- start + 1; } count=DS2786_DATA_SIZE-start+1; ret = i2c_ds2786_read(di->client, di->raw + start, start, count); if (ret != count) { dev_warn(di->dev, "call to i2c_ds2786_read failed\n"); return 1; } di->update_time = jiffies; /* DS2786 reports voltage in signed units of 1.22mV, but the battery class * reports in units of uV, so convert by multiplying by 1220. */ di->voltage_raw = (((signed char)di->raw[DS2786_VOLTAGE_MSB]) << 5)| (di->raw[DS2786_VOLTAGE_LSB] >> 3); di->voltage_uV = di->voltage_raw * 1220; if (di->voltage_uV<1000*1000) { di->rem_capacity =-1; di->life_sec = -1; return -1; } dprintk(KERN_ALERT"di->voltage_uV=%d\n",di->voltage_uV); /* DS2786 reports current in signed units of 2.5mA, but the battery * class reports in units of µA, so convert by multiplying by 2500. */ di->current_raw = (((signed char)di->raw[DS2786_CURRENT_MSB]) << 4) | (di->raw[DS2786_CURRENT_LSB] >> 4); di->current_uA = di->current_raw * 2500; dprintk(KERN_ALERT"di->current_uA=%d\n",di->current_uA); /* DS2760 reports temperature in signed units of 0.125°C, but the * battery class reports in units of 1/10 °C, so we convert by * multiplying by .125 * 10 = 1.25. */ di->temp_raw = (((signed char)di->raw[DS2786_AUX1_TEMP_MSB]) << 3) |(di->raw[DS2786_AUX1_TEMP_LSB] >> 5); di->temp_C = di->temp_raw + (di->temp_raw / 4); dprintk(KERN_ALERT"di->temp_C=%d\n",di->temp_C); /*cap=1/(reg_val*0.78125*Rsns) Rsns=0.01*/ i2c_ds2786_read(di->client,di->raw+DS2786_INITIAL_CAP,DS2786_INITIAL_CAP,1); //di->rated_capacity = 128*1000000L/(long)di->raw[DS2786_INITIAL_CAP]; dprintk(KERN_ALERT"di->raw[DS2786_INITIAL_CAP]=%d\n",(int)di->raw[DS2786_INITIAL_CAP]); dprintk(KERN_ALERT"di->rated_capacity=%d\n",di->rated_capacity); if (!di->raw[DS2786_LEARNED_CAPACITY]) di->full_active_uAh = di->rated_capacity; else di->full_active_uAh=128*1000000L/di->raw[DS2786_LEARNED_CAPACITY]; dprintk(KERN_ALERT"di->full_active_uAh=%d\n",di->full_active_uAh); di->empty_uAh = (di->full_active_uAh/100)*5;//(5%) //printk(KERN_ALERT"di->empty_uAh=%d\n",di->empty_uAh); di->rem_capacity = di->raw[DS2786_RELATIVE_CAP]/2; //printk(KERN_ALERT"di->rem_capacity=%d\n",di->rem_capacity); di->accum_current_uAh =-((di->raw[DS2786_RELATIVE_CAP]-di->raw[DS2786_LAST_OCV_RELATIVE_CAP])) /2*di->full_active_uAh; dprintk(KERN_ALERT"di->accum_current_uAh=%d\n",di->accum_current_uAh); dprintk(KERN_ALERT"DS2786_LAST_OCV_RELATIVE_CAP=%d\n",di->raw[DS2786_LAST_OCV_RELATIVE_CAP]); dprintk(KERN_ALERT"DS2786_RELATIVE_CAP=%d\n",di->raw[DS2786_RELATIVE_CAP]); if (di->current_uA<-10*1000) { di->life_sec = -((((di->full_active_uAh*di->rem_capacity)/100 - di->empty_uAh)) / di->current_uA)*3600L; //di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; } else if (di->current_uA>10*1000) {//charge di->life_sec = -((((long int)di->full_active_uAh*(long int)(200-di->raw[DS2786_RELATIVE_CAP]))/100) /di->current_uA)*1800L; //di->charge_status = POWER_SUPPLY_STATUS_CHARGING; } else di->life_sec = 0; dprintk(KERN_ALERT"di->life_sec=%d\n",di->life_sec); return 0;}static void ds2786_battery_update_status(struct ds2786_device_info *dx){ int old_charge_status = di->charge_status; DBG("\n"); ds2786_battery_read_status(di); if (di->rem_capacity<0) { di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; if (di->charge_status != old_charge_status) power_supply_changed(&di->bat); return; } if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN) di->full_counter = 0; if (di->current_uA > 10*1000) { di->charge_status = POWER_SUPPLY_STATUS_CHARGING; di->full_counter = 0; } else if(di->current_uA < -10*1000) { di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -