/*M-MT:setup discharge/charge current threshold.*/ int bq27410_write_current_threshold(struct i2c_client *client) { int ret = 0; int dsg_current_threshold = 0; u8 buf[16]; // 1. unseal bq27410. bq27410_unseal_device(); // 2. write BlockDataControl command to enable block data flash control. buf[0] = 0x00; bq27410_write(client,0x61,buf,1); udelay(66); // 3. write subclass id. buf[0] = 0x51; bq27410_write(client,0x3E,buf,1); udelay(66); // 4. write subclass current offset. buf[0] = 0x00; bq27410_write(client,0x3F,buf,1); udelay(66); // 5. read old dsg current. ret = bq27410_read(client,0x40,buf,2); dsg_current_threshold = get_unaligned_le16(buf); DBG("dsg_current_threshold = 0x%x \n",dsg_current_threshold); #if 0 ret = bq27410_read(client,0x40,buf,16); { int i; for(i =0; i< 16; i++) { DBG("0x%x ",buf[i]); } DBG(" \n"); } #endif // 6. read old checksum. //ret = bq27410_read(client,0x60,buf,1); //DBG("checksum = 0x%x \n",buf[0]); #if 0 // 7. write new taper current. buf[0] = 0x00; buf[1] = 0xC8; bq27410_write(client,0x40,buf,2); udelay(66); ret = bq27410_read(client,0x40,buf,2); dsg_current_threshold = get_unaligned_le16(buf); DBG("dsg_current_threshold =0x%x \n",dsg_current_threshold); // 8. write new checksum. #endif bq27410_soft_reset(); bq27410_sealed(); return 0; }
/* * Return the battery Relative State-of-Charge * Or < 0 if something fails. */ static int bq27410_battery_rsoc(struct bq27410_device_info *di) { int ret; int rsoc = 0; int flags = 0; int status = 0; u8 buf[2]; if(virtual_battery_enable == 1) return 50/*100*/; ret = bq27410_read(di->client,BQ27410_REG_STATE_OF_CHARGER,buf,2); if (ret<0) { dev_err(di->dev, "error reading relative State-of-Charge\n"); return ret; } rsoc = get_unaligned_le16(buf); DBG("Enter:%s %d--read reaming capacity = %d\n",__FUNCTION__,__LINE__,rsoc); /* covert the capacity range */ rsoc = min(rsoc, 100); if ((g_pdata != NULL) && g_pdata->capacity_max && g_pdata->capacity_min) { rsoc = max(rsoc, g_pdata->capacity_min); rsoc = ((rsoc - g_pdata->capacity_min) * 100 + (g_pdata->capacity_max - g_pdata->capacity_min) / 2) / (g_pdata->capacity_max - g_pdata->capacity_min); } bq27410_cap = rsoc; /*check full flags,if not full, show 99%*/ ret = bq27410_read(di->client,BQ27410_REG_FLAGS, buf, 2); if (ret < 0) { dev_err(di->dev, "error reading flags\n"); return ret; } flags = get_unaligned_le16(buf); DBG("Enter:%s %d--flags = 0x%x\n",__FUNCTION__,__LINE__,flags); if ((bq27410_cap > 99) && (flags & BQ27410_FLAG_FC)) status = POWER_SUPPLY_STATUS_FULL; if(status != POWER_SUPPLY_STATUS_FULL) rsoc = min(rsoc, 99); DBG("Enter:%s %d--cal rsoc = %d\n",__FUNCTION__,__LINE__,rsoc); return rsoc; }
static int bq27410_health_status(struct bq27410_device_info *di, union power_supply_propval *val) { u8 buf[2] = {0}; int flags = 0; int status; int ret; if(virtual_battery_enable == 1) { val->intval = POWER_SUPPLY_HEALTH_GOOD; return 0; } ret = bq27410_read(di->client,BQ27410_REG_FLAGS, buf, 2); if (ret < 0) { dev_err(di->dev, "error reading flags\n"); return ret; } flags = get_unaligned_le16(buf); DBG("Enter:%s %d--status = %x\n",__FUNCTION__,__LINE__,flags); if (flags & BQ27410_FLAG_SOCF) status = POWER_SUPPLY_HEALTH_DEAD; else if(flags & BQ27410_FLAG_OTC) status = POWER_SUPPLY_HEALTH_OVERHEAT; else status = POWER_SUPPLY_HEALTH_GOOD; val->intval = status; return 0; }
static int bq27410_battery_voltage(struct bq27410_device_info *di) { int ret; u8 buf[2] = {0}; int volt = 0; if(virtual_battery_enable == 1) return 4000000/*4000000*/; ret = bq27410_read(di->client,BQ27410_REG_VOL,buf,2); if (ret<0) { dev_err(di->dev, "error reading voltage\n"); return ret; } volt = get_unaligned_le16(buf); if(di->bat_num == 2){ volt = volt * 1000 * 2; }else{ volt = volt * 1000; } DBG("Enter:%s %d--volt = %d\n",__FUNCTION__,__LINE__,volt); return volt; }
/* * Return the battery average current * Note that current can be negative signed as well * Or 0 if something fails. */ static int bq27410_battery_current(struct bq27410_device_info *di) { int ret; int curr = 0; u8 buf[2] = {0}; if(virtual_battery_enable == 1) return 11000/*22000*/; ret = bq27410_read(di->client,BQ27410_REG_AVERAGE_CURRENT,buf,2); if (ret<0) { dev_err(di->dev, "error reading current\n"); return 0; } curr = get_unaligned_le16(buf); DBG("curr = %x \n",curr); if(curr > 0x8000){ curr = 0xFFFF ^ (curr -1); } curr = curr * 1000; DBG("Enter:%s %d--curr = %d\n",__FUNCTION__,__LINE__,curr); return curr; }
static int bq27410_battery_temperature(struct bq27410_device_info *di) { int ret; int temp = 0; u8 buf[2] ={0}; if(virtual_battery_enable == 1) return 125/*258*/; ret = bq27410_read(di->client,BQ27410_REG_TEMP,buf,2); if (ret<0) { dev_err(di->dev, "error reading temperature\n"); return ret; } temp = get_unaligned_le16(buf); //temp = 5 * temp / 2; temp = temp - 2731; //K DBG("Enter:%s %d--temp = %d\n",__FUNCTION__,__LINE__,temp); #if defined(CONFIG_CHARGER_LIMITED_BY_TEMP) if((temp >= 450) && (0 == charge_en_flags)){ bq24196_charge_disable(); charge_en_flags = 1; }else if((temp <= 400) && (1 == charge_en_flags)){ bq24196_charge_en(); charge_en_flags = 0; } #endif return temp; }
/*M-MT:write batt insert command.*/ int bq27410_write_batt_insert(struct i2c_client *client) { int ret = 0; int control_status = 0; u8 buf[2]; int flags = 0; if(g_bq27410_mode == BQ27410_NORMAL_MODE){ buf[0] = 0x00; //CONTROL_STATUS buf[1] = 0x00; bq27410_write(client,0x00,buf,2); udelay(66); ret = bq27410_read(client,0x00,buf,2); control_status = get_unaligned_le16(buf); printk("control_status=0x%x \n",control_status); ret = bq27410_read(client,BQ27410_REG_FLAGS, buf, 2); if (ret < 0) { DBG("error reading flags\n"); return ret; } flags = get_unaligned_le16(buf); printk("1 Enter:%s --flags = 0x%x\n",__FUNCTION__,flags); if (!(flags & BQ27410_FLAG_BAT_DET)) { buf[0] = 0x0c; //batt insert buf[1] = 0x00; bq27410_write(client,0x00,buf,2); udelay(66); ret = bq27410_read(client,BQ27410_REG_FLAGS, buf, 2); if (ret < 0) { DBG("error reading flags\n"); return ret; } flags = get_unaligned_le16(buf); if (!(flags & BQ27410_FLAG_BAT_DET)){ printk("27410: %s, flags = 0x%x, set virtual_battery_enable = 1\n",__FUNCTION__,flags); virtual_battery_enable = 1; } } } return 0; }
void bq27410_sealed(void) { int ret = 0; u8 buf[2]; if(g_bq27410_mode == BQ27410_NORMAL_MODE){ buf[0] = 0x20; //seal buf[1] = 0x00; bq27410_write(g_client,0x00,buf,2); udelay(66); ret = bq27410_read(g_client,0x00,buf,2); } }
static int bq27410_battery_status(struct bq27410_device_info *di, union power_supply_propval *val) { u8 buf[2] = {0}; int flags = 0; int status = 0; int ret = 0; if(virtual_battery_enable == 1) { val->intval = POWER_SUPPLY_STATUS_FULL; return 0; } ret = bq27410_read(di->client,BQ27410_REG_FLAGS, buf, 2); if (ret < 0) { dev_err(di->dev, "error reading flags\n"); return ret; } flags = get_unaligned_le16(buf); DBG("Enter:%s %d--flags = 0x%x\n",__FUNCTION__,__LINE__,flags); if ((g_pdata != NULL) && g_pdata->get_charging_stat) { #ifdef CONFIG_BATTERY_BQ24196_OTG_MODE if(!(*g_pdata->get_charging_stat)() || (bq24196_mode == 1)) #else if(!(*g_pdata->get_charging_stat)()) #endif status = POWER_SUPPLY_STATUS_DISCHARGING; else if(1 == (*g_pdata->get_charging_stat)()){ if ((bq27410_cap > 99) && (flags & BQ27410_FLAG_FC)) status = POWER_SUPPLY_STATUS_FULL; else status = POWER_SUPPLY_STATUS_CHARGING; } } val->intval = status; DBG("Enter:%s %d--status = %x\n",__FUNCTION__,__LINE__,status); return 0; }
static int bq27410_battery_rsoc_level(struct bq27410_device_info *di, union power_supply_propval *val) { u8 buf[2] = {0}; int flags = 0; int status = 0; int ret = 0; if(virtual_battery_enable == 1) { val->intval = POWER_SUPPLY_STATUS_FULL; return 0; } ret = bq27410_read(di->client,BQ27410_REG_FLAGS, buf, 2); if (ret < 0) { dev_err(di->dev, "error reading flags\n"); return ret; } flags = get_unaligned_le16(buf); DBG("Enter:%s %d--flags = 0x%x\n",__FUNCTION__,__LINE__,flags); if (flags & BQ27410_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; else if (flags & BQ27410_FLAG_SOC1) status = POWER_SUPPLY_CAPACITY_LEVEL_LOW; else if (flags & BQ27410_FLAG_SOCF) status = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; else status = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; val->intval = status; DBG("Enter:%s %d--status = %x\n",__FUNCTION__,__LINE__,status); return 0; }
static int bq27410_update_flash_data(struct file *filp, const char __user *buff, unsigned long len, void *data) { int ret = 0; u8 buf[2]; bq27410_unseal_device(); bq27410_update_enter_rom_mode(); ret = bq27410_read(g_client,0x66,buf,2); if(ret < 0) bq27410_update_flag = 1; else ret = bq27410_update_fw(filp, buff, len, data); g_bq27410_mode = BQ27410_NORMAL_MODE; g_client->addr = 0x55; bq27410_exit_rom_mode(); bq27410_soft_reset(); bq27410_sealed(); return ret; }
static int bq27410_battery_temperature(struct bq27410_device_info *di) { int ret; int temp = 0; u8 buf[2] ={0}; if(virtual_battery_enable == 1) return 125/*258*/; ret = bq27410_read(di->client,BQ27410_REG_TEMP,buf,2); if (ret<0) { dev_err(di->dev, "error reading temperature\n"); return ret; } temp = get_unaligned_le16(buf); //temp = 5 * temp / 2; temp = temp - 2731; //K DBG("Enter:%s %d--temp = %d\n",__FUNCTION__,__LINE__,temp); return temp; }
static void bq27410_battery_work(struct work_struct *work) { struct bq27410_device_info *di = container_of(work, struct bq27410_device_info, work.work); int ret = 0; u8 buf[2]; int battflags = 0; static int flag = 0; if(flag == 0){ #if 0 mutex_lock(&g_bq27410_mutex); bq27410_write_batt_insert(g_client); mutex_unlock(&g_bq27410_mutex); virtual_battery_enable = 0; flag = 1; #else mutex_lock(&g_bq27410_mutex); ret = bq27410_read(g_client,BQ27410_REG_FLAGS, buf, 2); if (ret < 0) { DBG("error reading flags\n"); return ret; } mutex_unlock(&g_bq27410_mutex); battflags = get_unaligned_le16(buf); if ((battflags & BQ27410_FLAG_BAT_DET)){ printk("27410: %s, battflags = 0x%x, set virtual_battery_enable = 0\n",__FUNCTION__,battflags); flag = 1; virtual_battery_enable = 0; } #endif } bq27410_battery_update_status(di); /* reschedule for the next time */ schedule_delayed_work(&di->work, di->interval); }
static int bq27410_update_fw(struct file *filp, char *buff, unsigned long len, void *data) { struct file * file_data = NULL; mm_segment_t old_fs; int file_len; char *data_buf = NULL; int ret; char buf[128]; char *pch = NULL; s32 base = 16; int count = 0; int wCount = 0; int xCount = 0; int cCount = 0; #define isspace(c) ((c) == ' ') file_len = update_get_flen(buff); ///Open update file. file_data = update_file_open(buff, &old_fs); if(file_data == NULL) { return -1; } data_buf = kzalloc(file_len, GFP_KERNEL); if(file_len > 0) { ret = file_data->f_op->read(file_data, data_buf, file_len, &file_data->f_pos); //printk("get file strlen(data_buf): %d.\n", strlen(data_buf)); pch = data_buf; while(count < file_len) { if(*pch == 'W') { wCount = 0; pch +=2; count +=2; while(*pch != 'W' && *pch != 'X' && *pch != 'C') { while(isspace(*pch)) { pch++; count ++; } ret = simple_strtoul(pch, NULL, base); buf[wCount] = ret; wCount++; //printk("%x ", ret); pch +=2; count +=2; while((*pch == '\r') || (*pch == '\n')) { pch +=2; count +=2; } } //printk("\n"); //printk("\n W count = %d , wCount=%d\n", count, wCount); ret = bq27410_write(g_client,buf[1],&buf[2],wCount - 2); //printk("W ret =%d ", ret); } else if(*pch == 'X') { xCount = 0; pch +=2; count +=2; while(*pch != 'W' && *pch != 'X' && *pch != 'C') { while(isspace(*pch)) { pch++; count ++; } ret = simple_strtoul(pch, NULL, 10); //printk("%d ", ret); xCount++; if(ret < 0x10) { pch +=1; count +=1; } else if(ret < 0x100){ pch +=2; count +=2; } else if(ret < 0x1000){ pch +=3; count +=3; } else if(ret < 0x10000){ pch +=4; count +=4; } while((*pch == '\r') || (*pch == '\n')) { pch +=2; count +=2; } if(ret == 4000) { count = file_len; break; } } //printk("\n X count = %d, xCount=%d \n", count, xCount); //printk("\n"); msleep(ret); } else if(*pch == 'C') { cCount = 0; pch +=2; count +=2; while(*pch != 'W' && *pch != 'X' && *pch != 'C') { while(isspace(*pch)) { pch++; count ++; } ret = simple_strtoul(pch, NULL, base); buf[cCount] = ret; //printk("%x ", ret); cCount++; pch +=2; count +=2; while((*pch == '\r') || (*pch == '\n')) { pch +=2; count +=2; break; } } //printk("\n"); //printk("\n C count = %d , cCount=%d\n", count, cCount); ret = bq27410_read(g_client,buf[1],&buf[2],cCount - 2); //printk("C ret =%d ", ret); } } } if(data_buf){ kfree(data_buf); data_buf = NULL; } ///Close file update_file_close(file_data, old_fs); return 0; }
static int bq27410_battery_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bq27410_device_info *di; int retval = 0; u8 buf[2]; struct bq27410_platform_data *pdata; DBG("********** bq27410_battery_probe************** \n"); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){ client->addr = 0x0B; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){ return -ENODEV; } else{ g_bq27410_mode = BQ27410_ROM_MODE; } } else{ g_bq27410_mode = BQ27410_NORMAL_MODE; } printk("+ g_bq27410_mode=%d \n", g_bq27410_mode); pdata = client->dev.platform_data; g_pdata = pdata; g_client = client; di = kzalloc(sizeof(*di), GFP_KERNEL); if (!di) { dev_err(&client->dev, "failed to allocate device info data\n"); retval = -ENOMEM; goto batt_failed_2; } mutex_init(&g_bq27410_mutex); i2c_set_clientdata(client, di); di->dev = &client->dev; di->bat.name = "battery"; di->client = client; /* 4 seconds between monotor runs interval */ di->interval = msecs_to_jiffies(4 * 1000); di->bat_num = pdata->bat_num; di->dc_check_pin = pdata->dc_check_pin; di->bat_check_pin = pdata->bat_check_pin; di->wake_irq = pdata->low_power_pin; if (pdata->io_init) pdata->io_init(); bq27410_di = di; retval = bq27410_read(client,0x00,buf,2); if(retval < 0){ printk("failed to find bq27410\n"); goto batt_failed_2; }else{ rk30_bat_unregister(); bq27410_init = 1; } //command batt insert. bq27410_write_batt_insert(client); if(g_bq27410_mode == BQ27410_NORMAL_MODE){ if(!bq27410_read_control_status(client)) { virtual_battery_enable = 1; bq27410_powersupply_init(di); retval = power_supply_register(&client->dev, &di->bat); INIT_DELAYED_WORK(&di->update_work, bq27410_battery_update_work); schedule_delayed_work(&di->update_work, msecs_to_jiffies(15 * 1000)); } else{ printk("NOT need bq27410_update_firmware \n"); bq27410_powersupply_init(di); retval = power_supply_register(&client->dev, &di->bat); if (retval) { dev_err(&client->dev, "failed to register battery\n"); goto batt_failed_4; } INIT_DELAYED_WORK(&di->work, bq27410_battery_work); schedule_delayed_work(&di->work, di->interval); dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION); } } else { INIT_DELAYED_WORK(&di->update_work, bq27410_battery_update_work); schedule_delayed_work(&di->update_work, msecs_to_jiffies(15 * 1000)); } printk("- g_bq27410_mode=%d \n", g_bq27410_mode); //M-MT:setup discharge/charge current threshold. //bq27410_write_current_threshold(client); // battery low irq if(pdata->low_power_pin != INVALID_GPIO) { di->wake_irq = gpio_to_irq(pdata->low_power_pin); retval = request_irq(di->wake_irq, bq27410_bat_wakeup, IRQF_TRIGGER_FALLING, "bq27410_battery", di); if (retval) { printk("failed to request low_power_pin irq\n"); goto err_batirq_failed; } INIT_DELAYED_WORK(&di->wakeup_work, bq27410_battery_wake_work); enable_irq_wake(di->wake_irq); } bq27410_proc_entry = create_proc_entry("bq27410-update", 0666, NULL); if(bq27410_proc_entry == NULL) { printk("Malata bq27410 Couldn't create proc entry!\n"); return -ENOMEM; } else { printk("Malata bq27410 Create proc entry success!\n"); bq27410_proc_entry->write_proc = bq27410_update_write; bq27410_proc_entry->read_proc = bq27410_update_read; } battery_capacity_check(di); return 0; batt_failed_4: kfree(di); batt_failed_2: err_batirq_failed: gpio_free(pdata->bat_check_pin); return retval; }