blob: 9e72408c37ea859fe531e87c9a039edfe6aaa21e [file] [log] [blame]
/*
* Driver for the TI zx234502 battery charger.
*
* Author: Mark A. Greer <mgreer@animalcreek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/power_supply.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/kthread.h>
//#include <linux/mutex.h>
#include <linux/semaphore.h>
#include <linux/power/zx234502_charger.h>
#include <linux/mfd/zx234290.h>
#include <mach/gpio.h>
#include <mach/pcu.h>
#include <mach/zx29_usb.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <asm/uaccess.h>
/*
* The FAULT register is latched by the zx234502 (except for NTC_FAULT)
* so the first read after a fault returns the latched value and subsequent
* reads return the current value. In order to return the fault status
* to the user, have the interrupt handler save the reg's value and retrieve
* it in the appropriate health/status routine. Each routine has its own
* flag indicating whether it should use the value stored by the last run
* of the interrupt handler or do an actual reg read. That way each routine
* can report back whatever fault may have occured.
*/
struct zx234502_dev_info {
struct i2c_client *client;
struct device *dev;
struct power_supply charger;
struct power_supply battery;
struct power_supply boost;
char model_name[I2C_NAME_SIZE];
kernel_ulong_t model;
unsigned int gpio_int;
unsigned int irq;
struct mutex bs_reg_lock;
bool first_time; /*let the first reset do not ask mmi*/
bool charger_health_valid;
bool battery_health_valid;
bool battery_status_valid;
u8 ciis_curr_reg;
u8 cbis_curr_reg;
u8 pis_curr_reg;
u8 cfis_curr_reg;
u8 ciis_pre_reg;
u8 cbis_pre_reg;
u8 pis_pre_reg;
u8 cfis_pre_reg;
u8 watchdog;
u8 boost_online_flag;
struct delayed_work boostWorkStruct ;
struct workqueue_struct *boostQueue;
struct semaphore chgirq_sem;
struct task_struct *chg_irq_thread;
u16 boostcount;
struct zx234502_platform_data *pdata;
//unsigned int chg_type;
};
//struct zx234502_platform_data *g_platform_data = NULL;
struct zx234502_dev_info *g_bdi = NULL;
/* REG01[2:0] (ISET_DCIN) in mAh */
static const int zx234502_in_ilimit_values[] = {
150, 450, 850, 1000, 1500, 2000, 2500, 3000
};
/* REG01[6:3] (VINDPM) in mVh */
static const int zx234502_vindpm_values[] = {
3880, 3960, 4040, 4120,
4200,4280, 4360, 4440,
4520,4600, 4680, 4760,
4840,4920, 5000, 5080,
};
/*
* The tables below provide a 2-way mapping for the value that goes in
* the register field and the real-world value that it represents.
* The index of the array is the value that goes in the register; the
* number at that index in the array is the real-world value that it
* represents.
*/
/* REG02[7:2] (ISETA) in mAh */
static const int zx234502_isc_ichg_values[] = {
512, 576, 640, 704, 768, 832, 896, 960,
1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472,
1536, 1600, 1664, 1728, 1792, 1856, 1920, 1984,
2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496,
2560, 2624, 2688, 2752, 2816, 2880, 2944, 3008,
3072, 3136, 3200, 3264, 3328, 3392, 3456, 3520,
3584, 3648, 3712, 3776, 3840, 3904, 3968, 4032,
4096, 4160, 4224, 4288, 4352, 4416, 4480, 4544
};
/* REG04[3:1] (VSETA) in mV */
static const int zx234502_cvc_vseta_values[] = {
4100, 4150, 4200, 4250, 4300, 4350, 4400, 4450,
};
/* REG02[7:5] (VSYS_MIN) in mV */
static const int zx234502_vsc_sysmin_values[] = {
3100, 3200, 3300, 3400, 3500, 3600, 3700,3800
};
static int stopchg_flag = 0;
/*
* Return the index in 'tbl' of greatest value that is less than or equal to
* 'val'. The index range returned is 0 to 'tbl_size' - 1. Assumes that
* the values in 'tbl' are sorted from smallest to largest and 'tbl_size'
* is less than 2^8.
*/
static u8 zx234502_find_idx(const int tbl[], int tbl_size, int v)
{
int i;
for (i = 1; i < tbl_size; i++)
if (v < tbl[i])
break;
return i - 1;
}
/* Basic driver I/O routines */
static int zx234502_read(struct zx234502_dev_info *bdi, u8 reg, u8 *data)
{
int ret;
ret = i2c_smbus_read_byte_data(bdi->client, reg);
if (ret < 0)
return ret;
*data = ret;
return 0;
}
static int zx234502_write(struct zx234502_dev_info *bdi, u8 reg, u8 data)
{
return i2c_smbus_write_byte_data(bdi->client, reg, data);
}
static int zx234502_read_mask(struct zx234502_dev_info *bdi, u8 reg,
u8 mask, u8 shift, u8 *data)
{
u8 v;
int ret;
ret = zx234502_read(bdi, reg, &v);
if (ret < 0)
return ret;
v &= mask;
v >>= shift;
*data = v;
return 0;
}
static int zx234502_write_mask(struct zx234502_dev_info *bdi, u8 reg,
u8 mask, u8 shift, u8 data)
{
u8 v;
int ret;
ret = zx234502_read(bdi, reg, &v);
if (ret < 0)
return ret;
v &= ~mask;
v |= ((data << shift) & mask);
return zx234502_write(bdi, reg, v);
}
static int zx234502_get_field_val(struct zx234502_dev_info *bdi,
u8 reg, u8 mask, u8 shift,
const int tbl[], int tbl_size,
int *val)
{
u8 v;
int ret;
ret = zx234502_read_mask(bdi, reg, mask, shift, &v);
if (ret < 0)
return ret;
v = (v >= tbl_size) ? (tbl_size - 1) : v;
*val = tbl[v];
return 0;
}
static int zx234502_set_field_val(struct zx234502_dev_info *bdi,
u8 reg, u8 mask, u8 shift,
const int tbl[], int tbl_size,
int val)
{
u8 idx;
idx = zx234502_find_idx(tbl, tbl_size, val);
return zx234502_write_mask(bdi, reg, mask, shift, idx);
}
//#ifdef CONFIG_SYSFS
#if 0
/*
* There are a numerous options that are configurable on the zx234502
* that go well beyond what the power_supply properties provide access to.
* Provide sysfs access to them so they can be examined and possibly modified
* on the fly. They will be provided for the charger power_supply object only
* and will be prefixed by 'f_' to make them easier to recognize.
*/
struct zx234502_sysfs_field_info {
struct device_attribute attr;
u8 reg;
u8 mask;
u8 shift;
};
#define ZX234502_SYSFS_FIELD(_name, r, f, m, store) \
{ \
.attr = __ATTR(f_##_name, m, zx234502_sysfs_show, store), \
.reg = ZX234502_REG_##r, \
.mask = ZX234502_REG_##r##_##f##_MASK, \
.shift = ZX234502_REG_##r##_##f##_SHIFT, \
}
#define ZX234502_SYSFS_FIELD_RW(_name, r, f) \
ZX234502_SYSFS_FIELD(_name, r, f, S_IWUSR | S_IRUGO, \
zx234502_sysfs_store)
#define ZX234502_SYSFS_FIELD_RO(_name, r, f) \
ZX234502_SYSFS_FIELD(_name, r, f, S_IRUGO, NULL)
static ssize_t zx234502_sysfs_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t zx234502_sysfs_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
/* On i386 ptrace-abi.h defines SS that breaks the macro calls below. */
//#undef SS
static struct zx234502_sysfs_field_info zx234502_sysfs_field_tbl[] = {
/* sysfs name reg field in reg */
ZX234502_SYSFS_FIELD_RW(chg_reset, MS, CHGRST),
ZX234502_SYSFS_FIELD_RW(pmuon, MS, PMUON),
ZX234502_SYSFS_FIELD_RW(en_ship, MS, ENSHIP),
ZX234502_SYSFS_FIELD_RW(en_otg, MS, ENOTG),
ZX234502_SYSFS_FIELD_RW(chg_config, MS, ENCHG),
ZX234502_SYSFS_FIELD_RW(en_powerbank, MS, PWRBNK),
ZX234502_SYSFS_FIELD_RW(insus, MS, INSUS),
ZX234502_SYSFS_FIELD_RW(in_limit, ISC, ISET_DCIN),
ZX234502_SYSFS_FIELD_RW(vsys_min, VSC, VSYS_MIN),
ZX234502_SYSFS_FIELD_RW(ichg, CCC, ISETA),
ZX234502_SYSFS_FIELD_RW(force_ichg_50pct, CCC, ICHG_50PCT),
ZX234502_SYSFS_FIELD_RW(iprechg, CVC, IPRECHG),
ZX234502_SYSFS_FIELD_RW(vbat_full, CVC, VBAT),
ZX234502_SYSFS_FIELD_RW(vpre_to_fast, CVC, VBATFC),
ZX234502_SYSFS_FIELD_RW(en_timer, CTC, EN_TIMER),
ZX234502_SYSFS_FIELD_RW(en_2timer, CTC, EN_2XTIMER),
ZX234502_SYSFS_FIELD_RW(stopchg_flag, OTGC, OTGV),
ZX234502_SYSFS_FIELD_RW(iotg_limit, OTGC, OTGI_LIM),
ZX234502_SYSFS_FIELD_RW(ents_otg, OTGC, ENTS_OTG),
ZX234502_SYSFS_FIELD_RO(en_therm, THR, EN_THERM),
ZX234502_SYSFS_FIELD_RW(therm_threshold, THR, THERM_THLD),
ZX234502_SYSFS_FIELD_RW(en_2det, THR, EN_2DET),
ZX234502_SYSFS_FIELD_RW(en_1det, THR, EN_1DET),
ZX234502_SYSFS_FIELD_RW(en_jeita, THR, EN_JEITA),
ZX234502_SYSFS_FIELD_RW(en_ilimitdj, THR, EN_ILIMITDJ),
ZX234502_SYSFS_FIELD_RW(tscold, TS, COLD),
ZX234502_SYSFS_FIELD_RW(tscool, TS, COOL),
ZX234502_SYSFS_FIELD_RW(tswarm, TS, WARM),
ZX234502_SYSFS_FIELD_RW(tshot, TS, HOT),
ZX234502_SYSFS_FIELD_RW(ntcdet, PTS,NTCDET),
ZX234502_SYSFS_FIELD_RW(int_clear, CIIS, INT),
ZX234502_SYSFS_FIELD_RO(ciis_reg, CIIS, ALL),
ZX234502_SYSFS_FIELD_RW(mask_chgrun, CIIM, MASK_CHGRUN),
ZX234502_SYSFS_FIELD_RW(mask_inlimit, CIIM, MASK_INLIMIT),
ZX234502_SYSFS_FIELD_RW(mask_thr, CIIM, MASK_THR),
ZX234502_SYSFS_FIELD_RW(mask_ts, CIIM, MASK_TS),
ZX234502_SYSFS_FIELD_RW(mask_dcdet, CIIM, MASK_DCDET),
ZX234502_SYSFS_FIELD_RO(cbis_reg, CBIS, ALL),
ZX234502_SYSFS_FIELD_RW(mask_pg, CBIM, MASK_DCIN_PG),
ZX234502_SYSFS_FIELD_RW(mask_batlow, CBIM, MASK_BATLOW),
ZX234502_SYSFS_FIELD_RW(mask_nobat, CBIM, MASK_NOBAT),
ZX234502_SYSFS_FIELD_RW(mask_eoc, CBIM, MASK_EOC),
ZX234502_SYSFS_FIELD_RW(mask_timer, CBIM, MASK_SAFE_TIMER),
ZX234502_SYSFS_FIELD_RW(mask_otg_fault, CBIM, MASK_OTG_FAULT),
ZX234502_SYSFS_FIELD_RO(pis_reg, PIS, ALL),
ZX234502_SYSFS_FIELD_RW(mask_pwron_it, PIM, MASK_POWERON_IT),
ZX234502_SYSFS_FIELD_RW(mask_pwron_lp, PIM, MASK_POWERON_LP),
ZX234502_SYSFS_FIELD_RO(cfis_reg, CFIS, ALL),
ZX234502_SYSFS_FIELD_RO(version_info, VER, INFO),
};
static struct attribute *
zx234502_sysfs_attrs[ARRAY_SIZE(zx234502_sysfs_field_tbl) + 1];
static const struct attribute_group zx234502_sysfs_attr_group = {
.attrs = zx234502_sysfs_attrs,
};
static void zx234502_sysfs_init_attrs(void)
{
int i, limit = ARRAY_SIZE(zx234502_sysfs_field_tbl);
for (i = 0; i < limit; i++)
zx234502_sysfs_attrs[i] = &zx234502_sysfs_field_tbl[i].attr.attr;
zx234502_sysfs_attrs[limit] = NULL; /* Has additional entry for this */
}
static struct zx234502_sysfs_field_info *zx234502_sysfs_field_lookup(
const char *name)
{
int i, limit = ARRAY_SIZE(zx234502_sysfs_field_tbl);
for (i = 0; i < limit; i++)
if (!strcmp(name, zx234502_sysfs_field_tbl[i].attr.attr.name))
break;
if (i >= limit)
return NULL;
return &zx234502_sysfs_field_tbl[i];
}
static ssize_t zx234502_sysfs_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct zx234502_dev_info *bdi =
container_of(psy, struct zx234502_dev_info, charger);
struct zx234502_sysfs_field_info *info;
int ret;
u8 v;
info = zx234502_sysfs_field_lookup(attr->attr.name);
if (!info)
return -EINVAL;
ret = zx234502_read_mask(bdi, info->reg, info->mask, info->shift, &v);
if (ret)
return ret;
return scnprintf(buf, PAGE_SIZE, "%hhx\n", v);
}
static ssize_t zx234502_sysfs_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct zx234502_dev_info *bdi =
container_of(psy, struct zx234502_dev_info, charger);
struct zx234502_sysfs_field_info *info;
int ret;
u8 v;
info = zx234502_sysfs_field_lookup(attr->attr.name);
if (!info)
return -EINVAL;
ret = kstrtou8(buf, 0, &v);
if (ret < 0)
return ret;
ret = zx234502_write_mask(bdi, info->reg, info->mask, info->shift, v);
if (ret)
return ret;
return count;
}
static int zx234502_sysfs_create_group(struct zx234502_dev_info *bdi)
{
zx234502_sysfs_init_attrs();
return sysfs_create_group(&bdi->charger.dev->kobj,
&zx234502_sysfs_attr_group);
}
static void zx234502_sysfs_remove_group(struct zx234502_dev_info *bdi)
{
sysfs_remove_group(&bdi->charger.dev->kobj, &zx234502_sysfs_attr_group);
}
#else
static int zx234502_sysfs_create_group(struct zx234502_dev_info *bdi)
{
return 0;
}
static inline void zx234502_sysfs_remove_group(struct zx234502_dev_info *bdi) {}
#endif
#if 0
/*set the Vsys min*/
static int zx234502_set_vsys_min(struct zx234502_dev_info *bdi,const union power_supply_propval *val)
{
return zx234502_set_field_val(bdi, ZX234502_REG_VSC,
ZX234502_REG_VSC_VSYS_MIN_MASK, ZX234502_REG_VSC_VSYS_MIN_SHIFT,
zx234502_cvc_vseta_values,
ARRAY_SIZE(zx234502_cvc_vseta_values), val->intval);
//return 0
}
#endif
static int zx234502_register_reset(struct zx234502_dev_info *bdi)
{
int ret, limit = 100;
u8 v;
/* Reset the registers */
ret = zx234502_write_mask(bdi, ZX234502_REG_MS,ZX234502_REG_MS_CHGRST_MASK,ZX234502_REG_MS_CHGRST_SHIFT,0x1);
if (ret < 0)
return ret;
/* Reset bit will be cleared by hardware so poll until it is */
do
{
ret = zx234502_read_mask(bdi, ZX234502_REG_MS,ZX234502_REG_MS_CHGRST_MASK,ZX234502_REG_MS_CHGRST_SHIFT,&v);
if (ret < 0)
return ret;
if (!v)
break;
udelay(10);
} while (--limit);
if (!limit)
return -EIO;
return 0;
}
/* Charger power supply property routines */
static int zx234502_charger_get_charge_type(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
val->intval = bdi->charger.type;
return 0;
}
static int zx234502_charger_get_status(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
u8 cbis_reg=0,ms_reg=0;
int status=POWER_SUPPLY_STATUS_UNKNOWN;
int ret=0;
mutex_lock(&bdi->bs_reg_lock);
if (bdi->battery_status_valid) {
cbis_reg = bdi->cbis_curr_reg;
bdi->battery_status_valid = false;
mutex_unlock(&bdi->bs_reg_lock);
} else {
mutex_unlock(&bdi->bs_reg_lock);
ret = zx234502_read(bdi, ZX234502_REG_CBIS, &cbis_reg);
if (ret < 0)
return ret;
}
ret = zx234502_read(bdi, ZX234502_REG_MS, &ms_reg);
if (ret < 0)
return ret;
/*
* The battery must be discharging when any of these are true:
* - there is no good power source;
* - there is a charge fault.
* Could also be discharging when in "supplement mode" but
* there is no way to tell when its in that mode.
*/
if (!(cbis_reg & ZX234502_REG_CBIS_DCIN_PG_MASK )||(cbis_reg & ZX234502_REG_CBIS_NOBAT_MASK )) {
status = POWER_SUPPLY_STATUS_DISCHARGING;
}
else if (ms_reg & ZX234502_REG_MS_ENCHG_MASK){
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
}
// disable or full
else if(ms_reg & ZX234502_REG_MS_INSUS_MASK)
{
if (((cbis_reg & ZX234502_REG_CBIS_CHGSTAT_MASK) >> ZX234502_REG_CBIS_CHGSTAT_SHIFT)== 3)
{
status = POWER_SUPPLY_STATUS_FULL;
}
else
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
}
else {
cbis_reg &= ZX234502_REG_CBIS_CHGSTAT_MASK;
cbis_reg >>= ZX234502_REG_CBIS_CHGSTAT_SHIFT;
switch (cbis_reg) {
case 0x0: /* Charging */
case 0x2: /* ReCharging frome THERM */
status = POWER_SUPPLY_STATUS_CHARGING;
break;
case 0x3: /* Charge Termination Done */
status = POWER_SUPPLY_STATUS_FULL;
break;
default:
ret = -EIO;
}
}
if (!ret)
val->intval = status;
return ret;
}
static int zx234502_charger_get_health(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
u8 ciis_reg=0, cbis_reg=0, cfis_reg=0;
int health=0, ret=0;
mutex_lock(&bdi->bs_reg_lock);
if (bdi->charger_health_valid) {
bdi->charger_health_valid = false;
cbis_reg = bdi->cbis_curr_reg;
ciis_reg = bdi->ciis_curr_reg;
cfis_reg = bdi->cfis_curr_reg;
mutex_unlock(&bdi->bs_reg_lock);
} else {
mutex_unlock(&bdi->bs_reg_lock);
ret = zx234502_read(bdi, ZX234502_REG_CIIS, &ciis_reg);
if (ret < 0)
return ret;
ret = zx234502_read(bdi, ZX234502_REG_CBIS, &cbis_reg);
if (ret < 0)
return ret;
ret = zx234502_read(bdi, ZX234502_REG_CFIS, &cfis_reg);
if (ret < 0)
return ret;
printk(KERN_INFO "zx234502_charger_get_health REG_0xa=0x%x,REG_0xc=0x%x,REG_0x10=0x%x.\n",ciis_reg,cbis_reg,cfis_reg);
}
if (cbis_reg & ZX234502_REG_CBIS_OTG_FAULT_MASK) {
/*
* This could be over-current or over-voltage but there's
* no way to tell which. Return 'OVERVOLTAGE' since there
* isn't an 'OVERCURRENT' value defined that we can return
* even if it was over-current.
*/
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
}
else if(cbis_reg & ZX234502_REG_CBIS_SAFE_TIMER_MASK) {
health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
}
else if(cbis_reg & ZX234502_REG_CBIS_DCIN_PG_MASK) {
health = POWER_SUPPLY_HEALTH_GOOD;
}
else if(ciis_reg & (ZX234502_REG_CIIS_INLIMIT_MASK|ZX234502_REG_CIIS_THERM_MASK)) {
health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
}
else if(cfis_reg & ZX234502_REG_CFIS_THSD_MASK){
health = POWER_SUPPLY_HEALTH_OVERHEAT; /*the chip hot error*/
}
else{
health = POWER_SUPPLY_HEALTH_UNKNOWN;
}
val->intval = health;
return 0;
}
int zx234502_charger_get_online(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
u8 v;
int ret;
ret = zx234502_read_mask(bdi, ZX234502_REG_CBIS,ZX234502_REG_CBIS_DCIN_PG_MASK,
ZX234502_REG_CBIS_DCIN_PG_SHIFT, &v);
if (ret < 0)
return ret;
val->intval = v;
return 0;
}EXPORT_SYMBOL (zx234502_charger_get_online);
#if 0
static int zx234502_charger_get_current(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
int curr, ret;
ret = zx234502_get_field_val(bdi, ZX234502_REG_ISC,
ZX234502_REG_ISC_ISET_DCIN_MASK, ZX234502_REG_ISC_ISET_DCIN_SHIFT,
zx234502_in_ilimit_values,
ARRAY_SIZE(zx234502_in_ilimit_values), &curr);
if (ret < 0)
return ret;
val->intval = curr;
return 0;
}
static int zx234502_charger_get_current_max(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
int idx = ARRAY_SIZE(zx234502_in_ilimit_values) - 1;
val->intval = zx234502_in_ilimit_values[idx];
return 0;
}
static int zx234502_charger_get_voltage(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
#if 0
int voltage, ret;
ret = zx234502_get_field_val(bdi, ZX234502_REG_CVC,
ZX234502_REG_CVC_VBAT_MASK, ZX234502_REG_CVC_VBAT_SHIFT,
zx234502_cvc_vseta_values,
ARRAY_SIZE(zx234502_cvc_vseta_values), &voltage);
if (ret < 0)
return ret;
val->intval = voltage;
#endif
return 0;
}
#endif
static int zx234502_charger_get_charger_enabled(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
u8 charger_enabled;
int ret;
ret = zx234502_read_mask(bdi, ZX234502_REG_MS,
ZX234502_REG_MS_INSUS_MASK,
ZX234502_REG_MS_INSUS_SHIFT, &charger_enabled);
if (ret < 0)
return ret;
val->intval = !charger_enabled;
return 0;
}
static int zx234502_charger_get_voltage_max(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
int voltage, ret;
ret = zx234502_get_field_val(bdi, ZX234502_REG_CVC,
ZX234502_REG_CVC_VBAT_MASK, ZX234502_REG_CVC_VBAT_SHIFT,
zx234502_cvc_vseta_values,
ARRAY_SIZE(zx234502_cvc_vseta_values), &voltage);
if (ret < 0)
return ret;
val->intval = voltage;
return 0;
}
#if 0
static int zx234502_charger_set_current(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
return zx234502_set_field_val(bdi, ZX234502_REG_ISC,
ZX234502_REG_ISC_ISET_DCIN_MASK, ZX234502_REG_ISC_ISET_DCIN_MASK,
zx234502_in_ilimit_values,
ARRAY_SIZE(zx234502_in_ilimit_values), val->intval);
}
#endif
static int zx234502_charger_set_voltage(struct zx234502_dev_info *bdi,const union power_supply_propval *val)
{
return zx234502_set_field_val(bdi, ZX234502_REG_CVC,
ZX234502_REG_CVC_VBAT_MASK, ZX234502_REG_CVC_VBAT_SHIFT,
zx234502_cvc_vseta_values,
ARRAY_SIZE(zx234502_cvc_vseta_values), val->intval);
}
static int zx234502_charger_set_charger_config(struct zx234502_dev_info *bdi,const union power_supply_propval *val)
{
int ret;
union power_supply_propval enable_charge;
if (val->intval){
enable_charge.intval = 0 ;
stopchg_flag = 1;
ret = zx234502_write_mask(bdi, ZX234502_REG_OTGC,
ZX234502_REG_OTGC_OTGV_MASK,
ZX234502_REG_OTGC_OTGV_SHIFT,1);
printk("mmi start chg\n");
}
else{
stopchg_flag = 2;
enable_charge.intval = 1 ;
ret = zx234502_write_mask(bdi, ZX234502_REG_OTGC,
ZX234502_REG_OTGC_OTGV_MASK,
ZX234502_REG_OTGC_OTGV_SHIFT,2);
printk("mmi stop chg\n");
}
ret = zx234502_write_mask(bdi, ZX234502_REG_MS,
ZX234502_REG_MS_INSUS_MASK,
ZX234502_REG_MS_INSUS_SHIFT, enable_charge.intval ); /*0:disable 1:enable*/
return ret;
}
static int zx234502_charger_get_property(struct power_supply *psy,enum power_supply_property psp, union power_supply_propval *val)
{
struct zx234502_dev_info *bdi = container_of(psy, struct zx234502_dev_info, charger);
int ret;
//dev_dbg(bdi->dev, "prop: %d\n", psp);
//pm_runtime_get_sync(bdi->dev);
switch (psp) {
case POWER_SUPPLY_PROP_PC1_AC2:
ret = zx234502_charger_get_charge_type(bdi, val);
break;
case POWER_SUPPLY_PROP_STATUS:
ret = zx234502_charger_get_status(bdi, val);
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = zx234502_charger_get_health(bdi, val);
break;
case POWER_SUPPLY_PROP_ONLINE:
ret = zx234502_charger_get_online(bdi, val);
break;
/*
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = zx234502_charger_get_current(bdi, val);
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
ret = zx234502_charger_get_current_max(bdi, val);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = zx234502_charger_get_voltage(bdi, val);
break;
*/
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
ret = zx234502_charger_get_voltage_max(bdi, val);
break;
case POWER_SUPPLY_PROP_CHARGE_ENABLED:
ret = zx234502_charger_get_charger_enabled(bdi, val);
break;
default:
ret = -ENODATA;
}
//pm_runtime_put_sync(bdi->dev);
return ret;
}
static int zx234502_charger_set_property(struct power_supply *psy,enum power_supply_property psp,
const union power_supply_propval *val)
{
struct zx234502_dev_info *bdi =
container_of(psy, struct zx234502_dev_info, charger);
int ret;
//dev_dbg(bdi->dev, "prop: %d\n", psp);
//pm_runtime_get_sync(bdi->dev);
switch (psp) {
#if 0
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = zx234502_charger_set_current(bdi, val);
break;
#endif
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
ret = zx234502_charger_set_voltage(bdi, val);
break;
case POWER_SUPPLY_PROP_CHARGE_ENABLED:
ret = zx234502_charger_set_charger_config(bdi, val);
power_supply_changed(&bdi->charger);
break;
default:
ret = -EINVAL;
}
//pm_runtime_put_sync(bdi->dev);
return ret;
}
static int zx234502_charger_property_is_writeable(struct power_supply *psy,enum power_supply_property psp)
{
int ret;
switch (psp)
{
//case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
case POWER_SUPPLY_PROP_CHARGE_ENABLED:
ret = 1;
break;
default:
ret = 0;
break;
}
return ret;
}
static enum power_supply_property zx234502_charger_properties[] = {
POWER_SUPPLY_PROP_PC1_AC2,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_ONLINE,
//POWER_SUPPLY_PROP_CURRENT_NOW,
//POWER_SUPPLY_PROP_CURRENT_MAX,
//POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CHARGE_ENABLED,
};
static char *zx234502_charger_supplied_to[] = {
"main-battery",
};
static void zx234502_charger_init(struct power_supply *charger)
{
charger->name = "charger";
charger->type = POWER_SUPPLY_PCAC_UNKNOWN;
charger->properties = zx234502_charger_properties;
charger->num_properties = ARRAY_SIZE(zx234502_charger_properties);
charger->supplied_to = zx234502_charger_supplied_to;
//charger->num_supplies = ARRAY_SIZE(zx234502_charger_supplied_to);
charger->get_property = zx234502_charger_get_property;
charger->set_property = zx234502_charger_set_property;
charger->property_is_writeable = zx234502_charger_property_is_writeable;
}
/* Battery power supply property routines */
static int zx234502_battery_get_health(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
u8 v;
int health, ret;
mutex_lock(&bdi->bs_reg_lock);
if(true!=bdi->pdata->ts_flag){
val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
mutex_unlock(&bdi->bs_reg_lock);
return 0;
}
if (bdi->battery_health_valid) {
v = bdi->ciis_curr_reg;
bdi->battery_health_valid = false;
} else {
ret = zx234502_read(bdi, ZX234502_REG_CIIS, &v);
if (ret < 0){
mutex_unlock(&bdi->bs_reg_lock);
return ret;
}
}
if (v & ZX234502_REG_CIIS_THERM_MASK) {
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
} else {
v &= ZX234502_REG_CIIS_TS_METER_MASK;
v >>= ZX234502_REG_CIIS_TS_METER_SHIFT;
switch (v) {
case ZX234502_REG_CISS_TS_NORMAL: /* Normal */
health = POWER_SUPPLY_HEALTH_GOOD;
break;
case ZX234502_REG_CISS_TS_WARM: /*warm*/
health = POWER_SUPPLY_HEALTH_WARM;
break;
case ZX234502_REG_CISS_TS_HOT: /* TS1 Hot */
health = POWER_SUPPLY_HEALTH_OVERHEAT;
break;
case ZX234502_REG_CISS_TS_COOL:/*Cool*/
health = POWER_SUPPLY_HEALTH_COOL;
break;
case ZX234502_REG_CISS_TS_COLD: /* TS1 Cold */
health = POWER_SUPPLY_HEALTH_COLD;
break;
default:
health = POWER_SUPPLY_HEALTH_UNKNOWN;
}
}
val->intval = health;
mutex_unlock(&bdi->bs_reg_lock);
return 0;
}
static int zx234502_battery_get_online(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
u8 batfet_disable;
int ret;
ret = zx234502_read_mask(bdi, ZX234502_REG_CBIS,
ZX234502_REG_CBIS_NOBAT_MASK,
ZX234502_REG_CBIS_NOBAT_SHIFT, &batfet_disable);
if (ret < 0)
return ret;
val->intval = !batfet_disable;
return 0;
}
#if 0
static int zx234502_battery_get_capacity(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
int ret;
uint volt;
int bat_levl = 0;
int i =0;
//union power_supply_propval * online;
struct zx234502_bat_calibration *calibration;
//zx234502_charger_get_online(bdi,online);
u8 v;
ret = zx234502_read_mask(bdi, ZX234502_REG_CBIS,ZX234502_REG_CBIS_DCIN_PG_MASK,
ZX234502_REG_CBIS_DCIN_PG_SHIFT, &v);
if(v)
calibration = bdi->pdata->charging;
else
calibration = bdi->pdata->discharging;
//#ifdef CONFIG_ZX234290_ADC
volt = get_battery_voltage();
//#endif
if (volt > calibration[0].voltage) {
bat_levl = calibration[0].level;
} else {
for (i = 0; calibration[i+1].voltage >= 0; i++) {
if (volt <= calibration[i].voltage &&
volt >= calibration[i+1].voltage) {
/* interval found - interpolate within range */
bat_levl = calibration[i].level -
((calibration[i].voltage - volt) *
(calibration[i].level -
calibration[i+1].level)) /
(calibration[i].voltage -
calibration[i+1].voltage);
break;
}
}
}
val->intval = bat_levl;
//return 0;
return ret;
}
#endif
static int zx234502_battery_set_online(struct zx234502_dev_info *bdi,const union power_supply_propval *val)
{
return zx234502_write_mask(bdi, ZX234502_REG_MS,
ZX234502_REG_MS_ENSHIP_MASK,
ZX234502_REG_MS_ENSHIP_SHIFT, !val->intval); /*1:enable 0:disable*/
}
static int zx234502_battery_get_property(struct power_supply *psy,enum power_supply_property psp, union power_supply_propval *val)
{
struct zx234502_dev_info *bdi =
container_of(psy, struct zx234502_dev_info, battery);
int ret;
//dev_dbg(bdi->dev, "prop: %d\n", psp);
//pm_runtime_get_sync(bdi->dev);
switch (psp) {
case POWER_SUPPLY_PROP_HEALTH:
ret = zx234502_battery_get_health(bdi, val);
break;
case POWER_SUPPLY_PROP_ONLINE:
ret = zx234502_battery_get_online(bdi, val);
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = get_adc2_voltage();
ret = 0;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
//#ifdef CONFIG_ZX234290_ADC
val->intval = get_adc1_voltage() - 15;
ret = 0;
break;
#if 0
case POWER_SUPPLY_PROP_CAPACITY:
ret = zx234502_battery_get_capacity(bdi, val);
break;
#endif
default:
ret = -ENODATA;
}
//pm_runtime_put_sync(bdi->dev);
return ret;
}
static int zx234502_battery_set_property(struct power_supply *psy,enum power_supply_property psp,
const union power_supply_propval *val)
{
struct zx234502_dev_info *bdi =
container_of(psy, struct zx234502_dev_info, battery);
int ret;
//dev_dbg(bdi->dev, "prop: %d\n", psp);
//pm_runtime_put_sync(bdi->dev);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
ret = zx234502_battery_set_online(bdi, val);
break;
default:
ret = -EINVAL;
}
//pm_runtime_put_sync(bdi->dev);
return ret;
}
static int zx234502_battery_property_is_writeable(struct power_supply *psy,enum power_supply_property psp)
{
int ret;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
ret = 1;
break;
default:
ret = 0;
}
return ret;
}
static enum power_supply_property zx234502_battery_properties[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
//POWER_SUPPLY_PROP_CAPACITY,
};
static void zx234502_battery_init(struct power_supply *battery)
{
battery->name = "battery";
battery->type = POWER_SUPPLY_PCAC_UNKNOWN;
battery->properties = zx234502_battery_properties;
battery->num_properties = ARRAY_SIZE(zx234502_battery_properties);
battery->get_property = zx234502_battery_get_property;
battery->set_property = zx234502_battery_set_property;
battery->property_is_writeable = zx234502_battery_property_is_writeable;
}
static int zx234502_boost_get_online(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
val->intval = bdi->boost_online_flag;
return 0;
}
static int zx234502_boost_get_current_now(struct zx234502_dev_info *bdi,union power_supply_propval *val)
{
//gpio_set1 is GPIO39
//gpio_set2 is GPIO40
//gpio_set3 is GPIO41
int gpio_set1,gpio_set2,gpio_set3;
gpio_set1 = gpio_get_value(bdi->pdata->boost_cur_gpio1);
gpio_set2 = gpio_get_value(bdi->pdata->boost_cur_gpio2);
gpio_set3 = gpio_get_value(bdi->pdata->boost_cur_gpio3);
if((GPIO_LOW==gpio_set1)&(GPIO_LOW==gpio_set2)&(GPIO_HIGH==gpio_set3)){
val->intval = 600;
}
else if((GPIO_LOW==gpio_set1)&(GPIO_HIGH==gpio_set2)&(GPIO_LOW==gpio_set3)){
val->intval = 1000;
}
else if((GPIO_LOW==gpio_set1)&(GPIO_HIGH==gpio_set2)&(GPIO_HIGH==gpio_set3)){
val->intval = 1750;
}
else if((GPIO_HIGH==gpio_set1)&(GPIO_LOW==gpio_set2)&(GPIO_LOW==gpio_set3)){
val->intval = 2000;
}
else if((GPIO_HIGH==gpio_set1)&(GPIO_LOW==gpio_set2)&(GPIO_HIGH==gpio_set3)){
val->intval = 2850;
}
else{
dev_dbg(bdi->dev, "The boost current set with err\n");
return -1;
}
return 0;
}
#if 0
static int zx234502_boost_get_current_max(struct zx234502_dev_info *bdi, union power_supply_propval *val)
{
val->intval = 2850;
return 0;
}
#endif
static int zx234502_boost_set_charge_enable(struct zx234502_dev_info *bdi,const union power_supply_propval *val)
{
zx234502_write_mask(bdi,ZX234502_REG_MS,
ZX234502_REG_MS_PWRBNK_MASK,ZX234502_REG_MS_PWRBNK_SHIFT,val->intval);
/*set the gpio34 open = HIGH*/
gpio_set_value(bdi->pdata->boost_loadswitch_gpio,val->intval);
//zx29_gpio_output_data(bdi->pdata->boost_loadswitch_gpio,GPIO_HIGH);
return 0;
}
static int zx234502_boost_set_current_now(struct zx234502_dev_info *bdi,const union power_supply_propval *val)
{
//gpio from 35/36/37 changed to 39/40/41
//gpio_set1 is GPIO39
//gpio_set2 is GPIO40
//gpio_set3 is GPIO41
int gpio_set1,gpio_set2,gpio_set3;
if(val->intval < 601){
/*0.6A GPIO35:LOW GPIO36:LOW GPIO37:HIGH 40k */
gpio_set1 = GPIO_LOW;
gpio_set2 = GPIO_LOW;
gpio_set3 = GPIO_HIGH;
}
else if( (600 < val->intval)&&(val->intval< 1001)){
/*1A GPIO35:LOW GPIO36:HIGH GPIO37:LOW 20k */
gpio_set1 = GPIO_LOW;
gpio_set2 = GPIO_HIGH;
gpio_set3 = GPIO_LOW;
}
else if( (1000 < val->intval)&&(val->intval < 1751)){
/*1.75A GPIO35:LOW GPIO36:HIGH GPIO37:HIGH 13.3k*/
gpio_set1 = GPIO_LOW;
gpio_set2 = GPIO_HIGH;
gpio_set3 = GPIO_HIGH;
}
else if(( 1750 < val->intval) &&( val->intval < 2001)){
/*2A GPIO35:HIGH GPIO36:LOW GPIO37:LOW 10k */
gpio_set1 = GPIO_HIGH;
gpio_set2 = GPIO_LOW;
gpio_set3 = GPIO_LOW;
}
else{
/*2.85A GPIO35:HIGH GPIO36:LOW GPIO37:HIGH 8k*/
gpio_set1 = GPIO_HIGH;
gpio_set2 = GPIO_LOW;
gpio_set3 = GPIO_HIGH;
}
gpio_set_value(bdi->pdata->boost_cur_gpio1, gpio_set1);
gpio_set_value(bdi->pdata->boost_cur_gpio2, gpio_set2);
gpio_set_value(bdi->pdata->boost_cur_gpio3, gpio_set3);
return 0;
}
static int zx234502_boost_get_property(struct power_supply *psy,enum power_supply_property psp, union power_supply_propval *val)
{
struct zx234502_dev_info *bdi =
container_of(psy, struct zx234502_dev_info, boost);
int ret;
//dev_dbg(bdi->dev, "prop: %d\n", psp);
//pm_runtime_get_sync(bdi->dev);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
ret = zx234502_boost_get_online(bdi, val);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = zx234502_boost_get_current_now(bdi, val);
break;
#if 0
case POWER_SUPPLY_PROP_CURRENT_MAX:
ret = zx234502_boost_get_current_max(bdi, val);
break;
#endif
default:
ret = -ENODATA;
}
//pm_runtime_put_sync(bdi->dev);
return ret;
}
static int zx234502_boost_set_property(struct power_supply *psy,enum power_supply_property psp,
const union power_supply_propval *val)
{
struct zx234502_dev_info *bdi =
container_of(psy, struct zx234502_dev_info, boost);
int ret;
//dev_dbg(bdi->dev, "prop: %d\n", psp);
//pm_runtime_put_sync(bdi->dev);
switch (psp) {
case POWER_SUPPLY_PROP_BOOST_ENABLE:
ret = zx234502_boost_set_charge_enable(bdi, val);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = zx234502_boost_set_current_now(bdi, val);
break;
default:
ret = -EINVAL;
}
//pm_runtime_put_sync(bdi->dev);
return ret;
}
static int zx234502_boost_property_is_writeable(struct power_supply *psy,enum power_supply_property psp)
{
int ret;
switch (psp) {
case POWER_SUPPLY_PROP_BOOST_ENABLE:
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = 1;
break;
default:
ret = 0;
}
return ret;
}
static enum power_supply_property zx234502_boost_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CURRENT_NOW,
//WER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_BOOST_ENABLE,
};
static void zx234502_boost_init(struct power_supply *boost)
{
#ifdef DBG_CHARGE
//printk(KERN_INFO "zx234502_boost_init =%x.\n",boost);
#endif
boost->name = "boost";
boost->type = POWER_SUPPLY_PCAC_UNKNOWN;
boost->properties = zx234502_boost_properties;
boost->num_properties = ARRAY_SIZE(zx234502_boost_properties);
boost->get_property = zx234502_boost_get_property;
boost->set_property = zx234502_boost_set_property;
boost->property_is_writeable = zx234502_boost_property_is_writeable;
}
static int zx234502_charger_gpio_config(struct device *dev,int pin,gpio_func_id func_sel,char *name)
{
int ret = -1;
if (gpio_is_valid(pin)){
ret = gpio_request(pin, name);
if (ret){
dev_err(dev, "cannot get [%s] gpio\n",name);
return -1;
}
ret = zx29_gpio_config(pin,func_sel);
if(ret){
dev_err(dev, "cannot config [%s] gpio\n",name);
gpio_free(pin);
return -1;
}
}
return 0;
}
#if 0
//zx234500_charger_gpio_config_input
static void zx234502_charger_gpio_config_input(struct device *dev,int gpio,gpio_func_id func_sel,char *str)
{
if(zx234502_charger_gpio_config(dev,gpio,func_sel,str)!=0)
return;
zx29_gpio_set_direction(gpio,GPIO_IN);
zx29_gpio_input_data(gpio);
}
#endif
//zx234500_charger_gpio_config_output
static int zx234502_charger_gpio_config_output(struct device *dev,int gpio,gpio_func_id func_sel,char *str,int value2)
{
if(zx234502_charger_gpio_config(dev,gpio,func_sel,str)!=0)
return -1;
zx29_gpio_set_direction(gpio,GPIO_OUT);
zx29_gpio_output_data(gpio,value2);
return 0;
}
static int zx234502_boost_gpio_init(struct device *dev, struct zx234502_platform_data *pdata)
{
int ret = -1;
ret = zx234502_charger_gpio_config_output(dev,pdata->boost_loadswitch_gpio,pdata->boost_loadswitch_fun_sel,"LoadSwitch",GPIO_LOW);
/*set the out put current 1A*/
ret += zx234502_charger_gpio_config_output(dev,pdata->boost_cur_gpio1,pdata->boost_gpio1_fun_sel,"Iset1",GPIO_LOW);
ret += zx234502_charger_gpio_config_output(dev,pdata->boost_cur_gpio2,pdata->boost_gpio2_fun_sel,"Iset2",GPIO_HIGH);
ret += zx234502_charger_gpio_config_output(dev,pdata->boost_cur_gpio3,pdata->boost_gpio3_fun_sel,"Iset3",GPIO_LOW);
return ret;
}
static void zx234502_boost_gpio_uninit(struct zx234502_platform_data *pdata)
{
gpio_free(pdata->boost_loadswitch_gpio);
gpio_free(pdata->boost_cur_gpio1);
gpio_free(pdata->boost_cur_gpio2);
gpio_free(pdata->boost_cur_gpio3);
}
/*
when BAT goes under BAEdet(3.4V) or CHGRST set 1
Group B(A1,A2,A3,A4,A5,A7) registers will reset
call this function in init or dcin
*/
static int zx234502_charger_config_groupB_Regs(struct zx234502_dev_info *bdi)
{
int ret;
/*set VINDPM to 4.52V*/
ret = zx234502_write_mask(bdi,ZX234502_REG_ISC,
ZX234502_REG_ISC_VINDPM_MASK,
ZX234502_REG_ISC_VINDPM_SHIFT,
0x8);
/*set VSETA to 4.2V*/
ret += zx234502_write_mask(bdi,ZX234502_REG_CVC,
ZX234502_REG_CVC_VBAT_MASK,
ZX234502_REG_CVC_VBAT_SHIFT,
0x2);
/*disable the timer*/
ret += zx234502_write_mask(bdi,ZX234502_REG_CTC,
ZX234502_REG_CTC_EN_TIMER_MASK,
ZX234502_REG_CTC_EN_TIMER_SHIFT,
0x0);
/*disable the 2*timer*/
ret += zx234502_write_mask(bdi,ZX234502_REG_CTC,
ZX234502_REG_CTC_EN_2XTIMER_MASK,
ZX234502_REG_CTC_EN_2XTIMER_SHIFT,
0x0);
/*set CVCOMP to 20mV*/
ret += zx234502_write_mask(bdi,ZX234502_REG_CTC,
ZX234502_REG_CTC_CVCOMP_MASK,
ZX234502_REG_CTC_CVCOMP_SHIFT,
0x4);
/*disable the JETTA*/
ret += zx234502_write_mask(bdi,ZX234502_REG_THR,
ZX234502_REG_THR_EN_JEITA_MASK,
ZX234502_REG_THR_EN_JEITA_SHIFT,
0x0);
/*set the temprator cool */
/*00:0 01:5 10:10 11:15*/
ret += zx234502_write_mask(bdi,ZX234502_REG_TS,
ZX234502_REG_TS_COOL_MASK,
ZX234502_REG_TS_COOL_SHIFT,
0x0);
if (ret < 0){
goto out;
}
return 0;
out:
//pm_runtime_put_sync(bdi->dev);
dev_err(bdi->dev, "zx234502_charger_groupB_config ERROR: %d\n", ret);
return ret;
}
static int zx234502_hw_init(struct zx234502_dev_info *bdi)
{
u8 v;
int ret;
union power_supply_propval voltage_val = {0};
#ifdef DBG_CHARGE
//set pshold1 1,just for test
//gpio_request(ZX29_GPIO_51, "pshold1");
//zx29_gpio_config(ZX29_GPIO_51, GPIO51_GPIO51);
//zx29_gpio_set_direction(ZX29_GPIO_51, GPIO_OUT);
//zx29_gpio_output_data(ZX29_GPIO_51, 1);
#endif
//pm_runtime_get_sync(bdi->dev);
/* First check that the device really is what its supposed to be */
ret = zx234502_read_mask(bdi, ZX234502_REG_VER,
ZX234502_REG_VER_INFO_MASK,
ZX234502_REG_VER_INFO_SHIFT,
&v);
if (ret < 0){
goto out;
}
//printk(KERN_INFO "zx234502_hw_init:zx234502-charger version reg: 0x%x\n", v);
/*
if (v != bdi->model) {
ret = -ENODEV;
goto out;
}
*/
voltage_val.intval = 4350;
ret = zx234502_charger_set_voltage(bdi,&voltage_val);
if (ret < 0){
goto out;
}
/*disable the timer*/
ret = zx234502_write_mask(bdi,ZX234502_REG_CTC,
ZX234502_REG_CTC_EN_TIMER_MASK,
ZX234502_REG_CTC_EN_TIMER_SHIFT,
0x0);
if (ret < 0){
goto out;
}
/*disable the 2*timer*/
ret = zx234502_write_mask(bdi,ZX234502_REG_CTC,
ZX234502_REG_CTC_EN_2XTIMER_MASK,
ZX234502_REG_CTC_EN_2XTIMER_SHIFT,
0x0);
if (ret < 0){
goto out;
}
//enable PMUON
//ret = zx234502_write_mask(bdi,ZX234502_REG_MS,
// ZX234502_REG_MS_PMUON_MASK,
// ZX234502_REG_MS_PMUON_SHIFT,
// 0x1);
//config NTCDET
// 0:enable auto ntc detection
// 1:disable auto ntc detection
ret += zx234502_write_mask(bdi,ZX234502_REG_PTS,
ZX234502_REG_PTS_NTCDET_MASK,
ZX234502_REG_PTS_NTCDET_SHIFT,
0x1);
//when NTCDET is 1 ,TSSEL is to select R
// 0:10k
// 1:100k
//ret += zx234502_write_mask(bdi,ZX234502_REG_PTS,
// ZX234502_REG_PTS_TSSEL_MASK,
// ZX234502_REG_PTS_TSSEL_SHIFT,
// 0x0);
/*disable the charger detect*/
ret += zx234502_write_mask(bdi,ZX234502_REG_THR,
ZX234502_REG_THR_EN_2DET_MASK,
ZX234502_REG_THR_EN_2DET_SHIFT,
0x0);
ret += zx234502_write_mask(bdi,ZX234502_REG_THR,
ZX234502_REG_THR_EN_1DET_MASK,
ZX234502_REG_THR_EN_1DET_SHIFT,
0x0);
//enable charger
ret += zx234502_write_mask(bdi,ZX234502_REG_MS,
ZX234502_REG_MS_ENCHG_MASK,
ZX234502_REG_MS_ENCHG_SHIFT,
0x0);
ret += zx234502_write_mask(bdi,ZX234502_REG_MS,
ZX234502_REG_MS_INSUS_MASK,
ZX234502_REG_MS_INSUS_SHIFT,
0x0);
//mask /SAFE it
ret += zx234502_write_mask(bdi,ZX234502_REG_CBIM,ZX234502_REG_CBIM_MASK_SAFE_TIMER_MASK,
ZX234502_REG_CBIM_MASK_SAFE_TIMER_SHIFT,0x1);
//mask CHGRUN it
ret += zx234502_write_mask(bdi,ZX234502_REG_CIIM,ZX234502_REG_CIIM_MASK_CHGRUN_MASK,
ZX234502_REG_CIIM_MASK_CHGRUN_SHIFT,0x1);
//mask INLIMIT it
ret += zx234502_write_mask(bdi,ZX234502_REG_CIIM,ZX234502_REG_CIIM_MASK_INLIMIT_MASK,
ZX234502_REG_CIIM_MASK_INLIMIT_SHIFT,0x1);
//mask DCDET it
ret += zx234502_write_mask(bdi,ZX234502_REG_CIIM,ZX234502_REG_CIIM_MASK_DCDET_MASK,
ZX234502_REG_CIIM_MASK_DCDET_SHIFT,0x1);
if (ret < 0){
goto out;
}
//set ts_meter interrupt mask
if(true !=bdi->pdata->ts_flag){
zx234502_write_mask(bdi,ZX234502_REG_CIIM,ZX234502_REG_CIIM_MASK_TS_MASK,
ZX234502_REG_CIIM_MASK_TS_SHIFT,0x1);
}
return 0;
out:
//pm_runtime_put_sync(bdi->dev);
dev_err(bdi->dev, "zx234502_hw_init err: %d\n", ret);
return ret;
}
static irqreturn_t zx234502_charger_irq_primary_handler(int irq, struct zx234502_dev_info *bdi)
{
disable_irq_nosync(irq);
//pcu_int_clear(irq);
pcu_clr_irq_pending(irq);
up(&bdi->chgirq_sem);
return IRQ_HANDLED;
}
static irqreturn_t zx234502_irq_handler_thread(void *data)
{
struct zx234502_dev_info *bdi = data;
bool charger_changed_flag = false;
bool battery_changed_flag = false;
bool boost_changed_flag = false;
int ret;
u8 ms;
struct sched_param param = { .sched_priority = 2 };
param.sched_priority= 31;
sched_setscheduler(current, SCHED_FIFO, &param);
while(1)
{
down(&bdi->chgirq_sem);
//pm_runtime_get_sync(bdi->dev);
mutex_lock(&bdi->bs_reg_lock);
bdi->cbis_pre_reg=bdi->cbis_curr_reg;
bdi->pis_pre_reg=bdi->pis_curr_reg;
bdi->ciis_pre_reg=bdi->ciis_curr_reg;
bdi->cfis_pre_reg = bdi->cfis_curr_reg;
//printk(KERN_INFO"zx234502_irq_handler_thread\n");
ret = zx234502_read(bdi, ZX234502_REG_MS, &ms);
if (ret < 0) {
dev_err(bdi->dev, "Can't read MS reg: %d\n", ret);
mutex_unlock(&bdi->bs_reg_lock);
goto out;
}
//printk("zx234502 MS reg is %d",ms);
ret = zx234502_read(bdi, ZX234502_REG_CBIS, &bdi->cbis_curr_reg);
if (ret < 0) {
dev_err(bdi->dev, "Can't read CBIS reg: %d\n", ret);
mutex_unlock(&bdi->bs_reg_lock);
goto out;
}
ret = zx234502_read(bdi, ZX234502_REG_PIS, &bdi->pis_curr_reg);
if (ret < 0) {
dev_err(bdi->dev, "Can't read PIS reg: %d\n", ret);
mutex_unlock(&bdi->bs_reg_lock);
goto out;
}
ret = zx234502_read(bdi, ZX234502_REG_CIIS, &bdi->ciis_curr_reg);
if (ret < 0) {
dev_err(bdi->dev, "Can't read CIIS reg: %d\n", ret);
mutex_unlock(&bdi->bs_reg_lock);
goto out;
}
printk("cbis_reg: 0x%02x, pis_reg: 0x%02x, ciis_reg: 0x%02x\n",
bdi->cbis_curr_reg, bdi->pis_curr_reg, bdi->ciis_curr_reg);
/*clear the A10 int*/
ret = zx234502_write_mask(bdi, ZX234502_REG_CIIS,ZX234502_REG_CIIS_INT_MASK,ZX234502_REG_CIIS_INT_SHIFT,0);
if (ret < 0) {
printk(KERN_INFO"chg clear int failed 1\n");
ret = zx234502_write_mask(bdi, ZX234502_REG_CIIS,ZX234502_REG_CIIS_INT_MASK,ZX234502_REG_CIIS_INT_SHIFT,0);
if (ret < 0) {
printk(KERN_INFO"chg clear int failed 2\n");
ret = zx234502_write_mask(bdi, ZX234502_REG_CIIS,ZX234502_REG_CIIS_INT_MASK,ZX234502_REG_CIIS_INT_SHIFT,0);
if (ret < 0) {
printk(KERN_INFO"chg clear int failed 3\n");
}
else{
printk(KERN_INFO"chg clear int failed ok 3\n");
//panic(0);
}
}
else{
printk(KERN_INFO"chg clear int failed ok 2\n");
}
}
if(bdi->cbis_curr_reg != bdi->cbis_pre_reg){
if ((bdi->cbis_curr_reg & ZX234502_REG_CBIS_DCIN_PG_MASK) != (bdi->cbis_pre_reg & ZX234502_REG_CBIS_DCIN_PG_MASK)) {
if(bdi->cbis_curr_reg & ZX234502_REG_CBIS_DCIN_PG_MASK){
//zx234502_hw_init(bdi);
printk(KERN_INFO"chg usb in\n");
dwc_otg_chg_inform(0);/*usb in*/
ret = zx234502_charger_config_groupB_Regs(bdi);
if(ret)
{
printk(KERN_INFO"chg config groupB Regs failed\n");
}
// ret = zx234502_write_mask(bdi,ZX234502_REG_MS,ZX234502_REG_MS_PMUON_MASK,ZX234502_REG_MS_PMUON_SHIFT,0x1);
}
else{
printk(KERN_INFO"chg usb out\n");
dwc_otg_chg_inform(1);/*usb out*/
bdi->charger.type = POWER_SUPPLY_PCAC_UNKNOWN;
// ret = zx234502_write_mask(bdi,ZX234502_REG_MS,ZX234502_REG_MS_PMUON_MASK,ZX234502_REG_MS_PMUON_SHIFT,0x0);
}
}
charger_changed_flag = true;
}
if(true == bdi->pdata->boost_flag){
if (bdi->pis_curr_reg != bdi->pis_pre_reg) {
if(bdi->pis_curr_reg & ZX234502_REG_PIS_POWERON_IT_MASK){
queue_delayed_work(g_bdi->boostQueue,&bdi->boostWorkStruct,2000);
}
boost_changed_flag = true;
bdi->boost_online_flag = 1;
}
}
if (bdi->ciis_curr_reg != bdi->ciis_pre_reg) {
if(true == bdi->pdata->ts_flag){
if((bdi->ciis_curr_reg & ZX234502_REG_CIIS_TS_METER_MASK) != (bdi->ciis_pre_reg & ZX234502_REG_CIIS_TS_METER_MASK)){
#ifdef DBG_CHARGE
switch((bdi->ciis_curr_reg&ZX234502_REG_CIIS_TS_METER_MASK )>>ZX234502_REG_CIIS_TS_METER_SHIFT){
case 0:
#ifdef DBG_CHARGE
printk(KERN_INFO"chg bat temp nomrmal\n");
#endif
break;
case ZX234502_REG_CISS_TS_WARM:/*warm*/
/*in warm state,stop boost*/
#ifdef DBG_CHARGE
printk(KERN_INFO"chg bat temp warm \n");
#endif
break;
case ZX234502_REG_CISS_TS_COOL:/*cool*/
/*in cool state,stop boost*/
#ifdef DBG_CHARGE
printk(KERN_INFO"chg bat temp cool\n");
#endif
//chg_disable.intval = 1;
//pwrbnk_enable.intval = 0;
//zx234502_charger_set_charger_config(bdi,&chg_disable);
//zx234502_boost_set_charge_enable(bdi, &pwrbnk_enable);
break;
case 3:/*hot*/
/*in hot state,charger chip auto off*/
printk(KERN_INFO"chg bat temp hot \n");
break;
case 6:/*cold*/
/*in cold state,charger chip auto off*/
printk(KERN_INFO"chg bat temp cold\n");
break;
default:
printk(KERN_INFO"chg bat temp unkonw\n");
break;
}
#endif
}
}
battery_changed_flag = true;
}
bdi->charger_health_valid = true;/****?***/
bdi->battery_health_valid = true;/****?***/
bdi->battery_status_valid = true;/****?***/
mutex_unlock(&bdi->bs_reg_lock);
/*
* Sometimes zx234502 gives a steady trickle of interrupts even
* though the watchdog timer is turned off and neither the STATUS
* nor FAULT registers have changed. Weed out these sprurious
* interrupts so userspace isn't alerted for no reason.
* In addition, the chip always generates an interrupt after
* register reset so we should ignore that one (the very first
* interrupt received).
*/
//if (charger_changed_flag && !bdi->first_time)
if (charger_changed_flag )
power_supply_changed(&bdi->charger);
//if (battery_changed_flag && !bdi->first_time)
if (battery_changed_flag)
power_supply_changed(&bdi->battery);
if (true == bdi->pdata->boost_flag){
//if (boost_changed_flag && !bdi->first_time)
if (boost_changed_flag )
power_supply_changed(&bdi->boost);
}
bdi->first_time = false;
out:
//pm_runtime_put_sync(bdi->dev);
dev_dbg(bdi->dev, "read reg:cbis_reg: 0x%02x, pis_reg: 0x%02x, ciis_reg: 0x%02x\n",
bdi->cbis_curr_reg, bdi->pis_curr_reg, bdi->ciis_curr_reg);
enable_irq(bdi->irq);
}
return 0;
}
static int zx234502_setup_pdata(struct zx234502_dev_info *bdi,
struct zx234502_platform_data *pdata)
{
int ret;
#if 0
if (!gpio_is_valid(pdata->gpio_int))
return -1;
ret = gpio_request(pdata->gpio_int, dev_name(bdi->dev));
if (ret < 0)
return -1;
ret = gpio_direction_input(pdata->gpio_int);
if (ret < 0)
goto out;
bdi->irq = gpio_to_irq(pdata->gpio_int);
if (!bdi->irq)
goto out;
#endif
bdi->irq = gpio_to_irq(pdata->gpio_int);
#ifdef DBG_CHARGE
printk(KERN_INFO"zx234502_setup_pdata irq= %d\n",bdi->irq);
#endif
/*Ñ¡Ôñ´Ë¸´Óù¦ÄÜΪÖжϹ¦ÄÜ*/
ret = zx29_gpio_config(pdata->gpio_int,pdata->gpio_int_fun_sel);/********GPIO11:0 /EXT_INT:5*******/
if (ret < 0){
printk(KERN_INFO"zx234502 zx29_gpio_config error int= %d,fun= %d\n",pdata->gpio_int,pdata->gpio_int_fun_sel);
return -1;
}
zx29_gpio_set_inttype(pdata->gpio_int,IRQ_TYPE_EDGE_FALLING/*IRQ_TYPE_EDGE_RISING*/); //INT_POSEDGE
zx29_gpio_pd_pu_set(pdata->gpio_int, IO_CFG_PULL_DISABLE);
bdi->gpio_int = pdata->gpio_int;
//pcu_int_clear(bdi->irq);
pcu_clr_irq_pending(bdi->irq);
return 0;
#if 0
out:
gpio_free(pdata->gpio_int);
return -1;
#endif
}
static int zx234502_init_state(struct zx234502_dev_info *bdi)
{
u8 cfis_reg,pis_reg,cbis_reg,ciis_reg;
int ret;
printk(KERN_INFO "zx234502_init_state start.\n");
ret = zx234502_read(bdi, ZX234502_REG_CBIS, &cbis_reg);
if (ret < 0){
printk(KERN_INFO "charger:init state:Can't read CBIS reg: 0x%x\n", cbis_reg);
return ret;
}
ret = zx234502_read(bdi, ZX234502_REG_PIS, &pis_reg);
if (ret < 0) {
printk(KERN_INFO "charger:init Power On /PMID state:Can't read PIS reg: %d\n", ret);
return ret;
}
ret = zx234502_read(bdi, ZX234502_REG_CFIS, &cfis_reg);
if (ret < 0) {
printk(KERN_INFO "charger:init Power On /PMID state:Can't read CFIS reg: %d\n", ret);
return ret;
}
ret = zx234502_read(bdi, ZX234502_REG_CIIS, &ciis_reg);
if (ret < 0) {
printk(KERN_INFO "charger:init Power On /PMID state:Can't read CISS reg: %d\n", ret);
return ret;
}
mutex_lock(&bdi->bs_reg_lock);
#ifdef DBG_CHARGE
printk(KERN_INFO "zx234502_init_state get lock ok.\n");
#endif
bdi->cbis_curr_reg= cbis_reg;
bdi->pis_curr_reg= pis_reg;
bdi->cfis_curr_reg= cfis_reg;
bdi->ciis_curr_reg = ciis_reg;
bdi->charger.type = POWER_SUPPLY_TYPE_UNKNOWN;
if(bdi->cbis_curr_reg & ZX234502_REG_CBIS_DCIN_PG_MASK){
printk(KERN_INFO"send ap usb in message\n");
dwc_otg_chg_inform(0);/*usb in*/
}
else{
printk(KERN_INFO"send ap usb out message\n");
dwc_otg_chg_inform(1);/*usb out*/
}
#ifdef DBG_CHARGE
if(bdi->cbis_curr_reg & BIT(5))
printk(KERN_INFO "no batterty.\n");
else
printk(KERN_INFO " baterry installed.\n");
#endif
mutex_unlock(&bdi->bs_reg_lock);
#ifdef DBG_CHARGE
printk(KERN_INFO "zx234502_init_state:cfis_reg10=0x%x,pis_reg0E=0x%x,cbis_reg0C=0x%x,ciis_reg0A=0x%x.\n",cfis_reg,pis_reg,cbis_reg,ciis_reg);
#endif
return 0;
}
static void zx234502_boost_workstruct_callback(struct work_struct *work )
{
uint vol_curr = 0;
#ifdef DBG_CHARGE
u8 mainset;
#endif
struct zx234502_dev_info *bdi =
container_of(work, struct zx234502_dev_info, boostWorkStruct);
vol_curr = get_adc2_voltage();/*get boost current*/
#ifdef DBG_CHARGE
printk(KERN_INFO"boost current is %d in\n",vol_curr);
zx234502_read(bdi, 0, &mainset);
printk(KERN_INFO"powerbank is %d in\n",mainset);
printk(KERN_INFO"workqueue test\n");
#endif
if (vol_curr < ZX234502_BOOST_V_LIMIT)/*50mV*/{
bdi->boostcount++;
}
if(bdi->boostcount > 3){
bdi->boostcount = 0;
bdi->boost_online_flag = 0;
power_supply_changed(&bdi->boost);
cancel_delayed_work_sync(&bdi->boostWorkStruct);
}
else{
bdi->boost_online_flag = 1;
queue_delayed_work(bdi->boostQueue,&bdi->boostWorkStruct,2000);
}
}
static void zx234502_charge_typedet(T_TYPE_USB_DETECT chg_type)
{
int ret;
#ifdef DBG_CHARGE
printk(KERN_INFO"charge type is %d in\n",chg_type);
#endif
if(TYPE_ADAPTER == chg_type){
printk(KERN_INFO"chg type is TYPE_ADAPTER\n");
/*set the DCIN Current = 1A*/
ret = zx234502_write_mask(g_bdi,ZX234502_REG_ISC,
ZX234502_REG_ISC_ISET_DCIN_MASK,
ZX234502_REG_ISC_ISET_DCIN_SHIFT,
0x3);
if (ret < 0){
printk(KERN_INFO"write REG_01 fault\n");
}
g_bdi->charger.type = POWER_SUPPLY_PCAC__AC;
}
else{
printk(KERN_INFO"chgage type is TYPE_PC\n");
/*set the DCIN Current = 450mA*/
ret = zx234502_write_mask(g_bdi,ZX234502_REG_ISC,
ZX234502_REG_ISC_ISET_DCIN_MASK,
ZX234502_REG_ISC_ISET_DCIN_SHIFT,
0x1);
if (ret < 0){
printk(KERN_INFO"write REG_01 fault\n");
}
g_bdi->charger.type = POWER_SUPPLY_PCAC__PC;
}
#ifdef CONFIG_CHARGER_ZX234502_EVB
/*set the DCIN Current = 2A*/
ret = zx234502_write_mask(g_bdi,ZX234502_REG_ISC,
ZX234502_REG_ISC_ISET_DCIN_MASK,
ZX234502_REG_ISC_ISET_DCIN_SHIFT,
0x5);
if (ret < 0){
printk(KERN_INFO"write REG_01 fault\n");
}
#endif
}
#if defined(CONFIG_DEBUG_FS)
static ssize_t debugfs_regs_write(struct file *file, const char __user *buf,size_t nbytes, loff_t *ppos)
{
unsigned int val1, val2;
u8 reg, value;
int ret;
char *kern_buf;
struct seq_file *s = file->private_data;
struct zx234502_dev_info *zx234502 = s->private;
kern_buf = kzalloc(nbytes, GFP_KERNEL);
if (!kern_buf) {
printk(KERN_INFO "zx234502_charger: Failed to allocate buffer\n");
return -ENOMEM;
}
if (copy_from_user(kern_buf, (void __user *)buf, nbytes)) {
kfree(kern_buf);
return -ENOMEM;
}
printk(KERN_INFO "%s input str=%s,nbytes=%d \n", __func__, kern_buf,nbytes);
ret = sscanf(kern_buf, "%x:%x", &val1, &val2);
if (ret < 2 || val1 > ZX234502_REG_MAX ) {
printk(KERN_INFO "zx234502_charger: failed to read user buf, ret=%d, input 0x%x:0x%x\n",
ret, val1, val2);
kfree(kern_buf);
return -EINVAL;
}
kfree(kern_buf);
reg = val1 & 0xff;
value = val2 & 0xff;
printk(KERN_INFO "%s input %x,%x; reg=%x,value=%x\n", __func__, val1, val2, reg, value);
ret = zx234502_write(zx234502,reg, value);
return ret ? ret : nbytes;
}
static int debugfs_regs_show(struct seq_file *s, void *v)
{
int i;
u8 value[ZX234502_REG_MAX];
int ret=0;
int curr = 0;
struct zx234502_dev_info *zx234502 = s->private;
for (i = 0; i < ZX234502_REG_MAX; i++){
ret = zx234502_read(zx234502, i, &(value[i]));
if(ret){
printk(KERN_INFO "%s err=%d, break\n", __func__, ret);
seq_printf(s, "%s err=%d, break", __func__, ret);
return ret;
}
}
for (i = 0; i < ZX234502_REG_MAX; i++) {
if((i+1)%9 == 0)
seq_printf(s, "\n");
seq_printf(s, "[0x%x]%02x ", i, value[i]);
}
/*charger type*/
if(zx234502->charger.type ==POWER_SUPPLY_PCAC__PC){
seq_printf(s, "charger type is PC\n");
}
else if(zx234502->charger.type ==POWER_SUPPLY_PCAC__AC){
seq_printf(s, "charger type is AC\n");
}
else
seq_printf(s, "charger type is unknow = %d\n",zx234502->charger.type);
/*stopchg_flag*/
if(1==stopchg_flag){
seq_printf(s, "mmi set charger enable,the charger state = %d\n",!(value[ZX234502_REG_MS]&ZX234502_REG_MS_INSUS_MASK));
}
else if(2==stopchg_flag){
seq_printf(s, "mmi set charger disable,the charger state = %d\n",!(value[ZX234502_REG_MS]&ZX234502_REG_MS_INSUS_MASK));
}
else
seq_printf(s, "mmi never set the charger ,the charger state = %d\n",!(value[ZX234502_REG_MS]&ZX234502_REG_MS_INSUS_MASK));
/*charge current*/
ret = zx234502_get_field_val(zx234502, ZX234502_REG_ISC,
ZX234502_REG_ISC_ISET_DCIN_MASK, ZX234502_REG_ISC_ISET_DCIN_SHIFT,
zx234502_in_ilimit_values,
ARRAY_SIZE(zx234502_in_ilimit_values), &curr);
if (ret < 0)
return ret;
seq_printf(s, "the charger current now = %dmA\n",curr);
return ret;
}
#define DEBUGFS_FILE_ENTRY(name) \
static int debugfs_##name##_open(struct inode *inode, struct file *file) \
{\
return single_open(file, debugfs_##name##_show, inode->i_private); \
}\
\
static const struct file_operations debugfs_##name##_fops = { \
.owner= THIS_MODULE, \
.open= debugfs_##name##_open, \
.write=debugfs_##name##_write, \
.read= seq_read, \
.llseek= seq_lseek, \
.release= single_release, \
}
DEBUGFS_FILE_ENTRY(regs);
static struct dentry *g_charger_root;
static void debugfs_charger_init(struct zx234502_dev_info *zx234502)
{
struct dentry *root;
struct dentry *node;
int i;
if(!zx234502)
return;
//create root
root = debugfs_create_dir("charger_zx29", NULL);
if (!root) {
dev_err(&zx234502->dev, "debugfs_create_dir err=%d\n", IS_ERR(root));
goto err;
}
//print regs;
node = debugfs_create_file("regs", S_IRUGO | S_IWUGO, root, zx234502, &debugfs_regs_fops);
if (!node){
dev_err(&zx234502->dev, "debugfs_create_dir err=%d\n", IS_ERR(node));
goto err;
}
g_charger_root = (void *)root;
return;
err:
dev_err(&zx234502->dev, "debugfs_charger_init err\n");
}
#endif
static int zx234502_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct device *dev = &client->dev;
struct zx234502_platform_data *pdata = client->dev.platform_data;
struct zx234502_dev_info *bdi;
int ret;
int i; /*when err try 3 times*/
//use for other
//g_platform_data= client->dev.platform_data;
printk(KERN_INFO "charger probe.\n");
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
return -ENODEV;
}
bdi = devm_kzalloc(dev, sizeof(*bdi), GFP_KERNEL);
if (!bdi) {
dev_err(dev, "Can't alloc bdi struct\n");
return -ENOMEM;
}
bdi->pdata = pdata;
bdi->client = client;
bdi->dev = dev;
bdi->model = id->driver_data;
strncpy(bdi->model_name, id->name, sizeof(bdi->model_name)-1);
mutex_init(&bdi->bs_reg_lock);
bdi->first_time = true;
bdi->charger_health_valid = false;
bdi->battery_health_valid = false;
bdi->battery_status_valid = false;
g_bdi = bdi;
i2c_set_clientdata(client, bdi);
if (dev->of_node)
ret = 0;//zx234502_setup_dt(bdi);
else
ret = zx234502_setup_pdata(bdi, pdata);
if (ret) {
dev_err(dev, "Can't get irq info\n");
return -EINVAL;
}
#ifdef DBG_CHARGE
printk(KERN_INFO "hwinit.\n");
#endif
//pm_runtime_enable(dev);
//pm_runtime_resume(dev);
i = 0;
do{
ret = zx234502_hw_init(bdi);
if (ret < 0) {
printk(KERN_INFO "zx234502_probe Hardware init failed times = %d\n",(i+1));
i++;
}
else
break;
}while(i<3);
#ifdef DBG_CHARGE
printk(KERN_INFO "zx234502_probe zx234502_hw_init ok.\n");
#endif
#ifdef CONFIG_CHARGER_ZX234502_EVB
/*set the DCIN Current = 2A*/
ret = zx234502_write_mask(g_bdi,ZX234502_REG_ISC,
ZX234502_REG_ISC_ISET_DCIN_MASK,
ZX234502_REG_ISC_ISET_DCIN_SHIFT,
0x5);
if (ret < 0){
printk(KERN_INFO"write REG_01 fault\n");
}
#endif
ret = zx234502_charger_config_groupB_Regs(bdi);
if (ret < 0) {
printk(KERN_INFO "groupB config faild.\n");
}
zx234502_charger_init(&bdi->charger);
ret = power_supply_register(dev, &bdi->charger);
if (ret) {
dev_err(dev, "Can't register charger\n");
goto out2;
}
printk(KERN_INFO "zx234502_probe power_supply_register charger ok.\n");
zx234502_battery_init(&bdi->battery);
ret = power_supply_register(dev, &bdi->battery);
if (ret) {
dev_err(dev, "Can't register battery\n");
goto out3;
}
printk(KERN_INFO "zx234502_probe power_supply_register battery ok.\n");
if(true == pdata->boost_flag){
zx234502_boost_init(&bdi->boost);
ret = power_supply_register(dev, &bdi->boost);
if (ret) {
dev_err(dev, "Can't register boost\n");
goto out4;
}
ret = zx234502_boost_gpio_init(dev,pdata);
if (ret) {
dev_err(dev, "Can't register boost\n");
goto out4;
}
#ifdef DBG_CHARGE
printk(KERN_INFO "zx234502_boost_gpio_init.\n");
#endif
bdi->boostQueue = create_workqueue("zx234502boost");
if (!bdi->boostQueue){
if(bdi->pdata->boost_cur_gpio1)
gpio_free(bdi->pdata->boost_cur_gpio1);
if(bdi->pdata->boost_cur_gpio2)
gpio_free(bdi->pdata->boost_cur_gpio2);
if(bdi->pdata->boost_cur_gpio3)
gpio_free(bdi->pdata->boost_cur_gpio3);
if(bdi->pdata->boost_loadswitch_gpio)
gpio_free(bdi->pdata->boost_loadswitch_gpio);
ret = -1;
goto out4;
}
INIT_DELAYED_WORK(&bdi->boostWorkStruct,zx234502_boost_workstruct_callback);
//queue_delayed_work(bdi->boostQueue,&bdi->boostWorkStruct,20000);
#ifdef DBG_CHARGE
printk(KERN_INFO "setup_workqueue.\n");
#endif
}
#if 0
//#ifdef CONFIG_SYSFS
ret = zx234502_sysfs_create_group(bdi);
if (ret) {
dev_err(dev, "Can't create sysfs entries\n");
goto out5;
}
#endif
dwc_chg_Regcallback(zx234502_charge_typedet);/*register for usb*/
sema_init(&bdi->chgirq_sem, 0);
ret = zx234502_init_state(bdi);
if(ret)
{
dev_err(dev, "zx234502 init state error.\n");
goto out1;
}
bdi->chg_irq_thread = kthread_run(zx234502_irq_handler_thread, bdi, "zx234502-chgirq");
BUG_ON(IS_ERR(bdi->chg_irq_thread));
ret = request_irq(bdi->irq, zx234502_charger_irq_primary_handler,IRQF_NO_THREAD, "zx234502-charger", bdi);
if (ret < 0) {
dev_err(dev, "Can't set up irq handler\n");
goto out1;
}
irq_set_irq_wake(bdi->irq, 1);
/*clear all int*/
ret = zx234502_write_mask(bdi,ZX234502_REG_CIIS,ZX234502_REG_CIIS_INT_MASK,ZX234502_REG_CIIS_INT_SHIFT,0x0);
if (ret == 0) {
printk(KERN_INFO "clear int ok \n");
}
#if defined(CONFIG_DEBUG_FS)
debugfs_charger_init(bdi);
#endif
#ifdef DBG_CHARGE
printk(KERN_INFO "zx234502_probe end.\n");
#endif
return 0;
#if 0
out5:
zx234502_sysfs_remove_group(bdi);
#endif
out4:
power_supply_unregister(&bdi->boost);
out3:
power_supply_unregister(&bdi->battery);
out2:
//pm_runtime_disable(dev);
power_supply_unregister(&bdi->charger);
out1:
if (bdi->gpio_int)
gpio_free(bdi->gpio_int);
dwc_chg_Regcallback(NULL);/*unregister for usb*/
return ret;
}
static int zx234502_remove(struct i2c_client *client)
{
struct zx234502_dev_info *bdi = i2c_get_clientdata(client);
struct zx234502_platform_data *pdata = client->dev.platform_data;
if(!bdi)
return -EINVAL;
//pm_runtime_get_sync(bdi->dev);
zx234502_register_reset(bdi);
//pm_runtime_put_sync(bdi->dev);
zx234502_sysfs_remove_group(bdi);
power_supply_unregister(&bdi->battery);
power_supply_unregister(&bdi->charger);
if(true == pdata->boost_flag)
power_supply_unregister(&bdi->boost);
pm_runtime_disable(bdi->dev);
if (bdi->gpio_int)
gpio_free(bdi->gpio_int);
zx234502_boost_gpio_uninit(pdata);
if(bdi->boostQueue)
destroy_workqueue(bdi->boostQueue);
#if defined(CONFIG_DEBUG_FS)
if(g_charger_root){
printk(KERN_INFO "zx234502_device_exit:debugfs_remove_recursive \n");
debugfs_remove_recursive(g_charger_root);
}
#endif
return 0;
}
static int zx234502_suspend(struct i2c_client *not_use, pm_message_t mesg)
{
disable_irq_nosync(g_bdi->irq);
return 0;
}
static int zx234502_resume(struct i2c_client *not_use)
{
enable_irq(g_bdi->irq);
return 0;
}
/*
* Only support the zx234502 right now. The bq24192, bq24192i, and bq24193
* are similar but not identical so the driver needs to be extended to
* support them.
*/
static const struct i2c_device_id zx234502_i2c_ids[] = {
{ "zx234502-charger", ZX234502_REG_VERS },
{ },
};
static struct i2c_driver zx234502_driver = {
.probe = zx234502_probe,
.remove = zx234502_remove,
.id_table = zx234502_i2c_ids,
.driver = {
.name = "zx234502-charger",
.owner = THIS_MODULE,
},
.suspend = zx234502_suspend,
.resume = zx234502_resume,
};
static int __init zx234502_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&zx234502_driver);
if (ret != 0)
pr_err("Failed to register visionox_i2c_driver : %d\n", ret);
return ret;
}
/* init early so consumer devices can complete system boot */
module_init(zx234502_i2c_init);
static void __exit zx234502_i2c_exit(void)
{
i2c_del_driver(&zx234502_driver);
}
module_exit(zx234502_i2c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>");
MODULE_ALIAS("i2c:zx234502-charger");
MODULE_DESCRIPTION("TI ZX234502 Charger Driver");