* [PATCHv2 4/11] Power:Battery module of DA9052 PMIC device driver (RESEND)
@ 2010-07-14 9:44 Dajun Chen
2010-07-14 10:24 ` Mark Brown
0 siblings, 1 reply; 2+ messages in thread
From: Dajun Chen @ 2010-07-14 9:44 UTC (permalink / raw)
To: cbou, dwmw2; +Cc: linux-kernel
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@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, ®_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, ®_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, ®_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, ®_data);
+ if (ret)
+ return ret;
+
+ if (reg_data & DA9052_ADCCONT_ADCMODE)
+ msleep(1);
+ else
+ msleep(10);
+
+ ret = da9052_read(da9052, DA9052_VDDRES_REG, ®_data);
+ if (ret)
+ return ret;
+
+ *data = (u16)reg_data;
+
+ ret = da9052_read(da9052, DA9052_ADCCONT_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, ®_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, ®_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, ®_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, ®value);
+ if (ret)
+ return;
+
+ ret = da9052_read(chg_device->da9052, DA9052_STATUSB_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,
+ ¤t_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,
+ ¤t_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, ¤t_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, ®_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, ®_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, ®_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, ®_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@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
+
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCHv2 4/11] Power:Battery module of DA9052 PMIC device driver (RESEND)
2010-07-14 9:44 [PATCHv2 4/11] Power:Battery module of DA9052 PMIC device driver (RESEND) Dajun Chen
@ 2010-07-14 10:24 ` Mark Brown
0 siblings, 0 replies; 2+ messages in thread
From: Mark Brown @ 2010-07-14 10:24 UTC (permalink / raw)
To: Dajun Chen; +Cc: cbou, dwmw2, linux-kernel
On Wed, Jul 14, 2010 at 10:44:10AM +0100, Dajun Chen wrote:
> +#ifndef _BAT_H
> +#define _BAT_H
This is likely to run into namespace conflicts with other stuff -
including the chip name should fix that.
> +
> +#define DA9052_CHG_MEASUREMENT_FAIL -154
As has been pointed out with all your other patches please you standard
Linux error codes rather than making up your own. This applies to
pretty much all kernel code.
> +/* STATIC CONFIGURATION */
> +#define SW_TEMP_CONTROL_EN 0
> +#define MONITORING_INTERVAL 500
> +#define SW_BAT_TEMP_THRESHOLD 60
> +#define SW_JUNC_TEMP_THRESHOLD 120
This all needs to be platform data rather than #defines in the header -
the kernel contains code for a very wide range of boards so editing the
header file per-board isn't going to work and the valeus you pick may be
actively harmful if they fail to correspond to the actual battery
properties.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2010-07-14 10:24 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-07-14 9:44 [PATCHv2 4/11] Power:Battery module of DA9052 PMIC device driver (RESEND) Dajun Chen
2010-07-14 10:24 ` Mark Brown
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®