6db4831e98
Android 14
951 lines
22 KiB
C
951 lines
22 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (c) 2021 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/alarmtimer.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/device.h>
|
|
#include <linux/kdev_t.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/power_supply.h>
|
|
#include <linux/time.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/version.h>
|
|
#include <linux/i2c.h>
|
|
|
|
#ifdef CONFIG_OF
|
|
#include <linux/of.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_device.h>
|
|
#endif
|
|
#include "upmu_common.h"
|
|
#include "hl7005.h"
|
|
#include "mtk_charger_intf.h"
|
|
|
|
const unsigned int VBAT_CVTH[] = {
|
|
3500000, 3520000, 3540000, 3560000,
|
|
3580000, 3600000, 3620000, 3640000,
|
|
3660000, 3680000, 3700000, 3720000,
|
|
3740000, 3760000, 3780000, 3800000,
|
|
3820000, 3840000, 3860000, 3880000,
|
|
3900000, 3920000, 3940000, 3960000,
|
|
3980000, 4000000, 4020000, 4040000,
|
|
4060000, 4080000, 4100000, 4120000,
|
|
4140000, 4160000, 4180000, 4200000,
|
|
4220000, 4240000, 4260000, 4280000,
|
|
4300000, 4320000, 4340000, 4360000,
|
|
4380000, 4400000, 4420000, 4440000
|
|
};
|
|
|
|
const unsigned int CSTH[] = {
|
|
550000, 650000, 750000, 850000,
|
|
950000, 1050000, 1150000, 1250000
|
|
};
|
|
|
|
/*hl7005 REG00 IINLIM[5:0]*/
|
|
const unsigned int INPUT_CSTH[] = {
|
|
100000, 500000, 800000, 5000000
|
|
};
|
|
|
|
/* hl7005 REG0A BOOST_LIM[2:0], mA */
|
|
const unsigned int BOOST_CURRENT_LIMIT[] = {
|
|
500, 750, 1200, 1400, 1650, 1875, 2150,
|
|
};
|
|
|
|
struct hl7005_info {
|
|
struct charger_device *chg_dev;
|
|
struct charger_properties chg_props;
|
|
struct device *dev;
|
|
struct alarm otg_kthread_gtimer;
|
|
struct workqueue_struct *otg_boost_workq;
|
|
struct work_struct kick_work;
|
|
unsigned int polling_interval;
|
|
bool polling_enabled;
|
|
|
|
const char *chg_dev_name;
|
|
const char *eint_name;
|
|
enum charger_type chg_type;
|
|
int irq;
|
|
};
|
|
|
|
static struct hl7005_info *g_info;
|
|
static struct i2c_client *new_client;
|
|
static const struct i2c_device_id hl7005_i2c_id[] = { {"hl7005", 0}, {} };
|
|
|
|
static void enable_boost_polling(bool poll_en);
|
|
static void usbotg_boost_kick_work(struct work_struct *work);
|
|
static enum alarmtimer_restart usbotg_gtimer_func(struct alarm *alarm,
|
|
ktime_t now);
|
|
|
|
unsigned int charging_value_to_parameter(const unsigned int *parameter,
|
|
const unsigned int array_size,
|
|
const unsigned int val)
|
|
{
|
|
if (val < array_size)
|
|
return parameter[val];
|
|
pr_info("Can't find the parameter\n");
|
|
return parameter[0];
|
|
}
|
|
|
|
unsigned int charging_parameter_to_value(const unsigned int *parameter,
|
|
const unsigned int array_size,
|
|
const unsigned int val)
|
|
{
|
|
unsigned int i;
|
|
|
|
pr_debug_ratelimited("array_size = %d\n", array_size);
|
|
|
|
for (i = 0; i < array_size; i++) {
|
|
if (val == *(parameter + i))
|
|
return i;
|
|
}
|
|
|
|
pr_info("NO register value match\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int bmt_find_closest_level(const unsigned int *pList,
|
|
unsigned int number,
|
|
unsigned int level)
|
|
{
|
|
unsigned int i;
|
|
unsigned int max_value_in_last_element;
|
|
|
|
if (pList[0] < pList[1])
|
|
max_value_in_last_element = 1;
|
|
else
|
|
max_value_in_last_element = 0;
|
|
|
|
if (max_value_in_last_element == 1) {
|
|
for (i = (number - 1); i != 0; i--) {
|
|
if (pList[i] <= level) {
|
|
pr_debug_ratelimited("zzf_%d<=%d, i=%d\n",
|
|
pList[i], level, i);
|
|
return pList[i];
|
|
}
|
|
}
|
|
pr_info("Can't find closest level\n");
|
|
return pList[0];
|
|
/* return 000; */
|
|
} else {
|
|
for (i = 0; i < number; i++) {
|
|
if (pList[i] <= level)
|
|
return pList[i];
|
|
}
|
|
pr_info("Can't find closest level\n");
|
|
return pList[number - 1];
|
|
/* return 000; */
|
|
}
|
|
}
|
|
|
|
unsigned char hl7005_reg[HL7005_REG_NUM] = { 0 };
|
|
static DEFINE_MUTEX(hl7005_i2c_access);
|
|
static DEFINE_MUTEX(hl7005_access_lock);
|
|
|
|
static int hl7005_read_byte(u8 reg_addr, u8 *rd_buf, int rd_len)
|
|
{
|
|
int ret = 0;
|
|
struct i2c_adapter *adap = new_client->adapter;
|
|
struct i2c_msg msg[2];
|
|
u8 *w_buf = NULL;
|
|
u8 *r_buf = NULL;
|
|
|
|
memset(msg, 0, 2 * sizeof(struct i2c_msg));
|
|
|
|
w_buf = kzalloc(1, GFP_KERNEL);
|
|
if (w_buf == NULL)
|
|
return -1;
|
|
r_buf = kzalloc(rd_len, GFP_KERNEL);
|
|
if (r_buf == NULL)
|
|
return -1;
|
|
|
|
*w_buf = reg_addr;
|
|
|
|
msg[0].addr = new_client->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].len = 1;
|
|
msg[0].buf = w_buf;
|
|
|
|
msg[1].addr = new_client->addr;
|
|
msg[1].flags = 1;
|
|
msg[1].len = rd_len;
|
|
msg[1].buf = r_buf;
|
|
|
|
ret = i2c_transfer(adap, msg, 2);
|
|
|
|
memcpy(rd_buf, r_buf, rd_len);
|
|
|
|
kfree(w_buf);
|
|
kfree(r_buf);
|
|
return ret;
|
|
}
|
|
|
|
int hl7005_write_byte(unsigned char reg_num, u8 *wr_buf, int wr_len)
|
|
{
|
|
int ret = 0;
|
|
struct i2c_adapter *adap = new_client->adapter;
|
|
struct i2c_msg msg;
|
|
u8 *w_buf = NULL;
|
|
|
|
memset(&msg, 0, sizeof(struct i2c_msg));
|
|
|
|
w_buf = kzalloc(wr_len, GFP_KERNEL);
|
|
if (w_buf == NULL)
|
|
return -1;
|
|
|
|
w_buf[0] = reg_num;
|
|
memcpy(w_buf + 1, wr_buf, wr_len);
|
|
|
|
msg.addr = new_client->addr;
|
|
msg.flags = 0;
|
|
msg.len = wr_len;
|
|
msg.buf = w_buf;
|
|
|
|
ret = i2c_transfer(adap, &msg, 1);
|
|
|
|
kfree(w_buf);
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hl7005_read_interface(unsigned char reg_num, unsigned char *val,
|
|
unsigned char MASK, unsigned char SHIFT)
|
|
{
|
|
unsigned char hl7005_reg = 0;
|
|
unsigned int ret = 0;
|
|
|
|
ret = hl7005_read_byte(reg_num, &hl7005_reg, 1);
|
|
pr_debug_ratelimited("hl7005 Reg[%x] = 0x%x\n", reg_num, hl7005_reg);
|
|
hl7005_reg &= (MASK << SHIFT);
|
|
*val = (hl7005_reg >> SHIFT);
|
|
pr_debug_ratelimited("hl7005 val = 0x%x\n", *val);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int hl7005_config_interface(unsigned char reg_num, unsigned char val,
|
|
unsigned char MASK, unsigned char SHIFT)
|
|
{
|
|
unsigned char hl7005_reg = 0;
|
|
unsigned char hl7005_reg_ori = 0;
|
|
unsigned int ret = 0;
|
|
|
|
mutex_lock(&hl7005_access_lock);
|
|
ret = hl7005_read_byte(reg_num, &hl7005_reg, 1);
|
|
hl7005_reg_ori = hl7005_reg;
|
|
hl7005_reg &= ~(MASK << SHIFT);
|
|
hl7005_reg |= (val << SHIFT);
|
|
if (reg_num == HL7005_CON4)
|
|
hl7005_reg &= ~(1 << CON4_RESET_SHIFT);
|
|
|
|
ret = hl7005_write_byte(reg_num, &hl7005_reg, 2);
|
|
mutex_unlock(&hl7005_access_lock);
|
|
pr_debug_ratelimited("hl7005 write Reg[%x]=0x%x from 0x%x\n", reg_num,
|
|
hl7005_reg, hl7005_reg_ori);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* write one register directly */
|
|
unsigned int hl7005_reg_config_interface(unsigned char reg_num,
|
|
unsigned char val)
|
|
{
|
|
unsigned char hl7005_reg = val;
|
|
|
|
return hl7005_write_byte(reg_num, &hl7005_reg, 2);
|
|
}
|
|
|
|
void hl7005_set_tmr_rst(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON0),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON0_TMR_RST_MASK),
|
|
(unsigned char)(CON0_TMR_RST_SHIFT)
|
|
);
|
|
}
|
|
|
|
unsigned int hl7005_get_otg_status(void)
|
|
{
|
|
unsigned char val = 0;
|
|
|
|
hl7005_read_interface((unsigned char)(HL7005_CON0),
|
|
(unsigned char *)(&val),
|
|
(unsigned char)(CON0_OTG_MASK),
|
|
(unsigned char)(CON0_OTG_SHIFT)
|
|
);
|
|
return val;
|
|
}
|
|
|
|
void hl7005_set_en_stat(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON0),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON0_EN_STAT_MASK),
|
|
(unsigned char)(CON0_EN_STAT_SHIFT)
|
|
);
|
|
}
|
|
|
|
unsigned int hl7005_get_chip_status(void)
|
|
{
|
|
unsigned char val = 0;
|
|
|
|
hl7005_read_interface((unsigned char)(HL7005_CON0),
|
|
(unsigned char *)(&val),
|
|
(unsigned char)(CON0_STAT_MASK),
|
|
(unsigned char)(CON0_STAT_SHIFT)
|
|
);
|
|
return val;
|
|
}
|
|
|
|
unsigned int hl7005_get_boost_status(void)
|
|
{
|
|
unsigned char val = 0;
|
|
|
|
hl7005_read_interface((unsigned char)(HL7005_CON0),
|
|
(unsigned char *)(&val),
|
|
(unsigned char)(CON0_BOOST_MASK),
|
|
(unsigned char)(CON0_BOOST_SHIFT)
|
|
);
|
|
return val;
|
|
|
|
}
|
|
|
|
unsigned int hl7005_get_fault_status(void)
|
|
{
|
|
unsigned char val = 0;
|
|
|
|
hl7005_read_interface((unsigned char)(HL7005_CON0),
|
|
(unsigned char *)(&val),
|
|
(unsigned char)(CON0_FAULT_MASK),
|
|
(unsigned char)(CON0_FAULT_SHIFT)
|
|
);
|
|
return val;
|
|
}
|
|
|
|
void hl7005_set_input_charging_current(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON1),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON1_LIN_LIMIT_MASK),
|
|
(unsigned char)(CON1_LIN_LIMIT_SHIFT)
|
|
);
|
|
}
|
|
|
|
unsigned int hl7005_get_input_charging_current(void)
|
|
{
|
|
unsigned char val = 0;
|
|
|
|
hl7005_read_interface((unsigned char)(HL7005_CON1),
|
|
(unsigned char *)(&val),
|
|
(unsigned char)(CON1_LIN_LIMIT_MASK),
|
|
(unsigned char)(CON1_LIN_LIMIT_SHIFT)
|
|
);
|
|
|
|
return val;
|
|
}
|
|
|
|
void hl7005_set_v_low(unsigned int val)
|
|
{
|
|
|
|
hl7005_config_interface((unsigned char)(HL7005_CON1),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON1_LOW_V_MASK),
|
|
(unsigned char)(CON1_LOW_V_SHIFT)
|
|
);
|
|
}
|
|
|
|
void hl7005_set_te(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON1),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON1_TE_MASK),
|
|
(unsigned char)(CON1_TE_SHIFT)
|
|
);
|
|
}
|
|
|
|
void hl7005_set_ce(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON1),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON1_CE_MASK),
|
|
(unsigned char)(CON1_CE_SHIFT)
|
|
);
|
|
}
|
|
|
|
void hl7005_set_hz_mode(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON1),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON1_HZ_MODE_MASK),
|
|
(unsigned char)(CON1_HZ_MODE_SHIFT)
|
|
);
|
|
}
|
|
|
|
void hl7005_set_opa_mode(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON1),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON1_OPA_MODE_MASK),
|
|
(unsigned char)(CON1_OPA_MODE_SHIFT)
|
|
);
|
|
}
|
|
|
|
void hl7005_set_oreg(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON2),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON2_OREG_MASK),
|
|
(unsigned char)(CON2_OREG_SHIFT)
|
|
);
|
|
}
|
|
void hl7005_set_otg_pl(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON2),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON2_OTG_PL_MASK),
|
|
(unsigned char)(CON2_OTG_PL_SHIFT)
|
|
);
|
|
}
|
|
void hl7005_set_otg_en(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON2),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON2_OTG_EN_MASK),
|
|
(unsigned char)(CON2_OTG_EN_SHIFT)
|
|
);
|
|
}
|
|
|
|
unsigned int hl7005_get_vender_code(void)
|
|
{
|
|
unsigned char val = 0;
|
|
|
|
hl7005_read_interface((unsigned char)(HL7005_CON3),
|
|
(unsigned char *)(&val),
|
|
(unsigned char)(CON3_VENDER_CODE_MASK),
|
|
(unsigned char)(CON3_VENDER_CODE_SHIFT)
|
|
);
|
|
return val;
|
|
}
|
|
|
|
unsigned int hl7005_get_pn(void)
|
|
{
|
|
unsigned char val = 0;
|
|
|
|
hl7005_read_interface((unsigned char)(HL7005_CON3),
|
|
(unsigned char *)(&val),
|
|
(unsigned char)(CON3_PIN_MASK),
|
|
(unsigned char)(CON3_PIN_SHIFT)
|
|
);
|
|
return val;
|
|
}
|
|
|
|
unsigned int hl7005_get_revision(void)
|
|
{
|
|
unsigned char val = 0;
|
|
|
|
hl7005_read_interface((unsigned char)(HL7005_CON3),
|
|
(unsigned char *)(&val),
|
|
(unsigned char)(CON3_REVISION_MASK),
|
|
(unsigned char)(CON3_REVISION_SHIFT)
|
|
);
|
|
return val;
|
|
}
|
|
|
|
void hl7005_set_reset(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON4),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON4_RESET_MASK),
|
|
(unsigned char)(CON4_RESET_SHIFT)
|
|
);
|
|
}
|
|
|
|
void hl7005_set_iocharge(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON4),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON4_I_CHR_MASK),
|
|
(unsigned char)(CON4_I_CHR_SHIFT)
|
|
);
|
|
}
|
|
|
|
void hl7005_set_iterm(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON4),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON4_I_TERM_MASK),
|
|
(unsigned char)(CON4_I_TERM_SHIFT)
|
|
);
|
|
}
|
|
|
|
void hl7005_set_dis_vreg(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON5),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON5_DIS_VREG_MASK),
|
|
(unsigned char)(CON5_DIS_VREG_SHIFT)
|
|
);
|
|
}
|
|
|
|
void hl7005_set_io_level(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON5),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON5_IO_LEVEL_MASK),
|
|
(unsigned char)(CON5_IO_LEVEL_SHIFT)
|
|
);
|
|
}
|
|
|
|
unsigned int hl7005_get_sp_status(void)
|
|
{
|
|
unsigned char val = 0;
|
|
|
|
hl7005_read_interface((unsigned char)(HL7005_CON5),
|
|
(unsigned char *)(&val),
|
|
(unsigned char)(CON5_SP_STATUS_MASK),
|
|
(unsigned char)(CON5_SP_STATUS_SHIFT)
|
|
);
|
|
return val;
|
|
}
|
|
|
|
unsigned int hl7005_get_en_level(void)
|
|
{
|
|
unsigned char val = 0;
|
|
|
|
hl7005_read_interface((unsigned char)(HL7005_CON5),
|
|
(unsigned char *)(&val),
|
|
(unsigned char)(CON5_EN_LEVEL_MASK),
|
|
(unsigned char)(CON5_EN_LEVEL_SHIFT)
|
|
);
|
|
return val;
|
|
}
|
|
|
|
void hl7005_set_vsp(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON5),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON5_VSP_MASK),
|
|
(unsigned char)(CON5_VSP_SHIFT)
|
|
);
|
|
}
|
|
|
|
void hl7005_set_i_safe(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON6),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON6_ISAFE_MASK),
|
|
(unsigned char)(CON6_ISAFE_SHIFT)
|
|
);
|
|
}
|
|
|
|
void hl7005_set_v_safe(unsigned int val)
|
|
{
|
|
hl7005_config_interface((unsigned char)(HL7005_CON6),
|
|
(unsigned char)(val),
|
|
(unsigned char)(CON6_VSAFE_MASK),
|
|
(unsigned char)(CON6_VSAFE_SHIFT)
|
|
);
|
|
}
|
|
|
|
static int hl7005_dump_register(struct charger_device *chg_dev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < HL7005_REG_NUM; i++) {
|
|
hl7005_read_byte(i, &hl7005_reg[i], 1);
|
|
pr_debug("[0x%x]=0x%x ", i, hl7005_reg[i]);
|
|
}
|
|
pr_debug("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hl7005_parse_dt(struct hl7005_info *info, struct device *dev)
|
|
{
|
|
struct device_node *np = dev->of_node;
|
|
|
|
pr_info("%s\n", __func__);
|
|
|
|
if (!np) {
|
|
pr_err("%s: no of node\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (of_property_read_string(np, "charger_name",
|
|
&info->chg_dev_name) < 0) {
|
|
info->chg_dev_name = "primary_chg";
|
|
pr_warn("%s: no charger name\n", __func__);
|
|
}
|
|
|
|
if (of_property_read_string(np, "alias_name",
|
|
&(info->chg_props.alias_name)) < 0) {
|
|
info->chg_props.alias_name = "hl7005";
|
|
pr_warn("%s: no alias name\n", __func__);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hl7005_do_event(struct charger_device *chg_dev, unsigned int event,
|
|
unsigned int args)
|
|
{
|
|
if (chg_dev == NULL)
|
|
return -EINVAL;
|
|
|
|
pr_info("%s: event = %d\n", __func__, event);
|
|
|
|
switch (event) {
|
|
case EVENT_EOC:
|
|
charger_dev_notify(chg_dev, CHARGER_DEV_NOTIFY_EOC);
|
|
break;
|
|
case EVENT_RECHARGE:
|
|
charger_dev_notify(chg_dev, CHARGER_DEV_NOTIFY_RECHG);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hl7005_enable_charging(struct charger_device *chg_dev, bool en)
|
|
{
|
|
unsigned int status = 0;
|
|
|
|
if (en) {
|
|
hl7005_set_ce(0);
|
|
hl7005_set_hz_mode(0);
|
|
hl7005_set_opa_mode(0);
|
|
} else {
|
|
hl7005_set_ce(1);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static int hl7005_set_cv_voltage(struct charger_device *chg_dev, u32 cv)
|
|
{
|
|
int status = 0;
|
|
unsigned short int array_size;
|
|
unsigned int set_cv_voltage;
|
|
unsigned short int register_value;
|
|
/*static kal_int16 pre_register_value; */
|
|
array_size = ARRAY_SIZE(VBAT_CVTH);
|
|
/*pre_register_value = -1; */
|
|
set_cv_voltage = bmt_find_closest_level(VBAT_CVTH, array_size, cv);
|
|
|
|
register_value =
|
|
charging_parameter_to_value(VBAT_CVTH, array_size, set_cv_voltage);
|
|
pr_info("charging_set_cv_voltage register_value=0x%x %d %d\n",
|
|
register_value, cv, set_cv_voltage);
|
|
hl7005_set_oreg(register_value);
|
|
|
|
return status;
|
|
}
|
|
|
|
static int hl7005_get_current(struct charger_device *chg_dev, u32 *ichg)
|
|
{
|
|
int status = 0;
|
|
unsigned int array_size;
|
|
unsigned char reg_value;
|
|
|
|
array_size = ARRAY_SIZE(CSTH);
|
|
hl7005_read_interface(0x1, ®_value, 0x3, 0x6);
|
|
*ichg = charging_value_to_parameter(CSTH, array_size, reg_value);
|
|
|
|
return status;
|
|
}
|
|
|
|
static int hl7005_set_current(struct charger_device *chg_dev, u32 current_value)
|
|
{
|
|
unsigned int status = 0;
|
|
unsigned int set_chr_current;
|
|
unsigned int array_size;
|
|
unsigned int register_value;
|
|
|
|
if (current_value <= 35000) {
|
|
hl7005_set_io_level(1);
|
|
} else {
|
|
hl7005_set_io_level(0);
|
|
array_size = ARRAY_SIZE(CSTH);
|
|
set_chr_current = bmt_find_closest_level(CSTH,
|
|
array_size, current_value);
|
|
register_value = charging_parameter_to_value(CSTH, array_size,
|
|
set_chr_current);
|
|
hl7005_set_iocharge(register_value);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static int hl7005_get_input_current(struct charger_device *chg_dev, u32 *aicr)
|
|
{
|
|
unsigned int status = 0;
|
|
unsigned int array_size;
|
|
unsigned int register_value;
|
|
|
|
array_size = ARRAY_SIZE(INPUT_CSTH);
|
|
register_value = hl7005_get_input_charging_current();
|
|
*aicr = charging_parameter_to_value(INPUT_CSTH, array_size,
|
|
register_value);
|
|
|
|
return status;
|
|
}
|
|
|
|
static int hl7005_set_input_current(struct charger_device *chg_dev,
|
|
u32 current_value)
|
|
{
|
|
unsigned int status = 0;
|
|
unsigned int set_chr_current;
|
|
unsigned int array_size;
|
|
unsigned int register_value;
|
|
|
|
if (current_value > 50000) {
|
|
register_value = 0x3;
|
|
} else {
|
|
array_size = ARRAY_SIZE(INPUT_CSTH);
|
|
set_chr_current = bmt_find_closest_level(INPUT_CSTH, array_size,
|
|
current_value);
|
|
register_value =
|
|
charging_parameter_to_value(INPUT_CSTH, array_size, set_chr_current);
|
|
}
|
|
|
|
hl7005_set_input_charging_current(register_value);
|
|
|
|
return status;
|
|
}
|
|
|
|
static int hl7005_get_charging_status(struct charger_device *chg_dev,
|
|
bool *is_done)
|
|
{
|
|
unsigned int status = 0;
|
|
unsigned int ret_val;
|
|
|
|
ret_val = hl7005_get_chip_status();
|
|
|
|
if (ret_val == 0x2)
|
|
*is_done = true;
|
|
else
|
|
*is_done = false;
|
|
|
|
return status;
|
|
}
|
|
|
|
static int hl7005_reset_watch_dog_timer(struct charger_device *chg_dev)
|
|
{
|
|
hl7005_set_tmr_rst(1);
|
|
return 0;
|
|
}
|
|
|
|
static int hl7005_charger_enable_otg(struct charger_device *chg_dev, bool en)
|
|
{
|
|
hl7005_set_opa_mode(en);
|
|
enable_boost_polling(en);
|
|
return 0;
|
|
}
|
|
|
|
static void enable_boost_polling(bool poll_en)
|
|
{
|
|
struct timespec time, time_now, end_time;
|
|
ktime_t ktime;
|
|
|
|
if (g_info) {
|
|
if (poll_en) {
|
|
get_monotonic_boottime(&time_now);
|
|
time.tv_sec = g_info->polling_interval;
|
|
time.tv_nsec = 0;
|
|
timespec_add(time_now, time);
|
|
ktime = ktime_set(end_time.tv_sec, end_time.tv_nsec);
|
|
alarm_start(&g_info->otg_kthread_gtimer, ktime);
|
|
g_info->polling_enabled = true;
|
|
} else {
|
|
g_info->polling_enabled = false;
|
|
alarm_cancel(&g_info->otg_kthread_gtimer);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void usbotg_boost_kick_work(struct work_struct *work)
|
|
{
|
|
ktime_t ktime;
|
|
struct timespec time, time_now, end_time;
|
|
struct hl7005_info *boost_manager =
|
|
container_of(work, struct hl7005_info, kick_work);
|
|
|
|
pr_debug_ratelimited("hl7005 otg detect\n");
|
|
|
|
hl7005_set_tmr_rst(1);
|
|
|
|
if (boost_manager->polling_enabled == true) {
|
|
get_monotonic_boottime(&time_now);
|
|
time.tv_sec = boost_manager->polling_interval;
|
|
time.tv_nsec = 0;
|
|
timespec_add(time_now, time);
|
|
ktime = ktime_set(end_time.tv_sec, end_time.tv_nsec);
|
|
alarm_start(&boost_manager->otg_kthread_gtimer, ktime);
|
|
}
|
|
}
|
|
|
|
static enum alarmtimer_restart usbotg_gtimer_func(struct alarm *alarm,
|
|
ktime_t now)
|
|
{
|
|
struct hl7005_info *boost_manager =
|
|
container_of(alarm, struct hl7005_info,
|
|
otg_kthread_gtimer);
|
|
|
|
queue_work(boost_manager->otg_boost_workq,
|
|
&boost_manager->kick_work);
|
|
|
|
return ALARMTIMER_NORESTART;
|
|
}
|
|
|
|
static struct charger_ops hl7005_chg_ops = {
|
|
|
|
/* Normal charging */
|
|
.dump_registers = hl7005_dump_register,
|
|
.enable = hl7005_enable_charging,
|
|
.get_charging_current = hl7005_get_current,
|
|
.set_charging_current = hl7005_set_current,
|
|
.get_input_current = hl7005_get_input_current,
|
|
.set_input_current = hl7005_set_input_current,
|
|
/*.get_constant_voltage = hl7005_get_battery_voreg,*/
|
|
.set_constant_voltage = hl7005_set_cv_voltage,
|
|
.kick_wdt = hl7005_reset_watch_dog_timer,
|
|
.is_charging_done = hl7005_get_charging_status,
|
|
/* OTG */
|
|
.enable_otg = hl7005_charger_enable_otg,
|
|
.event = hl7005_do_event,
|
|
};
|
|
|
|
static int hl7005_driver_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int ret = 0;
|
|
struct hl7005_info *info = NULL;
|
|
|
|
info = devm_kzalloc(&client->dev, sizeof(struct hl7005_info),
|
|
GFP_KERNEL);
|
|
|
|
if (!info)
|
|
return -ENOMEM;
|
|
|
|
new_client = client;
|
|
info->dev = &client->dev;
|
|
ret = hl7005_parse_dt(info, &client->dev);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Register charger device */
|
|
info->chg_dev = charger_device_register(info->chg_dev_name,
|
|
&client->dev, info, &hl7005_chg_ops, &info->chg_props);
|
|
|
|
if (IS_ERR_OR_NULL(info->chg_dev)) {
|
|
pr_err("%s: register charger device failed\n", __func__);
|
|
ret = PTR_ERR(info->chg_dev);
|
|
return ret;
|
|
}
|
|
|
|
ret = hl7005_get_vender_code();
|
|
|
|
if (ret != 2) {
|
|
pr_err("%s: get vendor id failed\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
#if defined(HIGH_BATTERY_VOLTAGE_SUPPORT)
|
|
/* ISAFE = 1250mA, VSAFE = 4.34V */
|
|
hl7005_reg_config_interface(0x06, 0x77);
|
|
#else
|
|
hl7005_reg_config_interface(0x06, 0x70);
|
|
#endif
|
|
/* kick chip watch dog */
|
|
hl7005_reg_config_interface(0x00, 0xC0);
|
|
/* TE=1, CE=0, HZ_MODE=0, OPA_MODE=0 */
|
|
hl7005_reg_config_interface(0x01, 0xb8);
|
|
hl7005_reg_config_interface(0x05, 0x03);
|
|
/* 146mA */
|
|
hl7005_reg_config_interface(0x04, 0x1A);
|
|
|
|
hl7005_dump_register(info->chg_dev);
|
|
|
|
alarm_init(&info->otg_kthread_gtimer, ALARM_BOOTTIME,
|
|
usbotg_gtimer_func);
|
|
|
|
info->otg_boost_workq =
|
|
create_singlethread_workqueue("otg_boost_workq");
|
|
INIT_WORK(&info->kick_work, usbotg_boost_kick_work);
|
|
info->polling_interval = 20;
|
|
g_info = info;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id hl7005_of_match[] = {
|
|
{.compatible = "halo,hl7005"},
|
|
{},
|
|
};
|
|
#else
|
|
static struct i2c_board_info i2c_hl7005 __initdata = {
|
|
I2C_BOARD_INFO("hl7005", (hl7005_SLAVE_ADDR_WRITE >> 1))
|
|
};
|
|
#endif
|
|
|
|
static struct i2c_driver hl7005_driver = {
|
|
.driver = {
|
|
.name = "hl7005",
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = hl7005_of_match,
|
|
#endif
|
|
},
|
|
.probe = hl7005_driver_probe,
|
|
.id_table = hl7005_i2c_id,
|
|
};
|
|
|
|
static int __init hl7005_init(void)
|
|
{
|
|
|
|
if (i2c_add_driver(&hl7005_driver) != 0)
|
|
pr_info("Failed to register hl7005 i2c driver.\n");
|
|
else
|
|
pr_info("Success to register hl7005 i2c driver.\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit hl7005_exit(void)
|
|
{
|
|
i2c_del_driver(&hl7005_driver);
|
|
}
|
|
|
|
module_init(hl7005_init);
|
|
module_exit(hl7005_exit);
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("I2C hl7005 Driver");
|
|
MODULE_AUTHOR("Henry Chen<henryc.chen@mediatek.com>");
|