static int sec_fg_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct sec_fuelgauge_info *fuelgauge = container_of(psy, struct sec_fuelgauge_info, psy_fg); switch (psp) { case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_AVG: case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_AVG: case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TEMP_AMBIENT: if (!sec_hal_fg_get_property(fuelgauge->client, psp, val)) return -EINVAL; if (psp == POWER_SUPPLY_PROP_CAPACITY) { if (fuelgauge->pdata->capacity_calculation_type & SEC_FUELGAUGE_CAPACITY_TYPE_SCALE) sec_fg_get_scaled_capacity(fuelgauge, val); /* capacity should be between 0% and 100% * (0.1% degree) */ if (val->intval > 1000) val->intval = 1000; if (val->intval < 0) val->intval = 0; /* get only integer part */ val->intval /= 10; /* (Only for atomic capacity) * In initial time, capacity_old is 0. * and in resume from sleep, * capacity_old is too different from actual soc. * should update capacity_old * by val->intval in booting or resume. */ if (fuelgauge->initial_update_of_soc) { /* updated old capacity */ fuelgauge->capacity_old = val->intval; fuelgauge->initial_update_of_soc = false; break; } if (fuelgauge->pdata->capacity_calculation_type & SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC) sec_fg_get_atomic_capacity(fuelgauge, val); } break; default: return -EINVAL; } return 0; }
static int sec_fg_calculate_dynamic_scale( struct sec_fuelgauge_info *fuelgauge) { union power_supply_propval raw_soc_val; raw_soc_val.intval = SEC_FUELGAUGE_CAPACITY_TYPE_RAW; if (!sec_hal_fg_get_property(fuelgauge->client, POWER_SUPPLY_PROP_CAPACITY, &raw_soc_val)) return -EINVAL; raw_soc_val.intval /= 10; if (raw_soc_val.intval < fuelgauge->pdata->capacity_max - fuelgauge->pdata->capacity_max_margin) { fuelgauge->capacity_max = fuelgauge->pdata->capacity_max - fuelgauge->pdata->capacity_max_margin; #if defined(CONFIG_SEC_DEBUG_FUELGAUGE_LOG) dev_dbg(&fuelgauge->client->dev, "%s: capacity_max (%d)", __func__, fuelgauge->capacity_max); #endif } else { fuelgauge->capacity_max = (raw_soc_val.intval > fuelgauge->pdata->capacity_max + fuelgauge->pdata->capacity_max_margin) ? (fuelgauge->pdata->capacity_max + fuelgauge->pdata->capacity_max_margin) : raw_soc_val.intval; #if defined(CONFIG_SEC_DEBUG_FUELGAUGE_LOG) dev_dbg(&fuelgauge->client->dev, "%s: raw soc (%d)", __func__, fuelgauge->capacity_max); #endif } fuelgauge->capacity_max = (fuelgauge->capacity_max * 99 / 100); #if defined(CONFIG_SEC_DEBUG_FUELGAUGE_LOG) dev_info(&fuelgauge->client->dev, "%s: %d is used for capacity_max\n", __func__, fuelgauge->capacity_max); #endif return fuelgauge->capacity_max; }
static int sec_fg_calculate_dynamic_scale( struct sec_fuelgauge_info *fuelgauge) { union power_supply_propval raw_soc_val; raw_soc_val.intval = SEC_FUELGAUGE_CAPACITY_TYPE_RAW; if (!sec_hal_fg_get_property(fuelgauge->client, POWER_SUPPLY_PROP_CAPACITY, &raw_soc_val)) return -EINVAL; raw_soc_val.intval /= 10; if (raw_soc_val.intval < fuelgauge->pdata->capacity_max - fuelgauge->pdata->capacity_max_margin) { fuelgauge->capacity_max = fuelgauge->pdata->capacity_max - fuelgauge->pdata->capacity_max_margin; dev_dbg(&fuelgauge->client->dev, "%s: capacity_max (%d)", __func__, fuelgauge->capacity_max); } else { fuelgauge->capacity_max = (raw_soc_val.intval > fuelgauge->pdata->capacity_max + fuelgauge->pdata->capacity_max_margin) ? (fuelgauge->pdata->capacity_max + fuelgauge->pdata->capacity_max_margin) : raw_soc_val.intval; dev_dbg(&fuelgauge->client->dev, "%s: raw soc (%d)", __func__, fuelgauge->capacity_max); } fuelgauge->capacity_max = (fuelgauge->capacity_max * 99 / 100); /* update capacity_old for sec_fg_get_atomic_capacity algorithm */ fuelgauge->capacity_old = 100; dev_info(&fuelgauge->client->dev, "%s: %d is used for capacity_max\n", __func__, fuelgauge->capacity_max); return fuelgauge->capacity_max; }
/* capacity is integer */ static void sec_fg_get_atomic_capacity( struct sec_fuelgauge_info *fuelgauge, union power_supply_propval *val) { #if 0 if (fuelgauge->capacity_old < val->intval) val->intval = fuelgauge->capacity_old + 1; else if (fuelgauge->capacity_old > val->intval) val->intval = fuelgauge->capacity_old - 1; #endif int value = 0; #if defined(CONFIG_SEC_DEBUG_FUELGAUGE_LOG) dev_info(&fuelgauge->client->dev, "%s: old : %d, current : %d\n", __func__, fuelgauge->capacity_old, val->intval); #endif if(fuelgauge->capacity_old > val->intval) { value = (int)fuelgauge->capacity_old - val->intval; } else { value = val->intval - (int)fuelgauge->capacity_old; } if(value >= 10) { /* is in the quickstart? */ if(!fuelgauge->is_reset) { dev_err(&fuelgauge->client->dev, "%s: SOC error panic\n", __func__); if (!sec_hal_fg_get_property(fuelgauge->client, POWER_SUPPLY_PROP_MANUFACTURER, val)) return; } else fuelgauge->is_reset = false; } /* updated old capacity */ fuelgauge->capacity_old = val->intval; }
static int __devinit sec_fuelgauge_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct sec_fuelgauge_info *fuelgauge; int ret = 0; bool fuelalert_init_ret = false; union power_supply_propval raw_soc_val; dev_dbg(&client->dev, "%s: SEC Fuelgauge Driver Loading\n", __func__); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) return -EIO; fuelgauge = kzalloc(sizeof(*fuelgauge), GFP_KERNEL); if (!fuelgauge) return -ENOMEM; mutex_init(&fuelgauge->fg_lock); fuelgauge->client = client; fuelgauge->pdata = client->dev.platform_data; i2c_set_clientdata(client, fuelgauge); fuelgauge->psy_fg.name = "sec-fuelgauge"; fuelgauge->psy_fg.type = POWER_SUPPLY_TYPE_UNKNOWN; fuelgauge->psy_fg.get_property = sec_fg_get_property; fuelgauge->psy_fg.set_property = sec_fg_set_property; fuelgauge->psy_fg.properties = sec_fuelgauge_props; fuelgauge->psy_fg.num_properties = ARRAY_SIZE(sec_fuelgauge_props); fuelgauge->capacity_max = fuelgauge->pdata->capacity_max; raw_soc_val.intval = SEC_FUELGAUGE_CAPACITY_TYPE_RAW; sec_hal_fg_get_property(fuelgauge->client, POWER_SUPPLY_PROP_CAPACITY, &raw_soc_val); raw_soc_val.intval /= 10; if(raw_soc_val.intval > fuelgauge->pdata->capacity_max) sec_fg_calculate_dynamic_scale(fuelgauge); if (!fuelgauge->pdata->fg_gpio_init()) { dev_err(&client->dev, "%s: Failed to Initialize GPIO\n", __func__); goto err_free; } if (!sec_hal_fg_init(fuelgauge->client)) { dev_err(&client->dev, "%s: Failed to Initialize Fuelgauge\n", __func__); goto err_free; } ret = power_supply_register(&client->dev, &fuelgauge->psy_fg); if (ret) { dev_err(&client->dev, "%s: Failed to Register psy_fg\n", __func__); goto err_free; } if (fuelgauge->pdata->fg_irq) { INIT_DELAYED_WORK_DEFERRABLE( &fuelgauge->isr_work, sec_fg_isr_work); ret = request_threaded_irq(fuelgauge->pdata->fg_irq, NULL, sec_fg_irq_thread, fuelgauge->pdata->fg_irq_attr, "fuelgauge-irq", fuelgauge); if (ret) { dev_err(&client->dev, "%s: Failed to Reqeust IRQ\n", __func__); goto err_supply_unreg; } ret = enable_irq_wake(fuelgauge->pdata->fg_irq); if (ret < 0) dev_err(&client->dev, "%s: Failed to Enable Wakeup Source(%d)\n", __func__, ret); } fuelgauge->is_fuel_alerted = false; if (fuelgauge->pdata->fuel_alert_soc >= 0) { fuelalert_init_ret = sec_hal_fg_fuelalert_init(fuelgauge->client, fuelgauge->pdata->fuel_alert_soc); if (fuelalert_init_ret) wake_lock_init(&fuelgauge->fuel_alert_wake_lock, WAKE_LOCK_SUSPEND, "fuel_alerted"); else { dev_err(&client->dev, "%s: Failed to Initialize Fuel-alert\n", __func__); goto err_irq; } } fuelgauge->initial_update_of_soc = true; ret = sec_fg_create_attrs(fuelgauge->psy_fg.dev); if (ret) { dev_err(&client->dev, "%s : Failed to create_attrs\n", __func__); goto err_irq; } dev_dbg(&client->dev, "%s: SEC Fuelgauge Driver Loaded\n", __func__); return 0; err_irq: if (fuelgauge->pdata->fg_irq) free_irq(fuelgauge->pdata->fg_irq, fuelgauge); if (fuelalert_init_ret) wake_lock_destroy(&fuelgauge->fuel_alert_wake_lock); err_supply_unreg: power_supply_unregister(&fuelgauge->psy_fg); err_free: mutex_destroy(&fuelgauge->fg_lock); kfree(fuelgauge); return ret; }
static int sec_fg_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct sec_fuelgauge_info *fuelgauge = container_of(psy, struct sec_fuelgauge_info, psy_fg); int soc_type = val->intval; switch (psp) { case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_AVG: case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_AVG: case POWER_SUPPLY_PROP_ENERGY_NOW: case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TEMP_AMBIENT: if (!sec_hal_fg_get_property(fuelgauge->client, psp, val)) return -EINVAL; if (psp == POWER_SUPPLY_PROP_CAPACITY) { if (soc_type == SEC_FUELGAUGE_CAPACITY_TYPE_RAW) break; if (fuelgauge->pdata->capacity_calculation_type & (SEC_FUELGAUGE_CAPACITY_TYPE_SCALE | SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE)) sec_fg_get_scaled_capacity(fuelgauge, val); /* capacity should be between 0% and 100% * (0.1% degree) */ if (val->intval > 1000) val->intval = 1000; if (val->intval < 0) val->intval = 0; /* get only integer part */ val->intval /= 10; /* check whether doing the wake_unlock */ if ((val->intval > fuelgauge->pdata->fuel_alert_soc) && fuelgauge->is_fuel_alerted) { wake_unlock(&fuelgauge->fuel_alert_wake_lock); sec_hal_fg_fuelalert_init(fuelgauge->client, fuelgauge->pdata->fuel_alert_soc); } /* (Only for atomic capacity) * In initial time, capacity_old is 0. * and in resume from sleep, * capacity_old is too different from actual soc. * should update capacity_old * by val->intval in booting or resume. */ if (fuelgauge->initial_update_of_soc) { /* updated old capacity */ fuelgauge->capacity_old = val->intval; fuelgauge->initial_update_of_soc = false; break; } if (fuelgauge->pdata->capacity_calculation_type & (SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC | SEC_FUELGAUGE_CAPACITY_TYPE_SKIP_ABNORMAL)) sec_fg_get_atomic_capacity(fuelgauge, val); } break; case POWER_SUPPLY_PROP_STATUS: case POWER_SUPPLY_PROP_CHARGE_FULL: break; default: return -EINVAL; } return 0; }
static int sec_fuelgauge_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct sec_fuelgauge_info *fuelgauge; sec_battery_platform_data_t *pdata = NULL; struct battery_data_t *battery_data = NULL; int ret = 0; union power_supply_propval raw_soc_val; dev_info(&client->dev, "%s: SEC Fuelgauge Driver Loading\n", __func__); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) return -EIO; fuelgauge = kzalloc(sizeof(*fuelgauge), GFP_KERNEL); if (!fuelgauge) return -ENOMEM; mutex_init(&fuelgauge->fg_lock); fuelgauge->client = client; if (client->dev.of_node) { int error; pdata = devm_kzalloc(&client->dev, sizeof(sec_battery_platform_data_t), GFP_KERNEL); if (!pdata) { dev_err(&client->dev, "Failed to allocate memory\n"); ret = -ENOMEM; goto err_free; } fuelgauge->pdata = pdata; error = fuelgauge_parse_dt(&client->dev, fuelgauge); if (error) { dev_err(&client->dev, "%s: Failed to get fuel_int\n", __func__); } } else { dev_err(&client->dev, "%s: Failed to get of_node\n", __func__); fuelgauge->pdata = client->dev.platform_data; } i2c_set_clientdata(client, fuelgauge); if (fuelgauge->pdata->fg_gpio_init != NULL) { if (!fuelgauge->pdata->fg_gpio_init()) { dev_err(&client->dev, "%s: Failed to Initialize GPIO\n", __func__); goto err_devm_free; } } if (!sec_hal_fg_init(fuelgauge->client)) { dev_err(&client->dev, "%s: Failed to Initialize Fuelgauge\n", __func__); goto err_devm_free; } fuelgauge->psy_fg.name = "sec-fuelgauge"; fuelgauge->psy_fg.type = POWER_SUPPLY_TYPE_UNKNOWN; fuelgauge->psy_fg.get_property = sec_fg_get_property; fuelgauge->psy_fg.set_property = sec_fg_set_property; fuelgauge->psy_fg.properties = sec_fuelgauge_props; fuelgauge->psy_fg.num_properties = ARRAY_SIZE(sec_fuelgauge_props); fuelgauge->capacity_max = fuelgauge->pdata->capacity_max; raw_soc_val.intval = SEC_FUELGAUGE_CAPACITY_TYPE_RAW; sec_hal_fg_get_property(fuelgauge->client, POWER_SUPPLY_PROP_CAPACITY, &raw_soc_val); raw_soc_val.intval /= 10; if(raw_soc_val.intval > fuelgauge->pdata->capacity_max) sec_fg_calculate_dynamic_scale(fuelgauge); ret = power_supply_register(&client->dev, &fuelgauge->psy_fg); if (ret) { dev_err(&client->dev, "%s: Failed to Register psy_fg\n", __func__); goto err_devm_free; } fuelgauge->is_fuel_alerted = false; if (fuelgauge->pdata->fuel_alert_soc >= 0) { if (sec_hal_fg_fuelalert_init(fuelgauge->client, fuelgauge->pdata->fuel_alert_soc)) wake_lock_init(&fuelgauge->fuel_alert_wake_lock, WAKE_LOCK_SUSPEND, "fuel_alerted"); else { dev_err(&client->dev, "%s: Failed to Initialize Fuel-alert\n", __func__); goto err_supply_unreg; } } if (fuelgauge->pdata->fg_irq > 0) { INIT_DELAYED_WORK( &fuelgauge->isr_work, sec_fg_isr_work); fuelgauge->fg_irq = gpio_to_irq(fuelgauge->pdata->fg_irq); dev_info(&client->dev, "%s: fg_irq = %d\n", __func__, fuelgauge->fg_irq); if (fuelgauge->fg_irq > 0) { ret = request_threaded_irq(fuelgauge->fg_irq, NULL, sec_fg_irq_thread, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "fuelgauge-irq", fuelgauge); if (ret) { dev_err(&client->dev, "%s: Failed to Reqeust IRQ\n", __func__); goto err_supply_unreg; } ret = enable_irq_wake(fuelgauge->fg_irq); if (ret < 0) dev_err(&client->dev, "%s: Failed to Enable Wakeup Source(%d)\n", __func__, ret); } else { dev_err(&client->dev, "%s: Failed gpio_to_irq(%d)\n", __func__, fuelgauge->fg_irq); goto err_supply_unreg; } } fuelgauge->initial_update_of_soc = true; ret = sec_fg_create_attrs(fuelgauge->psy_fg.dev); if (ret) { dev_err(&client->dev, "%s : Failed to create_attrs\n", __func__); goto err_irq; } dev_info(&client->dev, "%s: SEC Fuelgauge Driver Loaded\n", __func__); return 0; err_irq: if (fuelgauge->fg_irq > 0) free_irq(fuelgauge->fg_irq, fuelgauge); wake_lock_destroy(&fuelgauge->fuel_alert_wake_lock); err_supply_unreg: power_supply_unregister(&fuelgauge->psy_fg); err_devm_free: if(pdata) devm_kfree(&client->dev, pdata); if(battery_data) devm_kfree(&client->dev, battery_data); err_free: mutex_destroy(&fuelgauge->fg_lock); kfree(fuelgauge); dev_info(&client->dev, "%s: Fuel gauge probe failed\n", __func__); return ret; }
static int sec_fuelgauge_probe(struct platform_device *pdev) { struct sec_fuelgauge_info *fuelgauge; #ifndef CONFIG_FUELGAUGE_SPRD2713 sec_fuelgauge_dev_t *mfd_dev = dev_get_drvdata(pdev->dev.parent); sec_fuelgauge_pdata_t *pdata = dev_get_platdata(mfd_dev->dev); #else sec_battery_platform_data_t *pdata = dev_get_platdata(&pdev->dev); #endif int ret = 0; bool fuelalert_init_ret = false; union power_supply_propval raw_soc_val; dev_info(&pdev->dev, "%s: SEC Fuelgauge Driver Loading\n", __func__); fuelgauge = kzalloc(sizeof(*fuelgauge), GFP_KERNEL); if (!fuelgauge) return -ENOMEM; platform_set_drvdata(pdev, fuelgauge); #ifndef CONFIG_FUELGAUGE_SPRD2713 fuelgauge->client = mfd_dev->i2c; fuelgauge->pdata = pdata->fuelgauge_data; #else fuelgauge->pdata = pdata; #endif mutex_init(&fuelgauge->fg_lock); fuelgauge->psy_fg.name = "sec-fuelgauge"; fuelgauge->psy_fg.type = POWER_SUPPLY_TYPE_UNKNOWN; fuelgauge->psy_fg.get_property = sec_fg_get_property; fuelgauge->psy_fg.set_property = sec_fg_set_property; fuelgauge->psy_fg.properties = sec_fuelgauge_props; fuelgauge->psy_fg.num_properties = ARRAY_SIZE(sec_fuelgauge_props); fuelgauge->capacity_max = fuelgauge->pdata->capacity_max; raw_soc_val.intval = SEC_FUELGAUGE_CAPACITY_TYPE_RAW; sec_hal_fg_get_property(fuelgauge, POWER_SUPPLY_PROP_CAPACITY, &raw_soc_val); raw_soc_val.intval /= 10; if(raw_soc_val.intval > fuelgauge->pdata->capacity_max) sec_fg_calculate_dynamic_scale(fuelgauge); if (!fuelgauge->pdata->fg_gpio_init()) { dev_err(&pdev->dev, "%s: Failed to Initialize GPIO\n", __func__); goto err_free; } if (!sec_hal_fg_init(fuelgauge)) { dev_err(&pdev->dev, "%s: Failed to Initialize Fuelgauge\n", __func__); goto err_free; } ret = power_supply_register(&pdev->dev, &fuelgauge->psy_fg); if (ret) { dev_err(&pdev->dev, "%s: Failed to Register psy_fg\n", __func__); goto err_free; } fuelgauge->is_fuel_alerted = false; if (fuelgauge->pdata->fuel_alert_soc >= 0) { fuelalert_init_ret = sec_hal_fg_fuelalert_init(fuelgauge, fuelgauge->pdata->fuel_alert_soc); if (fuelalert_init_ret) wake_lock_init(&fuelgauge->fuel_alert_wake_lock, WAKE_LOCK_SUSPEND, "fuel_alerted"); else { dev_err(&pdev->dev, "%s: Failed to Initialize Fuel-alert\n", __func__); goto err_irq; } } #ifndef CONFIG_FUELGAUGE_SPRD2713 if (fuelgauge->pdata->fg_irq) { INIT_DELAYED_WORK_DEFERRABLE( &fuelgauge->isr_work, sec_fg_isr_work); fuelgauge->fg_irq = gpio_to_irq(fuelgauge->pdata->fg_irq); ret = request_threaded_irq(fuelgauge->fg_irq, NULL, sec_fg_irq_thread, fuelgauge->pdata->fg_irq_attr, "fuelgauge-irq", fuelgauge); if (ret) { dev_err(&pdev->dev, "%s: Failed to Reqeust IRQ\n", __func__); goto err_supply_unreg; } ret = enable_irq_wake(fuelgauge->pdata->fg_irq); if (ret < 0) dev_err(&pdev->dev, "%s: Failed to Enable Wakeup Source(%d)\n", __func__, ret); } #endif fuelgauge->initial_update_of_soc = true; ret = sec_fg_create_attrs(fuelgauge->psy_fg.dev); if (ret) { dev_err(&pdev->dev, "%s : Failed to create_attrs\n", __func__); goto err_irq; } dev_info(&pdev->dev, "%s: SEC Fuelgauge Driver Loaded\n", __func__); return 0; err_irq: if (fuelgauge->pdata->fg_irq) free_irq(fuelgauge->pdata->fg_irq, fuelgauge); if (fuelalert_init_ret) wake_lock_destroy(&fuelgauge->fuel_alert_wake_lock); err_supply_unreg: power_supply_unregister(&fuelgauge->psy_fg); err_free: mutex_destroy(&fuelgauge->fg_lock); kfree(fuelgauge); return ret; }