You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
kernel_samsung_sm7125/drivers/battery/charger/sm5714_charger_oper.c

435 lines
14 KiB

/*
* sm5714-charger.c - SM5714 Charger operation mode control module.
*
* Copyright (C) 2017 Samsung Electronics Co.Ltd
*
* 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/slab.h>
#include <linux/delay.h>
#include <linux/mfd/sm/sm5714/sm5714.h>
#include <linux/mfd/sm/sm5714/sm5714-private.h>
#include <linux/usb/typec/common/pdic_notifier.h>
#include "../common/sec_charging_common.h"
enum {
OP_MODE_SUSPEND = 0x0,
OP_MODE_CHG_ON_VBUS = 0x5,
OP_MODE_USB_OTG = 0x7,
OP_MODE_FLASH_BOOST = 0x8,
};
enum {
BSTOUT_4400mV = 0x0,
BSTOUT_4500mV = 0x1,
BSTOUT_4600mV = 0x2,
BSTOUT_4700mV = 0x3,
BSTOUT_4800mV = 0x4,
BSTOUT_5000mV = 0x5,
BSTOUT_5100mV = 0x6,
BSTOUT_5200mV = 0x7,
BSTOUT_5300mV = 0x9,
BSTOUT_5400mV = 0xA,
BSTOUT_5500mV = 0xB,
};
enum {
OTG_CURRENT_500mA = 0x0,
OTG_CURRENT_900mA = 0x1,
OTG_CURRENT_1200mA = 0x2,
OTG_CURRENT_1500mA = 0x3,
};
#define make_OP_STATUS(vbus, otg, pwr_shar, flash, torch, suspend) \
(((vbus & 0x1) << SM5714_CHARGER_OP_EVENT_VBUSIN) | \
((otg & 0x1) << SM5714_CHARGER_OP_EVENT_USB_OTG) | \
((pwr_shar & 0x1) << SM5714_CHARGER_OP_EVENT_PWR_SHAR) | \
((flash & 0x1) << SM5714_CHARGER_OP_EVENT_FLASH) | \
((torch & 0x1) << SM5714_CHARGER_OP_EVENT_TORCH) | \
((suspend & 0x1) << SM5714_CHARGER_OP_EVENT_SUSPEND))
struct sm5714_charger_oper_table_info {
unsigned short status;
unsigned char oper_mode;
unsigned char BST_OUT;
unsigned char OTG_CURRENT;
};
struct sm5714_charger_oper_info {
struct i2c_client *i2c;
struct mutex op_mutex;
int max_table_num;
int table_index;
struct sm5714_charger_oper_table_info current_table;
/* for Factory mode control */
unsigned char factory_RID;
int chg_float_voltage;
};
static struct sm5714_charger_oper_info *oper_info;
/**
* (VBUS in/out) (USB-OTG in/out) (PWR-SHAR in/out)
* (Flash on/off) (Torch on/off) (Suspend mode on/off)
**/
static struct sm5714_charger_oper_table_info sm5714_charger_op_mode_table[] = {
/* Charger=ON Mode in a valid Input */
{ make_OP_STATUS(0, 0, 0, 0, 0, 0),
OP_MODE_CHG_ON_VBUS, BSTOUT_4500mV, OTG_CURRENT_500mA},
{ make_OP_STATUS(1, 0, 0, 0, 0, 0),
OP_MODE_CHG_ON_VBUS, BSTOUT_4500mV, OTG_CURRENT_500mA},
{ make_OP_STATUS(1, 1, 0, 0, 0, 0), /* Prevent : VBUS + OTG timing sync */
OP_MODE_CHG_ON_VBUS, BSTOUT_4500mV, OTG_CURRENT_500mA},
{ make_OP_STATUS(1, 0, 0, 0, 1, 0),
OP_MODE_CHG_ON_VBUS, BSTOUT_4500mV, OTG_CURRENT_500mA},
/* Flash Boost Mode */
{ make_OP_STATUS(0, 0, 0, 1, 0, 0),
OP_MODE_FLASH_BOOST, BSTOUT_5100mV, OTG_CURRENT_500mA},
{ make_OP_STATUS(1, 0, 0, 1, 0, 0),
OP_MODE_FLASH_BOOST, BSTOUT_5100mV, OTG_CURRENT_500mA},
{ make_OP_STATUS(0, 0, 1, 1, 0, 0),
OP_MODE_FLASH_BOOST, BSTOUT_5100mV, OTG_CURRENT_900mA},
{ make_OP_STATUS(0, 0, 0, 1, 1, 0),
OP_MODE_FLASH_BOOST, BSTOUT_5100mV, OTG_CURRENT_900mA},
{ make_OP_STATUS(0, 0, 0, 0, 1, 0),
OP_MODE_FLASH_BOOST, BSTOUT_5100mV, OTG_CURRENT_900mA},
/* USB OTG Mode */
{ make_OP_STATUS(0, 1, 0, 0, 0, 0),
OP_MODE_USB_OTG, BSTOUT_5100mV, OTG_CURRENT_900mA},
{ make_OP_STATUS(0, 0, 1, 0, 0, 0),
OP_MODE_USB_OTG, BSTOUT_5100mV, OTG_CURRENT_900mA},
{ make_OP_STATUS(0, 1, 0, 1, 0, 0),
OP_MODE_USB_OTG, BSTOUT_5100mV, OTG_CURRENT_900mA},
{ make_OP_STATUS(0, 1, 0, 0, 1, 0),
OP_MODE_USB_OTG, BSTOUT_5100mV, OTG_CURRENT_900mA},
{ make_OP_STATUS(0, 0, 1, 0, 1, 0),
OP_MODE_USB_OTG, BSTOUT_5100mV, OTG_CURRENT_900mA},
/* Suspend Mode : Reserved position of SUSPEND mode table */
{ make_OP_STATUS(0, 0, 0, 0, 0, 1),
OP_MODE_SUSPEND, BSTOUT_5100mV, OTG_CURRENT_900mA},
};
static void sm5714_charger_oper_set_batreg(u16 float_voltage)
{
u8 offset;
if (float_voltage <= 3700)
offset = 0x0;
else if (float_voltage < 3900)
offset = ((float_voltage - 3700) / 50); /* BATREG = 3.70 ~ 3.85V in 0.05V steps */
else if (float_voltage < 4050)
offset = (((float_voltage - 3900) / 100) + 4); /* BATREG = 3.90, 4.0V in 0.1V steps */
else if (float_voltage < 4630)
offset = (((float_voltage - 4050) / 10) + 6); /* BATREG = 4.05 ~ 4.62V in 0.01V steps */
else {
pr_err("sm5714-charger: %s: can't support BATREG at over voltage 4.62V (mV=%d)\n",
__func__, float_voltage);
offset = 0x15; /* default Offset : 4.2V */
}
pr_info("%s: set as (mV=%d) batreg Control\n", __func__, float_voltage);
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_CHGCNTL4, ((offset & 0x3F) << 0), (0x3F << 0));
}
static int set_OP_MODE(struct i2c_client *i2c, u8 mode)
{
return sm5714_update_reg(i2c, SM5714_CHG_REG_CNTL2, (mode << 0), (0xF << 0));
}
static int set_BSTOUT(struct i2c_client *i2c, u8 bstout)
{
return sm5714_update_reg(i2c, SM5714_CHG_REG_BSTCNTL1, (bstout << 0), (0xF << 0));
}
static int set_OTG_CURRENT(struct i2c_client *i2c, u8 otg_curr)
{
return sm5714_update_reg(i2c, SM5714_CHG_REG_BSTCNTL1, (otg_curr << 6), (0x3 << 6));
}
static inline int change_op_table(unsigned char new_status)
{
int i = 0;
pr_info("%s: Old table[%d] info (STATUS: 0x%x, MODE: %d, BST_OUT: 0x%x, OTG_CURRENT: 0x%x)\n",
__func__, oper_info->table_index, oper_info->current_table.status,
oper_info->current_table.oper_mode, oper_info->current_table.BST_OUT,
oper_info->current_table.OTG_CURRENT);
/* Check actvated Suspend Mode */
if (new_status & (0x1 << SM5714_CHARGER_OP_EVENT_SUSPEND)) {
i = oper_info->max_table_num - 1; /* Reserved SUSPEND Mode Table index */
} else {
/* Search matched Table */
for (i = 0; i < oper_info->max_table_num; ++i) {
if (new_status == sm5714_charger_op_mode_table[i].status)
break;
}
}
if (i == oper_info->max_table_num) {
pr_err("%s: can't find matched charger op_mode table (status = 0x%x)\n", __func__, new_status);
return -EINVAL;
}
/* Update current table info */
set_BSTOUT(oper_info->i2c, sm5714_charger_op_mode_table[i].BST_OUT);
oper_info->current_table.BST_OUT = sm5714_charger_op_mode_table[i].BST_OUT;
set_OTG_CURRENT(oper_info->i2c, sm5714_charger_op_mode_table[i].OTG_CURRENT);
oper_info->current_table.OTG_CURRENT = sm5714_charger_op_mode_table[i].OTG_CURRENT;
/* Factory 523K-JIG Test : Torch Light - Prevent VBUS input source */
if ((sm5714_charger_op_mode_table[i].status & 0x02) &&
(oper_info->factory_RID == RID_255K || oper_info->factory_RID == RID_523K)) {
pr_info("sm5714-charger: %s: skip Flash Boost mode for Factory JIG fled:torch test\n", __func__);
/* Factory 523K-JIG Test : Flash Light - Prevent VBUS input source */
} else if ((sm5714_charger_op_mode_table[i].status & 0x04) &&
(oper_info->factory_RID == RID_255K || oper_info->factory_RID == RID_523K)) {
pr_info("sm5714-charger: %s: skip Flash Boost mode for Factory JIG fled:flash test\n", __func__);
} else {
set_OP_MODE(oper_info->i2c, sm5714_charger_op_mode_table[i].oper_mode);
oper_info->current_table.oper_mode = sm5714_charger_op_mode_table[i].oper_mode;
}
oper_info->current_table.status = new_status;
oper_info->table_index = i;
pr_info("%s: New table[%d] (STATUS: 0x%x, MODE: %d, BST_OUT: 0x%x, OTG_CURRENT: 0x%x)\n",
__func__, oper_info->table_index, oper_info->current_table.status,
oper_info->current_table.oper_mode, oper_info->current_table.BST_OUT,
oper_info->current_table.OTG_CURRENT);
return 0;
}
static inline unsigned char update_status(int event_type, bool enable)
{
if (event_type > SM5714_CHARGER_OP_EVENT_VBUSIN) {
pr_debug("sm5714-charger: %s: invalid event type (type=0x%x)\n", __func__, event_type);
return oper_info->current_table.status;
}
if (enable)
return (oper_info->current_table.status | (1 << event_type));
else
return (oper_info->current_table.status & ~(1 << event_type));
}
int sm5714_charger_oper_push_event(int event_type, bool enable)
{
unsigned char new_status;
int ret = 0;
if (oper_info == NULL) {
pr_err("sm5714-charger: %s: required init op_mode table\n", __func__);
return -ENOENT;
}
pr_info("sm5714-charger: %s: event_type=%d, enable=%d\n", __func__, event_type, enable);
mutex_lock(&oper_info->op_mutex);
new_status = update_status(event_type, enable);
if (new_status == oper_info->current_table.status)
goto out;
ret = change_op_table(new_status);
out:
mutex_unlock(&oper_info->op_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(sm5714_charger_oper_push_event);
static inline int detect_initial_table_index(struct i2c_client *i2c)
{
return 0;
}
int sm5714_charger_oper_table_init(struct sm5714_dev *sm5714)
{
struct i2c_client *i2c = sm5714->charger;
int index, ret = 0;
struct device_node *np = NULL;
if (oper_info) {
pr_info("sm5714-charger: %s: already initialized\n", __func__);
return 0;
}
if (i2c == NULL) {
pr_err("sm5714-charger: %s: invalid i2c client handler=n", __func__);
return -EINVAL;
}
oper_info = kmalloc(sizeof(struct sm5714_charger_oper_info), GFP_KERNEL);
if (oper_info == NULL)
return -ENOMEM;
oper_info->i2c = i2c;
mutex_init(&oper_info->op_mutex);
/* set default operation mode condition */
oper_info->max_table_num = ARRAY_SIZE(sm5714_charger_op_mode_table);
index = detect_initial_table_index(oper_info->i2c);
oper_info->table_index = index;
oper_info->current_table.status = sm5714_charger_op_mode_table[index].status;
oper_info->current_table.oper_mode = sm5714_charger_op_mode_table[index].oper_mode;
oper_info->current_table.BST_OUT = sm5714_charger_op_mode_table[index].BST_OUT;
oper_info->current_table.OTG_CURRENT = sm5714_charger_op_mode_table[index].OTG_CURRENT;
set_OP_MODE(oper_info->i2c, oper_info->current_table.oper_mode);
set_BSTOUT(oper_info->i2c, oper_info->current_table.BST_OUT);
set_OTG_CURRENT(oper_info->i2c, oper_info->current_table.OTG_CURRENT);
oper_info->factory_RID = 0;
np = of_find_node_by_name(NULL, "battery");
if (!np) {
dev_err(sm5714->dev, "%s: can't find battery node\n", __func__);
} else {
ret = of_property_read_u32(np, "battery,chg_float_voltage", &oper_info->chg_float_voltage);
if (ret) {
dev_info(sm5714->dev, "%s: battery,chg_float_voltage is Empty\n", __func__);
oper_info->chg_float_voltage = 4350;
}
pr_info("%s: battery,chg_float_voltage is %d\n", __func__, oper_info->chg_float_voltage);
}
pr_info("%s: current table[%d] (STATUS: 0x%x, MODE: %d, BST_OUT: 0x%x, OTG_CURRENT: 0x%x)\n",
__func__, oper_info->table_index, oper_info->current_table.status,
oper_info->current_table.oper_mode, oper_info->current_table.BST_OUT,
oper_info->current_table.OTG_CURRENT);
return 0;
}
EXPORT_SYMBOL_GPL(sm5714_charger_oper_table_init);
int sm5714_charger_oper_get_current_status(void)
{
if (oper_info == NULL)
return -EINVAL;
return oper_info->current_table.status;
}
EXPORT_SYMBOL_GPL(sm5714_charger_oper_get_current_status);
int sm5714_charger_oper_get_current_op_mode(void)
{
if (oper_info == NULL)
return -EINVAL;
return oper_info->current_table.oper_mode;
}
EXPORT_SYMBOL_GPL(sm5714_charger_oper_get_current_op_mode);
int sm5714_charger_oper_en_factory_mode(int dev_type, int rid, bool enable)
{
u8 reg = 0x0;
union power_supply_propval val = {0, };
if (oper_info == NULL)
return -EINVAL;
if (enable) {
switch (rid) {
case RID_523K:
sm5714_charger_oper_set_batreg(4200);
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_CNTL1,
(0x0 << 6), (0x1 << 6)); /* AICLEN_VBUS = 0 (Disable) */
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_FACTORY1,
(0x1 << 0), (0x1 << 0)); /* NOZX = 1 (Disable) */
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_VBUSCNTL,
(0x7F << 0), (0x7F << 0)); /* VBUS_LIMIT = MAX(3275mA) */
break;
case RID_301K:
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_CNTL1,
(0x0 << 6), (0x1 << 6)); /* AICLEN_VBUS = 0 (Disable) */
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_FACTORY1,
(0x0 << 0), (0x1 << 0)); /* NOZX = 0 (Enable) */
#if defined(CONFIG_SEC_FACTORY)
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_VBUSCNTL,
(0x7F << 0), (0x7F << 0)); /* VBUS_LIMIT = MAX(3275mA) */
#else
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_VBUSCNTL,
(0x44 << 0), (0x7F << 0)); /* VBUS_LIMIT = MAX(1800mA) */
#endif
break;
case RID_619K:
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_CNTL1,
(0x0 << 6), (0x1 << 6)); /* AICLEN_VBUS = 0 (Disable) */
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_FACTORY1,
(0x0 << 0), (0x1 << 0)); /* NOZX = 0 (Enable) */
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_VBUSCNTL,
(0x44 << 0), (0x7F << 0)); /* VBUS_LIMIT = 1800mA */
break;
case RID_255K:
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_CNTL1,
(0x0 << 6), (0x1 << 6)); /* AICLEN_VBUS = 0 (Disable) */
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_FACTORY1,
(0x0 << 0), (0x1 << 0)); /* NOZX = 0 (Enable) */
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_VBUSCNTL,
(0x7F << 0), (0x7F << 0)); /* VBUS_LIMIT = MAX(3275mA) */
break;
}
psy_do_property("sm5714-fuelgauge", set,
POWER_SUPPLY_PROP_ENERGY_NOW, val);
oper_info->factory_RID = rid;
sm5714_read_reg(oper_info->i2c, SM5714_CHG_REG_VBUSCNTL, &reg);
pr_info("%s: enable factory mode configuration(RID=%d, vbuslimit=0x%02X)\n", __func__, rid, reg);
} else {
sm5714_charger_oper_set_batreg(oper_info->chg_float_voltage);
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_CHGCNTL11,
(0x0 << 0), (0x1 << 0)); /* forced_vsys = disable */
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_CNTL1,
(0x1 << 6), (0x1 << 6)); /* AICLEN_VBUS = 1 (Enable) */
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_FACTORY1,
(0x0 << 0), (0x1 << 0)); /* NOZX = 0 (Enable) */
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_VBUSCNTL,
(0x10 << 0), (0x7F << 0)); /* VBUS_LIMIT = 500mA */
oper_info->factory_RID = 0;
sm5714_read_reg(oper_info->i2c, SM5714_CHG_REG_VBUSCNTL, &reg);
pr_info("%s: disable factory mode configuration(vbuslimit=0x%02X)\n", __func__, reg);
}
return 0;
}
EXPORT_SYMBOL_GPL(sm5714_charger_oper_en_factory_mode);
int sm5714_charger_oper_forced_vbus_limit_control(int mA)
{
u8 old_offset, new_offset;
int old_mA;
int msec;
sm5714_read_reg(oper_info->i2c, SM5714_CHG_REG_VBUSCNTL, &old_offset);
old_mA = ((old_offset & 0x7F) * 25) + 100;
new_offset = ((mA - 100) / 25);
sm5714_update_reg(oper_info->i2c, SM5714_CHG_REG_VBUSCNTL, ((new_offset & 0x7F) << 0), (0x7F << 0));
msec = (old_mA - mA) / 10; // 10mA/ms
if (msec < 0)
msec *= (-1);
msleep(msec);
pr_info("sm5714-charger: %s VBUSLIMIT control 0x%X[%dmA] -> 0x%X[%dmA] (%d ms)\n",
__func__, old_offset, old_mA, new_offset, mA, msec);
return 0;
}
EXPORT_SYMBOL_GPL(sm5714_charger_oper_forced_vbus_limit_control);