Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752103Ab0LURnw (ORCPT ); Tue, 21 Dec 2010 12:43:52 -0500 Received: from mail-qw0-f46.google.com ([209.85.216.46]:40564 "EHLO mail-qw0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751490Ab0LURnu (ORCPT ); Tue, 21 Dec 2010 12:43:50 -0500 DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:date:message-id:subject:from:to:cc:content-type; b=ZJKHW8IS2JlnATeZTDmc1vXXYw2kQZTrqBTRAez9bS26HkihCuZoOI+gqV286aXfTX RqhlwZini/Ecgf5mUefocM1mjd7iKjDOwduYmiZB1bGjwhIDzTi/gW0zTptLGObfQQvV x2oMCjsqERYjb58y719l38h5Jl6ZfGFH+iBHA= MIME-Version: 1.0 Date: Tue, 21 Dec 2010 18:43:49 +0100 Message-ID: Subject: [PATCHv3 4/11] BATTERY: Battery module of DA9052 device driver From: dd diasemi To: astarikovskiy@suse.de Cc: linux-kernel@vger.kernel.org Content-Type: text/plain; charset=ISO-8859-1 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 31009 Lines: 1067 Battery module for DA9052 PMIC device from Dialog Semiconductor. Changes made since last submission: . added chip specific parameters to priv data. . removed the DA9052 specific error codes. . replaced kernel thread with workqueue. Linux Kernel Version: 2.6.34 Signed-off-by: D. Chen diff -Naur A-source/drivers/power/da9052-battery.c B-source/drivers/power/da9052-battery.c --- A-source/drivers/power/da9052-battery.c 1970-01-01 05:00:00.000000000 +0500 +++ B-source/drivers/power/da9052-battery.c 2010-12-08 15:22:43.000000000 +0500 @@ -0,0 +1,778 @@ +/* + * da9052-battery.c -- Batttery Driver for Dialog DA9052 + * + * Copyright(c) 2009 Dialog Semiconductor Ltd. + + * Author: Dialog Semiconductor Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DA9052_BAT_DEVICE_NAME "da9052-bat" + +static struct da9052_bat_hysteresis bat_hysteresis; +static struct da9052_bat_event_registration event_status; +static u16 array_hys_batvoltage[2]; +static u16 bat_volt_arr[3]; +static u8 hys_flag = FALSE; + +static int da9052_read(struct da9052 *da9052, u8 reg_address, u8 *reg_data) +{ + struct da9052_ssc_msg msg; + int ret; + + msg.addr = reg_address; + msg.data = 0; + + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto ssc_comm_err; + da9052_unlock(da9052); + + *reg_data = msg.data; + return 0; +ssc_comm_err: + da9052_unlock(da9052); + return ret; +} + +static s32 da9052_adc_read_ich(struct da9052 *da9052, u16 *data) +{ + struct da9052_ssc_msg msg; + da9052_lock(da9052); + /* Read charging conversion register */ + msg.addr = DA9052_ICHGAV_REG; + msg.data = 0; + if (da9052->read(da9052, &msg)) { + da9052_unlock(da9052); + return -EIO; + } + da9052_unlock(da9052); + + *data = (u16)msg.data; + return 0; +} + +static s32 da9052_adc_read_tbat(struct da9052 *da9052, u16 *data) +{ + s32 ret; + u8 reg_data; + + ret = da9052_read(da9052, DA9052_TBATRES_REG, ®_data); + if (ret) + return ret; + *data = (u16)reg_data; + + return 0; +} + +s32 da9052_adc_read_vbat(struct da9052 *da9052, u16 *data) +{ + s32 ret; + + ret = da9052_manual_read(da9052, DA9052_ADC_VBAT); + if (ret == -EIO) { + *data = 0; + return ret; + } else { + *data = ret; + return 0; + } + return 0; +} + +static u16 filter_sample(u16 *buffer) +{ + u8 count; + u16 tempvalue = 0; + u16 ret; + + if (buffer == NULL) + return -EINVAL; + + for (count = 0; count < FILTER_SIZE; count++) + tempvalue = tempvalue + *(buffer + count); + + ret = tempvalue/FILTER_SIZE; + return ret; +} + +static s32 da9052_bat_get_battery_temperature(struct da9052_charger_device + *chg_device, u16 *buffer) +{ + + u8 count; + u16 filterqueue[FILTER_SIZE]; + + /* Measure the battery temperature using ADC function. + Number of read equal to average filter size*/ + for (count = 0; count < FILTER_SIZE; count++) + if (da9052_adc_read_tbat(chg_device->da9052, + &filterqueue[count])) + return -EIO; + + /* Apply Average filter */ + filterqueue[0] = filter_sample(filterqueue); + + chg_device->bat_temp = filterqueue[0]; + *buffer = chg_device->bat_temp; + return 0; +} + +static s32 da9052_bat_get_chg_current(struct da9052_charger_device + *chg_device, u16 *buffer) +{ + if (chg_device->status == POWER_SUPPLY_STATUS_DISCHARGING) + return -EIO; + + /* Measure the Charger current using ADC function */ + if (da9052_adc_read_ich(chg_device->da9052, buffer)) + return -EIO; + + /* Convert the raw value in terms of mA */ + chg_device->chg_current = ichg_reg_to_mA(*buffer); + *buffer = chg_device->chg_current; + + return 0; +} + + +s32 da9052_bat_get_battery_voltage(struct da9052_charger_device *chg_device, + u16 *buffer) +{ + u8 count; + u16 filterqueue[FILTER_SIZE]; + + /* Measure the battery voltage using ADC function. + Number of read equal to average filter size*/ + for (count = 0; count < FILTER_SIZE; count++) + if (da9052_adc_read_vbat(chg_device->da9052, + &filterqueue[count])) + return -EIO; + + /* Apply average filter */ + filterqueue[0] = filter_sample(filterqueue); + + /* Convert battery voltage raw value in terms of mV */ + chg_device->bat_voltage = volt_reg_to_mV(filterqueue[0]); + *buffer = chg_device->bat_voltage; + return 0; +} + +static void da9052_bat_status_update(struct da9052_charger_device + *chg_device) +{ + struct da9052_ssc_msg msg; + u16 current_value = 0; + u16 buffer = 0; + u8 regvalue = 0; + u8 old_status = chg_device->status; + + /* Read Status A register */ + msg.addr = DA9052_STATUSA_REG; + msg.data = 0; + da9052_lock(chg_device->da9052); + + if (chg_device->da9052->read(chg_device->da9052, &msg)) { + da9052_unlock(chg_device->da9052); + return; + } + regvalue = msg.data; + + /* Read Status B register */ + msg.addr = DA9052_STATUSB_REG; + msg.data = 0; + if (chg_device->da9052->read(chg_device->da9052, &msg)) { + da9052_unlock(chg_device->da9052); + return; + } + da9052_unlock(chg_device->da9052); + + /* If DCINDET and DCINSEL are set then connected charger is + WALL Charger unit */ + if ((regvalue & DA9052_STATUSA_DCINSEL) + && (regvalue & DA9052_STATUSA_DCINDET)) + chg_device->charger_type = DA9052_WALL_CHARGER; + /* If VBUS_DET and VBUSEL are set then connected charger is + USB Type */ + else if ((regvalue & DA9052_STATUSA_VBUSSEL) + && (regvalue & DA9052_STATUSA_VBUSDET)) { + if (regvalue & DA9052_STATUSA_VDATDET) + chg_device->charger_type = DA9052_USB_CHARGER; + else + /* Else it has to be USB Host charger */ + chg_device->charger_type = DA9052_USB_HUB; + } else { + chg_device->charger_type = DA9052_NOCHARGER; + /* Eqv to DISCHARGING_WITHOUT_CHARGER state */ + chg_device->status = POWER_SUPPLY_STATUS_DISCHARGING; + } + if (chg_device->charger_type != DA9052_NOCHARGER) { + /* if Charging end flag is set and Charging current is greater + than charging end limit then battery is charging */ + if ((msg.data & DA9052_STATUSB_CHGEND) != 0) { + if (da9052_bat_get_chg_current(chg_device, + ¤t_value)) + return; + if (current_value >= chg_device->chg_end_current) + chg_device->status = + POWER_SUPPLY_STATUS_CHARGING; + else + /* Eqv to DISCHARGING_WITH_CHARGER state*/ + chg_device->status = + POWER_SUPPLY_STATUS_NOT_CHARGING; + } + /* if Charging end flag is cleared then battery is charging */ + else + chg_device->status = POWER_SUPPLY_STATUS_CHARGING; + + if (POWER_SUPPLY_STATUS_CHARGING == chg_device->status) { + if (msg.data != DA9052_STATUSB_CHGPRE) { + /* + Measure battery voltage. if battery voltage + is greater than (VCHG_BAT - VCHG_DROP) then + battery is in the termintation mode. + */ + if (da9052_bat_get_battery_voltage(chg_device, + &buffer)) + return ; + if (buffer > (chg_device->bat_target_voltage - + chg_device->charger_voltage_drop) && + (chg_device->cal_capacity >= 99)) + chg_device->status = + POWER_SUPPLY_STATUS_FULL; + } + } + } + + if (chg_device->illegal) + chg_device->health = POWER_SUPPLY_HEALTH_UNKNOWN; + else if (chg_device->cal_capacity < chg_device->bat_capacity_limit_low) + chg_device->health = POWER_SUPPLY_HEALTH_DEAD; + else + chg_device->health = POWER_SUPPLY_HEALTH_GOOD; + + if (chg_device->status != old_status) + power_supply_changed(&chg_device->psy); + return; +} + +static s32 da9052_bat_suspend_charging(struct da9052_charger_device *chg_device) +{ + struct da9052_ssc_msg msg; + + if ((chg_device->status == POWER_SUPPLY_STATUS_DISCHARGING) || + (chg_device->status == POWER_SUPPLY_STATUS_NOT_CHARGING)) + return 0; + + msg.addr = DA9052_INPUTCONT_REG; + msg.data = 0; + da9052_lock(chg_device->da9052); + /* Read Input condition register */ + if (chg_device->da9052->read(chg_device->da9052, &msg)) { + da9052_unlock(chg_device->da9052); + return -EIO; + } + + /* set both Wall charger and USB charger suspend bit */ + msg.data = set_bits(msg.data, DA9052_INPUTCONT_DCINSUSP); + msg.data = set_bits(msg.data, DA9052_INPUTCONT_VBUSSUSP); + + /* Write to Input control register */ + if (chg_device->da9052->write(chg_device->da9052, &msg)) { + da9052_unlock(chg_device->da9052); + return -EIO; + } + da9052_unlock(chg_device->da9052); + + return 0; +} + +u32 interpolated(u32 vbat_lower, u32 vbat_upper, u32 level_lower, + u32 level_upper, u32 bat_voltage) +{ + s32 temp; + /*apply formula y= yk + (x - xk) * (yk+1 -yk)/(xk+1 -xk) */ + temp = ((level_upper - level_lower) * 1000)/(vbat_upper - vbat_lower); + temp = level_lower + (((bat_voltage - vbat_lower) * temp)/1000); + + return temp; +} + +s32 capture_first_correct_vbat_sample(struct da9052_charger_device *chg_device, +u16 *battery_voltage) +{ + static u8 count; + s32 ret = 0; + u32 temp_data = 0; + + ret = da9052_bat_get_battery_voltage(chg_device, + &bat_volt_arr[count]); + if (ret) + return ret; + count++; + + if (count < chg_device->vbat_first_valid_detect_iteration) + return -1; + for (count = 0; count < + (chg_device->vbat_first_valid_detect_iteration - 1); + count++) { + temp_data = (bat_volt_arr[count] * + (chg_device->hysteresis_window_size))/100; + bat_hysteresis.upper_limit = bat_volt_arr[count] + temp_data; + bat_hysteresis.lower_limit = bat_volt_arr[count] - temp_data; + + if ((bat_volt_arr[count + 1] < bat_hysteresis.upper_limit) && + (bat_volt_arr[count + 1] > + bat_hysteresis.lower_limit)) { + *battery_voltage = (bat_volt_arr[count] + + bat_volt_arr[count+1]) / 2; + hys_flag = TRUE; + return 0; + } + } + + for (count = 0; count < + (chg_device->vbat_first_valid_detect_iteration - 1); + count++) + bat_volt_arr[count] = bat_volt_arr[count + 1]; + + return -1; +} + +s32 check_hystersis(struct da9052_charger_device *chg_device, u16 *bat_voltage) +{ + u8 ret = 0; + u32 offset = 0; + + /* Measure battery voltage using BAT internal function*/ + if (hys_flag == FALSE) { + ret = capture_first_correct_vbat_sample + (chg_device, &array_hys_batvoltage[0]); + if (ret) + return ret; + } + + ret = da9052_bat_get_battery_voltage + (chg_device, &array_hys_batvoltage[1]);linux-kernel@vger.kernel.org + if (ret) + return ret; + *bat_voltage = array_hys_batvoltage[1]; + + /* Check if measured battery voltage value is within the hysteresis + window limit using measured battey votlage value */ + if ((bat_hysteresis.upper_limit < *bat_voltage) || + (bat_hysteresis.lower_limit > *bat_voltage)) { + + bat_hysteresis.index++; + if (bat_hysteresis.index == + chg_device->hysteresis_no_of_reading) { + /* Hysteresis Window is set to +- of + HYSTERESIS_WINDOW_SIZE percentage of current VBAT */ + bat_hysteresis.index = 0; + offset = ((*bat_voltage) * + chg_device->hysteresis_window_size)/ + 100; + bat_hysteresis.upper_limit = (*bat_voltage) + offset; + bat_hysteresis.lower_limit = (*bat_voltage) - offset; + } else + return -EIO; + } else { + bat_hysteresis.index = 0; + offset = ((*bat_voltage) * + chg_device->hysteresis_window_size)/100; + bat_hysteresis.upper_limit = (*bat_voltage) + offset; + bat_hysteresis.lower_limit = (*bat_voltage) - offset; + } + + /* Digital C Filter, formula Yn = k Yn-1 + (1-k) Xn */ + *bat_voltage = ((chg_device->chg_hysteresis_const * + array_hys_batvoltage[0])/100) + + (((100 - chg_device->chg_hysteresis_const) * + array_hys_batvoltage[1])/100); +linux-kernel@vger.kernel.org + if ((chg_device->status == POWER_SUPPLY_STATUS_DISCHARGING) && + (*bat_voltage > array_hys_batvoltage[0])) { + *bat_voltage = array_hys_batvoltage[0]; + } + + array_hys_batvoltage[0] = *bat_voltage; + return 0; +} + +u8 select_temperature(u8 temp_index, u16 bat_temperature) +{ + u16 temp_temperature = 0; + temp_temperature = (temperature_lookup_ref[temp_index] + + temperature_lookup_ref[temp_index+1]) / 2; + + if (bat_temperature >= temp_temperature) { + temp_index += 1; + return temp_index; + } else + return temp_index; +} + +s32 da9052_bat_level_update(struct da9052_charger_device *chg_device) +{ + u16 bat_temperature; + u16 bat_voltage; + u32 vbat_lower, vbat_upper, level_upper, level_lower, level; + u8 access_index = 0; + u8 index = 0, ret; + u8 flag = FALSE;linux-kernel@vger.kernel.org + + ret = 0; + vbat_lower = 0; + vbat_upper = 0; + level_upper = 0; + level_lower = 0; + + ret = check_hystersis(chg_device, &bat_voltage); + if (ret) + return ret; + + ret = da9052_bat_get_battery_temperature(chg_device, + &bat_temperature); + if (ret) + return ret; + + for (index = 0; index < (DA9052_NO_OF_LOOKUP_TABLE-1); index++) { + if (bat_temperature <= temperature_lookup_ref[0]) { + access_index = 0; + break; + } else if (bat_temperature > + temperature_lookup_ref[DA9052_NO_OF_LOOKUP_TABLE]){ + access_index = DA9052_NO_OF_LOOKUP_TABLE - 1; + break; + } else if ((bat_temperature >= temperature_lookup_ref[index]) && + (bat_temperature >= temperature_lookup_ref[index+1])) { + access_index = select_temperature(index, + bat_temperature); + break; + } + } + if (bat_voltage >= vbat_vs_capacity_look_up[access_index][0][0]) { + chg_device->cal_capacity = 100; + return 0; + } + if (bat_voltage <= vbat_vs_capacity_look_up[access_index]linux-kernel@vger.kernel.org + [DA9052_LOOK_UP_TABLE_SIZE-1][0]){ + chg_device->cal_capacity = 0; + return 0; + } + flag = FALSE; + + for (index = 0; index < (DA9052_LOOK_UP_TABLE_SIZE-1); index++) { + if ((bat_voltage <= + vbat_vs_capacity_look_up[access_index][index][0]) && + (bat_voltage >= + vbat_vs_capacity_look_up[access_index][index+1][0])) { + vbat_upper = + vbat_vs_capacity_look_up[access_index][index][0]; + vbat_lower = + vbat_vs_capacity_look_up[access_index][index+1][0]; + level_upper = + vbat_vs_capacity_look_up[access_index][index][1]; + level_lower = + vbat_vs_capacity_look_up[access_index][index+1][1]; + flag = TRUE; + break; + } + } + if (!flag) + return -EIO; + + level = interpolated(vbat_lower, vbat_upper, level_lower, + level_upper, bat_voltage); + chg_device->cal_capacity = level; + return 0; +} + +void da9052_bat_tbat_handler(struct da9052_eh_nb *eh_data, unsigned int event) +{ + struct da9052_charger_device *chg_device = + container_of(eh_data, struct da9052_charger_device, tbat_eh_data); + + chg_device->health = POWER_SUPPLY_HEALTH_OVERHEAT; + +} + +static s32 da9052_bat_register_event(struct da9052_charger_device *chg_device) +{ + s32 ret; + + if (event_status.da9052_event_tbat == FALSE) { + chg_device->tbat_eh_data.eve_type = TBAT_EVE; + chg_device->tbat_eh_data.call_back = da9052_bat_tbat_handler; + ret = chg_device->da9052->register_event_notifier + (chg_device->da9052, &chg_device->tbat_eh_data); + if (ret) + return -EIO; + event_status.da9052_event_tbat = TRUE; + } + return 0; +} + +static s32 da9052_bat_unregister_event(struct da9052_charger_device *chg_device) +{ + s32 ret; + if (event_status.da9052_event_tbat) { + ret = + chg_device->da9052->unregister_event_notifier + (chg_device->da9052, &chg_device->tbat_eh_data); + if (ret) + return -EIO; + event_status.da9052_event_tbat = FALSE; + } + return 0; +} + +static int da9052_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct da9052_charger_device *chg_device = + container_of(psy, struct da9052_charger_device, psy); + + /* Validate battery presence */ + if (chg_device->illegal && psp != POWER_SUPPLY_PROP_PRESENT) + return -ENODEV; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = chg_device->status; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = + (chg_device->charger_type == DA9052_NOCHARGER) ? 0 : 1; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = chg_device->illegal; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = chg_device->health; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = chg_device->bat_target_voltage * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = chg_device->bat_volt_cutoff * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + val->intval = chg_device->bat_voltage * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = chg_device->chg_current * 1000; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = chg_device->cal_capacity; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = bat_temp_reg_to_C(chg_device->bat_temp); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = chg_device->technology; + break; + default: + return -EINVAL; + break; + } + return 0; +} + + +static u8 detect_illegal_battery(struct da9052_charger_device *chg_device) +{ + u16 buffer = 0; + s32 ret = 0; + + /* Measure battery temeperature */ + ret = da9052_bat_get_battery_temperature(chg_device, &buffer); + if (ret) + return ret; + + if (buffer > chg_device->bat_with_no_resistor) + chg_device->illegal = TRUE; + else + chg_device->illegal = FALSE; + + + /* suspend charging of battery if illegal battey is detected */ + if (chg_device->illegal) + da9052_bat_suspend_charging(chg_device); + + return chg_device->illegal; +} + +void da9052_update_bat_properties(struct da9052_charger_device *chg_device) +{ + /* Get Bat status and type */ + da9052_bat_status_update(chg_device); + da9052_bat_level_update(chg_device); +} + +static void da9052_bat_external_power_changed(struct power_supply *psy) +{linux-kernel@vger.kernel.org + struct da9052_charger_device *chg_device = + container_of(psy, struct da9052_charger_device, psy); + + cancel_delayed_work(&chg_device->monitor_work); + queue_delayed_work(chg_device->monitor_wqueue, + &chg_device->monitor_work, HZ/10); +} + +static void da9052_bat_work(struct work_struct *work) +{ + struct da9052_charger_device *chg_device = container_of(work, + struct da9052_charger_device, monitor_work.work); + da9052_update_bat_properties(chg_device); + queue_delayed_work(chg_device->monitor_wqueue, + &chg_device->monitor_work, HZ * 8); +} + +static enum power_supply_property da9052_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, +}; + +static s32 __devinit da9052_bat_probe(struct platform_device *pdev) +{ + struct da9052_charger_device *chg_device; + u8 reg_data; + int ret; + + chg_device = kzalloc(sizeof(*chg_device), GFP_KERNEL); + if (!chg_device) + return -ENOMEM; + + chg_device->da9052 = dev_get_drvdata(pdev->dev.parent); + platform_set_drvdata(pdev, chg_device); + chg_device->psy.name = DA9052_BAT_DEVICE_NAME; + chg_device->psy.type = POWER_SUPPLY_TYPE_BATTERY; + chg_device->psy.properties = da9052_bat_props; + chg_device->psy.num_properties = ARRAY_SIZE(da9052_bat_props); + chg_device->psy.get_property = da9052_bat_get_property; + chg_device->psy.external_power_changed = + da9052_bat_external_power_changed; + chg_device->psy.use_for_apm = 1; + chg_device->charger_type = DA9052_NOCHARGER; + chg_device->status = POWER_SUPPLY_STATUS_UNKNOWN; + chg_device->health = POWER_SUPPLY_HEALTH_UNKNOWN; + chg_device->technology = POWER_SUPPLY_TECHNOLOGY_LION; + chg_device->bat_with_no_resistor = 62; + chg_device->bat_capacity_limit_low = 4; + chg_device->bat_capacity_limit_high = 70; + chg_device->bat_capacity_full = 100; + chg_device->bat_volt_cutoff = 2800; + chg_device->vbat_first_valid_detect_iteration = 3; + chg_device->hysteresis_window_size = 1; + chg_device->chg_hysteresis_const = 89; + chg_device->hysteresis_reading_interval = 1000; + chg_device->hysteresis_no_of_reading = 10; + + ret = da9052_read(chg_device->da9052, DA9052_CHGCONT_REG, ®_data); + if (ret) + goto err_charger_init; + chg_device->charger_voltage_drop = bat_drop_reg_to_mV(reg_data && + DA9052_CHGCONT_TCTR); + chg_device->bat_target_voltage = + bat_reg_to_mV(reg_data && DA9052_CHGCONT_VCHGBAT); + + ret = da9052_read(chg_device->da9052, DA9052_ICHGEND_REG, ®_data); + if (ret) + goto err_charger_init; + chg_device->chg_end_current = ichg_reg_to_mA(reg_data); + + bat_hysteresis.upper_limit = 0; + bat_hysteresis.lower_limit = 0; + bat_hysteresis.hys_flag = 0; + chg_device->illegal = FALSE; + detect_illegal_battery(chg_device); + + da9052_bat_register_event(chg_device); + if (ret) + goto err_charger_init; + + ret = power_supply_register(&pdev->dev, &chg_device->psy); + if (ret) + goto err_charger_init; + + INIT_DELAYED_WORK(&chg_device->monitor_work, da9052_bat_work); + chg_device->monitor_wqueue = + create_singlethread_workqueue + (dev_name(&pdev->dev)); + if (!chg_device->monitor_wqueue) + goto err_charger_init; + queue_delayed_work(chg_device->monitor_wqueue, + &chg_device->monitor_work, HZ * 1); + return 0; + +err_charger_init: + platform_set_drvdata(pdev, NULL); + kfree(chg_device); + return ret; +} +static int __devexit da9052_bat_remove(struct platform_device *dev) +{ + struct da9052_charger_device *chg_device = platform_get_drvdata(dev); + /* unregister the events.*/ + da9052_bat_unregister_event(chg_device); + cancel_rearming_delayed_workqueue(chg_device->monitor_wqueue, + &chg_device->monitor_work); + destroy_workqueue(chg_device->monitor_wqueue); + power_supply_unregister(&chg_device->psy); + return 0; +} + +static struct platform_driver da9052_bat_driver = { + .probe = da9052_bat_probe, + .remove = __devexit_p(da9052_bat_remove), + .driver.name = DA9052_BAT_DEVICE_NAME, + .driver.owner = THIS_MODULE, +}; + +static int __init da9052_bat_init(void) +{ + return platform_driver_register(&da9052_bat_driver); +} + +static void __exit da9052_bat_exit(void) +{ + platform_driver_unregister(&da9052_bat_driver); +} + +module_init(da9052_bat_init); +module_exit(da9052_bat_exit); + +MODULE_AUTHOR("Dialog Semiconductor Ltd"); +MODULE_DESCRIPTION("DA9052 BAT Device Driver"); +MODULE_LICENSE("GPL"); diff -Naur A-source/drivers/power/Kconfig B-source/drivers/power/Kconfig --- A-source/drivers/power/Kconfig 2010-05-17 02:17:36.000000000 +0500 +++ B-source/drivers/power/Kconfig 2010-12-08 15:12:56.000000000 +0500 @@ -117,6 +117,13 @@ Say Y here to enable support for batteries charger integrated into DA9030 PMIC. +config BATTERY_DA9052 + tristate "Dialog DA9052 Battery" + depends on PMIC_DA9052 + help + Say Y here to enable support for batteries charger integrated into + DA9052 PMIC. + config BATTERY_MAX17040 tristate "Maxim MAX17040 Fuel Gauge" depends on I2C diff -Naur A-source/drivers/power/Makefile B-source/drivers/power/Makefile --- A-source/drivers/power/Makefile 2010-05-17 02:17:36.000000000 +0500 +++ B-source/drivers/power/Makefile 2010-12-08 15:12:56.000000000 +0500 @@ -30,5 +30,6 @@ obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o +obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o diff -Naur A-source/include/linux/mfd/da9052/bat.h B-source/include/linux/mfd/da9052/bat.h --- A-source/include/linux/mfd/da9052/bat.h 1970-01-01 05:00:00.000000000 +0500 +++ B-source/include/linux/mfd/da9052/bat.h 2010-12-08 15:19:19.000000000 +0500 @@ -0,0 +1,233 @@ +/* + * da9052 BAT module declarations. + * + * Copyright(c) 2009 Dialog Semiconductor Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_BAT_H +#define __LINUX_MFD_DA9052_BAT_H + +#include + +/* STATIC CONFIGURATION */ +#define DA9052_LOOK_UP_TABLE_SIZE 68 +#define DA9052_NO_OF_LOOKUP_TABLE 3 +#define FILTER_SIZE 4 +#define DA9052_NUMBER_OF_STORE_CURENT_READING 4 + +static const u16 temperature_lookup_ref + [DA9052_NO_OF_LOOKUP_TABLE] = {10, 25, 40}; +static u32 const vbat_vs_capacity_look_up[DA9052_NO_OF_LOOKUP_TABLE] + [DA9052_LOOK_UP_TABLE_SIZE][2] = { + /* For temperature 10 degree celisus*/ + { + {4082, 100}, {4036, 98}, + {4020, 96}, {4008, 95}, + {3997, 93}, {3983, 91}, + {3964, 90}, {3943, 88}, + {3926, 87}, {3912, 85}, + {3900, 84}, {3890, 82}, + {3881, 80}, {3873, 79}, + {3865, 77}, {3857, 76}, + {3848, 74}, {3839, 73}, + {3829, 71}, {3820, 70}, + {3811, 68}, {3802, 67}, + {3794, 65}, {3785, 64}, + {3778, 62}, {3770, 61}, + {3763, 59}, {3756, 58}, + {3750, 56}, {3744, 55}, + {3738, 53}, {3732, 52}, + {3727, 50}, {3722, 49}, + {3717, 47}, {3712, 46}, + {3708, 44}, {3703, 43}, + {3700, 41}, {3696, 40}, + {3693, 38}, {3691, 37}, + {3688, 35}, {3686, 34}, + {3683, 32}, {3681, 31}, + {3678, 29}, {3675, 28}, + {3672, 26}, {3669, 25}, + {3665, 23}, {3661, 22}, + {3656, 21}, {3651, 19}, + {3645, 18}, {3639, 16}, + {3631, 15}, {3622, 13}, + {3611, 12}, {3600, 10}, + {3587, 9}, {3572, 7}, + {3548, 6}, {3503, 5}, + {3420, 3}, {3268, 2}, + {2992, 1}, {2746, 0} + }, + /* For temperature 25 degree celisus */ + { + {4102, 100}, {4065, 98}, + {4048, 96}, {4034, 95}, + {4021, 93}, {4011, 92}, + {4001, 90}, {3986, 88}, + {3968, 87}, {3952, 85}, + {3938, 84}, {3926, 82}, + {3916, 81}, {3908, 79}, + {3900, 77}, {3892, 76}, + {3883, 74}, {3874, 73}, + {3864, 71}, {3855, 70}, + {3846, 68}, {3836, 67}, + {3827, 65}, {3819, 64}, + {3810, 62}, {3801, 61}, + {3793, 59}, {3786, 58}, + {3778, 56}, {3772, 55}, + {3765, 53}, {3759, 52}, + {3754, 50}, {3748, 49}, + {3743, 47}, {3738, 46}, + {3733, 44}, {3728, 43}, + {3724, 41}, {3720, 40}, + {3716, 38}, {3712, 37}, + {3709, 35}, {3706, 34}, + {3703, 33}, {3701, 31}, + {3698, 30}, {3696, 28}, + {3693, 27}, {3690, 25}, + {3687, 24}, {3683, 22}, + {3680, 21}, {3675, 19}, + {3671, 18}, {3666, 17}, + {3660, 15}, {3654, 14}, + {3647, 12}, {3639, 11}, + {3630, 9}, {3621, 8}, + {3613, 6}, {3606, 5}, + {3597, 4}, {3582, 2}, + {3546, 1}, {2747, 0} + }, + /* For temperature 40 degree celisus*/ + { + {4114, 100}, {4081, 98}, + {4065, 96}, {4050, 95}, + {4036, 93}, {4024, 92}, + {4013, 90}, {4002, 88}, + {3990, 87}, {3976, 85}, + {3962, 84}, {3950, 82}, + {3939, 81}, {3930, 79}, + {3921, 77}, {3912, 76}, + {3902, 74}, {3893, 73}, + {3883, 71}, {3874, 70}, + {3865, 68}, {3856, 67}, + {3847, 65}, {3838, 64}, + {3829, 62}, {3820, 61}, + {3812, 59}, {3803, 58}, + {3795, 56}, {3787, 55}, + {3780, 53}, {3773, 52}, + {3767, 50}, {3761, 49}, + {3756, 47}, {3751, 46}, + {3746, 44}, {3741, 43}, + {3736, 41}, {3732, 40}, + {3728, 38}, {3724, 37}, + {3720, 35}, {3716, 34}, + {3713, 33}, {3710, 31}, + {3707, 30}, {3704, 28}, + {3701, 27}, {3698, 25}, + {3695, 24}, {3691, 22}, + {3686, 21}, {3681, 19}, + {3676, 18}, {3671, 17}, + {3666, 15}, {3661, 14}, + {3655, 12}, {3648, 11}, + {3640, 9}, {3632, 8}, + {3622, 6}, {3616, 5}, + {3611, 4}, {3604, 2}, + {3594, 1}, {2747, 0} + } +}; + +enum charger_type_enum { + DA9052_NOCHARGER = 1, + DA9052_USB_HUB, + DA9052_USB_CHARGER, + DA9052_WALL_CHARGER +}; + +struct da9052_bat_event_registration { + u8 da9052_event_tbat:1; +}; + +struct da9052_bat_hysteresis { + u16 bat_volt_arr[3]; + u16 array_hys_batvoltage[2]; + u16 upper_limit; + u16 lower_limit; + u8 index; + u8 hys_flag; +}; + +struct da9052_charger_device { + struct da9052 *da9052; + struct workqueue_struct *monitor_wqueue; + struct delayed_work monitor_work; + struct power_supply psy; + struct da9052_eh_nb tbat_eh_data; + u8 cal_capacity; + u8 charger_type; + u8 health; + u8 status; + u8 illegal; + u16 technology; + u16 chg_current; + u16 bat_voltage; + u16 bat_capacity_limit_low; + u16 bat_capacity_full; + u16 bat_capacity_limit_high; + u16 bat_volt_cutoff; + u16 bat_with_no_resistor; + u16 bat_temp; + u8 hys_flag; + u16 charger_voltage_drop; + u16 bat_target_voltage; + u16 chg_end_current; + u16 hysteresis_window_size; + u16 chg_hysteresis_const; + u16 hysteresis_reading_interval; + u16 hysteresis_no_of_reading; + u16 vbat_first_valid_detect_iteration; +}; + +static inline u8 bat_temp_reg_to_C(u16 value) { return (55 - value); } +static inline u8 bat_mV_to_reg(u16 value) { return (((value-4100)/100)<<4); } +static inline u8 bat_drop_mV_to_reg(u16 value) + { return (((value-100)/100)<<6); } +static inline u16 bat_reg_to_mV(u8 value) { return ((value*100) + 4100); } +static inline u16 bat_drop_reg_to_mV(u8 value) { return ((value*100)+100); } +static inline u8 vch_thr_mV_to_reg(u16 value) { return ((value-3700)/100); } +static inline u8 precharge_mA_to_reg(u8 value) { return ((value/20)<<6); } +static inline u8 vddout_mon_mV_to_reg(u16 value) + { return (((value-2500)*128)/1000); } +static inline u16 vddout_reg_to_mV(u8 value) + { return ((value*1000)/128)+2500; } +static inline u16 volt_reg_to_mV(u16 value) + { return ((value*1000)/512)+2500; } +static inline u8 ichg_mA_to_reg(u16 value) { return (value/4); } +static inline u16 ichg_reg_to_mA(u8 value) { return ((value*3900)/1000); } +static inline u8 iset_mA_to_reg(u16 iset_value) + {\ + if ((70 <= iset_value) && (iset_value <= 120)) \ + return (iset_value-70)/10; \ + else if ((400 <= iset_value) && (iset_value <= 700)) \ + return ((iset_value-400)/50)+6; \ + else if ((900 <= iset_value) && (iset_value <= 1300)) \ + return ((iset_value-900)/200)+13; else return 0; + } + +#define TRUE 1 +#define FALSE 0 + +#define set_bits(value, mask) (value | mask) +#define clear_bits(value, mask) (value & ~(mask)) + +#endif /* __LINUX_MFD_DA9052_BAT_H */ -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/