1934 lines
54 KiB
C
1934 lines
54 KiB
C
/* drivers/battery/sm5703_charger.c
|
|
* SM5703 Charger Driver
|
|
*
|
|
* Copyright (C) 2016 Siliconmitus Technology Corp.
|
|
*
|
|
* 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; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/battery/sec_charger.h>
|
|
#ifdef CONFIG_USB_HOST_NOTIFY
|
|
#include <linux/host_notify.h>
|
|
#include <linux/usb_notify.h>
|
|
#endif
|
|
#include <linux/wakelock.h>
|
|
#ifdef CONFIG_SM5703_MUIC
|
|
#include <linux/i2c/sm5703-muic.h>
|
|
#endif
|
|
|
|
#include <linux/mfd/sm5703.h>
|
|
|
|
//#ifdef CONFIG_FLED_SM5703
|
|
#include <linux/leds/sm5703_fled.h>
|
|
#include <linux/leds/smfled.h>
|
|
#include <linux/mfd/smfled.h>
|
|
#include <linux/mfd/sm5703_fled.h>
|
|
//#endif /* CONFIG_FLED_SM5703 */
|
|
#include <linux/version.h>
|
|
|
|
#include <linux/debugfs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/of_gpio.h>
|
|
|
|
|
|
#define EN_NOBAT_IRQ 0
|
|
#define EN_DONE_IRQ 1
|
|
#define EN_TOPOFF_IRQ 1
|
|
#define EN_CHGON_IRQ 0
|
|
#define EN_OTGFAIL_IRQ 1
|
|
#define EN_VBUSLIMIT_IRQ 0
|
|
#define EN_AICL_IRQ 1
|
|
#define DEFAULT_CHARGING_CURRENT 500
|
|
|
|
#define MINVAL(a, b) ((a <= b) ? a : b)
|
|
|
|
#ifndef EN_TEST_READ
|
|
#define EN_TEST_READ 1
|
|
#endif
|
|
|
|
static int sm5703_reg_map[] = {
|
|
SM5703_INTMSK1,
|
|
SM5703_INTMSK2,
|
|
SM5703_INTMSK3,
|
|
SM5703_INTMSK4,
|
|
SM5703_STATUS1,
|
|
SM5703_STATUS2,
|
|
SM5703_STATUS3,
|
|
SM5703_STATUS4,
|
|
SM5703_CNTL,
|
|
SM5703_VBUSCNTL,
|
|
SM5703_CHGCNTL1,
|
|
SM5703_CHGCNTL2,
|
|
SM5703_CHGCNTL3,
|
|
SM5703_CHGCNTL4,
|
|
SM5703_CHGCNTL5,
|
|
SM5703_CHGCNTL6,
|
|
SM5703_OTGCURRENTCNTL,
|
|
SM5703_Q3LIMITCNTL,
|
|
SM5703_STATUS5,
|
|
};
|
|
|
|
typedef struct sm5703_charger_data {
|
|
struct i2c_client *client;
|
|
sm5703_mfd_chip_t *sm5703;
|
|
struct power_supply psy_chg;
|
|
struct power_supply psy_otg;
|
|
sm5703_charger_platform_data_t *pdata;
|
|
int charging_current;
|
|
struct wake_lock vbuslimit_wake_lock;
|
|
struct delayed_work vbuslimit_work;
|
|
int current_max;
|
|
bool is_current_reduced;
|
|
int siop_level;
|
|
int cable_type;
|
|
bool is_charging;
|
|
struct mutex io_lock;
|
|
/* register programming */
|
|
int reg_addr;
|
|
int reg_data;
|
|
int nchgen;
|
|
|
|
bool full_charged;
|
|
bool ovp;
|
|
bool is_mdock;
|
|
struct workqueue_struct *wq;
|
|
int status;
|
|
#ifdef CONFIG_FLED_SM5703
|
|
struct sm_fled_info *fled_info;
|
|
#endif /* CONFIG_FLED_SM5703 */
|
|
} sm5703_charger_data_t;
|
|
|
|
static enum power_supply_property sec_charger_props[] = {
|
|
POWER_SUPPLY_PROP_STATUS,
|
|
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
|
POWER_SUPPLY_PROP_HEALTH,
|
|
POWER_SUPPLY_PROP_PRESENT,
|
|
POWER_SUPPLY_PROP_ONLINE,
|
|
POWER_SUPPLY_PROP_CURRENT_MAX,
|
|
POWER_SUPPLY_PROP_CURRENT_AVG,
|
|
POWER_SUPPLY_PROP_CURRENT_NOW,
|
|
#if defined(CONFIG_BATTERY_SWELLING) || defined(CONFIG_BATTERY_SWELLING_SELF_DISCHARGING)
|
|
POWER_SUPPLY_PROP_VOLTAGE_MAX,
|
|
#endif
|
|
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL,
|
|
POWER_SUPPLY_PROP_ENERGY_NOW,
|
|
};
|
|
|
|
static enum power_supply_property sm5703_otg_props[] = {
|
|
POWER_SUPPLY_PROP_ONLINE,
|
|
};
|
|
|
|
int otg_enable_flag;
|
|
|
|
static int sm5703_get_charging_health(
|
|
struct sm5703_charger_data *charger);
|
|
static int sm5703_get_charging_status(struct sm5703_charger_data *charger);
|
|
static void sm5703_read_regs(struct i2c_client *i2c, char *str)
|
|
{
|
|
u8 data = 0;
|
|
int i = 0;
|
|
for (i = SM5703_INTMSK1; i < ARRAY_SIZE(sm5703_reg_map); i++) {
|
|
data = sm5703_reg_read(i2c, sm5703_reg_map[i]);
|
|
sprintf(str+strlen(str), "0x%02x, ", data);
|
|
}
|
|
}
|
|
|
|
static void sm5703_test_read(struct i2c_client *i2c)
|
|
{
|
|
char str[1000] = {0,};
|
|
int i;
|
|
|
|
/* SM5703 REG: 0x04 ~ 0x13 */
|
|
for (i = SM5703_INTMSK1; i <= SM5703_CHGCNTL6; i++) {
|
|
int data;
|
|
data = sm5703_reg_read(i2c, i);
|
|
sprintf(str+strlen(str), "0x%0x = 0x%02x, ", i, data);
|
|
}
|
|
|
|
sprintf(str+strlen(str), "0x%0x = 0x%02x, ",SM5703_OTGCURRENTCNTL,
|
|
sm5703_reg_read(i2c, SM5703_OTGCURRENTCNTL));
|
|
sprintf(str+strlen(str), "0x%0x = 0x%02x, ", SM5703_STATUS5,
|
|
sm5703_reg_read(i2c, SM5703_STATUS5));
|
|
sprintf(str+strlen(str), "0x%0x = 0x%02x, ", SM5703_Q3LIMITCNTL,
|
|
sm5703_reg_read(i2c, SM5703_Q3LIMITCNTL));
|
|
pr_info("%s: %s\n", __func__, str);
|
|
}
|
|
|
|
#define SM5703_FLEDCNTL6 0x19
|
|
static void sm5703_charger_otg_control(struct sm5703_charger_data *charger,
|
|
bool enable)
|
|
{
|
|
pr_info("%s: called charger otg control : %s\n", __func__,
|
|
enable ? "on" : "off");
|
|
|
|
otg_enable_flag = enable;
|
|
|
|
if (!enable) {
|
|
/* turn off OTG */
|
|
sm5703_assign_bits(charger->sm5703->i2c_client,
|
|
SM5703_FLEDCNTL6, SM5703_BSTOUT_MASK,
|
|
SM5703_BSTOUT_4P5);
|
|
|
|
#ifdef CONFIG_FLED_SM5703
|
|
if (charger->fled_info == NULL)
|
|
charger->fled_info = sm_fled_get_info_by_name(NULL);
|
|
if (charger->fled_info)
|
|
sm5703_boost_notification(charger->fled_info, 0);
|
|
#else
|
|
sm5703_assign_bits(charger->sm5703->i2c_client,
|
|
SM5703_CNTL, SM5703_OPERATION_MODE_MASK,
|
|
SM5703_OPERATION_MODE_CHARGING_ON);
|
|
#endif
|
|
} else {
|
|
sm5703_assign_bits(charger->sm5703->i2c_client,
|
|
SM5703_FLEDCNTL6, SM5703_BSTOUT_MASK,
|
|
SM5703_BSTOUT_5P0);
|
|
#ifdef CONFIG_FLED_SM5703
|
|
if (charger->fled_info == NULL)
|
|
charger->fled_info = sm_fled_get_info_by_name(NULL);
|
|
if (charger->fled_info)
|
|
sm5703_boost_notification(charger->fled_info, 1);
|
|
#else
|
|
sm5703_assign_bits(charger->sm5703->i2c_client,
|
|
SM5703_CNTL, SM5703_OPERATION_MODE_MASK,
|
|
SM5703_OPERATION_MODE_USB_OTG_MODE);
|
|
#endif
|
|
charger->cable_type = POWER_SUPPLY_TYPE_OTG;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_CHARGER_SM5703_SOFT_START_CHARGING
|
|
static int sm5703_get_input_current_limit(struct i2c_client *i2c);
|
|
#endif
|
|
|
|
static void sm5703_enable_charger_switch(struct sm5703_charger_data *charger,
|
|
int onoff)
|
|
{
|
|
#ifdef CONFIG_CHARGER_SM5703_SOFT_START_CHARGING
|
|
int get_input_current=0;
|
|
int data=0;
|
|
#endif
|
|
#ifdef CONFIG_CHARGER_SM5703_DUALPATH
|
|
union power_supply_propval batt_pres;
|
|
#endif
|
|
int prev_charging_status = charger->is_charging;
|
|
|
|
charger->is_charging = onoff ? true : false;
|
|
if ((onoff > 0) && (prev_charging_status == false)) {
|
|
pr_info("%s: turn on charger\n", __func__);
|
|
|
|
#ifdef CONFIG_FLED_SM5703
|
|
if (charger->fled_info == NULL)
|
|
charger->fled_info = sm_fled_get_info_by_name(NULL);
|
|
if (charger->fled_info)
|
|
sm5703_charger_notification(charger->fled_info,1);
|
|
#endif
|
|
#ifdef CONFIG_CHARGER_SM5703_SOFT_START_CHARGING
|
|
get_input_current=sm5703_get_input_current_limit(charger->sm5703->i2c_client);
|
|
mutex_lock(&charger->io_lock);
|
|
data = sm5703_reg_read(charger->sm5703->i2c_client, SM5703_VBUSCNTL);
|
|
data &= ~SM5703_VBUSLIMIT;
|
|
sm5703_reg_write(charger->sm5703->i2c_client, SM5703_VBUSCNTL, data);
|
|
#endif
|
|
sm5703_assign_bits(charger->sm5703->i2c_client,
|
|
SM5703_CNTL, SM5703_OPERATION_MODE_MASK,
|
|
SM5703_OPERATION_MODE_CHARGING_ON);
|
|
|
|
charger->nchgen = false;
|
|
gpio_direction_output(charger->pdata->chgen_gpio,
|
|
charger->nchgen); /* nCHG enable */
|
|
|
|
#ifdef CONFIG_CHARGER_SM5703_SOFT_START_CHARGING
|
|
mdelay(100);
|
|
if (get_input_current > 100) {
|
|
int temp;
|
|
temp = ((get_input_current - 100) / 50) | data;
|
|
sm5703_reg_write(charger->sm5703->i2c_client, SM5703_VBUSCNTL, temp);
|
|
}
|
|
mutex_unlock(&charger->io_lock);
|
|
#endif
|
|
pr_info("%s : STATUS OF CHARGER ON(0)/OFF(1): %d\n",
|
|
__func__, charger->nchgen);
|
|
} else if (onoff == 0) {
|
|
charger->full_charged = false;
|
|
pr_info("%s: turn off charger\n", __func__);
|
|
|
|
charger->charging_current = DEFAULT_CHARGING_CURRENT;
|
|
charger->nchgen = true;
|
|
#ifdef CONFIG_FLED_SM5703
|
|
if (charger->fled_info == NULL)
|
|
charger->fled_info = sm_fled_get_info_by_name(NULL);
|
|
if (charger->fled_info)
|
|
sm5703_charger_notification(charger->fled_info,0);
|
|
#endif
|
|
gpio_direction_output(charger->pdata->chgen_gpio,
|
|
charger->nchgen); /* nCHG disable */
|
|
pr_info("%s : STATUS OF CHARGER ON(0)/OFF(1): %d\n",
|
|
__func__, charger->nchgen);
|
|
#ifdef CONFIG_CHARGER_SM5703_DUALPATH
|
|
psy_do_property("battery", get,
|
|
POWER_SUPPLY_PROP_PRESENT, batt_pres);
|
|
if(batt_pres.intval== false){
|
|
sm5703_assign_bits(charger->sm5703->i2c_client,
|
|
SM5703_CNTL, SM5703_OPERATION_MODE_MASK,
|
|
SM5703_OPERATION_MODE_SUSPEND);
|
|
pr_info("%s: DUALPATH set to SM5703_OPERATION_MODE_SUSPEND \n",__func__);
|
|
}
|
|
#endif
|
|
} else {
|
|
pr_info("%s: repeated to set charger switch(%d), prev stat = %d\n",
|
|
__func__, onoff, prev_charging_status ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
static int sm5703_CHG_set_TOPOFF_TMR(struct sm5703_charger_data *charger,
|
|
unsigned char topoff_timer)
|
|
{
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
|
|
sm5703_assign_bits(i2c,
|
|
SM5703_CHGCNTL5, SM5703_TOPOFF_TIMER_MASK,
|
|
((topoff_timer & SM5703_TOPOFF_TIMER) << SM5703_TOPOFF_TIMER_SHIFT));
|
|
pr_info("TOPOFF_TMR set (timer=%d)\n", topoff_timer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sm5703_enable_autostop(struct sm5703_charger_data *charger,
|
|
int onoff)
|
|
{
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
|
|
pr_info("%s:[BATT] Autostop set(%d)\n", __func__, onoff);
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
|
|
if (onoff)
|
|
sm5703_set_bits(i2c, SM5703_CHGCNTL4, SM5703_AUTOSTOP_MASK);
|
|
else
|
|
sm5703_clr_bits(i2c, SM5703_CHGCNTL4, SM5703_AUTOSTOP_MASK);
|
|
|
|
mutex_unlock(&charger->io_lock);
|
|
}
|
|
|
|
static void sm5703_enable_autoset(struct sm5703_charger_data *charger,
|
|
int onoff)
|
|
{
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
pr_info("%s:[BATT] Autoset set(%d)\n", __func__, onoff);
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
if (onoff)
|
|
sm5703_set_bits(i2c, SM5703_CNTL, SM5703_AUTOSET_MASK);
|
|
else
|
|
sm5703_clr_bits(i2c, SM5703_CNTL, SM5703_AUTOSET_MASK);
|
|
mutex_unlock(&charger->io_lock);
|
|
}
|
|
|
|
static void sm5703_enable_aiclen(struct sm5703_charger_data *charger,
|
|
int onoff)
|
|
{
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
pr_info("%s:[BATT] AICLEN set(%d)\n", __func__, onoff);
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
if (onoff)
|
|
sm5703_set_bits(i2c, SM5703_CHGCNTL5, SM5703_AICLEN_MASK);
|
|
else
|
|
sm5703_clr_bits(i2c, SM5703_CHGCNTL5, SM5703_AICLEN_MASK);
|
|
mutex_unlock(&charger->io_lock);
|
|
}
|
|
|
|
static void sm5703_set_aiclth(struct sm5703_charger_data *charger,
|
|
int aiclth)
|
|
{
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
int data = 0, temp = 0;
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
data = sm5703_reg_read(i2c, SM5703_CHGCNTL5);
|
|
data &= ~SM5703_AICLTH;
|
|
|
|
if (aiclth >= 4900)
|
|
aiclth = 4900;
|
|
|
|
if (aiclth >= 4300){
|
|
temp = (aiclth - 4300)/100;
|
|
data |= temp;
|
|
}
|
|
|
|
sm5703_reg_write(i2c, SM5703_CHGCNTL5, data);
|
|
|
|
data = sm5703_reg_read(i2c, SM5703_CHGCNTL5);
|
|
pr_info("%s : SM5703_CHGCNTL5 (AICHTH) : 0x%02x\n",
|
|
__func__, data);
|
|
mutex_unlock(&charger->io_lock);
|
|
}
|
|
|
|
#if EN_AICL_IRQ
|
|
static void sm5703_set_aicl_irq(struct sm5703_charger_data *charger,
|
|
int mask)
|
|
{
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
int data = 0;
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
data = sm5703_reg_read(i2c, SM5703_INTMSK1);
|
|
data &= 0xFE;
|
|
|
|
if (mask)
|
|
data |= 0x01;
|
|
|
|
sm5703_reg_write(i2c, SM5703_INTMSK1, data);
|
|
|
|
data = sm5703_reg_read(i2c, SM5703_INTMSK1);
|
|
pr_info("%s : SM5703_INTMSK1 (AICH-MASK) : 0x%02x, mask : %d\n",
|
|
__func__, data, mask);
|
|
mutex_unlock(&charger->io_lock);
|
|
}
|
|
#endif
|
|
|
|
static void sm5703_set_freqsel(struct sm5703_charger_data *charger,
|
|
int freqsel_hz)
|
|
{
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
int data = 0;
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
data = sm5703_reg_read(i2c, SM5703_CHGCNTL6);
|
|
data &= ~SM5703_FREQSEL_MASK;
|
|
data |= (freqsel_hz << SM5703_FREQSEL_SHIFT);
|
|
|
|
sm5703_reg_write(i2c, SM5703_CHGCNTL6, data);
|
|
|
|
data = sm5703_reg_read(i2c, SM5703_CHGCNTL6);
|
|
pr_info("%s : SM5703_CHGCNTL6 (FREQSEL) : 0x%02x\n",
|
|
__func__, data);
|
|
mutex_unlock(&charger->io_lock);
|
|
}
|
|
|
|
static void sm5703_set_input_current_limit(struct sm5703_charger_data *charger,
|
|
int current_limit)
|
|
{
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
int data = 0, temp = 0;
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
data = sm5703_reg_read(i2c, SM5703_VBUSCNTL);
|
|
data &= ~SM5703_VBUSLIMIT;
|
|
|
|
if (charger->siop_level < 100 && current_limit >= SIOP_INPUT_LIMIT_CURRENT)
|
|
current_limit = SIOP_INPUT_LIMIT_CURRENT;
|
|
|
|
if (current_limit >= 2100)
|
|
current_limit = 2100;
|
|
|
|
if (charger->current_max < current_limit && charger->is_current_reduced) {
|
|
pr_info("%s: skip set input current limit(%d <--> %d)\n",
|
|
__func__, charger->current_max, current_limit);
|
|
} else {
|
|
if (current_limit > 100) {
|
|
temp = ((current_limit - 100) / 50) | data;
|
|
sm5703_reg_write(i2c, SM5703_VBUSCNTL, temp);
|
|
}
|
|
|
|
data = sm5703_reg_read(i2c, SM5703_VBUSCNTL);
|
|
pr_info("%s : SM5703_VBUSCNTL (Input current limit) : 0x%02x\n",
|
|
__func__, data);
|
|
if (charger->pdata->chg_vbuslimit
|
|
#if EN_AICL_IRQ
|
|
/* check aicl state */
|
|
&& (sm5703_reg_read(i2c, SM5703_STATUS1) & 0x01)
|
|
#endif
|
|
) {
|
|
/* start vbuslimit work */
|
|
wake_lock(&charger->vbuslimit_wake_lock);
|
|
queue_delayed_work_on(0, charger->wq,
|
|
&charger->vbuslimit_work, msecs_to_jiffies(START_VBUSLIMIT_DELAY));
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&charger->io_lock);
|
|
}
|
|
|
|
static int sm5703_get_input_current_limit(struct i2c_client *i2c)
|
|
{
|
|
int ret, current_limit = 0;
|
|
ret = sm5703_reg_read(i2c, SM5703_VBUSCNTL);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret&=SM5703_VBUSLIMIT_MASK;
|
|
current_limit = (100 + (ret*50));
|
|
|
|
return current_limit;
|
|
}
|
|
|
|
static void sm5703_set_regulation_voltage(struct sm5703_charger_data *charger,
|
|
int float_voltage)
|
|
{
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
int data = 0;
|
|
|
|
data = sm5703_reg_read(i2c, SM5703_CHGCNTL3);
|
|
|
|
data &= ~SM5703_BATREG_MASK;
|
|
|
|
if ((float_voltage) <= 4120)
|
|
data = 0x00;
|
|
else if ((float_voltage) >= 4430)
|
|
data = 0x1f;
|
|
else
|
|
data = ((float_voltage - 4120) / 10);
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
sm5703_reg_write(i2c, SM5703_CHGCNTL3, data);
|
|
data = sm5703_reg_read(i2c, SM5703_CHGCNTL3);
|
|
pr_info("%s : SM5703_CHGCNTL3 (Battery regulation voltage) : 0x%02x\n",
|
|
__func__, data);
|
|
mutex_unlock(&charger->io_lock);
|
|
}
|
|
|
|
#if defined(CONFIG_BATTERY_SWELLING) || defined(CONFIG_BATTERY_SWELLING_SELF_DISCHARGING)
|
|
static int sm5703_get_regulation_voltage(struct sm5703_charger_data *charger)
|
|
{
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
int data = 0;
|
|
|
|
data = sm5703_reg_read(i2c, SM5703_CHGCNTL3);
|
|
data &= SM5703_BATREG_MASK;
|
|
|
|
return (4120 + (data * 10));
|
|
}
|
|
#endif
|
|
|
|
static void __sm5703_set_fast_charging_current(struct i2c_client *i2c,
|
|
int charging_current)
|
|
{
|
|
int data = 0;
|
|
|
|
if(charging_current <= 100)
|
|
charging_current = 100;
|
|
else if (charging_current >= 2500)
|
|
charging_current = 2500;
|
|
|
|
data = (charging_current - 100) / 50;
|
|
|
|
sm5703_reg_write(i2c, SM5703_CHGCNTL2, data);
|
|
|
|
data = sm5703_reg_read(i2c, SM5703_CHGCNTL2);
|
|
pr_info("%s : SM5703_CHGCNTL2 (fastchg current) : 0x%02x\n",
|
|
__func__, data);
|
|
}
|
|
|
|
static int sm5703_get_fast_charging_current(struct i2c_client *i2c)
|
|
{
|
|
int data = sm5703_reg_read(i2c, SM5703_CHGCNTL2);
|
|
int charging_current = 0;
|
|
|
|
if (data < 0)
|
|
return data;
|
|
|
|
data &= SM5703_FASTCHG_MASK;
|
|
charging_current = (100 + (data*50));
|
|
|
|
return charging_current;
|
|
}
|
|
|
|
static int sm5703_get_current_topoff_setting(struct sm5703_charger_data *charger)
|
|
{
|
|
int ret, data = 0, topoff_current = 0;
|
|
mutex_lock(&charger->io_lock);
|
|
ret = sm5703_reg_read(charger->sm5703->i2c_client, SM5703_CHGCNTL4);
|
|
mutex_unlock(&charger->io_lock);
|
|
if (ret < 0) {
|
|
pr_info("%s: warning --> fail to read i2c register(%d)\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
data = ((ret & SM5703_TOPOFF_MASK) >> SM5703_TOPOFF_SHIFT);
|
|
topoff_current = (100 + (data*25));
|
|
|
|
return topoff_current;
|
|
}
|
|
|
|
static void __sm5703_set_termination_current_limit(struct i2c_client *i2c,
|
|
int current_limit)
|
|
{
|
|
int data = 0, temp = 0;
|
|
|
|
pr_info("%s : Set Termination\n", __func__);
|
|
|
|
data = sm5703_reg_read(i2c, SM5703_CHGCNTL4);
|
|
data &= ~SM5703_TOPOFF_MASK;
|
|
|
|
if(current_limit <= 100)
|
|
current_limit = 100;
|
|
else if (current_limit >= 475)
|
|
current_limit = 475;
|
|
|
|
temp = (current_limit - 100) / 25;
|
|
data |= (temp << SM5703_TOPOFF_SHIFT);
|
|
|
|
sm5703_reg_write(i2c, SM5703_CHGCNTL4, data);
|
|
|
|
data = sm5703_reg_read(i2c, SM5703_CHGCNTL4);
|
|
pr_info("%s : SM5703_CHGCNTL4 (Top-off current threshold) : 0x%02x\n",
|
|
__func__, data);
|
|
}
|
|
|
|
static void sm5703_set_charging_current(struct sm5703_charger_data *charger, int topoff)
|
|
{
|
|
int adj_current = 0;
|
|
#ifndef CONFIG_DISABLE_MINIMUM_SIOP_CHARGING
|
|
const int usb_charging_current = charger->pdata->charging_current_table[
|
|
POWER_SUPPLY_TYPE_USB].fast_charging_current;
|
|
#endif
|
|
|
|
adj_current = charger->charging_current * charger->siop_level / 100;
|
|
#ifndef CONFIG_DISABLE_MINIMUM_SIOP_CHARGING
|
|
if (adj_current > 0 && adj_current < usb_charging_current)
|
|
adj_current = usb_charging_current;
|
|
#endif
|
|
#if CONFIG_SIOP_CHARGING_LIMIT_CURRENT
|
|
if(charger->siop_level < 100 && adj_current > CONFIG_SIOP_CHARGING_LIMIT_CURRENT)
|
|
adj_current = CONFIG_SIOP_CHARGING_LIMIT_CURRENT;
|
|
#endif
|
|
pr_info("%s adj_current = %dmA charger->siop_level = %d\n",__func__, adj_current,charger->siop_level);
|
|
mutex_lock(&charger->io_lock);
|
|
__sm5703_set_fast_charging_current(charger->sm5703->i2c_client,
|
|
adj_current);
|
|
|
|
__sm5703_set_termination_current_limit(
|
|
charger->sm5703->i2c_client, topoff);
|
|
mutex_unlock(&charger->io_lock);
|
|
}
|
|
|
|
static void sm5703_set_otgcurrent(struct sm5703_charger_data *charger,
|
|
int otg_current)
|
|
{
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
int data = 0;
|
|
|
|
data = sm5703_reg_read(i2c, SM5703_OTGCURRENTCNTL);
|
|
data &= ~SM5703_OTGCURRENT_MASK;
|
|
|
|
if (otg_current <= 500)
|
|
data = 0x00;
|
|
else if (otg_current <= 700)
|
|
data = 0x01;
|
|
else if (otg_current <= 900)
|
|
data = 0x02;
|
|
else
|
|
data = 0x3;
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
sm5703_reg_write(i2c, SM5703_OTGCURRENTCNTL, data);
|
|
data = sm5703_reg_read(i2c, SM5703_OTGCURRENTCNTL);
|
|
pr_info("%s : SM5703_OTGCURRENTCNTL (OTG current) : 0x%02x\n",
|
|
__func__, data);
|
|
mutex_unlock(&charger->io_lock);
|
|
}
|
|
|
|
static void sm5703_set_bst_iq3limit(struct sm5703_charger_data *charger,
|
|
int iq3limit)
|
|
{
|
|
int data = 0;
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
data = sm5703_reg_read(charger->sm5703->i2c_client, SM5703_Q3LIMITCNTL);
|
|
data &= ~SM5703_BST_IQ3LIMIT_MASK;
|
|
data |= (iq3limit << SM5703_BST_IQ3LIMIT_SHIFT);
|
|
|
|
sm5703_reg_write(charger->sm5703->i2c_client, SM5703_Q3LIMITCNTL, data);
|
|
|
|
data = sm5703_reg_read(charger->sm5703->i2c_client, SM5703_Q3LIMITCNTL);
|
|
pr_info("%s : SM5703_Q3LIMITCNTL (BST_IQ3LIMIT) : 0x%02x\n",
|
|
__func__, data);
|
|
mutex_unlock(&charger->io_lock);
|
|
}
|
|
|
|
static void sm5703_configure_charger(struct sm5703_charger_data *charger)
|
|
{
|
|
int topoff;
|
|
union power_supply_propval val, chg_now, swelling_state;
|
|
int full_check_type;
|
|
|
|
pr_info("%s : Set config charging\n", __func__);
|
|
if (charger->charging_current < 0) {
|
|
pr_info("%s : OTG is activated. Ignore command!\n",
|
|
__func__);
|
|
return;
|
|
}
|
|
|
|
psy_do_property("battery", get,
|
|
POWER_SUPPLY_PROP_CHARGE_NOW, val);
|
|
|
|
/* Input current limit */
|
|
pr_info("%s : input current (%dmA)\n",
|
|
__func__, charger->pdata->charging_current_table
|
|
[charger->cable_type].input_current_limit);
|
|
|
|
sm5703_set_input_current_limit(charger,
|
|
charger->pdata->charging_current_table
|
|
[charger->cable_type].input_current_limit);
|
|
|
|
/* Float voltage */
|
|
pr_info("%s : float voltage (%dmV)\n",
|
|
__func__, charger->pdata->chg_float_voltage);
|
|
sm5703_set_regulation_voltage(charger,
|
|
charger->pdata->chg_float_voltage);
|
|
|
|
/* Fast charge and Termination current */
|
|
charger->charging_current = charger->pdata->charging_current_table
|
|
[charger->cable_type].fast_charging_current;
|
|
topoff = charger->pdata->charging_current_table
|
|
[charger->cable_type].full_check_current_1st;
|
|
psy_do_property("battery", get,
|
|
POWER_SUPPLY_PROP_CHARGE_NOW, chg_now);
|
|
|
|
if (chg_now.intval == SEC_BATTERY_CHARGING_1ST)
|
|
full_check_type = charger->pdata->full_check_type;
|
|
else
|
|
full_check_type = charger->pdata->full_check_type_2nd;
|
|
|
|
switch (full_check_type) {
|
|
case SEC_BATTERY_FULLCHARGED_CHGPSY:
|
|
case SEC_BATTERY_FULLCHARGED_FG_CURRENT:
|
|
#if defined(CONFIG_BATTERY_SWELLING)
|
|
psy_do_property("battery", get,
|
|
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, swelling_state);
|
|
#else
|
|
swelling_state.intval = 0;
|
|
#endif
|
|
if (chg_now.intval == SEC_BATTERY_CHARGING_1ST && (!swelling_state.intval)) {
|
|
pr_info("%s : termination current (%dmA)\n",
|
|
__func__, charger->pdata->charging_current_table[
|
|
charger->cable_type].full_check_current_1st);
|
|
|
|
/** Setting 1st termination current as charger termination current*/
|
|
topoff = charger->pdata->charging_current_table
|
|
[charger->cable_type].full_check_current_1st;
|
|
} else {
|
|
pr_info("%s : termination current (%dmA)\n",
|
|
__func__, charger->pdata->charging_current_table[
|
|
charger->cable_type].full_check_current_2nd);
|
|
|
|
if (sm5703_get_charging_status(charger) == POWER_SUPPLY_STATUS_FULL) {
|
|
sm5703_enable_charger_switch(charger, 0);
|
|
charger->charging_current = charger->pdata->charging_current_table
|
|
[charger->cable_type].fast_charging_current;
|
|
}
|
|
/** Setting 2nd termination current as new charger termination current*/
|
|
topoff = charger->pdata->charging_current_table
|
|
[charger->cable_type].full_check_current_2nd;
|
|
}
|
|
break;
|
|
}
|
|
pr_info("%s : fast charging current (%dmA), topoff current (%dmA)\n",
|
|
__func__, charger->charging_current, topoff);
|
|
|
|
sm5703_set_charging_current(charger, topoff);
|
|
sm5703_enable_charger_switch(charger, 1);
|
|
|
|
}
|
|
|
|
int sm5703_chg_fled_init(struct i2c_client *client)
|
|
{
|
|
int ret = 0;//, rev_id;
|
|
#if 0
|
|
sm5703_mfd_chip_t *chip = i2c_get_clientdata(client);
|
|
struct sm5703_charger_data *charger = chip->charger;
|
|
#endif
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(sm5703_chg_fled_init);
|
|
|
|
/* here is set init charger data */
|
|
static bool sm5703_chg_init(struct sm5703_charger_data *charger)
|
|
{
|
|
sm5703_mfd_chip_t *chip = i2c_get_clientdata(charger->sm5703->i2c_client);
|
|
chip->charger = charger;
|
|
sm5703_chg_fled_init(charger->sm5703->i2c_client);
|
|
//int data = 0;
|
|
charger->full_charged = false;
|
|
|
|
/* AUTOSTOP */
|
|
sm5703_enable_autostop(chip->charger, (int)charger->pdata->chg_autostop);
|
|
/* AUTOSET */
|
|
sm5703_enable_autoset(chip->charger, (int)charger->pdata->chg_autoset);
|
|
/* AICLEN */
|
|
sm5703_enable_aiclen(chip->charger, (int)charger->pdata->chg_aiclen);
|
|
/* AICLTH */
|
|
sm5703_set_aiclth(chip->charger, (int)charger->pdata->chg_aiclth);
|
|
/* FREQSEL */
|
|
sm5703_set_freqsel(chip->charger, SM5703_FREQSEL_1P5MHZ);
|
|
|
|
/* Auto-Stop configuration for Emergency status */
|
|
__sm5703_set_termination_current_limit(charger->sm5703->i2c_client, 300);
|
|
sm5703_CHG_set_TOPOFF_TMR(charger, SM5703_TOPOFF_TIMER_45m);
|
|
|
|
/* MUST set correct regulation voltage first
|
|
* Before MUIC pass cable type information to charger
|
|
* charger would be already enabled (default setting)
|
|
* it might cause EOC event by incorrect regulation voltage */
|
|
sm5703_set_regulation_voltage(charger,
|
|
charger->pdata->chg_float_voltage);
|
|
|
|
sm5703_set_otgcurrent(charger, 1200); /* OTGCURRENT : 1.2A */
|
|
|
|
sm5703_set_bst_iq3limit(charger, SM5703_BST_IQ3LIMIT_1X);
|
|
|
|
sm5703_test_read(charger->sm5703->i2c_client);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static int sm5703_get_charging_status(struct sm5703_charger_data *charger)
|
|
{
|
|
int status = POWER_SUPPLY_STATUS_UNKNOWN;
|
|
int chg_status3,chg_status5;
|
|
int nCHG = 0;
|
|
|
|
chg_status3 = sm5703_reg_read(charger->sm5703->i2c_client, SM5703_STATUS3);
|
|
if (chg_status3<0) {
|
|
pr_info("Error : SM5703_STATUS3 can't get charging status (%d)\n", chg_status3);
|
|
}
|
|
pr_info("%s chg_status3 = %d \n",__func__, chg_status3);
|
|
|
|
chg_status5 = sm5703_reg_read(charger->sm5703->i2c_client, SM5703_STATUS5);
|
|
if (chg_status5<0) {
|
|
pr_info("Error : SM5703_STATUS5 can't get charging status (%d)\n", chg_status5);
|
|
}
|
|
|
|
pr_info("%s charger->full_charged = %d, charger->cable_type = %d \n",__func__,charger->full_charged,charger->cable_type);
|
|
|
|
nCHG = gpio_get_value(charger->pdata->chgen_gpio);
|
|
|
|
if(((chg_status3 & SM5703_STATUS3_DONE) || (chg_status3 & SM5703_STATUS3_TOPOFF))
|
|
&& (chg_status5 & SM5703_STATUS5_VBUSOK)
|
|
&& (charger->cable_type != POWER_SUPPLY_TYPE_OTG)
|
|
&& (charger->cable_type != POWER_SUPPLY_TYPE_POWER_SHARING))
|
|
{
|
|
status = POWER_SUPPLY_STATUS_FULL;
|
|
charger->full_charged = true;
|
|
pr_info("%s : Status, Power Supply Full \n", __func__);
|
|
}else if(chg_status3 & SM5703_STATUS3_CHGON){
|
|
status = POWER_SUPPLY_STATUS_CHARGING;
|
|
}else {
|
|
if (nCHG)
|
|
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
|
else
|
|
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
}
|
|
|
|
/* TEMP_TEST : when OTG is enabled(charging_current -1), handle OTG func. */
|
|
if (charger->charging_current < 0) {
|
|
int chg_status1;
|
|
/* For OTG mode, SM5703 would still report "charging" */
|
|
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
|
chg_status1 = sm5703_reg_read(charger->sm5703->i2c_client, SM5703_STATUS1);
|
|
|
|
if (chg_status1 & SM5703_STATUS1_OTGFAIL) {
|
|
pr_info("%s: otg overcurrent limit\n", __func__);
|
|
sm5703_charger_otg_control(charger, false);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static int sm5703_get_charging_health(struct sm5703_charger_data *charger)
|
|
{
|
|
int vbus_status = sm5703_reg_read(charger->sm5703->i2c_client, SM5703_STATUS5);
|
|
int health = POWER_SUPPLY_HEALTH_GOOD;
|
|
int chg_status3;
|
|
int nCHG = 0;
|
|
|
|
chg_status3 = sm5703_reg_read(charger->sm5703->i2c_client, SM5703_STATUS3);
|
|
|
|
pr_info("%s : charger->is_charging = %d, charger->cable_type = %d, is_current_reduced = %d\n",
|
|
__func__, charger->is_charging, charger->cable_type, charger->is_current_reduced);
|
|
|
|
/* temp for test */
|
|
pr_info("%s : vbus_status = %d\n", __func__, vbus_status);
|
|
|
|
if (vbus_status < 0) {
|
|
health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
|
pr_info("%s : Health : %d, vbus_status : %d\n", __func__, health,vbus_status);
|
|
|
|
return (int)health;
|
|
}
|
|
|
|
if (vbus_status & SM5703_STATUS5_VBUSOK)
|
|
health = POWER_SUPPLY_HEALTH_GOOD;
|
|
else if (vbus_status & SM5703_STATUS5_VBUSOVP)
|
|
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
|
else if (vbus_status & SM5703_STATUS5_VBUSUVLO)
|
|
health = POWER_SUPPLY_HEALTH_UNDERVOLTAGE;
|
|
else
|
|
health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
|
|
|
if (health == POWER_SUPPLY_HEALTH_GOOD) {
|
|
/* check if chgen */
|
|
nCHG = gpio_get_value(charger->pdata->chgen_gpio);
|
|
|
|
/* print the log at the abnormal case */
|
|
if ((charger->is_charging == 1) && (chg_status3 & SM5703_STATUS3_DONE) &&
|
|
(nCHG)) {
|
|
sm5703_test_read(charger->sm5703->i2c_client);
|
|
gpio_direction_output(charger->pdata->chgen_gpio,
|
|
!(charger->is_charging)); /* re-enable Charger */
|
|
pr_info("%s : FORCE RE-ENABLE Charger in Fake DONE state\n", __func__);
|
|
}
|
|
}
|
|
|
|
pr_info("%s : Health : %d\n", __func__, health);
|
|
|
|
return (int)health;
|
|
}
|
|
|
|
static int sec_chg_get_property(struct power_supply *psy,
|
|
enum power_supply_property psp,
|
|
union power_supply_propval *val)
|
|
{
|
|
int chg_curr, aicr, vbus_status;
|
|
int data = 0;
|
|
|
|
struct sm5703_charger_data *charger =
|
|
container_of(psy, struct sm5703_charger_data, psy_chg);
|
|
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_ONLINE:
|
|
vbus_status = sm5703_reg_read(charger->sm5703->i2c_client, SM5703_STATUS5);
|
|
if (charger->cable_type != POWER_SUPPLY_TYPE_BATTERY &&
|
|
!(vbus_status & SM5703_STATUS5_VBUSOK))
|
|
charger->cable_type = POWER_SUPPLY_TYPE_BATTERY;
|
|
|
|
val->intval = charger->cable_type;
|
|
pr_info("%s: Charger Cable type : %d\n", __func__, charger->cable_type);
|
|
break;
|
|
case POWER_SUPPLY_PROP_STATUS:
|
|
val->intval = sm5703_get_charging_status(charger);
|
|
break;
|
|
case POWER_SUPPLY_PROP_HEALTH:
|
|
val->intval = sm5703_get_charging_health(charger);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
|
sm5703_test_read(charger->sm5703->i2c_client);
|
|
val->intval = sm5703_get_fast_charging_current(charger->sm5703->i2c_client);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
|
break;
|
|
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
|
if (charger->charging_current) {
|
|
aicr = sm5703_get_input_current_limit(charger->sm5703->i2c_client);
|
|
chg_curr = sm5703_get_fast_charging_current(charger->sm5703->i2c_client);
|
|
val->intval = MINVAL(aicr, chg_curr);
|
|
} else
|
|
val->intval = 0;
|
|
break;
|
|
#if defined(CONFIG_BATTERY_SWELLING) || defined(CONFIG_BATTERY_SWELLING_SELF_DISCHARGING)
|
|
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
|
val->intval = sm5703_get_regulation_voltage(charger);
|
|
break;
|
|
#endif
|
|
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
|
if (!charger->is_charging || charger->cable_type == POWER_SUPPLY_TYPE_BATTERY ||
|
|
charger->cable_type == POWER_SUPPLY_TYPE_OTG) {
|
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
|
} else if (charger->current_max <= SLOW_CHARGING_CURRENT_STANDARD) {
|
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_SLOW;
|
|
pr_info("%s: slow-charging mode\n", __func__);
|
|
} else
|
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
|
|
break;
|
|
case POWER_SUPPLY_PROP_PRESENT:
|
|
val->intval = 0;
|
|
data = sm5703_reg_read(charger->sm5703->i2c_client, SM5703_STATUS2);
|
|
if((data & (1 << 4)) == 0x0)
|
|
val->intval = 1;
|
|
pr_info("%s: batt_present : %d\n", __func__, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL:
|
|
break;
|
|
case POWER_SUPPLY_PROP_ENERGY_NOW:
|
|
return -ENODATA;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sec_chg_set_property(struct power_supply *psy,
|
|
enum power_supply_property psp,
|
|
const union power_supply_propval *val)
|
|
{
|
|
struct sm5703_charger_data *charger =
|
|
container_of(psy, struct sm5703_charger_data, psy_chg);
|
|
|
|
int topoff;
|
|
union power_supply_propval value;
|
|
int previous_cable_type = charger->cable_type;
|
|
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_STATUS:
|
|
charger->status = val->intval;
|
|
break;
|
|
/* val->intval : type */
|
|
case POWER_SUPPLY_PROP_ONLINE:
|
|
charger->cable_type = val->intval;
|
|
if (previous_cable_type != charger->cable_type) {
|
|
charger->current_max = charger->pdata->charging_current_table
|
|
[charger->cable_type].input_current_limit;
|
|
charger->charging_current = charger->pdata->charging_current_table
|
|
[charger->cable_type].fast_charging_current;
|
|
|
|
charger->is_current_reduced = false;
|
|
if (charger->pdata->chg_vbuslimit) {
|
|
wake_unlock(&charger->vbuslimit_wake_lock);
|
|
cancel_delayed_work(&charger->vbuslimit_work);
|
|
}
|
|
#if EN_AICL_IRQ
|
|
sm5703_set_aicl_irq(charger, 0);
|
|
#endif
|
|
}
|
|
|
|
if (val->intval == POWER_SUPPLY_TYPE_POWER_SHARING) {
|
|
psy_do_property("ps", get,
|
|
POWER_SUPPLY_PROP_STATUS, value);
|
|
|
|
sm5703_charger_otg_control(charger, value.intval);
|
|
} else if (charger->cable_type == POWER_SUPPLY_TYPE_BATTERY) {
|
|
pr_info("%s:[BATT] Type Battery\n", __func__);
|
|
/* sm5703_enable_charger_switch(charger, 0); */
|
|
if (previous_cable_type == POWER_SUPPLY_TYPE_OTG)
|
|
sm5703_charger_otg_control(charger, false);
|
|
else
|
|
sm5703_enable_charger_switch(charger, 0);
|
|
/* set default input current */
|
|
charger->current_max = charger->pdata->charging_current_table
|
|
[POWER_SUPPLY_TYPE_USB].input_current_limit;
|
|
charger->is_mdock = false;
|
|
sm5703_set_input_current_limit(charger, charger->current_max);
|
|
} else if (charger->cable_type == POWER_SUPPLY_TYPE_OTG) {
|
|
pr_info("%s: OTG mode\n", __func__);
|
|
//2017.01.06 : If Lanhub cable is changed to OTG cable, needed to disable charger operation.
|
|
pr_info("%s: previous_cable_type = %d, cable_type = %d\n", __func__,previous_cable_type, charger->cable_type);
|
|
if (previous_cable_type == POWER_SUPPLY_TYPE_LAN_HUB)
|
|
{
|
|
pr_info("%s: LAN HUB condition is turned off by charger driver\n", __func__);
|
|
sm5703_enable_charger_switch(charger, 0);
|
|
}
|
|
sm5703_charger_otg_control(charger, true);
|
|
charger->full_charged = false;
|
|
} else {
|
|
pr_info("%s:[BATT] Set charging"
|
|
", Cable type = %d\n", __func__, charger->cable_type);
|
|
/* check mdock */
|
|
if (charger->is_mdock) { /* if mdock was alread inserted, then check OTG, or NOTG state */
|
|
if (charger->cable_type == POWER_SUPPLY_TYPE_SMART_NOTG) {
|
|
charger->charging_current =
|
|
charger->pdata->charging_current_table
|
|
[POWER_SUPPLY_TYPE_MDOCK_TA].fast_charging_current;
|
|
charger->current_max =
|
|
charger->pdata->charging_current_table
|
|
[POWER_SUPPLY_TYPE_MDOCK_TA].input_current_limit;
|
|
} else if (charger->cable_type == POWER_SUPPLY_TYPE_SMART_OTG) {
|
|
charger->charging_current =
|
|
charger->pdata->charging_current_table
|
|
[POWER_SUPPLY_TYPE_MDOCK_TA].fast_charging_current - 500;
|
|
charger->current_max =
|
|
charger->pdata->charging_current_table
|
|
[POWER_SUPPLY_TYPE_MDOCK_TA].input_current_limit - 500;
|
|
}
|
|
} else { /* if mdock wasn't inserted, then check mdock state */
|
|
if (charger->cable_type == POWER_SUPPLY_TYPE_MDOCK_TA) {
|
|
charger->is_mdock = true;
|
|
}
|
|
}
|
|
|
|
//2017.01.06 : If OTG cable is changed to Lanhub cable, needed to disable OTG operation.
|
|
pr_info("%s: previous_cable_type = %d\n", __func__,previous_cable_type);
|
|
if (previous_cable_type == POWER_SUPPLY_TYPE_OTG && charger->cable_type == POWER_SUPPLY_TYPE_LAN_HUB)
|
|
{
|
|
pr_info("%s:OTG condition is turned off by charger driver\n", __func__);
|
|
sm5703_charger_otg_control(charger, false);
|
|
}
|
|
|
|
/* Enable charger */
|
|
sm5703_configure_charger(charger);
|
|
}
|
|
|
|
if (sec_bat_get_slate_mode() == ENABLE) {
|
|
sm5703_enable_charger_switch(charger, false);
|
|
sm5703_assign_bits(charger->sm5703->i2c_client,
|
|
SM5703_CNTL, SM5703_OPERATION_MODE_MASK,
|
|
SM5703_OPERATION_MODE_SUSPEND);
|
|
pr_info("%s: SM5703 OPERATION MODE SUSPEND\n",__func__);
|
|
} else {
|
|
sm5703_enable_charger_switch(charger, charger->is_charging);
|
|
}
|
|
#if EN_TEST_READ
|
|
/* msleep(100); */
|
|
sm5703_test_read(charger->sm5703->i2c_client);
|
|
#endif
|
|
break;
|
|
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
|
#if defined(CONFIG_BATTERY_SWELLING)
|
|
if (val->intval > charger->pdata->charging_current_table
|
|
[charger->cable_type].fast_charging_current) {
|
|
break;
|
|
}
|
|
#endif
|
|
topoff = sm5703_get_current_topoff_setting(charger);
|
|
pr_info("%s:Set chg current = %d mA, topoff = %d mA\n", __func__,
|
|
val->intval, topoff);
|
|
charger->charging_current = val->intval;
|
|
sm5703_set_charging_current(charger, topoff);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
|
/* decrease the charging current according to siop level */
|
|
charger->siop_level = val->intval;
|
|
pr_info("%s:SIOP level = %d, chg current = %d\n", __func__,
|
|
val->intval, charger->charging_current);
|
|
if (charger->is_charging) {
|
|
sm5703_configure_charger(charger);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
|
/* set charging current */
|
|
if (charger->is_charging) {
|
|
sm5703_configure_charger(charger);
|
|
}
|
|
if (sec_bat_get_slate_mode() == ENABLE) {
|
|
sm5703_enable_charger_switch(charger, false);
|
|
sm5703_assign_bits(charger->sm5703->i2c_client,
|
|
SM5703_CNTL, SM5703_OPERATION_MODE_MASK,
|
|
SM5703_OPERATION_MODE_SUSPEND);
|
|
pr_info("%s: SM5703 OPERATION MODE SUSPEND\n",__func__);
|
|
} else {
|
|
sm5703_enable_charger_switch(charger, charger->is_charging);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_POWER_NOW:
|
|
topoff = sm5703_get_current_topoff_setting(charger);
|
|
pr_info("%s:Set Power Now -> chg current = %d mA, topoff = %d mA\n", __func__,
|
|
val->intval, topoff);
|
|
sm5703_set_charging_current(charger, topoff);
|
|
break;
|
|
#if defined(CONFIG_BATTERY_SWELLING) || defined(CONFIG_BATTERY_SWELLING_SELF_DISCHARGING)
|
|
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
|
pr_info("%s: float voltage(%d)\n", __func__, val->intval);
|
|
charger->pdata->chg_float_voltage = val->intval;
|
|
sm5703_set_regulation_voltage(charger, val->intval);
|
|
break;
|
|
#endif
|
|
case POWER_SUPPLY_PROP_HEALTH:
|
|
/* charger->ovp = val->intval; */
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL:
|
|
sm5703_charger_otg_control(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_PROP_ENERGY_NOW:
|
|
/* Switch-off charger if JIG is connected */
|
|
if (val->intval) {
|
|
pr_info("%s: JIG Connection status: %d \n", __func__,
|
|
val->intval);
|
|
sm5703_enable_charger_switch(charger, false);
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sm5703_otg_get_property(struct power_supply *psy,
|
|
enum power_supply_property psp,
|
|
union power_supply_propval *val)
|
|
{
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_ONLINE:
|
|
val->intval = otg_enable_flag;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sm5703_otg_set_property(struct power_supply *psy,
|
|
enum power_supply_property psp,
|
|
const union power_supply_propval *val)
|
|
{
|
|
struct sm5703_charger_data *charger =
|
|
container_of(psy, struct sm5703_charger_data, psy_otg);
|
|
union power_supply_propval value;
|
|
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_ONLINE:
|
|
value.intval = val->intval;
|
|
pr_info("%s: OTG %s\n", __func__, value.intval > 0 ? "on" : "off");
|
|
psy_do_property(charger->pdata->charger_name, set,
|
|
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ssize_t sm5703_chg_show_attrs(struct device *dev,
|
|
const ptrdiff_t offset, char *buf)
|
|
{
|
|
struct power_supply *psy = dev_get_drvdata(dev);
|
|
struct sm5703_charger_data *charger =
|
|
container_of(psy, struct sm5703_charger_data, psy_chg);
|
|
int i = 0;
|
|
char *str = NULL;
|
|
|
|
switch (offset) {
|
|
case CHG_REG:
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "%x\n",
|
|
charger->reg_addr);
|
|
break;
|
|
case CHG_DATA:
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "%x\n",
|
|
charger->reg_data);
|
|
break;
|
|
case CHG_REGS:
|
|
str = kzalloc(sizeof(char) * 256, GFP_KERNEL);
|
|
if (!str)
|
|
return -ENOMEM;
|
|
|
|
sm5703_read_regs(charger->sm5703->i2c_client, str);
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n",
|
|
str);
|
|
|
|
kfree(str);
|
|
break;
|
|
default:
|
|
i = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
ssize_t sm5703_chg_store_attrs(struct device *dev,
|
|
const ptrdiff_t offset,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct power_supply *psy = dev_get_drvdata(dev);
|
|
struct sm5703_charger_data *charger =
|
|
container_of(psy, struct sm5703_charger_data, psy_chg);
|
|
|
|
int ret = 0;
|
|
int x = 0;
|
|
uint8_t data = 0;
|
|
|
|
switch (offset) {
|
|
case CHG_REG:
|
|
if (sscanf(buf, "%x\n", &x) == 1) {
|
|
charger->reg_addr = x;
|
|
data = sm5703_reg_read(charger->sm5703->i2c_client,
|
|
charger->reg_addr);
|
|
charger->reg_data = data;
|
|
dev_dbg(dev, "%s: (read) addr = 0x%x, data = 0x%x\n",
|
|
__func__, charger->reg_addr, charger->reg_data);
|
|
ret = count;
|
|
}
|
|
break;
|
|
case CHG_DATA:
|
|
if (sscanf(buf, "%x\n", &x) == 1) {
|
|
data = (u8)x;
|
|
|
|
dev_dbg(dev, "%s: (write) addr = 0x%x, data = 0x%x\n",
|
|
__func__, charger->reg_addr, data);
|
|
ret = sm5703_reg_write(charger->sm5703->i2c_client,
|
|
charger->reg_addr, data);
|
|
if (ret < 0) {
|
|
dev_dbg(dev, "I2C write fail Reg0x%x = 0x%x\n",
|
|
(int)charger->reg_addr, (int)data);
|
|
}
|
|
ret = count;
|
|
}
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct sm5703_chg_irq_handler {
|
|
char *name;
|
|
int irq_index;
|
|
irqreturn_t (*handler)(int irq, void *data);
|
|
};
|
|
#if EN_NOBAT_IRQ
|
|
static irqreturn_t sm5703_chg_nobat_irq_handler(int irq, void *data)
|
|
{
|
|
struct sm5703_charger_data *info = data;
|
|
struct i2c_client *iic = info->sm5703->i2c_client;
|
|
|
|
/* set full charged flag
|
|
* until TA/USB unplug event / stop charging by PSY
|
|
*/
|
|
|
|
pr_info("%s : Nobat\n", __func__);
|
|
|
|
#if EN_TEST_READ
|
|
sm5703_test_read(iic);
|
|
#endif
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif /*EN_NOBAT_IRQ*/
|
|
|
|
#if EN_DONE_IRQ
|
|
static irqreturn_t sm5703_chg_done_irq_handler(int irq, void *data)
|
|
{
|
|
struct sm5703_charger_data *info = data;
|
|
struct i2c_client *iic = info->sm5703->i2c_client;
|
|
|
|
/* set full charged flag
|
|
* until TA/USB unplug event / stop charging by PSY
|
|
*/
|
|
|
|
pr_info("%s : Full charged(done)\n", __func__);
|
|
info->full_charged = true;
|
|
|
|
/* nCHG pin toggle */
|
|
gpio_direction_output(info->pdata->chgen_gpio, info->is_charging);
|
|
msleep(10);
|
|
/* Removed so to be able to check top-off timer at the factory */
|
|
/* gpio_direction_output(info->pdata->chgen_gpio, !(info->is_charging)); */
|
|
|
|
#if EN_TEST_READ
|
|
sm5703_test_read(iic);
|
|
#endif
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif/*EN_DONE_IRQ*/
|
|
|
|
#if EN_TOPOFF_IRQ
|
|
static irqreturn_t sm5703_chg_topoff_irq_handler(int irq, void *data)
|
|
{
|
|
struct sm5703_charger_data *info = data;
|
|
struct i2c_client *iic = info->sm5703->i2c_client;
|
|
|
|
/* set full charged flag
|
|
* until TA/USB unplug event / stop charging by PSY
|
|
*/
|
|
|
|
pr_info("%s : Full charged(topoff)\n", __func__);
|
|
info->full_charged = true;
|
|
|
|
#if EN_TEST_READ
|
|
sm5703_test_read(iic);
|
|
#endif
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif /*EN_TOPOFF_IRQ*/
|
|
|
|
#if EN_CHGON_IRQ
|
|
static irqreturn_t sm5703_chg_chgon_irq_handler(int irq, void *data)
|
|
{
|
|
struct sm5703_charger_data *info = data;
|
|
struct i2c_client *iic = info->sm5703->i2c_client;
|
|
|
|
pr_info("%s : Chgon\n", __func__);
|
|
|
|
#if EN_TEST_READ
|
|
sm5703_test_read(iic);
|
|
#endif
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif /*EN_CHGON_IRQ*/
|
|
|
|
#if EN_OTGFAIL_IRQ
|
|
static irqreturn_t sm5703_chg_otgfail_irq_handler(int irq, void *data)
|
|
{
|
|
struct sm5703_charger_data *info = data;
|
|
/* struct i2c_client *iic = info->sm5703->i2c_client; */
|
|
int ret;
|
|
|
|
#ifdef CONFIG_USB_HOST_NOTIFY
|
|
struct otg_notify *o_notify;
|
|
|
|
o_notify = get_otg_notify();
|
|
#endif
|
|
pr_info("%s : OTG Failed\n", __func__);
|
|
|
|
ret = sm5703_reg_read(info->sm5703->i2c_client, SM5703_STATUS1);
|
|
if (ret & SM5703_STATUS1_OTGFAIL) {
|
|
pr_info("%s: otg overcurrent limit\n", __func__);
|
|
#ifdef CONFIG_USB_HOST_NOTIFY
|
|
send_otg_notify(o_notify, NOTIFY_EVENT_OVERCURRENT, 0);
|
|
#endif
|
|
sm5703_charger_otg_control(info, false);
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif /*EN_CHGON_IRQ*/
|
|
|
|
static void sm5703_chg_vbuslimit_work(struct work_struct *work)
|
|
{
|
|
struct sm5703_charger_data *charger =
|
|
container_of(work, struct sm5703_charger_data, vbuslimit_work.work);
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
|
|
if (charger->cable_type != POWER_SUPPLY_TYPE_BATTERY) {
|
|
int vbuslimit_state;
|
|
|
|
vbuslimit_state = sm5703_reg_read(i2c, SM5703_STATUS1) & 0x08;
|
|
if (vbuslimit_state || (charger->current_max <= MINIMUM_INPUT_CURRENT)) {
|
|
/* check slow charging */
|
|
if (charger->is_current_reduced &&
|
|
charger->current_max <= SLOW_CHARGING_CURRENT_STANDARD) {
|
|
union power_supply_propval value;
|
|
psy_do_property("battery", set,
|
|
POWER_SUPPLY_PROP_CHARGE_TYPE, value);
|
|
pr_info("%s: slow charging on : input current(%dmA), cable type(%d)\n",
|
|
__func__, charger->current_max, charger->cable_type);
|
|
}
|
|
wake_unlock(&charger->vbuslimit_wake_lock);
|
|
} else {
|
|
/* reduce input current & restart vbuslimit work */
|
|
int reg_data, temp;
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
charger->is_current_reduced = true;
|
|
|
|
charger->current_max -= REDUCE_CURRENT_STEP;
|
|
reg_data = sm5703_reg_read(i2c, SM5703_VBUSCNTL);
|
|
reg_data &= ~SM5703_VBUSLIMIT;
|
|
temp = ((charger->current_max - 100) / 50) | reg_data;
|
|
sm5703_reg_write(i2c, SM5703_VBUSCNTL, temp);
|
|
pr_info("%s: reduce input current(%d)\n", __func__, charger->current_max);
|
|
mutex_unlock(&charger->io_lock);
|
|
|
|
queue_delayed_work_on(0, charger->wq,
|
|
&charger->vbuslimit_work, msecs_to_jiffies(VBUSLIMIT_DELAY));
|
|
}
|
|
pr_info("%s: vbuslimit state(%d)\n", __func__, vbuslimit_state);
|
|
} else {
|
|
wake_unlock(&charger->vbuslimit_wake_lock);
|
|
}
|
|
}
|
|
|
|
#if EN_VBUSLIMIT_IRQ
|
|
static irqreturn_t sm5703_chg_vbuslimit_irq_handler(int irq, void *data)
|
|
{
|
|
struct sm5703_charger_data *charger = data;
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
|
|
pr_info("%s: VBUS Limit\n", __func__);
|
|
|
|
#if EN_TEST_READ
|
|
sm5703_test_read(i2c);
|
|
#endif
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif /* EN_VBUSLIMIT_IRQ */
|
|
|
|
#if EN_AICL_IRQ
|
|
static irqreturn_t sm5703_chg_aicl_irq_handler(int irq, void *data)
|
|
{
|
|
struct sm5703_charger_data *charger = data;
|
|
struct i2c_client *i2c = charger->sm5703->i2c_client;
|
|
|
|
pr_info("%s: AICL\n", __func__);
|
|
|
|
sm5703_set_aicl_irq(charger, 1);
|
|
|
|
if (charger->pdata->chg_vbuslimit &&
|
|
charger->cable_type != POWER_SUPPLY_TYPE_BATTERY) {
|
|
/* start vbuslimit work */
|
|
wake_lock(&charger->vbuslimit_wake_lock);
|
|
queue_delayed_work_on(0, charger->wq,
|
|
&charger->vbuslimit_work, msecs_to_jiffies(START_VBUSLIMIT_DELAY));
|
|
}
|
|
|
|
#if EN_TEST_READ
|
|
sm5703_test_read(i2c);
|
|
#endif
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif /* EN_AICL_IRQ */
|
|
|
|
const struct sm5703_chg_irq_handler sm5703_chg_irq_handlers[] = {
|
|
#if EN_NOBAT_IRQ
|
|
{
|
|
.name = "NOBAT",
|
|
.handler = sm5703_chg_nobat_irq_handler,
|
|
.irq_index = SM5703_NOBAT_IRQ,
|
|
},
|
|
#endif /*EN_NOBAT_IRQ*/
|
|
#if EN_DONE_IRQ
|
|
{
|
|
.name = "DONE",
|
|
.handler = sm5703_chg_done_irq_handler,
|
|
.irq_index = SM5703_DONE_IRQ,
|
|
},
|
|
#endif/*EN_DONE_IRQ*/
|
|
#if EN_TOPOFF_IRQ
|
|
{
|
|
.name = "TOPOFF",
|
|
.handler = sm5703_chg_topoff_irq_handler,
|
|
.irq_index = SM5703_TOPOFF_IRQ,
|
|
},
|
|
#endif /*EN_TOPOFF_IRQ*/
|
|
#if EN_CHGON_IRQ
|
|
{
|
|
.name = "CHGON",
|
|
.handler = sm5703_chg_chgon_irq_handler,
|
|
.irq_index = SM5703_CHGON_IRQ,
|
|
},
|
|
#endif /*EN_CHGON_IRQ*/
|
|
#if EN_OTGFAIL_IRQ
|
|
{
|
|
.name = "OTGFAIL",
|
|
.handler = sm5703_chg_otgfail_irq_handler,
|
|
.irq_index = SM5703_OTGFAIL_IRQ,
|
|
},
|
|
#endif /* EN_OTGFAIL_IRQ */
|
|
#if EN_VBUSLIMIT_IRQ
|
|
{
|
|
.name = "VBUSLIMIT",
|
|
.handler = sm5703_chg_vbuslimit_irq_handler,
|
|
.irq_index = SM5703_VBUSLIMIT_IRQ,
|
|
},
|
|
#endif /* EN_VBUSLIMIT_IRQ */
|
|
#if EN_AICL_IRQ
|
|
{
|
|
.name = "AICL",
|
|
.handler = sm5703_chg_aicl_irq_handler,
|
|
.irq_index = SM5703_AICL_IRQ,
|
|
},
|
|
#endif /* EN_AICL_IRQ */
|
|
};
|
|
|
|
|
|
static int register_irq(struct platform_device *pdev,
|
|
struct sm5703_charger_data *info)
|
|
{
|
|
int irq;
|
|
int i, j;
|
|
int ret;
|
|
const struct sm5703_chg_irq_handler *irq_handler = sm5703_chg_irq_handlers;
|
|
const char *irq_name;
|
|
for (i = 0; i < ARRAY_SIZE(sm5703_chg_irq_handlers); i++) {
|
|
irq_name = sm5703_get_irq_name_by_index(irq_handler[i].irq_index);
|
|
irq = platform_get_irq_byname(pdev, irq_name);
|
|
ret = request_threaded_irq(irq, NULL, irq_handler[i].handler,
|
|
IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
|
|
IRQF_NO_SUSPEND, irq_name, info);
|
|
if (ret < 0) {
|
|
pr_err("%s : Failed to request IRQ (%s): #%d: %d\n",
|
|
__func__, irq_name, irq, ret);
|
|
goto err_irq;
|
|
}
|
|
|
|
pr_info("%s : Register IRQ%d(%s) successfully\n",
|
|
__func__, irq, irq_name);
|
|
}
|
|
|
|
return 0;
|
|
err_irq:
|
|
for (j = 0; j < i; j++) {
|
|
irq_name = sm5703_get_irq_name_by_index(irq_handler[j].irq_index);
|
|
irq = platform_get_irq_byname(pdev, irq_name);
|
|
free_irq(irq, info);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void unregister_irq(struct platform_device *pdev,
|
|
struct sm5703_charger_data *info)
|
|
{
|
|
int irq;
|
|
int i;
|
|
const char *irq_name;
|
|
const struct sm5703_chg_irq_handler *irq_handler = sm5703_chg_irq_handlers;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(sm5703_chg_irq_handlers); i++) {
|
|
irq_name = sm5703_get_irq_name_by_index(irq_handler[i].irq_index);
|
|
irq = platform_get_irq_byname(pdev, irq_name);
|
|
free_irq(irq, info);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
|
|
static int sec_bat_read_u32_index_dt(const struct device_node *np,
|
|
const char *propname,
|
|
u32 index, u32 *out_value)
|
|
{
|
|
struct property *prop = of_find_property(np, propname, NULL);
|
|
u32 len = (index + 1) * sizeof(*out_value);
|
|
|
|
if (!prop)
|
|
return (-EINVAL);
|
|
if (!prop->value)
|
|
return (-ENODATA);
|
|
if (len > prop->length)
|
|
return (-EOVERFLOW);
|
|
|
|
*out_value = be32_to_cpup(((__be32 *)prop->value) + index);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sm5703_charger_parse_dt(struct device *dev,
|
|
struct sm5703_charger_platform_data *pdata)
|
|
{
|
|
struct device_node *np = dev->of_node;
|
|
const u32 *p;
|
|
int ret, i, len;
|
|
|
|
/* chg_autostop */
|
|
ret = of_property_read_u32(np, "chg_autostop",
|
|
&pdata->chg_autostop);
|
|
if (ret < 0) {
|
|
pr_info("%s : cannot get chg autostop\n", __func__);
|
|
return -ENODATA;
|
|
}
|
|
/* chg_autoset */
|
|
ret = of_property_read_u32(np, "chg_autoset",
|
|
&pdata->chg_autoset);
|
|
if (ret < 0) {
|
|
pr_info("%s : cannot get chg autoset\n", __func__);
|
|
return -ENODATA;
|
|
}
|
|
/* chg_aiclen */
|
|
ret = of_property_read_u32(np, "chg_aiclen",
|
|
&pdata->chg_aiclen);
|
|
if (ret < 0) {
|
|
pr_info("%s : cannot get chg aiclen\n", __func__);
|
|
return -ENODATA;
|
|
}
|
|
/* chg_aiclth */
|
|
ret = of_property_read_u32(np, "chg_aiclth",
|
|
&pdata->chg_aiclth);
|
|
if (ret < 0) {
|
|
pr_info("%s : cannot get chg aiclth\n", __func__);
|
|
pdata->chg_aiclth = 4500;
|
|
return -ENODATA;
|
|
}
|
|
|
|
/* fg_vol_val */
|
|
ret = of_property_read_u32(np, "fg_vol_val",
|
|
&pdata->fg_vol_val);
|
|
if (ret < 0) {
|
|
pr_info("%s : cannot get fg_vol_val\n", __func__);
|
|
return -ENODATA;
|
|
}
|
|
|
|
/* fg_soc_val */
|
|
ret = of_property_read_u32(np, "fg_soc_val",
|
|
&pdata->fg_soc_val);
|
|
if (ret < 0) {
|
|
pr_info("%s : cannot get fg_soc_val\n", __func__);
|
|
return -ENODATA;
|
|
}
|
|
|
|
/* fg_curr_avr_val */
|
|
ret = of_property_read_u32(np, "fg_curr_avr_val",
|
|
&pdata->fg_curr_avr_val);
|
|
if (ret < 0) {
|
|
pr_info("%s : cannot get fg_curr_avr_val\n", __func__);
|
|
return -ENODATA;
|
|
}
|
|
|
|
np = of_find_node_by_name(NULL, "battery");
|
|
if (!np) {
|
|
pr_info("%s : np NULL\n", __func__);
|
|
return -ENODATA;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,full_check_type",
|
|
&pdata->full_check_type);
|
|
pr_info("%s full_check_type: %d\n", __func__, pdata->full_check_type);
|
|
if (ret < 0)
|
|
pr_err("%s error reading battery,full_check_type %d\n", __func__, ret);
|
|
|
|
ret = of_property_read_u32(np, "battery,full_check_type_2nd",
|
|
&pdata->full_check_type_2nd);
|
|
pr_info("%s full_check_type_2nd: %d\n", __func__, pdata->full_check_type_2nd);
|
|
if (ret < 0)
|
|
pr_err("%s error reading battery,full_check_type_2nd %d\n", __func__, ret);
|
|
|
|
np = of_find_node_by_name(NULL, "charger");
|
|
if (!np) {
|
|
pr_info("%s : np NULL\n", __func__);
|
|
return -ENODATA;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,chg_vbuslimit", &pdata->chg_vbuslimit);
|
|
if (ret < 0) {
|
|
pr_info("%s : cannot get chg vbuslimit\n", __func__);
|
|
pdata->chg_vbuslimit = 0;
|
|
}
|
|
|
|
p = of_get_property(np, "battery,input_current_limit", &len);
|
|
|
|
len = len / sizeof(u32);
|
|
|
|
pdata->charging_current_table =
|
|
kzalloc(sizeof(sec_charging_current_t) * len, GFP_KERNEL);
|
|
|
|
for(i = 0; i < len; i++) {
|
|
ret = sec_bat_read_u32_index_dt(np,
|
|
"battery,input_current_limit", i,
|
|
&pdata->charging_current_table[i].input_current_limit);
|
|
ret = sec_bat_read_u32_index_dt(np,
|
|
"battery,fast_charging_current", i,
|
|
&pdata->charging_current_table[i].fast_charging_current);
|
|
ret = sec_bat_read_u32_index_dt(np,
|
|
"battery,full_check_current_1st", i,
|
|
&pdata->charging_current_table[i].full_check_current_1st);
|
|
ret = sec_bat_read_u32_index_dt(np,
|
|
"battery,full_check_current_2nd", i,
|
|
&pdata->charging_current_table[i].full_check_current_2nd);
|
|
}
|
|
|
|
/* battery,chg_float_voltage */
|
|
ret = of_property_read_u32(np, "battery,chg_float_voltage",
|
|
&pdata->chg_float_voltage);
|
|
if (ret < 0) {
|
|
pr_info("%s : cannot get chg float voltage\n", __func__);
|
|
return -ENODATA;
|
|
}
|
|
|
|
ret = of_property_read_string(np,
|
|
"battery,charger_name", (char const **)&pdata->charger_name);
|
|
if (ret) {
|
|
pdata->charger_name = "sm5703-charger";
|
|
pr_info("%s: Charger name is Empty. Set default.\n", __func__);
|
|
}
|
|
|
|
pdata->chgen_gpio = of_get_named_gpio(np, "battery,chg_gpio_en", 0);
|
|
if (pdata->chgen_gpio < 0) {
|
|
pr_err("%s : cannot get chgen gpio : %d\n",
|
|
__func__, pdata->chgen_gpio);
|
|
return -ENODATA;
|
|
} else {
|
|
pr_info("%s: chgen gpio : %d\n", __func__, pdata->chgen_gpio);
|
|
}
|
|
|
|
dev_info(dev,"sm5703 charger parse dt retval = %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static struct of_device_id sm5703_charger_match_table[] = {
|
|
{ .compatible = "siliconmitus,sm5703-charger",},
|
|
{},
|
|
};
|
|
#else
|
|
static int sm5703_charger_parse_dt(struct device *dev,
|
|
struct sm5703_charger_platform_data *pdata)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
#define sm5703_charger_match_table NULL
|
|
#endif /* CONFIG_OF */
|
|
|
|
static int sm5703_charger_probe(struct platform_device *pdev)
|
|
{
|
|
sm5703_mfd_chip_t *chip = dev_get_drvdata(pdev->dev.parent);
|
|
struct sm5703_mfd_platform_data *mfd_pdata = dev_get_platdata(chip->dev);
|
|
struct sm5703_charger_data *charger;
|
|
int ret = 0;
|
|
|
|
otg_enable_flag = 0;
|
|
|
|
pr_info("%s:[BATT] SM5703 Charger driver probe..\n", __func__);
|
|
|
|
charger = kzalloc(sizeof(*charger), GFP_KERNEL);
|
|
if (!charger)
|
|
return -ENOMEM;
|
|
|
|
#ifdef CONFIG_OF
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0))
|
|
if (pdev->dev.parent->of_node) {
|
|
pdev->dev.of_node = of_find_compatible_node(
|
|
of_node_get(pdev->dev.parent->of_node), NULL,
|
|
sm5703_charger_match_table[0].compatible);
|
|
}
|
|
#endif
|
|
#endif
|
|
mutex_init(&charger->io_lock);
|
|
charger->wq = create_workqueue("sm5703chg_workqueue");
|
|
charger->sm5703= chip;
|
|
/* if (pdev->dev.of_node) */
|
|
if(1)
|
|
{
|
|
charger->pdata = devm_kzalloc(&pdev->dev, sizeof(*(charger->pdata)), GFP_KERNEL);
|
|
if (!charger->pdata) {
|
|
dev_err(&pdev->dev, "Failed to allocate memory\n");
|
|
ret = -ENOMEM;
|
|
goto err_parse_dt_nomem;
|
|
}
|
|
ret = sm5703_charger_parse_dt(&pdev->dev, charger->pdata);
|
|
if (ret < 0)
|
|
goto err_parse_dt;
|
|
} else
|
|
charger->pdata = mfd_pdata->charger_platform_data;
|
|
|
|
platform_set_drvdata(pdev, charger);
|
|
|
|
if (charger->pdata->charger_name == NULL)
|
|
charger->pdata->charger_name = "sm5703-charger";
|
|
|
|
charger->psy_chg.name = charger->pdata->charger_name;
|
|
charger->psy_chg.type = POWER_SUPPLY_TYPE_UNKNOWN;
|
|
charger->psy_chg.get_property = sec_chg_get_property;
|
|
charger->psy_chg.set_property = sec_chg_set_property;
|
|
charger->psy_chg.properties = sec_charger_props;
|
|
charger->psy_chg.num_properties = ARRAY_SIZE(sec_charger_props);
|
|
charger->psy_otg.name = "otg";
|
|
charger->psy_otg.type = POWER_SUPPLY_TYPE_OTG;
|
|
charger->psy_otg.get_property = sm5703_otg_get_property;
|
|
charger->psy_otg.set_property = sm5703_otg_set_property;
|
|
charger->psy_otg.properties = sm5703_otg_props;
|
|
charger->psy_otg.num_properties = ARRAY_SIZE(sm5703_otg_props);
|
|
|
|
charger->siop_level = 100;
|
|
charger->ovp = 0;
|
|
charger->is_mdock = false;
|
|
sm5703_chg_init(charger);
|
|
|
|
if (charger->pdata->chg_vbuslimit) {
|
|
INIT_DELAYED_WORK(&charger->vbuslimit_work, sm5703_chg_vbuslimit_work);
|
|
wake_lock_init(&charger->vbuslimit_wake_lock, WAKE_LOCK_SUSPEND, "sm5703-vbuslimit");
|
|
}
|
|
|
|
ret = power_supply_register(&pdev->dev, &charger->psy_chg);
|
|
if (ret) {
|
|
pr_err("%s: Failed to Register psy_chg\n", __func__);
|
|
goto err_power_supply_register;
|
|
}
|
|
|
|
ret = power_supply_register(&pdev->dev, &charger->psy_otg);
|
|
if (ret) {
|
|
pr_err("%s: Failed to Register otg_chg\n", __func__);
|
|
goto err_power_supply_register_otg;
|
|
}
|
|
|
|
ret = register_irq(pdev, charger);
|
|
if (ret < 0)
|
|
goto err_reg_irq;
|
|
|
|
ret = gpio_request(charger->pdata->chgen_gpio, "sm5703_nCHGEN");
|
|
if (ret) {
|
|
pr_info("%s : Request GPIO %d failed\n",
|
|
__func__, (int)charger->pdata->chgen_gpio);
|
|
}
|
|
|
|
sm5703_test_read(charger->sm5703->i2c_client);
|
|
pr_info("%s:[BATT] SM5703 charger driver loaded OK\n", __func__);
|
|
|
|
return 0;
|
|
err_reg_irq:
|
|
power_supply_unregister(&charger->psy_otg);
|
|
err_power_supply_register_otg:
|
|
power_supply_unregister(&charger->psy_chg);
|
|
err_power_supply_register:
|
|
if (charger->pdata->chg_vbuslimit) {
|
|
wake_lock_destroy(&charger->vbuslimit_wake_lock);
|
|
}
|
|
err_parse_dt:
|
|
err_parse_dt_nomem:
|
|
destroy_workqueue(charger->wq);
|
|
mutex_destroy(&charger->io_lock);
|
|
kfree(charger);
|
|
return ret;
|
|
}
|
|
|
|
static int sm5703_charger_remove(struct platform_device *pdev)
|
|
{
|
|
struct sm5703_charger_data *charger =
|
|
platform_get_drvdata(pdev);
|
|
unregister_irq(pdev, charger);
|
|
power_supply_unregister(&charger->psy_chg);
|
|
destroy_workqueue(charger->wq);
|
|
if (charger->pdata->chg_vbuslimit) {
|
|
wake_lock_destroy(&charger->vbuslimit_wake_lock);
|
|
}
|
|
mutex_destroy(&charger->io_lock);
|
|
kfree(charger);
|
|
return 0;
|
|
}
|
|
|
|
#if defined CONFIG_PM
|
|
static int sm5703_charger_suspend(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sm5703_charger_resume(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
#else
|
|
#define sm5703_charger_suspend NULL
|
|
#define sm5703_charger_resume NULL
|
|
#endif
|
|
|
|
static void sm5703_charger_shutdown(struct device *dev)
|
|
{
|
|
pr_info("%s: SM5703 Charger driver shutdown\n", __func__);
|
|
}
|
|
|
|
static SIMPLE_DEV_PM_OPS(sm5703_charger_pm_ops, sm5703_charger_suspend,
|
|
sm5703_charger_resume);
|
|
|
|
static struct platform_driver sm5703_charger_driver = {
|
|
.driver = {
|
|
.name = "sm5703-charger",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = sm5703_charger_match_table,
|
|
.pm = &sm5703_charger_pm_ops,
|
|
.shutdown = sm5703_charger_shutdown,
|
|
},
|
|
.probe = sm5703_charger_probe,
|
|
.remove = sm5703_charger_remove,
|
|
};
|
|
|
|
static int __init sm5703_charger_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
pr_info("%s \n", __func__);
|
|
ret = platform_driver_register(&sm5703_charger_driver);
|
|
|
|
return ret;
|
|
}
|
|
device_initcall(sm5703_charger_init);
|
|
|
|
static void __exit sm5703_charger_exit(void)
|
|
{
|
|
platform_driver_unregister(&sm5703_charger_driver);
|
|
}
|
|
module_exit(sm5703_charger_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("Charger driver for SM5703");
|