236 lines
9.4 KiB
C
236 lines
9.4 KiB
C
/*
|
|
* drivers/battery/sm5705_charger_oper.c
|
|
*
|
|
* SM5705 Charger Operation Mode controller
|
|
*
|
|
* Copyright (C) 2015 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/kernel.h>
|
|
#include "include/charger/sm5705_charger.h"
|
|
#include "include/charger/sm5705_charger_oper.h"
|
|
|
|
enum {
|
|
BST_OUT_4000mV = 0x0,
|
|
BST_OUT_4100mV = 0x1,
|
|
BST_OUT_4200mV = 0x2,
|
|
BST_OUT_4300mV = 0x3,
|
|
BST_OUT_4400mV = 0x4,
|
|
BST_OUT_4500mV = 0x5,
|
|
BST_OUT_4600mV = 0x6,
|
|
BST_OUT_4700mV = 0x7,
|
|
BST_OUT_4800mV = 0x8,
|
|
BST_OUT_4900mV = 0x9,
|
|
BST_OUT_5000mV = 0xA,
|
|
BST_OUT_5100mV = 0xB,
|
|
};
|
|
|
|
enum {
|
|
OTG_CURRENT_500mA = 0x0,
|
|
OTG_CURRENT_700mA = 0x1,
|
|
OTG_CURRENT_900mA = 0x2,
|
|
OTG_CURRENT_1500mA = 0x3,
|
|
};
|
|
|
|
#define SM5705_OPERATION_MODE_MASK 0x07
|
|
#define SM5705_BSTOUT_MASK 0x0F
|
|
#define SM5705_OTGCURRENT_MASK 0xC
|
|
|
|
struct sm5705_charger_oper_table_info {
|
|
unsigned char status;
|
|
unsigned char oper_mode;
|
|
unsigned char BST_OUT;
|
|
unsigned char OTG_CURRENT;
|
|
};
|
|
|
|
struct sm5705_charger_oper_info {
|
|
struct i2c_client *i2c;
|
|
|
|
int max_table_num;
|
|
struct sm5705_charger_oper_table_info current_table;
|
|
};
|
|
static struct sm5705_charger_oper_info oper_info;
|
|
|
|
/**
|
|
* (VBUS in/out) (WPC in/out) (FLASH on/off) (TORCH on/off) (OTG cable in/out) (Power Sharing cable in/out)
|
|
**/
|
|
static struct sm5705_charger_oper_table_info sm5705_charger_operation_mode_table[] = {
|
|
/* Charger mode : Charging ON */
|
|
{ make_OP_STATUS(0,0,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
{ make_OP_STATUS(1,0,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
{ make_OP_STATUS(1,1,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
{ make_OP_STATUS(1,0,0,1,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
{ make_OP_STATUS(1,1,0,1,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
{ make_OP_STATUS(0,1,0,0,0,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
#if defined(CONFIG_SM5705_SUPPORT_LANHUB) || defined(CONFIG_SM5705_SUPPORT_GAMEPAD)
|
|
{ make_OP_STATUS(1,0,0,0,1,0), SM5705_CHARGER_OP_MODE_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
#endif
|
|
/* Charger mode : Flash Boost */
|
|
{ make_OP_STATUS(0,0,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
{ make_OP_STATUS(0,0,1,0,1,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_900mA},
|
|
{ make_OP_STATUS(0,0,1,0,0,1), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_900mA},
|
|
{ make_OP_STATUS(0,1,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
{ make_OP_STATUS(0,1,1,0,1,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_900mA},
|
|
{ make_OP_STATUS(0,1,1,0,0,1), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_900mA},
|
|
{ make_OP_STATUS(1,0,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
{ make_OP_STATUS(1,1,1,0,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
{ make_OP_STATUS(0,0,0,1,0,0), SM5705_CHARGER_OP_MODE_FLASH_BOOST, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
/* Charger mode : Wireless OTG & Charger ON */
|
|
{ make_OP_STATUS(0,1,0,1,1,0), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
|
{ make_OP_STATUS(0,1,0,1,0,1), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
|
{ make_OP_STATUS(0,1,0,1,0,0), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_4500mV, OTG_CURRENT_500mA},
|
|
{ make_OP_STATUS(0,1,0,0,1,0), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
|
{ make_OP_STATUS(0,1,0,0,0,1), SM5705_CHARGER_OP_MODE_WPC_OTG_CHG_ON, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
|
/* Charger mode : USB OTG */
|
|
{ make_OP_STATUS(0,0,0,1,1,0), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
|
{ make_OP_STATUS(0,0,0,1,0,1), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
|
{ make_OP_STATUS(0,0,0,0,1,0), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
|
{ make_OP_STATUS(0,0,0,0,0,1), SM5705_CHARGER_OP_MODE_USB_OTG, BST_OUT_5100mV, OTG_CURRENT_900mA},
|
|
};
|
|
|
|
/**
|
|
* SM5705 Charger operation mode controller relative I2C setup
|
|
*/
|
|
|
|
static int sm5705_charger_oper_set_mode(struct i2c_client *i2c, unsigned char mode)
|
|
{
|
|
return sm5705_update_reg(i2c, SM5705_REG_CNTL, mode, SM5705_OPERATION_MODE_MASK);
|
|
}
|
|
|
|
static int sm5705_charger_oper_set_BSTOUT(struct i2c_client *i2c, unsigned char BSTOUT)
|
|
{
|
|
return sm5705_update_reg(i2c, SM5705_REG_FLEDCNTL6, BSTOUT, SM5705_BSTOUT_MASK);
|
|
}
|
|
|
|
static int sm5705_charger_oper_set_OTG_CURRENT(struct i2c_client *i2c, unsigned char OTG_CURRENT)
|
|
{
|
|
return sm5705_update_reg(i2c, SM5705_REG_CHGCNTL6, OTG_CURRENT << 2, SM5705_OTGCURRENT_MASK);
|
|
}
|
|
|
|
/**
|
|
* SM5705 Charger operation mode controller API functions.
|
|
*/
|
|
|
|
static inline unsigned char _update_status(int event_type, bool enable)
|
|
{
|
|
if (event_type > SM5705_CHARGER_OP_EVENT_VBUS) {
|
|
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));
|
|
}
|
|
}
|
|
|
|
static inline void sm5705_charger_oper_change_state(unsigned char new_status)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i < oper_info.max_table_num; ++i) {
|
|
if (new_status == sm5705_charger_operation_mode_table[i].status) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == oper_info.max_table_num) {
|
|
pr_err("sm5705-charger: %s: can't find matched Charger Operation Mode Table (status = 0x%x)\n", __func__, new_status);
|
|
return;
|
|
}
|
|
|
|
if (sm5705_charger_operation_mode_table[i].BST_OUT != oper_info.current_table.BST_OUT) {
|
|
sm5705_charger_oper_set_BSTOUT(oper_info.i2c, sm5705_charger_operation_mode_table[i].BST_OUT);
|
|
oper_info.current_table.BST_OUT = sm5705_charger_operation_mode_table[i].BST_OUT;
|
|
}
|
|
if (sm5705_charger_operation_mode_table[i].OTG_CURRENT != oper_info.current_table.OTG_CURRENT) {
|
|
sm5705_charger_oper_set_OTG_CURRENT(oper_info.i2c, sm5705_charger_operation_mode_table[i].OTG_CURRENT);
|
|
oper_info.current_table.OTG_CURRENT = sm5705_charger_operation_mode_table[i].OTG_CURRENT;
|
|
}
|
|
|
|
if (sm5705_call_fg_device_id() < 5) {
|
|
/* USB_OTG to CHG_ON work-around for BAT_REG stabilize */
|
|
if (oper_info.current_table.oper_mode == SM5705_CHARGER_OP_MODE_USB_OTG && \
|
|
sm5705_charger_operation_mode_table[i].oper_mode == SM5705_CHARGER_OP_MODE_CHG_ON) {
|
|
pr_info("sm5705-charger: %s: trans op_mode:suspend for BAT_REG stabilize (time=100ms)\n", __func__);
|
|
sm5705_charger_oper_set_mode(oper_info.i2c, SM5705_CHARGER_OP_MODE_SUSPEND);
|
|
msleep(100);
|
|
}
|
|
}
|
|
|
|
if (sm5705_charger_operation_mode_table[i].oper_mode != oper_info.current_table.oper_mode) {
|
|
sm5705_charger_oper_set_mode(oper_info.i2c, sm5705_charger_operation_mode_table[i].oper_mode);
|
|
oper_info.current_table.oper_mode = sm5705_charger_operation_mode_table[i].oper_mode;
|
|
}
|
|
oper_info.current_table.status = new_status;
|
|
|
|
pr_info("sm5705-charger: %s: New table[%d] info (STATUS: 0x%x, MODE: %d, BST_OUT: 0x%x, OTG_CURRENT: 0x%x\n", \
|
|
__func__, i, oper_info.current_table.status, oper_info.current_table.oper_mode, oper_info.current_table.BST_OUT, oper_info.current_table.OTG_CURRENT);
|
|
}
|
|
|
|
int sm5705_charger_oper_push_event(int event_type, bool enable)
|
|
{
|
|
unsigned char new_status;
|
|
|
|
if (oper_info.i2c == NULL) {
|
|
pr_err("sm5705-charger: %s: required sm5705 charger operation table initialize\n", __func__);
|
|
return -ENOENT;
|
|
}
|
|
|
|
pr_info("sm5705-charger: %s: event_type=%d, enable=%d\n", __func__, event_type, enable);
|
|
|
|
new_status = _update_status(event_type, enable);
|
|
if (new_status == oper_info.current_table.status) {
|
|
goto out;
|
|
}
|
|
|
|
sm5705_charger_oper_change_state(new_status);
|
|
|
|
out:
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(sm5705_charger_oper_push_event);
|
|
|
|
int sm5705_charger_oper_table_init(struct i2c_client *i2c)
|
|
{
|
|
if (i2c == NULL) {
|
|
pr_err("sm5705-charger: %s: invalid i2c client handler=n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
oper_info.i2c = i2c;
|
|
|
|
/* set default operation mode condition */
|
|
oper_info.max_table_num = ARRAY_SIZE(sm5705_charger_operation_mode_table);
|
|
oper_info.current_table.status = make_OP_STATUS(0, 0, 0, 0, 0, 0);
|
|
oper_info.current_table.oper_mode = SM5705_CHARGER_OP_MODE_CHG_ON;
|
|
oper_info.current_table.BST_OUT = BST_OUT_4500mV;
|
|
oper_info.current_table.OTG_CURRENT = OTG_CURRENT_500mA;
|
|
|
|
sm5705_charger_oper_set_mode(oper_info.i2c, oper_info.current_table.oper_mode);
|
|
sm5705_charger_oper_set_BSTOUT(oper_info.i2c, oper_info.current_table.BST_OUT);
|
|
sm5705_charger_oper_set_OTG_CURRENT(oper_info.i2c, oper_info.current_table.OTG_CURRENT);
|
|
|
|
pr_info("sm5705-charger: %s: current table info (STATUS: 0x%x, MODE: %d, BST_OUT: 0x%x, OTG_CURRENT: 0x%x\n", \
|
|
__func__, 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(sm5705_charger_oper_table_init);
|
|
|
|
int sm5705_charger_oper_get_current_status(void)
|
|
{
|
|
return oper_info.current_table.status;
|
|
}
|
|
EXPORT_SYMBOL(sm5705_charger_oper_get_current_status);
|
|
|
|
int sm5705_charger_oper_get_current_op_mode(void)
|
|
{
|
|
return oper_info.current_table.oper_mode;
|
|
}
|
|
EXPORT_SYMBOL(sm5705_charger_oper_get_current_op_mode);
|