From: Dajun Chen on
Battery module of the device driver for DA9052 PMIC device from Dialog
Semiconductor.

Changes made since last submission:
.. code has been reorganised as per the standard linux kernel driver framework
.. removal of redundant printk and comments
.. removal of test framework

Linux Kernel Version: 2.6.34

Signed-off-by: D. Chen <dchen(a)diasemi.com>
---
diff -urpN linux-2.6.34/drivers/power/da9052_battery.c
linux-2.6.34_test/drivers/power/da9052_battery.c
--- linux-2.6.34/drivers/power/da9052_battery.c 1970-01-01
05:00:00.000000000 +0500
+++ linux-2.6.34_test/drivers/power/da9052_battery.c 2010-07-13
17:49:01.000000000 +0500
@@ -0,0 +1,1377 @@
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/freezer.h>
+
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/bat.h>
+#include <linux/mfd/da9052/adc.h>
+
+#define DRIVER_NAME "da9052-battery"
+
+static struct da9052_bat_device bat_info;
+static struct da9052_bat_status bat_status;
+static struct da9052_bat_hysteresis bat_hysteresis;
+static struct da9052_bat_event_registration event_status;
+static struct monitoring_state monitoring_status;
+struct power_supply_info battery_info;
+
+/* Populate it with the releavant values as per the battery used. */
+static struct da9052_bat_threshold thresholds;
+/* Populate it with the releavant values as per the battery used. */
+struct da9052_charger_device charger;
+
+static u16 bat_target_voltage;
+u8 tbat_event_occur;
+
+static int da9052_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val);
+
+
+static enum power_supply_property da902_bat_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ 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_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+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 int da9052_write(struct da9052 *da9052, u8 reg_address, u8 reg_data)
+{
+ struct da9052_ssc_msg msg;
+ int ret;
+
+ msg.addr = reg_address;
+ msg.data = reg_data;
+
+ da9052_lock(da9052);
+ ret = da9052->write(da9052, &msg);
+ if (ret)
+ goto ssc_comm_err;
+ da9052_unlock(da9052);
+
+ return 0;
+ssc_comm_err:
+ da9052_unlock(da9052);
+ return ret;
+}
+
+static s32 da9052_read_ich(struct da9052 *da9052, u16 *data)
+{
+ s32 ret;
+ u8 reg_data;
+
+ ret = da9052_read(da9052, DA9052_ICHGAV_REG, &reg_data);
+ if (ret)
+ return ret;
+
+ *data = (u16)reg_data;
+ return 0;
+ return 0;
+}
+
+static s32 da9052_read_vbbat(struct da9052 *da9052, u16 *data)
+{
+ s32 ret;
+ u16 temp;
+
+ ret = da9052_adc_read(da9052, DA9052_ADC_VBBAT, &temp);
+ if (ret)
+ return ret;
+ else {
+ *data = temp;
+ return 0;
+ }
+}
+
+static s32 da9052_read_vbat(struct da9052 *da9052, u16 *data)
+{
+ s32 ret;
+ u16 temp;
+
+ ret = da9052_adc_read(da9052, DA9052_ADC_VBAT, &temp);
+ if (ret)
+ return ret;
+ else {
+ *data = temp;
+ return 0;
+ }
+}
+
+static s32 da9052_read_tjunc(struct da9052 *da9052, u16 *data)
+{
+ s32 ret;
+ u16 temp;
+
+ ret = da9052_adc_read(da9052, DA9052_TJUNCRES_REG, &temp);
+ if (ret)
+ return ret;
+ *data = temp;
+ temp = 0;
+
+ ret = da9052_adc_read(da9052, DA9052_TOFFSET_REG, &temp);
+ if (ret)
+ return ret;
+
+ *data -= temp;
+
+ return 0;
+}
+
+static s32 da9052_read_tbat(struct da9052 *da9052, u16 *data)
+{
+ s32 ret;
+ u8 reg_data;
+
+ ret = da9052_read(da9052, DA9052_TBATRES_REG, &reg_data);
+ if (ret)
+ return ret;
+
+ *data = (u16)reg_data;;
+ return 0;
+}
+
+static s32 da9052_read_vddout(struct da9052 *da9052, u16 *data)
+{
+ u8 reg_data;
+ s32 ret;
+
+ ret = da9052_read(da9052, DA9052_ADCCONT_REG, &reg_data);
+ if (ret)
+ return ret;
+
+ if (!(reg_data & DA9052_ADCCONT_AUTOVDDEN)) {
+ reg_data = (reg_data | DA9052_ADCCONT_AUTOVDDEN);
+
+ ret = da9052_write(da9052, DA9052_INPUTCONT_REG, reg_data);
+ if (ret)
+ return ret;
+ reg_data = 0x0;
+
+ ret = da9052_read(da9052, DA9052_ADCCONT_REG, &reg_data);
+ if (ret)
+ return ret;
+
+ if (reg_data & DA9052_ADCCONT_ADCMODE)
+ msleep(1);
+ else
+ msleep(10);
+
+ ret = da9052_read(da9052, DA9052_VDDRES_REG, &reg_data);
+ if (ret)
+ return ret;
+
+ *data = (u16)reg_data;
+
+ ret = da9052_read(da9052, DA9052_ADCCONT_REG, &reg_data);
+ if (ret)
+ return ret;
+
+ reg_data = reg_data & ~(DA9052_ADCCONT_AUTOVDDEN);
+ ret = da9052_write(da9052, DA9052_ADCCONT_REG, reg_data);
+ if (ret)
+ return ret;
+ } else {
+ ret = da9052_read(da9052, DA9052_VDDRES_REG, &reg_data);
+ if (ret)
+ return ret;
+
+ *data = (u16)reg_data;
+ }
+ return 0;
+}
+
+static s32 da9052_bat_get_chg_current(struct da9052 *da9052, u16 *buffer)
+{
+
+ if (bat_status.status == DA9052_DISCHARGING_WITHOUT_CHARGER)
+ return DA9052_BAT_NOT_CHARGING;
+
+ if (da9052_read_ich(da9052, buffer))
+ return DA9052_CHG_MEASUREMENT_FAIL;
+
+ bat_info.chg_current = ichg_reg_to_mA(*buffer);
+ *buffer = bat_info.chg_current;
+
+ 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_chg_junc_temperature(struct da9052 *da9052,
+ u16 *buffer)
+{
+ u8 count;
+ u16 filterqueue[FILTER_SIZE];
+
+ if (bat_status.status != DA9052_CHARGING)
+ return DA9052_BAT_NOT_CHARGING;
+
+ for (count = 0; count < FILTER_SIZE; count++) {
+ if (da9052_read_tjunc(da9052, &filterqueue[count]))
+ return DA9052_CHG_MEASUREMENT_FAIL;
+ }
+ filterqueue[0] = filter_sample(filterqueue);
+ bat_info.chg_junc_temp = (((1708 * filterqueue[0])/1000) - 106);
+ *buffer = bat_info.chg_junc_temp;
+ return 0;
+}
+
+static s32 da9052_bat_get_battery_voltage(struct da9052 *da9052, u16 *buffer)
+{
+ u8 count;
+ u16 filterqueue[FILTER_SIZE];
+ s32 ret;
+ for (count = 0; count < FILTER_SIZE; count++) {
+ ret = da9052_read_vbat(da9052, &filterqueue[count]);
+ if (ret)
+ return DA9052_CHG_MEASUREMENT_FAIL;
+ }
+ filterqueue[0] = filter_sample(filterqueue);
+ bat_info.bat_voltage = volt_reg_to_mV(filterqueue[0]);
+ *buffer = bat_info.bat_voltage;
+ return 0;
+}
+
+static s32 da9052_bat_get_backup_battery_voltage(struct da9052 *da9052,
+ u16 *buffer)
+{
+ u8 count;
+ u16 filterqueue[FILTER_SIZE];
+
+ for (count = 0; count < FILTER_SIZE; count++) {
+ if (da9052_read_vbbat(da9052, &filterqueue[count]))
+ return DA9052_CHG_MEASUREMENT_FAIL;
+ }
+ filterqueue[0] = filter_sample(filterqueue);
+ bat_info.backup_bat_voltage =
+ volt_reg_to_mV(filterqueue[0]);
+ *buffer = bat_info.backup_bat_voltage;
+
+ return 0;
+}
+
+static s32 capture_first_correct_vbat_sample(struct da9052 *da9052,
+ u16 *battery_voltage)
+{
+ static u8 count;
+ s32 ret = 0;
+ u32 temp_data = 0;
+
+ ret = da9052_bat_get_battery_voltage(da9052,
+ &bat_hysteresis.bat_volt_arr[count]);
+ if (ret)
+ return ret;
+
+ count++;
+
+ if (count < VBAT_FIRST_VALID_DETECT_ITERATION)
+ return -EINVAL;
+
+ for (count = 0; count < (VBAT_FIRST_VALID_DETECT_ITERATION - 1);
+ count++) {
+ temp_data = (bat_hysteresis.bat_volt_arr[count] *
+ HYSTERESIS_WINDOW_SIZE)/100;
+ bat_hysteresis.upper_limit = bat_hysteresis.bat_volt_arr[count]
+ + temp_data;
+ bat_hysteresis.lower_limit = bat_hysteresis.bat_volt_arr[count]
+ - temp_data;
+
+ if ((bat_hysteresis.bat_volt_arr[count + 1] <
+ bat_hysteresis.upper_limit) &&
+ (bat_hysteresis.bat_volt_arr[count + 1] >
+ bat_hysteresis.lower_limit)){
+
+ *battery_voltage = (bat_hysteresis.bat_volt_arr[count] +
+ bat_hysteresis.bat_volt_arr[count+1])/2;
+ bat_hysteresis.hys_flag = 1;
+ return 0;
+ }
+ }
+
+ for (count = 0; count < (VBAT_FIRST_VALID_DETECT_ITERATION - 1);
+ count++)
+ bat_hysteresis.bat_volt_arr[count] =
+ bat_hysteresis.bat_volt_arr[count + 1];
+
+ return -EINVAL;
+}
+
+static u32 interpolated(u32 vbat_lower, u32 vbat_upper, u32 level_lower,
+ u32 level_upper, u32 bat_voltage)
+{
+ s32 temp;
+
+ temp = ((level_upper - level_lower) * 1000)/(vbat_upper - vbat_lower);
+ temp = level_lower + (((bat_voltage - vbat_lower) * temp)/1000);
+
+ return temp;
+}
+
+static u8 select_temperature(u8 temp_index, u16 bat_temperature)
+{
+ u16 temp_temperature = 0;
+ u16 ret;
+
+ temp_temperature = (temperature_lookup_ref[temp_index] +
+ temperature_lookup_ref[temp_index+1]) / 2;
+
+ if (bat_temperature >= temp_temperature) {
+ ret = temp_index+1;
+ return ret;
+ } else
+ return temp_index;
+}
+
+static s32 check_hystersis(struct da9052 *da9052, u16 *bat_voltage)
+{
+ u8 ret = 0;
+ u32 offset = 0;
+
+ if (bat_hysteresis.hys_flag == 0) {
+ ret =
+ capture_first_correct_vbat_sample(da9052,
+ &bat_hysteresis.array_hys_batvoltage[0]);
+ if (ret)
+ return ret;
+ }
+
+ ret = da9052_bat_get_battery_voltage(da9052,
+ &bat_hysteresis.array_hys_batvoltage[1]);
+
+ if (ret)
+ return ret;
+ *bat_voltage = bat_hysteresis.array_hys_batvoltage[1];
+
+ if ((bat_hysteresis.upper_limit < *bat_voltage) ||
+ (bat_hysteresis.lower_limit > *bat_voltage)) {
+
+ bat_hysteresis.index++;
+
+ if (bat_hysteresis.index == HYSTERESIS_NO_OF_READING) {
+ bat_hysteresis.index = 0;
+ offset = ((*bat_voltage) * HYSTERESIS_WINDOW_SIZE)/100;
+ bat_hysteresis.upper_limit = (*bat_voltage) + offset;
+ bat_hysteresis.lower_limit = (*bat_voltage) - offset;
+
+ } else
+ return CHG_HYSTERSIS_CHECK_FAILED;
+ } else {
+ bat_hysteresis.index = 0;
+ offset = ((*bat_voltage) * HYSTERESIS_WINDOW_SIZE)/100;
+ bat_hysteresis.upper_limit = (*bat_voltage) + offset;
+ bat_hysteresis.lower_limit = (*bat_voltage) - offset;
+ }
+ *bat_voltage = ((CHG_HYSTERESIS_CONST *
+ bat_hysteresis.array_hys_batvoltage[0])/100) +
+ (((100 - CHG_HYSTERESIS_CONST) *
+ bat_hysteresis.array_hys_batvoltage[1])/100);
+
+ if ((bat_status.status == DA9052_DISCHARGING_WITHOUT_CHARGER) &&
+ (*bat_voltage > bat_hysteresis.array_hys_batvoltage[0]))
+ *bat_voltage = bat_hysteresis.array_hys_batvoltage[0];
+
+ bat_hysteresis.array_hys_batvoltage[0] = *bat_voltage;
+
+ return 0;
+}
+
+static s32 da9052_bat_get_charger_vddout(struct da9052 *da9052, u16 *buffer)
+{
+ u8 count;
+ s32 ret;
+ u16 filterqueue[FILTER_SIZE];
+
+ if (bat_status.status != DA9052_CHARGING)
+ return DA9052_BAT_NOT_CHARGING;
+
+ for (count = 0; count < FILTER_SIZE; count++) {
+ ret = da9052_read_vddout(da9052, &filterqueue[count]);
+ if (ret)
+ return DA9052_CHG_MEASUREMENT_FAIL;
+ }
+ filterqueue[0] = filter_sample(filterqueue);
+ bat_info.vddout = vddout_reg_to_mV(filterqueue[0]);
+ *buffer = bat_info.vddout;
+
+ return 0;
+}
+
+void da9052_bat_vddlow_notifier(struct da9052_eh_nb *eh_data, u32 event)
+{
+ struct da9052_charger_device *charger =\
+ container_of(eh_data, struct da9052_charger_device,
+ vddlow_eh_data);
+ u16 buffer = 0;
+ s32 ret;
+
+ if (!monitoring_status.vddout_status) {
+ monitoring_status.vddout_status = 1;
+ ret = da9052_bat_get_charger_vddout(charger->da9052, &buffer);
+ monitoring_status.vddout_value = buffer;
+ }
+}
+
+void da9052_bat_tbat_notifier(struct da9052_eh_nb *eh_data, u32 event)
+{
+ if (!tbat_event_occur) {
+ bat_status.health = POWER_SUPPLY_HEALTH_OVERHEAT;
+ tbat_event_occur = 1;
+ monitoring_status.bat_temp_status = 1;
+ monitoring_status.bat_temp_value = bat_info.bat_temp;
+ }
+}
+
+static s32 da9052_bat_suspend_charging(struct da9052 *da9052)
+{
+
+ u8 reg_data;
+ u8 msg_data;
+ s32 ret;
+
+ if ((bat_status.status == DA9052_DISCHARGING_WITHOUT_CHARGER) ||
+ (bat_status.status == DA9052_DISCHARGING_WITH_CHARGER))
+ return 0;
+
+ ret = da9052_read(da9052, DA9052_INPUTCONT_REG, &reg_data);
+ if (ret)
+ return ret;
+
+ msg_data = (reg_data | DA9052_INPUTCONT_DCINSUSP);
+ msg_data = (msg_data | DA9052_INPUTCONT_VBUSSUSP);
+
+ ret = da9052_write(da9052, DA9052_INPUTCONT_REG, msg_data);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static s32 da9052_bat_resume_charging(struct da9052 *da9052)
+{
+ u8 reg_data;
+ u8 msg_data;
+ s32 ret;
+
+ if (bat_status.illegalbattery)
+ return -EINVAL;
+
+ if ((bat_status.status == DA9052_CHARGING))
+ return 0;
+
+ ret = da9052_read(da9052, DA9052_INPUTCONT_REG, &reg_data);
+ if (ret)
+ return ret;
+
+ msg_data = (reg_data & ~(DA9052_INPUTCONT_DCINSUSP));
+ msg_data = (reg_data & ~(DA9052_INPUTCONT_VBUSSUSP));
+
+ ret = da9052_write(da9052, DA9052_INPUTCONT_REG, msg_data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+static s32 da9052_bat_get_battery_temperature(struct da9052 *da9052,
+ u16 *buffer)
+{
+ u8 count;
+ u16 filterqueue[FILTER_SIZE];
+ for (count = 0; count < FILTER_SIZE; count++) {
+ if (da9052_read_tbat(da9052, &filterqueue[count]))
+ return DA9052_CHG_MEASUREMENT_FAIL;
+ }
+ filterqueue[0] = filter_sample(filterqueue);
+ bat_info.bat_temp = filterqueue[0];
+ *buffer = bat_info.bat_temp;
+ return 0;
+}
+
+static s32 da9052_bat_register_event(struct da9052_charger_device *chg_device,
+ u8 event_type)
+{
+ s32 ret;
+ switch (event_type) {
+ case VDD_LOW_EVE:
+ if (!event_status.da9052_event_vddlow) {
+ chg_device->vddlow_eh_data.eve_type = event_type;
+ chg_device->vddlow_eh_data.call_back =
+ da9052_bat_vddlow_notifier;
+ ret = chg_device->da9052->register_event_notifier
+ (chg_device->da9052,
+ &chg_device->vddlow_eh_data);
+ if (ret)
+ return DA9052_IRQ_REGISTER_FAILED;
+ event_status.da9052_event_vddlow = 1;
+ }
+ break;
+ case TBAT_EVE:
+ if (!event_status.da9052_event_tbat) {
+ chg_device->tbat_eh_data.eve_type = event_type;
+ chg_device->tbat_eh_data.call_back =
+ da9052_bat_tbat_notifier;
+ ret = chg_device->da9052->register_event_notifier
+ (chg_device->da9052,
+ &chg_device->tbat_eh_data);
+ if (ret)
+ return DA9052_IRQ_REGISTER_FAILED;
+ event_status.da9052_event_tbat = 1;
+ }
+ break;
+ default:
+ return DA9052_BAT_INVALID_EVENT;
+ }
+
+ return 0;
+}
+
+static s32 da9052_bat_unregister_event(struct da9052_charger_device
*chg_device,
+ u8 event_type)
+{
+ s32 ret;
+ switch (event_type) {
+ case VDD_LOW_EVE:
+ if (event_status.da9052_event_vddlow) {
+ ret =
+ chg_device->da9052->unregister_event_notifier
+ (chg_device->da9052,
+ &chg_device->vddlow_eh_data);
+ if (ret)
+ return DA9052_IRQ_UNREGISTER_FAILED;
+ event_status.da9052_event_vddlow = 0;
+ }
+ break;
+ case TBAT_EVE:
+ if (event_status.da9052_event_tbat) {
+ ret =
+ chg_device->da9052->unregister_event_notifier
+ (chg_device->da9052, &chg_device->tbat_eh_data);
+ if (ret)
+ return DA9052_IRQ_UNREGISTER_FAILED;
+ event_status.da9052_event_tbat = 0;
+ }
+ break;
+ default:
+ return DA9052_BAT_INVALID_EVENT;
+ }
+
+ return 0;
+}
+#if (DA9052_ILLEGAL_BATTERY_DETECT)
+static s32 detect_illegal_battery(struct da9052 *da9052)
+{
+ u16 buffer = 0;
+ s32 ret = 0;
+
+ ret = da9052_bat_get_battery_temperature(da9052, &buffer);
+ if (ret)
+ return ret;
+
+ if (buffer > BAT_WITH_NO_RESISTOR)
+ bat_status.illegalbattery = 1;
+ else
+ bat_status.illegalbattery = 0;
+
+ if (bat_status.illegalbattery)
+ da9052_bat_suspend_charging(da9052);
+
+ return 0;
+}
+#endif
+
+static int da9052_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ s32 ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (bat_status.status == DA9052_CHARGING)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+
+ else if (bat_status.status ==
+ DA9052_DISCHARGING_WITH_CHARGER)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+ else if (bat_status.status ==
+ DA9052_DISCHARGING_WITHOUT_CHARGER)
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+
+ else if (bat_status.status == DA9052_CHARGEEND)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (bat_status.charger_type == DA9052_NOCHARGER)
+ val->intval = 0;
+ else
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ if (bat_status.illegalbattery)
+ val->intval = 0;
+ else
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (bat_status.health != POWER_SUPPLY_HEALTH_OVERHEAT) {
+ if (bat_status.illegalbattery)
+ bat_status.health = POWER_SUPPLY_HEALTH_UNKNOWN;
+
+ else if (bat_status.cal_capacity <
+ BAT_CAPACITY_LIMIT_LOW)
+ bat_status.health = POWER_SUPPLY_HEALTH_DEAD;
+
+ else
+ bat_status.health = POWER_SUPPLY_HEALTH_GOOD;
+ }
+ val->intval = bat_status.health;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = (bat_target_voltage * 1000);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = (BAT_VOLT_CUTOFF * 1000);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ val->intval = (bat_info.bat_voltage * 1000);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = (bat_info.chg_current * 1000);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = bat_status.cal_capacity;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+ if (bat_status.illegalbattery)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+
+ else if (bat_status.cal_capacity < BAT_CAPACITY_LIMIT_LOW)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+
+ else if (bat_status.cal_capacity < BAT_CAPACITY_LIMIT_HIGH)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+
+ else if (bat_status.cal_capacity == BAT_CAPACITY_FULL)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+
+ else if (bat_status.cal_capacity > BAT_CAPACITY_LIMIT_HIGH)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+
+ else
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = bat_temp_reg_to_C(bat_info.bat_temp);
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = BAT_MANUFACTURER;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = BAT_TYPE;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return 0;
+}
+static void da9052_battery_setup_psy(struct da9052_charger_device *chg_device)
+{
+ battery_info.name = DRIVER_NAME;
+ battery_info.technology = BAT_TYPE;
+ battery_info.voltage_max_design =
+ (chg_device->bat_target_voltage*1000);
+ battery_info.voltage_min_design = (BAT_VOLT_CUTOFF*1000);
+ battery_info.energy_full_design = BAT_CAPACITY_FULL;
+ battery_info.energy_empty_design = BAT_CAPACITY_LIMIT_LOW;
+ battery_info.use_for_apm = 1;
+
+ chg_device->psy.name = DRIVER_NAME;
+ chg_device->psy.use_for_apm = 1;
+ chg_device->psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ chg_device->psy.get_property = da9052_bat_get_property;
+
+ chg_device->psy.properties = da902_bat_props;
+ chg_device->psy.num_properties = ARRAY_SIZE(da902_bat_props);
+
+};
+
+static void da9052_charger_status_update(struct da9052_charger_device
+ *chg_device)
+{
+ u16 current_value = 0;
+ u8 regvalue = 0;
+ u8 reg_data;
+ s32 ret;
+
+ ret = da9052_read(chg_device->da9052, DA9052_STATUSA_REG, &regvalue);
+ if (ret)
+ return;
+
+ ret = da9052_read(chg_device->da9052, DA9052_STATUSB_REG, &reg_data);
+ if (ret)
+ return;
+
+ if ((regvalue & DA9052_STATUSA_DCINSEL)
+ && (regvalue & DA9052_STATUSA_DCINDET)) {
+
+ if ((reg_data & DA9052_STATUSB_CHGEND) != 0) {
+ if (da9052_bat_get_chg_current(chg_device->da9052,
+ &current_value))
+ return;
+
+ if (current_value >= chg_device->chg_end_current) {
+ bat_status.status = DA9052_CHARGING;
+ bat_status.charger_type = DA9052_WALL_CHARGER;
+ } else {
+ bat_status.charger_type = DA9052_WALL_CHARGER;
+ bat_status.status =
+ DA9052_DISCHARGING_WITH_CHARGER;
+ }
+ } else {
+ bat_status.status = DA9052_CHARGING;
+ bat_status.charger_type = DA9052_WALL_CHARGER;
+ }
+ } else if ((regvalue & DA9052_STATUSA_VBUSSEL)
+ && (regvalue & DA9052_STATUSA_VBUSDET)) {
+
+ if (regvalue & DA9052_STATUSA_VDATDET)
+ bat_status.charger_type = DA9052_USB_CHARGER;
+ else
+ bat_status.charger_type = DA9052_USB_HUB;
+ if ((reg_data & DA9052_STATUSB_CHGEND) != 0) {
+ if (da9052_bat_get_chg_current(chg_device->da9052,
+ &current_value))
+ return;
+ if (current_value >= chg_device->chg_end_current)
+ bat_status.status = DA9052_CHARGING;
+ else
+ bat_status.status =
+ DA9052_DISCHARGING_WITH_CHARGER;
+ } else
+ bat_status.status = DA9052_CHARGING;
+ } else if (regvalue & DA9052_STATUSA_DCINDET) {
+ bat_status.charger_type = DA9052_WALL_CHARGER;
+ bat_status.status = DA9052_DISCHARGING_WITH_CHARGER;
+ } else if (regvalue & DA9052_STATUSA_VBUSDET) {
+ if (regvalue & DA9052_STATUSA_VDATDET) {
+ bat_status.charger_type = DA9052_USB_CHARGER;
+ bat_status.status = DA9052_DISCHARGING_WITH_CHARGER;
+ } else {
+ bat_status.charger_type = DA9052_USB_HUB;
+ bat_status.status = DA9052_DISCHARGING_WITH_CHARGER;
+ }
+ } else {
+ bat_status.charger_type = DA9052_NOCHARGER;
+ bat_status.status = DA9052_DISCHARGING_WITHOUT_CHARGER;
+ }
+ return;
+}
+
+static s32 monitor_current(struct da9052_charger_device *chg_device)
+{
+ static u8 flag1;
+ u8 count = 0;
+ u16 current_value = 0;
+ u16 tempvalue = 0;
+ u16 avg_value = 0;
+
+ if (da9052_bat_get_chg_current(chg_device->da9052, &current_value))
+ return DA9052_CHG_MEASUREMENT_FAIL;
+
+ if (flag1 == 0) {
+ for (count = 0; count < NUMBER_OF_STORE_CURENT_READING; count++)
+ bat_info.chg_current_raw[count] = 0;
+
+ tempvalue = (CURRENT_MONITORING_WINDOW*current_value)/100;
+ chg_device->threshold.ichg_av_thr_min = current_value-tempvalue;
+ chg_device->threshold.ichg_av_thr_max = current_value+tempvalue;
+ flag1 = 1;
+ }
+
+ for (count = (NUMBER_OF_STORE_CURENT_READING - 1); count > 0; count--)
+ bat_info.chg_current_raw[count] =
+ bat_info.chg_current_raw[count-1];
+ bat_info.chg_current_raw[0] = current_value;
+
+ for (count = 0; count < NUMBER_OF_STORE_CURENT_READING; count++) {
+ if (bat_info.chg_current_raw[count] == 0)
+ break;
+ avg_value = avg_value + bat_info.chg_current_raw[count];
+ }
+ if (count != 0)
+ avg_value = avg_value/count;
+ else
+ avg_value = current_value;
+
+ tempvalue = (CURRENT_MONITORING_WINDOW*avg_value)/100;
+
+ if (((current_value < chg_device->threshold.ichg_av_thr_min)
+ || (current_value > chg_device->threshold.ichg_av_thr_max))) {
+
+ monitoring_status.current_status = 1;
+ monitoring_status.current_value = current_value;
+
+ chg_device->threshold.ichg_av_thr_min = avg_value - tempvalue;
+ chg_device->threshold.ichg_av_thr_max = avg_value + tempvalue;
+ return -(DA9052_CHG_MONITORING_FAIL);
+ } else {
+ monitoring_status.current_status = 0;
+ monitoring_status.current_value = current_value;
+ }
+ chg_device->threshold.ichg_av_thr_min = avg_value - tempvalue;
+ chg_device->threshold.ichg_av_thr_max = avg_value + tempvalue;
+
+ return 0;
+
+}
+
+static s32 monitor_junc_temperature(struct da9052_charger_device *chg_device)
+{
+ u16 buffer;
+ u8 ret = 0;
+
+ ret = da9052_bat_get_chg_junc_temperature(chg_device->da9052, &buffer);
+ if (ret)
+ return ret;
+
+ if (buffer > chg_device->threshold.tjunc_thr_limit) {
+ if (chg_device->sw_temp_cntr == 1)
+ da9052_bat_suspend_charging(chg_device->da9052);
+
+ monitoring_status.junc_temp_status = 1;
+ monitoring_status.junc_temp_value = buffer;
+
+ return DA9052_CHG_MONITORING_FAIL;
+ } else {
+ monitoring_status.junc_temp_status = 0;
+ monitoring_status.junc_temp_value = buffer;
+ }
+ return 0;
+}
+
+static s32 da9052_get_bat_level(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 = 0;
+
+ ret = 0;
+ vbat_lower = 0;
+ vbat_upper = 0;
+ level_upper = 0;
+ level_lower = 0;
+
+ ret = check_hystersis(chg_device->da9052, &bat_voltage);
+ if (ret)
+ return ret;
+
+ ret = da9052_bat_get_battery_temperature(chg_device->da9052,
+ &bat_temperature);
+ if (ret)
+ return ret;
+ for (index = 0; index < (NO_OF_LOOKUP_TABLE-1); index++) {
+ if (bat_temperature <= temperature_lookup_ref[0]) {
+ access_index = 0;
+ break;
+ } else if (bat_temperature >
+ temperature_lookup_ref[NO_OF_LOOKUP_TABLE]){
+ access_index = 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]) {
+ bat_status.cal_capacity = 100;
+ return 0;
+ }
+ if (bat_voltage <=
+ vbat_vs_capacity_look_up[access_index][LOOK_UP_TABLE_SIZE-1][0]) {
+ bat_status.cal_capacity = 0;
+ return 0;
+ }
+
+ flag = 0;
+
+ for (index = 0; index < (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 = 1;
+ break;
+ }
+ }
+
+ if (!flag)
+ return DA9052_INVALID_VBAT_VALUE;
+
+ level = interpolated(vbat_lower, vbat_upper, level_lower,
+ level_upper, bat_voltage);
+ bat_status.cal_capacity = level;
+
+ return 0;
+}
+
+static s32 monitor_bat_temperature(struct da9052_charger_device *chg_device)
+{
+ u16 buffer;
+ u8 ret = 0;
+
+ ret = da9052_bat_get_battery_temperature(chg_device->da9052, &buffer);
+ if (ret)
+ return ret;
+
+ if (buffer > chg_device->threshold.tbat_thr_limit) {
+ if (chg_device->sw_temp_cntr == 1)
+ da9052_bat_suspend_charging(chg_device->da9052);
+
+ monitoring_status.bat_temp_status = 1;
+ monitoring_status.bat_temp_value = buffer;
+ return DA9052_CHG_MONITORING_FAIL;
+ } else {
+ monitoring_status.bat_temp_status = 0;
+ monitoring_status.bat_temp_value = buffer;
+ }
+
+ return 0;
+}
+
+static void da9052_monitoring_thread(struct work_struct *work)
+{
+ u8 mon_count = 0;
+ s32 ret = 0;
+ struct da9052_charger_device *chg_device;
+ chg_device = container_of(work, struct da9052_charger_device,
+ work.work);
+
+ if (bat_status.status == DA9052_CHARGING) {
+ if (mon_count == 0) {
+ ret = monitor_current(chg_device);
+ if (DA9052_CHG_MONITORING_FAIL == ret)
+ pr_debug("charging Current Monitoring\
+ failed, %d\n", mon_count);
+ } else if (mon_count == 1) {
+ ret = monitor_junc_temperature(chg_device);
+ if (DA9052_CHG_MONITORING_FAIL == ret)
+ pr_debug("Charger Junction Temperature\
+ Monitoring failed\n");
+ }
+ }
+
+ if (mon_count == 2) {
+ ret = da9052_get_bat_level(chg_device);
+ if (!ret) {
+ if (bat_status.cal_capacity < BAT_CAPACITY_LIMIT_LOW) {
+ monitoring_status.bat_level_status = 1;
+ monitoring_status.bat_level =
+ bat_status.cal_capacity;
+ } else {
+ monitoring_status.bat_level_status = 0;
+ monitoring_status.bat_level =
+ bat_status.cal_capacity;
+ }
+ } else
+ pr_debug("Battery Measurement Fails = %d\n", ret);
+ }
+
+ if (mon_count == 3) {
+ ret = monitor_bat_temperature(chg_device);
+ if (ret)
+ pr_debug("BAT Temperature Monitoring failed\n");
+ }
+
+ mon_count++;
+ if (mon_count == 4)
+ mon_count = 0;
+
+ schedule_delayed_work(&chg_device->work,
+ chg_device->monitoring_interval);
+}
+
+static s32 da9052_bat_configure_thresholds(
+ struct da9052_charger_device *chg_device,
+ struct da9052_bat_threshold thresholds)
+{
+ s32 ret;
+ u8 reg_data;
+
+ if ((VDDOUT_MON_LOWER > thresholds.vddout_mon) &&
+ (VDDOUT_MON_UPPER < thresholds.vddout_mon))
+ return DA9052_INVALID_VDDOUT_MON_VALUE;
+
+ reg_data = vddout_mon_mV_to_reg(thresholds.vddout_mon);
+ ret = da9052_write(chg_device->da9052, DA9052_VDDMON_REG, reg_data);
+ if (ret)
+ return ret;
+
+ reg_data = 0;
+
+ if (ICHG_THRESHOLD_UPPER < thresholds.ichg_thr)
+ return DA9052_INVALID_ICHG_THRESHOLD_VALUE;
+
+ reg_data = ichg_mA_to_reg(thresholds.ichg_thr);
+ ret = da9052_write(chg_device->da9052, DA9052_ICHGTHD_REG, reg_data);
+ if (ret)
+ return ret;
+ reg_data = 0;
+
+ if (TBAT_MAX_THRESHOLD_LIMIT < thresholds.tbat_thr_max)
+ return DA9052_INVALID_BAT_TEMP_HIGH;
+
+ reg_data = thresholds.tbat_thr_max;
+ ret = da9052_write(chg_device->da9052, DA9052_TBATHIGHP_REG, reg_data);
+ if (ret)
+ return ret;
+ reg_data = 0;
+
+ if (TBAT_MIN_THRESHOLD_LIMIT < thresholds.tbat_thr_min)
+ return DA9052_INVALID_BAT_TEMP_LOW;
+
+ reg_data = thresholds.tbat_thr_min;
+ ret = da9052_write(chg_device->da9052, DA9052_TBATLOW_REG, reg_data);
+ if (ret)
+ return ret;
+ reg_data = 0;
+
+ if (TBAT_HIGHNS_THRESHOLD_LIMIT < thresholds.tbat_thr_highns)
+ return DA9052_INVALID_BAT_TEMP_HIGHN;
+
+ reg_data = thresholds.tbat_thr_highns;
+ ret = da9052_write(chg_device->da9052, DA9052_TBATHIGHIN_REG,
+ reg_data);
+ if (ret)
+ return ret;
+ reg_data = 0;
+
+ chg_device->threshold = thresholds;
+
+ return 0;
+}
+
+static s32 da9052_bat_configure_charger(
+ struct da9052_charger_device *chg_device,
+ struct da9052_charger_device charger)
+{
+ s32 ret;
+ u8 reg_data;
+
+ ret = da9052_read(chg_device->da9052, DA9052_CHGBUCK_REG, &reg_data);
+ if (ret)
+ return ret;
+
+ reg_data = charger.charger_buck_lp ?
+ (reg_data | DA9052_CHGBUCK_CHGBUCKLP) :
+ (reg_data & ~(DA9052_CHGBUCK_CHGBUCKLP));
+
+ reg_data = charger.usb_charger_det ?
+ (reg_data | DA9052_CHGBUCK_CHGUSBILIM) :
+ (reg_data & ~(DA9052_CHGBUCK_CHGUSBILIM));
+
+ reg_data = charger.auto_temp_cntr ?
+ (reg_data | DA9052_CHGBUCK_CHGTEMP) :
+ (reg_data & ~(DA9052_CHGBUCK_CHGTEMP));
+
+ ret = da9052_write(chg_device->da9052, DA9052_CHGBUCK_REG, reg_data);
+ if (ret)
+ return ret;
+
+ reg_data = 0x0;
+
+ if ((ISET_LOW > charger.dcin_current) &&
+ (charger.dcin_current > ISET_HIGH))
+ return DA9052_INVALID_DCIN_CURRENT_LIMIT_VALUE;
+
+ if ((ISET_LOW > charger.vbus_current) &&
+ (charger.vbus_current > ISET_HIGH))
+ return DA9052_INVALID_VBUS_CURRENT_LIMIT_VALUE;
+
+ if ((ISET_LOW > charger.usb_charger_current) &&
+ (charger.usb_charger_current > ISET_HIGH))
+ return DA9052_INVALID_USB_CHARGER_CURRENT_LIMIT_VALUE;
+
+ chg_device->dcin_current = charger.dcin_current;
+ chg_device->vbus_current = charger.vbus_current;
+ chg_device->usb_charger_current = charger.usb_charger_current;
+
+ if ((iset_mA_to_reg(charger.vbus_current) &&
+ iset_mA_to_reg(charger.dcin_current)) == 0)
+ return -EINVAL;
+
+ reg_data = iset_mA_to_reg(charger.vbus_current) |
+ (iset_mA_to_reg(charger.dcin_current) << 4);
+
+ ret = da9052_write(chg_device->da9052, DA9052_ISET_REG, reg_data);
+ if (ret)
+ return ret;
+
+ reg_data = 0x0;
+
+ ret = da9052_read(chg_device->da9052, DA9052_BATCHG_REG, &reg_data);
+ if (ret)
+ return ret;
+
+ if ((PRE_CHARGE_0MA != charger.precharging_current) &&
+ (PRE_CHARGE_20MA != charger.precharging_current) &&
+ (PRE_CHARGE_40MA != charger.precharging_current) &&
+ (PRE_CHARGE_60MA != charger.precharging_current)) {
+ return DA9052_INVALID_PRECHARGE_CURRENT_VALUE;
+ }
+
+ reg_data = (reg_data & ~(DA9052_BATCHG_ICHGPRE));
+ reg_data = (reg_data |
+ (precharge_mA_to_reg(charger.precharging_current)));
+
+ ret = da9052_write(chg_device->da9052, DA9052_BATCHG_REG, reg_data);
+ if (ret)
+ return ret;
+
+ reg_data = 0x0;
+
+ if (charger.charging_time > MAX_BAT_CHARGING_TIME)
+ return DA9052_INVALID_CHARGING_TIME;
+
+ if ((BAT_TARGET_VOLTAGE_LOWER_LIMIT > charger.bat_target_voltage) &&
+ (charger.bat_target_voltage > BAT_TARGET_VOLTAGE_UPPER_LIMIT))
+ return DA9052_INVALID_CHG_BAT_VOLTAGE;
+
+ if ((CHARGER_VOLTAGE_DROP_LOWER_LIMIT > charger.charger_voltage_drop) &&
+ (charger.charger_voltage_drop >
+ CHARGER_VOLTAGE_DROP_UPPER_LIMIT))
+ return DA9052_INVALID_CHG_VOLTAGE_DROP;
+
+ reg_data = (charger.charging_time/CHARGING_TIME_INTERVAL) |
+ bat_mV_to_reg(charger.bat_target_voltage) |
+ bat_drop_mV_to_reg(charger.charger_voltage_drop);
+
+ ret = da9052_write(chg_device->da9052, DA9052_CHGCONT_REG, reg_data);
+ if (ret)
+ return ret;
+
+ reg_data = 0x0;
+
+ if ((BAT_VOLTAGE_THRESHOLD_LOWER_LIMIT > charger.voltage_threshold) &&
+ (charger.voltage_threshold >
+ BAT_VOLTAGE_THRESHOLD_UPPER_LIMIT))
+ return DA9052_INVALID_CHG_VOLTAGE_THRESHOLD;
+
+ reg_data = vch_thr_mV_to_reg(charger.voltage_threshold);
+ reg_data |= (charger.ichg_low_cntr ? DA9052_INPUTCONT_ICHGLOW : 0);
+ reg_data |= (charger.timer_mode ? DA9052_INPUTCONT_TCTRMODE : 0);
+
+ ret = da9052_write(chg_device->da9052, DA9052_INPUTCONT_REG, reg_data);
+ if (ret)
+ return ret;
+
+ reg_data = 0x0;
+
+ reg_data = (charger.chg_end_current / 4);
+
+ ret = da9052_write(chg_device->da9052, DA9052_ICHGEND_REG, reg_data);
+ if (ret)
+ return ret;
+ reg_data = 0x0;
+
+ chg_device->sw_temp_cntr = charger.sw_temp_cntr;
+ chg_device->monitoring_interval =
+ msecs_to_jiffies(charger.monitoring_interval);
+
+ return 0;
+}
+
+static int da9052_battery_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->monitoring_interval = msecs_to_jiffies(MONITORING_INTERVAL);
+ chg_device->sw_temp_cntr = SW_TEMP_CONTROL_EN;
+ chg_device->usb_charger_current = 0;
+
+ ret = da9052_read(chg_device->da9052, DA9052_CHGCONT_REG, &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);
+ bat_target_voltage = chg_device->bat_target_voltage;
+
+ reg_data = 0;
+ ret = da9052_read(chg_device->da9052, DA9052_ICHGEND_REG, &reg_data);
+ if (ret)
+ goto err_charger_init;
+
+ chg_device->chg_end_current = ichg_reg_to_mA(reg_data);
+
+ chg_device->threshold.tbat_thr_limit = SW_BAT_TEMP_THRESHOLD;
+ chg_device->threshold.tjunc_thr_limit = SW_JUNC_TEMP_THRESHOLD;
+
+ sprintf(bat_info.manufacture, BAT_MANUFACTURER);
+ bat_status.illegalbattery = 0;
+
+ bat_hysteresis.upper_limit = 0;
+ bat_hysteresis.lower_limit = 0;
+ bat_hysteresis.hys_flag = 0;
+
+ bat_status.charger_type = DA9052_NOCHARGER;
+ bat_status.status = DA9052_CHARGING;
+ bat_status.charging_mode = DA9052_NONE;
+ tbat_event_occur = 0;
+#if (DA9052_ILLEGAL_BATTERY_DETECT)
+ detect_illegal_battery(chg_device->da9052);
+#endif
+
+ da9052_charger_status_update(chg_device);
+
+ da9052_battery_setup_psy(chg_device);
+
+ INIT_DELAYED_WORK(&chg_device->work, da9052_monitoring_thread);
+ schedule_delayed_work(&chg_device->work,
+ chg_device->monitoring_interval);
+
+ ret = da9052_bat_register_event(chg_device, VDD_LOW_EVE);
+ if (ret)
+ goto err_charger_init;
+ da9052_bat_register_event(chg_device, TBAT_EVE);
+ if (ret)
+ goto err_charger_init;
+
+ ret = da9052_bat_configure_thresholds(chg_device, thresholds);
+ if (ret)
+ goto err_charger_init;
+
+ ret = da9052_bat_configure_charger(chg_device, charger);
+ if (ret)
+ goto err_charger_init;
+
+ ret = power_supply_register(&pdev->dev, &chg_device->psy);
+ if (ret)
+ goto err_charger_init;
+
+ return 0;
+err_charger_init:
+ platform_set_drvdata(pdev, NULL);
+ kfree(chg_device);
+ return ret;
+}
+
+static int da9052_battery_remove(struct platform_device *dev)
+{
+
+ struct da9052_charger_device *chg_device = platform_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&chg_device->work);
+
+ da9052_bat_unregister_event(chg_device, VDD_LOW_EVE);
+ da9052_bat_unregister_event(chg_device, TBAT_EVE);
+
+ cancel_delayed_work_sync(&chg_device->work);
+
+ power_supply_unregister(&chg_device->psy);
+
+ kfree(chg_device);
+
+ return 0;
+}
+
+static struct platform_driver da9052_battery_driver = {
+ .driver = {
+ .name = "DRIVER_NAME",
+ .owner = THIS_MODULE,
+ },
+ .probe = da9052_battery_probe,
+ .remove = da9052_battery_remove,
+};
+
+static int da9052_battery_init(void)
+{
+ return platform_driver_register(&da9052_battery_driver);
+}
+module_init(da9052_battery_init);
+
+static void da9052_battery_exit(void)
+{
+ platform_driver_unregister(&da9052_battery_driver);
+}
+module_exit(da9052_battery_exit);
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen(a)diasemi.com>");
+MODULE_DESCRIPTION("Battery driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff -urpN linux-2.6.34/drivers/power/Kconfig
linux-2.6.34_test/drivers/power/Kconfig
--- linux-2.6.34/drivers/power/Kconfig 2010-05-17 02:17:36.000000000 +0500
+++ linux-2.6.34_test/drivers/power/Kconfig 2010-07-13 17:49:07.000000000 +0500
@@ -131,4 +131,10 @@ config CHARGER_PCF50633
help
Say Y to include support for NXP PCF50633 Main Battery Charger.

+config BATTERY_DA9052
+ tristate "DA9052 battery driver"
+ depends on PMIC_DA9052
+ help
+ Say Y here to enable support for batteries charger integrated into
+ DA9052 PMIC.
endif # POWER_SUPPLY
diff -urpN linux-2.6.34/drivers/power/Makefile
linux-2.6.34_test/drivers/power/Makefile
--- linux-2.6.34/drivers/power/Makefile 2010-05-17 02:17:36.000000000 +0500
+++ linux-2.6.34_test/drivers/power/Makefile 2010-07-13 17:49:13.000000000 +0500
@@ -32,3 +32,4 @@ obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
+obj-$(CONFIG_BATTERY_DA9052) += da9052_battery.o
diff -urpN linux-2.6.34/include/linux/mfd/da9052/bat.h
linux-2.6.34_test/include/linux/mfd/da9052/bat.h
--- linux-2.6.34/include/linux/mfd/da9052/bat.h 1970-01-01
05:00:00.000000000 +0500
+++ linux-2.6.34_test/include/linux/mfd/da9052/bat.h 2010-07-13
17:50:04.000000000 +0500
@@ -0,0 +1,335 @@
+/*
+ * 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.
+ *
+ * bat.h: BAT driver header file
+*/
+
+#ifndef _BAT_H
+#define _BAT_H
+
+#define DA9052_CHG_MEASUREMENT_FAIL -154
+#define DA9052_INVALID_VDDOUT_MON_VALUE -155
+#define DA9052_INVALID_ICHG_THRESHOLD_VALUE -156
+#define DA9052_INVALID_BAT_TEMP_HIGH -157
+#define DA9052_INVALID_BAT_TEMP_LOW -158
+#define DA9052_INVALID_BAT_TEMP_HIGHN -159
+#define DA9052_INVALID_PRECHARGE_CURRENT_VALUE -160
+#define DA9052_INVALID_CHARGING_TIME -161
+#define DA9052_INVALID_CHG_BAT_VOLTAGE -162
+#define DA9052_INVALID_CHG_VOLTAGE_DROP -163
+#define DA9052_INVALID_CHG_VOLTAGE_THRESHOLD -164
+#define DA9052_INVALID_CHG_CURRENT_END_VALUE -165
+#define DA9052_INVALID_DCIN_CURRENT_LIMIT_VALUE -166
+#define DA9052_INVALID_VBUS_CURRENT_LIMIT_VALUE -167
+#define DA9052_INVALID_USB_CHARGER_CURRENT_LIMIT_VALUE -176
+#define DA9052_CHG_MONITORING_FAIL -169
+#define CHG_HYSTERSIS_CHECK_FAILED -177
+#define DA9052_IRQ_UNREGISTER_FAILED -178
+#define DA9052_IRQ_REGISTER_FAILED -179
+#define DA9052_BAT_INVALID_EVENT -180
+#define DA9052_INVALID_VBAT_VALUE -181
+#define DA9052_BAT_NOT_CHARGING -183
+
+/* STATIC CONFIGURATION */
+#define SW_TEMP_CONTROL_EN 0
+#define MONITORING_INTERVAL 500
+#define SW_BAT_TEMP_THRESHOLD 60
+#define SW_JUNC_TEMP_THRESHOLD 120
+#define BAT_MANUFACTURER "Samsung"
+#define BAT_TYPE POWER_SUPPLY_TECHNOLOGY_LION
+#define LOOK_UP_TABLE_SIZE 68
+#define NO_OF_LOOKUP_TABLE 3
+#define HYSTERESIS_WINDOW_SIZE 1
+#define CURRENT_MONITORING_WINDOW 10
+#define BAT_WITH_NO_RESISTOR 62
+#define BAT_CAPACITY_LIMIT_LOW 4
+#define BAT_CAPACITY_FULL 100
+#define BAT_CAPACITY_LIMIT_HIGH 70
+#define CHG_HYSTERESIS_CONST 89
+#define HYSTERESIS_READING_INTERVAL 1000
+#define HYSTERESIS_NO_OF_READING 10
+#define FILTER_SIZE 4
+#define NUMBER_OF_STORE_CURENT_READING 4
+#define BAT_VOLT_CUTOFF 2800
+#define VBAT_FIRST_VALID_DETECT_ITERATION 3
+#define DA9052_ILLEGAL_BATTERY_DETECT 1
+#define DA9052_BAT_FILTER_HYS 0
+
+#define VDDOUT_MON_LOWER 2500
+#define VDDOUT_MON_UPPER 4500
+#define ICHG_THRESHOLD_UPPER 1000
+#define TBAT_MAX_THRESHOLD_LIMIT 255
+#define TBAT_MIN_THRESHOLD_LIMIT 255
+#define TBAT_HIGHNS_THRESHOLD_LIMIT 255
+#define ISET_LOW 70
+#define ISET_HIGH 1300
+#define MAX_BAT_CHARGING_TIME 450
+#define BAT_TARGET_VOLTAGE_LOWER_LIMIT 4100
+#define BAT_TARGET_VOLTAGE_UPPER_LIMIT 4400
+#define CHARGER_VOLTAGE_DROP_LOWER_LIMIT 100
+#define CHARGER_VOLTAGE_DROP_UPPER_LIMIT 400
+#define CHARGING_TIME_INTERVAL 30
+#define BAT_VOLTAGE_THRESHOLD_LOWER_LIMIT 3700
+#define BAT_VOLTAGE_THRESHOLD_UPPER_LIMIT 4400
+
+static const u16 temperature_lookup_ref[NO_OF_LOOKUP_TABLE] = {10, 25, 40};
+static u32 const vbat_vs_capacity_look_up[NO_OF_LOOKUP_TABLE]
+ [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 charge_status_enum {
+ DA9052_NONE = 1,
+ DA9052_CHARGING,
+ DA9052_DISCHARGING_WITH_CHARGER,
+ DA9052_DISCHARGING_WITHOUT_CHARGER,
+ DA9052_PRECHARGING,
+ DA9052_LINEARCHARGING,
+ DA9052_CHARGEEND
+};
+
+enum charger_type_enum {
+ DA9052_NOCHARGER = 1,
+ DA9052_USB_HUB,
+ DA9052_USB_CHARGER,
+ DA9052_WALL_CHARGER
+};
+
+enum precharge_enum {
+ PRE_CHARGE_0MA = 0,
+ PRE_CHARGE_20MA = 20,
+ PRE_CHARGE_40MA = 40,
+ PRE_CHARGE_60MA = 60
+};
+
+struct da9052_bat_event_registration {
+ u8 da9052_event_vddlow:1;
+ u8 da9052_event_tbat:1;
+};
+
+struct da9052_bat_threshold {
+ u16 vddout_mon;
+ u16 ichg_thr;
+ u16 tbat_thr_min;
+ u16 tbat_thr_max;
+ u16 tbat_thr_highns;
+ u16 tbat_thr_limit;
+ u16 tjunc_thr_limit;
+ u16 ichg_av_thr_min;
+ u16 ichg_av_thr_max;
+};
+
+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_bat_status {
+ u8 cal_capacity;
+ u8 charging_mode;
+ u8 charger_type;
+ u8 health;
+ u8 status;
+ u8 illegalbattery;
+};
+
+struct monitoring_state {
+ u16 vddout_value;
+ u16 bat_temp_value;
+ u16 current_value;
+ u16 junc_temp_value;
+ u8 bat_level;
+ u8 vddout_status:1;
+ u8 bat_temp_status:1;
+ u8 junc_temp_status:1;
+ u8 current_status:1;
+ u8 bat_level_status:1;
+};
+
+struct da9052_bat_device {
+ char manufacture[32];
+ u16 chg_current_raw[NUMBER_OF_STORE_CURENT_READING];
+ u16 chg_current;
+ u16 chg_junc_temp;
+ u16 bat_voltage;
+ u16 backup_bat_voltage;
+ u16 bat_temp;
+ u16 vddout;
+};
+
+struct da9052_charger_device {
+ struct da9052_bat_threshold threshold;
+ struct da9052 *da9052;
+ struct delayed_work work;
+ struct power_supply psy;
+ struct da9052_eh_nb vddlow_eh_data;
+ struct da9052_eh_nb tbat_eh_data;
+ u16 monitoring_interval;
+ u8 hys_flag;
+ u16 charger_voltage_drop;
+ u16 bat_target_voltage;
+ u16 voltage_threshold;
+ u16 dcin_current;
+ u16 vbus_current;
+ u16 usb_charger_current;;
+ u16 chg_end_current;
+ u16 precharging_current;
+ u16 charging_time;
+ u8 timer_mode:1;
+ u8 charger_buck_lp:1;
+ u8 usb_charger_det:1;
+ u8 ichg_low_cntr:1;
+ u8 sw_temp_cntr:1;
+ u8 auto_temp_cntr:1;
+};
+
+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;
+ }
+
+#endif
+
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/