7732 lines
246 KiB
C
Executable File
7732 lines
246 KiB
C
Executable File
/*
|
|
* stwlc89_charger.c
|
|
* Samsung stwlc89 IC Charger Driver
|
|
*
|
|
* Copyright (C) 2024 Samsung Electronics
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include "stwlc89_charger.h"
|
|
#include <linux/errno.h>
|
|
#include <linux/version.h>
|
|
#include <linux/device.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/irqdomain.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/kernel.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/sysctl.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/firmware.h>
|
|
#if IS_ENABLED(CONFIG_SPU_VERIFY)
|
|
#include <linux/spu-verify.h>
|
|
#endif
|
|
#include "stwlc89_debug.h"
|
|
|
|
#define ENABLE 1
|
|
#define DISABLE 0
|
|
#define CMD_CNT 3
|
|
#define ISR_CNT 10
|
|
#define MAX_I2C_ERROR_COUNT 30
|
|
#define MAX_GPIO_IRQ_MISSING_COUNT 20
|
|
#define MAX_MTP_PGM_CNT 3
|
|
|
|
#define MAX_BUF 4095
|
|
#define SENDSZ 16
|
|
|
|
#define VALID_VRECT_LEVEL 2700
|
|
|
|
static char * __read_mostly carrierid;
|
|
module_param(carrierid, charp, 0444);
|
|
|
|
#if defined(CONFIG_WIRELESS_IC_PARAM)
|
|
static unsigned int __read_mostly wireless_ic;
|
|
module_param(wireless_ic, uint, 0444);
|
|
#endif
|
|
|
|
static u8 ADT_buffer_rdata[MAX_BUF] = {0, };
|
|
static int adt_readSize;
|
|
static bool is_shutdn;
|
|
|
|
static int pdrc_active;
|
|
static int pdrc_inactive;
|
|
|
|
typedef struct _sgf_data {
|
|
unsigned int size;
|
|
unsigned int type;
|
|
char *data;
|
|
} sgf_data;
|
|
|
|
static const u16 mfc_stm_vout_val16[] = {
|
|
0x0035, /* MFC_VOUT_4_5V */
|
|
0x003F, /* MFC_VOUT_4_7V */
|
|
0x0044, /* MFC_VOUT_4_8V */
|
|
0x0049, /* MFC_VOUT_4_9V */
|
|
0x004E, /* MFC_VOUT_5V */
|
|
0x0067, /* MFC_VOUT_5_5V */
|
|
0x0080, /* MFC_VOUT_6V */
|
|
0x00B2, /* MFC_VOUT_7V */
|
|
0x00E4, /* MFC_VOUT_8V */
|
|
0x0116, /* MFC_VOUT_9V */
|
|
0x0148, /* MFC_VOUT_10V */
|
|
0x017A, /* MFC_VOUT_11V */
|
|
0x01AC, /* MFC_VOUT_12V */
|
|
0x01C5, /* MFC_VOUT_12_5V */
|
|
0x003F, /* MFC_VOUT_OTG, 4.7V */
|
|
0x01DE, /* MFC_VOUT_13V */
|
|
};
|
|
|
|
static struct device_attribute mfc_attrs[] = {
|
|
STWLC89_ATTR(mfc_addr),
|
|
STWLC89_ATTR(mfc_size),
|
|
STWLC89_ATTR(mfc_data),
|
|
STWLC89_ATTR(mfc_packet),
|
|
STWLC89_ATTR(mfc_epp_t),
|
|
};
|
|
|
|
static enum power_supply_property mfc_charger_props[] = {
|
|
POWER_SUPPLY_PROP_ONLINE,
|
|
};
|
|
|
|
static int mfc_reg_multi_write_verify(struct i2c_client *client, u16 reg, const u8 *val, int size);
|
|
static irqreturn_t mfc_wpc_det_irq_thread(int irq, void *irq_data);
|
|
static irqreturn_t mfc_wpc_irq_thread(int irq, void *irq_data);
|
|
static int mfc_reg_multi_write_verify(struct i2c_client *client, u16 reg, const u8 *val, int size);
|
|
static void mfc_deactivate_work_content(struct mfc_charger_data *charger);
|
|
static void mfc_mpp_epp_nego_done(struct mfc_charger_data *charger);
|
|
|
|
#if defined(CONFIG_WIRELESS_IC_PARAM)
|
|
static unsigned int mfc_get_wrlic(void) { return wireless_ic; }
|
|
#endif
|
|
|
|
static bool carrierid_is(char *str)
|
|
{
|
|
if (carrierid == NULL)
|
|
return false;
|
|
|
|
pr_info("%s: %s\n", __func__, carrierid);
|
|
|
|
return !strncmp(carrierid, str, 3);
|
|
}
|
|
|
|
static int mfc_get_volt(u32 vout)
|
|
{
|
|
switch(vout) {
|
|
case WIRELESS_VOUT_5V:
|
|
return 5;
|
|
case WIRELESS_VOUT_9V:
|
|
return 9;
|
|
case WIRELESS_VOUT_10V:
|
|
return 10;
|
|
case WIRELESS_VOUT_11V:
|
|
return 11;
|
|
case WIRELESS_VOUT_12V:
|
|
return 12;
|
|
case WIRELESS_VOUT_13V:
|
|
return 13;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int stwlc89_get_op_mode(void *pdata)
|
|
{
|
|
struct mfc_charger_data *charger = pdata;
|
|
int wc_opmode = WPC_OP_MODE_NONE;
|
|
|
|
if (!charger->det_state)
|
|
return wc_opmode;
|
|
|
|
switch (charger->rx_op_mode) {
|
|
case MFC_RX_MODE_WPC_BPP:
|
|
wc_opmode = is_ppde_wireless_type(charger->pdata->cable_type) ? WPC_OP_MODE_PPDE : WPC_OP_MODE_BPP;
|
|
break;
|
|
case MFC_RX_MODE_WPC_EPP:
|
|
case MFC_RX_MODE_WPC_EPP_NEGO:
|
|
wc_opmode = WPC_OP_MODE_EPP;
|
|
break;
|
|
case MFC_RX_MODE_WPC_MPP_RESTRICT:
|
|
case MFC_RX_MODE_WPC_MPP_FULL:
|
|
case MFC_RX_MODE_WPC_MPP_CLOAK:
|
|
case MFC_RX_MODE_WPC_MPP_NEGO:
|
|
wc_opmode = charger->pdata->mpp_disable ? WPC_OP_MODE_EPP : WPC_OP_MODE_MPP;
|
|
break;
|
|
}
|
|
return wc_opmode;
|
|
}
|
|
|
|
static bool stwlc_is_mpp_op_mode(struct mfc_charger_data *charger)
|
|
{
|
|
bool ret = false;
|
|
|
|
switch (charger->rx_op_mode) {
|
|
case MFC_RX_MODE_WPC_MPP_RESTRICT:
|
|
case MFC_RX_MODE_WPC_MPP_FULL:
|
|
case MFC_RX_MODE_WPC_MPP_CLOAK:
|
|
case MFC_RX_MODE_WPC_MPP_NEGO:
|
|
ret = true;
|
|
// Fall through
|
|
break;
|
|
default:
|
|
ret = false;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int stwlc89_get_qi_ver(void *pdata)
|
|
{
|
|
struct mfc_charger_data *charger = pdata;
|
|
|
|
if (!charger->det_state)
|
|
return 0;
|
|
|
|
return (charger->mpp_epp_tx_id >> 16);
|
|
}
|
|
|
|
static int stwlc89_get_auth_mode(void *pdata)
|
|
{
|
|
struct mfc_charger_data *charger = pdata;
|
|
|
|
pr_info("%s: det_state(%d) rx_op_mode(%d)\n",
|
|
__func__, charger->det_state, charger->rx_op_mode);
|
|
|
|
if (!charger->det_state)
|
|
return WPC_AUTH_MODE_NONE;
|
|
|
|
if (mpp_mode(charger->rx_op_mode))
|
|
return WPC_AUTH_MODE_MPP;
|
|
|
|
if (is_ppde_wireless_type(charger->pdata->cable_type))
|
|
return WPC_AUTH_MODE_PPDE;
|
|
|
|
if (epp_mode(charger->rx_op_mode))
|
|
return WPC_AUTH_MODE_EPP;
|
|
|
|
return WPC_AUTH_MODE_BPP;
|
|
}
|
|
|
|
static const struct sb_wireless_op stwlc89_sbw_op = {
|
|
.get_op_mode = stwlc89_get_op_mode,
|
|
.get_qi_ver = stwlc89_get_qi_ver,
|
|
.get_auth_mode = stwlc89_get_auth_mode,
|
|
};
|
|
|
|
static void mfc_check_i2c_error(struct mfc_charger_data *charger, bool is_error)
|
|
{
|
|
u8 wpc_det = 0;
|
|
u8 wpc_pdrc = 0;
|
|
u8 wpc_pdet_b = 0;
|
|
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_MPP_FAKE)
|
|
return;
|
|
|
|
if (!is_error) {
|
|
charger->i2c_error_count = 0;
|
|
charger->gpio_irq_missing_wa_cnt = 0;
|
|
return;
|
|
}
|
|
|
|
wpc_det = gpio_get_value(charger->pdata->wpc_det);
|
|
wpc_pdrc = gpio_get_value(charger->pdata->wpc_pdrc);
|
|
wpc_pdet_b = gpio_get_value(charger->pdata->wpc_pdet_b);
|
|
|
|
charger->i2c_error_count =
|
|
(charger->det_state && wpc_det) ?
|
|
(charger->i2c_error_count + 1) : 0;
|
|
|
|
if (charger->i2c_error_count > MAX_I2C_ERROR_COUNT) {
|
|
charger->i2c_error_count = 0;
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_i2c_error_work, 0);
|
|
}
|
|
|
|
/* gpio irq missing W/A */
|
|
if (wpc_det || (wpc_pdrc == pdrc_active) || charger->pdata->cable_type == SEC_BATTERY_CABLE_NONE ||
|
|
(charger->rx_phm_status && !wpc_pdet_b)) {
|
|
charger->gpio_irq_missing_wa_cnt = 0;
|
|
} else {
|
|
charger->gpio_irq_missing_wa_cnt++;
|
|
pr_info("%s: gpio irq missing W/A(%d), det(%d), pdrc(%d), ct(%d), phm(%d), pdet_b(%d)\n",
|
|
__func__, charger->gpio_irq_missing_wa_cnt, wpc_det, wpc_pdrc,
|
|
charger->pdata->cable_type, charger->rx_phm_status, wpc_pdet_b);
|
|
|
|
if (charger->gpio_irq_missing_wa_cnt > MAX_GPIO_IRQ_MISSING_COUNT) {
|
|
charger->gpio_irq_missing_wa_cnt = 0;
|
|
__pm_stay_awake(charger->wpc_det_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_deactivate_work, msecs_to_jiffies(0));
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool is_no_hv(struct mfc_charger_data *charger)
|
|
{
|
|
return (charger->pdata->no_hv == 1);
|
|
}
|
|
|
|
static int mfc_reg_read(struct i2c_client *client, u16 reg, u8 *val)
|
|
{
|
|
struct mfc_charger_data *charger = i2c_get_clientdata(client);
|
|
int ret;
|
|
struct i2c_msg msg[2];
|
|
u8 wbuf[2];
|
|
u8 rbuf[2];
|
|
|
|
if (charger->reg_access_lock) {
|
|
pr_err("%s: can not access to reg during fw update\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
msg[0].addr = client->addr;
|
|
msg[0].flags = client->flags & I2C_M_TEN;
|
|
msg[0].len = 2;
|
|
msg[0].buf = wbuf;
|
|
|
|
wbuf[0] = (reg & 0xFF00) >> 8;
|
|
wbuf[1] = (reg & 0xFF);
|
|
|
|
msg[1].addr = client->addr;
|
|
msg[1].flags = I2C_M_RD;
|
|
msg[1].len = 1;
|
|
msg[1].buf = rbuf;
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
ret = i2c_transfer(client->adapter, msg, 2);
|
|
mfc_check_i2c_error(charger, (ret < 0));
|
|
mutex_unlock(&charger->io_lock);
|
|
if (ret < 0) {
|
|
pr_err("%s: i2c read error, reg: 0x%x, ret: %d (called by %ps)\n",
|
|
__func__, reg, ret, __builtin_return_address(0));
|
|
return -1;
|
|
}
|
|
|
|
*val = rbuf[0];
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mfc_reg_multi_read(struct i2c_client *client, u16 reg, u8 *val, int size)
|
|
{
|
|
struct mfc_charger_data *charger = i2c_get_clientdata(client);
|
|
int ret;
|
|
struct i2c_msg msg[2];
|
|
u8 wbuf[2];
|
|
|
|
if (charger->reg_access_lock) {
|
|
pr_err("%s: can not access to reg during fw update\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
msg[0].addr = client->addr;
|
|
msg[0].flags = client->flags & I2C_M_TEN;
|
|
msg[0].len = 2;
|
|
msg[0].buf = wbuf;
|
|
|
|
wbuf[0] = (reg & 0xFF00) >> 8;
|
|
wbuf[1] = (reg & 0xFF);
|
|
|
|
msg[1].addr = client->addr;
|
|
msg[1].flags = I2C_M_RD;
|
|
msg[1].len = size;
|
|
msg[1].buf = val;
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
ret = i2c_transfer(client->adapter, msg, 2);
|
|
mfc_check_i2c_error(charger, (ret < 0));
|
|
mutex_unlock(&charger->io_lock);
|
|
if (ret < 0) {
|
|
pr_err("%s: i2c transfer fail", __func__);
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mfc_reg_write(struct i2c_client *client, u16 reg, u8 val)
|
|
{
|
|
struct mfc_charger_data *charger = i2c_get_clientdata(client);
|
|
int ret;
|
|
unsigned char data[3] = { reg >> 8, reg & 0xff, val };
|
|
|
|
if (charger->reg_access_lock) {
|
|
pr_err("%s: can not access to reg during fw update\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
ret = i2c_master_send(client, data, 3);
|
|
mfc_check_i2c_error(charger, (ret < 3));
|
|
mutex_unlock(&charger->io_lock);
|
|
if (ret < 3) {
|
|
pr_err("%s: i2c write error, reg: 0x%x, ret: %d (called by %ps)\n",
|
|
__func__, reg, ret, __builtin_return_address(0));
|
|
return ret < 0 ? ret : -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mfc_reg_multi_write(struct i2c_client *client, u16 reg, u8 *val, int size)
|
|
{
|
|
struct mfc_charger_data *charger = i2c_get_clientdata(client);
|
|
int ret;
|
|
unsigned char *data;
|
|
|
|
if (charger->reg_access_lock) {
|
|
pr_err("%s: can not access to reg during fw update\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
data = kmalloc((2 + size) * sizeof(u8), GFP_KERNEL);
|
|
if (data == NULL)
|
|
return -1;
|
|
|
|
data[0] = reg >> 8;
|
|
data[1] = reg & 0xff;
|
|
memcpy(&data[2], val, size);
|
|
|
|
mutex_lock(&charger->io_lock);
|
|
ret = i2c_master_send(client, data, (2 + size));
|
|
mfc_check_i2c_error(charger, (ret < (2 + size)));
|
|
mutex_unlock(&charger->io_lock);
|
|
kfree(data);
|
|
if (ret < 3) {
|
|
pr_err("%s: i2c write error, reg: 0x%x, ret: %d (called by %ps)\n",
|
|
__func__, reg, ret, __builtin_return_address(0));
|
|
return ret < 0 ? ret : -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mfc_reg_update(struct i2c_client *client, u16 reg, u8 val, u8 mask)
|
|
{
|
|
//val = 0b 0000 0001
|
|
//ms = 0b 1111 1110
|
|
struct mfc_charger_data *charger = i2c_get_clientdata(client);
|
|
unsigned char data[3] = {reg >> 8, reg & 0xff, val};
|
|
u8 data2;
|
|
int ret;
|
|
|
|
if (charger->reg_access_lock) {
|
|
pr_err("%s: can not access to reg during fw update\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
ret = mfc_reg_read(client, reg, &data2);
|
|
if (ret >= 0) {
|
|
u8 old_val = data2 & 0xff;
|
|
u8 new_val = (val & mask) | (old_val & (~mask));
|
|
|
|
data[2] = new_val;
|
|
mutex_lock(&charger->io_lock);
|
|
ret = i2c_master_send(client, data, 3);
|
|
mfc_check_i2c_error(charger, (ret < 3));
|
|
mutex_unlock(&charger->io_lock);
|
|
if (ret < 3) {
|
|
pr_err("%s: i2c write error, reg: 0x%x, ret: %d\n",
|
|
__func__, reg, ret);
|
|
return ret < 0 ? ret : -EIO;
|
|
}
|
|
}
|
|
mfc_reg_read(client, reg, &data2);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int stwlc_i2c_read(struct i2c_client *client, u8 *cmd, int cmd_length,
|
|
u8 *read_data, int read_count)
|
|
{
|
|
struct i2c_msg msg[2];
|
|
int err;
|
|
|
|
msg[0].addr = client->addr;
|
|
msg[0].buf = cmd;
|
|
msg[0].len = cmd_length;
|
|
msg[0].flags = 0;
|
|
|
|
msg[1].addr = client->addr;
|
|
msg[1].buf = read_data;
|
|
msg[1].len = read_count;
|
|
msg[1].flags = I2C_M_RD;
|
|
|
|
err = i2c_transfer(client->adapter, msg, 2);
|
|
if (err < OK) {
|
|
pr_err("%s mfc, i2c transfer failed! err: %d\n", __func__, err);
|
|
return err;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
static int stwlc_i2c_write(struct i2c_client *client, u8 *cmd, int cmd_length)
|
|
{
|
|
struct i2c_msg msg[1];
|
|
int err;
|
|
|
|
msg[0].addr = client->addr;
|
|
msg[0].buf = cmd;
|
|
msg[0].len = cmd_length;
|
|
msg[0].flags = 0;
|
|
|
|
err = i2c_transfer(client->adapter, msg, 1);
|
|
if (err < OK) {
|
|
pr_err("%s mfc, i2c transfer failed! err: %d\n", __func__, err);
|
|
return err;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
static int stwlc_hw_i2c_write(struct i2c_client *client, u32 addr, u8 *data,
|
|
u32 data_length)
|
|
{
|
|
u8 *cmd = kmalloc((5 + data_length) * sizeof(u8), GFP_KERNEL);
|
|
|
|
cmd[0] = OPCODE_WRITE;
|
|
cmd[1] = (u8)((addr >> 24) & 0xFF);
|
|
cmd[2] = (u8)((addr >> 16) & 0xFF);
|
|
cmd[3] = (u8)((addr >> 8) & 0xFF);
|
|
cmd[4] = (u8)((addr >> 0) & 0xFF);
|
|
memcpy(&cmd[5], data, data_length);
|
|
|
|
if ((stwlc_i2c_write(client, cmd, (5 + data_length))) < OK) {
|
|
pr_err("%s mfc, Error in writing Hardware I2c!\n", __func__);
|
|
kfree(cmd);
|
|
return E_BUS_W;
|
|
}
|
|
|
|
kfree(cmd);
|
|
return OK;
|
|
}
|
|
|
|
static int stwlc_fw_i2c_write(struct i2c_client *client, u16 addr, u8 *data,
|
|
u32 data_length)
|
|
{
|
|
u8 *cmd = kmalloc((2 + data_length) * sizeof(u8), GFP_KERNEL);
|
|
|
|
cmd[0] = (u8)((addr >> 8) & 0xFF);
|
|
cmd[1] = (u8)((addr >> 0) & 0xFF);
|
|
memcpy(&cmd[2], data, data_length);
|
|
|
|
if ((stwlc_i2c_write(client, cmd, (2 + data_length))) < OK) {
|
|
pr_err("%s mfc, ERROR: in writing Hardware I2c!\n", __func__);
|
|
kfree(cmd);
|
|
return E_BUS_W;
|
|
}
|
|
|
|
kfree(cmd);
|
|
return OK;
|
|
}
|
|
|
|
static int stwlc_fw_i2c_read(struct i2c_client *client, u16 addr, u8 *read_buff,
|
|
int read_count)
|
|
{
|
|
u8 cmd[2];
|
|
|
|
cmd[0] = (u8)((addr >> 8) & 0xFF);
|
|
cmd[1] = (u8)((addr >> 0) & 0xFF);
|
|
|
|
if ((stwlc_i2c_read(client, cmd, 2, read_buff, read_count)) < OK) {
|
|
pr_err("%s mfc, Error in writing Hardware I2c!\n", __func__);
|
|
return E_BUS_WR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
static int stwlc_nvm_write_sector(struct i2c_client *client, const u8 *data,
|
|
int data_length, int sector_index)
|
|
{
|
|
int err = 0;
|
|
int i = 0;
|
|
int timeout = 1;
|
|
u8 reg_value = (u8)sector_index;
|
|
u8 write_buff[NVM_SECTOR_SIZE_BYTES];
|
|
const int VNM_SECTOR_PAYLOAD_SIZE = 0x30;
|
|
int remaining = data_length;
|
|
int to_write_now = 0;
|
|
int written_already = 0;
|
|
|
|
pr_info("%s mfc, writing sector %02X\n", __func__, sector_index);
|
|
if (data_length > NVM_SECTOR_SIZE_BYTES) {
|
|
pr_err("%s mfc, sector data bigger than 128 bytes\n", __func__);
|
|
return E_INVALID_INPUT;
|
|
}
|
|
|
|
memset(write_buff, 0, NVM_SECTOR_SIZE_BYTES);
|
|
memcpy(write_buff, data, data_length);
|
|
|
|
err = stwlc_fw_i2c_write(client, FWREG_NVM_SECTOR_INDEX_ADDR, ®_value, 1);
|
|
if (err != 0)
|
|
return err;
|
|
|
|
while (remaining > 0) {
|
|
to_write_now = remaining > VNM_SECTOR_PAYLOAD_SIZE
|
|
? VNM_SECTOR_PAYLOAD_SIZE : remaining;
|
|
err = stwlc_fw_i2c_write(client, FWREG_AUX_DATA_00_ADDR + (written_already & 0xff),
|
|
write_buff + written_already, to_write_now);
|
|
if (err != 0)
|
|
return err;
|
|
remaining -= to_write_now;
|
|
written_already += to_write_now;
|
|
}
|
|
|
|
reg_value = 0x04;
|
|
err = stwlc_fw_i2c_write(client, FWREG_SYS_CMD_ADDR, ®_value, 1);
|
|
if (err != 0)
|
|
return err;
|
|
|
|
for (i = 0; i < NVM_SECTOR_WRITE_POLLING_TIMEOUT; i++) {
|
|
msleep(NVM_SECTOR_WRITE_SLEEP_MS);
|
|
err = stwlc_fw_i2c_read(client, FWREG_SYS_CMD_ADDR, ®_value, 1);
|
|
if (err != 0)
|
|
return err;
|
|
if ((reg_value & 0x04) == 0) {
|
|
timeout = 0;
|
|
break;
|
|
}
|
|
}
|
|
return timeout == 0 ? 0 : E_TIMEOUT;
|
|
}
|
|
|
|
static int stwlc_nvm_write_bulk(struct i2c_client *client, u8 *data,
|
|
int data_length, u8 sector_index)
|
|
{
|
|
int err = 0;
|
|
int remaining = data_length;
|
|
int to_write_now = 0;
|
|
int written_already = 0;
|
|
|
|
while (remaining > 0) {
|
|
to_write_now = remaining > NVM_SECTOR_SIZE_BYTES
|
|
? NVM_SECTOR_SIZE_BYTES : remaining;
|
|
err = stwlc_nvm_write_sector(client, data + written_already,
|
|
to_write_now, sector_index);
|
|
if (err != 0)
|
|
return err;
|
|
remaining -= to_write_now;
|
|
written_already += to_write_now;
|
|
sector_index++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int PgmOTPwRAM_STM(struct mfc_charger_data *charger, unsigned short OtpAddr,
|
|
const u8 *srcData, int srcOffs, int size)
|
|
{
|
|
int err = MFC_FWUP_ERR_COMMON_FAIL;
|
|
u8 fw_ver[6] = {0,};
|
|
const struct stmwlc_fw_bin_header *fw_header = (struct stmwlc_fw_bin_header *)srcData;
|
|
u8 reg_value = 0;
|
|
int i = 0;
|
|
int timeout = 1;
|
|
u8 *patch_data = (u8 *)(srcData + 32);
|
|
u8 *cfg_data = (u8 *)(patch_data + fw_header->sec0_size);
|
|
|
|
stwlc_fw_i2c_read(charger->client, MFC_FW_MAJOR_REV_L_REG, &fw_ver[0], 1);
|
|
stwlc_fw_i2c_read(charger->client, MFC_FW_MAJOR_REV_H_REG, &fw_ver[1], 1);
|
|
pr_info("%s mfc, BEFORE rx major firmware version 0x%x\n",
|
|
__func__, fw_ver[0] | (fw_ver[1] << 8));
|
|
|
|
stwlc_fw_i2c_read(charger->client, MFC_FW_MINOR_REV_L_REG, &fw_ver[2], 1);
|
|
stwlc_fw_i2c_read(charger->client, MFC_FW_MINOR_REV_H_REG, &fw_ver[3], 1);
|
|
pr_info("%s mfc, BEFORE rx minor firmware version 0x%x\n",
|
|
__func__, fw_ver[2] | (fw_ver[3] << 8));
|
|
|
|
stwlc_fw_i2c_read(charger->client, MFC_ADC_DIE_TEMP_L_REG, &fw_ver[4], 1);
|
|
stwlc_fw_i2c_read(charger->client, MFC_ADC_DIE_TEMP_H_REG, &fw_ver[5], 1);
|
|
pr_info("%s mfc, Chip temp 0x%x(%d)\n",
|
|
__func__, fw_ver[4] | (fw_ver[5] << 8), fw_ver[4] | (fw_ver[5] << 8));
|
|
|
|
pr_info("%s mfc, NEW rx major firmware version 0x%x\n",
|
|
__func__, fw_header->fw_major);
|
|
pr_info("%s mfc, NEW rx minor firmware version 0x%x\n",
|
|
__func__, fw_header->fw_minor);
|
|
|
|
/* System reset and disable patch loading */
|
|
reg_value = 0x80;
|
|
err = stwlc_fw_i2c_write(charger->client, FWREG_SYS_CMD_ADDR, ®_value, 1);
|
|
if (err != OK)
|
|
return err;
|
|
msleep(AFTER_SYS_RESET_SLEEP_MS);
|
|
|
|
/* DC mode checking */
|
|
err = stwlc_fw_i2c_read(charger->client, FWREG_OP_MODE_ADDR, ®_value, 1);
|
|
if (err != OK)
|
|
return err;
|
|
pr_info("%s mfc, OP MODE %02X\n", __func__, reg_value);
|
|
if (reg_value != FW_OP_MODE_SA) {
|
|
pr_err("%s mfc, no DC power detected, nvm programming aborted\n", __func__);
|
|
return E_UNEXPECTED_OP_MODE;
|
|
}
|
|
|
|
pr_info("%s mfc, NVM Erase...\n", __func__);
|
|
reg_value = 0xC5;
|
|
err = stwlc_fw_i2c_write(charger->client, FWREG_NVM_WR_PWD_ADDR, ®_value, 1);
|
|
if (err != OK)
|
|
return err;
|
|
|
|
reg_value = 0x20;
|
|
err = stwlc_fw_i2c_write(charger->client, FWREG_SYS_CMD_ADDR, ®_value, 1);
|
|
if (err != OK)
|
|
return err;
|
|
|
|
for (i = 0; i < NVM_ERASE_POLLING_TIMEOUT; i++) {
|
|
msleep(NVM_ERASE_SLEEP_MS);
|
|
err = stwlc_fw_i2c_read(charger->client, FWREG_SYS_CMD_ADDR, ®_value, 1);
|
|
if (err != OK)
|
|
return err;
|
|
if ((reg_value & 0x20) == 0) {
|
|
timeout = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (timeout == 1)
|
|
return E_TIMEOUT;
|
|
|
|
pr_info("%s mfc, NVM Programming...\n", __func__);
|
|
err = stwlc_nvm_write_bulk(charger->client, patch_data, fw_header->sec0_size,
|
|
NVM_PATCH_START_SECTOR_INDEX);
|
|
if (err != OK)
|
|
return err;
|
|
|
|
err = stwlc_nvm_write_bulk(charger->client, cfg_data, fw_header->sec1_size,
|
|
NVM_CFG_START_SECTOR_INDEX);
|
|
if (err != OK)
|
|
return err;
|
|
|
|
/* System reset */
|
|
reg_value = 0x01;
|
|
stwlc_hw_i2c_write(charger->client, HWREG_RST_ADDR, ®_value, 1);
|
|
msleep(AFTER_SYS_RESET_SLEEP_MS);
|
|
|
|
return (err == OK ? MFC_FWUP_ERR_SUCCEEDED : MFC_FWUP_ERR_COMMON_FAIL);
|
|
}
|
|
|
|
static void stwlc89_set_target_ilim(struct mfc_charger_data *charger, int ilim)
|
|
{
|
|
u8 data = ilim/25;
|
|
|
|
if (charger->sleep_mode && charger->target_current <= ilim && charger->target_current) {
|
|
pr_info("@MPP %s: do not ctrl because sleep_mode(%d, %d)\n",
|
|
__func__, charger->target_current, ilim);
|
|
return;
|
|
}
|
|
|
|
charger->target_current = ilim;
|
|
|
|
pr_info("%s: ilim(%d) reg(0x%x)\n", __func__, charger->target_current, data);
|
|
mfc_reg_write(charger->client, MFC_TARGET_ILIM_REG, data);
|
|
}
|
|
|
|
static void stwlc89_set_recent_ilim(struct mfc_charger_data *charger, int ilim)
|
|
{
|
|
u8 data = ilim/25;
|
|
|
|
pr_info("%s: ilim(%d) reg(0x%x)\n", __func__, ilim, data);
|
|
mfc_reg_write(charger->client, MFC_RECENT_ILIM_REG, data);
|
|
}
|
|
|
|
static unsigned int mfc_get_firmware_version(struct mfc_charger_data *charger, int firm_mode)
|
|
{
|
|
unsigned int ver_major = 0, ver_minor = 0;
|
|
int ret;
|
|
u8 fw_major[2] = {0, };
|
|
u8 fw_minor[2] = {0, };
|
|
|
|
pr_info("%s: called by (%ps)\n", __func__, __builtin_return_address(0));
|
|
switch (firm_mode) {
|
|
case MFC_RX_FIRMWARE:
|
|
ret = mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_L_REG, &fw_major[0]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ret = mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_H_REG, &fw_major[1]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ver_major = fw_major[0] | (fw_major[1] << 8);
|
|
pr_info("%s: rx major firmware version 0x%x\n", __func__, ver_major);
|
|
|
|
ret = mfc_reg_read(charger->client, MFC_FW_MINOR_REV_L_REG, &fw_minor[0]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ret = mfc_reg_read(charger->client, MFC_FW_MINOR_REV_H_REG, &fw_minor[1]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ver_minor = fw_minor[0] | (fw_minor[1] << 8);
|
|
pr_info("%s: rx minor firmware version 0x%x\n", __func__, ver_minor);
|
|
break;
|
|
default:
|
|
pr_err("%s: Wrong firmware mode\n", __func__);
|
|
break;
|
|
}
|
|
|
|
#if defined(CONFIG_WIRELESS_IC_PARAM)
|
|
if (ver_minor > 0 && charger->wireless_fw_ver_param != ver_minor) {
|
|
pr_info("%s: fw_ver (param:0x%04X, version:0x%04X)\n",
|
|
__func__, charger->wireless_fw_ver_param, ver_minor);
|
|
charger->wireless_fw_ver_param = ver_minor;
|
|
charger->wireless_param_info &= 0xFF0000FF;
|
|
charger->wireless_param_info |= (charger->wireless_fw_ver_param & 0xFFFF) << 8;
|
|
pr_info("%s: wireless_param_info (0x%08X)\n", __func__, charger->wireless_param_info);
|
|
}
|
|
#endif
|
|
|
|
return ver_minor;
|
|
}
|
|
|
|
static unsigned int mfc_get_bin_fw_version(struct mfc_charger_data *charger)
|
|
{
|
|
int ret = 0;
|
|
static unsigned int bin_fw_ver;
|
|
const struct firmware *firm_data_bin;
|
|
const struct stmwlc_fw_bin_header *fw_header;
|
|
|
|
if (bin_fw_ver != 0)
|
|
return bin_fw_ver;
|
|
|
|
charger->fw_path = MFC_FLASH_FW_HEX_STM_PATH;
|
|
ret = request_firmware(&firm_data_bin, charger->fw_path, &charger->client->dev);
|
|
if (ret < 0) {
|
|
dev_err(&charger->client->dev, "%s: failed to request firmware %s (%d)\n",
|
|
__func__, charger->fw_path, ret);
|
|
return 0;
|
|
}
|
|
|
|
/* get minor firmware version from firmware file */
|
|
fw_header = (struct stmwlc_fw_bin_header *)firm_data_bin->data;
|
|
bin_fw_ver = fw_header->fw_minor;
|
|
release_firmware(firm_data_bin);
|
|
pr_info("%s: bin rx minor firmware version 0x%04X\n", __func__, bin_fw_ver);
|
|
|
|
return bin_fw_ver;
|
|
}
|
|
|
|
static bool mfc_check_chip_id(u8 chip_id_l, u8 chip_id_h)
|
|
{
|
|
pr_info("%s: CHIP_L(0x%x), CHIP_H(0x%x)\n", __func__, chip_id_l, chip_id_h);
|
|
if ((chip_id_l == 0x59) && (chip_id_h == 0x01))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static int mfc_get_chip_id(struct mfc_charger_data *charger)
|
|
{
|
|
u8 chip_id_l = 0, chip_id_h = 0;
|
|
int ret = 0;
|
|
|
|
ret = mfc_reg_read(charger->client, MFC_CHIP_ID_L_REG, &chip_id_l);
|
|
if (ret < 0)
|
|
return -1;
|
|
|
|
ret = mfc_reg_read(charger->client, MFC_CHIP_ID_H_REG, &chip_id_h);
|
|
if (ret < 0)
|
|
return -1;
|
|
|
|
if (!mfc_check_chip_id(chip_id_l, chip_id_h))
|
|
return -1;
|
|
|
|
charger->chip_id = MFC_CHIP_STM;
|
|
#if defined(CONFIG_WIRELESS_IC_PARAM)
|
|
if (chip_id_l > 0 && charger->wireless_chip_id_param != chip_id_l) {
|
|
pr_info("%s: chip_id_l (param:0x%02X, chip_id:0x%02X)\n",
|
|
__func__, charger->wireless_chip_id_param, chip_id_l);
|
|
charger->wireless_chip_id_param = chip_id_l;
|
|
charger->wireless_param_info &= 0x00FFFFFF;
|
|
charger->wireless_param_info |= (charger->wireless_chip_id_param & 0xFF) << 24;
|
|
pr_info("%s: wireless_param_info (0x%08X)\n", __func__, charger->wireless_param_info);
|
|
}
|
|
#endif
|
|
|
|
return charger->chip_id;
|
|
}
|
|
|
|
static int mfc_get_ic_revision(struct mfc_charger_data *charger, int read_mode)
|
|
{
|
|
u8 temp;
|
|
int ret = -1;
|
|
|
|
pr_info("%s: called by (%ps)\n", __func__, __builtin_return_address(0));
|
|
|
|
switch (read_mode) {
|
|
case MFC_IC_REVISION:
|
|
ret = mfc_reg_read(charger->client, MFC_CHIP_REVISION_REG, &temp);
|
|
if (ret >= 0) {
|
|
temp &= MFC_CHIP_REVISION_MASK;
|
|
pr_info("%s: ic revision = %d\n", __func__, temp);
|
|
ret = temp;
|
|
}
|
|
break;
|
|
case MFC_IC_FONT:
|
|
ret = mfc_reg_read(charger->client, MFC_CHIP_REVISION_REG, &temp);
|
|
if (ret >= 0) {
|
|
temp &= MFC_CHIP_FONT_MASK;
|
|
pr_info("%s: ic font = %d\n", __func__, temp);
|
|
ret = temp;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void mfc_set_cmd_h_reg(struct mfc_charger_data *charger, u8 val, u8 mask)
|
|
{
|
|
u8 temp = 0;
|
|
int ret = 0, i = 0;
|
|
|
|
do {
|
|
ret = mfc_reg_update(charger->client, MFC_AP2MFC_CMD_H_REG, val, mask); // command
|
|
if (ret >= 0) {
|
|
usleep_range(10000, 11000);
|
|
ret = mfc_reg_read(charger->client, MFC_AP2MFC_CMD_H_REG, &temp); // check out set bit exists
|
|
pr_info("%s val(0x%x) mask(0x%x) --> read(0x%x)\n", __func__, val, mask, temp);
|
|
if (temp != 0)
|
|
pr_info("%s: CMD is not clear yet, cnt = %d\n", __func__, i);
|
|
if (ret < 0 || i > 3)
|
|
break;
|
|
}
|
|
i++;
|
|
} while ((temp != 0) && (i < 3));
|
|
}
|
|
|
|
static void mfc_soft_reset_ask_stop(struct mfc_charger_data *charger)
|
|
{
|
|
mfc_set_cmd_h_reg(charger, MFC_CMD2_ASK_DIS_MASK, MFC_CMD2_ASK_DIS_MASK);
|
|
pr_info("%s: CEP Timeout\n", __func__);
|
|
}
|
|
|
|
static int mfc_get_adc(struct mfc_charger_data *charger, int adc_type)
|
|
{
|
|
int ret = 0;
|
|
u8 data[2] = {0,};
|
|
|
|
switch (adc_type) {
|
|
case MFC_ADC_VOUT:
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_VOUT_L_REG, &data[0]);
|
|
if (ret < 0)
|
|
break;
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_VOUT_H_REG, &data[1]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ret = (data[0] | (data[1] << 8));
|
|
charger->mfc_adc_vout = ret;
|
|
break;
|
|
case MFC_ADC_VRECT:
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_VRECT_L_REG, &data[0]);
|
|
if (ret < 0)
|
|
break;
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_VRECT_H_REG, &data[1]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ret = (data[0] | (data[1] << 8));
|
|
charger->mfc_adc_vrect = ret;
|
|
break;
|
|
case MFC_ADC_RX_IOUT:
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_IOUT_L_REG, &data[0]);
|
|
if (ret < 0)
|
|
break;
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_IOUT_H_REG, &data[1]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ret = (data[0] | (data[1] << 8));
|
|
charger->mfc_adc_rx_iout = ret;
|
|
break;
|
|
case MFC_ADC_DIE_TEMP:
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_DIE_TEMP_L_REG, &data[0]);
|
|
if (ret < 0)
|
|
break;
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_DIE_TEMP_H_REG, &data[1]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
/* only 4 MSB[3:0] field is used, Celsius */
|
|
data[1] &= 0x0f;
|
|
ret = (data[0] | (data[1] << 8));
|
|
break;
|
|
case MFC_ADC_OP_FRQ: /* kHz */
|
|
ret = mfc_reg_read(charger->client, MFC_TRX_OP_FREQ_L_REG, &data[0]);
|
|
if (ret < 0)
|
|
break;
|
|
ret = mfc_reg_read(charger->client, MFC_TRX_OP_FREQ_H_REG, &data[1]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ret = (data[0] | (data[1] << 8));
|
|
charger->mfc_adc_op_frq = ret;
|
|
break;
|
|
case MFC_ADC_TX_MAX_OP_FRQ:
|
|
ret = mfc_reg_read(charger->client, MFC_TX_MAX_OP_FREQ_L_REG, &data[0]);
|
|
if (ret < 0)
|
|
break;
|
|
ret = mfc_reg_read(charger->client, MFC_TX_MAX_OP_FREQ_H_REG, &data[1]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ret = (data[0] | (data[1] << 8)) / 10;
|
|
charger->mfc_adc_tx_max_op_frq = ret;
|
|
break;
|
|
case MFC_ADC_TX_MIN_OP_FRQ:
|
|
ret = mfc_reg_read(charger->client, MFC_TX_MIN_OP_FREQ_L_REG, &data[0]);
|
|
if (ret < 0)
|
|
break;
|
|
ret = mfc_reg_read(charger->client, MFC_TX_MIN_OP_FREQ_H_REG, &data[1]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ret = (data[0] | (data[1] << 8)) / 10;
|
|
charger->mfc_adc_tx_min_op_frq = ret;
|
|
break;
|
|
case MFC_ADC_PING_FRQ:
|
|
ret = mfc_reg_read(charger->client, MFC_TX_PING_FREQ_L_REG, &data[0]);
|
|
if (ret < 0)
|
|
break;
|
|
ret = mfc_reg_read(charger->client, MFC_TX_PING_FREQ_H_REG, &data[1]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ret = (data[0] | (data[1] << 8)) / 10;
|
|
charger->mfc_adc_ping_frq = ret;
|
|
break;
|
|
case MFC_ADC_TX_VOUT:
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_VOUT_L_REG, &data[0]);
|
|
if (ret < 0)
|
|
break;
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_VOUT_H_REG, &data[1]);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
ret = (data[0] | (data[1] << 8));
|
|
charger->mfc_adc_tx_vout = ret;
|
|
break;
|
|
case MFC_ADC_TX_IOUT:
|
|
if (charger->wc_tx_enable) {
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_IOUT_L_REG, &data[0]);
|
|
if (ret < 0)
|
|
break;
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_IOUT_H_REG, &data[1]);
|
|
if (ret < 0)
|
|
break;
|
|
} else {
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_IOUT_L_REG, &data[0]);
|
|
if (ret < 0)
|
|
break;
|
|
ret = mfc_reg_read(charger->client, MFC_ADC_IOUT_H_REG, &data[1]);
|
|
if (ret < 0)
|
|
break;
|
|
}
|
|
|
|
ret = (data[0] | (data[1] << 8));
|
|
charger->mfc_adc_tx_iout = ret;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mfc_set_wpc_en(struct mfc_charger_data *charger, char flag, char on)
|
|
{
|
|
union power_supply_propval value = {0, };
|
|
int enable = 0, temp = charger->wpc_en_flag;
|
|
int old_val = 0, new_val = 0;
|
|
|
|
psy_do_property("battery", get,
|
|
POWER_SUPPLY_PROP_STATUS, value);
|
|
|
|
mutex_lock(&charger->wpc_en_lock);
|
|
|
|
if (on) {
|
|
if (value.intval == POWER_SUPPLY_STATUS_DISCHARGING)
|
|
charger->wpc_en_flag |= WPC_EN_CHARGING;
|
|
charger->wpc_en_flag |= flag;
|
|
} else {
|
|
charger->wpc_en_flag &= ~flag;
|
|
}
|
|
|
|
if (charger->wpc_en_flag & WPC_EN_FW)
|
|
enable = 1;
|
|
else if (!(charger->wpc_en_flag & WPC_EN_SYSFS) || !(charger->wpc_en_flag & WPC_EN_CCIC))
|
|
enable = 0;
|
|
else if (!(charger->wpc_en_flag & (WPC_EN_SLATE | WPC_EN_MST)))
|
|
enable = 0;
|
|
else if (!(charger->wpc_en_flag & (WPC_EN_CHARGING | WPC_EN_MST | WPC_EN_TX)))
|
|
enable = 0;
|
|
else
|
|
enable = 1;
|
|
|
|
if (charger->pdata->wpc_en >= 0) {
|
|
old_val = gpio_get_value(charger->pdata->wpc_en);
|
|
if (enable)
|
|
gpio_direction_output(charger->pdata->wpc_en, 0);
|
|
else
|
|
gpio_direction_output(charger->pdata->wpc_en, 1);
|
|
new_val = gpio_get_value(charger->pdata->wpc_en);
|
|
|
|
pr_info("@DIS_MFC %s: before(0x%x), after(0x%x), en(%d), val(%d->%d)\n",
|
|
__func__, temp, charger->wpc_en_flag, enable, old_val, new_val);
|
|
|
|
mutex_unlock(&charger->wpc_en_lock);
|
|
if (old_val != new_val) {
|
|
value.intval = enable;
|
|
psy_do_property("battery", set,
|
|
POWER_SUPPLY_EXT_PROP_WPC_EN, value);
|
|
}
|
|
} else {
|
|
pr_info("@DIS_MFC %s: there`s no wpc_en\n", __func__);
|
|
mutex_unlock(&charger->wpc_en_lock);
|
|
}
|
|
}
|
|
|
|
static void mfc_set_coil_sw_en(struct mfc_charger_data *charger, int enable)
|
|
{
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
pr_info("@Tx_Mode %s: do not set with factory bin\n", __func__);
|
|
return;
|
|
#endif
|
|
|
|
if (charger->pdata->coil_sw_en < 0) {
|
|
pr_info("@Tx_Mode %s: no coil_sw_en\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (gpio_get_value(charger->pdata->coil_sw_en) == enable) {
|
|
pr_info("@Tx_Mode %s: Skip to set same config, val(%d)\n",
|
|
__func__, gpio_get_value(charger->pdata->coil_sw_en));
|
|
return;
|
|
}
|
|
|
|
gpio_direction_output(charger->pdata->coil_sw_en, enable);
|
|
pr_info("@Tx_Mode %s: en(%d), val(%d)\n", __func__,
|
|
enable, gpio_get_value(charger->pdata->coil_sw_en));
|
|
}
|
|
|
|
static void mfc_set_force_vout(struct mfc_charger_data *charger, int vout)
|
|
{
|
|
u8 data[2] = {0,};
|
|
|
|
if ((charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_EPP_FAKE) &&
|
|
(charger->rx_op_mode == MFC_RX_MODE_WPC_EPP_NEGO)) {
|
|
pr_info("%s: Escape MPP and EPP Calibration step\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (charger->wc_ldo_status == MFC_LDO_OFF) {
|
|
pr_info("%s: vout updated to 5V becauseof wc ldo\n", __func__);
|
|
vout = MFC_VOUT_5V;
|
|
}
|
|
|
|
data[0] = mfc_stm_vout_val16[vout] & 0xff;
|
|
data[1] = (mfc_stm_vout_val16[vout] & 0xff00) >> 8;
|
|
mfc_reg_write(charger->client, MFC_VOUT_SET_H_REG, data[1]);
|
|
mfc_reg_write(charger->client, MFC_VOUT_SET_L_REG, data[0]);
|
|
|
|
stwlc89_mfc_fod_set_vout(charger->fod, (3440 + (mfc_stm_vout_val16[vout] * 20)));
|
|
stwlc89_mfc_cmfet_set_vout(charger->cmfet, (3440 + (mfc_stm_vout_val16[vout] * 20)));
|
|
msleep(100);
|
|
|
|
pr_info("%s: set force vout(%s, 0x%04X) read = %d mV\n", __func__,
|
|
sb_rx_vout_str(vout), (data[0] | (data[1] << 8)), mfc_get_adc(charger, MFC_ADC_VOUT));
|
|
mfc_get_adc(charger, MFC_ADC_VRECT);
|
|
charger->pdata->vout_status = vout;
|
|
}
|
|
|
|
static void mfc_set_vout(struct mfc_charger_data *charger, int vout)
|
|
{
|
|
u8 data[2] = {0,};
|
|
|
|
if ((charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_MPP) ||
|
|
((charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_EPP_FAKE) &&
|
|
(charger->rx_op_mode == MFC_RX_MODE_WPC_EPP_NEGO))) {
|
|
pr_info("%s: Escape MPP and EPP Calibration step\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (charger->wc_ldo_status == MFC_LDO_OFF) {
|
|
pr_info("%s: vout updated to 5V becauseof wc ldo\n", __func__);
|
|
vout = MFC_VOUT_5V;
|
|
}
|
|
|
|
if (charger->is_otg_on && (vout > MFC_VOUT_5V)) {
|
|
pr_info("%s: skip set vout in otg case\n", __func__);
|
|
return;
|
|
}
|
|
|
|
data[0] = mfc_stm_vout_val16[vout] & 0xff;
|
|
data[1] = (mfc_stm_vout_val16[vout] & 0xff00) >> 8;
|
|
mfc_reg_write(charger->client, MFC_VOUT_SET_H_REG, data[1]);
|
|
mfc_reg_write(charger->client, MFC_VOUT_SET_L_REG, data[0]);
|
|
msleep(100);
|
|
|
|
pr_info("%s: set vout(%s, 0x%04X) read = %d mV\n", __func__,
|
|
sb_rx_vout_str(vout), (data[0] | (data[1] << 8)), mfc_get_adc(charger, MFC_ADC_VOUT));
|
|
mfc_get_adc(charger, MFC_ADC_VRECT);
|
|
charger->pdata->vout_status = vout;
|
|
|
|
stwlc89_mfc_cmfet_set_vout(charger->cmfet, (3440 + (mfc_stm_vout_val16[vout] * 20)));
|
|
}
|
|
|
|
static int mfc_get_vout(struct mfc_charger_data *charger)
|
|
{
|
|
u8 data[2] = {0,};
|
|
int ret;
|
|
|
|
ret = mfc_reg_read(charger->client, MFC_VOUT_SET_L_REG, &data[0]);
|
|
ret = mfc_reg_read(charger->client, MFC_VOUT_SET_H_REG, &data[1]);
|
|
|
|
if (ret < 0) {
|
|
pr_err("%s: fail to read vout. (%d)\n", __func__, ret);
|
|
} else {
|
|
ret = (data[0] | (data[1] << 8));
|
|
pr_info("%s: vout(0x%04x)\n", __func__, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mfc_uno_on(struct mfc_charger_data *charger, bool on)
|
|
{
|
|
union power_supply_propval value = {0, };
|
|
|
|
if (charger->wc_tx_enable && on) { /* UNO ON */
|
|
value.intval = SEC_BAT_CHG_MODE_UNO_ONLY;
|
|
psy_do_property("otg", set,
|
|
POWER_SUPPLY_EXT_PROP_CHARGE_UNO_CONTROL, value);
|
|
pr_info("%s: UNO ON\n", __func__);
|
|
} else if (on) { /* UNO ON */
|
|
value.intval = 1;
|
|
psy_do_property("otg", set,
|
|
POWER_SUPPLY_EXT_PROP_CHARGE_UNO_CONTROL, value);
|
|
pr_info("%s: UNO ON\n", __func__);
|
|
} else { /* UNO OFF */
|
|
value.intval = 0;
|
|
psy_do_property("otg", set,
|
|
POWER_SUPPLY_EXT_PROP_CHARGE_UNO_CONTROL, value);
|
|
|
|
if (delayed_work_pending(&charger->wpc_tx_duty_min_work)) {
|
|
__pm_relax(charger->wpc_tx_duty_min_ws);
|
|
cancel_delayed_work(&charger->wpc_tx_duty_min_work);
|
|
}
|
|
pr_info("%s: UNO OFF\n", __func__);
|
|
}
|
|
}
|
|
|
|
static void mfc_set_tx_min_op_freq(struct mfc_charger_data *charger, unsigned int op_freq)
|
|
{
|
|
u8 data[2] = {0,};
|
|
|
|
pr_info("%s: tx_min_op_freq = %d KHz\n", __func__, op_freq / 10);
|
|
|
|
data[0] = op_freq & 0xFF;
|
|
data[1] = (op_freq & 0xFF00) >> 8;
|
|
mfc_reg_write(charger->client, MFC_TX_MIN_OP_FREQ_L_REG, data[0]);
|
|
mfc_reg_write(charger->client, MFC_TX_MIN_OP_FREQ_H_REG, data[1]);
|
|
}
|
|
|
|
static void mfc_set_tx_max_op_freq(struct mfc_charger_data *charger, unsigned int op_freq)
|
|
{
|
|
u8 data[2] = {0,};
|
|
|
|
pr_info("%s: tx_max_op_freq = %d KHz\n", __func__, op_freq / 10);
|
|
|
|
data[0] = op_freq & 0xFF;
|
|
data[1] = (op_freq & 0xFF00) >> 8;
|
|
mfc_reg_write(charger->client, MFC_TX_MAX_OP_FREQ_L_REG, data[0]);
|
|
mfc_reg_write(charger->client, MFC_TX_MAX_OP_FREQ_H_REG, data[1]);
|
|
}
|
|
|
|
static void mfc_set_min_duty(struct mfc_charger_data *charger, unsigned int duty)
|
|
{
|
|
pr_info("%s: min duty = %d\n", __func__, duty);
|
|
mfc_reg_write(charger->client, MFC_TX_MIN_DUTY_SETTING_REG, duty);
|
|
}
|
|
|
|
static void mfc_set_cmd_l_reg(struct mfc_charger_data *charger, u8 val, u8 mask)
|
|
{
|
|
u8 temp = 0;
|
|
int ret = 0, i = 0;
|
|
|
|
do {
|
|
pr_info("%s\n", __func__);
|
|
ret = mfc_reg_update(charger->client, MFC_AP2MFC_CMD_L_REG, val, mask); // command
|
|
if (ret >= 0) {
|
|
usleep_range(10000, 11000);
|
|
ret = mfc_reg_read(charger->client, MFC_AP2MFC_CMD_L_REG, &temp); // check out set bit exists
|
|
if (temp != 0)
|
|
pr_info("%s: CMD is not clear yet, cnt = %d\n", __func__, i);
|
|
if (ret < 0 || i > 3)
|
|
break;
|
|
}
|
|
i++;
|
|
} while ((temp != 0) && (i < 3));
|
|
}
|
|
|
|
static void mfc_mpp_exit_cloak_turn_off_power(struct mfc_charger_data *charger)
|
|
{
|
|
charger->mpp_cloak = 0;
|
|
if (charger->pdata->mpp_sw > 0) {
|
|
if (gpio_get_value(charger->pdata->mpp_sw))
|
|
gpio_direction_output(charger->pdata->mpp_sw, 0);
|
|
}
|
|
pr_info("@MPP %s: coil sw (%d)\n", __func__,
|
|
(charger->pdata->mpp_sw < 0 ? charger->pdata->mpp_sw :
|
|
gpio_get_value(charger->pdata->mpp_sw)));
|
|
}
|
|
|
|
//need power on wireless IC
|
|
static void mfc_mpp_exit_cloak(struct mfc_charger_data *charger)
|
|
{
|
|
mfc_set_cmd_h_reg(charger, MFC_CMD2_MPP_EXIT_CLOAK_MASK, MFC_CMD2_MPP_EXIT_CLOAK_MASK);
|
|
pr_info("@MPP %s\n", __func__);
|
|
|
|
if (delayed_work_pending(&charger->set_mpp_cloak_work)) {
|
|
__pm_relax(charger->set_mpp_cloak_ws);
|
|
cancel_delayed_work(&charger->set_mpp_cloak_work);
|
|
}
|
|
__pm_stay_awake(charger->set_mpp_cloak_ws);
|
|
charger->mpp_cloak_status = false;
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->set_mpp_cloak_work, msecs_to_jiffies(2000));
|
|
}
|
|
|
|
static void mfc_mpp_enter_cloak(struct mfc_charger_data *charger, u8 cloak_reason)
|
|
{
|
|
charger->mpp_cloak = cloak_reason;
|
|
|
|
if (mfc_reg_write(charger->client, MFC_MPP_CLOAK_REASON_REG, cloak_reason) >= 0) {
|
|
mfc_set_cmd_h_reg(charger, MFC_CMD2_MPP_ENTER_CLOAK_MASK, MFC_CMD2_MPP_ENTER_CLOAK_MASK);
|
|
pr_info("@MPP %s: enter cloak and reason is %d\n", __func__, charger->mpp_cloak);
|
|
}
|
|
if (delayed_work_pending(&charger->set_mpp_cloak_work)) {
|
|
__pm_relax(charger->set_mpp_cloak_ws);
|
|
cancel_delayed_work(&charger->set_mpp_cloak_work);
|
|
}
|
|
__pm_stay_awake(charger->set_mpp_cloak_ws);
|
|
charger->mpp_cloak_status = false;
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->set_mpp_cloak_work, msecs_to_jiffies(2000));
|
|
}
|
|
|
|
#if 0
|
|
static void mfc_mpp_full_mode_enable(struct mfc_charger_data *charger)
|
|
{
|
|
mfc_set_cmd_h_reg(charger, MFC_CMD2_MPP_FULL_MODE_SHIFT, MFC_CMD2_MPP_FULL_MODE_SHIFT);
|
|
pr_info("%s: restrict mode to full mode\n", __func__);
|
|
}
|
|
|
|
//if trans_type = MFC_RX_MPP_FULL_MODE_TRAN_POWER_RESET, need disable restrict mode and enable full mode after wireless chip restart
|
|
static void mfc_mpp_full_mode_trans(struct mfc_charger_data *charger, u8 trans_type)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = mfc_reg_write(charger->client, MFC_MPP_FULL_MODE_TRANS_TYPE_REG, trans_type);
|
|
if (ret < 0)
|
|
return;
|
|
|
|
pr_info("%s: mpp full mode transfer %d\n", __func__, trans_type);
|
|
}
|
|
#endif
|
|
|
|
//set negotiate power with Tx, set before negotiation phase.
|
|
static void mfc_mpp_epp_nego_power_set(struct mfc_charger_data *charger, u8 nego_load_power)
|
|
{
|
|
int ret = 0;
|
|
u8 reg_data = 0;
|
|
|
|
ret = mfc_reg_read(charger->client, MFC_INT_A_ENABLE_2_REG, ®_data);
|
|
if ((ret >= 0) && !(reg_data & MFC_INTB_L_INCREASE_POWER_MASK)) {
|
|
pr_info("@MPP @EPP %s: Skip set negotiate power by high temp\n", __func__);
|
|
return;
|
|
}
|
|
|
|
ret = mfc_reg_write(charger->client, MFC_MPP_POWER_LEVEL_SETTING_REG, (nego_load_power << 1));
|
|
if (ret < 0)
|
|
return;
|
|
|
|
pr_info("@MPP @EPP %s: MPP or EPP set negotiate power %dW\n", __func__, nego_load_power);
|
|
}
|
|
|
|
static void mfc_mpp_inc_int_ctrl(struct mfc_charger_data *charger, u32 enable)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = mfc_reg_update(charger->client, MFC_INT_A_ENABLE_2_REG,
|
|
(enable << MFC_INTB_L_INCREASE_POWER_SHIFT), MFC_INTB_L_INCREASE_POWER_MASK);
|
|
if (ret < 0)
|
|
pr_info("@MPP %s: MFC_INT_A_ENABLE_2_REG write error(%d)\n",
|
|
__func__, ret);
|
|
|
|
pr_info("@MPP %s: enable(%d)\n", __func__, enable);
|
|
}
|
|
|
|
static void mfc_mpp_thermal_ctrl(struct mfc_charger_data *charger, u32 enable)
|
|
{
|
|
int ret = 0;
|
|
u8 reg_data = 0;
|
|
|
|
if (charger->sleep_mode && !enable) {
|
|
pr_info("@MPP %s: do not ctrl because sleep_mode(%d)\n",
|
|
__func__, charger->sleep_mode);
|
|
return;
|
|
}
|
|
|
|
if (enable)
|
|
reg_data = ((charger->target_current / 25) << 1 | enable);
|
|
|
|
ret = mfc_reg_write(charger->client, MFC_MPP_THERMAL_CTRL_REG, reg_data);
|
|
if (ret < 0)
|
|
pr_info("@MPP %s: MFC_MPP_THERMAL_CTRL_REG write error(%d)\n",
|
|
__func__, ret);
|
|
|
|
pr_info("@MPP %s: enable(%d) current(%d)\n", __func__, enable, charger->target_current);
|
|
}
|
|
|
|
static void mfc_epp_enable(struct mfc_charger_data *charger, u32 enable)
|
|
{
|
|
if (charger->high_swell && enable) {
|
|
pr_info("@MPP %s: do not enable - high_swell(%d)\n",
|
|
__func__, charger->high_swell);
|
|
enable = 0;
|
|
}
|
|
|
|
if (charger->pdata->mag_det >= 0)
|
|
gpio_direction_output(charger->pdata->mag_det, enable);
|
|
|
|
if (charger->pdata->wpc_mode >= 0)
|
|
gpio_direction_output(charger->pdata->wpc_mode, enable);
|
|
|
|
pr_info("@MPP %s: enable(%d), wpc_mode = %d\n",
|
|
__func__, enable,
|
|
(charger->pdata->wpc_mode >= 0 ? gpio_get_value(charger->pdata->wpc_mode) : -1));
|
|
}
|
|
|
|
#if 0
|
|
static u32 mfc_mpp_epp_get_nego_done_power(struct mfc_charger_data *charger)
|
|
{
|
|
u32 data = 0;
|
|
u8 temp = 0;
|
|
|
|
mfc_reg_read(charger->client, MFC_MPP_EPP_NEGO_DONE_POWER_L_REG, &temp);
|
|
data = temp;
|
|
mfc_reg_read(charger->client, MFC_MPP_EPP_NEGO_DONE_POWER_H_REG, &temp);
|
|
data |= (temp << 8);
|
|
data = data * 100;
|
|
pr_info("@MPP @EPP %s: mpp or epp negotiated power %dmW\n", __func__, data);
|
|
return data;
|
|
}
|
|
|
|
static u8 mfc_mpp_get_gp_state(struct mfc_charger_data *charger)
|
|
{
|
|
u8 data = 0;
|
|
|
|
mfc_reg_read(charger->client, MFC_MPP_GP_STATE_REG, &data);
|
|
pr_info("%s: mpp gp state %02X\n", __func__, data);
|
|
return data;
|
|
}
|
|
#endif
|
|
|
|
static void mfc_send_eop(struct mfc_charger_data *charger, int health_mode)
|
|
{
|
|
int i = 0;
|
|
int ret = 0;
|
|
|
|
pr_info("%s: health_mode(0x%x), cable_type(%d)\n",
|
|
__func__, health_mode, charger->pdata->cable_type);
|
|
|
|
switch (health_mode) {
|
|
case POWER_SUPPLY_HEALTH_OVERHEAT:
|
|
case POWER_SUPPLY_EXT_HEALTH_OVERHEATLIMIT:
|
|
case POWER_SUPPLY_HEALTH_COLD:
|
|
pr_info("%s: ept-ot\n", __func__);
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_PMA_WIRELESS) {
|
|
for (i = 0; i < CMD_CNT; i++) {
|
|
ret = mfc_reg_write(charger->client, MFC_EPT_REG, MFC_WPC_EPT_END_OF_CHG);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_SEND_EOP_MASK, MFC_CMD_SEND_EOP_MASK);
|
|
msleep(250);
|
|
}
|
|
} else {
|
|
for (i = 0; i < CMD_CNT; i++) {
|
|
ret = mfc_reg_write(charger->client, MFC_EPT_REG, MFC_WPC_EPT_OVER_TEMP);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_SEND_EOP_MASK, MFC_CMD_SEND_EOP_MASK);
|
|
msleep(250);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void mfc_packet_assist(struct mfc_charger_data *charger)
|
|
{
|
|
u8 dummy;
|
|
|
|
mfc_reg_read(charger->client, MFC_WPC_RX_DATA_COM_REG, &dummy);
|
|
mfc_reg_read(charger->client, MFC_WPC_RX_DATA_VALUE0_REG, &dummy);
|
|
mfc_reg_read(charger->client, MFC_AP2MFC_CMD_L_REG, &dummy);
|
|
}
|
|
|
|
static void mfc_wpc_check_fod_count(struct mfc_charger_data *charger, int fod_state)
|
|
{
|
|
if (charger->pdata->ping_nen < 0)
|
|
return;
|
|
|
|
pr_info("%s: state(%d) cnt(%d) cnt_thresh(%d) phm(%d)!\n",
|
|
__func__, fod_state, charger->fod_cnt, charger->pdata->fod_cnt_thresh, charger->rx_phm_state);
|
|
|
|
if (fod_state == FOD_CNT_CLEAR) {
|
|
if (charger->fod_cnt == 0)
|
|
return;
|
|
if (charger->fod_cnt > charger->pdata->fod_cnt_thresh && charger->rx_phm_state != ENTER_PHM) {
|
|
pr_info("%s: set wpc_mode high\n", __func__);
|
|
mfc_epp_enable(charger, 1);
|
|
mfc_soft_reset_ask_stop(charger);
|
|
}
|
|
charger->fod_cnt = 0;
|
|
return;
|
|
}
|
|
if (fod_state == FOD_CNT_ADD) {
|
|
if (++charger->fod_cnt > charger->pdata->fod_cnt_thresh) {
|
|
pr_info("%s: set wpc_mode low\n", __func__);
|
|
mfc_epp_enable(charger, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mfc_send_packet(struct mfc_charger_data *charger, u8 header, u8 rx_data_com, u8 *data_val, int data_size)
|
|
{
|
|
int i;
|
|
|
|
mfc_reg_write(charger->client, MFC_WPC_PCKT_HEADER_REG, header);
|
|
mfc_reg_write(charger->client, MFC_WPC_RX_DATA_COM_REG, rx_data_com);
|
|
|
|
for (i = 0; i < data_size; i++)
|
|
mfc_reg_write(charger->client, MFC_WPC_RX_DATA_VALUE0_REG + i, data_val[i]);
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_SEND_TRX_DATA_MASK, MFC_CMD_SEND_TRX_DATA_MASK);
|
|
}
|
|
|
|
static int mfc_send_cs100(struct mfc_charger_data *charger)
|
|
{
|
|
int i = 0;
|
|
int ret = 0;
|
|
|
|
for (i = 0; i < CMD_CNT; i++) {
|
|
ret = mfc_reg_write(charger->client, MFC_BATTERY_CHG_STATUS_REG, 100);
|
|
if (ret >= 0) {
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_SEND_CHG_STS_MASK, MFC_CMD_SEND_CHG_STS_MASK);
|
|
msleep(250);
|
|
ret = 1;
|
|
} else {
|
|
ret = -1;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void mfc_send_ept_rst(struct mfc_charger_data *charger)
|
|
{
|
|
u8 value = 0;
|
|
|
|
pr_info("%s: EPT-rst\n", __func__);
|
|
|
|
value = MFC_CMD_SEND_EOP_MASK;
|
|
mfc_reg_write(charger->client, MFC_EPT_REG, MFC_WPC_EPT_RESTART);
|
|
mfc_set_cmd_l_reg(charger, value, value);
|
|
}
|
|
|
|
static void mfc_send_ept_unknown(struct mfc_charger_data *charger)
|
|
{
|
|
pr_info("%s: EPT-Unknown\n", __func__);
|
|
mfc_reg_write(charger->client, MFC_EPT_REG, MFC_WPC_EPT_UNKNOWN);
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_SEND_EOP_MASK, MFC_CMD_SEND_EOP_MASK);
|
|
}
|
|
|
|
static void mfc_send_command(struct mfc_charger_data *charger, int cmd_mode)
|
|
{
|
|
u8 data_val[4];
|
|
u8 cmd = 0;
|
|
u8 i;
|
|
|
|
switch (cmd_mode) {
|
|
case MFC_AFC_CONF_5V:
|
|
pr_info("%s: WPC/PMA set 5V\n", __func__);
|
|
cmd = WPC_COM_AFC_SET;
|
|
data_val[0] = 0x05; /* Value for WPC AFC_SET 5V */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1); // 0x28 0x06 0x05
|
|
msleep(120);
|
|
|
|
charger->vout_mode = WIRELESS_VOUT_5V;
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
__pm_stay_awake(charger->wpc_vout_mode_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_vout_mode_work, 0);
|
|
|
|
pr_info("%s: vout read = %d\n", __func__, mfc_get_adc(charger, MFC_ADC_VOUT));
|
|
mfc_packet_assist(charger);
|
|
break;
|
|
case MFC_AFC_CONF_10V:
|
|
pr_info("%s: WPC set 10V\n", __func__);
|
|
/* trigger 10V vout work after 8sec */
|
|
__pm_stay_awake(charger->wpc_afc_vout_ws);
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_afc_vout_work, msecs_to_jiffies(3000));
|
|
#else
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_afc_vout_work, msecs_to_jiffies(8000));
|
|
#endif
|
|
break;
|
|
case MFC_AFC_CONF_5V_TX:
|
|
for (i = 0; i < CMD_CNT; i++) {
|
|
cmd = WPC_COM_AFC_SET;
|
|
data_val[0] = RX_DATA_VAL2_5V; /* Value for WPC AFC_SET 5V */
|
|
pr_info("%s: set 5V to TX, cnt = %d\n", __func__, i);
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1); // 28 06 05
|
|
mfc_packet_assist(charger);
|
|
msleep(100);
|
|
}
|
|
break;
|
|
case MFC_AFC_CONF_10V_TX:
|
|
for (i = 0; i < CMD_CNT; i++) {
|
|
cmd = WPC_COM_AFC_SET;
|
|
data_val[0] = RX_DATA_VAL2_10V; /* Value for WPC AFC_SET 10V */
|
|
pr_info("%s: set 10V to TX, cnt = %d\n", __func__, i);
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1); // 28 06 2c
|
|
mfc_packet_assist(charger);
|
|
msleep(100);
|
|
}
|
|
break;
|
|
case MFC_AFC_CONF_12V_TX:
|
|
for (i = 0; i < CMD_CNT; i++) {
|
|
cmd = WPC_COM_AFC_SET;
|
|
data_val[0] = RX_DATA_VAL2_12V; /* Value for WPC AFC_SET 12V */
|
|
pr_info("%s: set 12V to TX, cnt = %d\n", __func__, i);
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
mfc_packet_assist(charger);
|
|
msleep(100);
|
|
}
|
|
break;
|
|
case MFC_AFC_CONF_12_5V_TX:
|
|
disable_irq(charger->pdata->irq_wpc_int); /* disable int in order not to have rx power int before this work done */
|
|
for (i = 0; i < CMD_CNT; i++) {
|
|
cmd = WPC_COM_AFC_SET;
|
|
data_val[0] = RX_DATA_VAL2_12_5V; /* Value for WPC AFC_SET 12V */
|
|
pr_info("%s: set 12.5V to TX, cnt = %d\n", __func__, i);
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
mfc_packet_assist(charger);
|
|
msleep(100);
|
|
}
|
|
enable_irq(charger->pdata->irq_wpc_int);
|
|
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20) {
|
|
if (delayed_work_pending(&charger->wpc_rx_power_trans_fail_work)) {
|
|
__pm_relax(charger->wpc_rx_power_trans_fail_ws);
|
|
cancel_delayed_work(&charger->wpc_rx_power_trans_fail_work);
|
|
}
|
|
charger->check_rx_power = true;
|
|
__pm_stay_awake(charger->wpc_rx_power_trans_fail_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_rx_power_trans_fail_work, msecs_to_jiffies(3000)); /* this work is for in case of missing rx power intterupt */
|
|
}
|
|
break;
|
|
case MFC_AFC_CONF_20V_TX:
|
|
for (i = 0; i < CMD_CNT; i++) {
|
|
cmd = WPC_COM_AFC_SET;
|
|
data_val[0] = RX_DATA_VAL2_20V; /* Value for WPC AFC_SET 20V */
|
|
pr_info("%s: set 20V to TX, cnt = %d\n", __func__, i);
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
mfc_packet_assist(charger);
|
|
msleep(100);
|
|
}
|
|
break;
|
|
case MFC_LED_CONTROL_ON:
|
|
pr_info("%s: led on\n", __func__);
|
|
cmd = WPC_COM_LED_CONTROL;
|
|
data_val[0] = 0x00; /* Value for WPC LED ON */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
msleep(100);
|
|
break;
|
|
case MFC_LED_CONTROL_OFF:
|
|
pr_info("%s: led off\n", __func__);
|
|
cmd = WPC_COM_LED_CONTROL;
|
|
data_val[0] = 0xff; /* Value for WPC LED OFF */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
msleep(100);
|
|
break;
|
|
case MFC_LED_CONTROL_DIMMING:
|
|
pr_info("%s: led dimming\n", __func__);
|
|
cmd = WPC_COM_LED_CONTROL;
|
|
data_val[0] = 0x55; /* Value for WPC LED DIMMING */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
msleep(100);
|
|
break;
|
|
case MFC_FAN_CONTROL_ON:
|
|
pr_info("%s: fan on\n", __func__);
|
|
cmd = WPC_COM_COOLING_CTRL;
|
|
data_val[0] = 0x00; /* Value for WPC FAN ON */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
msleep(100);
|
|
break;
|
|
case MFC_FAN_CONTROL_OFF:
|
|
pr_info("%s: fan off\n", __func__);
|
|
cmd = WPC_COM_COOLING_CTRL;
|
|
data_val[0] = 0xff; /* Value for WPC FAN OFF */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
msleep(100);
|
|
break;
|
|
case MFC_REQUEST_AFC_TX:
|
|
pr_info("%s: request afc tx, cable(0x%x)\n", __func__, charger->pdata->cable_type);
|
|
cmd = WPC_COM_REQ_AFC_TX;
|
|
data_val[0] = 0x00; /* Value for WPC Request AFC_TX */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
break;
|
|
case MFC_REQUEST_TX_ID:
|
|
pr_info("%s: request TX ID\n", __func__);
|
|
cmd = WPC_COM_TX_ID;
|
|
data_val[0] = 0x00; /* Value for WPC TX ID */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
break;
|
|
case MFC_DISABLE_TX:
|
|
pr_info("%s: Disable TX\n", __func__);
|
|
cmd = WPC_COM_DISABLE_TX;
|
|
data_val[0] = 0x55; /* Value for Disable TX */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
break;
|
|
case MFC_PHM_ON:
|
|
pr_info("%s: Enter PHM\n", __func__);
|
|
cmd = WPC_COM_ENTER_PHM;
|
|
data_val[0] = 0x01; /* Enter PHM */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
break;
|
|
case MFC_SET_OP_FREQ:
|
|
pr_info("%s: set tx op freq\n", __func__);
|
|
cmd = WPC_COM_OP_FREQ_SET;
|
|
data_val[0] = 0x69; /* Value for OP FREQ 120.5kHz */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
break;
|
|
case MFC_TX_UNO_OFF:
|
|
pr_info("%s: UNO off TX\n", __func__);
|
|
cmd = WPC_COM_DISABLE_TX;
|
|
data_val[0] = 0xFF; /* Value for Uno off */
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
break;
|
|
case MFC_REQ_TX_PWR_BUDG:
|
|
pr_info("%s: request PWR BUDG\n", __func__);
|
|
cmd = WPC_COM_REQ_PWR_BUDG;
|
|
data_val[0] = 0x00;
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void mfc_send_fsk(struct mfc_charger_data *charger, u8 tx_data_com, u8 data_val)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
mfc_reg_write(charger->client, MFC_WPC_TX_DATA_COM_REG, tx_data_com);
|
|
mfc_reg_write(charger->client, MFC_WPC_TX_DATA_VALUE0_REG, data_val);
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_SEND_TRX_DATA_MASK, MFC_CMD_SEND_TRX_DATA_MASK);
|
|
msleep(250);
|
|
}
|
|
}
|
|
|
|
static void mfc_led_control(struct mfc_charger_data *charger, int state)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < CMD_CNT; i++)
|
|
mfc_send_command(charger, state);
|
|
}
|
|
|
|
static void mfc_fan_control(struct mfc_charger_data *charger, bool on)
|
|
{
|
|
int i = 0;
|
|
|
|
if (on) {
|
|
for (i = 0; i < CMD_CNT - 1; i++)
|
|
mfc_send_command(charger, MFC_FAN_CONTROL_ON);
|
|
} else {
|
|
for (i = 0; i < CMD_CNT - 1; i++)
|
|
mfc_send_command(charger, MFC_FAN_CONTROL_OFF);
|
|
}
|
|
}
|
|
|
|
static void mfc_set_vrect_adjust(struct mfc_charger_data *charger, int set)
|
|
{
|
|
if (charger->vout_mode == charger->pdata->wpc_vout_ctrl_full)
|
|
return;
|
|
|
|
pr_info("%s no headroom mode\n", __func__);
|
|
}
|
|
|
|
static void mfc_set_vout_ctrl_1st_full(struct mfc_charger_data *charger)
|
|
{
|
|
if (!volt_ctrl_pad(charger->tx_id) || (charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_MPP))
|
|
return;
|
|
|
|
charger->vout_mode = WIRELESS_VOUT_CC_CV_VOUT;
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
__pm_stay_awake(charger->wpc_vout_mode_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_vout_mode_work, msecs_to_jiffies(250));
|
|
}
|
|
|
|
static void mfc_set_vout_ctrl_2nd_full(struct mfc_charger_data *charger)
|
|
{
|
|
if (!charger->pdata->wpc_vout_ctrl_full
|
|
|| !volt_ctrl_pad(charger->tx_id)
|
|
|| (charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_MPP))
|
|
return;
|
|
|
|
if (charger->pdata->wpc_headroom_ctrl_full)
|
|
mfc_set_vrect_adjust(charger, MFC_HEADROOM_7);
|
|
charger->vout_mode = charger->pdata->wpc_vout_ctrl_full;
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
__pm_stay_awake(charger->wpc_vout_mode_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_vout_mode_work, msecs_to_jiffies(250));
|
|
pr_info("%s: 2nd wireless charging done! vout set %s & headroom offset %dmV!\n",
|
|
__func__, sb_vout_ctr_mode_str(charger->vout_mode),
|
|
charger->pdata->wpc_headroom_ctrl_full ? -600 : 0);
|
|
}
|
|
|
|
/* these pads can control fans for sleep/auto mode */
|
|
static bool is_sleep_mode_active(int pad_id)
|
|
{
|
|
/* standard fw, hv pad, no multiport pad, non hv pad and PG950 pad */
|
|
if (fan_ctrl_pad(pad_id)) {
|
|
pr_info("%s: this pad can control fan for auto mode\n", __func__);
|
|
return 1;
|
|
}
|
|
|
|
pr_info("%s: this pad cannot control fan\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static void mfc_mis_align(struct mfc_charger_data *charger)
|
|
{
|
|
if (charger->wc_checking_align || (charger->rx_op_mode == MFC_RX_MODE_WPC_MPP_CLOAK)) {
|
|
pr_info("%s: skip Reset M0\n", __func__);
|
|
return;
|
|
}
|
|
|
|
pr_info("%s: Reset M0\n", __func__);
|
|
/* reset MCU of MFC IC */
|
|
//mfc_set_cmd_l_reg(charger, MFC_CMD_MCU_RESET_MASK, MFC_CMD_MCU_RESET_MASK);
|
|
mfc_soft_reset_ask_stop(charger);
|
|
}
|
|
|
|
static bool mfc_tx_function_check(struct mfc_charger_data *charger)
|
|
{
|
|
u8 reg_f2, reg_f4;
|
|
|
|
mfc_reg_read(charger->client, MFC_TX_RXID1_READ_REG, ®_f2);
|
|
mfc_reg_read(charger->client, MFC_TX_RXID3_READ_REG, ®_f4);
|
|
|
|
pr_info("@Tx_Mode %s: 0x%x 0x%x\n", __func__, reg_f2, reg_f4);
|
|
|
|
if ((reg_f2 == SS_ID) && (reg_f4 == SS_CODE))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
static void mfc_set_tx_ioffset(struct mfc_charger_data *charger, int ioffset)
|
|
{
|
|
u8 ioffset_data_l = 0, ioffset_data_h = 0;
|
|
|
|
ioffset_data_l = 0xFF & ioffset;
|
|
ioffset_data_h = 0xFF & (ioffset >> 8);
|
|
|
|
mfc_reg_write(charger->client, MFC_TX_IUNO_OFFSET_L_REG, ioffset_data_l);
|
|
mfc_reg_write(charger->client, MFC_TX_IUNO_OFFSET_H_REG, ioffset_data_h);
|
|
|
|
pr_info("@Tx_Mode %s: Tx Iout set %d(0x%x 0x%x)\n", __func__, ioffset, ioffset_data_h, ioffset_data_l);
|
|
}
|
|
|
|
static void mfc_set_tx_iout(struct mfc_charger_data *charger, unsigned int iout)
|
|
{
|
|
u8 iout_data_l = 0, iout_data_h = 0;
|
|
|
|
iout_data_l = 0xFF & iout;
|
|
iout_data_h = 0xFF & (iout >> 8);
|
|
|
|
mfc_reg_write(charger->client, MFC_TX_IUNO_LIMIT_L_REG, iout_data_l);
|
|
mfc_reg_write(charger->client, MFC_TX_IUNO_LIMIT_H_REG, iout_data_h);
|
|
|
|
pr_info("@Tx_Mode %s: Tx Iout set %d(0x%x 0x%x)\n", __func__, iout, iout_data_h, iout_data_l);
|
|
|
|
if (iout == 1100)
|
|
mfc_set_tx_ioffset(charger, 100);
|
|
else
|
|
mfc_set_tx_ioffset(charger, 50);
|
|
}
|
|
|
|
static void mfc_test_read(struct mfc_charger_data *charger)
|
|
{
|
|
u8 reg_data;
|
|
|
|
if (!charger->wc_tx_enable)
|
|
return;
|
|
|
|
if (mfc_reg_read(charger->client, MFC_STARTUP_EPT_COUNTER, ®_data) <= 0)
|
|
return;
|
|
|
|
pr_info("%s: [AOV] ept-counter = %d\n", __func__, reg_data);
|
|
}
|
|
|
|
static void mfc_set_tx_vout(struct mfc_charger_data *charger, unsigned int vout)
|
|
{
|
|
unsigned int vout_val;
|
|
u8 vout_data_l = 0, vout_data_h = 0;
|
|
|
|
if (vout < WC_TX_VOUT_MIN) {
|
|
pr_err("%s: vout(%d) is lower than min\n", __func__, vout);
|
|
vout = WC_TX_VOUT_MIN;
|
|
} else if (vout > WC_TX_VOUT_MAX) {
|
|
pr_err("%s: vout(%d) is higher than max\n", __func__, vout);
|
|
vout = WC_TX_VOUT_MAX;
|
|
}
|
|
|
|
mfc_test_read(charger);
|
|
|
|
if (vout <= 2280)
|
|
vout_val = 0;
|
|
else
|
|
vout_val = (vout - 2280) / 20;
|
|
vout_data_l = 0xFF & vout_val;
|
|
vout_data_h = 0xFF & (vout_val >> 8);
|
|
|
|
mfc_reg_write(charger->client, MFC_VOUT_SET_L_REG, vout_data_l);
|
|
mfc_reg_write(charger->client, MFC_VOUT_SET_H_REG, vout_data_h);
|
|
|
|
pr_info("@Tx_Mode %s: Tx Vout set %d(0x%x 0x%x)\n", __func__, vout, vout_data_h, vout_data_l);
|
|
}
|
|
|
|
static void mfc_print_buffer(struct mfc_charger_data *charger, u8 *buffer, int size)
|
|
{
|
|
int start_idx = 0;
|
|
|
|
do {
|
|
char temp_buf[1024] = {0, };
|
|
int size_temp = 0, str_len = 1024;
|
|
int old_idx = start_idx;
|
|
|
|
size_temp = ((start_idx + 0x7F) > size) ? size : (start_idx + 0x7F);
|
|
for (; start_idx < size_temp; start_idx++) {
|
|
snprintf(temp_buf + strlen(temp_buf), str_len, "0x%02x ", buffer[start_idx]);
|
|
str_len = 1024 - strlen(temp_buf);
|
|
}
|
|
pr_info("%s: (%04d ~ %04d) %s\n", __func__, old_idx, start_idx - 1, temp_buf);
|
|
} while (start_idx < size);
|
|
}
|
|
|
|
static int mfc_set_psy_wrl(struct mfc_charger_data *charger, int prop, int data)
|
|
{
|
|
union power_supply_propval value = { data, };
|
|
|
|
return psy_do_property("wireless", set, prop, value);
|
|
}
|
|
|
|
static void mfc_auth_send_adt_status(struct mfc_charger_data *charger, u8 adt_status)
|
|
{
|
|
charger->adt_transfer_status = adt_status;
|
|
mfc_set_psy_wrl(charger, POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS, adt_status);
|
|
}
|
|
|
|
static void mfc_set_online(struct mfc_charger_data *charger, int type)
|
|
{
|
|
charger->pdata->cable_type = type;
|
|
mfc_set_psy_wrl(charger, POWER_SUPPLY_PROP_ONLINE, type);
|
|
}
|
|
|
|
static void mfc_set_tx_phm(struct mfc_charger_data *charger, bool enable)
|
|
{
|
|
charger->tx_device_phm = enable;
|
|
mfc_set_psy_wrl(charger, POWER_SUPPLY_EXT_PROP_GEAR_PHM_EVENT, enable);
|
|
}
|
|
|
|
static void mfc_set_rx_power(struct mfc_charger_data *charger, unsigned int rx_power)
|
|
{
|
|
if (rx_power > TX_RX_POWER_20W * 100000)
|
|
rx_power = TX_RX_POWER_12W * 100000;
|
|
|
|
mfc_set_psy_wrl(charger, POWER_SUPPLY_EXT_PROP_WIRELESS_RX_POWER, rx_power);
|
|
}
|
|
|
|
static void mfc_set_mpp_default_current(struct mfc_charger_data *charger, int input_current)
|
|
{
|
|
mfc_set_psy_wrl(charger, POWER_SUPPLY_EXT_PROP_WIRELESS_MPP_PWR, input_current);
|
|
}
|
|
|
|
static int mfc_auth_adt_read(struct mfc_charger_data *charger, u8 *readData)
|
|
{
|
|
int buffAddr = MFC_ADT_BUFFER_ADT_PARAM_REG;
|
|
int i = 0, size = 0;
|
|
int ret = 0;
|
|
u8 size_temp[2] = {0, 0};
|
|
|
|
ret = mfc_reg_multi_read(charger->client, MFC_ADT_BUFFER_ADT_TYPE_REG, size_temp, 2);
|
|
if (ret < 0) {
|
|
pr_err("%s: failed to read adt type (ret = %d)\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
adt_readSize = ((size_temp[0] & 0x07) << 8) | (size_temp[1]);
|
|
pr_info("%s %s: (size_temp = 0x%x, readSize = %d bytes)\n",
|
|
WC_AUTH_MSG, __func__, (size_temp[0] | size_temp[1]), adt_readSize);
|
|
if (adt_readSize <= 0) {
|
|
pr_err("%s %s: failed to read adt data size\n", WC_AUTH_MSG, __func__);
|
|
return -EINVAL;
|
|
}
|
|
size = adt_readSize;
|
|
if ((buffAddr + adt_readSize) - 1 > MFC_ADT_BUFFER_ADT_PARAM_MAX_REG) {
|
|
pr_err("%s %s: failed to read adt stream, too large data\n", WC_AUTH_MSG, __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (size <= 2) {
|
|
pr_err("%s %s: data from mcu has invalid size\n", WC_AUTH_MSG, __func__);
|
|
/* notify auth service to send TX PAD a request key */
|
|
mfc_set_psy_wrl(charger,
|
|
POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS, WIRELESS_AUTH_FAIL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
while (size > SENDSZ) {
|
|
ret = mfc_reg_multi_read(charger->client, buffAddr, readData + i, SENDSZ);
|
|
if (ret < 0) {
|
|
pr_err("%s %s: failed to read adt stream (%d, buff %d)\n",
|
|
WC_AUTH_MSG, __func__, ret, buffAddr);
|
|
return ret;
|
|
}
|
|
i += SENDSZ;
|
|
buffAddr += SENDSZ;
|
|
size = size - SENDSZ;
|
|
pr_info("%s %s: 0x%x %d %d\n", WC_AUTH_MSG, __func__, i, buffAddr, size);
|
|
}
|
|
|
|
if (size > 0) {
|
|
ret = mfc_reg_multi_read(charger->client, buffAddr, readData + i, size);
|
|
if (ret < 0) {
|
|
pr_err("%s %s: failed to read adt stream (%d, buff %d)\n",
|
|
WC_AUTH_MSG, __func__, ret, buffAddr);
|
|
return ret;
|
|
}
|
|
}
|
|
mfc_print_buffer(charger, readData, adt_readSize);
|
|
mfc_auth_send_adt_status(charger, WIRELESS_AUTH_RECEIVED);
|
|
|
|
pr_info("%s %s: succeeded to read ADT\n", WC_AUTH_MSG, __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mfc_auth_adt_write(struct mfc_charger_data *charger, u8 *srcData, int srcSize)
|
|
{
|
|
int buffAddr = MFC_ADT_BUFFER_ADT_PARAM_REG;
|
|
u8 wdata = 0;
|
|
u8 sBuf[144] = {0,};
|
|
int ret;
|
|
u8 i;
|
|
|
|
pr_info("%s %s: start to write ADT\n", WC_AUTH_MSG, __func__);
|
|
|
|
if ((buffAddr + srcSize) - 1 > MFC_ADT_BUFFER_ADT_PARAM_MAX_REG) {
|
|
pr_err("%s %s: failed to write adt stream, too large data.\n", WC_AUTH_MSG, __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (charger->rx_op_mode == MFC_RX_MODE_WPC_MPP_FULL) {
|
|
wdata = (MFC_ADT_MPP_OPEN << 3); /*MPP Authentication purpose */
|
|
} else if (charger->rx_op_mode == MFC_RX_MODE_WPC_EPP) {
|
|
if (is_samsung_pad((charger->mpp_epp_tx_id & 0xFF))) {
|
|
wdata = (MFC_ADT_FWC_EPP_GENERAL << 3);/*FWC Authentication purpose */
|
|
} else {
|
|
wdata = (MFC_ADT_FWC_EPP_AUTHENTICATION << 3); /* EPP Authentication purpose */
|
|
}
|
|
} else if (charger->rx_op_mode == MFC_RX_MODE_WPC_BPP) {
|
|
wdata = (MFC_ADT_FWC_EPP_GENERAL << 3);/*FWC Authentication purpose */
|
|
} else {
|
|
pr_info("%s %s: error Rx mode%d\n", WC_AUTH_MSG, __func__, charger->rx_op_mode);
|
|
return -EINVAL;
|
|
}
|
|
|
|
pr_info("%s %s: wdata(0x%x)\n", WC_AUTH_MSG, __func__, wdata);
|
|
wdata = wdata | ((srcSize >> 8) & 0x07);
|
|
mfc_reg_write(charger->client, MFC_ADT_BUFFER_ADT_TYPE_REG, wdata);
|
|
|
|
wdata = srcSize; /* need to check */
|
|
mfc_reg_write(charger->client, MFC_ADT_BUFFER_ADT_MSG_SIZE_REG, wdata);
|
|
|
|
buffAddr = MFC_ADT_BUFFER_ADT_PARAM_REG;
|
|
for (i = 0; i < srcSize; i += 128) { //write each 128byte
|
|
int restSize = srcSize - i;
|
|
|
|
if (restSize < 128) {
|
|
memcpy(sBuf, srcData + i, restSize);
|
|
ret = mfc_reg_multi_write_verify(charger->client, buffAddr, sBuf, restSize);
|
|
if (ret < 0) {
|
|
pr_err("%s %s: failed to write adt stream (%d, buff %d)\n",
|
|
WC_AUTH_MSG, __func__, ret, buffAddr);
|
|
return ret;
|
|
}
|
|
break;
|
|
}
|
|
|
|
memcpy(sBuf, srcData + i, 128);
|
|
ret = mfc_reg_multi_write_verify(charger->client, buffAddr, sBuf, 128);
|
|
if (ret < 0) {
|
|
pr_err("%s %s: failed to write adt stream (%d, buff %d)\n",
|
|
WC_AUTH_MSG, __func__, ret, buffAddr);
|
|
return ret;
|
|
}
|
|
buffAddr += 128;
|
|
|
|
if (buffAddr > MFC_ADT_BUFFER_ADT_PARAM_MAX_REG - 128)
|
|
break;
|
|
}
|
|
pr_info("%s %s: succeeded to write ADT\n", WC_AUTH_MSG, __func__);
|
|
return 0;
|
|
}
|
|
|
|
static void mfc_auth_adt_send(struct mfc_charger_data *charger, u8 *srcData, int srcSize)
|
|
{
|
|
u8 temp;
|
|
int ret = 0;
|
|
|
|
charger->adt_transfer_status = WIRELESS_AUTH_SENT;
|
|
ret = mfc_auth_adt_write(charger, srcData, srcSize); /* write buff fw datas to send fw datas to tx */
|
|
|
|
mfc_reg_read(charger->client, MFC_AP2MFC_CMD_H_REG, &temp); // check out set bit exists
|
|
pr_info("%s %s: MFC_CMD_H_REG(0x%x)\n", WC_AUTH_MSG, __func__, temp);
|
|
temp |= 0x02;
|
|
pr_info("%s %s: MFC_CMD_H_REG(0x%x)\n", WC_AUTH_MSG, __func__, temp);
|
|
mfc_reg_write(charger->client, MFC_AP2MFC_CMD_H_REG, temp);
|
|
|
|
mfc_reg_read(charger->client, MFC_AP2MFC_CMD_H_REG, &temp); // check out set bit exists
|
|
pr_info("%s %s: MFC_CMD_H_REG(0x%x)\n", WC_AUTH_MSG, __func__, temp);
|
|
|
|
mfc_reg_update(charger->client, MFC_INT_A_ENABLE_1_REG,
|
|
MFC_STAT_H_ADT_SENT_MASK, MFC_STAT_H_ADT_SENT_MASK);
|
|
}
|
|
|
|
#define AUTH_READY 0
|
|
#define AUTH_COMPLETE 1
|
|
#define AUTH_OPFREQ 140
|
|
static void mfc_auth_set_configs(struct mfc_charger_data *charger, int opt)
|
|
{
|
|
//u8 data = 0;
|
|
|
|
if (opt == AUTH_READY) {
|
|
int op_freq = mfc_get_adc(charger, MFC_ADC_OP_FRQ);
|
|
|
|
pr_info("%s: op_freq(%d)\n", __func__, op_freq);
|
|
stwlc89_mfc_cmfet_set_auth(charger->cmfet, true);
|
|
if ((charger->tx_id == WC_PAD_P5200) && (op_freq >= AUTH_OPFREQ)) {
|
|
//mfc_reg_write(charger->client, MFC_RECT_MODE_AP_CTRL, 0x01);
|
|
//mfc_reg_update(charger->client, MFC_RECTMODE_REG, 0x40, 0xC0);
|
|
//mfc_reg_read(charger->client, MFC_RECTMODE_REG, &data);
|
|
}
|
|
} else if (opt == AUTH_COMPLETE) {
|
|
if (charger->tx_id == WC_PAD_P5200) {
|
|
//mfc_reg_write(charger->client, MFC_RECT_MODE_AP_CTRL, 0x00);
|
|
//mfc_reg_read(charger->client, MFC_RECTMODE_REG, &data);
|
|
}
|
|
stwlc89_mfc_cmfet_set_auth(charger->cmfet, false);
|
|
} else {
|
|
pr_info("%s: undefined cmd(%d)\n", __func__, opt);
|
|
}
|
|
}
|
|
|
|
|
|
static void mfc_set_tx_cep_timeout(struct mfc_charger_data *charger, u32 tx_cep_timeout_cnt)
|
|
{
|
|
u8 data = 0;
|
|
int ret = 0;
|
|
|
|
pr_info("%s: set the ce timeout scale = %d ms\n", __func__, tx_cep_timeout_cnt);
|
|
|
|
//timeout unit : 100[ms]
|
|
data = tx_cep_timeout_cnt / 100;
|
|
ret = mfc_reg_write(charger->client, MFC_TX_CE_TIMEOUT_REG, data);
|
|
mfc_reg_read(charger->client, MFC_TX_CE_TIMEOUT_REG, &data);
|
|
|
|
pr_info("%s: get the ce timeout scale = %d ms, ret(%d)\n", __func__, data, ret);
|
|
}
|
|
|
|
/* uno on/off control function */
|
|
static void mfc_set_tx_power(struct mfc_charger_data *charger, bool on)
|
|
{
|
|
union power_supply_propval value = {0, };
|
|
|
|
charger->wc_tx_enable = on;
|
|
|
|
if (on) {
|
|
u32 tx_max_op_freq = charger->pdata->tx_max_op_freq;
|
|
u32 tx_min_op_freq = charger->pdata->tx_min_op_freq;
|
|
|
|
u32 temp_tx_cep_timeout = charger->pdata->tx_cep_timeout;
|
|
|
|
pr_info("@Tx_Mode %s: Turn On TX Power\n", __func__);
|
|
mfc_set_coil_sw_en(charger, 1);
|
|
mfc_uno_on(charger, true);
|
|
msleep(200);
|
|
|
|
charger->otp_firmware_ver = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE);
|
|
charger->wc_rx_fod = false;
|
|
if (delayed_work_pending(&charger->wpc_tx_duty_min_work)) {
|
|
__pm_relax(charger->wpc_tx_duty_min_ws);
|
|
cancel_delayed_work(&charger->wpc_tx_duty_min_work);
|
|
}
|
|
|
|
mfc_set_tx_cep_timeout(charger, temp_tx_cep_timeout);
|
|
|
|
/* Set TX OP Freq (MAX/MIN) */
|
|
if (tx_max_op_freq > 0)
|
|
mfc_set_tx_max_op_freq(charger, tx_max_op_freq);
|
|
if (tx_min_op_freq > 0)
|
|
mfc_set_tx_min_op_freq(charger, tx_min_op_freq);
|
|
} else {
|
|
pr_info("@Tx_Mode %s: Turn Off TX Power, and reset UNO config\n", __func__);
|
|
|
|
mfc_uno_on(charger, false);
|
|
mfc_set_coil_sw_en(charger, 0);
|
|
|
|
value.intval = WC_TX_VOUT_5000MV;
|
|
psy_do_property("otg", set,
|
|
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_VOUT, value);
|
|
|
|
charger->wc_rx_connected = false;
|
|
charger->wc_rx_type = NO_DEV;
|
|
charger->duty_min = MIN_DUTY_SETTING_20_DATA;
|
|
charger->tx_device_phm = 0;
|
|
|
|
alarm_cancel(&charger->phm_alarm);
|
|
if (delayed_work_pending(&charger->wpc_rx_type_det_work)) {
|
|
cancel_delayed_work(&charger->wpc_rx_type_det_work);
|
|
__pm_relax(charger->wpc_rx_det_ws);
|
|
}
|
|
if (delayed_work_pending(&charger->wpc_rx_connection_work))
|
|
cancel_delayed_work(&charger->wpc_rx_connection_work);
|
|
if (delayed_work_pending(&charger->wpc_tx_isr_work)) {
|
|
cancel_delayed_work(&charger->wpc_tx_isr_work);
|
|
__pm_relax(charger->wpc_tx_ws);
|
|
}
|
|
if (delayed_work_pending(&charger->wpc_tx_phm_work)) {
|
|
cancel_delayed_work(&charger->wpc_tx_phm_work);
|
|
__pm_relax(charger->wpc_tx_phm_ws);
|
|
}
|
|
if (delayed_work_pending(&charger->mode_change_work)) {
|
|
cancel_delayed_work(&charger->mode_change_work);
|
|
__pm_relax(charger->mode_change_ws);
|
|
}
|
|
|
|
charger->tx_status = SEC_TX_OFF;
|
|
}
|
|
}
|
|
|
|
/* determine rx connection status with tx sharing mode */
|
|
static void mfc_wpc_rx_connection_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_rx_connection_work.work);
|
|
|
|
if (!charger->wc_tx_enable) {
|
|
pr_info("@Tx_Mode %s : wc_tx_enable(%d)\n", __func__, charger->wc_tx_enable);
|
|
charger->wc_rx_connected = false;
|
|
return;
|
|
}
|
|
|
|
if (charger->wc_rx_connected) {
|
|
pr_info("@Tx_Mode %s: Rx(%s) connected\n",
|
|
__func__, mfc_tx_function_check(charger) ? "Samsung" : "Other");
|
|
|
|
cancel_delayed_work(&charger->wpc_rx_type_det_work);
|
|
__pm_stay_awake(charger->wpc_rx_det_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_rx_type_det_work, msecs_to_jiffies(1000));
|
|
|
|
__pm_stay_awake(charger->wpc_tx_duty_min_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_tx_duty_min_work, msecs_to_jiffies(5000));
|
|
|
|
pr_info("%s: tx op freq = %dKhz\n", __func__, mfc_get_adc(charger, MFC_ADC_TX_MAX_OP_FRQ));
|
|
charger->duty_min = MIN_DUTY_SETTING_30_DATA;
|
|
mfc_set_min_duty(charger, MIN_DUTY_SETTING_30_DATA);
|
|
} else {
|
|
pr_info("@Tx_Mode %s: Rx disconnected\n", __func__);
|
|
|
|
mfc_set_coil_sw_en(charger, 1);
|
|
charger->wc_rx_fod = false;
|
|
charger->wc_rx_type = NO_DEV;
|
|
charger->tx_device_phm = 0;
|
|
|
|
if (delayed_work_pending(&charger->wpc_rx_type_det_work)) {
|
|
__pm_relax(charger->wpc_rx_det_ws);
|
|
cancel_delayed_work(&charger->wpc_rx_type_det_work);
|
|
}
|
|
|
|
if (delayed_work_pending(&charger->wpc_tx_duty_min_work)) {
|
|
__pm_relax(charger->wpc_tx_duty_min_ws);
|
|
cancel_delayed_work(&charger->wpc_tx_duty_min_work);
|
|
}
|
|
}
|
|
|
|
/* set rx connection condition */
|
|
mfc_set_psy_wrl(charger, POWER_SUPPLY_EXT_PROP_WIRELESS_RX_CONNECTED, charger->wc_rx_connected);
|
|
}
|
|
|
|
/*
|
|
* determine rx connection status with tx sharing mode,
|
|
* this TX device has MFC_INTA_H_TRX_DATA_RECEIVED_MASK irq and RX device is connected HV wired cable
|
|
*/
|
|
static void mfc_tx_handle_rx_packet(struct mfc_charger_data *charger)
|
|
{
|
|
u8 cmd_data = 0, val_data = 0, val2_data = 0;
|
|
union power_supply_propval value = {0, };
|
|
|
|
mfc_reg_read(charger->client, MFC_WPC_TRX_DATA2_COM_REG, &cmd_data);
|
|
mfc_reg_read(charger->client, MFC_WPC_TRX_DATA2_VALUE0_REG, &val_data);
|
|
mfc_reg_read(charger->client, MFC_WPC_TRX_DATA2_VALUE1_REG, &val2_data);
|
|
charger->pdata->trx_data_cmd = cmd_data;
|
|
charger->pdata->trx_data_val = val_data;
|
|
|
|
pr_info("@Tx_Mode %s: CMD : 0x%x, DATA : 0x%x, DATA2 : 0x%x\n",
|
|
__func__, cmd_data, val_data, val2_data);
|
|
|
|
/* When RX device has got a AFC TA, this TX device should turn off TX power sharing(uno) */
|
|
if (cmd_data == MFC_HEADER_AFC_CONF) { /* 0x28 */
|
|
switch (val_data) {
|
|
case WPC_COM_DISABLE_TX: /* 0x19 Turn off UNO of TX */
|
|
if (val2_data == RX_DATA_VAL2_MISALIGN) {
|
|
psy_do_property("wireless", get, POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ERR, value);
|
|
if (value.intval & BATT_TX_EVENT_WIRELESS_TX_RETRY) {
|
|
pr_info("@Tx_Mode %s: ignore misalign packet during TX retry\n", __func__);
|
|
return;
|
|
}
|
|
pr_info("@Tx_Mode %s: RX send TX off packet(Misalign or darkzone)\n", __func__);
|
|
value.intval = BATT_TX_EVENT_WIRELESS_TX_MISALIGN;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ERR, value);
|
|
} else if (val2_data == RX_DATA_VAL2_TA_CONNECT_DURING_WC) {
|
|
pr_info("@Tx_Mode %s: TX dev should turn off TX(uno), RX dev has AFC TA\n", __func__);
|
|
value.intval = BATT_TX_EVENT_WIRELESS_RX_CHG_SWITCH;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ERR, value);
|
|
}
|
|
break;
|
|
case WPC_COM_ENTER_PHM: /* 0x18 Received PHM, GEAR sent PHM packet to TX */
|
|
if (val2_data == RX_DATA_VAL2_ENABLE)
|
|
pr_info("@Tx_Mode %s: Received PHM\n", __func__);
|
|
break;
|
|
case WPC_COM_RX_ID: /* 0x0E Received RX ID */
|
|
if (val2_data >= RX_DATA_VAL2_RXID_ACC_BUDS && val2_data <= RX_DATA_VAL2_RXID_ACC_BUDS_MAX) {
|
|
charger->wc_rx_type = SS_BUDS;
|
|
pr_info("@Tx_Mode %s: Buds connected\n", __func__);
|
|
|
|
value.intval = charger->wc_rx_type;
|
|
psy_do_property("wireless", set,
|
|
POWER_SUPPLY_EXT_PROP_WIRELESS_RX_TYPE, value);
|
|
}
|
|
break;
|
|
default:
|
|
pr_info("@Tx_Mode %s: unsupport : 0x%x", __func__, val_data);
|
|
break;
|
|
}
|
|
} else if (cmd_data == MFC_HEADER_END_CHARGE_STATUS) {
|
|
if (val_data == MFC_ECS_CS100) { /* CS 100 */
|
|
pr_info("@Tx_Mode %s: CS100 Received, TX power off\n", __func__);
|
|
value.intval = BATT_TX_EVENT_WIRELESS_RX_CS100;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ERR, value);
|
|
}
|
|
} else if (cmd_data == MFC_HEADER_END_POWER_TRANSFER) {
|
|
if (val_data == MFC_EPT_CODE_OVER_TEMPERATURE) {
|
|
pr_info("@Tx_Mode %s: EPT-OT Received, TX power off\n", __func__);
|
|
value.intval = BATT_TX_EVENT_WIRELESS_RX_UNSAFE_TEMP;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ERR, value);
|
|
} else if (val_data == MFC_EPT_CODE_BATTERY_FAILURE) {
|
|
if (charger->wc_rx_type != SS_GEAR) {
|
|
pr_info("@Tx_Mode %s: EPT06 Received, TX power off and retry\n", __func__);
|
|
value.intval = BATT_TX_EVENT_WIRELESS_TX_RETRY;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ERR, value);
|
|
}
|
|
}
|
|
} else if (cmd_data == MFC_HEADER_PROPRIETARY_1_BYTE) {
|
|
if (val_data == WPC_COM_WDT_ERR) {
|
|
pr_info("@Tx_Mode %s: WDT Error Received, TX power off\n", __func__);
|
|
value.intval = BATT_TX_EVENT_WIRELESS_TX_ETC;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ERR, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int datacmp(const char *cs, const char *ct, int count)
|
|
{
|
|
unsigned char c1, c2;
|
|
|
|
while (count) {
|
|
c1 = *cs++;
|
|
c2 = *ct++;
|
|
if (c1 != c2) {
|
|
pr_err("%s: cnt %d\n", __func__, count);
|
|
return c1 < c2 ? -1 : 1;
|
|
}
|
|
count--;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mfc_reg_multi_write_verify(struct i2c_client *client, u16 reg, const u8 *val, int size)
|
|
{
|
|
int ret = 0;
|
|
int cnt = 0;
|
|
int retry_cnt = 0;
|
|
unsigned char data[SENDSZ + 2];
|
|
u8 rdata[SENDSZ + 2];
|
|
struct mfc_charger_data *charger = i2c_get_clientdata(client);
|
|
|
|
if (charger->reg_access_lock) {
|
|
pr_err("%s: can not access to reg during fw update\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
while (size > SENDSZ) {
|
|
data[0] = (reg + cnt) >> 8;
|
|
data[1] = (reg + cnt) & 0xFF;
|
|
memcpy(data + 2, val + cnt, SENDSZ);
|
|
// dev_dbg(&client->dev, "%s: addr: 0x%x, cnt: 0x%x\n", __func__, reg + cnt, cnt);
|
|
ret = i2c_master_send(client, data, SENDSZ + 2);
|
|
if (ret < SENDSZ + 2) {
|
|
pr_err("%s: i2c write error, reg: 0x%x\n", __func__, reg);
|
|
return ret < 0 ? ret : -EIO;
|
|
}
|
|
if (mfc_reg_multi_read(client, reg + cnt, rdata, SENDSZ) < 0) {
|
|
pr_err("%s: read failed(%d)\n", __func__, reg + cnt);
|
|
return -1;
|
|
}
|
|
if (datacmp(val + cnt, rdata, SENDSZ)) {
|
|
pr_err("%s: data is not matched. retry(%d)\n", __func__, retry_cnt);
|
|
retry_cnt++;
|
|
if (retry_cnt > 4) {
|
|
pr_err("%s: data is not matched. write failed\n", __func__);
|
|
retry_cnt = 0;
|
|
return -1;
|
|
}
|
|
continue;
|
|
}
|
|
// pr_debug("%s: data is matched!\n", __func__);
|
|
cnt += SENDSZ;
|
|
size -= SENDSZ;
|
|
retry_cnt = 0;
|
|
}
|
|
while (size > 0) {
|
|
data[0] = (reg + cnt) >> 8;
|
|
data[1] = (reg + cnt) & 0xFF;
|
|
memcpy(data + 2, val + cnt, size);
|
|
// dev_dbg(&client->dev, "%s: addr: 0x%x, cnt: 0x%x, size: 0x%x\n", __func__, reg + cnt, cnt, size);
|
|
ret = i2c_master_send(client, data, size + 2);
|
|
if (ret < size + 2) {
|
|
pr_err("%s: i2c write error, reg: 0x%x\n", __func__, reg);
|
|
return ret < 0 ? ret : -EIO;
|
|
}
|
|
if (mfc_reg_multi_read(client, reg + cnt, rdata, size) < 0) {
|
|
pr_err("%s: read failed(%d)\n", __func__, reg + cnt);
|
|
return -1;
|
|
}
|
|
if (datacmp(val + cnt, rdata, size)) {
|
|
pr_err("%s: data is not matched. retry(%d)\n", __func__, retry_cnt);
|
|
retry_cnt++;
|
|
if (retry_cnt > 4) {
|
|
pr_err("%s: data is not matched. write failed\n", __func__);
|
|
retry_cnt = 0;
|
|
return -1;
|
|
}
|
|
continue;
|
|
}
|
|
size = 0;
|
|
pr_err("%s: data is matched!\n", __func__);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void mfc_reset_rx_power(struct mfc_charger_data *charger, u8 rx_power)
|
|
{
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
if (delayed_work_pending(&charger->wpc_rx_power_work))
|
|
cancel_delayed_work(&charger->wpc_rx_power_work);
|
|
#endif
|
|
|
|
if (charger->adt_transfer_status == WIRELESS_AUTH_PASS)
|
|
mfc_set_rx_power(charger, rx_power * 100000);
|
|
else
|
|
pr_info("%s %s: undefine rx power scenario, It is auth failed case how dose it get rx power?\n",
|
|
WC_AUTH_MSG, __func__);
|
|
}
|
|
|
|
static void mfc_wpc_rx_power_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_rx_power_work.work);
|
|
|
|
pr_info("%s: rx power = %d, This W/A is only for Factory\n", __func__, charger->max_power_by_txid);
|
|
mfc_set_rx_power(charger, charger->max_power_by_txid);
|
|
}
|
|
|
|
static void mfc_set_pad_hv(struct mfc_charger_data *charger, bool set_hv)
|
|
{
|
|
if (!is_hv_wireless_type(charger->pdata->cable_type) ||
|
|
(charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_MPP) ||
|
|
(charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_EPP))
|
|
return;
|
|
|
|
if (set_hv && charger->is_full_status)
|
|
return;
|
|
|
|
if (set_hv) {
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20)
|
|
mfc_send_command(charger, charger->vrect_by_txid);
|
|
else
|
|
mfc_send_command(charger, MFC_AFC_CONF_10V_TX);
|
|
|
|
} else {
|
|
mfc_send_command(charger, MFC_AFC_CONF_5V_TX);
|
|
}
|
|
charger->is_afc_tx = set_hv;
|
|
}
|
|
|
|
static void mfc_recover_vout_by_pad(struct mfc_charger_data *charger)
|
|
{
|
|
if (!is_hv_wireless_type(charger->pdata->cable_type) ||
|
|
(charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_MPP))
|
|
return;
|
|
|
|
if (charger->is_full_status)
|
|
return;
|
|
|
|
if (charger->vout_mode != WIRELESS_VOUT_5V &&
|
|
charger->vout_mode != WIRELESS_VOUT_5V_STEP &&
|
|
charger->vout_mode != WIRELESS_VOUT_5_5V_STEP &&
|
|
charger->vout_mode != WIRELESS_VOUT_OTG) {
|
|
// need to fix here
|
|
if ((charger->pdata->cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20) ||
|
|
(charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_EPP))
|
|
charger->vout_mode = charger->vout_by_txid;
|
|
else
|
|
charger->vout_mode = WIRELESS_VOUT_10V;
|
|
|
|
if (!charger->is_otg_on) {
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
__pm_stay_awake(charger->wpc_vout_mode_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_vout_mode_work, 0);
|
|
|
|
if ((charger->pdata->cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20) &&
|
|
(charger->tx_id == WC_PAD_FG)) {
|
|
pr_info("%s: set power = %d\n", __func__, charger->current_rx_power);
|
|
mfc_reset_rx_power(charger, charger->current_rx_power);
|
|
}
|
|
}
|
|
} else if (charger->sleep_mode && (charger->mpp_epp_nego_done_power >= TX_RX_POWER_8W)) {
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
__pm_stay_awake(charger->wpc_vout_mode_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_vout_mode_work, 0);
|
|
}
|
|
}
|
|
|
|
static void mfc_wpc_afc_vout_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_afc_vout_work.work);
|
|
|
|
pr_info("%s: start, current cable(%d)\n", __func__, charger->pdata->cable_type);
|
|
|
|
/* change cable type */
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV)
|
|
charger->pdata->cable_type = SEC_BATTERY_CABLE_HV_WIRELESS;
|
|
|
|
mfc_set_online(charger, charger->pdata->cable_type);
|
|
|
|
pr_info("%s: check OTG(%d), full(%d) tx_id(0x%x)\n",
|
|
__func__, charger->is_otg_on, charger->is_full_status, charger->tx_id);
|
|
|
|
if (charger->is_full_status && volt_ctrl_pad(charger->tx_id)) {
|
|
pr_info("%s: skip voltgate set to pad, full status with PG950 pad\n", __func__);
|
|
goto skip_set_afc_vout;
|
|
}
|
|
// need to fix here to get vout setting
|
|
mfc_set_pad_hv(charger, true);
|
|
mfc_recover_vout_by_pad(charger);
|
|
pr_info("%s: is_afc_tx = %d vout read = %d\n", __func__,
|
|
charger->is_afc_tx, mfc_get_adc(charger, MFC_ADC_VOUT));
|
|
skip_set_afc_vout:
|
|
__pm_relax(charger->wpc_afc_vout_ws);
|
|
}
|
|
|
|
static void mfc_wpc_fw_update_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_fw_update_work.work);
|
|
union power_supply_propval value = {0, };
|
|
int ret = 0;
|
|
bool is_changed = false;
|
|
const char *fw_path = charger->fw_path;
|
|
size_t fw_size;
|
|
const struct firmware *firm_data_bin;
|
|
unsigned int bin_fw_ver;
|
|
u8 pgmCnt = 0;
|
|
bool repairEn = false;
|
|
|
|
pr_info("%s: firmware update mode is = %d\n", __func__, charger->fw_cmd);
|
|
|
|
if (gpio_get_value(charger->pdata->wpc_en)) {
|
|
pr_info("%s: wpc_en disabled\n", __func__);
|
|
goto end_of_fw_work;
|
|
}
|
|
mfc_set_wpc_en(charger, WPC_EN_FW, true); /* keep the wpc_en low during fw update */
|
|
|
|
switch (charger->fw_cmd) {
|
|
case SEC_WIRELESS_FW_UPDATE_SPU_MODE:
|
|
case SEC_WIRELESS_FW_UPDATE_SPU_VERIFY_MODE:
|
|
case SEC_WIRELESS_FW_UPDATE_SDCARD_MODE:
|
|
case SEC_WIRELESS_FW_UPDATE_AUTO_MODE:
|
|
case SEC_WIRELESS_FW_UPDATE_BUILTIN_MODE:
|
|
mfc_uno_on(charger, true);
|
|
msleep(200);
|
|
if (mfc_get_chip_id(charger) < 0) {
|
|
pr_info("%s: current IC's chip_id is not matching to driver's chip_id(0x%x)\n",
|
|
__func__, MFC_CHIP_STM);
|
|
break;
|
|
}
|
|
|
|
bin_fw_ver = mfc_get_bin_fw_version(charger);
|
|
if (charger->fw_cmd == SEC_WIRELESS_FW_UPDATE_AUTO_MODE) {
|
|
charger->otp_firmware_ver = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE);
|
|
pr_info("%s: current version (0x%04X) ------ BIN_VERSION (0x%04X)\n",
|
|
__func__, charger->otp_firmware_ver, bin_fw_ver);
|
|
#if defined(CONFIG_WIRELESS_IC_PARAM)
|
|
if (charger->wireless_fw_mode_param == SEC_WIRELESS_FW_UPDATE_SPU_MODE &&
|
|
charger->otp_firmware_ver > bin_fw_ver) {
|
|
pr_info("%s: current version (0x%04X) is higher than BIN_VERSION by SPU(0x%04X)\n",
|
|
__func__, charger->otp_firmware_ver, bin_fw_ver);
|
|
break;
|
|
}
|
|
#endif
|
|
if (charger->otp_firmware_ver == bin_fw_ver) {
|
|
pr_info("%s: current version (0x%04X) is same to BIN_VERSION (0x%04X)\n",
|
|
__func__, charger->otp_firmware_ver, bin_fw_ver);
|
|
break;
|
|
}
|
|
}
|
|
charger->pdata->otp_firmware_result = MFC_FWUP_ERR_RUNNING;
|
|
is_changed = true;
|
|
|
|
dev_err(&charger->client->dev, "%s, request_firmware\n", __func__);
|
|
|
|
if (charger->fw_cmd == SEC_WIRELESS_FW_UPDATE_SPU_MODE ||
|
|
charger->fw_cmd == SEC_WIRELESS_FW_UPDATE_SPU_VERIFY_MODE)
|
|
fw_path = MFC_FW_SPU_BIN_PATH;
|
|
else if (charger->fw_cmd == SEC_WIRELESS_FW_UPDATE_SDCARD_MODE)
|
|
fw_path = MFC_FW_SDCARD_BIN_PATH;
|
|
|
|
ret = request_firmware(&firm_data_bin, fw_path, &charger->client->dev);
|
|
if (ret < 0) {
|
|
dev_err(&charger->client->dev, "%s: failed to request firmware %s (%d)\n",
|
|
__func__, fw_path, ret);
|
|
charger->pdata->otp_firmware_result = MFC_FWUP_ERR_REQUEST_FW_BIN;
|
|
goto fw_err;
|
|
}
|
|
fw_size = firm_data_bin->size;
|
|
|
|
#if IS_ENABLED(CONFIG_SPU_VERIFY)
|
|
if (charger->fw_cmd == SEC_WIRELESS_FW_UPDATE_SPU_MODE ||
|
|
charger->fw_cmd == SEC_WIRELESS_FW_UPDATE_SPU_VERIFY_MODE) {
|
|
if (spu_firmware_signature_verify("MFC", firm_data_bin->data, fw_size) ==
|
|
((int)fw_size - SPU_METADATA_SIZE(MFC))) {
|
|
pr_err("%s: spu_firmware_signature_verify success\n", __func__);
|
|
(int)fw_size -= SPU_METADATA_SIZE(MFC);
|
|
if (charger->fw_cmd == SEC_WIRELESS_FW_UPDATE_SPU_VERIFY_MODE) {
|
|
charger->pdata->otp_firmware_result = MFC_FW_RESULT_PASS;
|
|
goto fw_err;
|
|
}
|
|
} else {
|
|
pr_err("%s: spu_firmware_signature_verify failed\n", __func__);
|
|
goto fw_err;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
disable_irq(charger->pdata->irq_wpc_int);
|
|
disable_irq(charger->pdata->irq_wpc_det);
|
|
if (charger->pdata->irq_wpc_pdrc)
|
|
disable_irq(charger->pdata->irq_wpc_pdrc);
|
|
if (charger->pdata->irq_wpc_pdet_b)
|
|
disable_irq(charger->pdata->irq_wpc_pdet_b);
|
|
__pm_stay_awake(charger->wpc_update_ws);
|
|
pr_info("%s data size = %ld\n", __func__, (long)fw_size);
|
|
|
|
do {
|
|
if (repairEn == true) {
|
|
if (charger->pdata->wpc_en >= 0)
|
|
gpio_direction_output(charger->pdata->wpc_en, 1);
|
|
mfc_uno_on(charger, false);
|
|
msleep(1000);
|
|
mfc_uno_on(charger, true);
|
|
if (charger->pdata->wpc_en >= 0)
|
|
gpio_direction_output(charger->pdata->wpc_en, 0);
|
|
msleep(300);
|
|
}
|
|
ret = PgmOTPwRAM_STM(charger, 0, firm_data_bin->data, 0, (int)fw_size);
|
|
if (ret != MFC_FWUP_ERR_SUCCEEDED)
|
|
repairEn = true;
|
|
else
|
|
repairEn = false;
|
|
|
|
pgmCnt++;
|
|
|
|
pr_info("%s %s: repairEn(%d), pgmCnt(%d), ret(%d)\n",
|
|
MFC_FW_MSG, __func__, repairEn, pgmCnt, ret);
|
|
} while ((ret != MFC_FWUP_ERR_SUCCEEDED) && (pgmCnt < MAX_MTP_PGM_CNT));
|
|
|
|
release_firmware(firm_data_bin);
|
|
|
|
charger->otp_firmware_ver = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE);
|
|
charger->pdata->wc_ic_rev = mfc_get_ic_revision(charger, MFC_IC_REVISION);
|
|
|
|
enable_irq(charger->pdata->irq_wpc_int);
|
|
enable_irq(charger->pdata->irq_wpc_det);
|
|
if (charger->pdata->irq_wpc_pdrc)
|
|
enable_irq(charger->pdata->irq_wpc_pdrc);
|
|
if (charger->pdata->irq_wpc_pdet_b)
|
|
enable_irq(charger->pdata->irq_wpc_pdet_b);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
msleep(200);
|
|
mfc_uno_on(charger, false);
|
|
pr_info("%s---------------------------------------------------------------\n", __func__);
|
|
|
|
if (is_changed) {
|
|
if (ret == MFC_FWUP_ERR_SUCCEEDED) {
|
|
charger->pdata->otp_firmware_result = MFC_FW_RESULT_PASS;
|
|
#if defined(CONFIG_WIRELESS_IC_PARAM)
|
|
charger->wireless_fw_mode_param = charger->fw_cmd & 0xF;
|
|
pr_info("%s: succeed. fw_mode(0x%01X)\n",
|
|
__func__, charger->wireless_fw_mode_param);
|
|
charger->wireless_param_info &= 0xFFFFFF0F;
|
|
charger->wireless_param_info |= (charger->wireless_fw_mode_param & 0xF) << 4;
|
|
pr_info("%s: wireless_param_info (0x%08X)\n", __func__, charger->wireless_param_info);
|
|
#endif
|
|
} else {
|
|
charger->pdata->otp_firmware_result = ret;
|
|
}
|
|
}
|
|
value.intval = false;
|
|
psy_do_property("battery", set, POWER_SUPPLY_EXT_PROP_MFC_FW_UPDATE, value);
|
|
|
|
mfc_set_wpc_en(charger, WPC_EN_FW, false);
|
|
__pm_relax(charger->wpc_update_ws);
|
|
return;
|
|
fw_err:
|
|
mfc_uno_on(charger, false);
|
|
mfc_set_wpc_en(charger, WPC_EN_FW, false);
|
|
end_of_fw_work:
|
|
value.intval = false;
|
|
psy_do_property("battery", set, POWER_SUPPLY_EXT_PROP_MFC_FW_UPDATE, value);
|
|
__pm_relax(charger->wpc_update_ws);
|
|
}
|
|
|
|
static void mfc_set_tx_data(struct mfc_charger_data *charger, int idx)
|
|
{
|
|
if (idx < 0)
|
|
idx = 0;
|
|
else if (idx >= charger->pdata->len_wc20_list)
|
|
idx = charger->pdata->len_wc20_list - 1;
|
|
|
|
charger->vout_by_txid = charger->pdata->wireless20_vout_list[idx];
|
|
charger->vrect_by_txid = charger->pdata->wireless20_vrect_list[idx];
|
|
charger->max_power_by_txid = charger->pdata->wireless20_max_power_list[idx];
|
|
}
|
|
|
|
static int mfc_set_sgf_data(struct mfc_charger_data *charger, sgf_data *pdata)
|
|
{
|
|
pr_info("%s: size = %d, type = %d\n", __func__, pdata->size, pdata->type);
|
|
|
|
switch (pdata->type) {
|
|
case WPC_TX_COM_RX_POWER:
|
|
{
|
|
union power_supply_propval value = {0, };
|
|
int tx_power = *(int *)pdata->data;
|
|
|
|
switch (tx_power) {
|
|
case TX_RX_POWER_7_5W:
|
|
pr_info("%s : TX Power is 7.5W\n", __func__);
|
|
charger->current_rx_power = TX_RX_POWER_7_5W;
|
|
mfc_set_tx_data(charger, 0);
|
|
break;
|
|
case TX_RX_POWER_12W:
|
|
pr_info("%s : TX Power is 12W\n", __func__);
|
|
charger->current_rx_power = TX_RX_POWER_12W;
|
|
mfc_set_tx_data(charger, 1);
|
|
break;
|
|
case TX_RX_POWER_15W:
|
|
pr_info("%s : TX Power is 15W\n", __func__);
|
|
charger->current_rx_power = TX_RX_POWER_15W;
|
|
mfc_set_tx_data(charger, 2);
|
|
break;
|
|
case TX_RX_POWER_17_5W:
|
|
pr_info("%s : TX Power is 17.5W\n", __func__);
|
|
charger->current_rx_power = TX_RX_POWER_17_5W;
|
|
mfc_set_tx_data(charger, 3);
|
|
break;
|
|
case TX_RX_POWER_20W:
|
|
pr_info("%s : TX Power is 20W\n", __func__);
|
|
charger->current_rx_power = TX_RX_POWER_20W;
|
|
mfc_set_tx_data(charger, 4);
|
|
break;
|
|
default:
|
|
pr_info("%s : Undefined TX Power(%d)\n", __func__, tx_power);
|
|
return -EINVAL;
|
|
}
|
|
charger->adt_transfer_status = WIRELESS_AUTH_PASS;
|
|
charger->pdata->cable_type = value.intval = SEC_BATTERY_CABLE_HV_WIRELESS_20;
|
|
pr_info("%s: change cable type to WPC HV 2.0\n", __func__);
|
|
|
|
__pm_stay_awake(charger->wpc_afc_vout_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_afc_vout_work, msecs_to_jiffies(0));
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_MST_V2)
|
|
static void mfc_send_mst_cmd(int cmd, struct mfc_charger_data *charger, u8 irq_src_l, u8 irq_src_h)
|
|
{
|
|
switch (cmd) {
|
|
case MST_MODE_ON:
|
|
/* clear interrupt */
|
|
mfc_reg_write(charger->client, MFC_INT_A_CLEAR_0_REG, irq_src_l); // clear int
|
|
mfc_reg_write(charger->client, MFC_INT_A_CLEAR_1_REG, irq_src_h); // clear int
|
|
mfc_set_cmd_l_reg(charger, 0x20, MFC_CMD_CLEAR_INT_MASK); // command
|
|
|
|
mfc_reg_write(charger->client, MFC_MST_MODE_SEL_REG, MFC_TX_MODE_MST_MODE2); /* set MST mode2 */
|
|
pr_info("%s 2AC Missing ! : MST on REV : %d\n", __func__, charger->pdata->wc_ic_rev);
|
|
|
|
/* clear interrupt */
|
|
mfc_reg_write(charger->client, MFC_INT_A_CLEAR_0_REG, irq_src_l); // clear int
|
|
mfc_reg_write(charger->client, MFC_INT_A_CLEAR_1_REG, irq_src_h); // clear int
|
|
mfc_set_cmd_l_reg(charger, 0x20, MFC_CMD_CLEAR_INT_MASK); // command
|
|
|
|
usleep_range(10000, 11000);
|
|
break;
|
|
case MST_MODE_OFF:
|
|
pr_info("%s: set MST mode off\n", __func__);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int mfc_get_mst_mode(struct mfc_charger_data *charger)
|
|
{
|
|
u8 mst_mode, reg_data;
|
|
int ret;
|
|
|
|
ret = mfc_reg_read(charger->client, MFC_MST_MODE_SEL_REG, &mst_mode);
|
|
if (ret < 0) {
|
|
pr_info("%s mst mode(0x2) i2c write failed, ret = %d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = mfc_reg_read(charger->client, MFC_SYS_OP_MODE_REG, ®_data);
|
|
if (ret < 0) {
|
|
pr_info("%s mst mode change irq(0x4) read failed, ret = %d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
pr_info("%s mst mode check: mst_mode = %d, reg_data = %d\n",
|
|
__func__, mst_mode, reg_data);
|
|
|
|
reg_data &= 0x0C; /* use only [3:2]bit of sys_op_mode register for MST */
|
|
|
|
ret = 0;
|
|
if (reg_data == 0x4)
|
|
ret = mst_mode;
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#define ALIGN_WORK_CHK_CNT 5
|
|
#define ALIGN_WORK_DELAY 500
|
|
#define ALIGN_CHK_PERIOD 1000
|
|
#define ALIGN_WORK_CHK_PERIOD 100
|
|
#define MISALIGN_TX_OFF_TIME 10
|
|
|
|
static int mfc_get_target_vout(struct mfc_charger_data *charger)
|
|
{
|
|
if (charger->vout_strength == 0)
|
|
return (charger->pdata->mis_align_target_vout + charger->pdata->mis_align_offset);
|
|
else
|
|
return charger->pdata->mis_align_target_vout; // falling uvlo
|
|
}
|
|
|
|
static int mfc_unsafe_vout_check(struct mfc_charger_data *charger)
|
|
{
|
|
int vout;
|
|
int target_vout;
|
|
|
|
if (charger->pdata->wpc_vout_ctrl_full && charger->is_full_status)
|
|
return 0;
|
|
|
|
vout = mfc_get_adc(charger, MFC_ADC_VOUT);
|
|
target_vout = mfc_get_target_vout(charger);
|
|
|
|
pr_info("%s: vout(%d) target_vout(%d)\n", __func__, vout, target_vout);
|
|
if (vout < target_vout)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static bool mfc_check_wire_status(void)
|
|
{
|
|
union power_supply_propval value = {0, };
|
|
int wire_type = SEC_BATTERY_CABLE_NONE;
|
|
|
|
psy_do_property("battery", get, POWER_SUPPLY_EXT_PROP_CHARGE_COUNTER_SHADOW, value);
|
|
wire_type = value.intval;
|
|
|
|
if (is_wired_type(wire_type) || (wire_type == SEC_BATTERY_CABLE_OTG)) {
|
|
pr_info("%s: return misalign check, cable_type(%d)\n",
|
|
__func__, wire_type);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void mfc_wpc_align_check_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, align_check_work.work);
|
|
struct timespec64 current_ts = {0, };
|
|
union power_supply_propval value = {0, };
|
|
long checking_time = 0;
|
|
int vout = 0, vout_avr = 0, i = 0;
|
|
static int vout_sum, align_work_cnt;
|
|
|
|
if (!charger->det_state)
|
|
goto end_align_work;
|
|
|
|
if (charger->pdata->wpc_vout_ctrl_full && charger->is_full_status)
|
|
goto end_align_work;
|
|
|
|
if (charger->wc_align_check_start.tv_sec == 0) {
|
|
charger->wc_align_check_start = ktime_to_timespec64(ktime_get_boottime());
|
|
align_work_cnt = 0;
|
|
vout_sum = 0;
|
|
}
|
|
current_ts = ktime_to_timespec64(ktime_get_boottime());
|
|
checking_time = current_ts.tv_sec - charger->wc_align_check_start.tv_sec;
|
|
|
|
vout = mfc_get_adc(charger, MFC_ADC_VOUT);
|
|
vout_sum += vout;
|
|
align_work_cnt++;
|
|
vout_avr = vout_sum / align_work_cnt;
|
|
|
|
pr_info("%s: vout(%d), vout_avr(%d), work_cnt(%d), checking_time(%ld)\n",
|
|
__func__, vout, vout_avr, align_work_cnt, checking_time);
|
|
|
|
if (align_work_cnt < ALIGN_WORK_CHK_CNT) {
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->align_check_work, msecs_to_jiffies(ALIGN_WORK_CHK_PERIOD));
|
|
|
|
return;
|
|
}
|
|
|
|
if (vout_avr >= mfc_get_target_vout(charger)) {
|
|
value.intval = charger->vout_strength = 100;
|
|
psy_do_property("battery",
|
|
set, POWER_SUPPLY_EXT_PROP_WPC_FREQ_STRENGTH, value);
|
|
psy_do_property("wireless", set, POWER_SUPPLY_PROP_CURRENT_MAX, value);
|
|
pr_info("%s: Finish to check (Align)\n", __func__);
|
|
goto end_align_work;
|
|
} else if (checking_time >= MISALIGN_TX_OFF_TIME) {
|
|
pr_info("%s: %s to check (Timer cnt :%d)\n",
|
|
__func__, charger->mis_align_tx_try_cnt == MISALIGN_TX_TRY_CNT ? "Finish" : "Retry",
|
|
charger->mis_align_tx_try_cnt);
|
|
|
|
for (i = 0; i < 30; i++) {
|
|
pr_info("%s: Send a packet to TX device to stop power sharing\n",
|
|
__func__);
|
|
mfc_send_command(charger, MFC_TX_UNO_OFF);
|
|
if (mfc_get_adc(charger, MFC_ADC_VRECT) <= 0)
|
|
break;
|
|
}
|
|
charger->mis_align_tx_try_cnt++;
|
|
mfc_set_wpc_en(charger, WPC_EN_CHARGING, false);
|
|
goto end_algin_work_by_retry;
|
|
} else if (checking_time >= MISALIGN_TX_OFF_TIME * MISALIGN_TX_TRY_CNT) {
|
|
pr_info("%s: Finish to check (Timer expired %d secs)\n",
|
|
__func__, MISALIGN_TX_OFF_TIME * MISALIGN_TX_TRY_CNT);
|
|
goto end_align_work;
|
|
} else {
|
|
if (mfc_check_wire_status())
|
|
goto end_align_work;
|
|
|
|
pr_info("%s: Continue to check until %d secs (Misalign)\n",
|
|
__func__, MISALIGN_TX_OFF_TIME * MISALIGN_TX_TRY_CNT);
|
|
value.intval = charger->vout_strength = 0;
|
|
psy_do_property("battery",
|
|
set, POWER_SUPPLY_EXT_PROP_WPC_FREQ_STRENGTH, value);
|
|
|
|
align_work_cnt = 0;
|
|
vout_sum = 0;
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->align_check_work, msecs_to_jiffies(ALIGN_CHK_PERIOD));
|
|
}
|
|
|
|
return;
|
|
|
|
end_align_work:
|
|
mfc_set_wpc_en(charger, WPC_EN_CHARGING, true);
|
|
charger->mis_align_tx_try_cnt = 1;
|
|
charger->wc_checking_align = false;
|
|
charger->wc_align_check_start.tv_sec = 0;
|
|
end_algin_work_by_retry:
|
|
__pm_relax(charger->align_check_ws);
|
|
}
|
|
|
|
static void mfc_wpc_align_check(struct mfc_charger_data *charger, unsigned int work_delay)
|
|
{
|
|
if (!charger->pdata->mis_align_guide)
|
|
return;
|
|
|
|
if (mfc_check_wire_status())
|
|
return;
|
|
|
|
if (charger->wc_checking_align) {
|
|
pr_info("%s: return, wc_checking_align(%d)\n", __func__, charger->wc_checking_align);
|
|
return;
|
|
}
|
|
|
|
if (!charger->pdata->is_charging) {
|
|
pr_info("%s: return, is_charging(%d)\n",
|
|
__func__, charger->pdata->is_charging);
|
|
return;
|
|
}
|
|
|
|
if (charger->vout_strength >= 100) {
|
|
if (!mfc_unsafe_vout_check(charger)) {
|
|
pr_info("%s: return, safe vout\n", __func__);
|
|
return;
|
|
}
|
|
}
|
|
|
|
pr_info("%s: start\n", __func__);
|
|
__pm_stay_awake(charger->align_check_ws);
|
|
charger->wc_checking_align = true;
|
|
queue_delayed_work(charger->wqueue, &charger->align_check_work, msecs_to_jiffies(work_delay));
|
|
}
|
|
|
|
static void mfc_start_wpc_tx_id_work(struct mfc_charger_data *charger, unsigned int delay)
|
|
{
|
|
__pm_stay_awake(charger->wpc_tx_id_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_tx_id_work, msecs_to_jiffies(delay));
|
|
}
|
|
|
|
static bool mfc_check_to_start_afc_tx(struct mfc_charger_data *charger)
|
|
{
|
|
int vrect_level, vout_level;
|
|
|
|
vrect_level = mfc_get_adc(charger, MFC_ADC_VRECT);
|
|
vout_level = mfc_get_adc(charger, MFC_ADC_VOUT);
|
|
pr_info("%s: read vrect(%dmV), vout(%dmV)\n", __func__, vrect_level, vout_level);
|
|
|
|
return (vrect_level < 8500 || vout_level < 8500);
|
|
}
|
|
|
|
static void mfc_set_mpp_sleep_mode(struct mfc_charger_data *charger, int enable)
|
|
{
|
|
int is_thm_limited = charger->thermal_ctrl;
|
|
|
|
pr_info("@MPP %s enable(%d) thm_ctrl(%d)\n", __func__, enable, is_thm_limited);
|
|
|
|
if (enable) {
|
|
stwlc89_set_target_ilim(charger, 400);
|
|
mfc_mpp_thermal_ctrl(charger, enable);
|
|
} else {
|
|
if (is_thm_limited)
|
|
return;
|
|
mfc_mpp_thermal_ctrl(charger, enable);
|
|
stwlc89_set_target_ilim(charger,
|
|
charger->mpp_epp_nego_done_power * 100 / mfc_get_volt(charger->pdata->mpp_vout));
|
|
}
|
|
}
|
|
|
|
static void mfc_start_bpp_mode(struct mfc_charger_data *charger)
|
|
{
|
|
if (!charger->is_full_status) {
|
|
/* send request afc_tx , request afc is mandatory */
|
|
msleep(charger->req_afc_delay);
|
|
mfc_send_command(charger, MFC_REQUEST_AFC_TX);
|
|
__pm_stay_awake(charger->wpc_tx_pwr_budg_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_tx_pwr_budg_work, msecs_to_jiffies(1200));
|
|
}
|
|
}
|
|
|
|
static void mfc_set_epp_mode(struct mfc_charger_data *charger, int nego_power)
|
|
{
|
|
mfc_set_psy_wrl(charger,
|
|
POWER_SUPPLY_EXT_PROP_WIRELESS_MAX_VOUT,
|
|
charger->vout_by_txid);
|
|
|
|
/* Update max power */
|
|
charger->max_power_by_txid = nego_power * 100000;
|
|
mfc_set_rx_power(charger, charger->max_power_by_txid);
|
|
charger->current_rx_power = nego_power;
|
|
}
|
|
|
|
static void mfc_set_epp_nv_mode(struct mfc_charger_data *charger)
|
|
{
|
|
mfc_set_psy_wrl(charger,
|
|
POWER_SUPPLY_EXT_PROP_WIRELESS_MAX_VOUT,
|
|
WIRELESS_VOUT_5_5V);
|
|
|
|
mfc_set_rx_power(charger, charger->max_power_by_txid);
|
|
}
|
|
|
|
static bool is_wpc_auth_support(struct mfc_charger_data *charger)
|
|
{
|
|
u8 reg_data = 0;
|
|
|
|
if (mfc_reg_read(charger->client, MFC_TX_WPC_AUTH_SUPPORT_REG, ®_data) >= 0) {
|
|
if (reg_data == 0x00) {
|
|
pr_info("@EPP %s: wpc auth not support (0x%x)\n", __func__, reg_data);
|
|
return false;
|
|
} else {
|
|
pr_info("@EPP %s: wpc auth support (0x%x)\n", __func__, reg_data);
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void mfc_set_epp_count(struct mfc_charger_data *charger, unsigned int count)
|
|
{
|
|
if (delayed_work_pending(&charger->epp_count_work)) {
|
|
__pm_relax(charger->epp_count_ws);
|
|
cancel_delayed_work(&charger->epp_count_work);
|
|
}
|
|
|
|
charger->epp_count = count;
|
|
pr_info("%s: %d\n", __func__, count);
|
|
if (count <= 0)
|
|
return;
|
|
|
|
__pm_stay_awake(charger->epp_count_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->epp_count_work, msecs_to_jiffies(4000));
|
|
}
|
|
|
|
static void mfc_epp_count_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, epp_count_work.work);
|
|
|
|
pr_info("%s: %d\n", __func__, charger->epp_count);
|
|
charger->epp_count = 0;
|
|
__pm_relax(charger->epp_count_ws);
|
|
}
|
|
|
|
static void mfc_wpc_mode_change_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, mode_change_work.work);
|
|
u8 op_mode = 0;
|
|
|
|
pr_info("%s: start\n", __func__);
|
|
if (mfc_reg_read(charger->client, MFC_SYS_OP_MODE_REG, &op_mode) <= 0)
|
|
goto end_work;
|
|
|
|
charger->rx_op_mode = op_mode >> 5;
|
|
pr_info("%s: rx op_mode register = 0x%x\n", __func__, charger->rx_op_mode);
|
|
|
|
//Enable authentication, but please note that re-nego or cloak will enter MPP or EPP again
|
|
switch (charger->rx_op_mode) {
|
|
case MFC_RX_MODE_AC_MISSING:
|
|
//pr_info("%s: MFC_RX_MODE_AC_MISSING\n", __func__);
|
|
break;
|
|
case MFC_RX_MODE_WPC_BPP:
|
|
pr_info("%s: MFC_RX_MODE_WPC_BPP\n", __func__);
|
|
if (charger->fod_cnt > charger->pdata->fod_cnt_thresh) {
|
|
pr_info("%s: clear fod cnt w/a\n", __func__);
|
|
mfc_wpc_check_fod_count(charger, FOD_CNT_CLEAR);
|
|
}
|
|
/*TODO: enable FWC AUTH*/
|
|
break;
|
|
case MFC_RX_MODE_WPC_EPP:
|
|
pr_info("@EPP %s: MFC_RX_MODE_WPC_EPP\n", __func__);
|
|
if (!is_3rd_pad((charger->mpp_epp_tx_id & 0xFFFF)))
|
|
mfc_epp_enable(charger, 1);
|
|
mfc_set_epp_count(charger, 0);
|
|
|
|
if (is_samsung_pad((charger->mpp_epp_tx_id & 0xFF))) {
|
|
if (charger->is_full_status || charger->sleep_mode) {
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
__pm_stay_awake(charger->wpc_vout_mode_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_vout_mode_work, 0);
|
|
}
|
|
mfc_start_wpc_tx_id_work(charger, 1000);
|
|
} else {
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
__pm_stay_awake(charger->wpc_vout_mode_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_vout_mode_work, 0);
|
|
if (charger->mpp_epp_nego_done_power < TX_RX_POWER_8W) {
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS_EPP_NV);
|
|
mfc_set_epp_nv_mode(charger);
|
|
break;
|
|
}
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS_EPP);
|
|
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
charger->adt_transfer_status = WIRELESS_AUTH_PASS;
|
|
#endif
|
|
if (charger->adt_transfer_status == WIRELESS_AUTH_PASS) {
|
|
mfc_set_epp_mode(charger, charger->mpp_epp_nego_done_power);
|
|
break;
|
|
}
|
|
|
|
if (charger->adt_transfer_status != WIRELESS_AUTH_WAIT)
|
|
break;
|
|
|
|
if (!is_wpc_auth_support(charger)) {
|
|
mfc_set_epp_mode(charger, TX_RX_POWER_8W);
|
|
break;
|
|
}
|
|
|
|
mfc_auth_set_configs(charger, AUTH_READY);
|
|
/* notify auth service to send TX PAD a request key */
|
|
mfc_auth_send_adt_status(charger, WIRELESS_AUTH_START);
|
|
}
|
|
break;
|
|
case MFC_RX_MODE_WPC_MPP_RESTRICT:
|
|
pr_info("@MPP %s: MFC_RX_MODE_WPC_MPP_RESTRICT\n", __func__);
|
|
break;
|
|
case MFC_RX_MODE_WPC_MPP_FULL:
|
|
pr_info("@MPP %s: MFC_RX_MODE_WPC_MPP_FULL\n", __func__);
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
charger->adt_transfer_status = WIRELESS_AUTH_PASS;
|
|
#endif
|
|
if (charger->adt_transfer_status == WIRELESS_AUTH_PASS) {
|
|
mfc_set_epp_mode(charger, charger->mpp_epp_nego_done_power);
|
|
break;
|
|
}
|
|
|
|
if (charger->adt_transfer_status != WIRELESS_AUTH_WAIT)
|
|
break;
|
|
|
|
if (!is_wpc_auth_support(charger)) {
|
|
mfc_set_epp_mode(charger, TX_RX_POWER_8W);
|
|
break;
|
|
}
|
|
|
|
mfc_auth_set_configs(charger, AUTH_READY);
|
|
/* notify auth service to send TX PAD a request key */
|
|
msleep(1800);
|
|
mfc_auth_send_adt_status(charger, WIRELESS_AUTH_START);
|
|
break;
|
|
case MFC_RX_MODE_WPC_MPP_CLOAK:
|
|
pr_info("@MPP %s: MFC_RX_MODE_WPC_MPP_CLOAK\n", __func__);
|
|
charger->mpp_cloak_status = true;
|
|
break;
|
|
case MFC_RX_MODE_WPC_MPP_NEGO:
|
|
pr_info("@MPP %s: MFC_RX_MODE_WPC_MPP_NEGO\n", __func__);
|
|
mfc_mpp_epp_nego_power_set(charger, MFC_RX_MPP_NEGO_POWER_15W); // need to add this in dtsi
|
|
if (charger->sleep_mode)
|
|
mfc_set_mpp_sleep_mode(charger, charger->sleep_mode);
|
|
break;
|
|
case MFC_RX_MODE_WPC_EPP_NEGO:
|
|
pr_info("@EPP %s: MFC_RX_MODE_WPC_EPP_NEGO\n", __func__);
|
|
//mfc_mpp_epp_nego_power_set(charger, MFC_RX_MPP_NEGO_POWER_15W); // need to add this in dtsi
|
|
break;
|
|
}
|
|
|
|
if (!charger->wc_tx_enable)
|
|
goto end_work;
|
|
|
|
op_mode = op_mode & 0xF;
|
|
pr_info("%s: tx op_mode = 0x%x\n", __func__, op_mode);
|
|
|
|
if (op_mode == MFC_TX_MODE_TX_PWR_HOLD) {
|
|
if (charger->wc_rx_type == SS_GEAR) {
|
|
/* start 3min alarm timer */
|
|
pr_info("@Tx_Mode %s: Received PHM and start PHM disable alarm by 3min\n", __func__);
|
|
alarm_start(&charger->phm_alarm,
|
|
ktime_add(ktime_get_boottime(), ktime_set(180, 0)));
|
|
} else {
|
|
pr_info("%s: TX entered PHM but no PHM disable 3min timer\n", __func__);
|
|
}
|
|
|
|
mfc_set_tx_phm(charger, true);
|
|
} else {
|
|
mfc_test_read(charger);
|
|
|
|
if (charger->tx_device_phm) {
|
|
pr_info("@Tx_Mode %s: Ended PHM\n", __func__);
|
|
mfc_set_tx_phm(charger, false);
|
|
}
|
|
if (charger->phm_alarm.state & ALARMTIMER_STATE_ENQUEUED) {
|
|
pr_info("@Tx_Mode %s: escape PHM mode, cancel PHM alarm\n", __func__);
|
|
cancel_delayed_work(&charger->wpc_tx_phm_work);
|
|
__pm_relax(charger->wpc_tx_phm_ws);
|
|
alarm_cancel(&charger->phm_alarm);
|
|
}
|
|
}
|
|
|
|
end_work:
|
|
__pm_relax(charger->mode_change_ws);
|
|
}
|
|
|
|
static void stwlc89_adt_transfer_result(struct mfc_charger_data *charger, int adt_state)
|
|
{
|
|
#if !defined(CONFIG_SEC_FACTORY)
|
|
if ((charger->pdata->cable_type == SEC_BATTERY_CABLE_NONE) ||
|
|
(charger->adt_transfer_status == WIRELESS_AUTH_WAIT)) {
|
|
pr_info("%s %s: auth service sent wrong cmd(%d)\n", WC_AUTH_MSG, __func__, adt_state);
|
|
return;
|
|
} else if (charger->adt_transfer_status == adt_state) {
|
|
pr_info("%s %s: skip a same PASS/FAIL result\n", WC_AUTH_MSG, __func__);
|
|
return;
|
|
} else if ((adt_state != WIRELESS_AUTH_PASS) && (adt_state != WIRELESS_AUTH_FAIL)) {
|
|
pr_info("%s %s: undefined PASS/FAIL result(%d)\n", WC_AUTH_MSG, __func__, adt_state);
|
|
charger->adt_transfer_status = adt_state;
|
|
goto end_adt;
|
|
}
|
|
|
|
charger->adt_transfer_status = adt_state;
|
|
switch (stwlc89_get_auth_mode(charger)) {
|
|
case WPC_AUTH_MODE_EPP:
|
|
mfc_set_epp_mode(charger,
|
|
(adt_state != WIRELESS_AUTH_PASS) ? TX_RX_POWER_8W : charger->mpp_epp_nego_done_power);
|
|
break;
|
|
case WPC_AUTH_MODE_MPP:
|
|
pr_info("@MPP @EPP %s: Tx nego done power: %d\n",
|
|
__func__, charger->mpp_epp_nego_done_power);
|
|
mfc_set_epp_mode(charger,
|
|
(adt_state != WIRELESS_AUTH_PASS) ? TX_RX_POWER_8W : charger->mpp_epp_nego_done_power);
|
|
break;
|
|
case WPC_AUTH_MODE_PPDE:
|
|
if (adt_state == WIRELESS_AUTH_PASS) {
|
|
stwlc89_mfc_fod_set_op_mode(charger->fod, WPC_OP_MODE_PPDE);
|
|
charger->pdata->cable_type = SEC_BATTERY_CABLE_HV_WIRELESS_20;
|
|
__pm_stay_awake(charger->wpc_afc_vout_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_afc_vout_work, msecs_to_jiffies(0));
|
|
pr_info("%s %s: PASS! type = %d\n", WC_AUTH_MSG,
|
|
__func__, charger->pdata->cable_type);
|
|
} else {
|
|
if (epp_mode(charger->rx_op_mode) ||
|
|
charger->afc_tx_done) {
|
|
charger->pdata->cable_type = SEC_BATTERY_CABLE_HV_WIRELESS;
|
|
__pm_stay_awake(charger->wpc_afc_vout_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_afc_vout_work, msecs_to_jiffies(0));
|
|
} else {
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS);
|
|
}
|
|
}
|
|
break;
|
|
case WPC_AUTH_MODE_BPP:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
end_adt:
|
|
mfc_auth_set_configs(charger, AUTH_COMPLETE);
|
|
#endif
|
|
}
|
|
|
|
static const char *mfc_bd_log(struct mfc_charger_data *charger, int wrl_mode)
|
|
{
|
|
memset(charger->d_buf, 0, MFC_BAT_DUMP_SIZE);
|
|
|
|
if (wrl_mode == SB_WRL_TX_MODE) {
|
|
snprintf(charger->d_buf, MFC_BAT_DUMP_SIZE, "%d,%d,%d,%d,%d,%d,%s,%x,0x%x,",
|
|
charger->mfc_adc_tx_vout,
|
|
charger->mfc_adc_tx_iout,
|
|
charger->mfc_adc_ping_frq,
|
|
charger->mfc_adc_tx_min_op_frq,
|
|
charger->mfc_adc_tx_max_op_frq,
|
|
charger->tx_device_phm,
|
|
sb_rx_type_str(charger->wc_rx_type),
|
|
charger->otp_firmware_ver,
|
|
charger->pdata->wc_ic_rev);
|
|
} else if (wrl_mode == SB_WRL_RX_MODE) {
|
|
snprintf(charger->d_buf, MFC_BAT_DUMP_SIZE, "%d,%d,%d,%d,0x%x,%d,%x,0x%x,0x%x,%016llX,%016llX,",
|
|
charger->mfc_adc_vout,
|
|
charger->mfc_adc_vrect,
|
|
charger->mfc_adc_rx_iout,
|
|
charger->mfc_adc_op_frq,
|
|
charger->tx_id,
|
|
charger->die_temp,
|
|
charger->otp_firmware_ver,
|
|
charger->pdata->wc_ic_rev,
|
|
charger->rx_op_mode,
|
|
charger->now_cmfet_state.value,
|
|
charger->now_fod_state.value);
|
|
}
|
|
|
|
return charger->d_buf;
|
|
}
|
|
|
|
static void mfc_check_phm_status(struct mfc_charger_data *charger)
|
|
{
|
|
union power_supply_propval value = { 0, };
|
|
int is_wireless = false;
|
|
|
|
psy_do_property("wireless", get, POWER_SUPPLY_PROP_ONLINE, value);
|
|
is_wireless = value.intval;
|
|
|
|
if (!is_wireless) {
|
|
charger->rx_phm_check_cnt = 0;
|
|
return;
|
|
}
|
|
pr_info("%s: is_wireless=%d cnt=%d\n", __func__, is_wireless, charger->rx_phm_check_cnt);
|
|
|
|
if (charger->rx_phm_check_cnt++ > 5) {
|
|
pr_info("%s wc deactivated during phm:rx_phm_status(%d)\n",
|
|
__func__, charger->rx_phm_status);
|
|
|
|
if (charger->rx_phm_status) {
|
|
charger->rx_phm_state = END_PHM;
|
|
__pm_stay_awake(charger->wpc_rx_phm_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_rx_phm_work, 0);
|
|
} else
|
|
mfc_deactivate_work_content(charger);
|
|
}
|
|
}
|
|
|
|
static void stwlc89_monitor_work(struct mfc_charger_data *charger)
|
|
{
|
|
union power_supply_propval value = { 0, };
|
|
struct sec_vote *chgen_vote = NULL;
|
|
int pdet_b = 1, wpc_det = 0;
|
|
int thermal_zone = BAT_THERMAL_NORMAL, capacity = 50, chgen = SEC_BAT_CHG_MODE_CHARGING;
|
|
int ret = 0, temperature = 0;
|
|
|
|
if (gpio_get_value(charger->pdata->wpc_en))
|
|
pr_info("@DIS_MFC %s: charger->wpc_en_flag(0x%x)\n", __func__, charger->wpc_en_flag);
|
|
|
|
if (charger->pdata->wpc_pdet_b >= 0)
|
|
pdet_b = gpio_get_value(charger->pdata->wpc_pdet_b);
|
|
|
|
if (charger->pdata->wpc_det >= 0)
|
|
wpc_det = gpio_get_value(charger->pdata->wpc_det);
|
|
|
|
if (!wpc_det) {
|
|
if (!pdet_b) {
|
|
pr_info("%s: now phm!\n", __func__);
|
|
charger->rx_phm_check_cnt = 0;
|
|
} else
|
|
mfc_check_phm_status(charger);
|
|
return;
|
|
}
|
|
charger->rx_phm_check_cnt = 0;
|
|
|
|
ret = psy_do_property("battery", get,
|
|
POWER_SUPPLY_EXT_PROP_THERMAL_ZONE, value);
|
|
if (!ret)
|
|
thermal_zone = value.intval;
|
|
|
|
ret = psy_do_property("battery", get,
|
|
POWER_SUPPLY_PROP_CAPACITY, value);
|
|
if (!ret)
|
|
capacity = value.intval;
|
|
|
|
ret = psy_do_property("battery", get,
|
|
POWER_SUPPLY_PROP_TEMP, value);
|
|
if (!ret)
|
|
temperature = value.intval;
|
|
|
|
chgen_vote = find_vote("CHGEN");
|
|
if (chgen_vote) {
|
|
ret = get_sec_voter_status(chgen_vote, VOTER_SWELLING, &chgen);
|
|
if (ret < 0)
|
|
chgen = SEC_BAT_CHG_MODE_CHARGING;
|
|
}
|
|
|
|
switch (thermal_zone) {
|
|
case BAT_THERMAL_OVERHEATLIMIT:
|
|
case BAT_THERMAL_OVERHEAT:
|
|
case BAT_THERMAL_WARM:
|
|
if (stwlc_is_mpp_op_mode(charger)) {
|
|
if (charger->rx_op_mode == MFC_RX_MODE_WPC_MPP_CLOAK)
|
|
mfc_mpp_exit_cloak(charger);
|
|
mfc_epp_enable(charger, 0);
|
|
if (delayed_work_pending(&charger->epp_clear_timer_work)) {
|
|
__pm_relax(charger->epp_clear_ws);
|
|
cancel_delayed_work(&charger->epp_clear_timer_work);
|
|
}
|
|
charger->high_swell = true;
|
|
mfc_send_ept_rst(charger);
|
|
}
|
|
stwlc89_mfc_cmfet_set_high_swell(charger->cmfet, true);
|
|
stwlc89_mfc_fod_set_high_swell(charger->fod, true);
|
|
if ((chgen == SEC_BAT_CHG_MODE_CHARGING_OFF) ||
|
|
(chgen == SEC_BAT_CHG_MODE_BUCK_OFF)) {
|
|
stwlc89_mfc_cmfet_set_chg_done(charger->cmfet, true);
|
|
stwlc89_mfc_fod_set_bat_state(charger->fod, MFC_FOD_BAT_STATE_FULL);
|
|
} else if (chgen == SEC_BAT_CHG_MODE_CHARGING) {
|
|
stwlc89_mfc_cmfet_set_chg_done(charger->cmfet, false);
|
|
}
|
|
break;
|
|
default:
|
|
if (charger->high_swell && charger->pdata->high_swell_recov >= temperature) {
|
|
mfc_soft_reset_ask_stop(charger);
|
|
charger->high_swell = false;
|
|
mfc_epp_enable(charger, 1);
|
|
}
|
|
stwlc89_mfc_cmfet_set_high_swell(charger->cmfet, false);
|
|
stwlc89_mfc_fod_set_high_swell(charger->fod, false);
|
|
if (chgen == SEC_BAT_CHG_MODE_CHARGING)
|
|
stwlc89_mfc_cmfet_set_chg_done(charger->cmfet, false);
|
|
break;
|
|
}
|
|
stwlc89_mfc_cmfet_set_bat_cap(charger->cmfet, capacity);
|
|
stwlc89_mfc_fod_set_bat_cap(charger->fod, capacity);
|
|
|
|
pr_info("%s: check thermal_zone = %d, capacity = %d, chgen = %d temp=%d swell(%d)\n",
|
|
__func__, thermal_zone, capacity, chgen, temperature, charger->high_swell);
|
|
}
|
|
|
|
static int is_param_bin_fw_ver_equal(unsigned int param_fw_ver, unsigned int bin_fw_ver)
|
|
{
|
|
#if defined(CONFIG_WIRELESS_IC_PARAM)
|
|
pr_info("%s: param_fw_ver(0x%04X), bin_fw_ver(0x%04X)\n",
|
|
__func__, param_fw_ver, bin_fw_ver);
|
|
|
|
return (param_fw_ver == bin_fw_ver) ? 1 : 0;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static bool in_opfreq_ctrl_pad_list(struct mfc_charger_data *charger)
|
|
{
|
|
int i = 0;
|
|
bool ret = false;
|
|
|
|
for (i = 0; i < charger->pdata->opfreq_ctrl_pad_list_size; i++)
|
|
ret |= (charger->pdata->opfreq_ctrl_pad_list[i] == charger->tx_id);
|
|
return ret;
|
|
}
|
|
|
|
static bool is_opfreq_ctrl_pad(struct mfc_charger_data *charger)
|
|
{
|
|
bool ret = false;
|
|
|
|
if (charger->pdata->opfreq_ctrl_pad_list_size > 0)
|
|
ret = in_opfreq_ctrl_pad_list(charger);
|
|
else
|
|
ret = opfreq_ctrl_pad(charger->tx_id);
|
|
return ret;
|
|
}
|
|
|
|
static int stwlc89_chg_get_property(struct power_supply *psy,
|
|
enum power_supply_property psp,
|
|
union power_supply_propval *val)
|
|
{
|
|
struct mfc_charger_data *charger = power_supply_get_drvdata(psy);
|
|
enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp;
|
|
|
|
switch ((int)psp) {
|
|
case POWER_SUPPLY_PROP_STATUS:
|
|
pr_info("%s: charger->pdata->cs100_status %d\n", __func__, charger->pdata->cs100_status);
|
|
val->intval = charger->pdata->cs100_status;
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
|
case POWER_SUPPLY_PROP_HEALTH:
|
|
return -ENODATA;
|
|
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
|
if (charger->pdata->is_charging)
|
|
val->intval = mfc_get_vout(charger);
|
|
else
|
|
val->intval = 0;
|
|
break;
|
|
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
|
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
|
return -ENODATA;
|
|
case POWER_SUPPLY_PROP_ONLINE:
|
|
pr_info("%s: cable_type =%d\n", __func__, charger->pdata->cable_type);
|
|
val->intval = charger->pdata->cable_type;
|
|
break;
|
|
case POWER_SUPPLY_PROP_MANUFACTURER:
|
|
pr_info("%s: POWER_SUPPLY_PROP_MANUFACTURER, intval(0x%x), called by(%ps)\n",
|
|
__func__, val->intval, __builtin_return_address(0));
|
|
if (val->intval == SEC_WIRELESS_OTP_FIRM_RESULT) {
|
|
pr_info("%s: otp firmware result = %d\n", __func__, charger->pdata->otp_firmware_result);
|
|
val->intval = charger->pdata->otp_firmware_result;
|
|
} else if (val->intval == SEC_WIRELESS_IC_REVISION) {
|
|
pr_info("%s: check ic revision\n", __func__);
|
|
val->intval = mfc_get_ic_revision(charger, MFC_IC_REVISION);
|
|
} else if (val->intval == SEC_WIRELESS_IC_CHIP_ID) {
|
|
pr_info("%s: check ic chip_id(0x%02X)\n", __func__, charger->chip_id);
|
|
val->intval = charger->chip_id;
|
|
} else if (val->intval == SEC_WIRELESS_OTP_FIRM_VER_BIN) {
|
|
/* update latest kernl f/w version */
|
|
val->intval = mfc_get_bin_fw_version(charger);
|
|
} else if (val->intval == SEC_WIRELESS_OTP_FIRM_VER) {
|
|
val->intval = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE);
|
|
pr_info("%s: check f/w revision (0x%x)\n", __func__, val->intval);
|
|
if (val->intval < 0 && charger->otp_firmware_ver > 0)
|
|
val->intval = charger->otp_firmware_ver;
|
|
} else if (val->intval == SEC_WIRELESS_OTP_FIRM_VERIFY) {
|
|
pr_info("%s: STM FIRM_VERIFY is not implemented\n", __func__);
|
|
val->intval = 1;
|
|
} else {
|
|
val->intval = -ENODATA;
|
|
pr_err("%s: wrong mode\n", __func__);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_ENERGY_NOW: /* vout */
|
|
if (charger->pdata->is_charging) {
|
|
val->intval = mfc_get_adc(charger, MFC_ADC_VOUT);
|
|
pr_info("%s: wc vout (%d)\n", __func__, val->intval);
|
|
} else {
|
|
val->intval = 0;
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_ENERGY_AVG: /* vrect */
|
|
if (charger->pdata->is_charging)
|
|
val->intval = mfc_get_adc(charger, MFC_ADC_VRECT);
|
|
else
|
|
val->intval = 0;
|
|
break;
|
|
case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
|
|
val->intval = charger->vrect_by_txid;
|
|
break;
|
|
case POWER_SUPPLY_PROP_SCOPE:
|
|
val->intval = mfc_get_adc(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_EMPTY:
|
|
val->intval = charger->wc_ldo_status;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_MIN ... POWER_SUPPLY_EXT_PROP_MAX:
|
|
switch (ext_psp) {
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_OP_FREQ:
|
|
val->intval = mfc_get_adc(charger, MFC_ADC_OP_FRQ);
|
|
pr_info("%s: Operating FQ %dkHz\n", __func__, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_OP_FREQ_STRENGTH:
|
|
val->intval = charger->vout_strength;
|
|
pr_info("%s: vout strength = (%d)\n",
|
|
__func__, charger->vout_strength);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_TRX_CMD:
|
|
val->intval = charger->pdata->trx_data_cmd;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_TRX_VAL:
|
|
val->intval = charger->pdata->trx_data_val;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ID:
|
|
val->intval = charger->tx_id;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ID_CNT:
|
|
val->intval = charger->tx_id_cnt;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_CONNECTED:
|
|
val->intval = charger->wc_rx_connected;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_TYPE:
|
|
val->intval = charger->wc_rx_type;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_UNO_VIN:
|
|
val->intval = mfc_get_adc(charger, MFC_ADC_TX_VOUT);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_UNO_IIN:
|
|
val->intval = mfc_get_adc(charger, MFC_ADC_TX_IOUT);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_POWER:
|
|
val->intval = charger->current_rx_power;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS:
|
|
val->intval = charger->adt_transfer_status;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_DATA:
|
|
{
|
|
//int i = 0;
|
|
//u8 *p_data;
|
|
|
|
if (charger->adt_transfer_status == WIRELESS_AUTH_RECEIVED) {
|
|
pr_info("%s %s: MFC_ADT_RECEIVED (%d)\n",
|
|
WC_AUTH_MSG, __func__, charger->adt_transfer_status);
|
|
val->strval = (u8 *)ADT_buffer_rdata;
|
|
//p_data = ADT_buffer_rdata;
|
|
//for (i = 0; i < adt_readSize; i++)
|
|
// pr_info("%s: auth read data = %x", __func__, p_data[i]);
|
|
//pr_info("\n", __func__);
|
|
} else {
|
|
pr_info("%s: data hasn't been received yet\n", __func__);
|
|
return -ENODATA;
|
|
}
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_SIZE:
|
|
val->intval = adt_readSize;
|
|
pr_info("%s %s: MFC_ADT_RECEIVED (%d), DATA SIZE(%d)\n",
|
|
WC_AUTH_MSG, __func__, charger->adt_transfer_status, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_VOUT:
|
|
val->intval = charger->vout_mode;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_INITIAL_WC_CHECK:
|
|
val->intval = charger->initial_wc_check;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_CHECK_FW_VER:
|
|
val->intval = is_param_bin_fw_ver_equal(
|
|
charger->wireless_fw_ver_param, mfc_get_bin_fw_version(charger));
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_MST_PWR_EN:
|
|
if (gpio_is_valid(charger->pdata->mst_pwr_en)) {
|
|
val->intval = gpio_get_value(charger->pdata->mst_pwr_en);
|
|
} else {
|
|
pr_info("%s: invalid gpio(mst_pwr_en)\n", __func__);
|
|
val->intval = 0;
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_PAD_VOLT_CTRL:
|
|
val->intval = charger->is_afc_tx;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WPC_EN:
|
|
val->intval = gpio_get_value(charger->pdata->wpc_en);
|
|
break;
|
|
#if defined(CONFIG_MST_V2)
|
|
case POWER_SUPPLY_EXT_PROP_MST_MODE:
|
|
val->intval = mfc_get_mst_mode(charger);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_MST_DELAY:
|
|
val->intval = DELAY_FOR_MST;
|
|
break;
|
|
#endif
|
|
case POWER_SUPPLY_EXT_PROP_MONITOR_WORK:
|
|
stwlc89_monitor_work(charger);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_GEAR_PHM_EVENT:
|
|
val->intval = charger->tx_device_phm;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_RX_PHM:
|
|
val->intval = charger->rx_phm_status;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL:
|
|
case POWER_SUPPLY_EXT_PROP_CHARGE_POWERED_OTG_CONTROL:
|
|
return -ENODATA;
|
|
case POWER_SUPPLY_EXT_PROP_INPUT_VOLTAGE_REGULATION:
|
|
val->intval = charger->pdata->vout_status;
|
|
break;
|
|
#if defined(CONFIG_WIRELESS_IC_PARAM)
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_PARAM_INFO:
|
|
val->intval = charger->wireless_param_info;
|
|
break;
|
|
#endif
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_SGF:
|
|
{
|
|
sgf_data data;
|
|
|
|
data.size = *(int *)val->strval;
|
|
data.type = *(int *)(val->strval + 4);
|
|
data.data = (char *)(val->strval + 8);
|
|
mfc_set_sgf_data(charger, &data);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WPC_FREQ_STRENGTH:
|
|
pr_info("%s: vout_strength = %d\n",
|
|
__func__, charger->vout_strength);
|
|
val->intval = charger->vout_strength;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_BATT_DUMP:
|
|
val->strval = mfc_bd_log(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_TX_PWR_BUDG:
|
|
switch (charger->tx_pwr_budg) {
|
|
case MFC_TX_PWR_BUDG_2W:
|
|
case MFC_TX_PWR_BUDG_5W:
|
|
val->intval = RX_POWER_5W;
|
|
break;
|
|
case MFC_TX_PWR_BUDG_7_5W:
|
|
val->intval = RX_POWER_7_5W;
|
|
break;
|
|
case MFC_TX_PWR_BUDG_12W:
|
|
val->intval = RX_POWER_12W;
|
|
break;
|
|
case MFC_TX_PWR_BUDG_15W:
|
|
val->intval = RX_POWER_15W;
|
|
break;
|
|
default:
|
|
val->intval = RX_POWER_NONE;
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_MPP_CLOAK:
|
|
val->intval = charger->rx_op_mode == MFC_RX_MODE_WPC_MPP_CLOAK ? 1 : 0;
|
|
pr_info("@MPP %s: MPP_CLOAK(%d)\n", __func__, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_OP_MODE:
|
|
val->intval = charger->rx_op_mode;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_MPP_VALUE:
|
|
{
|
|
u8 reg_data[2] = {0,};
|
|
|
|
if (!mpp_mode(charger->rx_op_mode)) {
|
|
val->intval = -1;
|
|
break;
|
|
}
|
|
mfc_reg_multi_read(charger->client, MFC_MPP_EPP_ESTIMATE_K_H_REG, reg_data, 2);
|
|
val->intval = ((reg_data[0] << 8) + reg_data[1]) * 1000 / 4095;
|
|
pr_info("@MPP %s: MPP_VALUE estimated K: %d\n", __func__, val->intval);
|
|
}
|
|
break;
|
|
default:
|
|
return -ENODATA;
|
|
}
|
|
break;
|
|
default:
|
|
return -ENODATA;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void mfc_wpc_vout_mode_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_vout_mode_work.work);
|
|
int vout_step = charger->pdata->vout_status;
|
|
int vout = MFC_VOUT_10V;
|
|
int wpc_vout_ctrl_lcd_on = 0;
|
|
union power_supply_propval value = {0, };
|
|
|
|
if (is_shutdn) {
|
|
pr_err("%s: Escape by shtudown\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_MPP &&
|
|
charger->vout_mode != WIRELESS_VOUT_13V) {
|
|
pr_info("@mpp %s: skip - vout_mode(%s), vout_status(%s)\n", __func__,
|
|
sb_vout_ctr_mode_str(charger->vout_mode),
|
|
sb_rx_vout_str(charger->pdata->vout_status));
|
|
__pm_relax(charger->wpc_vout_mode_ws);
|
|
}
|
|
|
|
pr_info("%s: start - vout_mode(%s), vout_status(%s)\n", __func__,
|
|
sb_vout_ctr_mode_str(charger->vout_mode),
|
|
sb_rx_vout_str(charger->pdata->vout_status));
|
|
switch (charger->vout_mode) {
|
|
case WIRELESS_VOUT_4_5V:
|
|
mfc_set_vout(charger, MFC_VOUT_4_5V);
|
|
break;
|
|
case WIRELESS_VOUT_5V:
|
|
mfc_set_vout(charger, MFC_VOUT_5V);
|
|
break;
|
|
case WIRELESS_VOUT_5_5V:
|
|
mfc_set_vout(charger, MFC_VOUT_5_5V);
|
|
break;
|
|
case WIRELESS_VOUT_9V:
|
|
mfc_set_vout(charger, MFC_VOUT_9V);
|
|
break;
|
|
case WIRELESS_VOUT_10V:
|
|
mfc_set_vout(charger, MFC_VOUT_10V);
|
|
/* reset AICL */
|
|
psy_do_property("wireless", set, POWER_SUPPLY_PROP_CURRENT_MAX, value);
|
|
break;
|
|
case WIRELESS_VOUT_11V:
|
|
mfc_set_vout(charger, MFC_VOUT_11V);
|
|
/* reset AICL */
|
|
psy_do_property("wireless", set, POWER_SUPPLY_PROP_CURRENT_MAX, value);
|
|
break;
|
|
case WIRELESS_VOUT_12V:
|
|
mfc_set_vout(charger, MFC_VOUT_12V);
|
|
/* reset AICL */
|
|
psy_do_property("wireless", set, POWER_SUPPLY_PROP_CURRENT_MAX, value);
|
|
break;
|
|
case WIRELESS_VOUT_12_5V:
|
|
mfc_set_vout(charger, MFC_VOUT_12_5V);
|
|
/* reset AICL */
|
|
psy_do_property("wireless", set, POWER_SUPPLY_PROP_CURRENT_MAX, value);
|
|
break;
|
|
case WIRELESS_VOUT_13V:
|
|
mfc_set_vout(charger, MFC_VOUT_13V);
|
|
/* reset AICL */
|
|
psy_do_property("wireless", set, POWER_SUPPLY_PROP_CURRENT_MAX, value);
|
|
break;
|
|
case WIRELESS_VOUT_5V_STEP:
|
|
vout_step--;
|
|
if (vout_step >= MFC_VOUT_5V) {
|
|
mfc_set_vout(charger, vout_step);
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_vout_mode_work, msecs_to_jiffies(250));
|
|
return;
|
|
}
|
|
break;
|
|
case WIRELESS_VOUT_5_5V_STEP:
|
|
value.intval = in_opfreq_ctrl_pad_list(charger);
|
|
psy_do_property("battery", set, POWER_SUPPLY_EXT_PROP_LCD_FLICKER, value); // Set VOUT CTRL MODE
|
|
|
|
psy_do_property("battery", get, POWER_SUPPLY_EXT_PROP_LCD_FLICKER, value);
|
|
wpc_vout_ctrl_lcd_on = value.intval;
|
|
vout_step--;
|
|
if (vout_step < MFC_VOUT_5_5V) {
|
|
if (wpc_vout_ctrl_lcd_on && is_opfreq_ctrl_pad(charger)) {
|
|
pr_info("%s: tx id = 0x%x , set op freq\n", __func__, charger->tx_id);
|
|
mfc_send_command(charger, MFC_SET_OP_FREQ);
|
|
msleep(500);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (wpc_vout_ctrl_lcd_on) {
|
|
psy_do_property("battery", get, POWER_SUPPLY_EXT_PROP_PAD_VOLT_CTRL, value);
|
|
if (value.intval && charger->is_afc_tx) {
|
|
if (vout_step == charger->flicker_vout_threshold) {
|
|
mfc_set_vout(charger, vout_step);
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_vout_mode_work,
|
|
msecs_to_jiffies(charger->flicker_delay));
|
|
return;
|
|
} else if (vout_step < charger->flicker_vout_threshold) {
|
|
pr_info("%s: set TX 5V because LCD ON\n", __func__);
|
|
mfc_set_pad_hv(charger, false);
|
|
charger->pad_ctrl_by_lcd = true;
|
|
}
|
|
}
|
|
}
|
|
mfc_set_vout(charger, vout_step);
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_vout_mode_work, msecs_to_jiffies(250));
|
|
return;
|
|
case WIRELESS_VOUT_4_5V_STEP:
|
|
vout_step--;
|
|
if (vout_step == MFC_VOUT_4_9V)
|
|
vout_step = MFC_VOUT_4_5V;
|
|
if (vout_step >= MFC_VOUT_4_5V) {
|
|
mfc_set_vout(charger, vout_step);
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_vout_mode_work, msecs_to_jiffies(250));
|
|
return;
|
|
}
|
|
break;
|
|
case WIRELESS_VOUT_9V_STEP:
|
|
vout = MFC_VOUT_9V;
|
|
fallthrough;
|
|
case WIRELESS_VOUT_10V_STEP:
|
|
vout_step++;
|
|
if (vout_step <= vout) {
|
|
mfc_set_vout(charger, vout_step);
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_vout_mode_work, msecs_to_jiffies(250));
|
|
return;
|
|
}
|
|
break;
|
|
case WIRELESS_VOUT_CC_CV_VOUT:
|
|
mfc_set_vout(charger, MFC_VOUT_5_5V);
|
|
break;
|
|
case WIRELESS_VOUT_OTG:
|
|
mfc_set_vout(charger, MFC_VOUT_OTG);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#if !defined(CONFIG_SEC_FACTORY)
|
|
if (charger->pdata->vout_status <= MFC_VOUT_5_5V &&
|
|
(charger->is_full_status || charger->sleep_mode || (charger->tx_id == WC_PAD_U1200)))
|
|
mfc_set_pad_hv(charger, false);
|
|
#endif
|
|
pr_info("%s: finish - vout_mode(%s), vout_status(%s)\n",
|
|
__func__, sb_vout_ctr_mode_str(charger->vout_mode), sb_rx_vout_str(charger->pdata->vout_status));
|
|
__pm_relax(charger->wpc_vout_mode_ws);
|
|
}
|
|
|
|
static void mfc_wpc_i2c_error_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_i2c_error_work.work);
|
|
|
|
if (charger->det_state &&
|
|
gpio_get_value(charger->pdata->wpc_det)) {
|
|
union power_supply_propval value;
|
|
|
|
psy_do_property("battery", set,
|
|
POWER_SUPPLY_EXT_PROP_WC_CONTROL, value);
|
|
}
|
|
}
|
|
|
|
static void mfc_wpc_rx_type_det_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_rx_type_det_work.work);
|
|
u8 reg_data, prmc_id;
|
|
union power_supply_propval value;
|
|
|
|
if (!charger->wc_tx_enable) {
|
|
__pm_relax(charger->wpc_rx_det_ws);
|
|
return;
|
|
}
|
|
|
|
mfc_reg_read(charger->client, MFC_STARTUP_EPT_COUNTER, ®_data);
|
|
mfc_reg_read(charger->client, MFC_TX_RXID1_READ_REG, &prmc_id);
|
|
|
|
pr_info("@Tx_Mode %s: prmc_id 0x%x\n", __func__, prmc_id);
|
|
|
|
if (prmc_id == 0x42 && reg_data >= 1) {
|
|
pr_info("@Tx_Mode %s: Samsung Gear Connected\n", __func__);
|
|
charger->wc_rx_type = SS_GEAR;
|
|
} else if (prmc_id == 0x42) {
|
|
pr_info("@Tx_Mode %s: Samsung Phone Connected\n", __func__);
|
|
charger->wc_rx_type = SS_PHONE;
|
|
mfc_set_coil_sw_en(charger, 0);
|
|
} else {
|
|
pr_info("@Tx_Mode %s: Unknown device connected\n", __func__);
|
|
charger->wc_rx_type = OTHER_DEV;
|
|
}
|
|
value.intval = charger->wc_rx_type;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_RX_TYPE, value);
|
|
|
|
__pm_relax(charger->wpc_rx_det_ws);
|
|
}
|
|
|
|
static void mfc_tx_duty_min_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_tx_duty_min_work.work);
|
|
|
|
charger->duty_min = MIN_DUTY_SETTING_20_DATA;
|
|
/* recover min duty */
|
|
mfc_set_min_duty(charger, MIN_DUTY_SETTING_20_DATA);
|
|
pr_info("%s: tx op freq = %dKhz\n", __func__, mfc_get_adc(charger, MFC_ADC_TX_MAX_OP_FRQ));
|
|
|
|
__pm_relax(charger->wpc_tx_duty_min_ws);
|
|
}
|
|
|
|
static void mfc_cs100_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_cs100_work.work);
|
|
|
|
charger->pdata->cs100_status = mfc_send_cs100(charger);
|
|
__pm_relax(charger->wpc_cs100_ws);
|
|
}
|
|
|
|
static void mfc_rx_power_trans_fail_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_rx_power_trans_fail_work.work);
|
|
|
|
pr_info("%s %d\n", __func__, charger->check_rx_power);
|
|
if (charger->check_rx_power) {
|
|
pr_info("%s: set 7.5W\n", __func__);
|
|
mfc_reset_rx_power(charger, TX_RX_POWER_7_5W);
|
|
charger->current_rx_power = TX_RX_POWER_7_5W;
|
|
}
|
|
__pm_relax(charger->wpc_rx_power_trans_fail_ws);
|
|
}
|
|
|
|
static void mfc_wpc_deactivate_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_deactivate_work.work);
|
|
|
|
pr_info("%s\n", __func__);
|
|
mfc_deactivate_work_content(charger);
|
|
__pm_relax(charger->wpc_det_ws);
|
|
}
|
|
|
|
static void mfc_tx_phm_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_tx_phm_work.work);
|
|
|
|
pr_info("@Tx_Mode %s\n", __func__);
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_TOGGLE_PHM_MASK, MFC_CMD_TOGGLE_PHM_MASK);
|
|
|
|
if (charger->tx_device_phm)
|
|
mfc_set_tx_phm(charger, false);
|
|
|
|
charger->skip_phm_work_in_sleep = false;
|
|
__pm_relax(charger->wpc_tx_phm_ws);
|
|
}
|
|
|
|
static void mfc_clear_mpla_thr_recov_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, mpla_thr_recov_work.work);
|
|
|
|
pr_info("@MPP %s: Clear MPLA throtting recovery\n", __func__);
|
|
|
|
charger->is_mpla_thr_recov = false;
|
|
__pm_relax(charger->mpla_thr_recov_ws);
|
|
}
|
|
|
|
static void mfc_set_mpp_cover_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, set_mpp_cover_work.work);
|
|
|
|
charger->mpp_case_det = charger->mpp_case;
|
|
stwlc89_mfc_fod_set_magnet_case_det(charger->fod, charger->mpp_case_det);
|
|
pr_info("@MPP %s: MPP_COVER(%d)\n", __func__, charger->mpp_case_det);
|
|
__pm_relax(charger->set_mpp_cover_ws);
|
|
}
|
|
|
|
static void mfc_set_mpp_cloak_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, set_mpp_cloak_work.work);
|
|
union power_supply_propval value;
|
|
|
|
pr_info("@MPP %s(%d)\n", __func__, charger->mpp_cloak_status);
|
|
if (charger->mpp_cloak_status) {
|
|
__pm_relax(charger->set_mpp_cloak_ws);
|
|
return;
|
|
}
|
|
|
|
value.intval = CLOAK_TURN_OFF_PWR;
|
|
psy_do_property("wireless", set,
|
|
POWER_SUPPLY_EXT_PROP_MPP_CLOAK, value);
|
|
mfc_soft_reset_ask_stop(charger);
|
|
mfc_send_ept_rst(charger);
|
|
__pm_relax(charger->set_mpp_cloak_ws);
|
|
}
|
|
|
|
static void mfc_wpc_check_mpp_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_check_mpp_work.work);
|
|
u8 wpc_det = 0;
|
|
u8 wpc_pdrc = 0;
|
|
|
|
wpc_det = gpio_get_value(charger->pdata->wpc_det);
|
|
wpc_pdrc = gpio_get_value(charger->pdata->wpc_pdrc);
|
|
|
|
pr_info("%s cable_type(%d) rx_op_mode(%d) wpc_det(%d) pdrc(%d)\n",
|
|
__func__, charger->pdata->cable_type, charger->rx_op_mode, wpc_det, wpc_pdrc);
|
|
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_MPP_FAKE) {
|
|
if ((!wpc_det && wpc_pdrc) || (!stwlc_is_mpp_op_mode(charger))) {
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_NONE);
|
|
mfc_epp_enable(charger, 1);
|
|
__pm_relax(charger->check_mpp_ws);
|
|
} else {
|
|
cancel_delayed_work(&charger->wpc_check_mpp_work);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_check_mpp_work, msecs_to_jiffies(2000));
|
|
}
|
|
} else
|
|
__pm_relax(charger->check_mpp_ws);
|
|
}
|
|
|
|
static void mfc_wpc_init_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_init_work.work);
|
|
union power_supply_propval value = {0, };
|
|
|
|
pr_info("%s\n", __func__);
|
|
if (charger->pdata->cable_type != SEC_BATTERY_CABLE_NONE) {
|
|
mfc_set_online(charger, charger->pdata->cable_type);
|
|
if (!charger->pdata->hall_ic_notifier) {
|
|
pr_info("%s: Reset M0\n", __func__);
|
|
/* reset MCU of MFC IC */
|
|
//mfc_set_cmd_l_reg(charger, MFC_CMD_MCU_RESET_MASK, MFC_CMD_MCU_RESET_MASK);
|
|
mfc_soft_reset_ask_stop(charger);
|
|
}
|
|
}
|
|
if (charger->is_otg_on) {
|
|
psy_do_property("wireless", set,
|
|
POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL, value);
|
|
}
|
|
}
|
|
|
|
static bool mfc_wpc_check_phm_exit_state(struct mfc_charger_data *charger)
|
|
{
|
|
int det_state, vrect, i, ret;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
u8 status_l = 0;
|
|
|
|
msleep(250);
|
|
|
|
det_state = gpio_get_value(charger->pdata->wpc_det);
|
|
vrect = mfc_get_adc(charger, MFC_ADC_VRECT);
|
|
ret = mfc_reg_read(charger->client, MFC_STATUS_0_REG, &status_l);
|
|
pr_info("%s: i(%d), det(%d), vrect(%d), status(%d, 0x%x)\n",
|
|
__func__, i, det_state, vrect, ret, status_l);
|
|
|
|
if (det_state)
|
|
return true;
|
|
|
|
if ((status_l & MFC_STAT_L_STAT_VRECT_MASK) &&
|
|
(vrect > VALID_VRECT_LEVEL))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void mfc_wpc_phm_exit_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_phm_exit_work.work);
|
|
int i, det_state, vrect;
|
|
|
|
det_state = gpio_get_value(charger->pdata->wpc_det);
|
|
vrect = mfc_get_adc(charger, MFC_ADC_VRECT);
|
|
pr_info("%s: phm(%d), det(%d), vrect(%d)\n",
|
|
__func__, charger->rx_phm_status, det_state, vrect);
|
|
if (det_state)
|
|
goto clear_phm;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
mfc_set_wpc_en(charger, WPC_EN_CHARGING, false);
|
|
msleep(510);
|
|
mfc_set_wpc_en(charger, WPC_EN_CHARGING, true);
|
|
|
|
if (mfc_wpc_check_phm_exit_state(charger))
|
|
goto clear_phm;
|
|
}
|
|
|
|
if (!mfc_wpc_check_phm_exit_state(charger)) {
|
|
/* reset rx ic and tx pad for phm exit */
|
|
mfc_set_wpc_en(charger, WPC_EN_CHARGING, false);
|
|
msleep(750);
|
|
mfc_deactivate_work_content(charger);
|
|
msleep(750);
|
|
mfc_set_wpc_en(charger, WPC_EN_CHARGING, true);
|
|
}
|
|
|
|
clear_phm:
|
|
charger->rx_phm_status = false;
|
|
gpio_direction_output(charger->pdata->ping_nen, 1);
|
|
mfc_set_psy_wrl(charger, POWER_SUPPLY_EXT_PROP_RX_PHM, false);
|
|
charger->tx_id = WC_PAD_UNKNOWN;
|
|
charger->tx_id_done = false;
|
|
charger->req_tx_id = false;
|
|
charger->tx_id_cnt = 0;
|
|
charger->pdata->cable_type = SEC_BATTERY_CABLE_NONE;
|
|
if ((charger->adt_transfer_status != WIRELESS_AUTH_PASS) &&
|
|
(charger->adt_transfer_status != WIRELESS_AUTH_FAIL))
|
|
charger->adt_transfer_status = WIRELESS_AUTH_WAIT;
|
|
|
|
__pm_relax(charger->wpc_phm_exit_ws);
|
|
}
|
|
|
|
static void mfc_epp_clear_timer_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, epp_clear_timer_work.work);
|
|
|
|
pr_info("%s : bpp -> epp gpio\n", __func__);
|
|
mfc_epp_enable(charger, 1);
|
|
|
|
__pm_relax(charger->epp_clear_ws);
|
|
}
|
|
|
|
static void mfc_set_afc_vout_control(struct mfc_charger_data *charger, int vout_mode)
|
|
{
|
|
if (is_shutdn || charger->pad_ctrl_by_lcd) {
|
|
pr_info("%s: block to set high vout level(vs=%s) because shutdn(%d)\n",
|
|
__func__, sb_rx_vout_str(charger->pdata->vout_status), is_shutdn);
|
|
return;
|
|
}
|
|
|
|
if (charger->is_full_status) {
|
|
pr_info("%s: block to set high vout level(vs=%s) because full status(%d)\n",
|
|
__func__, sb_rx_vout_str(charger->pdata->vout_status),
|
|
charger->is_full_status);
|
|
return;
|
|
}
|
|
|
|
if (!charger->is_afc_tx) {
|
|
pr_info("%s: need to set afc tx before vout control\n", __func__);
|
|
mfc_set_pad_hv(charger, true);
|
|
pr_info("%s: is_afc_tx = %d vout read = %d\n", __func__,
|
|
charger->is_afc_tx, mfc_get_adc(charger, MFC_ADC_VOUT));
|
|
}
|
|
charger->vout_mode = vout_mode;
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
__pm_stay_awake(charger->wpc_vout_mode_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_vout_mode_work, msecs_to_jiffies(250));
|
|
}
|
|
|
|
static void mfc_recover_vout(struct mfc_charger_data *charger)
|
|
{
|
|
int ct = charger->pdata->cable_type;
|
|
|
|
pr_info("%s: cable_type =%d\n", __func__, ct);
|
|
|
|
if (is_hv_wireless_type(ct) && !is_pwr_nego_wireless_type(ct))
|
|
mfc_set_vout(charger, MFC_VOUT_10V);
|
|
|
|
if (ct == SEC_BATTERY_CABLE_WIRELESS_MPP)
|
|
mfc_mpp_inc_int_ctrl(charger, 1);
|
|
}
|
|
|
|
#define RX_PHM_CMD_CNT 5
|
|
static void mfc_rx_phm_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_rx_phm_work.work);
|
|
|
|
union power_supply_propval value = {0, };
|
|
u8 pdet_b = 0, wpc_det = 0;
|
|
u8 count = RX_PHM_CMD_CNT;
|
|
|
|
if (charger->pdata->ping_nen < 0 || charger->pdata->wpc_pdet_b < 0 || charger->mpp_case_det) {
|
|
pr_info("%s: mpp_case_det(%d)\n", __func__, charger->mpp_case_det);
|
|
charger->rx_phm_state = NONE_PHM;
|
|
__pm_relax(charger->wpc_rx_phm_ws);
|
|
return;
|
|
}
|
|
|
|
pr_info("%s: tx_id_cnt(%d) tx_id(0x%x)\n", __func__, charger->tx_id_cnt, charger->tx_id);
|
|
if (charger->rx_phm_state == ENTER_PHM && !is_phm_supported_pad(charger->tx_id)) {
|
|
pr_info("%s: rx_phm unsupported\n", __func__);
|
|
charger->rx_phm_state = NONE_PHM;
|
|
__pm_relax(charger->wpc_rx_phm_ws);
|
|
return;
|
|
}
|
|
|
|
switch (charger->rx_phm_state) {
|
|
case ENTER_PHM:
|
|
pr_info("%s: set ping_nen low, enter phm\n", __func__);
|
|
charger->rx_phm_status = true;
|
|
gpio_direction_output(charger->pdata->ping_nen, 0);
|
|
value.intval = charger->rx_phm_status;
|
|
psy_do_property("wireless", set,
|
|
POWER_SUPPLY_EXT_PROP_RX_PHM, value);
|
|
|
|
while (count-- > 0) {
|
|
mfc_send_command(charger, MFC_PHM_ON);
|
|
msleep(300);
|
|
}
|
|
pdet_b = gpio_get_value(charger->pdata->wpc_pdet_b);
|
|
wpc_det = gpio_get_value(charger->pdata->wpc_det);
|
|
pr_info("%s: check pdet_b = %d, wpc_det = %d for fail case\n", __func__, pdet_b, wpc_det);
|
|
if (pdet_b || wpc_det) {
|
|
pr_info("%s: set ping_nen high, phm fail case\n", __func__);
|
|
charger->rx_phm_status = false;
|
|
gpio_direction_output(charger->pdata->ping_nen, 1);
|
|
value.intval = charger->rx_phm_status;
|
|
psy_do_property("wireless", set,
|
|
POWER_SUPPLY_EXT_PROP_RX_PHM, value);
|
|
msleep(50);
|
|
} else {
|
|
value.intval = 1;
|
|
psy_do_property("battery", set,
|
|
POWER_SUPPLY_EXT_PROP_RX_PHM, value);
|
|
}
|
|
break;
|
|
case EXIT_PHM:
|
|
if (charger->rx_phm_status) {
|
|
pr_info("%s: set ping_nen high, exit phm\n", __func__);
|
|
__pm_stay_awake(charger->wpc_phm_exit_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_phm_exit_work, 0);
|
|
} else
|
|
pr_info("%s: skip exit phm\n", __func__);
|
|
break;
|
|
case END_PHM:
|
|
if (charger->rx_phm_status) {
|
|
pr_info("%s: set ping_nen high, end phm with detach\n", __func__);
|
|
charger->rx_phm_status = false;
|
|
gpio_direction_output(charger->pdata->ping_nen, 1);
|
|
value.intval = charger->rx_phm_status;
|
|
psy_do_property("wireless", set,
|
|
POWER_SUPPLY_EXT_PROP_RX_PHM, value);
|
|
mfc_deactivate_work_content(charger);
|
|
} else
|
|
pr_info("%s: skip end phm\n", __func__);
|
|
break;
|
|
case FAILED_PHM:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
__pm_relax(charger->wpc_rx_phm_ws);
|
|
}
|
|
|
|
static void print_fod_log(struct mfc_charger_data *charger, union mfc_fod_state *state)
|
|
{
|
|
u8 fod_data[MFC_NUM_FOD_REG];
|
|
u16 fod_reg;
|
|
char str[512] = { 0, };
|
|
int i, ret = 0, str_size = 512;
|
|
|
|
fod_reg = MFC_WPC_FOD_0A_REG;
|
|
snprintf(str, str_size, "[0x%llX][%s]", state->value, sb_wrl_op_mode_str(state->fake_op_mode));
|
|
str_size = sizeof(str) - strlen(str);
|
|
|
|
for (i = 0; i < MFC_NUM_FOD_REG; i++) {
|
|
ret = mfc_reg_read(charger->client, fod_reg + i, &fod_data[i]);
|
|
if (ret < 0) {
|
|
pr_info("%s: %s failed to read fod reg ret(%d)\n", __func__, str, ret);
|
|
return;
|
|
}
|
|
|
|
snprintf(str + strlen(str), str_size, " 0x%02X:0x%02X", fod_reg + i, fod_data[i]);
|
|
str_size = sizeof(str) - strlen(str);
|
|
}
|
|
|
|
pr_info("%s: %s\n", __func__, str);
|
|
}
|
|
|
|
static int stwlc89_set_fod(struct device *dev, union mfc_fod_state *state, fod_data_t *data)
|
|
{
|
|
struct mfc_charger_data *charger = dev_get_drvdata(dev);
|
|
int i, ret = 0;
|
|
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_NONE)
|
|
return 0;
|
|
if (data == NULL)
|
|
return 0;
|
|
|
|
for (i = 0; i < MFC_NUM_FOD_REG; i++) {
|
|
ret = mfc_reg_write(charger->client, MFC_WPC_FOD_0A_REG + i, data[i]);
|
|
if (ret < 0)
|
|
goto err_write_reg;
|
|
}
|
|
|
|
print_fod_log(charger, state);
|
|
return 0;
|
|
|
|
err_write_reg:
|
|
pr_err("%s: failed to write fod reg(ret = %d)\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
static void stwlc89_print_fod(struct mfc_charger_data *charger)
|
|
{
|
|
union mfc_fod_state now_state = { 0, };
|
|
|
|
stwlc89_mfc_fod_get_state(charger->fod, &now_state);
|
|
charger->now_fod_state.value = now_state.value;
|
|
|
|
print_fod_log(charger, &now_state);
|
|
}
|
|
|
|
static int stwlc89_set_cmfet(struct device *dev, union mfc_cmfet_state *state, bool cma, bool cmb)
|
|
{
|
|
struct mfc_charger_data *charger = dev_get_drvdata(dev);
|
|
int ret = 0;
|
|
u8 data;
|
|
|
|
if (!charger->det_state) {
|
|
pr_info("%s: wireless is disconnected, state(%lld)\n", __func__, state->value);
|
|
return 0;
|
|
}
|
|
|
|
if (stwlc89_get_op_mode(charger) == WPC_OP_MODE_MPP ||
|
|
charger->rx_op_mode == MFC_RX_MODE_WPC_EPP_NEGO) {
|
|
pr_info("%s: do not ctrl cma/cmb when mpp. rx_op_mode(%d)\n", __func__, charger->rx_op_mode);
|
|
return 0;
|
|
}
|
|
|
|
data = ((cma) ? 0xC0 : 0x00) | ((cmb) ? 0x30 : 0x00);
|
|
ret = mfc_reg_write(charger->client, MFC_RX_COMM_MOD_FET_REG, data);
|
|
mfc_reg_read(charger->client, MFC_RX_COMM_MOD_FET_REG, &data);
|
|
|
|
pr_info("%s: state(0x%llX), ret(%d), data(0x%X)\n", __func__, state->value, ret, data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void stwlc89_print_cmfet(struct mfc_charger_data *charger)
|
|
{
|
|
union mfc_cmfet_state state = { 0, };
|
|
int ret = 0;
|
|
u8 data = 0;
|
|
|
|
stwlc89_mfc_cmfet_get_state(charger->cmfet, &state);
|
|
charger->now_cmfet_state.value = state.value;
|
|
|
|
ret = mfc_reg_read(charger->client, MFC_RX_COMM_MOD_FET_REG, &data);
|
|
pr_info("%s: [0x%llX] ret = %d, data = 0x%x\n", __func__, state.value, ret, data);
|
|
}
|
|
|
|
static bool stwlc89_set_force_vout(struct mfc_charger_data *charger, int vout)
|
|
{
|
|
bool ret = true;
|
|
|
|
switch (vout) {
|
|
case WIRELESS_VOUT_FORCE_13V:
|
|
mfc_set_force_vout(charger, MFC_VOUT_13V);
|
|
break;
|
|
case WIRELESS_VOUT_FORCE_10V:
|
|
mfc_set_force_vout(charger, MFC_VOUT_10V);
|
|
break;
|
|
case WIRELESS_VOUT_FORCE_9V:
|
|
mfc_set_force_vout(charger, MFC_VOUT_9V);
|
|
break;
|
|
case WIRELESS_VOUT_FORCE_5V:
|
|
mfc_set_force_vout(charger, MFC_VOUT_5V);
|
|
break;
|
|
case WIRELESS_VOUT_FORCE_4_7V:
|
|
mfc_set_force_vout(charger, MFC_VOUT_4_7V);
|
|
break;
|
|
case WIRELESS_VOUT_FORCE_4_8V:
|
|
mfc_set_force_vout(charger, MFC_VOUT_4_8V);
|
|
break;
|
|
case WIRELESS_VOUT_FORCE_4_9V:
|
|
mfc_set_force_vout(charger, MFC_VOUT_4_9V);
|
|
break;
|
|
default:
|
|
ret = false;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if defined(CONFIG_UPDATE_BATTERY_DATA)
|
|
static int mfc_chg_parse_dt(struct device *dev, mfc_charger_platform_data_t *pdata);
|
|
#endif
|
|
static int stwlc89_chg_set_property(struct power_supply *psy,
|
|
enum power_supply_property psp,
|
|
const union power_supply_propval *val)
|
|
{
|
|
struct mfc_charger_data *charger = power_supply_get_drvdata(psy);
|
|
enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp;
|
|
int i = 0;
|
|
u8 tmp = 0;
|
|
|
|
switch ((int)psp) {
|
|
case POWER_SUPPLY_PROP_STATUS:
|
|
if (val->intval == POWER_SUPPLY_STATUS_FULL) {
|
|
pr_info("%s: full status\n", __func__);
|
|
charger->is_full_status = 1;
|
|
if (!is_wireless_fake_type(charger->pdata->cable_type)) {
|
|
stwlc89_mfc_fod_set_bat_state(charger->fod, MFC_FOD_BAT_STATE_FULL);
|
|
__pm_stay_awake(charger->wpc_cs100_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_cs100_work, msecs_to_jiffies(0));
|
|
}
|
|
} else if (val->intval == POWER_SUPPLY_STATUS_NOT_CHARGING) {
|
|
mfc_mis_align(charger);
|
|
} else if (val->intval == POWER_SUPPLY_STATUS_CHARGING) {
|
|
charger->is_full_status = 0;
|
|
mfc_set_pad_hv(charger, true);
|
|
mfc_recover_vout_by_pad(charger);
|
|
|
|
pr_info("%s: CC status. tx_id(0x%x)\n", __func__, charger->tx_id);
|
|
} else if (val->intval == POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE) {
|
|
pr_info("%s: CV status. tx_id(0x%x)\n", __func__, charger->tx_id);
|
|
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_MPP)
|
|
mfc_reg_write(charger->client, MFC_MPP_DC_CURRENT_MOD_DEPTH_REG, 0x64);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_init_work, 0);
|
|
break;
|
|
case POWER_SUPPLY_PROP_HEALTH:
|
|
if (val->intval == POWER_SUPPLY_HEALTH_OVERHEAT ||
|
|
val->intval == POWER_SUPPLY_EXT_HEALTH_OVERHEATLIMIT ||
|
|
val->intval == POWER_SUPPLY_HEALTH_COLD)
|
|
mfc_send_eop(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_PROP_ONLINE:
|
|
pr_info("%s: ept-internal fault\n", __func__);
|
|
mfc_reg_write(charger->client, MFC_EPT_REG, MFC_WPC_EPT_INT_FAULT);
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_SEND_EOP_MASK, MFC_CMD_SEND_EOP_MASK);
|
|
break;
|
|
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
|
if (val->intval) {
|
|
charger->is_mst_on = MST_MODE_2;
|
|
pr_info("%s: set MST mode 2\n", __func__);
|
|
} else {
|
|
#if defined(CONFIG_MST_V2)
|
|
// it will send MST driver a message.
|
|
mfc_send_mst_cmd(MST_MODE_OFF, charger, 0, 0);
|
|
#endif
|
|
pr_info("%s: set MST mode off\n", __func__);
|
|
charger->is_mst_on = MST_MODE_0;
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_MANUFACTURER:
|
|
charger->pdata->otp_firmware_result = val->intval;
|
|
pr_info("%s: otp_firmware result initialize (%d)\n", __func__,
|
|
charger->pdata->otp_firmware_result);
|
|
break;
|
|
case POWER_SUPPLY_PROP_ENERGY_NOW:
|
|
if (!charger->rx_phm_status) {
|
|
if (charger->wc_tx_enable) {
|
|
pr_info("@Tx_Mode %s: FW Ver(0x%x) TX_VOUT(%dmV) TX_IOUT(%dmA), PHM(%d), %s connected\n", __func__,
|
|
charger->otp_firmware_ver,
|
|
mfc_get_adc(charger, MFC_ADC_TX_VOUT),
|
|
mfc_get_adc(charger, MFC_ADC_TX_IOUT),
|
|
charger->tx_device_phm,
|
|
sb_rx_type_str(charger->wc_rx_type));
|
|
pr_info("@Tx_Mode %s: PING_FRQ(%dKHz) OP_FRQ(%dKHz) TX_MIN_FRQ(%dKHz) TX_MAX_FRQ(%dKHz)\n",
|
|
__func__,
|
|
mfc_get_adc(charger, MFC_ADC_PING_FRQ),
|
|
mfc_get_adc(charger, MFC_ADC_OP_FRQ),
|
|
mfc_get_adc(charger, MFC_ADC_TX_MIN_OP_FRQ),
|
|
mfc_get_adc(charger, MFC_ADC_TX_MAX_OP_FRQ));
|
|
} else if (charger->pdata->cable_type != SEC_BATTERY_CABLE_NONE) {
|
|
charger->die_temp = mfc_get_adc(charger, MFC_ADC_DIE_TEMP);
|
|
pr_info("%s: FW Ver(%x) RX_VOUT(%dmV) RX_VRECT(%dmV) RX_IOUT(%dmA)\n", __func__,
|
|
charger->otp_firmware_ver,
|
|
mfc_get_adc(charger, MFC_ADC_VOUT),
|
|
mfc_get_adc(charger, MFC_ADC_VRECT),
|
|
mfc_get_adc(charger, MFC_ADC_RX_IOUT));
|
|
pr_info("%s: OP_FRQ(%dKHz) TX ID(0x%x) IC Rev(0x%x) Die temp(%d'C)\n", __func__,
|
|
mfc_get_adc(charger, MFC_ADC_OP_FRQ),
|
|
charger->tx_id,
|
|
charger->pdata->wc_ic_rev,
|
|
charger->die_temp);
|
|
|
|
stwlc89_print_fod(charger);
|
|
stwlc89_print_cmfet(charger);
|
|
}
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_CAPACITY:
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_EMPTY:
|
|
{
|
|
int vout = 0, vrect = 0;
|
|
u8 is_vout_on = 0;
|
|
bool error = false;
|
|
|
|
if (mfc_reg_read(charger->client, MFC_STATUS_0_REG, &is_vout_on) <0)
|
|
error = true;
|
|
is_vout_on = is_vout_on >> 7;
|
|
vout = mfc_get_adc(charger, MFC_ADC_VOUT);
|
|
vrect = mfc_get_adc(charger, MFC_ADC_VRECT);
|
|
pr_info("%s: SET MFC LDO (%s), Current VOUT STAT (%d), RX_VOUT = %dmV, RX_VRECT = %dmV, error(%d)\n",
|
|
__func__, (val->intval == MFC_LDO_ON ? "ON" : "OFF"), is_vout_on, vout, vrect, error);
|
|
if ((val->intval == MFC_LDO_ON) && (!is_vout_on || error)) { /* LDO ON */
|
|
pr_info("%s: MFC LDO ON toggle ------------ cable_work\n", __func__);
|
|
for (i = 0; i < 2; i++) {
|
|
mfc_reg_read(charger->client, MFC_STATUS_0_REG, &is_vout_on);
|
|
is_vout_on = is_vout_on >> 7;
|
|
if (!is_vout_on)
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_TOGGLE_LDO_MASK, MFC_CMD_TOGGLE_LDO_MASK);
|
|
else
|
|
break;
|
|
msleep(500);
|
|
mfc_reg_read(charger->client, MFC_STATUS_0_REG, &is_vout_on);
|
|
is_vout_on = is_vout_on >> 7;
|
|
if (is_vout_on) {
|
|
pr_info("%s: cnt = %d, LDO is ON -> MFC LDO STAT(%d)\n",
|
|
__func__, i, is_vout_on);
|
|
break;
|
|
}
|
|
msleep(1000);
|
|
vout = mfc_get_adc(charger, MFC_ADC_VOUT);
|
|
vrect = mfc_get_adc(charger, MFC_ADC_VRECT);
|
|
pr_info("%s: cnt = %d, LDO Should ON -> MFC LDO STAT(%d), RX_VOUT = %dmV, RX_VRECT = %dmV\n",
|
|
__func__, i, is_vout_on, vout, vrect);
|
|
}
|
|
charger->wc_ldo_status = MFC_LDO_ON;
|
|
mfc_recover_vout(charger);
|
|
} else if ((val->intval == MFC_LDO_OFF) && (is_vout_on || error)) { /* LDO OFF */
|
|
mfc_set_vout(charger, MFC_VOUT_5V);
|
|
pr_info("%s: MFC LDO OFF toggle ------------ cable_work\n", __func__);
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_TOGGLE_LDO_MASK, MFC_CMD_TOGGLE_LDO_MASK);
|
|
msleep(100);
|
|
mfc_reg_read(charger->client, MFC_STATUS_0_REG, &is_vout_on);
|
|
is_vout_on = is_vout_on >> 7;
|
|
vout = mfc_get_adc(charger, MFC_ADC_VOUT);
|
|
vrect = mfc_get_adc(charger, MFC_ADC_VRECT);
|
|
pr_info("%s: LDO Should OFF -> MFC LDO STAT(%d), RX_VOUT = %dmV, RX_VRECT = %dmV\n",
|
|
__func__, is_vout_on, vout, vrect);
|
|
charger->wc_ldo_status = MFC_LDO_OFF;
|
|
}
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
|
charger->input_current = val->intval;
|
|
pr_info("%s: input_current: %d\n", __func__, charger->input_current);
|
|
stwlc89_set_recent_ilim(charger, charger->input_current);
|
|
break;
|
|
case POWER_SUPPLY_PROP_SCOPE:
|
|
return -ENODATA;
|
|
case POWER_SUPPLY_EXT_PROP_MIN ... POWER_SUPPLY_EXT_PROP_MAX:
|
|
switch (ext_psp) {
|
|
case POWER_SUPPLY_EXT_PROP_WC_CONTROL:
|
|
if (val->intval == 0) {
|
|
tmp = 0x01;
|
|
mfc_send_packet(charger, MFC_HEADER_AFC_CONF,
|
|
0x20, &tmp, 1);
|
|
pr_info("%s: send command after wc control\n", __func__);
|
|
msleep(150);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WC_EPT_UNKNOWN:
|
|
if (val->intval == EPT_UNKNOWN)
|
|
mfc_send_ept_unknown(charger);
|
|
else if (val->intval == EPT_RESTART)
|
|
mfc_send_ept_rst(charger);
|
|
else if (val->intval == CEP_TIMEOUT)
|
|
mfc_soft_reset_ask_stop(charger);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_SWITCH:
|
|
/*
|
|
* It is a RX device , send a packet to TX device to stop power sharing.
|
|
* TX device will have MFC_INTA_H_TRX_DATA_RECEIVED_MASK irq
|
|
*/
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_TX) {
|
|
if (val->intval) {
|
|
pr_info("%s: It is a RX device , send a packet to TX device to stop power sharing\n",
|
|
__func__);
|
|
mfc_send_command(charger, MFC_DISABLE_TX);
|
|
}
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_SEND_FSK:
|
|
/* send fsk packet for rx device aicl reset */
|
|
if (val->intval && (charger->wc_rx_type != SS_GEAR)) {
|
|
pr_info("@Tx_mode %s: Send FSK packet for Rx device aicl reset\n", __func__);
|
|
mfc_send_fsk(charger, WPC_TX_COM_WPS, WPS_AICL_RESET);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ENABLE:
|
|
/* on/off tx power */
|
|
mfc_set_tx_power(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_CONNECTED:
|
|
charger->wc_rx_connected = val->intval;
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_rx_connection_work, 0);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS: /* it has only PASS and FAIL */
|
|
stwlc89_adt_transfer_result(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_DATA: /* data from auth service will be sent */
|
|
if (charger->pdata->cable_type != SEC_BATTERY_CABLE_NONE) {
|
|
u8 *p_data;
|
|
|
|
p_data = (u8 *)val->strval;
|
|
|
|
for (i = 0; i < adt_readSize; i++)
|
|
pr_info("%s %s: p_data[%d] = %x\n", WC_AUTH_MSG, __func__, i, p_data[i]);
|
|
mfc_auth_adt_send(charger, p_data, adt_readSize);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_SIZE:
|
|
if (charger->pdata->cable_type != SEC_BATTERY_CABLE_NONE)
|
|
adt_readSize = val->intval;
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_TYPE:
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_VOUT:
|
|
mfc_set_tx_vout(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_IOUT:
|
|
mfc_set_tx_iout(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_TIMER_ON:
|
|
pr_info("%s %s: TX receiver detecting timer enable(%d)\n", WC_AUTH_MSG, __func__, val->intval);
|
|
if (charger->wc_tx_enable) {
|
|
if (val->intval) {
|
|
pr_info("%s %s: enable TX OFF timer (90sec)", WC_AUTH_MSG, __func__);
|
|
mfc_reg_update(charger->client, MFC_INT_A_ENABLE_1_REG, (0x1 << 5), (0x1 << 5));
|
|
} else {
|
|
pr_info("%s %s: disable TX OFF timer (90sec)", WC_AUTH_MSG, __func__);
|
|
mfc_reg_update(charger->client, MFC_INT_A_ENABLE_1_REG, 0x0, (0x1 << 5));
|
|
}
|
|
} else {
|
|
pr_info("%s %s: Don't need to set TX 90sec timer, on TX OFF state\n",
|
|
WC_AUTH_MSG, __func__);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_MIN_DUTY:
|
|
if (delayed_work_pending(&charger->wpc_tx_duty_min_work)) {
|
|
__pm_relax(charger->wpc_tx_duty_min_ws);
|
|
cancel_delayed_work(&charger->wpc_tx_duty_min_work);
|
|
}
|
|
charger->duty_min = val->intval;
|
|
mfc_set_min_duty(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_CALL_EVENT:
|
|
if (val->intval & BATT_EXT_EVENT_CALL) {
|
|
charger->device_event |= BATT_EXT_EVENT_CALL;
|
|
|
|
#if defined(CONFIG_WIRELESS_RX_PHM_CTRL)
|
|
charger->rx_phm_state = ENTER_PHM;
|
|
__pm_stay_awake(charger->wpc_rx_phm_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_rx_phm_work, 0);
|
|
#else
|
|
/* call in is after wireless connection */
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK ||
|
|
charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_PACK ||
|
|
charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_TX) {
|
|
union power_supply_propval value2;
|
|
|
|
pr_info("%s %s: enter PHM\n", WC_TX_MSG, __func__);
|
|
/* notify "wireless" PHM status */
|
|
value2.intval = 1;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_CALL_EVENT, value2);
|
|
mfc_send_command(charger, MFC_PHM_ON);
|
|
msleep(250);
|
|
mfc_send_command(charger, MFC_PHM_ON);
|
|
}
|
|
#endif
|
|
} else if (val->intval == BATT_EXT_EVENT_NONE) {
|
|
if (charger->device_event & BATT_EXT_EVENT_CALL) {
|
|
charger->device_event &= ~BATT_EXT_EVENT_CALL;
|
|
#if defined(CONFIG_WIRELESS_RX_PHM_CTRL)
|
|
charger->rx_phm_state = EXIT_PHM;
|
|
__pm_stay_awake(charger->wpc_rx_phm_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_rx_phm_work, 0);
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_RX_PHM:
|
|
charger->rx_phm_state = val->intval;
|
|
__pm_stay_awake(charger->wpc_rx_phm_ws);
|
|
if (charger->tx_id_done)
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_rx_phm_work, 0);
|
|
else
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_rx_phm_work, msecs_to_jiffies(20000));
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_SLEEP_MODE:
|
|
charger->sleep_mode = val->intval;
|
|
if (stwlc89_get_op_mode(charger) == WPC_OP_MODE_MPP) {
|
|
pr_info("@MPP %s: sleep_mode(%d)\n", __func__, charger->sleep_mode);
|
|
mfc_set_mpp_sleep_mode(charger, charger->sleep_mode);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_PAD_VOLT_CTRL:
|
|
if (delayed_work_pending(&charger->wpc_vout_mode_work)) {
|
|
pr_info("%s: Already vout change. skip pad control\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
if (!volt_ctrl_pad(charger->tx_id))
|
|
break;
|
|
|
|
if (val->intval && charger->is_afc_tx) {
|
|
pr_info("%s: set TX 5V because LCD ON\n", __func__);
|
|
mfc_set_pad_hv(charger, false);
|
|
charger->pad_ctrl_by_lcd = true;
|
|
} else if (!val->intval && !charger->is_afc_tx && charger->pad_ctrl_by_lcd) {
|
|
mfc_set_pad_hv(charger, true);
|
|
charger->pad_ctrl_by_lcd = false;
|
|
pr_info("%s: need to set afc tx because LCD OFF\n", __func__);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_VOUT:
|
|
for (i = 0; i < charger->pdata->len_wc20_list; i++)
|
|
charger->pdata->wireless20_vout_list[i] = val->intval;
|
|
|
|
pr_info("%s: vout(%d) len(%d)\n", __func__, val->intval, i - 1);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_1ST_DONE:
|
|
/* 1st chg done ~ 2nd chg done : CMA+CMB (samsung pad only) */
|
|
stwlc89_mfc_cmfet_set_full(charger->cmfet, true);
|
|
pr_info("%s: 1st wireless charging done! CMA CMB\n", __func__);
|
|
mfc_set_vout_ctrl_1st_full(charger);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_2ND_DONE:
|
|
stwlc89_mfc_cmfet_set_chg_done(charger->cmfet, true);
|
|
pr_info("%s: 2nd wireless charging done! CMA only\n", __func__);
|
|
mfc_set_vout_ctrl_2nd_full(charger);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WPC_EN:
|
|
mfc_set_wpc_en(charger, val->strval[0], val->strval[1]);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WPC_EN_MST:
|
|
if (val->intval)
|
|
mfc_set_wpc_en(charger, WPC_EN_MST, true);
|
|
else
|
|
mfc_set_wpc_en(charger, WPC_EN_MST, false);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_CHARGE_OTG_CONTROL:
|
|
if (val->intval) {
|
|
charger->is_otg_on = true;
|
|
pr_info("%s: mfc otg on, rx_op_mode is %d\n", __func__, charger->rx_op_mode);
|
|
if (mpp_mode(charger->rx_op_mode)) {
|
|
pr_info("%s: mfc otg on, MPP mode change to BPP\n", __func__);
|
|
mfc_send_ept_rst(charger);
|
|
}
|
|
} else {
|
|
charger->is_otg_on = false;
|
|
pr_info("%s: mfc otg off, rx_op_mode is %d before OTG on\n",
|
|
__func__, charger->rx_op_mode);
|
|
if (mpp_mode(charger->rx_op_mode) || bpp_mode(charger->rx_op_mode))
|
|
mfc_soft_reset_ask_stop(charger);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_CHARGE_POWERED_OTG_CONTROL:
|
|
{
|
|
unsigned int work_state;
|
|
|
|
mutex_lock(&charger->fw_lock);
|
|
/* check delayed work state */
|
|
work_state = work_busy(&charger->wpc_fw_update_work.work);
|
|
pr_info("%s: check fw_work state(0x%x)\n", __func__, work_state);
|
|
if (work_state & (WORK_BUSY_PENDING | WORK_BUSY_RUNNING)) {
|
|
pr_info("%s: skip update_fw!!\n", __func__);
|
|
} else {
|
|
charger->fw_cmd = val->intval;
|
|
__pm_stay_awake(charger->wpc_update_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_fw_update_work, 0);
|
|
pr_info("%s: rx result = %d, tx result = %d\n", __func__,
|
|
charger->pdata->otp_firmware_result,
|
|
charger->pdata->tx_firmware_result);
|
|
}
|
|
mutex_unlock(&charger->fw_lock);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_INPUT_VOLTAGE_REGULATION:
|
|
if (val->intval == WIRELESS_VOUT_NORMAL_VOLTAGE) {
|
|
pr_info("%s: Wireless Vout forced set to 5V. + PAD CMD\n", __func__);
|
|
for (i = 0; i < CMD_CNT; i++) {
|
|
mfc_send_command(charger, MFC_AFC_CONF_5V);
|
|
msleep(250);
|
|
}
|
|
} else if (val->intval == WIRELESS_VOUT_HIGH_VOLTAGE) {
|
|
pr_info("%s: Wireless Vout forced set to 10V. + PAD CMD\n", __func__);
|
|
for (i = 0; i < CMD_CNT; i++) {
|
|
mfc_send_command(charger, MFC_AFC_CONF_10V);
|
|
msleep(250);
|
|
}
|
|
} else if (val->intval == WIRELESS_VOUT_CC_CV_VOUT) {
|
|
charger->vout_mode = val->intval;
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
__pm_stay_awake(charger->wpc_vout_mode_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_vout_mode_work, 0);
|
|
} else if (val->intval == WIRELESS_VOUT_5V ||
|
|
val->intval == WIRELESS_VOUT_5V_STEP ||
|
|
val->intval == WIRELESS_VOUT_5_5V_STEP ||
|
|
val->intval == WIRELESS_VOUT_OTG) {
|
|
int def_delay = 0;
|
|
|
|
charger->vout_mode = val->intval;
|
|
if (is_hv_wireless_type(charger->pdata->cable_type) &&
|
|
val->intval != WIRELESS_VOUT_OTG)
|
|
def_delay = 250;
|
|
if (def_delay && val->intval == WIRELESS_VOUT_5_5V_STEP)
|
|
def_delay = 2000;
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
__pm_stay_awake(charger->wpc_vout_mode_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_vout_mode_work, msecs_to_jiffies(def_delay));
|
|
} else if (val->intval == WIRELESS_VOUT_9V ||
|
|
val->intval == WIRELESS_VOUT_10V ||
|
|
val->intval == WIRELESS_VOUT_11V ||
|
|
val->intval == WIRELESS_VOUT_12V ||
|
|
val->intval == WIRELESS_VOUT_12_5V ||
|
|
val->intval == WIRELESS_VOUT_9V_STEP ||
|
|
val->intval == WIRELESS_VOUT_10V_STEP) {
|
|
pr_info("%s: vout (%d)\n", __func__, val->intval);
|
|
mfc_set_afc_vout_control(charger, val->intval);
|
|
} else {
|
|
if (!stwlc89_set_force_vout(charger, val->intval))
|
|
pr_info("%s: Unknown Command(%d)\n", __func__, val->intval);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_CONTROL:
|
|
if (val->intval == WIRELESS_PAD_FAN_OFF) {
|
|
pr_info("%s: fan off\n", __func__);
|
|
mfc_fan_control(charger, 0);
|
|
} else if (val->intval == WIRELESS_PAD_FAN_ON) {
|
|
pr_info("%s: fan on\n", __func__);
|
|
mfc_fan_control(charger, 1);
|
|
} else if (val->intval == WIRELESS_PAD_LED_OFF) {
|
|
pr_info("%s: led off\n", __func__);
|
|
mfc_led_control(charger, MFC_LED_CONTROL_OFF);
|
|
} else if (val->intval == WIRELESS_PAD_LED_ON) {
|
|
pr_info("%s: led on\n", __func__);
|
|
mfc_led_control(charger, MFC_LED_CONTROL_ON);
|
|
} else if (val->intval == WIRELESS_PAD_LED_DIMMING) {
|
|
pr_info("%s: led dimming\n", __func__);
|
|
mfc_led_control(charger, MFC_LED_CONTROL_DIMMING);
|
|
} else if (val->intval == WIRELESS_VRECT_ADJ_ON) {
|
|
pr_info("%s: vrect adjust to have big headroom(default value)\n", __func__);
|
|
mfc_set_vrect_adjust(charger, MFC_HEADROOM_1);
|
|
} else if (val->intval == WIRELESS_VRECT_ADJ_OFF) {
|
|
pr_info("%s: vrect adjust to have small headroom\n", __func__);
|
|
mfc_set_vrect_adjust(charger, MFC_HEADROOM_0);
|
|
} else if (val->intval == WIRELESS_VRECT_ADJ_ROOM_0) {
|
|
pr_info("%s: vrect adjust to have headroom 0(0mV)\n", __func__);
|
|
mfc_set_vrect_adjust(charger, MFC_HEADROOM_0);
|
|
} else if (val->intval == WIRELESS_VRECT_ADJ_ROOM_1) {
|
|
pr_info("%s: vrect adjust to have headroom 1(277mV)\n", __func__);
|
|
mfc_set_vrect_adjust(charger, MFC_HEADROOM_1);
|
|
} else if (val->intval == WIRELESS_VRECT_ADJ_ROOM_2) {
|
|
pr_info("%s: vrect adjust to have headroom 2(497mV)\n", __func__);
|
|
mfc_set_vrect_adjust(charger, MFC_HEADROOM_2);
|
|
} else if (val->intval == WIRELESS_VRECT_ADJ_ROOM_3) {
|
|
pr_info("%s: vrect adjust to have headroom 3(650mV)\n", __func__);
|
|
mfc_set_vrect_adjust(charger, MFC_HEADROOM_3);
|
|
} else if (val->intval == WIRELESS_VRECT_ADJ_ROOM_4) {
|
|
pr_info("%s: vrect adjust to have headroom 4(30mV)\n", __func__);
|
|
mfc_set_vrect_adjust(charger, MFC_HEADROOM_4);
|
|
} else if (val->intval == WIRELESS_VRECT_ADJ_ROOM_5) {
|
|
pr_info("%s: vrect adjust to have headroom 5(82mV)\n", __func__);
|
|
mfc_set_vrect_adjust(charger, MFC_HEADROOM_5);
|
|
} else if (val->intval == WIRELESS_CLAMP_ENABLE) {
|
|
pr_info("%s: enable clamp1, clamp2 for WPC modulation\n", __func__);
|
|
} else if (val->intval == WIRELESS_SLEEP_MODE_ENABLE) {
|
|
if (is_sleep_mode_active(charger->tx_id)) {
|
|
pr_info("%s: sleep_mode enable\n", __func__);
|
|
msleep(500);
|
|
pr_info("%s: led dimming\n", __func__);
|
|
mfc_led_control(charger, MFC_LED_CONTROL_DIMMING);
|
|
msleep(500);
|
|
pr_info("%s: fan off\n", __func__);
|
|
mfc_fan_control(charger, 0);
|
|
} else {
|
|
pr_info("%s: sleep_mode inactive\n", __func__);
|
|
}
|
|
} else if (val->intval == WIRELESS_SLEEP_MODE_DISABLE) {
|
|
if (is_sleep_mode_active(charger->tx_id)) {
|
|
pr_info("%s: sleep_mode disable\n", __func__);
|
|
msleep(500);
|
|
pr_info("%s: led on\n", __func__);
|
|
mfc_led_control(charger, MFC_LED_CONTROL_ON);
|
|
msleep(500);
|
|
pr_info("%s: fan on\n", __func__);
|
|
mfc_fan_control(charger, 1);
|
|
} else {
|
|
pr_info("%s: sleep_mode inactive\n", __func__);
|
|
}
|
|
} else {
|
|
pr_info("%s: Unknown Command(%d)\n", __func__, val->intval);
|
|
}
|
|
break;
|
|
#if defined(CONFIG_UPDATE_BATTERY_DATA)
|
|
case POWER_SUPPLY_EXT_PROP_POWER_DESIGN:
|
|
mfc_chg_parse_dt(charger->dev, charger->pdata);
|
|
break;
|
|
#endif
|
|
case POWER_SUPPLY_EXT_PROP_FILTER_CFG:
|
|
charger->led_cover = val->intval;
|
|
pr_info("%s: LED_COVER(%d)\n", __func__, charger->led_cover);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_TX_PWR_BUDG:
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_MPP_COVER:
|
|
{
|
|
int delay = 5000;
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
delay = 0;
|
|
#endif
|
|
if (charger->init_cover) {
|
|
delay = 0;
|
|
pr_info("@MPP %s: update delay(%d) as init(%d)\n",
|
|
__func__, delay, charger->init_cover);
|
|
charger->init_cover = false;
|
|
if (is_wireless_all_type(charger->pdata->cable_type))
|
|
mfc_soft_reset_ask_stop(charger);
|
|
}
|
|
|
|
if (delayed_work_pending(&charger->set_mpp_cover_work)) {
|
|
__pm_relax(charger->set_mpp_cover_ws);
|
|
cancel_delayed_work(&charger->set_mpp_cover_work);
|
|
}
|
|
pr_info("@MPP %s: MPP_COVER(%d->%d) delay(%d)\n",
|
|
__func__, charger->mpp_case, val->intval, delay);
|
|
charger->mpp_case = val->intval;
|
|
__pm_stay_awake(charger->set_mpp_cover_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->set_mpp_cover_work, msecs_to_jiffies(delay));
|
|
//mfc_mpp_enable(charger, val->intval);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_MPP_CLOAK:
|
|
pr_info("@MPP %s: MPP_CLOAK(%d)\n", __func__, val->intval);
|
|
if (val->intval == CLOAK_PTX_INITIATED)
|
|
mfc_mpp_enter_cloak(charger, MFC_TRX_MPP_CLOAK_PTX_INITIATED);
|
|
else if (val->intval == CLOAK_END_OF_CHARGE)
|
|
mfc_mpp_enter_cloak(charger, MFC_TRX_MPP_CLOAK_END_OF_CHARGE);
|
|
else if (val->intval == CLOAK_GENERIC)
|
|
mfc_mpp_enter_cloak(charger, MFC_TRX_MPP_CLOAK_GENERIC);
|
|
else if (val->intval == CLOAK_EXIT_CMD)
|
|
mfc_mpp_exit_cloak(charger);
|
|
else
|
|
mfc_mpp_exit_cloak_turn_off_power(charger);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_MPP_ICL_CTRL:
|
|
charger->thermal_ctrl = val->intval;
|
|
pr_info("@MPP %s: MPP_THERMAL_CTRL(%d)\n", __func__, charger->thermal_ctrl);
|
|
mfc_mpp_thermal_ctrl(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_MPP_INC_INT_CTRL:
|
|
pr_info("@MPP %s: %s INCREASE INT\n", __func__, val->intval ? "ENABLE" : "DISABLE");
|
|
#if 0
|
|
if (val->intval == 1)
|
|
mfc_mpp_epp_nego_power_set(charger, MFC_RX_MPP_NEGO_POWER_15W); // need delay?
|
|
#endif
|
|
mfc_mpp_inc_int_ctrl(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_WIRELESS_MPP_PWR:
|
|
pr_info("%s:MPP_PWR input_current: %d\n", __func__, val->intval);
|
|
stwlc89_set_target_ilim(charger, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_MPLA_THR_RECOV:
|
|
charger->is_mpla_thr_recov = true;
|
|
__pm_stay_awake(charger->mpla_thr_recov_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->mpla_thr_recov_work, msecs_to_jiffies(2000));
|
|
break;
|
|
default:
|
|
return -ENODATA;
|
|
}
|
|
break;
|
|
default:
|
|
return -ENODATA;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u32 mfc_get_wireless20_vout_by_txid(struct mfc_charger_data *charger, u8 txid)
|
|
{
|
|
u32 vout = 0, i = 0;
|
|
|
|
if (txid < WC_PAD_AUTH_PAD_START || txid > WC_PAD_AUTH_PAD_END) {
|
|
pr_info("%s: wrong tx id(0x%x) for wireless 2.0\n", __func__, txid);
|
|
i = 0; /* defalut value is WIRELESS_VOUT_10V */
|
|
} else if (txid >= WC_PAD_AUTH_PAD_START + charger->pdata->len_wc20_list) {
|
|
pr_info("%s: undefined tx id(0x%x) of the device\n", __func__, txid);
|
|
i = charger->pdata->len_wc20_list - 1;
|
|
} else
|
|
i = txid - WC_PAD_AUTH_PAD_START;
|
|
|
|
vout = charger->pdata->wireless20_vout_list[i];
|
|
pr_info("%s: vout = %d\n", __func__, vout);
|
|
return vout;
|
|
}
|
|
|
|
static u32 mfc_get_wireless20_vrect_by_txid(struct mfc_charger_data *charger, u8 txid)
|
|
{
|
|
u32 vrect = 0, i = 0;
|
|
|
|
if (txid < WC_PAD_AUTH_PAD_START || txid > WC_PAD_AUTH_PAD_END) {
|
|
pr_info("%s: wrong tx id(0x%x) for wireless 2.0\n", __func__, txid);
|
|
i = 0; /* defalut value is MFC_AFC_CONF_10V_TX */
|
|
} else if (txid >= WC_PAD_AUTH_PAD_START + charger->pdata->len_wc20_list) {
|
|
pr_info("%s: undefined tx id(0x%x) of the device\n", __func__, txid);
|
|
i = charger->pdata->len_wc20_list - 1;
|
|
} else {
|
|
i = txid - WC_PAD_AUTH_PAD_START;
|
|
}
|
|
|
|
vrect = charger->pdata->wireless20_vrect_list[i];
|
|
pr_info("%s: vrect = %d\n", __func__, vrect);
|
|
return vrect;
|
|
}
|
|
|
|
static u32 mfc_get_wireless20_max_power_by_txid(struct mfc_charger_data *charger, u8 txid)
|
|
{
|
|
u32 max_power = 0, i = 0;
|
|
|
|
if (txid < WC_PAD_AUTH_PAD_START || txid > WC_PAD_AUTH_PAD_END) {
|
|
pr_info("%s: wrong tx id(0x%x) for wireless 2.0\n", __func__, txid);
|
|
i = 0; /* defalut value is SEC_WIRELESS_RX_POWER_12W */
|
|
} else if (txid >= WC_PAD_AUTH_PAD_START + charger->pdata->len_wc20_list) {
|
|
pr_info("%s: undefined tx id(0x%x) of the device\n", __func__, txid);
|
|
i = charger->pdata->len_wc20_list - 1;
|
|
} else {
|
|
i = txid - WC_PAD_AUTH_PAD_START;
|
|
}
|
|
|
|
max_power = charger->pdata->wireless20_max_power_list[i];
|
|
pr_info("%s: max rx power = %d\n", __func__, max_power);
|
|
return max_power;
|
|
}
|
|
|
|
static void mfc_recv_tx_pwr_budg(struct mfc_charger_data *charger, u8 data)
|
|
{
|
|
switch (data) {
|
|
case MFC_TX_PWR_BUDG_2W:
|
|
pr_info("%s: TX POWER BUDGET 2W\n", __func__);
|
|
charger->tx_pwr_budg = MFC_TX_PWR_BUDG_2W;
|
|
break;
|
|
case MFC_TX_PWR_BUDG_5W:
|
|
pr_info("%s: TX POWER BUDGET 5W\n", __func__);
|
|
charger->tx_pwr_budg = MFC_TX_PWR_BUDG_5W;
|
|
break;
|
|
case MFC_TX_PWR_BUDG_7_5W:
|
|
pr_info("%s: TX POWER BUDGET 7.5W\n", __func__);
|
|
charger->tx_pwr_budg = MFC_TX_PWR_BUDG_7_5W;
|
|
break;
|
|
case MFC_TX_PWR_BUDG_12W:
|
|
pr_info("%s: TX POWER BUDGET 12W\n", __func__);
|
|
charger->tx_pwr_budg = MFC_TX_PWR_BUDG_12W;
|
|
break;
|
|
case MFC_TX_PWR_BUDG_15W:
|
|
pr_info("%s: TX POWER BUDGET 15W\n", __func__);
|
|
charger->tx_pwr_budg = MFC_TX_PWR_BUDG_15W;
|
|
break;
|
|
default:
|
|
pr_info("%s: NOT DEFINED TX POWER BUDGET(%d)\n", __func__, data);
|
|
charger->tx_pwr_budg = MFC_TX_PWR_BUDG_NONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void mfc_activate_work_content(struct mfc_charger_data *charger)
|
|
{
|
|
int cable_type;
|
|
u8 pad_mode;
|
|
u8 vrect;
|
|
|
|
charger->initial_wc_check = true;
|
|
charger->otp_firmware_ver = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE);
|
|
charger->pdata->wc_ic_rev = mfc_get_ic_revision(charger, MFC_IC_REVISION);
|
|
|
|
charger->wc_tx_enable = false;
|
|
|
|
/* enable Mode Change INT */
|
|
mfc_reg_update(charger->client, MFC_INT_A_ENABLE_0_REG,
|
|
MFC_STAT_L_OP_MODE_MASK, MFC_STAT_L_OP_MODE_MASK);
|
|
mfc_reg_update(charger->client, MFC_INT_A_ENABLE_0_REG,
|
|
MFC_STAT_L_OVER_TEMP_MASK, MFC_STAT_L_OVER_TEMP_MASK);
|
|
|
|
if (!charger->pdata->default_clamp_volt) {
|
|
/* SET OV 20V */
|
|
mfc_reg_write(charger->client, MFC_RX_OV_CLAMP_REG, 0x3);
|
|
}
|
|
/* SET ILIM 1.5A */
|
|
mfc_reg_write(charger->client, MFC_ILIM_SET_REG, 0x1D);
|
|
|
|
/* read vrect adjust */
|
|
mfc_reg_read(charger->client, MFC_VRECT_ADJ_REG, &vrect);
|
|
|
|
pr_info("%s: wireless charger activated, set V_INT as PN\n", __func__);
|
|
|
|
/* CMA/CMB refresh */
|
|
stwlc89_mfc_cmfet_refresh(charger->cmfet);
|
|
|
|
/* read pad mode */
|
|
mfc_reg_read(charger->client, MFC_SYS_OP_MODE_REG, &pad_mode);
|
|
charger->rx_op_mode = pad_mode >> 5;
|
|
pr_info("%s: Pad type (0x%x)\n", __func__, charger->rx_op_mode);
|
|
|
|
cable_type = charger->pdata->cable_type;
|
|
charger->pdata->is_charging = 1;
|
|
if ((cable_type == SEC_BATTERY_CABLE_NONE) ||
|
|
cable_type == SEC_BATTERY_CABLE_WIRELESS_MPP_FAKE ||
|
|
is_wireless_fake_type(cable_type)) {
|
|
if (bpp_mode(charger->rx_op_mode)) {
|
|
cable_type = SEC_BATTERY_CABLE_WIRELESS;
|
|
|
|
if (!charger->is_otg_on) {
|
|
charger->pdata->vout_status = MFC_VOUT_5_5V;
|
|
mfc_set_vout(charger, charger->pdata->vout_status);
|
|
}
|
|
|
|
if (!is_no_hv(charger)) {
|
|
if (mfc_check_to_start_afc_tx(charger)) {
|
|
mfc_start_bpp_mode(charger);
|
|
} else {
|
|
/* reboot status with previous hv voltage setting */
|
|
/* re-set vout level */
|
|
charger->pad_vout = PAD_VOUT_10V;
|
|
mfc_set_vout(charger, MFC_VOUT_10V);
|
|
|
|
/* change cable type */
|
|
cable_type = SEC_BATTERY_CABLE_HV_WIRELESS;
|
|
}
|
|
}
|
|
mfc_start_wpc_tx_id_work(charger, 2500);
|
|
mfc_set_online(charger, cable_type);
|
|
} else if (mpp_mode(charger->rx_op_mode)) {
|
|
if (cable_type != SEC_BATTERY_CABLE_WIRELESS_MPP) {
|
|
mfc_mpp_epp_nego_done(charger);
|
|
cancel_delayed_work(&charger->mode_change_work);
|
|
__pm_stay_awake(charger->mode_change_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->mode_change_work, 0);
|
|
}
|
|
} else if (charger->rx_op_mode == MFC_RX_MODE_WPC_EPP) {
|
|
if ((cable_type != SEC_BATTERY_CABLE_WIRELESS_EPP) &&
|
|
(cable_type != SEC_BATTERY_CABLE_WIRELESS_EPP_NV)) {
|
|
mfc_mpp_epp_nego_done(charger);
|
|
cancel_delayed_work(&charger->mode_change_work);
|
|
__pm_stay_awake(charger->mode_change_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->mode_change_work, 0);
|
|
}
|
|
|
|
if (is_samsung_pad((charger->mpp_epp_tx_id & 0xFF)) ||
|
|
is_3rd_pad((charger->mpp_epp_tx_id & 0xFFFF)))
|
|
charger->epp_time = 6000;
|
|
} else if (charger->rx_op_mode == MFC_RX_MODE_WPC_EPP_NEGO) {
|
|
if (cable_type != SEC_BATTERY_CABLE_WIRELESS_EPP_FAKE)
|
|
mfc_mpp_epp_nego_done(charger);
|
|
|
|
if (is_samsung_pad((charger->mpp_epp_tx_id & 0xFF)) ||
|
|
is_3rd_pad((charger->mpp_epp_tx_id & 0xFFFF)))
|
|
charger->epp_time = 6000;
|
|
}
|
|
stwlc89_mfc_fod_set_op_mode(charger->fod, stwlc89_get_op_mode(charger));
|
|
}
|
|
stwlc89_mfc_fod_set_magnet_case_det(charger->fod, charger->mpp_case_det);
|
|
stwlc89_mfc_fod_refresh(charger->fod);
|
|
}
|
|
|
|
static void mfc_deactivate_work_content(struct mfc_charger_data *charger)
|
|
{
|
|
union power_supply_propval value;
|
|
|
|
/* Send last tx_id to battery to count tx_id */
|
|
value.intval = charger->tx_id;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_PROP_AUTHENTIC, value);
|
|
charger->pdata->cable_type = SEC_BATTERY_CABLE_NONE;
|
|
charger->pdata->is_charging = 0;
|
|
charger->pdata->vout_status = MFC_VOUT_5V;
|
|
charger->pad_vout = PAD_VOUT_5V;
|
|
charger->pdata->opfq_cnt = 0;
|
|
charger->pdata->trx_data_cmd = 0;
|
|
charger->pdata->trx_data_val = 0;
|
|
charger->vout_mode = 0;
|
|
charger->is_full_status = 0;
|
|
charger->is_afc_tx = false;
|
|
charger->pad_ctrl_by_lcd = false;
|
|
charger->tx_id = WC_PAD_UNKNOWN;
|
|
charger->i2c_error_count = 0;
|
|
charger->gpio_irq_missing_wa_cnt = 0;
|
|
charger->adt_transfer_status = WIRELESS_AUTH_WAIT;
|
|
charger->current_rx_power = TX_RX_POWER_0W;
|
|
charger->tx_id_cnt = 0;
|
|
charger->wc_ldo_status = MFC_LDO_ON;
|
|
charger->tx_id_done = false;
|
|
charger->req_tx_id = false;
|
|
charger->req_afc_delay = REQ_AFC_DLY;
|
|
charger->afc_tx_done = false;
|
|
charger->wc_align_check_start.tv_sec = 0;
|
|
charger->vout_strength = 100;
|
|
charger->check_rx_power = false;
|
|
charger->tx_pwr_budg = MFC_TX_PWR_BUDG_NONE;
|
|
charger->mpp_epp_tx_id = 0;
|
|
charger->mpp_epp_nego_done_power = 0;
|
|
charger->mpp_epp_tx_potential_load_power = 0;
|
|
charger->mpp_epp_tx_negotiable_load_power = 0;
|
|
charger->mpp_cloak = 0;
|
|
charger->rx_op_mode = 0;
|
|
|
|
charger->vout_by_txid = 0;
|
|
charger->vrect_by_txid = 0;
|
|
charger->max_power_by_txid = 0;
|
|
charger->thermal_ctrl = 0;
|
|
//mfc_mpp_exit_cloak_turn_off_power(charger);
|
|
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_NONE);
|
|
stwlc89_mfc_fod_init_state(charger->fod);
|
|
stwlc89_mfc_cmfet_init_state(charger->cmfet);
|
|
value.intval = 0;
|
|
psy_do_property("battery", set,
|
|
POWER_SUPPLY_EXT_PROP_RX_PHM, value);
|
|
|
|
/* tx retry case for mis-align */
|
|
if (charger->wc_checking_align) {
|
|
/* reset try cnt in real detach status */
|
|
if (!gpio_get_value(charger->pdata->wpc_en))
|
|
charger->mis_align_tx_try_cnt = 1;
|
|
else {
|
|
if (charger->mis_align_tx_try_cnt > MISALIGN_TX_TRY_CNT)
|
|
charger->mis_align_tx_try_cnt = 1;
|
|
msleep(1000);
|
|
mfc_set_wpc_en(charger, WPC_EN_CHARGING, true);
|
|
}
|
|
} else
|
|
charger->mis_align_tx_try_cnt = 1;
|
|
|
|
charger->wc_checking_align = false;
|
|
|
|
pr_info("%s: wpc deactivated, set V_INT as PD\n", __func__);
|
|
|
|
if (delayed_work_pending(&charger->wpc_afc_vout_work)) {
|
|
__pm_relax(charger->wpc_afc_vout_ws);
|
|
cancel_delayed_work(&charger->wpc_afc_vout_work);
|
|
}
|
|
if (delayed_work_pending(&charger->wpc_vout_mode_work)) {
|
|
__pm_relax(charger->wpc_vout_mode_ws);
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
}
|
|
if (delayed_work_pending(&charger->wpc_cs100_work)) {
|
|
__pm_relax(charger->wpc_cs100_ws);
|
|
cancel_delayed_work(&charger->wpc_cs100_work);
|
|
}
|
|
if (delayed_work_pending(&charger->wpc_rx_power_trans_fail_work)) {
|
|
__pm_relax(charger->wpc_rx_power_trans_fail_ws);
|
|
cancel_delayed_work(&charger->wpc_rx_power_trans_fail_work);
|
|
}
|
|
if (delayed_work_pending(&charger->wpc_rx_phm_work)) {
|
|
__pm_relax(charger->wpc_rx_phm_ws);
|
|
cancel_delayed_work(&charger->wpc_rx_phm_work);
|
|
}
|
|
|
|
cancel_delayed_work(&charger->wpc_isr_work);
|
|
cancel_delayed_work(&charger->wpc_tx_isr_work);
|
|
cancel_delayed_work(&charger->wpc_tx_id_work);
|
|
cancel_delayed_work(&charger->wpc_tx_pwr_budg_work);
|
|
cancel_delayed_work(&charger->wpc_i2c_error_work);
|
|
cancel_delayed_work(&charger->align_check_work);
|
|
__pm_relax(charger->wpc_rx_ws);
|
|
__pm_relax(charger->wpc_tx_ws);
|
|
__pm_relax(charger->wpc_tx_id_ws);
|
|
__pm_relax(charger->wpc_tx_pwr_budg_ws);
|
|
__pm_relax(charger->align_check_ws);
|
|
}
|
|
|
|
static void mfc_wpc_det_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_det_work.work);
|
|
|
|
u8 pdet_b = 0, p_nen = 0;
|
|
|
|
pr_info("%s : start\n", __func__);
|
|
|
|
/*
|
|
* We don't have to handle the wpc detect handling,
|
|
* when it's the MST mode.
|
|
*/
|
|
if (charger->is_mst_on == MST_MODE_2) {
|
|
pr_info("%s: check det_state(%d - %d)\n", __func__,
|
|
charger->det_state, gpio_get_value(charger->pdata->wpc_det));
|
|
|
|
if (charger->det_state == 0) {
|
|
pr_info("%s: skip wpc_det_work for MST operation\n", __func__);
|
|
__pm_relax(charger->wpc_det_ws);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (charger->pdata->wpc_pdet_b >= 0)
|
|
pdet_b = gpio_get_value(charger->pdata->wpc_pdet_b);
|
|
if (charger->pdata->ping_nen >= 0)
|
|
p_nen = gpio_get_value(charger->pdata->ping_nen);
|
|
pr_info("%s: det = %d, pdet_b = %d, ping_nen = %d\n",
|
|
__func__, gpio_get_value(charger->pdata->wpc_det), pdet_b, p_nen);
|
|
|
|
if (charger->det_state) {
|
|
int i = 0;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if (mfc_get_chip_id(charger) >= 0)
|
|
break;
|
|
}
|
|
if (i == 3) {
|
|
__pm_relax(charger->wpc_det_ws);
|
|
return;
|
|
}
|
|
|
|
if (charger->rx_op_mode == 0 && charger->mpp_case_det) {
|
|
u8 op_mode = 0;
|
|
|
|
if (delayed_work_pending(&charger->mode_change_work)) {
|
|
mfc_reg_read(charger->client, MFC_SYS_OP_MODE_REG, &op_mode);
|
|
pr_info("%s: rx_op_mode %d 0x%x\n", __func__, charger->rx_op_mode, op_mode);
|
|
if ((op_mode >> 5) == 0) {
|
|
mfc_soft_reset_ask_stop(charger);
|
|
__pm_relax(charger->wpc_det_ws);
|
|
return;
|
|
}
|
|
} else {
|
|
mfc_soft_reset_ask_stop(charger);
|
|
__pm_relax(charger->wpc_det_ws);
|
|
return;
|
|
}
|
|
}
|
|
mfc_wpc_check_fod_count(charger, FOD_CNT_CLEAR);
|
|
mfc_get_adc(charger, MFC_ADC_VRECT);
|
|
mfc_get_adc(charger, MFC_ADC_VOUT);
|
|
mfc_activate_work_content(charger);
|
|
} else {
|
|
if (charger->rx_phm_status && !pdet_b) {
|
|
pr_info("%s: phm status, skip deactivated\n", __func__);
|
|
charger->tx_id_cnt = 0;
|
|
__pm_relax(charger->wpc_det_ws);
|
|
return;
|
|
}
|
|
mfc_deactivate_work_content(charger);
|
|
}
|
|
__pm_relax(charger->wpc_det_ws);
|
|
}
|
|
|
|
static void mfc_wpc_pdrc_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_pdrc_work.work);
|
|
|
|
if (charger->is_mst_on == MST_MODE_2 || charger->wc_tx_enable) {
|
|
pr_info("%s: Noise made false Vrect IRQ !\n", __func__);
|
|
if (charger->wc_tx_enable) {
|
|
union power_supply_propval value;
|
|
|
|
value.intval = BATT_TX_EVENT_WIRELESS_TX_ETC;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ERR, value);
|
|
}
|
|
__pm_relax(charger->wpc_pdrc_ws);
|
|
return;
|
|
}
|
|
|
|
pr_info("%s : cable_type: %d, status: %d\n", __func__,
|
|
charger->pdata->cable_type, charger->pdrc_state);
|
|
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_NONE && (charger->pdrc_state == pdrc_active)) {
|
|
int i = 0;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if (mfc_get_chip_id(charger) >= 0)
|
|
break;
|
|
}
|
|
if (i == 3) {
|
|
__pm_relax(charger->wpc_pdrc_ws);
|
|
return;
|
|
}
|
|
mfc_wpc_check_fod_count(charger, FOD_CNT_CLEAR);
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS_FAKE);
|
|
} else if ((is_wireless_fake_type(charger->pdata->cable_type)) && (charger->pdrc_state == pdrc_inactive)) {
|
|
union power_supply_propval value = {0, };
|
|
|
|
charger->is_full_status = 0;
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_NONE);
|
|
stwlc89_mfc_fod_init_state(charger->fod);
|
|
stwlc89_mfc_cmfet_init_state(charger->cmfet);
|
|
value.intval = 0;
|
|
psy_do_property("battery", set,
|
|
POWER_SUPPLY_EXT_PROP_RX_PHM, value);
|
|
}
|
|
|
|
__pm_relax(charger->wpc_pdrc_ws);
|
|
}
|
|
|
|
/* INT_A */
|
|
static void mfc_wpc_tx_isr_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_tx_isr_work.work);
|
|
|
|
u8 status_l = 0, status_h = 0;
|
|
int ret = 0;
|
|
|
|
pr_info("@Tx_Mode %s\n", __func__);
|
|
|
|
if (!charger->wc_tx_enable) {
|
|
__pm_relax(charger->wpc_tx_ws);
|
|
return;
|
|
}
|
|
|
|
ret = mfc_reg_read(charger->client, MFC_STATUS_0_REG, &status_l);
|
|
ret = mfc_reg_read(charger->client, MFC_STATUS_1_REG, &status_h);
|
|
|
|
pr_info("@Tx_Mode %s: DATA RECEIVED! status(0x%x)\n", __func__, status_h << 8 | status_l);
|
|
|
|
mfc_tx_handle_rx_packet(charger);
|
|
|
|
__pm_relax(charger->wpc_tx_ws);
|
|
}
|
|
|
|
/* INT_A */
|
|
static void mfc_wpc_isr_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_isr_work.work);
|
|
u8 cmd_data, val_data;
|
|
int wpc_vout_ctrl_lcd_on = 0;
|
|
union power_supply_propval value = {0, };
|
|
|
|
if (!charger->det_state) {
|
|
pr_info("%s: charger->det_state is 0. exit wpc_isr_work.\n", __func__);
|
|
__pm_relax(charger->wpc_rx_ws);
|
|
return;
|
|
}
|
|
|
|
pr_info("%s: cable_type (%d)\n", __func__, charger->pdata->cable_type);
|
|
|
|
mfc_reg_read(charger->client, MFC_WPC_TRX_DATA2_COM_REG, &cmd_data);
|
|
mfc_reg_read(charger->client, MFC_WPC_TRX_DATA2_VALUE0_REG, &val_data);
|
|
charger->pdata->trx_data_cmd = cmd_data;
|
|
charger->pdata->trx_data_val = val_data;
|
|
|
|
pr_info("%s: WPC Interrupt Occurred, CMD : 0x%x, DATA : 0x%x\n", __func__, cmd_data, val_data);
|
|
|
|
if (mpp_mode(charger->rx_op_mode)) {
|
|
if ((cmd_data == MFC_HEADER_CLOAK) && ((val_data & 0xF0) == 0)) {
|
|
pr_info("@MPP %s: MFC_HEADER_CLOAK - set vout 10v\n", __func__);
|
|
mfc_set_vout(charger, MFC_VOUT_10V);
|
|
value.intval = CLOAK_PTX_INITIATED;
|
|
psy_do_property("wireless", set,
|
|
POWER_SUPPLY_EXT_PROP_MPP_CLOAK, value);
|
|
}
|
|
pr_info("@MPP %s: op_mode is MPP(%d), exit sfc\n", __func__, charger->rx_op_mode);
|
|
return;
|
|
} else if (epp_mode(charger->rx_op_mode) && !is_samsung_pad((charger->mpp_epp_tx_id & 0xFF))) {
|
|
pr_info("@EPP %s: op_mode is EPP(%d), exit sfc\n", __func__, charger->rx_op_mode);
|
|
return;
|
|
}
|
|
|
|
/* None or RX mode */
|
|
if (cmd_data == WPC_TX_COM_AFC_SET) {
|
|
switch (val_data) {
|
|
case TX_AFC_SET_5V:
|
|
pr_info("%s: data = 0x%x, might be 5V irq\n", __func__, val_data);
|
|
charger->pad_vout = PAD_VOUT_5V;
|
|
break;
|
|
case TX_AFC_SET_10V:
|
|
pr_info("%s: data = 0x%x, might be 10V irq\n", __func__, val_data);
|
|
charger->afc_tx_done = true;
|
|
if (!gpio_get_value(charger->pdata->wpc_det)) {
|
|
pr_err("%s: Wireless charging is paused during set high voltage.\n", __func__);
|
|
__pm_relax(charger->wpc_rx_ws);
|
|
return;
|
|
}
|
|
if (!charger->pdata->no_hv) {
|
|
if (is_hv_wireless_type(charger->pdata->cable_type) ||
|
|
(charger->pdata->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV)) {
|
|
pr_err("%s: Is is already HV wireless cable. No need to set again\n", __func__);
|
|
__pm_relax(charger->wpc_rx_ws);
|
|
return;
|
|
}
|
|
|
|
/* send AFC_SET */
|
|
mfc_send_command(charger, MFC_AFC_CONF_10V);
|
|
msleep(500);
|
|
|
|
/* change cable type */
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV);
|
|
|
|
charger->pad_vout = PAD_VOUT_10V;
|
|
stwlc89_mfc_fod_set_op_mode(charger->fod, WPC_OP_MODE_PPDE);
|
|
}
|
|
break;
|
|
case TX_AFC_SET_12V:
|
|
break;
|
|
case TX_AFC_SET_18V:
|
|
case TX_AFC_SET_19V:
|
|
case TX_AFC_SET_20V:
|
|
case TX_AFC_SET_24V:
|
|
break;
|
|
default:
|
|
pr_info("%s: unsupport : 0x%x", __func__, val_data);
|
|
break;
|
|
}
|
|
} else if (cmd_data == WPC_TX_COM_TX_ID) {
|
|
if (charger->req_tx_id)
|
|
charger->req_tx_id = false;
|
|
|
|
if (!charger->tx_id_done) {
|
|
int capacity = 0;
|
|
bool skip_send_online = false;
|
|
|
|
charger->tx_id = val_data;
|
|
stwlc89_mfc_fod_set_tx_id(charger->fod, val_data);
|
|
|
|
psy_do_property("battery", get, POWER_SUPPLY_PROP_CAPACITY, value);
|
|
capacity = value.intval;
|
|
|
|
value.intval = in_opfreq_ctrl_pad_list(charger);
|
|
psy_do_property("battery", set, POWER_SUPPLY_EXT_PROP_LCD_FLICKER, value); // Set VOUT CTRL MODE
|
|
|
|
psy_do_property("battery", get, POWER_SUPPLY_EXT_PROP_LCD_FLICKER, value);
|
|
wpc_vout_ctrl_lcd_on = value.intval;
|
|
|
|
switch (val_data) {
|
|
case WC_PAD_UNKNOWN:
|
|
value.intval = charger->pdata->cable_type;
|
|
break;
|
|
case WC_PAD_HN930:
|
|
if (charger->pad_vout == PAD_VOUT_10V) {
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV) {
|
|
charger->pdata->cable_type = SEC_BATTERY_CABLE_WIRELESS_HV_VEHICLE;
|
|
skip_send_online = true;
|
|
} else {
|
|
charger->pdata->cable_type = value.intval =
|
|
SEC_BATTERY_CABLE_WIRELESS_HV_VEHICLE;
|
|
}
|
|
} else {
|
|
charger->pdata->cable_type = value.intval = SEC_BATTERY_CABLE_WIRELESS_VEHICLE;
|
|
}
|
|
pr_info("%s: VEHICLE Wireless Charge PAD %s\n", __func__,
|
|
charger->pad_vout == PAD_VOUT_10V ? "HV" : "");
|
|
|
|
break;
|
|
case WC_PAD_NG930:
|
|
if (charger->pad_vout == PAD_VOUT_10V) {
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV) {
|
|
charger->pdata->cable_type = SEC_BATTERY_CABLE_WIRELESS_HV_STAND;
|
|
skip_send_online = true;
|
|
} else {
|
|
charger->pdata->cable_type = value.intval = SEC_BATTERY_CABLE_WIRELESS_HV_STAND;
|
|
}
|
|
} else {
|
|
charger->pdata->cable_type = value.intval =
|
|
SEC_BATTERY_CABLE_WIRELESS_STAND;
|
|
}
|
|
pr_info("%s: Cable(%d), STAND Wireless Charge PAD %s\n", __func__,
|
|
charger->pdata->cable_type, charger->pad_vout == PAD_VOUT_10V ? "HV" : "");
|
|
break;
|
|
case WC_PAD_MULTI_PORT_START ... WC_PAD_MULTI_PORT_END:
|
|
value.intval = charger->pdata->cable_type;
|
|
pr_info("%s: MULTI PORT PAD : 0x%x\n", __func__, val_data);
|
|
break;
|
|
case WC_PAD_U1200 ... WC_PAD_BPACK_END:
|
|
if (charger->pad_vout == PAD_VOUT_10V) {
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV) {
|
|
charger->pdata->cable_type = SEC_BATTERY_CABLE_WIRELESS_HV_PACK;
|
|
skip_send_online = true;
|
|
pr_info("%s: WIRELESS HV BATTERY PACK (PREP)\n", __func__);
|
|
} else {
|
|
charger->pdata->cable_type = value.intval =
|
|
SEC_BATTERY_CABLE_WIRELESS_HV_PACK;
|
|
pr_info("%s: WIRELESS HV BATTERY PACK\n", __func__);
|
|
}
|
|
} else {
|
|
charger->pdata->cable_type = value.intval = SEC_BATTERY_CABLE_WIRELESS_PACK;
|
|
pr_info("%s: WIRELESS BATTERY PACK\n", __func__);
|
|
}
|
|
#if !defined(CONFIG_WIRELESS_RX_PHM_CTRL)
|
|
if (charger->device_event & BATT_EXT_EVENT_CALL) {
|
|
union power_supply_propval value2;
|
|
|
|
pr_info("%s: enter PHM\n", __func__);
|
|
/* notify "wireless" PHM status */
|
|
value2.intval = 1;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_CALL_EVENT, value2);
|
|
mfc_send_command(charger, MFC_PHM_ON);
|
|
msleep(250);
|
|
mfc_send_command(charger, MFC_PHM_ON);
|
|
}
|
|
#endif
|
|
break;
|
|
case WC_PAD_PHONE_D2D:
|
|
case WC_PAD_TX_B0 ... WC_PAD_TX_MAX:
|
|
charger->pdata->cable_type = value.intval = SEC_BATTERY_CABLE_WIRELESS_TX;
|
|
pr_info("@Tx_Mode %s: TX by UNO\n", __func__);
|
|
if (capacity < 100)
|
|
mfc_wpc_align_check(charger, ALIGN_WORK_DELAY);
|
|
#if !defined(CONFIG_WIRELESS_RX_PHM_CTRL)
|
|
if (charger->device_event & BATT_EXT_EVENT_CALL) {
|
|
pr_info("%s: enter PHM\n", __func__);
|
|
mfc_send_command(charger, MFC_PHM_ON);
|
|
msleep(250);
|
|
mfc_send_command(charger, MFC_PHM_ON);
|
|
}
|
|
#endif
|
|
break;
|
|
case WC_PAD_P3105 ... WC_PAD_MAX:
|
|
value.intval = charger->pdata->cable_type;
|
|
break;
|
|
case WC_PAD_AUTH_PAD_START ... WC_PAD_AUTH_PAD_END:
|
|
if (charger->pdata->no_hv) {
|
|
pr_info("%s: WIRELESS HV is disabled\n", __func__);
|
|
break;
|
|
}
|
|
if (epp_mode(charger->rx_op_mode) && (charger->tx_id == WC_PAD_P2400))
|
|
mfc_send_command(charger, MFC_AFC_CONF_10V_TX);
|
|
|
|
charger->vout_by_txid = mfc_get_wireless20_vout_by_txid(charger, val_data);
|
|
charger->vrect_by_txid = mfc_get_wireless20_vrect_by_txid(charger, val_data);
|
|
charger->max_power_by_txid = mfc_get_wireless20_max_power_by_txid(charger, val_data);
|
|
#if !defined(CONFIG_SEC_FACTORY)
|
|
/* power on charging */
|
|
if (charger->adt_transfer_status == WIRELESS_AUTH_WAIT) {
|
|
/* change first wireless type to prepare wc2.0 for auth mode */
|
|
charger->pdata->cable_type = value.intval = SEC_BATTERY_CABLE_PREPARE_WIRELESS_20;
|
|
pr_info("%s %s: AUTH PAD for WIRELESS 2.0 : 0x%x\n", WC_AUTH_MSG,
|
|
__func__, val_data);
|
|
|
|
mfc_auth_set_configs(charger, AUTH_READY);
|
|
if (delayed_work_pending(&charger->wpc_afc_vout_work)) {
|
|
__pm_relax(charger->wpc_afc_vout_ws);
|
|
cancel_delayed_work(&charger->wpc_afc_vout_work);
|
|
}
|
|
/* notify auth service to send TX PAD a request key */
|
|
mfc_auth_send_adt_status(charger, WIRELESS_AUTH_START);
|
|
} else if (charger->adt_transfer_status == WIRELESS_AUTH_PASS) {
|
|
if (delayed_work_pending(&charger->wpc_afc_vout_work)) {
|
|
__pm_relax(charger->wpc_afc_vout_ws);
|
|
cancel_delayed_work(&charger->wpc_afc_vout_work);
|
|
}
|
|
charger->pdata->cable_type = value.intval = SEC_BATTERY_CABLE_HV_WIRELESS_20;
|
|
skip_send_online = true;
|
|
__pm_stay_awake(charger->wpc_afc_vout_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_afc_vout_work, msecs_to_jiffies(0));
|
|
} else if (charger->adt_transfer_status == WIRELESS_AUTH_FAIL) {
|
|
charger->pdata->cable_type = SEC_BATTERY_CABLE_HV_WIRELESS;
|
|
value.intval = charger->pdata->cable_type;
|
|
skip_send_online = true;
|
|
if (charger->afc_tx_done) {
|
|
charger->pdata->cable_type = SEC_BATTERY_CABLE_HV_WIRELESS;
|
|
__pm_stay_awake(charger->wpc_afc_vout_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_afc_vout_work, msecs_to_jiffies(0));
|
|
} else {
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS);
|
|
}
|
|
} else {
|
|
value.intval = charger->pdata->cable_type;
|
|
pr_info("%s %s: undefined auth TX-ID scenario, auth service works strange...\n",
|
|
WC_AUTH_MSG, __func__);
|
|
}
|
|
#else /* FACTORY MODE dose not process auth, set 12W right after */
|
|
if (charger->adt_transfer_status == WIRELESS_AUTH_WAIT) {
|
|
if (delayed_work_pending(&charger->wpc_afc_vout_work)) {
|
|
__pm_relax(charger->wpc_afc_vout_ws);
|
|
cancel_delayed_work(&charger->wpc_afc_vout_work);
|
|
}
|
|
charger->adt_transfer_status = WIRELESS_AUTH_PASS;
|
|
charger->pdata->cable_type = value.intval = SEC_BATTERY_CABLE_HV_WIRELESS_20;
|
|
skip_send_online = true;
|
|
__pm_stay_awake(charger->wpc_afc_vout_ws);
|
|
queue_delayed_work(charger->wqueue,
|
|
&charger->wpc_afc_vout_work, msecs_to_jiffies(0));
|
|
}
|
|
pr_info("%s %s: AUTH PAD for WIRELESS 2.0 but FACTORY MODE\n", WC_AUTH_MSG, __func__);
|
|
#endif
|
|
break;
|
|
case WC_PAD_PG950_S:
|
|
value.intval = charger->pdata->cable_type;
|
|
pr_info("%s: PG950 PAD STAND : 0x%x\n", __func__, val_data);
|
|
break;
|
|
case WC_PAD_PG950_P:
|
|
value.intval = charger->pdata->cable_type;
|
|
pr_info("%s: PG950 PAD DOWN : 0x%x\n", __func__, val_data);
|
|
break;
|
|
case WC_PAD_P2400_P9500:
|
|
value.intval = charger->pdata->cable_type;
|
|
if (epp_mode(charger->rx_op_mode))
|
|
mfc_send_command(charger, MFC_AFC_CONF_10V_TX);
|
|
break;
|
|
default:
|
|
value.intval = charger->pdata->cable_type;
|
|
pr_info("%s: UNDEFINED PAD : 0x%x\n", __func__, val_data);
|
|
break;
|
|
}
|
|
|
|
if (wpc_vout_ctrl_lcd_on && is_opfreq_ctrl_pad(charger)) {
|
|
pr_info("%s: tx id = 0x%x , set op freq\n", __func__, val_data);
|
|
mfc_send_command(charger, MFC_SET_OP_FREQ);
|
|
msleep(500);
|
|
}
|
|
|
|
if (epp_mode(charger->rx_op_mode)) {
|
|
if (charger->adt_transfer_status == WIRELESS_AUTH_WAIT) {
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS_EPP);
|
|
charger->max_power_by_txid = TX_RX_POWER_8W * 100000;
|
|
charger->current_rx_power = TX_RX_POWER_8W;
|
|
skip_send_online = true;
|
|
}
|
|
}
|
|
|
|
if (!skip_send_online) {
|
|
pr_info("%s: change cable type\n", __func__);
|
|
mfc_set_online(charger, value.intval);
|
|
}
|
|
|
|
/* send max vout informaion of this pad such as WIRELESS_VOUT_10V, WIRELESS_VOUT_12_5V */
|
|
mfc_set_psy_wrl(charger, POWER_SUPPLY_EXT_PROP_WIRELESS_MAX_VOUT, charger->vout_by_txid);
|
|
|
|
/*
|
|
* send rx power informaion of this pad such as
|
|
* SEC_WIRELESS_RX_POWER_12W, SEC_WIRELESS_RX_POWER_17_5W
|
|
*/
|
|
mfc_set_rx_power(charger, charger->max_power_by_txid);
|
|
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_rx_power_work, msecs_to_jiffies(3000));
|
|
#endif
|
|
charger->tx_id_done = true;
|
|
pr_info("%s: TX_ID : 0x%x\n", __func__, val_data);
|
|
} else {
|
|
pr_err("%s: TX ID isr is already done\n", __func__);
|
|
}
|
|
} else if (cmd_data == WPC_TX_COM_CHG_ERR) {
|
|
switch (val_data) {
|
|
case TX_CHG_ERR_OTP:
|
|
case TX_CHG_ERR_OCP:
|
|
case TX_CHG_ERR_DARKZONE:
|
|
pr_info("%s: Received CHG error from the TX(0x%x)\n", __func__, val_data);
|
|
break;
|
|
/*case TX_CHG_ERR_FOD:*/
|
|
case 0x20 ... 0x27:
|
|
pr_info("%s: Received FOD state from the TX(0x%x)\n", __func__, val_data);
|
|
value.intval = val_data;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_ERR, value);
|
|
break;
|
|
default:
|
|
pr_info("%s: Undefined Type(0x%x)\n", __func__, val_data);
|
|
break;
|
|
}
|
|
} else if (cmd_data == WPC_TX_COM_RX_POWER) {
|
|
charger->check_rx_power = false;
|
|
if (delayed_work_pending(&charger->wpc_rx_power_trans_fail_work)) {
|
|
__pm_relax(charger->wpc_rx_power_trans_fail_ws);
|
|
cancel_delayed_work(&charger->wpc_rx_power_trans_fail_work);
|
|
}
|
|
if (charger->max_power_by_txid / 100000 < val_data) {
|
|
pr_info("%s %s: RX Power(%d) is bigger than max power(%d)\n",
|
|
WC_AUTH_MSG, __func__, val_data, charger->max_power_by_txid / 100000);
|
|
val_data = charger->max_power_by_txid / 100000;
|
|
}
|
|
if (charger->current_rx_power != val_data) {
|
|
switch (val_data) {
|
|
case TX_RX_POWER_3W:
|
|
pr_info("%s %s: RX Power is 3W\n", WC_AUTH_MSG, __func__);
|
|
charger->current_rx_power = TX_RX_POWER_3W;
|
|
break;
|
|
case TX_RX_POWER_5W:
|
|
pr_info("%s %s: RX Power is 5W\n", WC_AUTH_MSG, __func__);
|
|
charger->current_rx_power = TX_RX_POWER_5W;
|
|
break;
|
|
case TX_RX_POWER_6_5W:
|
|
pr_info("%s %s: RX Power is 6.5W\n", WC_AUTH_MSG, __func__);
|
|
charger->current_rx_power = TX_RX_POWER_6_5W;
|
|
break;
|
|
case TX_RX_POWER_7_5W:
|
|
pr_info("%s %s: RX Power is 7.5W\n", WC_AUTH_MSG, __func__);
|
|
mfc_reset_rx_power(charger, TX_RX_POWER_7_5W);
|
|
charger->current_rx_power = TX_RX_POWER_7_5W;
|
|
break;
|
|
case TX_RX_POWER_10W:
|
|
pr_info("%s %s: RX Power is 10W\n", WC_AUTH_MSG, __func__);
|
|
charger->current_rx_power = TX_RX_POWER_10W;
|
|
break;
|
|
case TX_RX_POWER_11W:
|
|
pr_info("%s %s: RX Power is 11W\n", WC_AUTH_MSG, __func__);
|
|
mfc_reset_rx_power(charger, TX_RX_POWER_11W);
|
|
charger->current_rx_power = TX_RX_POWER_11W;
|
|
break;
|
|
case TX_RX_POWER_12W:
|
|
pr_info("%s %s: RX Power is 12W\n", WC_AUTH_MSG, __func__);
|
|
mfc_reset_rx_power(charger, TX_RX_POWER_12W);
|
|
charger->current_rx_power = TX_RX_POWER_12W;
|
|
break;
|
|
case TX_RX_POWER_15W:
|
|
pr_info("%s %s: RX Power is 15W\n", WC_AUTH_MSG, __func__);
|
|
mfc_reset_rx_power(charger, TX_RX_POWER_15W);
|
|
charger->current_rx_power = TX_RX_POWER_15W;
|
|
break;
|
|
case TX_RX_POWER_17_5W:
|
|
pr_info("%s %s: RX Power is 17.5W\n", WC_AUTH_MSG, __func__);
|
|
mfc_reset_rx_power(charger, TX_RX_POWER_17_5W);
|
|
charger->current_rx_power = TX_RX_POWER_17_5W;
|
|
break;
|
|
case TX_RX_POWER_20W:
|
|
pr_info("%s %s: RX Power is 20W\n", WC_AUTH_MSG, __func__);
|
|
mfc_reset_rx_power(charger, TX_RX_POWER_20W);
|
|
charger->current_rx_power = TX_RX_POWER_20W;
|
|
break;
|
|
default:
|
|
pr_info("%s %s: Undefined RX Power(0x%x)\n", WC_AUTH_MSG, __func__, val_data);
|
|
break;
|
|
}
|
|
} else {
|
|
pr_info("%s: skip same RX Power\n", __func__);
|
|
}
|
|
} else if (cmd_data == WPC_TX_COM_WPS) {
|
|
switch (val_data) {
|
|
case WPS_AICL_RESET:
|
|
value.intval = 1;
|
|
pr_info("@Tx_mode %s: Rx devic AICL Reset\n", __func__);
|
|
psy_do_property("wireless", set, POWER_SUPPLY_PROP_CURRENT_MAX, value);
|
|
break;
|
|
default:
|
|
pr_info("%s %s: Undefined RX Power(0x%x)\n", WC_AUTH_MSG, __func__, val_data);
|
|
break;
|
|
}
|
|
} else if (cmd_data == WPC_TX_COM_TX_PWR_BUDG) {
|
|
pr_info("%s: WPC_TX_COM_TX_PWR_BUDG (0x%x)\n", __func__, val_data);
|
|
mfc_recv_tx_pwr_budg(charger, val_data);
|
|
}
|
|
__pm_relax(charger->wpc_rx_ws);
|
|
}
|
|
|
|
static void mfc_set_unknown_pad_config(struct mfc_charger_data *charger)
|
|
{
|
|
pr_info("%s: TX ID not Received, cable_type(%d)\n",
|
|
__func__, charger->pdata->cable_type);
|
|
|
|
if (epp_mode(charger->rx_op_mode)) {
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
__pm_stay_awake(charger->wpc_vout_mode_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_vout_mode_work, 0);
|
|
if (charger->mpp_epp_nego_done_power < TX_RX_POWER_8W) {
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS_EPP_NV);
|
|
mfc_set_epp_nv_mode(charger);
|
|
} else {
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS_EPP);
|
|
mfc_set_epp_mode(charger, TX_RX_POWER_8W);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!charger->pdata->default_clamp_volt) {
|
|
if (is_hv_wireless_type(charger->pdata->cable_type)) {
|
|
/* SET OV 13V */
|
|
mfc_reg_write(charger->client, MFC_RX_OV_CLAMP_REG, 0x6);
|
|
} else {
|
|
/* SET OV 11V */
|
|
mfc_reg_write(charger->client, MFC_RX_OV_CLAMP_REG, 0x7);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mfc_wpc_tx_pwr_budg_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_tx_pwr_budg_work.work);
|
|
|
|
mfc_send_command(charger, MFC_REQ_TX_PWR_BUDG);
|
|
__pm_relax(charger->wpc_tx_pwr_budg_ws);
|
|
}
|
|
|
|
static void mfc_wpc_tx_id_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_tx_id_work.work);
|
|
union power_supply_propval value;
|
|
|
|
pr_info("%s\n", __func__);
|
|
|
|
if (!charger->tx_id) {
|
|
if (charger->tx_id_cnt < 10) {
|
|
mfc_send_command(charger, MFC_REQUEST_TX_ID);
|
|
charger->req_tx_id = true;
|
|
charger->tx_id_cnt++;
|
|
|
|
pr_info("%s: request TX ID cnt (%d)\n", __func__, charger->tx_id_cnt);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_tx_id_work, msecs_to_jiffies(1500));
|
|
return;
|
|
}
|
|
|
|
mfc_set_unknown_pad_config(charger);
|
|
charger->req_tx_id = false;
|
|
} else {
|
|
pr_info("%s: TX ID (0x%x)\n", __func__, charger->tx_id);
|
|
if (charger->tx_id == WC_PAD_JIG) {
|
|
value.intval = 1;
|
|
pr_info("%s: it is jig pad\n", __func__);
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_JIG_PAD, value);
|
|
} else {
|
|
stwlc89_mfc_cmfet_set_tx_id(charger->cmfet, charger->tx_id);
|
|
}
|
|
}
|
|
|
|
__pm_relax(charger->wpc_tx_id_ws);
|
|
}
|
|
|
|
static irqreturn_t mfc_wpc_det_irq_thread(int irq, void *irq_data)
|
|
{
|
|
struct mfc_charger_data *charger = irq_data;
|
|
u8 det_state;
|
|
|
|
pr_info("%s(%d)\n", __func__, irq);
|
|
|
|
det_state = gpio_get_value(charger->pdata->wpc_det);
|
|
|
|
if (charger->det_state != det_state) {
|
|
union power_supply_propval value = {0, };
|
|
|
|
value.intval = 0;
|
|
psy_do_property("battery", set,
|
|
POWER_SUPPLY_EXT_PROP_RX_PHM, value);
|
|
|
|
pr_info("%s: det(%d to %d)\n", __func__, charger->det_state, det_state);
|
|
charger->det_state = det_state;
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_det_work, 0);
|
|
} else
|
|
__pm_relax(charger->wpc_det_ws);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static bool mfc_check_skip_epp_timer(struct mfc_charger_data *charger)
|
|
{
|
|
bool ret = false;
|
|
|
|
if (charger->rx_op_mode == MFC_RX_MODE_WPC_MPP_FULL ||
|
|
charger->rx_op_mode == MFC_RX_MODE_WPC_BPP)
|
|
ret = true;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mfc_wpc_mpp_enable_ctrl(struct mfc_charger_data *charger, bool pdrc_state)
|
|
{
|
|
int epp_clear_delay = 0;
|
|
|
|
if (pdrc_state != pdrc_inactive) {
|
|
/* disable epp */
|
|
mfc_epp_enable(charger, 0);
|
|
if (delayed_work_pending(&charger->epp_clear_timer_work)) {
|
|
__pm_relax(charger->epp_clear_ws);
|
|
cancel_delayed_work(&charger->epp_clear_timer_work);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (charger->rx_op_mode == MFC_RX_MODE_WPC_EPP)
|
|
if (!is_3rd_pad((charger->mpp_epp_tx_id & 0xFFFF)))
|
|
return;
|
|
|
|
if (mfc_check_skip_epp_timer(charger)) {
|
|
mfc_epp_enable(charger, 1);
|
|
mfc_set_epp_count(charger, 0);
|
|
} else {
|
|
if (charger->epp_time == 6000) {
|
|
epp_clear_delay = charger->epp_time;
|
|
charger->epp_time = charger->pdata->epp_time_final;
|
|
} else if (charger->epp_count == 0) {
|
|
epp_clear_delay = charger->pdata->epp_time_init;
|
|
} else if (charger->epp_count < 2) {
|
|
epp_clear_delay = charger->pdata->epp_time_mid_1;
|
|
} else if (charger->epp_count < charger->pdata->mpp_epp_max_count) {
|
|
epp_clear_delay = charger->pdata->epp_time_mid_2;
|
|
} else {
|
|
epp_clear_delay = charger->pdata->epp_time_final;
|
|
}
|
|
|
|
if ((charger->mpp_epp_tx_id & 0xFFFF) == 0x0028) {
|
|
pr_info("%s update delay %d -> 4000\n", __func__, epp_clear_delay);
|
|
epp_clear_delay = 4000;
|
|
}
|
|
|
|
pr_info("%s: set delay(%d), count(%d, %d)\n",
|
|
__func__, epp_clear_delay, charger->epp_count, charger->pdata->mpp_epp_max_count);
|
|
__pm_stay_awake(charger->epp_clear_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->epp_clear_timer_work,
|
|
msecs_to_jiffies(epp_clear_delay));
|
|
|
|
mfc_set_epp_count(charger, charger->epp_count + 1);
|
|
}
|
|
}
|
|
|
|
static irqreturn_t mfc_wpc_pdrc_irq_thread(int irq, void *irq_data)
|
|
{
|
|
struct mfc_charger_data *charger = irq_data;
|
|
u8 pdrc_state;
|
|
|
|
pr_info("%s(%d)\n", __func__, irq);
|
|
|
|
pdrc_state = gpio_get_value(charger->pdata->wpc_pdrc);
|
|
|
|
if (charger->pdrc_state != pdrc_state) {
|
|
pr_info("%s: pdrc(%d to %d)\n", __func__, charger->pdrc_state, pdrc_state);
|
|
charger->pdrc_state = pdrc_state;
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_NONE)
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_pdrc_work, msecs_to_jiffies(0));
|
|
else if (is_wireless_fake_type(charger->pdata->cable_type))
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_pdrc_work, msecs_to_jiffies(900));
|
|
else
|
|
__pm_relax(charger->wpc_pdrc_ws);
|
|
|
|
mfc_wpc_mpp_enable_ctrl(charger, pdrc_state);
|
|
} else {
|
|
if (!delayed_work_pending(&charger->wpc_pdrc_work))
|
|
__pm_relax(charger->wpc_pdrc_ws);
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t mfc_wpc_pdet_b_irq_thread(int irq, void *irq_data)
|
|
{
|
|
struct mfc_charger_data *charger = irq_data;
|
|
u8 pdetb_state = 0, det_state = 0;
|
|
|
|
pdetb_state = gpio_get_value(charger->pdata->wpc_pdet_b);
|
|
det_state = gpio_get_value(charger->pdata->wpc_det);
|
|
|
|
pr_info("%s : pdet_b = %d, wpc_det = %d\n",
|
|
__func__, pdetb_state, det_state);
|
|
|
|
if (charger->rx_phm_status && !det_state && pdetb_state) {
|
|
pr_info("%s wireless charger deactivated during phm\n", __func__);
|
|
charger->rx_phm_state = END_PHM;
|
|
__pm_stay_awake(charger->wpc_rx_phm_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_rx_phm_work, 0);
|
|
}
|
|
__pm_relax(charger->wpc_pdet_b_ws);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/* mfc_mst_routine : MST dedicated codes */
|
|
static void mfc_mst_routine(struct mfc_charger_data *charger, u8 irq_src_l, u8 irq_src_h)
|
|
{
|
|
u8 data = 0;
|
|
|
|
pr_info("%s\n", __func__);
|
|
|
|
if (charger->is_mst_on == MST_MODE_2) {
|
|
charger->wc_tx_enable = false;
|
|
#if defined(CONFIG_MST_V2)
|
|
// it will send MST driver a message.
|
|
mfc_send_mst_cmd(MST_MODE_ON, charger, irq_src_l, irq_src_h);
|
|
#endif
|
|
} else if (charger->wc_tx_enable) {
|
|
mfc_reg_read(charger->client, MFC_STATUS_1_REG, &data);
|
|
data &= 0x4; /* AC MISSING DETECT */
|
|
msleep(100);
|
|
pr_info("@Tx_Mode %s: 0x21 Register AC Missing(%d)\n", __func__, data);
|
|
if (data) {
|
|
/* initialize control reg */
|
|
mfc_reg_write(charger->client, MFC_TX_IUNO_HYS_REG, 0x50);
|
|
mfc_reg_write(charger->client, MFC_MST_MODE_SEL_REG, 0x03); /* set TX-ON mode */
|
|
|
|
pr_info("@Tx_Mode %s: TX-ON Mode : %d\n", __func__, charger->pdata->wc_ic_rev);
|
|
} // ac missing is 0, ie, TX detected
|
|
}
|
|
}
|
|
|
|
static irqreturn_t mfc_wpc_irq_handler(int irq, void *irq_data)
|
|
{
|
|
struct mfc_charger_data *charger = irq_data;
|
|
|
|
if (irq == charger->pdata->irq_wpc_int)
|
|
__pm_stay_awake(charger->wpc_ws);
|
|
else if (irq == charger->pdata->irq_wpc_det)
|
|
__pm_stay_awake(charger->wpc_det_ws);
|
|
else if (irq == charger->pdata->irq_wpc_pdrc)
|
|
__pm_stay_awake(charger->wpc_pdrc_ws);
|
|
else if (irq == charger->pdata->irq_wpc_pdet_b)
|
|
__pm_stay_awake(charger->wpc_pdet_b_ws);
|
|
|
|
#if IS_ENABLED(CONFIG_ENABLE_WIRELESS_IRQ_IN_SLEEP)
|
|
return charger->is_suspend ? IRQ_HANDLED : IRQ_WAKE_THREAD;
|
|
#else
|
|
return IRQ_WAKE_THREAD;
|
|
#endif
|
|
}
|
|
|
|
static void mfc_mpp_epp_nego_done(struct mfc_charger_data *charger)
|
|
{
|
|
u8 reg_data = 0;
|
|
u8 K_est[2] = {0,};
|
|
|
|
mfc_reg_read(charger->client, MFC_SYS_OP_MODE_REG, ®_data);
|
|
charger->rx_op_mode = reg_data >> 5;
|
|
pr_info("@MPP @EPP %s:Rx op_mode %d\n", __func__, charger->rx_op_mode);
|
|
|
|
mfc_reg_read(charger->client, MFC_MPP_EPP_NEGO_DONE_POWER_L_REG, ®_data);
|
|
charger->mpp_epp_nego_done_power = reg_data;
|
|
mfc_reg_read(charger->client, MFC_MPP_EPP_NEGO_DONE_POWER_H_REG, ®_data);
|
|
charger->mpp_epp_nego_done_power |= (reg_data << 8);
|
|
|
|
//Vrect is 12V and load current<595mA. modulation base: 70mA
|
|
//mfc_reg_write(charger->client, MFC_MPP_DC_CURRENT_MOD_BASE_LIGHT_REG, 70);
|
|
//DC current modulation depth setting value, 50mA
|
|
//mfc_reg_write(charger->client, MFC_MPP_DC_CURRENT_MOD_DEPTH_REG, 50);
|
|
|
|
mfc_reg_read(charger->client, MFC_MPP_PTX_EXTENDED_ID0_REG, ®_data);
|
|
charger->mpp_epp_tx_id = (reg_data << 16);
|
|
mfc_reg_read(charger->client, MFC_MPP_PTX_EXTENDED_ID1_REG, ®_data);
|
|
charger->mpp_epp_tx_id |= (reg_data << 8);
|
|
mfc_reg_read(charger->client, MFC_MPP_PTX_EXTENDED_ID2_REG, ®_data);
|
|
charger->mpp_epp_tx_id |= reg_data;
|
|
|
|
mfc_reg_read(charger->client, MFC_MPP_EPP_POTENTIAL_LOAD_POWER_L_REG, ®_data);
|
|
charger->mpp_epp_tx_potential_load_power = reg_data;
|
|
mfc_reg_read(charger->client, MFC_MPP_EPP_POTENTIAL_LOAD_POWER_H_REG, ®_data);
|
|
charger->mpp_epp_tx_potential_load_power |= (reg_data << 8);
|
|
|
|
mfc_reg_read(charger->client, MFC_MPP_EPP_NEGOTIABLE_LOAD_POWER_L_REG, ®_data);
|
|
charger->mpp_epp_tx_negotiable_load_power = reg_data;
|
|
mfc_reg_read(charger->client, MFC_MPP_EPP_NEGOTIABLE_LOAD_POWER_H_REG, ®_data);
|
|
charger->mpp_epp_tx_negotiable_load_power |= (reg_data << 8);
|
|
|
|
mfc_reg_multi_read(charger->client, MFC_MPP_EPP_ESTIMATE_K_H_REG, K_est, 2);
|
|
pr_info("@EPP %s: Tx id: 0x%06X, nego done pwr: %dmW, potential pwr: %dmW, negotiable pwr: %dmW, est K: %d\n",
|
|
__func__, charger->mpp_epp_tx_id, charger->mpp_epp_nego_done_power * 100,
|
|
charger->mpp_epp_tx_potential_load_power * 100,
|
|
charger->mpp_epp_tx_negotiable_load_power * 100, (((K_est[0] << 8) + K_est[1]) * 1000 / 4095));
|
|
|
|
stwlc89_mfc_cmfet_set_mpp_epp_tx_id(charger->cmfet, (charger->mpp_epp_tx_id & 0xFF));
|
|
if (epp_mode(charger->rx_op_mode)) {
|
|
charger->max_power_by_txid = charger->pdata->mpp_epp_def_power;
|
|
charger->current_rx_power = charger->max_power_by_txid / 100000;
|
|
|
|
/* check epp nego power */
|
|
if ((charger->mpp_epp_nego_done_power < TX_RX_POWER_8W) || charger->is_full_status) {
|
|
charger->vrect_by_txid = MFC_AFC_CONF_5V;
|
|
charger->vout_by_txid = WIRELESS_VOUT_5_5V;
|
|
charger->vout_mode = WIRELESS_VOUT_5_5V;
|
|
charger->pdata->vout_status = MFC_VOUT_5_5V;
|
|
} else {
|
|
charger->vrect_by_txid = MFC_AFC_CONF_12_5V_TX;
|
|
charger->vout_by_txid = charger->pdata->epp_vout;
|
|
charger->vout_mode = charger->pdata->epp_vout;
|
|
charger->pdata->vout_status = MFC_VOUT_12V;
|
|
if (charger->sleep_mode)
|
|
charger->vout_mode = WIRELESS_VOUT_5_5V_STEP;
|
|
}
|
|
|
|
mfc_set_epp_mode(charger, charger->mpp_epp_nego_done_power > TX_RX_POWER_8W ?
|
|
TX_RX_POWER_8W : charger->mpp_epp_nego_done_power);
|
|
if ((charger->vout_mode == WIRELESS_VOUT_5_5V_STEP) ||
|
|
(charger->vout_mode == WIRELESS_VOUT_5_5V))
|
|
stwlc89_mfc_fod_set_vout(charger->fod, 5500);
|
|
else
|
|
stwlc89_mfc_fod_set_vout(charger->fod, 12000);
|
|
stwlc89_mfc_fod_set_op_mode(charger->fod, WPC_OP_MODE_EPP);
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS_EPP_FAKE);
|
|
} else { // need to check mag_det ?
|
|
if (mpp_mode(charger->rx_op_mode) && charger->mpp_epp_nego_done_power < TX_RX_POWER_5W)
|
|
mfc_set_mpp_default_current(charger,
|
|
(charger->mpp_epp_nego_done_power * 100 / mfc_get_volt(charger->pdata->mpp_vout)));
|
|
|
|
if (charger->pdata->cable_type != SEC_BATTERY_CABLE_WIRELESS_MPP) {
|
|
charger->vrect_by_txid = MFC_AFC_CONF_12_5V_TX;
|
|
charger->vout_by_txid = charger->pdata->mpp_vout;
|
|
charger->vout_mode = charger->pdata->mpp_vout;
|
|
charger->pdata->vout_status = MFC_VOUT_12V;
|
|
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS_MPP);
|
|
stwlc89_mfc_fod_set_vout(charger->fod, 12000);
|
|
stwlc89_mfc_fod_set_op_mode(charger->fod, WPC_OP_MODE_MPP);
|
|
} else if (stwlc89_get_auth_mode(charger) == WPC_AUTH_MODE_MPP) {
|
|
if (charger->adt_transfer_status >= WIRELESS_AUTH_FAIL) { // Fail or Pass
|
|
pr_info("@MPP @EPP %s: update nego done power: %dmW adt_transfer_status(%d)\n",
|
|
__func__, charger->mpp_epp_nego_done_power, charger->adt_transfer_status);
|
|
mfc_set_epp_mode(charger,
|
|
(charger->adt_transfer_status != WIRELESS_AUTH_PASS) ?
|
|
TX_RX_POWER_8W : charger->mpp_epp_nego_done_power);
|
|
}
|
|
}
|
|
|
|
}
|
|
stwlc89_mfc_fod_set_vendor_id(charger->fod, (charger->mpp_epp_tx_id & 0xFF));
|
|
}
|
|
|
|
static irqreturn_t mfc_wpc_irq_thread(int irq, void *irq_data)
|
|
{
|
|
struct mfc_charger_data *charger = irq_data;
|
|
u8 int_state;
|
|
int ret = 0;
|
|
u8 irq_src[4];
|
|
u8 status[4];
|
|
bool end_irq = false;
|
|
union power_supply_propval value;
|
|
|
|
pr_info("%s: start(%d)!\n", __func__, irq);
|
|
|
|
int_state = gpio_get_value(charger->pdata->wpc_int);
|
|
pr_info("%s: int_state = %d\n", __func__, int_state);
|
|
|
|
if (int_state == 1) {
|
|
pr_info("%s: Falling edge, End up ISR\n", __func__);
|
|
mfc_wpc_check_fod_count(charger, FOD_CNT_ADD);
|
|
__pm_relax(charger->wpc_ws);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
ret = mfc_reg_multi_read(charger->client, MFC_INT_A_0_REG, irq_src, 4);
|
|
if (ret < 0) {
|
|
pr_err("%s: Failed to read INT_A_L_REG: %d\n", __func__, ret);
|
|
__pm_relax(charger->wpc_ws);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
ret = mfc_reg_multi_read(charger->client, MFC_STATUS_0_REG, status, 4);
|
|
if (ret < 0) {
|
|
pr_err("%s: Failed to read STATUS_L_REG: %d\n", __func__, ret);
|
|
__pm_relax(charger->wpc_ws);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
pr_info("%s: interrupt source(0x%.8x), status(0x%.8x)\n",
|
|
__func__, *((u32 *)irq_src), *((u32 *)status));
|
|
|
|
if (irq_src[3] & MFC_INTB_H_PROTOCOL_MASK) {
|
|
if (status[3] & MFC_STAT1_H_PROTOCOL_MASK) {
|
|
int capacity = 0, chg_type = 0;
|
|
u8 sel_protocol = 0;
|
|
|
|
ret = psy_do_property("battery", get, POWER_SUPPLY_PROP_CAPACITY, value);
|
|
if (!ret)
|
|
capacity = value.intval;
|
|
ret = psy_do_property("battery", get, POWER_SUPPLY_PROP_ONLINE, value);
|
|
if (!ret) {
|
|
pr_info("%s: online=%d\n", __func__, value.intval);
|
|
chg_type = is_all_hv_wire_type(value.intval);
|
|
}
|
|
|
|
if (charger->is_otg_on || charger->high_swell || capacity >= 100 || chg_type ||
|
|
charger->is_mpla_thr_recov)
|
|
sel_protocol = 0;
|
|
else if (!charger->mpp_case_det || charger->pdata->mpp_disable)
|
|
sel_protocol = MFC_MPP_PROTOCOL_SELECT_EPP;
|
|
else
|
|
sel_protocol = MFC_MPP_PROTOCOL_SELECT_MPP_FULL;
|
|
mfc_reg_write(charger->client, MFC_MPP_PROTOCOL_SELECT, sel_protocol);
|
|
pr_info("%s: Protocol Select (0x%x) det(%d) otg(%d) swell(%d) capacity(%d) chg_type(%d)\n",
|
|
__func__, sel_protocol, charger->mpp_case_det, charger->is_otg_on,
|
|
charger->high_swell, capacity, chg_type);
|
|
mfc_wpc_check_fod_count(charger, FOD_CNT_ADD);
|
|
}
|
|
}
|
|
|
|
if (irq_src[2] & MFC_INTB_L_MPP_SUPPROT_MASK) {
|
|
if (status[2] & MFC_STAT1_L_MPP_SUPPROT_MASK) {
|
|
pr_info("@MPP %s:Tx pad support MPP IRQ\n", __func__);
|
|
mfc_epp_enable(charger, 1);
|
|
mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS_MPP_FAKE);
|
|
cancel_delayed_work(&charger->wpc_check_mpp_work);
|
|
__pm_stay_awake(charger->check_mpp_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_check_mpp_work, msecs_to_jiffies(3000));
|
|
} else
|
|
pr_info("@MPP %s:Tx pad not support MPP IRQ, Rx will enter EPP mode\n", __func__);
|
|
}
|
|
|
|
if (irq_src[2] & MFC_INTB_L_EPP_SUPPROT_MASK) {
|
|
if (status[2] & MFC_STAT1_L_EPP_SUPPROT_MASK)
|
|
pr_info("@EPP %s:Tx pad support EPP IRQ\n", __func__);
|
|
else
|
|
pr_info("@EPP %s:Tx pad not support EPP IRQ, Rx will enter BPP mode\n", __func__);
|
|
}
|
|
|
|
if (irq_src[2] & MFC_INTB_L_360K_NEGO_PASS_MASK) {
|
|
pr_info("@MPP %s: Rx MPP nego pass IRQ\n", __func__);
|
|
mfc_mpp_epp_nego_done(charger);
|
|
}
|
|
|
|
if (irq_src[2] & MFC_INTB_L_EPP_NEGO_PASS_MASK) {
|
|
pr_info("@EPP %s:Rx EPP nego pass IRQ\n", __func__);
|
|
mfc_mpp_epp_nego_done(charger);
|
|
}
|
|
|
|
if (irq_src[2] & MFC_INTB_L_EPP_NEGO_FAIL_MASK)
|
|
pr_info("@EPP %s:Rx EPP nego fail IRQ\n", __func__);
|
|
|
|
if (irq_src[2] & MFC_INTB_L_INCREASE_POWER_MASK) {
|
|
pr_info("@MPP %s:Rx MPP increase power IRQ\n", __func__);
|
|
value.intval = 1;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_MPP_ICL_CTRL, value);
|
|
}
|
|
|
|
if (irq_src[2] & MFC_INTB_L_DECREASE_POWER_MASK) {
|
|
pr_info("@MPP %s:Rx MPP decrease power IRQ\n", __func__);
|
|
value.intval = 0;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_MPP_ICL_CTRL, value);
|
|
}
|
|
|
|
if (irq_src[2] & MFC_INTB_L_EXIT_CLOAK_MASK) {
|
|
int capacity = 0, chg_type = 0;
|
|
u8 cloak_exit_reason;
|
|
|
|
ret = psy_do_property("battery", get, POWER_SUPPLY_PROP_CAPACITY, value);
|
|
if (!ret)
|
|
capacity = value.intval;
|
|
ret = psy_do_property("battery", get, POWER_SUPPLY_PROP_ONLINE, value);
|
|
if (!ret) {
|
|
pr_info("%s: online=%d\n", __func__, value.intval);
|
|
chg_type = is_all_hv_wire_type(value.intval);
|
|
}
|
|
mfc_epp_enable(charger, !(chg_type || charger->is_otg_on || capacity >= 100));
|
|
mfc_reg_read(charger->client, MFC_MPP_EXIT_CLOAK_REASON_REG, &cloak_exit_reason);
|
|
pr_info("@MPP %s: Exit Cloak IRQ - reason : 0x%x mpp_cloak(%d) otg status: %d capacity(%d) ct(%d)\n",
|
|
__func__, cloak_exit_reason, charger->mpp_cloak, charger->is_otg_on, capacity, chg_type);
|
|
charger->mpp_cloak_status = true;
|
|
value.intval = CLOAK_TURN_OFF_PWR;
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_MPP_CLOAK, value);
|
|
stwlc89_set_force_vout(charger, WIRELESS_VOUT_FORCE_13V);
|
|
}
|
|
|
|
if (irq_src[1] & MFC_INTA_H_AC_MISSING_DET_MASK) {
|
|
pr_info("%s: 1AC Missing ! : MFC is on\n", __func__);
|
|
mfc_mst_routine(charger, irq_src[0], irq_src[1]);
|
|
}
|
|
|
|
if (irq_src[0] & MFC_INTA_L_OP_MODE_MASK) {
|
|
pr_info("%s: MODE CHANGE IRQ !\n", __func__);
|
|
__pm_stay_awake(charger->mode_change_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->mode_change_work, 0);
|
|
}
|
|
|
|
if ((irq_src[0] & MFC_INTA_L_OVER_VOL_MASK) || (irq_src[0] & MFC_INTA_L_OVER_CURR_MASK))
|
|
pr_info("%s: ABNORMAL STAT IRQ !\n", __func__);
|
|
|
|
if (irq_src[0] & MFC_INTA_L_OVER_TEMP_MASK) {
|
|
pr_info("%s: OVER TEMP IRQ !\n", __func__);
|
|
if (charger->wc_tx_enable) {
|
|
value.intval = BATT_TX_EVENT_WIRELESS_RX_UNSAFE_TEMP;
|
|
end_irq = true;
|
|
goto INT_END;
|
|
}
|
|
}
|
|
|
|
if (irq_src[0] & MFC_INTA_L_STAT_VRECT_MASK) {
|
|
int epp_ref_qf = 0, epp_ref_rf = 0;
|
|
|
|
mfc_get_adc(charger, MFC_ADC_VRECT);
|
|
stwlc89_mfc_fod_get_ext(charger->fod, MFC_FOD_EXT_EPP_REF_QF, &epp_ref_qf);
|
|
stwlc89_mfc_fod_get_ext(charger->fod, MFC_FOD_EXT_EPP_REF_RF, &epp_ref_rf);
|
|
pr_info("%s: Vrect IRQ (0x%x, 0x%x)!\n", __func__, epp_ref_qf, epp_ref_rf);
|
|
|
|
if (epp_ref_qf)
|
|
mfc_reg_write(charger->client, MFC_MPP_FOD_QF_REG, epp_ref_qf);
|
|
if (epp_ref_rf)
|
|
mfc_reg_write(charger->client, MFC_MPP_FOD_RF_REG, epp_ref_rf);
|
|
if (charger->is_mst_on == MST_MODE_2 || charger->wc_tx_enable) {
|
|
pr_info("%s: Noise made false Vrect IRQ!\n", __func__);
|
|
if (charger->wc_tx_enable) {
|
|
value.intval = BATT_TX_EVENT_WIRELESS_TX_ETC;
|
|
end_irq = true;
|
|
goto INT_END;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (irq_src[0] & MFC_INTA_L_TXCONFLICT_MASK) {
|
|
pr_info("@Tx_Mode %s: TXCONFLICT IRQ !\n", __func__);
|
|
if (charger->wc_tx_enable && (status[0] & MFC_STAT_L_TXCONFLICT_MASK)) {
|
|
value.intval = BATT_TX_EVENT_WIRELESS_TX_ETC;
|
|
end_irq = true;
|
|
goto INT_END;
|
|
}
|
|
}
|
|
|
|
if (irq_src[1] & MFC_INTA_H_TRX_DATA_RECEIVED_MASK) {
|
|
pr_info("%s: TX RECEIVED IRQ !\n", __func__);
|
|
if (charger->wc_tx_enable && !delayed_work_pending(&charger->wpc_tx_isr_work)) {
|
|
__pm_stay_awake(charger->wpc_tx_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_tx_isr_work, 0);
|
|
} else if (charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND ||
|
|
charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_STAND) {
|
|
pr_info("%s: Don't run ISR_WORK for NO ACK !\n", __func__);
|
|
} else if (!delayed_work_pending(&charger->wpc_isr_work)) {
|
|
__pm_stay_awake(charger->wpc_rx_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_isr_work, 0);
|
|
}
|
|
}
|
|
|
|
if (irq_src[1] & MFC_INTA_H_TX_CON_DISCON_MASK) {
|
|
if (charger->wc_tx_enable) {
|
|
pr_info("@Tx_Mode %s: TX CONNECT IRQ !\n", __func__);
|
|
charger->tx_status = SEC_TX_POWER_TRANSFER;
|
|
if (status[1] & MFC_STAT_H_TX_CON_DISCON_MASK) {
|
|
/* determine rx connection status with tx sharing mode */
|
|
if (!charger->wc_rx_connected) {
|
|
charger->wc_rx_connected = true;
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_rx_connection_work, 0);
|
|
}
|
|
} else {
|
|
/* determine rx connection status with tx sharing mode */
|
|
if (!charger->wc_rx_connected) {
|
|
pr_info("@Tx_Mode %s: Ignore IRQ!! already Rx disconnected!\n", __func__);
|
|
} else {
|
|
charger->wc_rx_connected = false;
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_rx_connection_work, 0);
|
|
}
|
|
}
|
|
} else {
|
|
if (status[0] & MFC_INTA_L_STAT_VRECT_MASK)
|
|
pr_info("%s: VRECT SIGNAL !\n", __func__);
|
|
}
|
|
}
|
|
|
|
/* when rx is not detacted in 3 mins */
|
|
if (irq_src[1] & MFC_INTA_H_TX_MODE_RX_NOT_DET_MASK) {
|
|
if (!(irq_src[1] & MFC_INTA_H_TX_CON_DISCON_MASK)) {
|
|
pr_info("@Tx_Mode %s: Receiver NOT DETECTED IRQ !\n", __func__);
|
|
if (charger->wc_tx_enable) {
|
|
value.intval = BATT_TX_EVENT_WIRELESS_TX_ETC;
|
|
end_irq = true;
|
|
goto INT_END;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (irq_src[1] & MFC_STAT_H_ADT_RECEIVED_MASK) {
|
|
pr_info("%s %s: ADT Received IRQ !\n", WC_AUTH_MSG, __func__);
|
|
mfc_auth_adt_read(charger, ADT_buffer_rdata);
|
|
}
|
|
|
|
if (irq_src[1] & MFC_STAT_H_ADT_SENT_MASK) {
|
|
pr_info("%s %s: ADT Sent successful IRQ !\n", WC_AUTH_MSG, __func__);
|
|
mfc_reg_update(charger->client, MFC_INT_A_ENABLE_1_REG, 0, MFC_STAT_H_ADT_SENT_MASK);
|
|
}
|
|
|
|
if (irq_src[1] & MFC_INTA_H_TX_FOD_MASK) {
|
|
pr_info("%s: TX FOD IRQ !\n", __func__);
|
|
// this code will move to wpc_tx_isr_work
|
|
if (charger->wc_tx_enable) {
|
|
if (status[1] & MFC_STAT_H_TX_FOD_MASK) {
|
|
charger->wc_rx_fod = true;
|
|
value.intval = BATT_TX_EVENT_WIRELESS_TX_FOD;
|
|
end_irq = true;
|
|
goto INT_END;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (irq_src[1] & MFC_INTA_H_TX_OCP_MASK)
|
|
pr_info("%s: TX Over Current IRQ !\n", __func__);
|
|
|
|
INT_END:
|
|
ret = mfc_reg_multi_write(charger->client, MFC_INT_A_CLEAR_0_REG, irq_src, 4); // clear int
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_CLEAR_INT_MASK, MFC_CMD_CLEAR_INT_MASK); // command
|
|
ret = mfc_reg_multi_read(charger->client, MFC_STATUS_0_REG, status, 4);
|
|
pr_info("%s: status after clear irq, status(0x%.8x)\n", __func__, *((u32 *)status));
|
|
|
|
/* tx off should work having done i2c */
|
|
if (end_irq)
|
|
psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ERR, value);
|
|
|
|
pr_info("%s: end!\n", __func__);
|
|
|
|
__pm_relax(charger->wpc_ws);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#if defined(CONFIG_WIRELESS_IC_PARAM)
|
|
static void mfc_parse_param_value(struct mfc_charger_data *charger)
|
|
{
|
|
unsigned int param_value = mfc_get_wrlic();
|
|
|
|
charger->wireless_param_info = param_value;
|
|
charger->wireless_chip_id_param = (param_value & 0xFF000000) >> 24;
|
|
charger->wireless_fw_ver_param = (param_value & 0x00FFFF00) >> 8;
|
|
charger->wireless_fw_mode_param = (param_value & 0x000000F0) >> 4;
|
|
|
|
pr_info("%s: wireless_ic(0x%08X), chip_id(0x%02X), fw_ver(0x%04X), fw_mode(0x%01X)\n",
|
|
__func__, param_value, charger->wireless_chip_id_param,
|
|
charger->wireless_fw_ver_param, charger->wireless_fw_mode_param);
|
|
}
|
|
#endif
|
|
|
|
static int mfc_chg_parse_dt(struct device *dev, mfc_charger_platform_data_t *pdata)
|
|
{
|
|
int ret = 0;
|
|
struct device_node *np = dev->of_node;
|
|
struct device_node *np_battery;
|
|
int len, i;
|
|
const u32 *p;
|
|
|
|
if (!np) {
|
|
pr_err("%s: np NULL\n", __func__);
|
|
return 1;
|
|
}
|
|
ret = of_property_read_string(np,
|
|
"battery,wireless_charger_name", (char const **)&pdata->wireless_charger_name);
|
|
if (ret < 0)
|
|
pr_info("%s: Wireless Charger name is Empty\n", __func__);
|
|
|
|
ret = of_property_read_string(np,
|
|
"battery,charger_name", (char const **)&pdata->wired_charger_name);
|
|
if (ret < 0)
|
|
pr_info("%s: Charger name is Empty\n", __func__);
|
|
|
|
ret = of_property_read_string(np,
|
|
"battery,fuelgauge_name", (char const **)&pdata->fuelgauge_name);
|
|
if (ret < 0)
|
|
pr_info("%s: Fuelgauge name is Empty\n", __func__);
|
|
|
|
ret = of_property_read_u32(np, "battery,phone_fod_thresh1",
|
|
&pdata->phone_fod_thresh1);
|
|
if (ret < 0) {
|
|
pr_info("%s: fail to read phone_fod_thresh1\n", __func__);
|
|
pdata->phone_fod_thresh1 = 0x07D0; /* default 2000mW */
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,phone_fod_ta_thresh",
|
|
&pdata->phone_fod_ta_thresh);
|
|
if (ret < 0) {
|
|
pr_info("%s: fail to read phone_fod_ta_thresh\n", __func__);
|
|
pdata->phone_fod_ta_thresh = 0x0320; /* default 800mW */
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,tx_fod_gain",
|
|
&pdata->tx_fod_gain);
|
|
if (ret < 0) {
|
|
pr_info("%s: fail to read tx_fod_gain\n", __func__);
|
|
pdata->tx_fod_gain = 0x72;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,tx_fod_offset",
|
|
&pdata->tx_fod_offset);
|
|
if (ret < 0) {
|
|
pr_info("%s: fail to read tx_fod_offset\n", __func__);
|
|
pdata->tx_fod_offset = 0xFF9C;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,buds_fod_thresh1",
|
|
&pdata->buds_fod_thresh1);
|
|
if (ret < 0) {
|
|
pr_info("%s: fail to read buds_fod_thresh1\n", __func__);
|
|
pdata->buds_fod_thresh1 = 0x07D0; /* default 2000mW */
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,buds_fod_ta_thresh",
|
|
&pdata->buds_fod_ta_thresh);
|
|
if (ret < 0) {
|
|
pr_info("%s: fail to read buds_fod_ta_thresh\n", __func__);
|
|
pdata->buds_fod_ta_thresh = 0x0320; /* default 800mW */
|
|
}
|
|
|
|
if (ret < 0) {
|
|
pr_info("%s: fail to read cep_timeout\n", __func__);
|
|
pdata->cep_timeout = 0;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,tx_max_op_freq",
|
|
&pdata->tx_max_op_freq);
|
|
if (ret < 0)
|
|
pr_info("%s: fail to read tx_max_op_freq\n", __func__);
|
|
|
|
ret = of_property_read_u32(np, "battery,tx_min_op_freq",
|
|
&pdata->tx_min_op_freq);
|
|
if (ret < 0)
|
|
pr_info("%s: fail to read tx_min_op_freq\n", __func__);
|
|
|
|
ret = of_property_read_u32(np, "battery,tx_conflict_curr",
|
|
&pdata->tx_conflict_curr);
|
|
if (ret < 0) {
|
|
pr_info("%s: fail to read tx_conflict_curr\n", __func__);
|
|
pdata->tx_conflict_curr = 1200;
|
|
}
|
|
|
|
/* wpc_det */
|
|
ret = pdata->wpc_det = sb_of_get_named_gpio_flags(np, "battery,wpc_det");
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: can't get wpc_det\r\n", __func__);
|
|
} else {
|
|
pdata->irq_wpc_det = gpio_to_irq(pdata->wpc_det);
|
|
pr_info("%s: wpc_det = 0x%x, irq_wpc_det = 0x%x\n",
|
|
__func__, pdata->wpc_det, pdata->irq_wpc_det);
|
|
}
|
|
/* wpc_int (This GPIO means MFC_AP_INT) */
|
|
ret = pdata->wpc_int = sb_of_get_named_gpio_flags(np, "battery,wpc_int");
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s: can't wpc_int\r\n", __func__);
|
|
} else {
|
|
pdata->irq_wpc_int = gpio_to_irq(pdata->wpc_int);
|
|
pr_info("%s: wpc_int = 0x%x, irq_wpc_int = 0x%x\n",
|
|
__func__, pdata->wpc_int, pdata->irq_wpc_int);
|
|
}
|
|
|
|
/* mst_pwr_en (MST PWR EN) */
|
|
ret = pdata->mst_pwr_en = sb_of_get_named_gpio_flags(np, "battery,mst_pwr_en");
|
|
if (ret < 0)
|
|
dev_err(dev, "%s: can't mst_pwr_en\r\n", __func__);
|
|
|
|
/* wpc_pdrc (This GPIO means VRECT_INT) */
|
|
ret = pdata->wpc_pdrc = sb_of_get_named_gpio_flags(np, "battery,wpc_pdrc");
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s : can't wpc_pdrc\r\n", __func__);
|
|
} else {
|
|
pdata->irq_wpc_pdrc = gpio_to_irq(pdata->wpc_pdrc);
|
|
pr_info("%s wpc_pdrc = 0x%x, irq_wpc_pdrc = 0x%x\n",
|
|
__func__, pdata->wpc_pdrc, pdata->irq_wpc_pdrc);
|
|
}
|
|
|
|
pdrc_active = 0;
|
|
pdrc_inactive = 1;
|
|
|
|
/* wpc_pdet_b (PDET_B) */
|
|
ret = pdata->wpc_pdet_b = sb_of_get_named_gpio_flags(np, "battery,wpc_pdet_b");
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s : can't wpc_pdet_b\r\n", __func__);
|
|
} else {
|
|
pdata->irq_wpc_pdet_b = gpio_to_irq(pdata->wpc_pdet_b);
|
|
pr_info("%s wpc_pdet_b = 0x%x, irq_wpc_pdet_b = 0x%x\n",
|
|
__func__, pdata->wpc_pdet_b, pdata->irq_wpc_pdet_b);
|
|
}
|
|
|
|
/* wpc_en (MFC EN) */
|
|
ret = pdata->wpc_en = sb_of_get_named_gpio_flags(np, "battery,wpc_en");
|
|
if (ret < 0)
|
|
dev_err(dev, "%s: can't wpc_en\r\n", __func__);
|
|
|
|
/* coil_sw_en (COIL SW EN N) */
|
|
ret = pdata->coil_sw_en = sb_of_get_named_gpio_flags(np, "battery,coil_sw_en");
|
|
if (ret < 0)
|
|
dev_err(dev, "%s: can't coil_sw_en\r\n", __func__);
|
|
|
|
/* ping_nen (PING nEN) */
|
|
ret = pdata->ping_nen = sb_of_get_named_gpio_flags(np, "battery,wpc_ping_nen");
|
|
if (ret < 0)
|
|
dev_err(dev, "%s : can't wpc_ping_nen\r\n", __func__);
|
|
|
|
ret = pdata->mag_det = sb_of_get_named_gpio_flags(np, "battery,wpc_mag_det");
|
|
if (ret < 0)
|
|
dev_err(dev, "%s: can't wpc_mag_det\r\n", __func__);
|
|
|
|
ret = pdata->wpc_mode = sb_of_get_named_gpio_flags(np, "battery,wpc_mode");
|
|
if (ret < 0)
|
|
dev_err(dev, "%s: can't wpc_mode\r\n", __func__);
|
|
|
|
ret = pdata->mpp_sw = sb_of_get_named_gpio_flags(np, "battery,wpc_mpp_sw");
|
|
if (ret < 0)
|
|
dev_err(dev, "%s: can't wpc_mpp_sw\r\n", __func__);
|
|
|
|
p = of_get_property(np, "battery,wireless20_vout_list", &len);
|
|
if (p) {
|
|
len = len / sizeof(u32);
|
|
pdata->len_wc20_list = len;
|
|
pdata->wireless20_vout_list = kcalloc(len, sizeof(*pdata->wireless20_vout_list), GFP_KERNEL);
|
|
ret = of_property_read_u32_array(np, "battery,wireless20_vout_list",
|
|
pdata->wireless20_vout_list, len);
|
|
|
|
for (i = 0; i < len; i++)
|
|
pr_info("%s: wireless20_vout_list = %d ", __func__, pdata->wireless20_vout_list[i]);
|
|
pr_info("%s: len_wc20_list = %d ", __func__, pdata->len_wc20_list);
|
|
} else {
|
|
pr_err("%s: there is no wireless20_vout_list\n", __func__);
|
|
}
|
|
|
|
p = of_get_property(np, "battery,wireless20_vrect_list", &len);
|
|
if (p) {
|
|
len = len / sizeof(u32);
|
|
pdata->wireless20_vrect_list =
|
|
kcalloc(len, sizeof(*pdata->wireless20_vrect_list), GFP_KERNEL);
|
|
ret = of_property_read_u32_array(np, "battery,wireless20_vrect_list",
|
|
pdata->wireless20_vrect_list, len);
|
|
|
|
for (i = 0; i < len; i++)
|
|
pr_info("%s: wireless20_vrect_list = %d ", __func__, pdata->wireless20_vrect_list[i]);
|
|
} else {
|
|
pr_err("%s: there is no wireless20_vrect_list\n", __func__);
|
|
}
|
|
|
|
p = of_get_property(np, "battery,wireless20_max_power_list", &len);
|
|
if (p) {
|
|
len = len / sizeof(u32);
|
|
pdata->wireless20_max_power_list =
|
|
kcalloc(len, sizeof(*pdata->wireless20_max_power_list), GFP_KERNEL);
|
|
ret = of_property_read_u32_array(np, "battery,wireless20_max_power_list",
|
|
pdata->wireless20_max_power_list, len);
|
|
|
|
for (i = 0; i < len; i++)
|
|
pr_info("%s: wireless20_max_power_list = %d\n",
|
|
__func__, pdata->wireless20_max_power_list[i]);
|
|
} else {
|
|
pr_err("%s: there is no wireless20_max_power_list\n", __func__);
|
|
}
|
|
pdata->no_hv =
|
|
of_property_read_bool(np, "battery,wireless_no_hv");
|
|
|
|
ret = of_property_read_u32(np, "battery,wpc_vout_ctrl_full",
|
|
&pdata->wpc_vout_ctrl_full);
|
|
if (ret)
|
|
pr_err("%s: wpc_vout_ctrl_full is Empty\n", __func__);
|
|
|
|
if (pdata->wpc_vout_ctrl_full)
|
|
pdata->wpc_headroom_ctrl_full = of_property_read_bool(np, "battery,wpc_headroom_ctrl_full");
|
|
|
|
pdata->mis_align_guide = of_property_read_bool(np, "battery,mis_align_guide");
|
|
if (pdata->mis_align_guide) {
|
|
ret = of_property_read_u32(np, "battery,mis_align_target_vout",
|
|
&pdata->mis_align_target_vout);
|
|
if (ret)
|
|
pdata->mis_align_target_vout = 5000;
|
|
|
|
ret = of_property_read_u32(np, "battery,mis_align_offset",
|
|
&pdata->mis_align_offset);
|
|
if (ret)
|
|
pdata->mis_align_offset = 200;
|
|
pr_info("%s: mis_align_guide, vout:%d, offset:%d\n", __func__,
|
|
pdata->mis_align_target_vout, pdata->mis_align_offset);
|
|
}
|
|
pdata->unknown_cmb_ctrl = of_property_read_bool(np, "battery,unknown_cmb_ctrl");
|
|
pdata->default_clamp_volt = of_property_read_bool(np, "battery,default_clamp_volt");
|
|
if (pdata->default_clamp_volt)
|
|
pr_info("%s: default_clamp_volt(%d)\n", __func__, pdata->default_clamp_volt);
|
|
|
|
ret = of_property_read_u32(np, "battery,epp_vout",
|
|
&pdata->epp_vout);
|
|
if (ret) {
|
|
pr_err("%s: epp_vout is Empty\n", __func__);
|
|
pdata->epp_vout = WIRELESS_VOUT_12V;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,mpp_vout",
|
|
&pdata->mpp_vout);
|
|
if (ret) {
|
|
pr_err("%s: mpp_vout is Empty\n", __func__);
|
|
pdata->mpp_vout = WIRELESS_VOUT_13V;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,mpp_epp_def_power",
|
|
&pdata->mpp_epp_def_power);
|
|
if (ret) {
|
|
pr_err("%s: mpp_epp_def_power is Empty\n", __func__);
|
|
pdata->mpp_epp_def_power = TX_RX_POWER_5W * 100000;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,mpp_epp_max_count",
|
|
&pdata->mpp_epp_max_count);
|
|
if (ret) {
|
|
pr_err("%s: mpp_epp_max_count is Empty\n", __func__);
|
|
pdata->mpp_epp_max_count = 3;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,epp_time_init",
|
|
&pdata->epp_time_init);
|
|
if (ret) {
|
|
pr_err("%s: epp_time_init is Empty\n", __func__);
|
|
pdata->epp_time_init = 1000;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,epp_time_mid_1",
|
|
&pdata->epp_time_mid_1);
|
|
if (ret) {
|
|
pr_err("%s: epp_time_mid_1 is Empty\n", __func__);
|
|
pdata->epp_time_mid_1 = 1000;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,epp_time_mid_2",
|
|
&pdata->epp_time_mid_2);
|
|
if (ret) {
|
|
pr_err("%s: epp_time_mid_2 is Empty\n", __func__);
|
|
pdata->epp_time_mid_2 = pdata->epp_time_mid_1;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,epp_time_final",
|
|
&pdata->epp_time_final);
|
|
if (ret) {
|
|
pr_err("%s: epp_time_final is Empty\n", __func__);
|
|
pdata->epp_time_final = 4000;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "battery,fod_cnt_thresh", &pdata->fod_cnt_thresh);
|
|
if (ret) {
|
|
pr_err("%s: fod_cnt_thresh is Empty\n", __func__);
|
|
pdata->fod_cnt_thresh = 5;
|
|
}
|
|
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
pdata->mpp_disable = false;
|
|
#else
|
|
pdata->mpp_disable = of_property_read_bool(np, "battery,mpp_disable");
|
|
#endif
|
|
|
|
if (carrierid_is("XAC")) {
|
|
pr_info("%s: use cep_timeout_xac\n", __func__);
|
|
ret = of_property_read_u32(np, "battery,cep_timeout_xac", &pdata->tx_cep_timeout);
|
|
} else {
|
|
ret = of_property_read_u32(np, "battery,cep_timeout", &pdata->tx_cep_timeout);
|
|
}
|
|
|
|
if (ret < 0) {
|
|
pr_info("%s: fail to read cep_timeout\n", __func__);
|
|
pdata->tx_cep_timeout = 1800; // 1.8s
|
|
}
|
|
|
|
p = of_get_property(np, "battery,opfreq_ctrl_pad_list", &len);
|
|
if (p) {
|
|
len = len / sizeof(u32);
|
|
pdata->opfreq_ctrl_pad_list_size = len;
|
|
pdata->opfreq_ctrl_pad_list = kcalloc(len, sizeof(*pdata->opfreq_ctrl_pad_list), GFP_KERNEL);
|
|
ret = of_property_read_u32_array(np, "battery,opfreq_ctrl_pad_list", pdata->opfreq_ctrl_pad_list, len);
|
|
|
|
for (i = 0; i < len; i++)
|
|
pr_info("%s: opfreq_ctrl_pad_list = %d ", __func__, pdata->opfreq_ctrl_pad_list[i]);
|
|
} else {
|
|
pdata->opfreq_ctrl_pad_list_size = 0;
|
|
pdata->opfreq_ctrl_pad_list = NULL;
|
|
pr_err("%s: there is no opfreq_ctrl_pad_list\n", __func__);
|
|
}
|
|
|
|
np = dev->of_node;
|
|
|
|
np_battery = of_find_node_by_name(NULL, "battery");
|
|
if (!np_battery) {
|
|
dev_err(dev, "%s: empty of_node for battery\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
ret = of_property_read_u32(np_battery, "battery,wpc_temp_v2_cond",
|
|
&pdata->high_swell_recov);
|
|
if (ret) {
|
|
dev_info(dev, "%s: battery,wpc_temp_v2_cond is Empty\n", __func__);
|
|
pdata->high_swell_recov = 390;
|
|
}
|
|
pr_info("%s: high_swell_recov(%d)\n", __func__, pdata->high_swell_recov);
|
|
|
|
pdata->hall_ic_notifier = of_property_read_bool(np_battery,
|
|
"battery,hall_ic_notifier");
|
|
pr_info("%s: support hall_ic_notifier(%d)\n", __func__, pdata->hall_ic_notifier);
|
|
return 0;
|
|
}
|
|
|
|
static int mfc_create_attrs(struct device *dev)
|
|
{
|
|
int i, rc;
|
|
|
|
for (i = 0; i < (int)ARRAY_SIZE(mfc_attrs); i++) {
|
|
rc = device_create_file(dev, &mfc_attrs[i]);
|
|
if (rc)
|
|
goto create_attrs_failed;
|
|
}
|
|
return rc;
|
|
|
|
create_attrs_failed:
|
|
dev_err(dev, "%s: failed (%d)\n", __func__, rc);
|
|
while (i--)
|
|
device_remove_file(dev, &mfc_attrs[i]);
|
|
return rc;
|
|
}
|
|
|
|
ssize_t stwlc89_show_attrs(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct power_supply *psy = dev_get_drvdata(dev);
|
|
struct mfc_charger_data *charger = power_supply_get_drvdata(psy);
|
|
|
|
const ptrdiff_t offset = attr - mfc_attrs;
|
|
int i = 0;
|
|
|
|
dev_info(charger->dev, "%s\n", __func__);
|
|
|
|
switch (offset) {
|
|
case MFC_ADDR:
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "0x%x\n", charger->addr);
|
|
break;
|
|
case MFC_SIZE:
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "0x%x\n", charger->size);
|
|
break;
|
|
case MFC_DATA:
|
|
if (charger->size == 0) {
|
|
charger->size = 1;
|
|
} else if (charger->size + charger->addr <= 0xFFFF) {
|
|
u8 data;
|
|
int j;
|
|
|
|
for (j = 0; j < charger->size; j++) {
|
|
if (mfc_reg_read(charger->client, charger->addr + j, &data) < 0) {
|
|
dev_info(charger->dev, "%s: read fail\n", __func__);
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "addr: 0x%x read fail\n",
|
|
charger->addr + j);
|
|
continue;
|
|
}
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "addr: 0x%x, data: 0x%x\n",
|
|
charger->addr + j, data);
|
|
}
|
|
}
|
|
break;
|
|
case MFC_PACKET:
|
|
break;
|
|
case MFC_EPP_T:
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "epp_time: i:%d, m1:%d, m2:%d, f:%d\n",
|
|
charger->pdata->epp_time_init, charger->pdata->epp_time_mid_1,
|
|
charger->pdata->epp_time_mid_2, charger->pdata->epp_time_final);
|
|
dev_info(charger->dev, "%s epp_time: i:%d, m1:%d, m2:%d, f:%d\n", __func__,
|
|
charger->pdata->epp_time_init, charger->pdata->epp_time_mid_1,
|
|
charger->pdata->epp_time_mid_2, charger->pdata->epp_time_final);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
ssize_t stwlc89_store_attrs(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct power_supply *psy = dev_get_drvdata(dev);
|
|
struct mfc_charger_data *charger = power_supply_get_drvdata(psy);
|
|
const ptrdiff_t offset = attr - mfc_attrs;
|
|
int x, ret;
|
|
u8 header, data_com, data_val;
|
|
|
|
dev_info(charger->dev, "%s\n", __func__);
|
|
|
|
switch (offset) {
|
|
case MFC_ADDR:
|
|
if (sscanf(buf, "0x%4x\n", &x) == 1)
|
|
charger->addr = x;
|
|
ret = count;
|
|
break;
|
|
case MFC_SIZE:
|
|
if (sscanf(buf, "%5d\n", &x) == 1)
|
|
charger->size = x;
|
|
ret = count;
|
|
break;
|
|
case MFC_DATA:
|
|
if (sscanf(buf, "0x%10x", &x) == 1) {
|
|
u8 data = x;
|
|
|
|
if (mfc_reg_write(charger->client, charger->addr, data) < 0)
|
|
dev_info(charger->dev, "%s: addr: 0x%x write fail\n", __func__, charger->addr);
|
|
}
|
|
ret = count;
|
|
break;
|
|
case MFC_PACKET:
|
|
{
|
|
u32 header_temp, data_com_temp, data_val_temp;
|
|
|
|
if (sscanf(buf, "0x%4x 0x%4x 0x%4x\n", &header_temp, &data_com_temp, &data_val_temp) == 3) {
|
|
header = (u8)header_temp;
|
|
data_com = (u8)data_com_temp;
|
|
data_val = (u8)data_val_temp;
|
|
dev_info(charger->dev, "%s 0x%x, 0x%x, 0x%x\n", __func__, header, data_com, data_val);
|
|
mfc_send_packet(charger, header, data_com, &data_val, 1);
|
|
}
|
|
ret = count;
|
|
}
|
|
break;
|
|
case MFC_EPP_T:
|
|
{
|
|
int init_epp_time, mid_epp_time_1, mid_epp_time_2, final_epp_time;
|
|
|
|
if (sscanf(buf,
|
|
"%10d %10d %10d %10d\n",
|
|
&init_epp_time, &mid_epp_time_1, &mid_epp_time_2, &final_epp_time) > 0) {
|
|
charger->pdata->epp_time_init = init_epp_time;
|
|
charger->pdata->epp_time_mid_1 = mid_epp_time_1;
|
|
charger->pdata->epp_time_mid_2 = mid_epp_time_2;
|
|
charger->pdata->epp_time_final = final_epp_time;
|
|
dev_info(charger->dev, "%s epp_time: i:%d, m1:%d, m2:%d, f:%d\n", __func__,
|
|
charger->pdata->epp_time_init, charger->pdata->epp_time_mid_1,
|
|
charger->pdata->epp_time_mid_2, charger->pdata->epp_time_final);
|
|
}
|
|
ret = count;
|
|
}
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static const struct power_supply_desc mfc_charger_power_supply_desc = {
|
|
.name = "mfc-charger",
|
|
.type = POWER_SUPPLY_TYPE_UNKNOWN,
|
|
.properties = mfc_charger_props,
|
|
.num_properties = ARRAY_SIZE(mfc_charger_props),
|
|
.get_property = stwlc89_chg_get_property,
|
|
.set_property = stwlc89_chg_set_property,
|
|
};
|
|
|
|
static void mfc_wpc_int_req_work(struct work_struct *work)
|
|
{
|
|
struct mfc_charger_data *charger =
|
|
container_of(work, struct mfc_charger_data, wpc_int_req_work.work);
|
|
|
|
int ret = 0;
|
|
|
|
pr_info("%s\n", __func__);
|
|
/* wpc_irq */
|
|
if (charger->pdata->irq_wpc_int) {
|
|
msleep(100);
|
|
ret = request_threaded_irq(charger->pdata->irq_wpc_int,
|
|
mfc_wpc_irq_handler, mfc_wpc_irq_thread,
|
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
|
"wpc-irq", charger);
|
|
if (ret)
|
|
pr_err("%s: Failed to Request IRQ\n", __func__);
|
|
}
|
|
if (ret < 0)
|
|
free_irq(charger->pdata->irq_wpc_det, NULL);
|
|
}
|
|
|
|
static enum alarmtimer_restart mfc_phm_alarm(
|
|
struct alarm *alarm, ktime_t now)
|
|
{
|
|
struct mfc_charger_data *charger = container_of(alarm,
|
|
struct mfc_charger_data, phm_alarm);
|
|
|
|
pr_info("%s: forced escape to PHM\n", __func__);
|
|
__pm_stay_awake(charger->wpc_tx_phm_ws);
|
|
if (charger->is_suspend)
|
|
charger->skip_phm_work_in_sleep = true;
|
|
else
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_tx_phm_work, 0);
|
|
|
|
return ALARMTIMER_NORESTART;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0))
|
|
static int stwlc89_charger_probe(struct i2c_client *client)
|
|
#else
|
|
static int stwlc89_charger_probe(
|
|
struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
#endif
|
|
{
|
|
struct device_node *of_node = client->dev.of_node;
|
|
struct mfc_charger_data *charger;
|
|
mfc_charger_platform_data_t *pdata = client->dev.platform_data;
|
|
struct power_supply_config mfc_cfg = {};
|
|
int ret = 0;
|
|
u8 int_state;
|
|
|
|
dev_info(&client->dev,
|
|
"%s: MFC stwlc89 Charger Driver Loading\n", __func__);
|
|
|
|
if (of_node) {
|
|
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
|
|
if (!pdata)
|
|
return -ENOMEM;
|
|
|
|
ret = mfc_chg_parse_dt(&client->dev, pdata);
|
|
if (ret < 0)
|
|
goto err_parse_dt;
|
|
} else {
|
|
pdata = client->dev.platform_data;
|
|
return -ENOMEM;
|
|
}
|
|
|
|
charger = kcalloc(1, sizeof(*charger), GFP_KERNEL);
|
|
if (charger == NULL) {
|
|
ret = -ENOMEM;
|
|
goto err_wpc_nomem;
|
|
}
|
|
charger->dev = &client->dev;
|
|
|
|
ret = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
|
|
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK);
|
|
if (!ret) {
|
|
ret = i2c_get_functionality(client->adapter);
|
|
dev_err(charger->dev, "I2C functionality is not supported.\n");
|
|
ret = -ENODEV;
|
|
goto err_i2cfunc_not_support;
|
|
}
|
|
|
|
is_shutdn = false;
|
|
charger->client = client;
|
|
charger->pdata = pdata;
|
|
|
|
pr_info("%s: %s\n", __func__, charger->pdata->wireless_charger_name);
|
|
|
|
i2c_set_clientdata(client, charger);
|
|
#if defined(CONFIG_WIRELESS_IC_PARAM)
|
|
mfc_parse_param_value(charger);
|
|
#endif
|
|
|
|
charger->fod = stwlc89_mfc_fod_init(charger->dev, MFC_NUM_FOD_REG, stwlc89_set_fod);
|
|
if (IS_ERR(charger->fod))
|
|
pr_err("%s: failed to init fod (ret = %ld)\n", __func__, PTR_ERR(charger->fod));
|
|
charger->cmfet = stwlc89_mfc_cmfet_init(charger->dev, stwlc89_set_cmfet);
|
|
if (IS_ERR(charger->cmfet))
|
|
pr_err("%s: failed to init cmfet (ret = %ld)\n", __func__, PTR_ERR(charger->cmfet));
|
|
|
|
charger->pdata->cable_type = SEC_BATTERY_CABLE_NONE;
|
|
charger->pdata->is_charging = 0;
|
|
charger->tx_status = 0;
|
|
charger->pdata->cs100_status = 0;
|
|
charger->pdata->vout_status = MFC_VOUT_5V;
|
|
charger->pdata->opfq_cnt = 0;
|
|
charger->flicker_delay = 1000;
|
|
charger->flicker_vout_threshold = MFC_VOUT_8V;
|
|
charger->is_mst_on = MST_MODE_0;
|
|
charger->chip_id = MFC_CHIP_STM;
|
|
charger->is_otg_on = false;
|
|
charger->led_cover = 0;
|
|
charger->vout_mode = WIRELESS_VOUT_OFF;
|
|
charger->is_full_status = 0;
|
|
charger->is_afc_tx = false;
|
|
charger->pad_ctrl_by_lcd = false;
|
|
charger->wc_tx_enable = false;
|
|
charger->initial_wc_check = false;
|
|
charger->wc_rx_connected = false;
|
|
charger->wc_rx_fod = false;
|
|
charger->adt_transfer_status = WIRELESS_AUTH_WAIT;
|
|
charger->current_rx_power = TX_RX_POWER_0W;
|
|
charger->tx_id = WC_PAD_UNKNOWN;
|
|
charger->tx_id_cnt = 0;
|
|
charger->wc_ldo_status = MFC_LDO_ON;
|
|
charger->tx_id_done = false;
|
|
charger->wc_rx_type = NO_DEV;
|
|
charger->is_suspend = false;
|
|
charger->device_event = 0;
|
|
charger->duty_min = MIN_DUTY_SETTING_20_DATA;
|
|
charger->wpc_en_flag = (WPC_EN_SYSFS | WPC_EN_CHARGING | WPC_EN_CCIC | WPC_EN_SLATE);
|
|
charger->req_tx_id = false;
|
|
charger->afc_tx_done = false;
|
|
charger->sleep_mode = false;
|
|
charger->req_afc_delay = REQ_AFC_DLY;
|
|
charger->mis_align_tx_try_cnt = 1;
|
|
charger->wc_checking_align = false;
|
|
charger->wc_align_check_start.tv_sec = 0;
|
|
charger->vout_strength = 100;
|
|
charger->reg_access_lock = false;
|
|
charger->det_state = 0;
|
|
charger->pdrc_state = pdrc_inactive;
|
|
charger->fw_cmd = SEC_WIRELESS_FW_UPDATE_AUTO_MODE;
|
|
charger->mpp_epp_tx_id = 0;
|
|
charger->mpp_epp_nego_done_power = 0;
|
|
charger->mpp_epp_tx_potential_load_power = 0;
|
|
charger->mpp_epp_tx_negotiable_load_power = 0;
|
|
charger->mpp_cloak = 0;
|
|
charger->mpp_case_det = 0;
|
|
charger->mpp_case = 0;
|
|
charger->target_current = 0;
|
|
charger->thermal_ctrl = 0;
|
|
charger->high_swell = false;
|
|
charger->init_cover = true;
|
|
charger->fod_cnt = 0;
|
|
charger->rx_phm_check_cnt = 0;
|
|
|
|
mutex_init(&charger->io_lock);
|
|
mutex_init(&charger->wpc_en_lock);
|
|
mutex_init(&charger->fw_lock);
|
|
|
|
charger->wqueue = create_singlethread_workqueue("mfc_workqueue");
|
|
if (!charger->wqueue) {
|
|
pr_err("%s: Fail to Create Workqueue\n", __func__);
|
|
goto err_pdata_free;
|
|
}
|
|
|
|
charger->wpc_ws = wakeup_source_register(&client->dev, "wpc_ws");
|
|
charger->wpc_det_ws = wakeup_source_register(&client->dev, "wpc_det_ws");
|
|
charger->wpc_rx_ws = wakeup_source_register(&client->dev, "wpc_rx_ws");
|
|
charger->wpc_tx_ws = wakeup_source_register(&client->dev, "wpc_tx_ws");
|
|
charger->wpc_update_ws = wakeup_source_register(&client->dev, "wpc_update_ws");
|
|
charger->wpc_tx_duty_min_ws = wakeup_source_register(&client->dev, "wpc_tx_duty_min_ws");
|
|
charger->wpc_afc_vout_ws = wakeup_source_register(&client->dev, "wpc_afc_vout_ws");
|
|
charger->wpc_vout_mode_ws = wakeup_source_register(&client->dev, "wpc_vout_mode_ws");
|
|
charger->wpc_rx_det_ws = wakeup_source_register(&client->dev, "wpc_rx_det_ws");
|
|
charger->wpc_tx_phm_ws = wakeup_source_register(&client->dev, "wpc_tx_phm_ws");
|
|
charger->wpc_tx_id_ws = wakeup_source_register(&client->dev, "wpc_tx_id_ws");
|
|
charger->wpc_tx_pwr_budg_ws = wakeup_source_register(&client->dev, "wpc_tx_pwr_budg_ws");
|
|
charger->wpc_pdrc_ws = wakeup_source_register(&client->dev, "wpc_pdrc_ws");
|
|
charger->align_check_ws = wakeup_source_register(&client->dev, "align_check_ws");
|
|
charger->mode_change_ws = wakeup_source_register(&client->dev, "mode_change_ws");
|
|
charger->wpc_cs100_ws = wakeup_source_register(&client->dev, "wpc_cs100_ws");
|
|
charger->wpc_rx_power_trans_fail_ws = wakeup_source_register(&client->dev, "wpc_rx_power_trans_fail_ws");
|
|
charger->wpc_pdet_b_ws = wakeup_source_register(&client->dev, "wpc_pdet_b_ws");
|
|
charger->wpc_rx_phm_ws = wakeup_source_register(&client->dev, "wpc_rx_phm_ws");
|
|
charger->wpc_phm_exit_ws = wakeup_source_register(&client->dev, "wpc_phm_exit_ws");
|
|
charger->epp_clear_ws = wakeup_source_register(&client->dev, "epp_clear_ws");
|
|
charger->epp_count_ws = wakeup_source_register(&client->dev, "epp_count_ws");
|
|
charger->check_mpp_ws = wakeup_source_register(&client->dev, "check_mpp_ws");
|
|
charger->set_mpp_cover_ws = wakeup_source_register(&client->dev, "set_mpp_cover_ws");
|
|
charger->set_mpp_cloak_ws = wakeup_source_register(&client->dev, "set_mpp_cloak_ws");
|
|
charger->mpla_thr_recov_ws = wakeup_source_register(&client->dev, "mpla_thr_recov_ws");
|
|
|
|
/* wpc_det */
|
|
if (charger->pdata->irq_wpc_det) {
|
|
INIT_DELAYED_WORK(&charger->wpc_det_work, mfc_wpc_det_work);
|
|
}
|
|
|
|
/* wpc_irq (INT_A) */
|
|
if (charger->pdata->irq_wpc_int) {
|
|
INIT_DELAYED_WORK(&charger->wpc_isr_work, mfc_wpc_isr_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_tx_isr_work, mfc_wpc_tx_isr_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_tx_id_work, mfc_wpc_tx_id_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_tx_pwr_budg_work, mfc_wpc_tx_pwr_budg_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_int_req_work, mfc_wpc_int_req_work);
|
|
}
|
|
INIT_DELAYED_WORK(&charger->wpc_vout_mode_work, mfc_wpc_vout_mode_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_afc_vout_work, mfc_wpc_afc_vout_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_fw_update_work, mfc_wpc_fw_update_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_i2c_error_work, mfc_wpc_i2c_error_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_rx_type_det_work, mfc_wpc_rx_type_det_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_rx_connection_work, mfc_wpc_rx_connection_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_tx_duty_min_work, mfc_tx_duty_min_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_tx_phm_work, mfc_tx_phm_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_rx_power_work, mfc_wpc_rx_power_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_init_work, mfc_wpc_init_work);
|
|
INIT_DELAYED_WORK(&charger->align_check_work, mfc_wpc_align_check_work);
|
|
INIT_DELAYED_WORK(&charger->mode_change_work, mfc_wpc_mode_change_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_cs100_work, mfc_cs100_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_rx_power_trans_fail_work, mfc_rx_power_trans_fail_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_rx_phm_work, mfc_rx_phm_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_deactivate_work, mfc_wpc_deactivate_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_phm_exit_work, mfc_wpc_phm_exit_work);
|
|
INIT_DELAYED_WORK(&charger->epp_clear_timer_work, mfc_epp_clear_timer_work);
|
|
INIT_DELAYED_WORK(&charger->epp_count_work, mfc_epp_count_work);
|
|
INIT_DELAYED_WORK(&charger->wpc_check_mpp_work, mfc_wpc_check_mpp_work);
|
|
INIT_DELAYED_WORK(&charger->set_mpp_cover_work, mfc_set_mpp_cover_work);
|
|
INIT_DELAYED_WORK(&charger->set_mpp_cloak_work, mfc_set_mpp_cloak_work);
|
|
INIT_DELAYED_WORK(&charger->mpla_thr_recov_work, mfc_clear_mpla_thr_recov_work);
|
|
|
|
/*
|
|
* Default Idle voltage of the INT_A is LOW.
|
|
* Prevent the un-wanted INT_A Falling handling.
|
|
* This is a work-around, and will be fixed by the revision.
|
|
*/
|
|
//INIT_DELAYED_WORK(&charger->mst_off_work, mfc_mst_off_work);
|
|
|
|
alarm_init(&charger->phm_alarm, ALARM_BOOTTIME, mfc_phm_alarm);
|
|
|
|
mfc_cfg.drv_data = charger;
|
|
charger->psy_chg = power_supply_register(charger->dev, &mfc_charger_power_supply_desc, &mfc_cfg);
|
|
if (IS_ERR(charger->psy_chg)) {
|
|
ret = PTR_ERR(charger->psy_chg);
|
|
pr_err("%s: Failed to Register psy_chg(%d)\n", __func__, ret);
|
|
goto err_supply_unreg;
|
|
}
|
|
|
|
ret = mfc_create_attrs(&charger->psy_chg->dev);
|
|
if (ret) {
|
|
dev_err(charger->dev,
|
|
"%s : Failed to create_attrs\n", __func__);
|
|
}
|
|
|
|
/* Enable interrupts after battery driver load */
|
|
/* wpc_det */
|
|
if (charger->pdata->irq_wpc_det) {
|
|
ret = request_threaded_irq(charger->pdata->irq_wpc_det,
|
|
mfc_wpc_irq_handler, mfc_wpc_det_irq_thread,
|
|
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
|
|
IRQF_ONESHOT,
|
|
"wpc-det-irq", charger);
|
|
if (ret) {
|
|
pr_err("%s: Failed to Request IRQ\n", __func__);
|
|
goto err_irq_wpc_det;
|
|
}
|
|
}
|
|
|
|
/* wpc_pdrc */
|
|
if (charger->pdata->irq_wpc_pdrc) {
|
|
INIT_DELAYED_WORK(&charger->wpc_pdrc_work, mfc_wpc_pdrc_work);
|
|
ret = request_threaded_irq(charger->pdata->irq_wpc_pdrc,
|
|
mfc_wpc_irq_handler, mfc_wpc_pdrc_irq_thread,
|
|
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
|
|
IRQF_ONESHOT,
|
|
"wpc-pdrc-irq", charger);
|
|
if (ret) {
|
|
pr_err("%s: Failed to Request pdrc IRQ\n", __func__);
|
|
goto err_irq_wpc_det;
|
|
}
|
|
}
|
|
|
|
/* wpc_pdet_b */
|
|
if (charger->pdata->irq_wpc_pdet_b) {
|
|
ret = request_threaded_irq(charger->pdata->irq_wpc_pdet_b,
|
|
mfc_wpc_irq_handler, mfc_wpc_pdet_b_irq_thread,
|
|
IRQF_TRIGGER_RISING |
|
|
IRQF_ONESHOT,
|
|
"wpc-pdet-b-irq", charger);
|
|
if (ret) {
|
|
pr_err("%s: Failed to Request pdet_b IRQ\n", __func__);
|
|
goto err_irq_wpc_det;
|
|
}
|
|
}
|
|
|
|
/* wpc_irq */
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_int_req_work, msecs_to_jiffies(100));
|
|
|
|
int_state = gpio_get_value(charger->pdata->wpc_int);
|
|
pr_info("%s: int_state = %d\n", __func__, int_state);
|
|
if (gpio_get_value(charger->pdata->wpc_det)) {
|
|
u8 irq_src[2];
|
|
|
|
pr_info("%s: Charger interrupt occurred during lpm\n", __func__);
|
|
charger->det_state = gpio_get_value(charger->pdata->wpc_det);
|
|
|
|
mfc_reg_read(charger->client, MFC_INT_A_0_REG, &irq_src[0]);
|
|
mfc_reg_read(charger->client, MFC_INT_A_1_REG, &irq_src[1]);
|
|
/* clear interrupt */
|
|
pr_info("%s: interrupt source(0x%x)\n", __func__, irq_src[1] << 8 | irq_src[0]);
|
|
mfc_reg_write(charger->client, MFC_INT_A_CLEAR_0_REG, irq_src[0]); // clear int
|
|
mfc_reg_write(charger->client, MFC_INT_A_CLEAR_1_REG, irq_src[1]); // clear int
|
|
mfc_set_cmd_l_reg(charger, MFC_CMD_CLEAR_INT_MASK, MFC_CMD_CLEAR_INT_MASK); // command
|
|
if (charger->pdata->wired_charger_name) {
|
|
union power_supply_propval value;
|
|
|
|
ret = psy_do_property(charger->pdata->wired_charger_name, get,
|
|
POWER_SUPPLY_EXT_PROP_INPUT_CURRENT_LIMIT_WRL, value);
|
|
charger->input_current = (ret) ? 500 : value.intval;
|
|
pr_info("%s: updated input current (%d)\n",
|
|
__func__, charger->input_current);
|
|
}
|
|
charger->req_afc_delay = 0;
|
|
__pm_stay_awake(charger->wpc_det_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_det_work, 0);
|
|
if (!int_state && !delayed_work_pending(&charger->wpc_isr_work)) {
|
|
__pm_stay_awake(charger->wpc_rx_ws);
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_isr_work, msecs_to_jiffies(2000));
|
|
}
|
|
}
|
|
|
|
sec_chg_set_dev_init(SC_DEV_WRL_CHG);
|
|
ret = sb_wireless_set_op(charger, &stwlc89_sbw_op);
|
|
|
|
stwlc89_debug_proc_init(charger);
|
|
|
|
dev_info(&client->dev, "%s: MFC stwlc89 Charger Driver Loaded(op = %d)\n", __func__, ret);
|
|
device_init_wakeup(charger->dev, 1);
|
|
return 0;
|
|
|
|
err_irq_wpc_det:
|
|
power_supply_unregister(charger->psy_chg);
|
|
err_supply_unreg:
|
|
wakeup_source_unregister(charger->wpc_ws);
|
|
wakeup_source_unregister(charger->wpc_det_ws);
|
|
wakeup_source_unregister(charger->wpc_rx_ws);
|
|
wakeup_source_unregister(charger->wpc_tx_ws);
|
|
wakeup_source_unregister(charger->wpc_update_ws);
|
|
wakeup_source_unregister(charger->wpc_tx_duty_min_ws);
|
|
wakeup_source_unregister(charger->wpc_afc_vout_ws);
|
|
wakeup_source_unregister(charger->wpc_vout_mode_ws);
|
|
wakeup_source_unregister(charger->wpc_rx_det_ws);
|
|
wakeup_source_unregister(charger->wpc_tx_phm_ws);
|
|
wakeup_source_unregister(charger->wpc_tx_id_ws);
|
|
wakeup_source_unregister(charger->wpc_pdrc_ws);
|
|
wakeup_source_unregister(charger->wpc_cs100_ws);
|
|
wakeup_source_unregister(charger->wpc_pdet_b_ws);
|
|
wakeup_source_unregister(charger->wpc_rx_phm_ws);
|
|
wakeup_source_unregister(charger->wpc_phm_exit_ws);
|
|
wakeup_source_unregister(charger->epp_clear_ws);
|
|
wakeup_source_unregister(charger->check_mpp_ws);
|
|
wakeup_source_unregister(charger->set_mpp_cover_ws);
|
|
wakeup_source_unregister(charger->set_mpp_cloak_ws);
|
|
wakeup_source_unregister(charger->mpla_thr_recov_ws);
|
|
err_pdata_free:
|
|
mutex_destroy(&charger->io_lock);
|
|
mutex_destroy(&charger->wpc_en_lock);
|
|
mutex_destroy(&charger->fw_lock);
|
|
err_i2cfunc_not_support:
|
|
kfree(charger);
|
|
err_wpc_nomem:
|
|
err_parse_dt:
|
|
devm_kfree(&client->dev, pdata);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
|
|
static int stwlc89_charger_remove(struct i2c_client *client)
|
|
#else
|
|
static void stwlc89_charger_remove(struct i2c_client *client)
|
|
#endif
|
|
{
|
|
struct mfc_charger_data *charger = i2c_get_clientdata(client);
|
|
|
|
alarm_cancel(&charger->phm_alarm);
|
|
stwlc89_debug_proc_remove();
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
#if defined(CONFIG_PM)
|
|
static int mfc_charger_suspend(struct device *dev)
|
|
{
|
|
struct mfc_charger_data *charger = dev_get_drvdata(dev);
|
|
|
|
pr_info("%s: det(%d) int(%d)\n", __func__,
|
|
gpio_get_value(charger->pdata->wpc_det),
|
|
gpio_get_value(charger->pdata->wpc_int));
|
|
|
|
charger->is_suspend = true;
|
|
|
|
if (device_may_wakeup(charger->dev)) {
|
|
enable_irq_wake(charger->pdata->irq_wpc_int);
|
|
enable_irq_wake(charger->pdata->irq_wpc_det);
|
|
if (charger->pdata->irq_wpc_pdrc)
|
|
enable_irq_wake(charger->pdata->irq_wpc_pdrc);
|
|
if (charger->pdata->irq_wpc_pdet_b)
|
|
enable_irq_wake(charger->pdata->irq_wpc_pdet_b);
|
|
}
|
|
#if !IS_ENABLED(CONFIG_ENABLE_WIRELESS_IRQ_IN_SLEEP)
|
|
disable_irq(charger->pdata->irq_wpc_int);
|
|
disable_irq(charger->pdata->irq_wpc_det);
|
|
if (charger->pdata->irq_wpc_pdrc)
|
|
disable_irq(charger->pdata->irq_wpc_pdrc);
|
|
if (charger->pdata->irq_wpc_pdet_b)
|
|
disable_irq(charger->pdata->irq_wpc_pdet_b);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mfc_charger_resume(struct device *dev)
|
|
{
|
|
struct mfc_charger_data *charger = dev_get_drvdata(dev);
|
|
|
|
pr_info("%s: det(%d) int(%d)\n", __func__,
|
|
gpio_get_value(charger->pdata->wpc_det),
|
|
gpio_get_value(charger->pdata->wpc_int));
|
|
|
|
charger->is_suspend = false;
|
|
|
|
if (device_may_wakeup(charger->dev)) {
|
|
disable_irq_wake(charger->pdata->irq_wpc_int);
|
|
disable_irq_wake(charger->pdata->irq_wpc_det);
|
|
if (charger->pdata->irq_wpc_pdrc)
|
|
disable_irq_wake(charger->pdata->irq_wpc_pdrc);
|
|
if (charger->pdata->irq_wpc_pdet_b)
|
|
disable_irq_wake(charger->pdata->irq_wpc_pdet_b);
|
|
}
|
|
#if !IS_ENABLED(CONFIG_ENABLE_WIRELESS_IRQ_IN_SLEEP)
|
|
enable_irq(charger->pdata->irq_wpc_int);
|
|
enable_irq(charger->pdata->irq_wpc_det);
|
|
if (charger->pdata->irq_wpc_pdrc)
|
|
enable_irq(charger->pdata->irq_wpc_pdrc);
|
|
if (charger->pdata->irq_wpc_pdet_b)
|
|
enable_irq(charger->pdata->irq_wpc_pdet_b);
|
|
#else
|
|
/* Level triggering makes infinite IRQ, Edge triggering is required */
|
|
__pm_stay_awake(charger->wpc_ws);
|
|
__pm_stay_awake(charger->wpc_det_ws);
|
|
mfc_wpc_irq_thread(0, charger);
|
|
if (charger->pdata->irq_wpc_pdrc) {
|
|
__pm_stay_awake(charger->wpc_pdrc_ws);
|
|
mfc_wpc_pdrc_irq_thread(0, charger);
|
|
}
|
|
if (charger->pdata->irq_wpc_pdet_b) {
|
|
__pm_stay_awake(charger->wpc_pdet_b_ws);
|
|
mfc_wpc_pdet_b_irq_thread(0, charger);
|
|
}
|
|
mfc_wpc_det_irq_thread(0, charger);
|
|
#endif
|
|
|
|
if (charger->skip_phm_work_in_sleep)
|
|
queue_delayed_work(charger->wqueue, &charger->wpc_tx_phm_work, 0);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
#define mfc_charger_suspend NULL
|
|
#define mfc_charger_resume NULL
|
|
#endif
|
|
|
|
#if defined(CONFIG_WIRELESS_RX_PHM_CTRL)
|
|
static void mfc_disable_irq_nosync(int irq)
|
|
{
|
|
struct irq_desc *desc;
|
|
|
|
if (irq <= 0)
|
|
return;
|
|
|
|
desc = irq_to_desc(irq);
|
|
if (desc->depth == 0)
|
|
disable_irq_nosync(irq);
|
|
}
|
|
#endif
|
|
|
|
static void stwlc89_charger_shutdown(struct i2c_client *client)
|
|
{
|
|
struct mfc_charger_data *charger = i2c_get_clientdata(client);
|
|
|
|
is_shutdn = true;
|
|
pr_info("%s\n", __func__);
|
|
|
|
cancel_delayed_work(&charger->wpc_vout_mode_work);
|
|
alarm_cancel(&charger->phm_alarm);
|
|
|
|
if (gpio_get_value(charger->pdata->wpc_det)) {
|
|
pr_info("%s: forced 5V Vout\n", __func__);
|
|
/* Prevent for unexpected FOD when reboot on morphie pad */
|
|
mfc_set_vrect_adjust(charger, MFC_HEADROOM_6);
|
|
mfc_set_vout(charger, MFC_VOUT_5V);
|
|
mfc_set_pad_hv(charger, false);
|
|
}
|
|
cancel_delayed_work(&charger->wpc_tx_duty_min_work);
|
|
cancel_delayed_work(&charger->wpc_rx_phm_work);
|
|
|
|
#if defined(CONFIG_WIRELESS_RX_PHM_CTRL)
|
|
if (charger->pdata->cable_type != SEC_BATTERY_CABLE_NONE) {
|
|
mfc_disable_irq_nosync(charger->pdata->irq_wpc_pdrc);
|
|
mfc_disable_irq_nosync(charger->pdata->irq_wpc_det);
|
|
mfc_disable_irq_nosync(charger->pdata->irq_wpc_pdet_b);
|
|
|
|
if (charger->pdata->cable_type == SEC_BATTERY_CABLE_WIRELESS_MPP)
|
|
mfc_send_ept_rst(charger);
|
|
|
|
|
|
if (charger->pdata->ping_nen >= 0) {
|
|
if (!gpio_get_value(charger->pdata->ping_nen)) {
|
|
/* reset rx ic and tx pad for phm exit */
|
|
mfc_set_wpc_en(charger, WPC_EN_CHARGING, false);
|
|
msleep(1500);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static const struct i2c_device_id stwlc89_charger_id_table[] = {
|
|
{ "stwlc89-charger", 0 },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, stwlc89_charger_id_table);
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id stwlc89_charger_match_table[] = {
|
|
{ .compatible = "stm,stwlc89-charger",},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, stwlc89_charger_match_table);
|
|
#else
|
|
#define stwlc89_charger_match_table NULL
|
|
#endif
|
|
|
|
const struct dev_pm_ops stwlc89_pm = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(mfc_charger_suspend, mfc_charger_resume)
|
|
};
|
|
|
|
static struct i2c_driver stwlc89_charger_driver = {
|
|
.driver = {
|
|
.name = "stwlc89-charger",
|
|
.owner = THIS_MODULE,
|
|
#if defined(CONFIG_PM)
|
|
.pm = &stwlc89_pm,
|
|
#endif /* CONFIG_PM */
|
|
.of_match_table = stwlc89_charger_match_table,
|
|
},
|
|
.shutdown = stwlc89_charger_shutdown,
|
|
.probe = stwlc89_charger_probe,
|
|
.remove = stwlc89_charger_remove,
|
|
.id_table = stwlc89_charger_id_table,
|
|
};
|
|
|
|
static int __init stwlc89_charger_init(void)
|
|
{
|
|
pr_info("%s\n", __func__);
|
|
return i2c_add_driver(&stwlc89_charger_driver);
|
|
}
|
|
|
|
static void __exit stwlc89_charger_exit(void)
|
|
{
|
|
pr_info("%s\n", __func__);
|
|
i2c_del_driver(&stwlc89_charger_driver);
|
|
}
|
|
|
|
module_init(stwlc89_charger_init);
|
|
module_exit(stwlc89_charger_exit);
|
|
|
|
MODULE_DESCRIPTION("Samsung STWLC89 Charger Driver");
|
|
MODULE_AUTHOR("Samsung Electronics");
|
|
MODULE_LICENSE("GPL");
|