From d14bfe879706bfb38814102c84fc4d891066a186 Mon Sep 17 00:00:00 2001 From: j7b3y Date: Wed, 15 Jan 2025 17:08:32 +0900 Subject: [PATCH] drivers/battery/wireless: add cps4038 --- drivers/battery/wireless/Kconfig | 10 + drivers/battery/wireless/Makefile | 6 +- drivers/battery/wireless/cps4038_charger.bzl | 7 + drivers/battery/wireless/cps4038_charger.c | 7547 +++++++++++++++++ drivers/battery/wireless/cps4038_charger.dtsi | 235 + drivers/battery/wireless/cps4038_charger.h | 1340 +++ drivers/battery/wireless/cps4038_cmfet.c | 335 + drivers/battery/wireless/cps4038_cmfet.h | 90 + drivers/battery/wireless/cps4038_firmware.h | 293 + drivers/battery/wireless/cps4038_fod.c | 721 ++ drivers/battery/wireless/cps4038_fod.h | 108 + 11 files changed, 10690 insertions(+), 2 deletions(-) create mode 100755 drivers/battery/wireless/cps4038_charger.bzl create mode 100755 drivers/battery/wireless/cps4038_charger.c create mode 100755 drivers/battery/wireless/cps4038_charger.dtsi create mode 100755 drivers/battery/wireless/cps4038_charger.h create mode 100755 drivers/battery/wireless/cps4038_cmfet.c create mode 100755 drivers/battery/wireless/cps4038_cmfet.h create mode 100755 drivers/battery/wireless/cps4038_firmware.h create mode 100755 drivers/battery/wireless/cps4038_fod.c create mode 100755 drivers/battery/wireless/cps4038_fod.h diff --git a/drivers/battery/wireless/Kconfig b/drivers/battery/wireless/Kconfig index 0fdeaf6280b3..9154ceb7c05e 100644 --- a/drivers/battery/wireless/Kconfig +++ b/drivers/battery/wireless/Kconfig @@ -8,6 +8,16 @@ config WIRELESS_CHARGING Wireless charging models should enable this charging option. it include various scenario for wireless charging models only. +config WIRELESS_CHARGER_CPS4038 + tristate "MFC IC CPS4038 charger support" + select WIRELESS_CHARGING + depends on BATTERY_SAMSUNG && I2C + help + Say Y here, to enable + support for the CPS4038 MFC IC + CPS4038 MFC include wireless charger driver. + It is for CPS(Conveninet Power Systems). + config WIRELESS_CHARGER_NU1668 tristate "MFC IC NU1668 charger support" select WIRELESS_CHARGING diff --git a/drivers/battery/wireless/Makefile b/drivers/battery/wireless/Makefile index 8fd441e6df96..b8f493b71e18 100644 --- a/drivers/battery/wireless/Makefile +++ b/drivers/battery/wireless/Makefile @@ -1,5 +1,7 @@ -obj-$(CONFIG_WIRELESS_CHARGER_NU1668) += nu1668-charger.o +obj-$(CONFIG_WIRELESS_CHARGER_CPS4038) += cps4038-charger.o +cps4038-charger-$(CONFIG_WIRELESS_CHARGER_CPS4038) += cps4038_fod.o cps4038_cmfet.o cps4038_charger.o +obj-$(CONFIG_WIRELESS_CHARGER_NU1668) += nu1668-charger.o nu1668-charger-$(CONFIG_WIRELESS_CHARGER_NU1668) += nu1668_fod.o nu1668_cmfet.o nu1668_charger.o -ccflags-y := -Wformat +ccflags-y := -Wformat \ No newline at end of file diff --git a/drivers/battery/wireless/cps4038_charger.bzl b/drivers/battery/wireless/cps4038_charger.bzl new file mode 100755 index 000000000000..823a8da45b61 --- /dev/null +++ b/drivers/battery/wireless/cps4038_charger.bzl @@ -0,0 +1,7 @@ +ko_list = [ + { + "ko_names" : [ + "drivers/battery/wireless/cps4038-charger.ko" + ] + } +] diff --git a/drivers/battery/wireless/cps4038_charger.c b/drivers/battery/wireless/cps4038_charger.c new file mode 100755 index 000000000000..5b59fa4fbd65 --- /dev/null +++ b/drivers/battery/wireless/cps4038_charger.c @@ -0,0 +1,7547 @@ +/* + * cps4038_charger.c + * Samsung CPS4038 IC Charger Driver + * + * Copyright (C) 2022 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 "cps4038_charger.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if IS_ENABLED(CONFIG_SPU_VERIFY) +#include +#endif + +#include "cps4038_firmware.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 + +#if defined(CONFIG_MST_PCR) +static bool use_pcr_fix_mode; +#endif + +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; + +typedef struct _sgf_data { + unsigned int size; + unsigned int type; + char *data; +} sgf_data; + +static const u16 mfc_cps_vout_val16[] = { + 0x006F, /* MFC_VOUT_4_5V */ + 0x0079, /* MFC_VOUT_4_7V */ + 0x007E, /* MFC_VOUT_4_8V */ + 0x0083, /* MFC_VOUT_4_9V */ + 0x0088, /* MFC_VOUT_5V */ + 0x00A1, /* MFC_VOUT_5_5V */ + 0x00BA, /* MFC_VOUT_6V */ + 0x00EC, /* MFC_VOUT_7V */ + 0x011E, /* MFC_VOUT_8V */ + 0x0150, /* MFC_VOUT_9V */ + 0x0182, /* MFC_VOUT_10V */ + 0x01B4, /* MFC_VOUT_11V */ + 0x01E6, /* MFC_VOUT_12V */ + 0x01ff, /* MFC_VOUT_12_5V */ + 0x0079, /* MFC_VOUT_OTG, 4.7V */ +}; + +static struct device_attribute mfc_attrs[] = { + CPS4038_ATTR(mfc_addr), + CPS4038_ATTR(mfc_size), + CPS4038_ATTR(mfc_data), + CPS4038_ATTR(mfc_packet), +}; + +static enum power_supply_property mfc_charger_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +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_set_tx_fod_thresh1(struct i2c_client *client, u32 fod_thresh1); +static void mfc_set_tx_fod_ta_thresh(struct i2c_client *client, u32 fod_thresh); +static int mfc_cps_wls_write_word(struct i2c_client *client, u32 reg, u32 val); +static int mfc_cps_wls_read_word(struct i2c_client *client, u32 reg, u8 *val); +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 cps4038_get_op_mode(void *pdata) +{ + struct mfc_charger_data *charger = pdata; + + if (!charger->det_state) + return WPC_OP_MODE_NONE; + + switch (charger->rx_op_mode) { + case MFC_RX_MODE_WPC_BPP: + if (is_ppde_wireless_type(charger->pdata->cable_type)) + return WPC_OP_MODE_PPDE; + return WPC_OP_MODE_BPP; + case MFC_RX_MODE_WPC_EPP: + case MFC_RX_MODE_WPC_EPP_NEGO: + return WPC_OP_MODE_EPP; + 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: + return WPC_OP_MODE_MPP; + } + + return WPC_OP_MODE_NONE; +} + +static int cps4038_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 cps4038_get_auth_mode(void *pdata) +{ + struct mfc_charger_data *charger = pdata; + + 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 cps4038_sbw_op = { + .get_op_mode = cps4038_get_op_mode, + .get_qi_ver = cps4038_get_qi_ver, + .get_auth_mode = cps4038_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 (!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 || 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 bool is_samsung_pad(u8 vendor_id) +{ + return (vendor_id == 0x42); +} + +static bool is_3rd_pad(u16 vendor_id) +{ + return (vendor_id == 0x6E00) || (vendor_id == 0x0066); +} + +static bool is_phm_supported_pad(struct mfc_charger_data *charger) +{ + pr_info("%s: tx_id_cnt(%d) tx_id(0x%x)\n", __func__, charger->tx_id_cnt, charger->tx_id); + + if (charger->tx_id == TX_ID_UNKNOWN || + charger->tx_id == TX_ID_N3300_V_PAD || + charger->tx_id == TX_ID_N3300_H_PAD || + charger->tx_id == TX_ID_N5200_V_PAD || + charger->tx_id == TX_ID_N5200_H_PAD || + charger->tx_id == TX_ID_BATT_PACK_U1200 || + charger->tx_id == TX_ID_BATT_PACK_U3300) + return false; + return true; +} + +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_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 mfc_get_firmware_version(struct mfc_charger_data *charger, int firm_mode) +{ + int ver_major = -1, ver_minor = -1; + 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__); + ver_major = -1; + ver_minor = -1; + 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 bool mfc_check_chip_id(struct mfc_charger_data *charger) +{ + u8 data[4] = {0, }; + + charger->reg_access_lock = true; + + mfc_cps_wls_write_word(charger->client, 0xFFFFFF00, 0x0000000E); /*enable 32bit i2c*/ + mfc_cps_wls_write_word(charger->client, 0x4000E75C, 0x00001250); /*write password*/ + mfc_cps_wls_read_word(charger->client, 0x60D4, data); + mfc_cps_wls_write_word(charger->client, 0xFFFFFF00, 0x00000000); /* i2c 32bit mode disable */ + + charger->reg_access_lock = false; + + pr_info("%s: CHIP_L(0x%x), CHIP_H(0x%x)\n", __func__, data[2], data[3]); + + if ((data[2] == 0x38) && (data[3] == 0x40)) + return true; + + return false; +} + +static int mfc_get_chip_id(struct mfc_charger_data *charger) +{ + u8 chip_id = 0, chip_id_h = 0; + int ret = 0; + + ret = mfc_reg_read(charger->client, MFC_CHIP_ID_L_REG, &chip_id); + ret = mfc_reg_read(charger->client, MFC_CHIP_ID_H_REG, &chip_id_h); + if (ret >= 0) { + pr_info("%s: CHIP_L(0x%x), CHIP_H(0x%x)\n", __func__, chip_id, chip_id_h); + charger->chip_id = MFC_CHIP_CPS; + } else + return -1; + +#if defined(CONFIG_WIRELESS_IC_PARAM) + if (chip_id > 0 && charger->wireless_chip_id_param != chip_id) { + pr_info("%s: chip_id (param:0x%02X, chip_id:0x%02X)\n", + __func__, charger->wireless_chip_id_param, chip_id); + charger->wireless_chip_id_param = chip_id; + 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 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)) / 10; + 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_IRECT_L_REG, &data[0]); + if (ret < 0) + break; + ret = mfc_reg_read(charger->client, MFC_ADC_IRECT_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_cps_vout_val16[vout] & 0xff; + data[1] = (mfc_cps_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]); + + mfc_fod_set_vout(charger->fod, (2280 + (mfc_cps_vout_val16[vout] * 20))); + mfc_cmfet_set_vout(charger->cmfet, (2280 + (mfc_cps_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)); + 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_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_cps_vout_val16[vout] & 0xff; + data[1] = (mfc_cps_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]); + + mfc_fod_set_vout(charger->fod, (2280 + (mfc_cps_vout_val16[vout] * 20))); + mfc_cmfet_set_vout(charger->cmfet, (2280 + (mfc_cps_vout_val16[vout] * 20))); + 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)); + charger->pdata->vout_status = vout; +} + +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_rpp_set(struct mfc_charger_data *charger, u8 val) +{ + u8 data; + int ret; + + pr_info("%s: Scale Factor %d\n", __func__, val); + mfc_reg_write(charger->client, MFC_RPP_SCALE_COEF_REG, val); + usleep_range(5000, 6000); + ret = mfc_reg_read(charger->client, MFC_RPP_SCALE_COEF_REG, &data); + if (ret < 0) + pr_err("%s: fail to read RPP scaling coefficient. (%d)\n", __func__, ret); + else + pr_info("%s: RPP scaling coefficient(0x%x)\n", __func__, data); +} + +static void mfc_set_tx_conflict_current(struct mfc_charger_data *charger, int tx_cf_current) +{ + u8 data = (u8)(tx_cf_current / 100); + + mfc_reg_write(charger->client, MFC_TX_CONFLICT_CURRENT_REG, data); + pr_info("%s: current = %d, data = 0x%x\n", __func__, tx_cf_current, data); +} + +static void mfc_set_tx_digital_ping_freq(struct mfc_charger_data *charger, unsigned int op_freq) +{ + u8 data[2] = {0,}; + + pr_info("%s: tx_digital_ping_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_PING_FREQ_L_REG, data[0]); + mfc_reg_write(charger->client, MFC_TX_PING_FREQ_H_REG, data[1]); +} + +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_set_cmd_h_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_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 + 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)); +} + +//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__); + + charger->mpp_cloak = 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__, gpio_get_value(charger->pdata->mpp_sw)); +} + +static void mfc_mpp_enter_cloak(struct mfc_charger_data *charger, u8 cloak_reason) +{ + int ret = 0; + + charger->mpp_cloak = cloak_reason; + //pr_info("@MPP %s: coil sw (%d)\n", __func__, gpio_get_value(charger->pdata->mpp_sw)); + + if (!gpio_get_value(charger->pdata->mpp_sw)) { + gpio_direction_output(charger->pdata->mpp_sw, 1); + } + + ret = mfc_reg_write(charger->client, MFC_MPP_CLOAK_REASON_REG, cloak_reason); + if (ret < 0) + return; + + 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 coil sw (%d)\n", __func__, + charger->mpp_cloak, gpio_get_value(charger->pdata->mpp_sw)); +} + + +#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); +} + +//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; +#if 0 + u8 reg_data = 0; + + ret = mfc_reg_read(charger->client, MFC_INT_B_ENABLE_L_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; + } +#endif + 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); +} +#endif + +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_B_ENABLE_L_REG, + (enable << MFC_INTB_L_INCREASE_POWER_SHIFT), MFC_INTB_L_INCREASE_POWER_MASK); + if (ret < 0) + pr_info("@MPP %s: MFC_INT_B_ENABLE_L_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; + + ret = mfc_reg_read(charger->client, MFC_MPP_THERMAL_CTRL_REG, ®_data); + if (ret >= 0) { + if ((reg_data == 0x0) && enable) + ret = mfc_reg_write(charger->client, MFC_MPP_THERMAL_CTRL_REG, 0x1); + else if ((reg_data == 0x1) && !enable) + ret = mfc_reg_write(charger->client, MFC_MPP_THERMAL_CTRL_REG, 0x0); + } else { + pr_info("@MPP %s: MFC_MPP_THERMAL_CTRL_REG read error(%d)\n", + __func__, ret); + } + + ret = mfc_reg_write(charger->client, MFC_MPP_THERMAL_CTRL_REG, enable); + if (ret < 0) + pr_info("@MPP %s: MFC_MPP_THERMAL_CTRL_REG write error(%d)\n", + __func__, ret); + + pr_info("@MPP %s: enable(%d)\n", __func__, enable); +} + +static void mfc_epp_enable(struct mfc_charger_data *charger, u32 enable) +{ + gpio_direction_output(charger->pdata->mag_det, enable); + + pr_info("@EPP %s: enable(%d)\n", __func__, enable); +} + +#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_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_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); + } + mfc_rpp_set(charger, 128); + 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); + } + mfc_rpp_set(charger, 64); + 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); + } + mfc_rpp_set(charger, 64); + break; + case MFC_AFC_CONF_12_5V_TX: + 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); + } + mfc_rpp_set(charger, 64); + + if (charger->pdata->cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20) { + if (delayed_work_pending(&charger->wpc_check_rx_power_work)) { + __pm_relax(charger->wpc_check_rx_power_ws); + cancel_delayed_work(&charger->wpc_check_rx_power_work); + } + charger->check_rx_power = true; + __pm_stay_awake(charger->wpc_check_rx_power_ws); + queue_delayed_work(charger->wqueue, + &charger->wpc_check_rx_power_work, msecs_to_jiffies(3000)); + } + 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); + } + mfc_rpp_set(charger, 32); + 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; + + switch (set) { + case MFC_HEADROOM_0: + mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x0); + break; + case MFC_HEADROOM_1: + mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x1c); //LSB 10mV + break; + case MFC_HEADROOM_2: + mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x32); + break; + case MFC_HEADROOM_3: + mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x41); + break; + case MFC_HEADROOM_4: + mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x03); + break; + case MFC_HEADROOM_5: + mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x08); + break; + case MFC_HEADROOM_6: + mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x0a); + break; + case MFC_HEADROOM_7: + mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0xc4); + break; + default: + pr_info("%s no headroom mode\n", __func__); + break; + } +} + +static void mfc_set_vout_ctrl_1st_full(struct mfc_charger_data *charger) +{ + if (!volt_ctrl_pad(charger->tx_id)) + 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)) + 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); +} + +static void mfc_set_cep_timeout(struct mfc_charger_data *charger, u32 timeout) +{ + if (timeout) { + pr_info("%s: timeout = %dmsec\n", __func__, timeout); + timeout = timeout/100; + mfc_reg_write(charger->client, MFC_CEP_TIME_OUT_REG, timeout); + } +} + +/* 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); +} + +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, 150); +} + +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 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 |= MFC_CMD2_ADT_SENT_MASK; + 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_H_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); + mfc_cmfet_set_auth(charger->cmfet, true); + if ((charger->tx_id == TX_ID_P5200_PAD) && (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 == TX_ID_P5200_PAD) { + mfc_reg_write(charger->client, MFC_RECT_MODE_AP_CTRL, 0x00); + mfc_reg_read(charger->client, MFC_RECTMODE_REG, &data); + } + mfc_cmfet_set_auth(charger->cmfet, false); + } else { + pr_info("%s: undefined cmd(%d)\n", __func__, opt); + } +} + +/* 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, + tx_min_op_freq = charger->pdata->tx_min_op_freq; + + 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->pdata->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); + } + + /* 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__); + mfc_set_tx_fod_thresh1(charger->client, charger->pdata->buds_fod_thresh1); + mfc_set_tx_fod_ta_thresh(charger->client, charger->pdata->buds_fod_ta_thresh); + 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; +} + +#if 0 +static int mfc_reg_multi_write(struct i2c_client *client, u16 reg, const u8 *val, int size) +{ + struct mfc_charger_data *charger = i2c_get_clientdata(client); + int ret = 0; + unsigned char data[SENDSZ + 2]; + int cnt = 0; + + pr_err("%s: size: 0x%x\n", __func__, size); + while (size > SENDSZ) { + data[0] = (reg + cnt) >> 8; + data[1] = (reg + cnt) & 0xff; + memcpy(data + 2, val + cnt, SENDSZ); + mutex_lock(&charger->io_lock); + ret = i2c_master_send(client, data, SENDSZ + 2); + mutex_unlock(&charger->io_lock); + if (ret < SENDSZ + 2) { + pr_err("%s: i2c write error, reg: 0x%x\n", __func__, reg); + return ret < 0 ? ret : -EIO; + } + cnt = cnt + SENDSZ; + size = size - SENDSZ; + } + if (size > 0) { + data[0] = (reg + cnt) >> 8; + data[1] = (reg + cnt) & 0xff; + memcpy(data + 2, val + cnt, size); + mutex_lock(&charger->io_lock); + ret = i2c_master_send(client, data, size + 2); + mutex_unlock(&charger->io_lock); + if (ret < size + 2) { + dev_err(&client->dev, "%s: i2c write error, reg: 0x%x\n", __func__, reg); + return ret < 0 ? ret : -EIO; + } + } + + return ret; +} +#endif + +static int mfc_cps_wls_write_word(struct i2c_client *client, u32 reg, u32 val) +{ + struct mfc_charger_data *charger = i2c_get_clientdata(client); + int ret = 0; + unsigned char data[8]; + + data[0] = reg >> 24; + data[1] = (reg >> 16) & 0xff; + data[2] = (reg >> 8) & 0xff; + data[3] = reg & 0xff; + data[4] = val & 0xff; + data[5] = (val >> 8) & 0xff; + data[6] = (val >> 16) & 0xff; + data[7] = (val >> 24) & 0xff; + mutex_lock(&charger->io_lock); + ret = i2c_master_send(client, data, 8); + mutex_unlock(&charger->io_lock); + if (ret < 8) { + dev_err(&client->dev, "%s: i2c write error, reg: 0x%x\n", __func__, reg); + return ret < 0 ? ret : -EIO; + } + + return ret; +} + +static int mfc_cps_wls_read_word(struct i2c_client *client, u32 reg, u8 *val) +{ + struct mfc_charger_data *charger = i2c_get_clientdata(client); + int ret; + struct i2c_msg msg[2]; + u8 wbuf[4]; + + msg[0].addr = client->addr; + msg[0].flags = client->flags & I2C_M_TEN; + msg[0].len = 4; + msg[0].buf = wbuf; + + wbuf[0] = reg >> 24; + wbuf[1] = (reg >> 16) & 0xff; + wbuf[2] = (reg >> 8) & 0xff; + wbuf[3] = reg & 0xff; + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 4; + 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 void mfc_cps_cmd_send(struct i2c_client *client, u32 cmd) +{ + //struct mfc_charger_data *charger = i2c_get_clientdata(client); + //u8 data[4]; + //u32 res; + + pr_info("%s: ---> send_cmd %02x\n", __func__, cmd); + mfc_cps_wls_write_word(client, ADDR_CMD, cmd); +} + +static int mfc_cps_wls_cmd_wait(struct i2c_client *client) +{ + int res; + //struct mfc_charger_data *charger = i2c_get_clientdata(client); + int cnt = 0; + u8 data[4]; + + while (1) { + usleep_range(1000, 1100); + if (mfc_cps_wls_read_word(client, ADDR_FLAG, data) < 0) { + pr_err("%s: read failed(%d)\n", __func__, ADDR_FLAG); + return -1; + } + res = data[0]; + switch (res) { + case RUNNING: + case PASS: + break; + case FAIL: + pr_info("%s: ---> FAIL : 0x%x\n", __func__, res); + return res; + case ILLEGAL: + pr_info("%s: ---> ILLEGAL : 0x%x\n", __func__, res); + return res; + default: + pr_info("%s: ---> ERROR-CODE : 0x%x\n", __func__, res); + return res; + } + if (res == PASS) + break; + /*3s over time*/ + if ((cnt++) == 3000) { + pr_info("%s: ---> CMD-OVERTIME\n", __func__); + break; + } + } + return res; +} + +static int mfc_cps_wls_program_sram(struct i2c_client *client, int reg, const u8 *wdate, int size) +{ + struct mfc_charger_data *charger = i2c_get_clientdata(client); + int ret = 0; + u8 data[SENDSZ + 4]; + int cnt = 0; + + pr_info("%s: size: 0x%x\n", __func__, size); + while (size > SENDSZ) { + data[0] = (reg+cnt) >> 24; + data[1] = ((reg+cnt) >> 16) & 0xff; + data[2] = ((reg+cnt) >> 8) & 0xff; + data[3] = (reg+cnt) & 0xff; + memcpy(data + 4, wdate + cnt, SENDSZ); + mutex_lock(&charger->io_lock); + ret = i2c_master_send(client, data, SENDSZ + 4); + mutex_unlock(&charger->io_lock); + if (ret < SENDSZ + 4) { + pr_err("%s: i2c write error, reg: 0x%x\n", __func__, (reg + cnt)); + return ret < 0 ? ret : -EIO; + } + cnt = cnt + SENDSZ; + size = size - SENDSZ; + } + if (size > 0) { + data[0] = (reg+cnt) >> 24; + data[1] = ((reg+cnt) >> 16) & 0xff; + data[2] = ((reg+cnt) >> 8) & 0xff; + data[3] = (reg+cnt) & 0xff; + memcpy(data + 4, wdate + cnt, size); + mutex_lock(&charger->io_lock); + ret = i2c_master_send(client, data, size + 4); + mutex_unlock(&charger->io_lock); + if (ret < size + 4) { + pr_err("%s: i2c write error, reg: 0x%x\n", __func__, (reg + cnt)); + return ret < 0 ? ret : -EIO; + } + } + + return ret; +} + +static int PgmOTPwRAM_CPS(struct mfc_charger_data *charger, unsigned short OtpAddr, + const u8 *srcData, int srcOffs, int size) +{ + int addr; + int i, j; + int ret, retry = 3; + u8 fw_ver[4] = {0, }; + u8 fw_ver_bin[4] = {0, }; + //int flag = 0; + int cfg_buf_size; + int buf0_flag; + int buf1_flag; + int result; + //int write_count = 0; + + mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_L_REG, &fw_ver[0]); + mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_H_REG, &fw_ver[1]); + pr_info("%s BEFORE rx major firmware version 0x%x\n", + __func__, fw_ver[0] | (fw_ver[1] << 8)); + + mfc_reg_read(charger->client, MFC_FW_MINOR_REV_L_REG, &fw_ver[2]); + mfc_reg_read(charger->client, MFC_FW_MINOR_REV_H_REG, &fw_ver[3]); + pr_info("%s BEFORE rx minor firmware version 0x%x\n", + __func__, fw_ver[2] | (fw_ver[3] << 8)); + + /* get major and minor firmware version from firmware file */ + memcpy(fw_ver_bin, &srcData[MFC_FW_VER_BIN_CPS], 4); + + pr_info("%s NEW rx major firmware version 0x%x\n", + __func__, fw_ver_bin[0] | (fw_ver_bin[1] << 8)); + pr_info("%s NEW rx minor firmware version 0x%x\n", + __func__, fw_ver_bin[2] | (fw_ver_bin[3] << 8)); + + /* + * block to access regisgter map during fw update + * register-map is not available since SRAM is usued as buffer or bootloader + */ + charger->reg_access_lock = true; + + /*************************************************************************************** + * Step1, load to sram * + ***************************************************************************************/ + mfc_cps_wls_write_word(charger->client, 0xFFFFFF00, 0x0000000E); /*enable 32bit i2c*/ + mfc_cps_wls_write_word(charger->client, 0x4000E75C, 0x00001250); /*write password*/ + mfc_cps_wls_write_word(charger->client, 0x40040010, 0x00000006); /*reset and halt mcu*/ + mfc_cps_wls_write_word(charger->client, 0x4000E01C, 0x000003E8); /*config SDA timeout to 1s*/ + pr_info("%s: START LOAD SRAM HEX\n", __func__); + mfc_cps_wls_program_sram(charger->client, 0x20000000, CPS4038_BL, 0x0800); + + mfc_cps_wls_write_word(charger->client, 0x400400A0, 0x000000FF); /*enable remap function*/ + mfc_cps_wls_write_word(charger->client, 0x40040010, 0x00008003); /*triming load function is disabled and run mcu*/ + + usleep_range(10000, 11000); + + mfc_cps_wls_write_word(charger->client, 0xFFFFFF00, 0x0000000E); /*enable 32bit i2c*/ + mfc_cps_wls_write_word(charger->client, 0x4000E01C, 0x000003E8); /*config SDA timeout to 1s*/ + + usleep_range(10000, 11000); + + /*************************************************************************************** + * Step2, bootloader crc check * + ***************************************************************************************/ + mfc_cps_cmd_send(charger->client, CACL_CRC_TEST); + result = mfc_cps_wls_cmd_wait(charger->client); + + if (result != PASS) { + pr_err("%s: ---> BOOTLOADER CRC FAIL\n", __func__); + goto fw_update_fail; + } + + pr_info("%s: ---> LOAD BOOTLOADER SUCCESSFUL\n", __func__); + + /*************************************************************************************** + * Step3, load firmware to MTP * + ***************************************************************************************/ + pr_info("%s: START LOAD APP HEX\n", __func__); + buf0_flag = 0; + buf1_flag = 0; + cfg_buf_size = 512; + addr = 0; + mfc_cps_wls_write_word(charger->client, ADDR_BUF_SIZE, cfg_buf_size); + + result = mfc_cps_wls_cmd_wait(charger->client); + if (result != PASS) { + pr_err("%s: ---> ERASE FAIL\n", __func__); + goto fw_update_fail; + } + + for (i = 0; i < (24*1024)/4/cfg_buf_size; i++) { + if (buf0_flag == 0) { + mfc_cps_wls_program_sram(charger->client, ADDR_BUFFER0, srcData+addr, cfg_buf_size*4); + addr = addr + cfg_buf_size*4; + + if (buf1_flag == 1) { + result = mfc_cps_wls_cmd_wait(charger->client); + if (result != PASS) { + pr_err("%s: ---> WRITE BUFFER1 DATA TO MTP FAIL\n", __func__); + goto fw_update_fail; + } + buf1_flag = 0; + } + mfc_cps_cmd_send(charger->client, PGM_BUFFER0); + buf0_flag = 1; + continue; + } + + if (buf1_flag == 0) { + mfc_cps_wls_program_sram(charger->client, ADDR_BUFFER1, srcData+addr, cfg_buf_size*4); + addr = addr + cfg_buf_size*4; + + if (buf0_flag == 1) { + result = mfc_cps_wls_cmd_wait(charger->client); + if (result != PASS) { + pr_err("%s: ---> WRITE BUFFER0 DATA TO MTP FAIL\n", __func__); + goto fw_update_fail; + } + buf0_flag = 0; + } + mfc_cps_cmd_send(charger->client, PGM_BUFFER1); + buf1_flag = 1; + continue; + } + } + + if (buf0_flag == 1) { + result = mfc_cps_wls_cmd_wait(charger->client); + if (result != PASS) { + pr_err("%s: ---> WRITE BUFFER0 DATA TO MTP FAIL\n", __func__); + goto fw_update_fail; + } + buf0_flag = 0; + } + + if (buf1_flag == 1) { + result = mfc_cps_wls_cmd_wait(charger->client); + if (result != PASS) { + pr_err("%s: ---> WRITE BUFFER1 DATA TO MTP FAIL\n", __func__); + return MFC_FWUP_ERR_FAIL; + } + buf1_flag = 0; + } + pr_info("%s: ---> LOAD APP HEX SUCCESSFUL\n", __func__); + /*************************************************************************************** + * Step4, check app CRC * + ***************************************************************************************/ + mfc_cps_cmd_send(charger->client, CACL_CRC_APP); + result = mfc_cps_wls_cmd_wait(charger->client); + + if (result != PASS) { + pr_err("%s: ---> APP CRC FAIL\n", __func__); + goto fw_update_fail; + } + pr_info("%s: ---> CHERK APP CRC SUCCESSFUL\n", __func__); + /*************************************************************************************** + * Step5, write mcu start flag * + ***************************************************************************************/ + mfc_cps_cmd_send(charger->client, PGM_WR_FLAG); + result = mfc_cps_wls_cmd_wait(charger->client); + if (result != PASS) { + pr_err("%s:---> WRITE MCU START FLAG FAIL\n", __func__); + goto fw_update_fail; + } + + pr_info("%s: ---> WRITE MCU START FLAG SUCCESSFUL\n", __func__); + mfc_cps_wls_write_word(charger->client, 0x40040010, 0x00000008); /* reset all system */ + + msleep(100); + + mfc_cps_wls_write_word(charger->client, 0xFFFFFF00, 0x00000000); /* i2c 32bit mode disable */ + + charger->reg_access_lock = false; + + for (i = 0; i < retry; i++) { + usleep_range(10000, 11000); + ret = MFC_FWUP_ERR_SUCCEEDED; + + mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_L_REG, &fw_ver[0]); + mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_H_REG, &fw_ver[1]); + pr_info("%s NOW rx major firmware version 0x%x\n", + __func__, fw_ver[0] | (fw_ver[1] << 8)); + + mfc_reg_read(charger->client, MFC_FW_MINOR_REV_L_REG, &fw_ver[2]); + mfc_reg_read(charger->client, MFC_FW_MINOR_REV_H_REG, &fw_ver[3]); + pr_info("%s NOW rx minor firmware version 0x%x\n", + __func__, fw_ver[2] | (fw_ver[3] << 8)); + + for (j = 0; j < 4; j++) + if (fw_ver[j] != fw_ver_bin[j]) + ret = MFC_FWUP_ERR_COMMON_FAIL; + + if (ret != MFC_FWUP_ERR_COMMON_FAIL) + break; + else { + mfc_uno_on(charger, false); + msleep(200); + mfc_uno_on(charger, true); + msleep(200); + } + } + + return ret; + +fw_update_fail: + charger->reg_access_lock = false; + return MFC_FWUP_ERR_FAIL; +} + +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_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)) + 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 == TX_ID_FG_PAD)) { + 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; + int i = 0; + bool is_changed = false; + char fwtime[8] = {32, 32, 32, 32, 32, 32, 32, 32}; + char fwdate[8] = {32, 32, 32, 32, 32, 32, 32, 32}; + u8 data = 32; /* ascii space */ + const char *fw_path = MFC_FLASH_FW_HEX_CPS_PATH; + int fw_size; + 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_check_chip_id(charger)) { + pr_info("%s: current IC's chip_id is not matching to driver's chip_id(0x%x)\n", + __func__, MFC_CHIP_CPS); + break; + } + if (charger->fw_cmd == SEC_WIRELESS_FW_UPDATE_AUTO_MODE) { + charger->pdata->otp_firmware_ver = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE); +#if defined(CONFIG_WIRELESS_IC_PARAM) + if (charger->wireless_fw_mode_param == SEC_WIRELESS_FW_UPDATE_SPU_MODE && + charger->pdata->otp_firmware_ver > MFC_FW_BIN_VERSION) { + pr_info("%s: current version (0x%x) is higher than BIN_VERSION by SPU(0x%x)\n", + __func__, charger->pdata->otp_firmware_ver, MFC_FW_BIN_VERSION); + break; + } +#endif + if (charger->pdata->otp_firmware_ver == MFC_FW_BIN_VERSION) { + pr_info("%s: current version (0x%x) is same to BIN_VERSION (0x%x)\n", + __func__, charger->pdata->otp_firmware_ver, MFC_FW_BIN_VERSION); + 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; + else + fw_path = MFC_FLASH_FW_HEX_CPS_PATH; + + ret = request_firmware(&charger->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 = (int)charger->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", charger->firm_data_bin->data, charger->firm_data_bin->size) == + (fw_size - SPU_METADATA_SIZE(MFC))) { + pr_err("%s: spu_firmware_signature_verify success\n", __func__); + 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); + 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_CPS(charger, 0, charger->firm_data_bin->data, 0, 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(charger->firm_data_bin); + + for (i = 0; i < 8; i++) { + if (mfc_reg_read(charger->client, MFC_FW_DATA_CODE_0+i, &data) > 0) + fwdate[i] = (char)data; + } + for (i = 0; i < 8; i++) { + if (mfc_reg_read(charger->client, MFC_FW_TIMER_CODE_0+i, &data) > 0) + fwtime[i] = (char)data; + } + pr_info("%s: %d%d%d%d%d%d%d%d, %d%d%d%d%d%d%d%d\n", __func__, + fwdate[0], fwdate[1], fwdate[2], fwdate[3], fwdate[4], fwdate[5], fwdate[6], fwdate[7], + fwtime[0], fwtime[1], fwtime[2], fwtime[3], fwtime[4], fwtime[5], fwtime[6], fwtime[7]); + + charger->pdata->otp_firmware_ver = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE); + charger->pdata->wc_ic_rev = mfc_get_ic_revision(charger, MFC_IC_REVISION); + + for (i = 0; i < 8; i++) { + if (mfc_reg_read(charger->client, MFC_FW_DATA_CODE_0+i, &data) > 0) + fwdate[i] = (char)data; + } + for (i = 0; i < 8; i++) { + if (mfc_reg_read(charger->client, MFC_FW_TIMER_CODE_0+i, &data) > 0) + fwtime[i] = (char)data; + } + pr_info("%s: %d%d%d%d%d%d%d%d, %d%d%d%d%d%d%d%d\n", __func__, + fwdate[0], fwdate[1], fwdate[2], fwdate[3], fwdate[4], fwdate[5], fwdate[6], fwdate[7], + fwtime[0], fwtime[1], fwtime[2], fwtime[3], fwtime[4], fwtime[5], fwtime[6], fwtime[7]); + + 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_L_REG, irq_src_l); // clear int + mfc_reg_write(charger->client, MFC_INT_A_CLEAR_H_REG, irq_src_h); // clear int + mfc_set_cmd_l_reg(charger, 0x20, MFC_CMD_CLEAR_INT_MASK); // command + +#if defined(CONFIG_MST_PCR) + pr_info("%s : MST ISET_PCR : %d\n", __func__, charger->pdata->mst_iset_pcr); + mfc_reg_write(charger->client, MFC_ISET_PCR, charger->pdata->mst_iset_pcr); + if (use_pcr_fix_mode) { + mfc_reg_write(charger->client, PCR_FIX_MODE, 0x01); + } + /* set PCR mode2 */ + mfc_reg_write(charger->client, MFC_MST_MODE_SEL_REG, MFC_TX_MODE_MST_PCR_MODE2); +#else + mfc_reg_write(charger->client, MFC_MST_MODE_SEL_REG, MFC_TX_MODE_MST_MODE2); /* set MST mode2 */ +#endif + 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_L_REG, irq_src_l); // clear int + mfc_reg_write(charger->client, MFC_INT_A_CLEAR_H_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_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; + } + } + + 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(6000)); +} + +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__); + /*TODO: enable FWC AUTH*/ + mfc_epp_enable(charger, 1); + mfc_set_epp_count(charger, 0); + 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 */ + 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__); + 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 + 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 cps4038_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 (cps4038_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: + break; + case WPC_AUTH_MODE_PPDE: + if (adt_state == WIRELESS_AUTH_PASS) { + 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->pdata->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,%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->pdata->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 cps4038_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; + + 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__); + return; + } + + 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; + + 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: + mfc_cmfet_set_high_swell(charger->cmfet, true); + mfc_fod_set_high_swell(charger->fod, true); + if ((chgen == SEC_BAT_CHG_MODE_CHARGING_OFF) || + (chgen == SEC_BAT_CHG_MODE_BUCK_OFF)) { + mfc_cmfet_set_chg_done(charger->cmfet, true); + mfc_fod_set_bat_state(charger->fod, MFC_FOD_BAT_STATE_FULL); + } else if (chgen == SEC_BAT_CHG_MODE_CHARGING) { + mfc_cmfet_set_chg_done(charger->cmfet, false); + } + break; + default: + mfc_cmfet_set_high_swell(charger->cmfet, false); + mfc_fod_set_high_swell(charger->fod, false); + if (chgen == SEC_BAT_CHG_MODE_CHARGING) + mfc_cmfet_set_chg_done(charger->cmfet, false); + break; + } + mfc_cmfet_set_bat_cap(charger->cmfet, capacity); + mfc_fod_set_bat_cap(charger->fod, capacity); + + pr_info("%s: check thermal_zone = %d, capacity = %d, chgen = %d\n", + __func__, thermal_zone, capacity, chgen); +} + +static int cps4038_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; +// union power_supply_propval value; + + 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_FW_BIN_VERSION; + } 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->pdata->otp_firmware_ver > 0) + val->intval = charger->pdata->otp_firmware_ver; + } else if (val->intval == SEC_WIRELESS_OTP_FIRM_VERIFY) { + pr_info("%s: CPS 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: +#if defined(CONFIG_WIRELESS_IC_PARAM) + pr_info("%s: fw_ver (param:0x%04X, build:0x%04X)\n", + __func__, charger->wireless_fw_ver_param, MFC_FW_BIN_VERSION); + if (charger->wireless_fw_ver_param == MFC_FW_BIN_VERSION) + val->intval = 1; + else + val->intval = 0; +#else + val->intval = 0; +#endif + 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: + cps4038_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; + 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; + } + 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_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: + 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 && opfreq_ctrl_pad(charger->tx_id)) { + 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 == TX_ID_BATT_PACK_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_set_tx_fod_common(struct mfc_charger_data *charger) +{ + u8 data[2] = {0,}; + + data[0] = charger->pdata->tx_fod_offset & 0xff; + data[1] = (charger->pdata->tx_fod_offset & 0xff00) >> 8; + pr_info("%s: tx_fod_gain(0x%x), tx_fod_offset(0x%x, 0x%x)\n", + __func__, charger->pdata->tx_fod_gain, data[1], data[0]); + + /* Gain */ + mfc_reg_write(charger->client, MFC_TX_FOD_GAIN_REG, charger->pdata->tx_fod_gain); + /* Offset */ + mfc_reg_write(charger->client, MFC_TX_FOD_OFFSET_L_REG, data[0]); + mfc_reg_write(charger->client, MFC_TX_FOD_OFFSET_H_REG, data[1]); +} + +static void mfc_set_tx_fod_thresh1(struct i2c_client *client, u32 fod_thresh1) +{ + u8 data[2] = {0,}; + + /* Thresh1 */ + data[0] = fod_thresh1 & 0xff; + data[1] = (fod_thresh1 & 0xff00) >> 8; + + pr_info("%s: fod_thresh1(0x%x, 0x%x)\n", __func__, data[1], data[0]); + mfc_reg_write(client, MFC_TX_FOD_THRESH1_L_REG, data[0]); + mfc_reg_write(client, MFC_TX_FOD_THRESH1_H_REG, data[1]); +} + +static void mfc_set_tx_fod_ta_thresh(struct i2c_client *client, u32 fod_thresh) +{ + u8 data[2] = {0,}; + + /* TA Thresh */ + data[0] = fod_thresh & 0xff; + data[1] = (fod_thresh & 0xff00) >> 8; + pr_info("%s: fod_thresh1(0x%x, 0x%x)\n", __func__, data[1], data[0]); + mfc_reg_write(client, MFC_TX_FOD_TA_THRESH_L_REG, data[0]); + mfc_reg_write(client, MFC_TX_FOD_TA_THRESH_H_REG, data[1]); +} + +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); + + mfc_set_tx_fod_common(charger); + if (prmc_id == 0x42 && reg_data >= 1) { + pr_info("@Tx_Mode %s: Samsung Gear Connected\n", __func__); + charger->wc_rx_type = SS_GEAR; + mfc_set_tx_digital_ping_freq(charger, charger->pdata->gear_op_freq); + if (charger->pdata->gear_min_op_freq_delay > 0) { + mfc_set_tx_min_op_freq(charger, charger->pdata->gear_min_op_freq); + cancel_delayed_work(&charger->wpc_tx_min_op_freq_work); + __pm_stay_awake(charger->wpc_tx_min_opfq_ws); + queue_delayed_work(charger->wqueue, &charger->wpc_tx_min_op_freq_work, + msecs_to_jiffies(charger->pdata->gear_min_op_freq_delay)); + } + } 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); + mfc_set_tx_fod_thresh1(charger->client, charger->pdata->phone_fod_thresh1); + mfc_set_tx_fod_ta_thresh(charger->client, charger->pdata->phone_fod_ta_thresh); + } 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_min_op_freq_work(struct work_struct *work) +{ + struct mfc_charger_data *charger = + container_of(work, struct mfc_charger_data, wpc_tx_min_op_freq_work.work); + + mfc_set_tx_min_op_freq(charger, TX_MIN_OP_FREQ_DEFAULT); + + __pm_relax(charger->wpc_tx_min_opfq_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_check_rx_power_work(struct work_struct *work) +{ + struct mfc_charger_data *charger = + container_of(work, struct mfc_charger_data, wpc_check_rx_power_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_check_rx_power_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_wpc_init_work(struct work_struct *work) +{ + struct mfc_charger_data *charger = + container_of(work, struct mfc_charger_data, wpc_init_work.work); + + pr_info("%s\n", __func__); + if (charger->pdata->cable_type != SEC_BATTERY_CABLE_NONE) { + mfc_set_online(charger, charger->pdata->cable_type); + 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); + } + if (charger->is_otg_on) { + union power_supply_propval value = {0, }; + + 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_L_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_INTA_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 = TX_ID_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); +} + +#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->rx_phm_state = NONE_PHM; + __pm_relax(charger->wpc_rx_phm_ws); + return; + } + if (charger->rx_phm_state == ENTER_PHM && !is_phm_supported_pad(charger)) { + 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; + + switch (state->fake_op_mode) { + case WPC_OP_MODE_PPDE: + fod_reg = MFC_WPC_FWC_FOD_0A_REG; + break; + case WPC_OP_MODE_EPP: + fod_reg = MFC_WPC_EPP_FOD_0A_REG; + break; + case WPC_OP_MODE_MPP: + case WPC_OP_MODE_BPP: + default: + fod_reg = MFC_WPC_FOD_0A_REG; + break; + } + + 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 cps4038_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_FWC_FOD_0A_REG + i, data[i]); + if (ret < 0) + goto err_write_reg; + + ret = mfc_reg_write(charger->client, MFC_WPC_EPP_FOD_0A_REG + i, data[i]); + if (ret < 0) + goto err_write_reg; + + 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 cps4038_print_fod(struct mfc_charger_data *charger) +{ + union mfc_fod_state now_state = { 0, }; + + mfc_fod_get_state(charger->fod, &now_state); + charger->now_fod_state.value = now_state.value; + + print_fod_log(charger, &now_state); +} + +static int cps4038_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; + } + + data = ((cma) ? 0xC0 : 0x00) | ((cmb) ? 0x30 : 0x00); + ret = mfc_reg_write(charger->client, MFC_CMFET_CTRL_REG, data); + mfc_reg_read(charger->client, MFC_CMFET_CTRL_REG, &data); + pr_info("%s: state(0x%llX), ret(%d), data(0x%X)\n", __func__, state->value, ret, data); + return ret; +} + +static void cps4038_print_cmfet(struct mfc_charger_data *charger) +{ + union mfc_cmfet_state state = { 0, }; + int ret = 0; + u8 data = 0; + + mfc_cmfet_get_state(charger->cmfet, &state); + charger->now_cmfet_state.value = state.value; + + ret = mfc_reg_read(charger->client, MFC_CMFET_CTRL_REG, &data); + pr_info("%s: [0x%llX] ret = %d, data = 0x%x\n", __func__, state.value, ret, data); +} + +static bool cps4038_set_force_vout(struct mfc_charger_data *charger, int vout) +{ + bool ret = true; + + switch (vout) { + 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 cps4038_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)) { + 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); + } + 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->pdata->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) { + pr_info("%s: FW Ver(%x) RX_VOUT(%dmV) RX_VRECT(%dmV) RX_IOUT(%dmA)\n", __func__, + charger->pdata->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)\n", __func__, + mfc_get_adc(charger, MFC_ADC_OP_FRQ), + charger->tx_id, + charger->pdata->wc_ic_rev); + + cps4038_print_fod(charger); + cps4038_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_L_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_L_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_L_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); + msleep(300); + 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(400); + mfc_reg_read(charger->client, MFC_STATUS_L_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); + 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 == 1) + mfc_send_ept_unknown(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 */ + cps4038_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_H_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_H_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; + 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) */ + 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: + 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) { + pr_info("%s: mfc otg on\n", __func__); + charger->is_otg_on = true; + } else { + pr_info("%s: mfc otg off\n", __func__); + charger->is_otg_on = false; + } + 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; + 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 (!cps4038_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: + pr_info("@MPP %s: MPP_COVER(%d)\n", __func__, val->intval); + //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 == 1) + mfc_mpp_enter_cloak(charger, MFC_TRX_MPP_CLOAK_PTX_INITIATED); + else if (val->intval == 2) + mfc_mpp_enter_cloak(charger, MFC_TRX_MPP_CLOAK_END_OF_CHARGE); + else + mfc_mpp_exit_cloak(charger); + break; + case POWER_SUPPLY_EXT_PROP_MPP_ICL_CTRL: + pr_info("@MPP %s: MPP_THERMAL_CTRL(%d)\n", __func__, val->intval); + 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; + 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 < TX_ID_AUTH_PAD || txid > TX_ID_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 >= TX_ID_AUTH_PAD + 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 - TX_ID_AUTH_PAD; + + 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 < TX_ID_AUTH_PAD || txid > TX_ID_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 >= TX_ID_AUTH_PAD + 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 - TX_ID_AUTH_PAD; + } + + 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 < TX_ID_AUTH_PAD || txid > TX_ID_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 >= TX_ID_AUTH_PAD + 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 - TX_ID_AUTH_PAD; + } + + 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->pdata->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_L_REG, + MFC_STAT_L_OP_MODE_MASK, MFC_STAT_L_OP_MODE_MASK); + mfc_reg_update(charger->client, MFC_INT_A_ENABLE_L_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, 0x4); + } + /* SET ILIM 1.6A */ + mfc_reg_write(charger->client, MFC_ILIM_SET_REG, 0x20); + + /* 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 */ + 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) || + 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 (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; + } + mfc_fod_set_op_mode(charger->fod, cps4038_get_op_mode(charger)); + } + 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 = TX_ID_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; + + mfc_mpp_exit_cloak(charger); + + mfc_set_online(charger, SEC_BATTERY_CABLE_NONE); + mfc_fod_init_state(charger->fod); + mfc_cmfet_init_state(charger->cmfet); + + /* 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_check_rx_power_work)) { + __pm_relax(charger->wpc_check_rx_power_ws); + cancel_delayed_work(&charger->wpc_check_rx_power_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; + } + 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 == 0)) { + 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_set_online(charger, SEC_BATTERY_CABLE_WIRELESS_FAKE); + mfc_set_epp_count(charger, charger->epp_count + 1); + } else if ((is_wireless_fake_type(charger->pdata->cable_type)) && (charger->pdrc_state == 1)) { + union power_supply_propval value = {0, }; + + charger->is_full_status = 0; + mfc_set_online(charger, SEC_BATTERY_CABLE_NONE); + mfc_fod_init_state(charger->fod); + 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_L_REG, &status_l); + ret = mfc_reg_read(charger->client, MFC_STATUS_H_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\n", __func__); + value.intval = 1; + 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; + 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; + mfc_fod_set_tx_id(charger->fod, val_data); + + psy_do_property("battery", get, POWER_SUPPLY_PROP_CAPACITY, value); + capacity = value.intval; + + psy_do_property("battery", get, POWER_SUPPLY_EXT_PROP_LCD_FLICKER, value); + wpc_vout_ctrl_lcd_on = value.intval; + + switch (val_data) { + case TX_ID_UNKNOWN: + value.intval = charger->pdata->cable_type; + break; + case TX_ID_VEHICLE_PAD: + 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 TX_ID_STAND_TYPE_START: + 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 TX_ID_MULTI_PORT_START ... TX_ID_MULTI_PORT_END: + value.intval = charger->pdata->cable_type; + pr_info("%s: MULTI PORT PAD : 0x%x\n", __func__, val_data); + break; + case TX_ID_BATT_PACK_U1200 ... TX_ID_BATT_PACK_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 TX_ID_UNO_TX: + case TX_ID_UNO_TX_B0 ... TX_ID_UNO_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 TX_ID_NON_AUTH_PAD ... TX_ID_NON_AUTH_PAD_END: + value.intval = charger->pdata->cable_type; + break; + case TX_ID_AUTH_PAD ... TX_ID_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 == TX_ID_P2400_PAD)) + 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 TX_ID_PG950_S_PAD: + value.intval = charger->pdata->cable_type; + pr_info("%s: PG950 PAD STAND : 0x%x\n", __func__, val_data); + break; + case TX_ID_PG950_D_PAD: + value.intval = charger->pdata->cable_type; + pr_info("%s: PG950 PAD DOWN : 0x%x\n", __func__, val_data); + break; + case TX_ID_P2400_PAD_NOAUTH: + 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 && opfreq_ctrl_pad(charger->tx_id)) { + 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_check_rx_power_work)) { + __pm_relax(charger->wpc_check_rx_power_ws); + cancel_delayed_work(&charger->wpc_check_rx_power_work); + } + 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_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, 0x1); + } else { + /* SET OV 11V */ + mfc_reg_write(charger->client, MFC_RX_OV_CLAMP_REG, 0x0); + } + } +} + +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 == TX_ID_JIG_PAD) { + 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 { + 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 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); + + if (pdrc_state) { + unsigned int epp_clear_delay = charger->epp_time; + + charger->epp_time = 1000; + if ((charger->epp_count > 0) && + (charger->epp_count >= charger->pdata->mpp_epp_max_count)) { + epp_clear_delay = 6000; + mfc_set_epp_count(charger, 0); + } + 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)); + } + } 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; +} + +static void mfc_set_iec_params(struct mfc_charger_data *charger, mfc_charger_platform_data_t *pdata) +{ + pr_info("%s: iec_q_thresh_1 = %d\n", __func__, pdata->iec_q_thresh_1); + mfc_reg_write(charger->client, MFC_IEC_Q_THRESH1_REG, pdata->iec_q_thresh_1); + + pr_info("%s: iec_q_thresh_2 = %d\n", __func__, pdata->iec_q_thresh_2); + mfc_reg_write(charger->client, MFC_IEC_Q_THRESH2_REG, pdata->iec_q_thresh_2); + + pr_info("%s: iec_fres_thresh_1 = %d\n", __func__, pdata->iec_fres_thresh_1); + mfc_reg_write(charger->client, MFC_IEC_FRES_THRESH1_REG, pdata->iec_fres_thresh_1); + + pr_info("%s: iec_fres_thresh_2 = %d\n", __func__, pdata->iec_fres_thresh_2); + mfc_reg_write(charger->client, MFC_IEC_FRES_THRESH2_REG, pdata->iec_fres_thresh_2); + + pr_info("%s: iec_power_limit_thresh = %d\n", __func__, pdata->iec_power_limit_thresh); + mfc_reg_write(charger->client, MFC_IEC_POWER_LIMIT_THRESH_L_REG, pdata->iec_power_limit_thresh); + mfc_reg_write(charger->client, MFC_IEC_POWER_LIMIT_THRESH_H_REG, (pdata->iec_power_limit_thresh >> 8)); + + pr_info("%s: iec_ploss_thresh_1 = %d\n", __func__, pdata->iec_ploss_thresh_1); + mfc_reg_write(charger->client, MFC_IEC_PLOSS_THRESH1_L_REG, pdata->iec_ploss_thresh_1); + mfc_reg_write(charger->client, MFC_IEC_PLOSS_THRESH1_H_REG, (pdata->iec_ploss_thresh_1 >> 8)); + + pr_info("%s: iec_ploss_thresh_2 = %d\n", __func__, pdata->iec_ploss_thresh_2); + mfc_reg_write(charger->client, MFC_IEC_PLOSS_THRESH2_L_REG, pdata->iec_ploss_thresh_2); + mfc_reg_write(charger->client, MFC_IEC_PLOSS_THRESH2_H_REG, (pdata->iec_ploss_thresh_2 >> 8)); + + pr_info("%s: iec_ploss_freq_thresh_1 = %d\n", __func__, pdata->iec_ploss_freq_thresh_1); + mfc_reg_write(charger->client, MFC_IEC_PLOSS_FREQ_THRESH1_REG, pdata->iec_ploss_freq_thresh_1); + + pr_info("%s: iec_ploss_freq_thresh_2 = %d\n", __func__, pdata->iec_ploss_freq_thresh_2); + mfc_reg_write(charger->client, MFC_IEC_PLOSS_FREQ_THRESH2_REG, pdata->iec_ploss_freq_thresh_2); + + pr_info("%s: iec_ta_power_limit_thresh = %d\n", __func__, pdata->iec_ta_power_limit_thresh); + mfc_reg_write(charger->client, MFC_IEC_TA_POWER_LIMIT_THRESH_L_REG, pdata->iec_ta_power_limit_thresh); + mfc_reg_write(charger->client, MFC_IEC_TA_POWER_LIMIT_THRESH_H_REG, (pdata->iec_ta_power_limit_thresh >> 8)); + + pr_info("%s: iec_ta_ploss_thresh_1 = %d\n", __func__, pdata->iec_ta_ploss_thresh_1); + mfc_reg_write(charger->client, MFC_IEC_TA_PLOSS_THRESH1_L_REG, pdata->iec_ta_ploss_thresh_1); + mfc_reg_write(charger->client, MFC_IEC_TA_PLOSS_THRESH1_H_REG, (pdata->iec_ta_ploss_thresh_1 >> 8)); + + pr_info("%s: iec_ta_ploss_thresh_2 = %d\n", __func__, pdata->iec_ta_ploss_thresh_2); + mfc_reg_write(charger->client, MFC_IEC_TA_PLOSS_THRESH2_L_REG, pdata->iec_ta_ploss_thresh_2); + mfc_reg_write(charger->client, MFC_IEC_TA_PLOSS_THRESH2_H_REG, (pdata->iec_ta_ploss_thresh_2 >> 8)); + + pr_info("%s: iec_ta_ploss_freq_thresh_1 = %d\n", __func__, pdata->iec_ta_ploss_freq_thresh_1); + mfc_reg_write(charger->client, MFC_IEC_TA_PLOSS_FREQ_THRESH1_REG, pdata->iec_ta_ploss_freq_thresh_1); + + pr_info("%s: iec_ta_ploss_freq_thresh_2 = %d\n", __func__, pdata->iec_ta_ploss_freq_thresh_2); + mfc_reg_write(charger->client, MFC_IEC_TA_PLOSS_FREQ_THRESH2_REG, pdata->iec_ta_ploss_freq_thresh_2); + + pr_info("%s: iec_ploss_fod_enable = %d\n", __func__, pdata->iec_ploss_fod_enable); + mfc_reg_write(charger->client, MFC_IEC_PLOSS_FOD_ENABLE_REG, pdata->iec_ploss_fod_enable); + + pr_info("%s: iec_qfod_enable = %d\n", __func__, pdata->iec_qfod_enable); + mfc_reg_write(charger->client, MFC_IEC_QFOD_ENABLE_REG, pdata->iec_qfod_enable); +} + +/* 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_H_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_set_tx_conflict_current(charger, charger->pdata->tx_conflict_curr); + mfc_set_iec_params(charger, charger->pdata); + mfc_reg_write(charger->client, MFC_TX_IUNO_HYS_REG, 0x50); + mfc_reg_write(charger->client, MFC_DEMOD1_REG, 0x00); + mfc_reg_write(charger->client, MFC_DEMOD2_REG, 0x00); + mfc_reg_write(charger->client, MFC_MST_MODE_SEL_REG, 0x03); /* set TX-ON mode */ + mfc_set_cep_timeout(charger, charger->pdata->cep_timeout); // this is only for CAN + 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; + + 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); + + pr_info("@MPP @EPP %s: Tx id: 0x%06X, Tx nego done power: %dmW, Tx potential power: %dmW, Tx negotiable power: %dmW\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); + + mfc_fod_set_vendor_id(charger->fod, (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->mpp_epp_vout; + charger->vout_mode = charger->pdata->mpp_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)) + mfc_fod_set_vout(charger->fod, 5500); + else + mfc_fod_set_vout(charger->fod, 12000); + mfc_fod_set_op_mode(charger->fod, WPC_OP_MODE_EPP); + mfc_set_online(charger, SEC_BATTERY_CABLE_WIRELESS_EPP_FAKE); + } +} + +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_l = 0, irq_src_h = 0, irq_src1_l = 0, irq_src1_h = 0; + u8 status_l = 0, status_h = 0, status1_l = 0, status1_h = 0; + 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__); + __pm_relax(charger->wpc_ws); + return IRQ_HANDLED; + } + + ret = mfc_reg_read(charger->client, MFC_INT_A_L_REG, &irq_src_l); + 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_read(charger->client, MFC_INT_A_H_REG, &irq_src_h); + if (ret < 0) { + pr_err("%s: Failed to read INT_A_H_REG: %d\n", __func__, ret); + __pm_relax(charger->wpc_ws); + return IRQ_HANDLED; + } + + ret = mfc_reg_read(charger->client, MFC_INT_B_L_REG, &irq_src1_l); + if (ret < 0) { + pr_err("%s: Failed to read INT_B_L_REG: %d\n", __func__, ret); + __pm_relax(charger->wpc_ws); + return IRQ_HANDLED; + } + ret = mfc_reg_read(charger->client, MFC_INT_B_H_REG, &irq_src1_h); + if (ret < 0) { + pr_err("%s: Failed to read INT_B_H_REG: %d\n", __func__, ret); + __pm_relax(charger->wpc_ws); + return IRQ_HANDLED; + } + + ret = mfc_reg_read(charger->client, MFC_STATUS_L_REG, &status_l); + if (ret < 0) { + pr_err("%s: Failed to read STATUS_L_REG: %d\n", __func__, ret); + __pm_relax(charger->wpc_ws); + return IRQ_HANDLED; + } + ret = mfc_reg_read(charger->client, MFC_STATUS_H_REG, &status_h); + if (ret < 0) { + pr_err("%s: Failed to read STATUS_H_REG: %d\n", __func__, ret); + __pm_relax(charger->wpc_ws); + return IRQ_HANDLED; + } + + ret = mfc_reg_read(charger->client, MFC_STATUS1_L_REG, &status1_l); + if (ret < 0) { + pr_err("%s: Failed to read STATUS1_L_REG: %d\n", __func__, ret); + __pm_relax(charger->wpc_ws); + return IRQ_HANDLED; + } + ret = mfc_reg_read(charger->client, MFC_STATUS1_H_REG, &status1_h); + if (ret < 0) { + pr_err("%s: Failed to read STATUS1_H_REG: %d\n", __func__, ret); + __pm_relax(charger->wpc_ws); + return IRQ_HANDLED; + } + pr_info("%s: interrupt source(0x%x), status(0x%x)\n", __func__, + ((irq_src1_h << 24) | (irq_src1_l << 16) | (irq_src_h << 8) | irq_src_l), + ((status1_h << 24) | (status1_l << 16) | (status_h << 8) | status_l)); + + if (irq_src1_l & MFC_INTB_L_MPP_SUPPROT_MASK) { + if (status1_l & MFC_STAT1_L_MPP_SUPPROT_MASK) + pr_info("@MPP %s:Tx pad support MPP IRQ\n", __func__); + else + pr_info("@MPP %s:Tx pad not support MPP IRQ, Rx will enter EPP mode\n", __func__); + } + + if (irq_src1_l & MFC_INTB_L_EPP_SUPPROT_MASK) { + if (status1_l & 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_src1_l & 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_src1_l & 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_src1_l & MFC_INTB_L_EPP_NEGO_FAIL_MASK) { + pr_info("@EPP %s:Rx EPP nego fail IRQ\n", __func__); + } + + if (irq_src1_l & 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_src1_l & 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_src1_l & MFC_INTB_L_EXIT_CLOAK_MASK) { + u8 cloak_exit_reason; + + 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)\n", + __func__, cloak_exit_reason, charger->mpp_cloak); + + value.intval = 0; + psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_MPP_CLOAK, value); + } + + if (irq_src_h & MFC_INTA_H_AC_MISSING_DET_MASK) { + pr_info("%s: 1AC Missing ! : MFC is on\n", __func__); + mfc_mst_routine(charger, irq_src_l, irq_src_h); + } + + if (irq_src_l & 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_l & MFC_INTA_L_OVER_VOL_MASK) || (irq_src_l & MFC_INTA_L_OVER_CURR_MASK)) + pr_info("%s: ABNORMAL STAT IRQ !\n", __func__); + + if (irq_src_l & 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_l & MFC_INTA_L_STAT_VRECT_MASK) { + int epp_ref_qf = 0, epp_ref_rf = 0; + + mfc_fod_get_ext(charger->fod, MFC_FOD_EXT_EPP_REF_QF, &epp_ref_qf); + 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); + /* 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); + } + + 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_l & MFC_INTA_L_TXCONFLICT_MASK) { + pr_info("@Tx_Mode %s: TXCONFLICT IRQ !\n", __func__); + if (charger->wc_tx_enable && (status_l & MFC_STAT_L_TXCONFLICT_MASK)) { + value.intval = BATT_TX_EVENT_WIRELESS_TX_ETC; + end_irq = true; + goto INT_END; + } + } + + if (irq_src_h & 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_h & 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_h & 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_l & MFC_INTA_L_STAT_VRECT_MASK) + pr_info("%s: VRECT SIGNAL !\n", __func__); + } + } + + /* when rx is not detacted in 3 mins */ + if (irq_src_h & MFC_INTA_H_TX_MODE_RX_NOT_DET_MASK) { + if (!(irq_src_h & 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_h & 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_h & 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_H_REG, 0, MFC_STAT_H_ADT_SENT_MASK); + } + + if (irq_src_h & 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_h & 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_h & MFC_INTA_H_TX_OCP_MASK) + pr_info("%s: TX Over Current IRQ !\n", __func__); + +INT_END: + ret = mfc_reg_write(charger->client, MFC_INT_A_CLEAR_L_REG, irq_src_l); // clear int + ret = mfc_reg_write(charger->client, MFC_INT_A_CLEAR_H_REG, irq_src_h); // clear int + ret = mfc_reg_write(charger->client, MFC_INT_B_CLEAR_L_REG, irq_src1_l); // clear int + ret = mfc_reg_write(charger->client, MFC_INT_B_CLEAR_H_REG, irq_src1_h); // clear int + mfc_set_cmd_l_reg(charger, MFC_CMD_CLEAR_INT_MASK, MFC_CMD_CLEAR_INT_MASK); // command + ret = mfc_reg_read(charger->client, MFC_STATUS_L_REG, &status_l); + ret = mfc_reg_read(charger->client, MFC_STATUS_H_REG, &status_h); + ret = mfc_reg_read(charger->client, MFC_STATUS1_L_REG, &status1_l); + ret = mfc_reg_read(charger->client, MFC_STATUS1_H_REG, &status1_h); + pr_info("%s: status after clear irq, status(0x%x)\n", __func__, + ((status1_h << 24) | (status1_l << 16) | (status_h << 8) | status_l)); + + /* 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; +} + +static void mfc_chg_parse_iec_data(struct device_node *np, mfc_charger_platform_data_t *pdata) +{ + int ret = 0; + + ret = of_property_read_u32(np, "battery,wireless20_iec_qfod_enable", &pdata->iec_qfod_enable); + if (ret < 0) { + pr_info("%s: fail to read iec_qfod_enable\n", __func__); + pdata->iec_qfod_enable = 0x00; + } else { + pr_info("%s: wireless20_iec_qfod_enable = %d\n", __func__, pdata->iec_qfod_enable); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_q_thresh_1", &pdata->iec_q_thresh_1); + if (ret < 0) { + pr_info("%s: fail to read iec_q_thresh_1\n", __func__); + pdata->iec_q_thresh_1 = 0x46; + } else { + pr_info("%s: wireless20_iec_q_thresh_1 = %d\n", __func__, pdata->iec_q_thresh_1); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_q_thresh_2", &pdata->iec_q_thresh_2); + if (ret < 0) { + pr_info("%s: fail to read iec_q_thresh_2\n", __func__); + pdata->iec_q_thresh_2 = 0x3c; + } else { + pr_info("%s: wireless20_iec_q_thresh_2 = %d\n", __func__, pdata->iec_q_thresh_2); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_fres_thresh_1", &pdata->iec_fres_thresh_1); + if (ret < 0) { + pr_info("%s: fail to read iec_fres_thresh_1\n", __func__); + pdata->iec_fres_thresh_1 = 0x47; + } else { + pr_info("%s: wireless20_iec_fres_thresh_1 = %d\n", __func__, pdata->iec_fres_thresh_1); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_fres_thresh_2", &pdata->iec_fres_thresh_2); + if (ret < 0) { + pr_info("%s: fail to read iec_fres_thresh_2\n", __func__); + pdata->iec_fres_thresh_2 = 0x55; + } else { + pr_info("%s: wireless20_iec_fres_thresh_2 = %d\n", __func__, pdata->iec_fres_thresh_2); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_power_limit_thresh", &pdata->iec_power_limit_thresh); + if (ret < 0) { + pr_info("%s: fail to read iec_power_limit_thresh\n", __func__); + pdata->iec_power_limit_thresh = 0x190; + } else { + pr_info("%s: wireless20_iec_power_limit_thresh = %d\n", __func__, pdata->iec_power_limit_thresh); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_ploss_thresh_1", &pdata->iec_ploss_thresh_1); + if (ret < 0) { + pr_info("%s: fail to read iec_ploss_thresh_1\n", __func__); + pdata->iec_ploss_thresh_1 = 0x384; + } else { + pr_info("%s: wireless20_iec_ploss_thresh_1 = %d\n", __func__, pdata->iec_ploss_thresh_1); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_ploss_thresh_2", &pdata->iec_ploss_thresh_2); + if (ret < 0) { + pr_info("%s: fail to read iec_ploss_thresh_2\n", __func__); + pdata->iec_ploss_thresh_2 = 0x384; + } else { + pr_info("%s: wireless20_iec_ploss_thresh_2 = %d\n", __func__, pdata->iec_ploss_thresh_2); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_ploss_freq_thresh_1", &pdata->iec_ploss_freq_thresh_1); + if (ret < 0) { + pr_info("%s: fail to read iec_ploss_freq_thresh_1\n", __func__); + pdata->iec_ploss_freq_thresh_1 = 0xff; + } else { + pr_info("%s: wireless20_iec_ploss_freq_thresh_1 = %d\n", __func__, pdata->iec_ploss_freq_thresh_1); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_ploss_freq_thresh_2", &pdata->iec_ploss_freq_thresh_2); + if (ret < 0) { + pr_info("%s: fail to read iec_ploss_freq_thresh_2\n", __func__); + pdata->iec_ploss_freq_thresh_2 = 0xff; + } else { + pr_info("%s: wireless20_iec_ploss_freq_thresh_2 = %d\n", __func__, pdata->iec_ploss_freq_thresh_2); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_ta_power_limit_thresh", &pdata->iec_ta_power_limit_thresh); + if (ret < 0) { + pr_info("%s: fail to read iec_ta_power_limit_thresh\n", __func__); + pdata->iec_ta_power_limit_thresh = 0x258; + } else { + pr_info("%s: wireless20_iec_ta_power_limit_thresh = %d\n", __func__, pdata->iec_ta_power_limit_thresh); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_ta_ploss_thresh_1", &pdata->iec_ta_ploss_thresh_1); + if (ret < 0) { + pr_info("%s: fail to read iec_ta_ploss_thresh_1\n", __func__); + pdata->iec_ta_ploss_thresh_1 = 0x4b0; + } else { + pr_info("%s: wireless20_iec_ta_ploss_thresh_1 = %d\n", __func__, pdata->iec_ta_ploss_thresh_1); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_ta_ploss_thresh_2", &pdata->iec_ta_ploss_thresh_2); + if (ret < 0) { + pr_info("%s: fail to read iec_ta_ploss_thresh_2\n", __func__); + pdata->iec_ta_ploss_thresh_2 = 0x4b0; + } else { + pr_info("%s: wireless20_iec_ta_ploss_thresh_2 = %d\n", __func__, pdata->iec_ta_ploss_thresh_2); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_ta_ploss_freq_thresh_1", &pdata->iec_ta_ploss_freq_thresh_1); + if (ret < 0) { + pr_info("%s: fail to read iec_ta_ploss_freq_thresh_1\n", __func__); + pdata->iec_ta_ploss_freq_thresh_1 = 0xff; + } else { + pr_info("%s: wireless20_iec_ta_ploss_freq_thresh_1 = %d\n", __func__, pdata->iec_ta_ploss_freq_thresh_1); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_ta_ploss_freq_thresh_2", &pdata->iec_ta_ploss_freq_thresh_2); + if (ret < 0) { + pr_info("%s: fail to read iec_ta_ploss_freq_thresh_2\n", __func__); + pdata->iec_ta_ploss_freq_thresh_2 = 0xff; + } else { + pr_info("%s: wireless20_iec_ta_ploss_freq_thresh_2 = %d\n", __func__, pdata->iec_ta_ploss_freq_thresh_2); + } + + ret = of_property_read_u32(np, "battery,wireless20_iec_ploss_fod_enable", &pdata->iec_ploss_fod_enable); + if (ret < 0) { + pr_info("%s: fail to read iec_ploss_fod_enable\n", __func__); + pdata->iec_ploss_fod_enable = 0x0; + } else { + pr_info("%s: wireless20_iec_ploss_fod_enable = %d\n", __func__, pdata->iec_ploss_fod_enable); + } +} + +#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; + enum of_gpio_flags irq_gpio_flags; + 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 */ + } + + 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__); + + if (carrierid_is("XAC")) { + pr_info("%s: use cep_timeout_xac\n", __func__); + ret = of_property_read_u32(np, "battery,cep_timeout_xac", + &pdata->cep_timeout); + } else { + ret = of_property_read_u32(np, "battery,cep_timeout", + &pdata->cep_timeout); + } + + if (ret < 0) { + pr_info("%s: fail to read cep_timeout\n", __func__); + pdata->cep_timeout = 0; /* default 0x11 1.7sec */ + } + + 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; + } + + ret = of_property_read_u32(np, "battery,gear_op_freq", + &pdata->gear_op_freq); + if (ret < 0) { + pr_info("%s: fail to read gear_op_freq\n", __func__); + pdata->gear_op_freq = 1450; // 145kHZ + } + + ret = of_property_read_u32(np, "battery,gear_min_op_freq", + &pdata->gear_min_op_freq); + if (ret < 0) { + pr_info("%s: fail to read gear_min_op_freq\n", __func__); + pdata->gear_min_op_freq = 1250; // 125kHZ + } + + ret = of_property_read_u32(np, "battery,tx_gear_min_op_freq_delay", + &pdata->gear_min_op_freq_delay); + if (ret < 0) { + pr_info("%s: fail to read gear_min_op_freq_delay\n", __func__); + pdata->gear_min_op_freq_delay = 0; + } + + /* wpc_det */ + ret = pdata->wpc_det = of_get_named_gpio_flags(np, "battery,wpc_det", + 0, &irq_gpio_flags); + 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 = of_get_named_gpio_flags(np, "battery,wpc_int", + 0, &irq_gpio_flags); + 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 = of_get_named_gpio_flags(np, "battery,mst_pwr_en", + 0, &irq_gpio_flags); + 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 = of_get_named_gpio_flags(np, "battery,wpc_pdrc", + 0, &irq_gpio_flags); + 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); + } + + /* wpc_pdet_b (PDET_B) */ + ret = pdata->wpc_pdet_b = of_get_named_gpio_flags(np, "battery,wpc_pdet_b", + 0, &irq_gpio_flags); + 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 = of_get_named_gpio_flags(np, "battery,wpc_en", + 0, &irq_gpio_flags); + 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 = of_get_named_gpio_flags(np, "battery,coil_sw_en", + 0, &irq_gpio_flags); + if (ret < 0) + dev_err(dev, "%s: can't coil_sw_en\r\n", __func__); + + /* ping_nen (PING nEN) */ + ret = pdata->ping_nen = of_get_named_gpio_flags(np, "battery,wpc_ping_nen", + 0, &irq_gpio_flags); + if (ret < 0) + dev_err(dev, "%s : can't wpc_ping_nen\r\n", __func__); + + ret = pdata->mag_det = of_get_named_gpio_flags(np, "battery,wpc_mag_det", + 0, &irq_gpio_flags); + if (ret < 0) + dev_err(dev, "%s: can't wpc_mag_det\r\n", __func__); + + ret = pdata->mpp_sw = of_get_named_gpio_flags(np, "battery,wpc_mpp_sw", + 0, &irq_gpio_flags); + 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); + +#if defined(CONFIG_MST_PCR) + ret = of_property_read_u32(np, "battery,mst_iset_pcr", + &pdata->mst_iset_pcr); + if (ret < 0) { + pr_info("%s: fail to read mst_iset_pcr. default set 2.8A(0x06)\n", __func__); + pdata->mst_iset_pcr = 0x06; /* 2.8A */ + } + use_pcr_fix_mode = of_property_read_bool(np, "battery,pcr_fix_mode"); + if (use_pcr_fix_mode) { + pr_info("%s: Using PCR_FIX_MODE for MST\n", __func__); + } +#endif + + ret = of_property_read_u32(np, "battery,mpp_epp_vout", + &pdata->mpp_epp_vout); + if (ret) { + pr_err("%s: mpp_epp_vout is Empty\n", __func__); + pdata->mpp_epp_vout = WIRELESS_VOUT_12V; + } + + 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; + } + + mfc_chg_parse_iec_data(np, pdata); + np = dev->of_node; + + 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 cps4038_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; + default: + return -EINVAL; + } + return i; +} + +ssize_t cps4038_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; + 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 = cps4038_chg_get_property, + .set_property = cps4038_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; +} + +static int cps4038_charger_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + 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 cps4038 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 = mfc_fod_init(charger->dev, MFC_NUM_FOD_REG, cps4038_set_fod); + if (IS_ERR(charger->fod)) + pr_err("%s: failed to init fod (ret = %ld)\n", __func__, PTR_ERR(charger->fod)); + charger->cmfet = mfc_cmfet_init(charger->dev, cps4038_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_CPS; + 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 = TX_ID_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 = 1; + 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->epp_time = 1000; + + 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_min_opfq_ws = wakeup_source_register(&client->dev, "wpc_tx_min_opfq_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_check_rx_power_ws = wakeup_source_register(&client->dev, "wpc_check_rx_power_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"); + + /* 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_min_op_freq_work, mfc_tx_min_op_freq_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_check_rx_power_work, mfc_check_rx_power_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); + + /* + * 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_L_REG, &irq_src[0]); + mfc_reg_read(charger->client, MFC_INT_A_H_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_L_REG, irq_src[0]); // clear int + mfc_reg_write(charger->client, MFC_INT_A_CLEAR_H_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, &cps4038_sbw_op); + dev_info(&client->dev, "%s: MFC cps4038 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_min_opfq_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); +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 cps4038_charger_remove(struct i2c_client *client) +#else +static void cps4038_charger_remove(struct i2c_client *client) +#endif +{ + struct mfc_charger_data *charger = i2c_get_clientdata(client); + + alarm_cancel(&charger->phm_alarm); + +#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 cps4038_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_min_op_freq_work); + 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); + + /* 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 cps4038_charger_id_table[] = { + { "cps4038-charger", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, cps4038_charger_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id cps4038_charger_match_table[] = { + { .compatible = "cps,cps4038-charger",}, + {}, +}; +MODULE_DEVICE_TABLE(of, cps4038_charger_match_table); +#else +#define cps4038_charger_match_table NULL +#endif + +const struct dev_pm_ops cps4038_pm = { + SET_SYSTEM_SLEEP_PM_OPS(mfc_charger_suspend, mfc_charger_resume) +}; + +static struct i2c_driver cps4038_charger_driver = { + .driver = { + .name = "cps4038-charger", + .owner = THIS_MODULE, +#if defined(CONFIG_PM) + .pm = &cps4038_pm, +#endif /* CONFIG_PM */ + .of_match_table = cps4038_charger_match_table, + }, + .shutdown = cps4038_charger_shutdown, + .probe = cps4038_charger_probe, + .remove = cps4038_charger_remove, + .id_table = cps4038_charger_id_table, +}; + +static int __init cps4038_charger_init(void) +{ + pr_info("%s\n", __func__); + return i2c_add_driver(&cps4038_charger_driver); +} + +static void __exit cps4038_charger_exit(void) +{ + pr_info("%s\n", __func__); + i2c_del_driver(&cps4038_charger_driver); +} + +module_init(cps4038_charger_init); +module_exit(cps4038_charger_exit); + +MODULE_DESCRIPTION("Samsung CPS4038 Charger Driver"); +MODULE_AUTHOR("Samsung Electronics"); +MODULE_LICENSE("GPL"); diff --git a/drivers/battery/wireless/cps4038_charger.dtsi b/drivers/battery/wireless/cps4038_charger.dtsi new file mode 100755 index 000000000000..c406451992f6 --- /dev/null +++ b/drivers/battery/wireless/cps4038_charger.dtsi @@ -0,0 +1,235 @@ +#include + +&smd { + sb_tx: sb-tx { + aov { + high_freq = <144>; + }; + }; +}; + +&tlmm { + cps_irq_default: cps_irq_default { + GPIO_CONFIG_PUD_DRV(AP,tlmm,84, FUNC_INPUT_WAKEUP, PULL_NONE, DRV_LV1); + }; +}; + +&pm8550_gpios { + cps_det_default: cps_det_default { + GPIO_CONFIG_PUD(PM,pm8550_gpios,9, FUNC_INPUT_WAKEUP, PULL_NONE); + }; +}; + +&tlmm { + cps_en_default: cps_en_default { + GPIO_CONFIG_PUD(AP,tlmm,82, FUNC_OUTPUT_LOW, PULL_NONE); + }; +}; + +#if 1 +&pm8550ve_gpios { + cps_pdrc_default: cps_pdrc_default { + GPIO_CONFIG_PUD(PM,pm8550ve_gpios,6, FUNC_INPUT_WAKEUP, PULL_NONE); + }; +}; +#endif + +#if 1 +&tlmm { + cps_ping_nen_default: cps_ping_nen_default { + GPIO_CONFIG_PUD(AP,tlmm,180, FUNC_OUTPUT_HIGH, PULL_NONE); + }; +}; +#endif + +#if 1 +&tlmm { + cps_pdet_b_default: cps_pdet_b_default { + GPIO_CONFIG_PUD(AP,tlmm,55, FUNC_INPUT_WAKEUP, PULL_NONE); + }; +}; +#endif + +#if 1 +&pm8550vs_g_gpios { + cps_mag_det_default: cps_mag_det_default { + GPIO_CONFIG_PUD(PM,pm8550vs_g_gpios,5, FUNC_OUTPUT_HIGH, PULL_NONE); + }; +}; +#endif + +&qupv3_hub_i2c7 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + clock-frequency = <100000>; + + cps4038_charger: cps4038-charger@38 { + compatible = "cps,cps4038-charger"; + reg = <0x38>; + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&cps_irq_default &cps_det_default &cps_en_default &cps_pdrc_default +#if 1 + &cps_ping_nen_default +#endif +#if 1 + &cps_pdet_b_default +#endif +#if 1 + &cps_mag_det_default +#endif + >; + + battery,wpc_int = ; /* MFC_AP_INT */ + battery,wpc_det = ; /* WPC_DET */ +#if 0 + battery,mst_pwr_en = ; /* MST_PWR_EN */ +#endif +#if 1 + battery,wpc_ping_nen = ; /* PING_NEN */ +#endif +#if 1 + battery,wpc_pdet_b = ; /* PDET_B */ +#endif + battery,wpc_en = ; /* WPC_EN */ +#if 1 + battery,wpc_pdrc = ; /* VRECT_INT */ +#endif +#if 1 + battery,wpc_mag_det = ; /* MAG_DET */ +#endif + + battery,charger_name = "max77775-charger"; + battery,fuelgauge_name = "max77775-fuelgauge"; + battery,wireless_charger_name = "cps4038-charger"; + battery,wc_cover_rpp = <0x44>; + battery,phone_fod_threshold = <0x3b>; + battery,wireless20_vout_list = ; /* 0xA5 */ + battery,wireless20_vrect_list = ; + battery,wireless20_max_power_list = ; + + battery,buds_fod_ta_thresh = <0x0898>; /* 2200mW */ + battery,wpc_vout_ctrl_full = ; + battery,mis_align_guide; + battery,mis_align_target_vout = <5000>; + battery,mpp_epp_vout = ; + + fod_list { + count = <1>; + + pad_0x00 { /* DEFAULT PAD */ + bpp { /* DEFAULT OP MODE */ + flag = <(SET_FOD_CC(ADD) | SET_FOD_CV(USE_CC) | SET_FOD_FULL(ADD))>; + cc = <0x4E 0x0F 0x4E 0x10 0x4E 0x0C 0x4E 0x0C 0x4E 0x0C 0x4E 0x0C 0x4E 0x0D 0x4E 0x0D 0x4E 0x0D 0x4E 0x0D>; + full = <0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F + 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F>; + }; + + ppde { + flag = <(SET_FOD_CC(USE_DEF_OP) | SET_FOD_CV(USE_DEF_OP) | SET_FOD_FULL(USE_DEF_OP))>; + }; + + epp { + flag = <(SET_FOD_CC(USE_DEF_OP) | SET_FOD_CV(USE_DEF_OP) | SET_FOD_FULL(USE_DEF_OP))>; + }; + + mpp { + flag = <(SET_FOD_CC(USE_DEF_OP) | SET_FOD_CV(USE_DEF_OP) | SET_FOD_FULL(USE_DEF_OP))>; + }; + }; + }; + }; +}; + +/* /home/dpi/qb5_8814/workspace/P4_1788/android/kernel_platform/kmodule/battery/stable/eureka/wireless/cps4038/cps4038_charger.e1q.dtsi */ +#include + +&cps_pdrc_default { + power-source = <1>; /* need to set default MV gpio to LV */ +}; + +&cps_det_default { + power-source = <1>; /* need to set default MV gpio to LV */ +}; + +#if 1 +&cps_mag_det_default { + power-source = <1>; /* need to set default MV gpio to LV */ +}; +#endif + +&cps4038_charger { + battery,unknown_cmb_ctrl; + battery,default_clamp_volt; + battery,tx_max_op_freq = <1450>; + battery,tx_min_op_freq = <1120>; + battery,wireless20_iec_ploss_fod_enable = <0x1>; + battery,tx_fod_gain = <0x74>; + battery,buds_fod_thresh1 = <0x0DAC>; + battery,buds_fod_ta_thresh = <0x0DAC>; + battery,cep_timeout_xac = <900>; + + fod_list { + epp_ref_qf = <0x24>; + epp_ref_rf = <0x69>; + + count = <3>; + + pad_0x00 { /* DEFAULT */ + bpp { /* DEFAULT OP MODE */ + flag = <(SET_FOD_CC(ADD) | SET_FOD_CV(ADD) | SET_FOD_FULL(ADD))>; + cc = <0x52 0x0E 0x52 0x0E 0x52 0x0A 0x52 0x0A 0x52 0x0A 0x52 0x0A 0x52 0x0B 0x52 0x0B 0x52 0x0B 0x52 0x0B>; + cv = <0x52 0x16 0x52 0x16 0x52 0x12 0x52 0x12 0x52 0x12 0x52 0x12 0x52 0x13 0x52 0x13 0x52 0x13 0x52 0x13>; + full = <0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F>; + }; + + epp { + flag = <(SET_FOD_CC(ADD) | SET_FOD_CV(ADD) | SET_FOD_FULL(ADD))>; + cc = <0x65 0x1B 0x65 0x1B 0x65 0x16 0x65 0x16 0x65 0x19 0x65 0x19 0x62 0x1D 0x62 0x1D 0x62 0x1D 0x0E 0x03>; + cv = <0x65 0x23 0x65 0x23 0x65 0x1E 0x65 0x1E 0x65 0x21 0x65 0x21 0x62 0x25 0x62 0x25 0x62 0x25 0x0E 0x03>; + full = <0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0x0E 0x03>; + }; + + ppde { + flag = <(SET_FOD_CC(ADD) | SET_FOD_CV(ADD) | SET_FOD_FULL(USE_DEF_OP))>; + cc = <0xA0 0x4F 0xA0 0x4F 0xA0 0x3B 0xA0 0x3D 0xA0 0x3E 0xA0 0x3E 0xA0 0x3E 0xA0 0x3B 0xA0 0x39 0xA0 0x39>; + cv = <0xA0 0x57 0xA0 0x57 0xA0 0x43 0xA0 0x45 0xA0 0x46 0xA0 0x46 0xA0 0x46 0xA0 0x43 0xA0 0x41 0xA0 0x41>; + }; + }; + + pad_0xA3 { /* p2400 */ + epp { + flag = <(SET_FOD_CC(ADD) | SET_FOD_CV(ADD) | SET_FOD_FULL(ADD))>; + cc = <0x65 0x41 0x65 0x41 0x65 0x3C 0x65 0x3C 0x65 0x3E 0x65 0x3E 0x62 0x43 0x62 0x43 0x62 0x43 0x0E 0x0A>; + cv = <0x65 0x49 0x65 0x49 0x65 0x44 0x65 0x44 0x65 0x46 0x65 0x46 0x62 0x4B 0x62 0x4B 0x62 0x4B 0x62 0x4B>; + full = <0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F>; + }; + }; + + pad_0x18 { /* p2400 */ + epp { + flag = <(SET_FOD_CC(ADD) | SET_FOD_CV(ADD) | SET_FOD_FULL(ADD))>; + cc = <0x65 0x41 0x65 0x41 0x65 0x3C 0x65 0x3C 0x65 0x3E 0x65 0x3E 0x62 0x43 0x62 0x43 0x62 0x43 0x0E 0x0A>; + cv = <0x65 0x49 0x65 0x49 0x65 0x44 0x65 0x44 0x65 0x46 0x65 0x46 0x62 0x4B 0x62 0x4B 0x62 0x4B 0x62 0x4B>; + full = <0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F>; + }; + }; + }; +}; diff --git a/drivers/battery/wireless/cps4038_charger.h b/drivers/battery/wireless/cps4038_charger.h new file mode 100755 index 000000000000..6b4a521e8d7d --- /dev/null +++ b/drivers/battery/wireless/cps4038_charger.h @@ -0,0 +1,1340 @@ +/* + * cps4038_charger.h + * Samsung CPS4038 Charger Header + * + * Copyright (C) 2022 Samsung Electronics, Inc. + * + * 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. + * + */ + +#ifndef __WIRELESS_CHARGER_CPS4038_H +#define __WIRELESS_CHARGER_CPS4038_H __FILE__ + +#include +#include +#include +#include +#include +#include +#include "../common/sec_charging_common.h" +#include "cps4038_fod.h" +#include "cps4038_cmfet.h" + +/* REGISTER MAPS */ +#define MFC_CHIP_ID_L_REG 0x00 +#define MFC_CHIP_ID_H_REG 0x01 +#define MFC_CHIP_REVISION_REG 0x02 +#define MFC_CUSTOMER_ID_REG 0x03 +#define MFC_FW_MAJOR_REV_L_REG 0x04 +#define MFC_FW_MAJOR_REV_H_REG 0x05 +#define MFC_FW_MINOR_REV_L_REG 0x06 +#define MFC_FW_MINOR_REV_H_REG 0x07 +#define MFC_PRMC_ID_L_REG 0x0A +#define MFC_PRMC_ID_H_REG 0x0B +/* RXID BIT[0:47] */ +#define MFC_WPC_RXID_0_REG 0x10 +#define MFC_WPC_RXID_1_REG 0x11 +#define MFC_WPC_RXID_2_REG 0x12 +#define MFC_WPC_RXID_3_REGs 0x13 +#define MFC_WPC_RXID_4_REG 0x14 +#define MFC_WPC_RXID_5_REG 0x15 + +#define MFC_STATUS_L_REG 0x20 +#define MFC_STATUS_H_REG 0x21 +#define MFC_STATUS1_L_REG 0x22 +#define MFC_STATUS1_H_REG 0x23 +#define MFC_INT_A_L_REG 0x24 +#define MFC_INT_A_H_REG 0x25 +#define MFC_INT_B_L_REG 0x26 +#define MFC_INT_B_H_REG 0x27 +#define MFC_INT_A_ENABLE_L_REG 0x28 +#define MFC_INT_A_ENABLE_H_REG 0x29 +#define MFC_INT_B_ENABLE_L_REG 0x2A +#define MFC_INT_B_ENABLE_H_REG 0x2B +#define MFC_INT_A_CLEAR_L_REG 0x2C +#define MFC_INT_A_CLEAR_H_REG 0x2D +#define MFC_INT_B_CLEAR_L_REG 0x2E +#define MFC_INT_B_CLEAR_H_REG 0x2F + +//#define MFC_CTRL_STS_REG 0x28 + +#define MFC_SYS_OP_MODE_REG 0x30 +#define MFC_BATTERY_CHG_STATUS_REG 0x3A +#define MFC_EPT_REG 0x3B /* EPT(End of Power Transfer) cases. PMA has only EOC case */ +#define MFC_ADC_VOUT_L_REG 0x3C +#define MFC_ADC_VOUT_H_REG 0x3D + +#define MFC_VOUT_SET_L_REG 0x3E +#define MFC_VOUT_SET_H_REG 0x3F +#define MFC_VRECT_ADJ_REG 0x39 +#define MFC_ADC_VRECT_L_REG 0x40 +#define MFC_ADC_VRECT_H_REG 0x41 +#define MFC_ADC_IRECT_L_REG 0x42 +#define MFC_ADC_IRECT_H_REG 0x43 +#define MFC_TX_IUNO_LIMIT_L_REG 0x34 +#define MFC_TX_IUNO_LIMIT_H_REG 0x35 +#define MFC_ADC_IOUT_L_REG 0x44 +#define MFC_ADC_IOUT_H_REG 0x45 +#define MFC_ADC_DIE_TEMP_L_REG 0x46 /* 8 LSB field is used, Celsius */ +#define MFC_ADC_DIE_TEMP_H_REG 0x47 /* only 4 MSB[3:0] field is used, Celsius */ +#define MFC_TRX_OP_FREQ_L_REG 0x48 /* kHZ */ +#define MFC_TRX_OP_FREQ_H_REG 0x49 /* kHZ */ +#define MFC_RX_PING_FREQ_L_REG 0x4A /* kHZ */ +#define MFC_RX_PING_FREQ_H_REG 0x4B /* kHZ */ +#define MFC_ILIM_SET_REG 0x4C /* ILim = value * 0.05(A) */ +#define MFC_ILIM_ADJ_REG 0x4D /* AdjVal = ILIM_ADJ * 50 */ + +#define MFC_AP2MFC_CMD_L_REG 0x4E +#define MFC_AP2MFC_CMD_H_REG 0x4F + +/********************************************************************************/ +/* Below register are functionally depends on the operation mode(TX or RX mode) */ +/* RX mode */ +#define MFC_WPC_PCKT_HEADER_REG 0x50 +#define MFC_WPC_RX_DATA_COM_REG 0x51 /* WPC Rx to Tx COMMAND */ +#define MFC_WPC_RX_DATA_VALUE0_REG 0x52 +#define MFC_WPC_RX_DATA_VALUE1_REG 0x53 +#define MFC_WPC_RX_DATA_VALUE2_REG 0x54 +#define MFC_WPC_RX_DATA_VALUE3_REG 0x55 +#define MFC_WPC_RX_DATA_VALUE4_REG 0x56 +#define MFC_WPC_RX_DATA_VALUE5_REG 0x57 +#define MFC_WPC_RX_DATA_VALUE6_REG 0x58 +#define MFC_WPC_RX_DATA_VALUE7_REG 0x59 + +/* TX mode */ +#define MFC_WPC_TX_DATA_COM_REG 0x50 /* WPC Tx to Rx COMMAND */ +#define MFC_WPC_TX_DATA_VALUE0_REG 0x51 +#define MFC_WPC_TX_DATA_VALUE1_REG 0x52 +#define MFC_WPC_TX_DATA_VALUE2_REG 0x53 +#define MFC_WPC_TX_DATA_VALUE3_REG 0x54 +#define MFC_WPC_TX_DATA_VALUE4_REG 0x55 +#define MFC_WPC_TX_DATA_VALUE5_REG 0x56 +#define MFC_WPC_TX_DATA_VALUE6_REG 0x57 +#define MFC_WPC_TX_DATA_VALUE7_REG 0x58 +#define MFC_WPC_TX_DATA_VALUE8_REG 0x59 + +/* Common */ +#define MFC_WPC_TRX_DATA2_COM_REG 0x5A +#define MFC_WPC_TRX_DATA2_VALUE0_REG 0x5B +#define MFC_WPC_TRX_DATA2_VALUE1_REG 0x5C +#define MFC_WPC_TRX_DATA2_VALUE2_REG 0x5D +#define MFC_WPC_TRX_DATA2_VALUE3_REG 0x5E +#define MFC_WPC_TRX_DATA2_VALUE4_REG 0x5F +#define MFC_WPC_TRX_DATA2_VALUE5_REG 0x60 +#define MFC_WPC_TRX_DATA2_VALUE6_REG 0x61 +#define MFC_WPC_TRX_DATA2_VALUE7_REG 0x62 +#define MFC_WPC_TRX_DATA2_VALUE8_REG 0x63 +/********************************************************************************/ + +#define MFC_ADT_TIMEOUT_PKT_REG 0x16B +#define MFC_ADT_TIMEOUT_STR_REG 0x16C + +#define MFC_TX_IUNO_HYS_REG 0x36 +#define MFC_TX_IUNO_OFFSET_L_REG 0x37 +#define MFC_TX_IUNO_OFFSET_H_REG 0x38 + +#define MFC_TX_OC_FOD1_LIMIT_L_REG 0x94 +#define MFC_TX_OC_FOD1_LIMIT_H_REG 0x95 +#define MFC_TX_OC_FOD2_LIMIT_L_REG 0x96 +#define MFC_TX_OC_FOD2_LIMIT_H_REG 0x97 + +#define MFC_STARTUP_EPT_COUNTER 0x6D + +#define MFC_TX_DUTY_CYCLE 0xE6 + +/* TX Digital Ping duty, default is 45% (0x2D=45) */ +#define MFC_TX_PING_DUTY_CYCLE 0x16D /* default 0x2D */ +/* TX Max Operating Frequency, default is 145kHz (0x5AA/10=145KHz) */ +#define MFC_TX_MAX_OP_FREQ_L_REG 0x16E /* default 0xAA */ +#define MFC_TX_MAX_OP_FREQ_H_REG 0x16F /* default 0x05 */ +/* TX Min Operating Frequency, default is 111kHz (0x456/10=111KHz) */ +#define MFC_TX_MIN_OP_FREQ_L_REG 0x170 /* default 0x56 */ +#define MFC_TX_MIN_OP_FREQ_H_REG 0x171 /* default 0x4 */ +/* TX Digital Ping Frequency, default is 145kHz (0x5AA/10=145KHz) */ +#define MFC_TX_PING_FREQ_L_REG 0x64 /* default 0xAA */ +#define MFC_TX_PING_FREQ_H_REG 0x65 /* default 0x05 */ +/* TX Mode Minimum Duty Setting Register, Min_Duty, default is 20% (0x14=20) */ +#define MFC_TX_MIN_DUTY_SETTING_REG 0x66 /* default 0x14 */ +#define TX_MIN_OP_FREQ_DEFAULT 1110 /* default 111kHz */ + +#define MFC_INVERTER_CTRL_REG 0x67 +#define MFC_CMFET_CTRL_REG 0x68 + +/* RX Mode Communication Modulation FET Ctrl */ +#define MFC_MST_MODE_SEL_REG 0x69 +#define MFC_RX_OV_CLAMP_REG 0x6A +//#define MFC_RX_COMM_MOD_AFC_FET_REG 0x37 +#define MFC_RX_COMM_MOD_FET_REG 0x6B + +#define MFC_RECTMODE_REG 0x6C +#define MFC_START_EPT_COUNTER_REG 0x6D +#define MFC_CTRL_MODE_REG 0x6E +#define MFC_RC_PHM_PING_PERIOD_REG 0x6F + +#define MFC_IEC_QFOD_ENABLE_REG 0x114 +#define MFC_IEC_QVALUE_REG 0x115 +#define MFC_IEC_FRES_L_REG 0x116 +#define MFC_IEC_FRES_R_REG 0x117 +#define MFC_IEC_Q_THRESH1_REG 0x118 +#define MFC_IEC_Q_THRESH2_REG 0x119 +#define MFC_IEC_FRES_THRESH1_REG 0x11A +#define MFC_IEC_FRES_THRESH2_REG 0x11B +#define MFC_IEC_POWER_LIMIT_THRESH_L_REG 0x11C +#define MFC_IEC_POWER_LIMIT_THRESH_H_REG 0x11D +#define MFC_IEC_PLOSS_THRESH1_L_REG 0x11E +#define MFC_IEC_PLOSS_THRESH1_H_REG 0x11F +#define MFC_IEC_PLOSS_THRESH2_L_REG 0x120 +#define MFC_IEC_PLOSS_THRESH2_H_REG 0x121 +#define MFC_IEC_PLOSS_FREQ_THRESH1_REG 0x122 +#define MFC_IEC_PLOSS_FREQ_THRESH2_REG 0x123 +#define MFC_IEC_TA_POWER_LIMIT_THRESH_L_REG 0x124 +#define MFC_IEC_TA_POWER_LIMIT_THRESH_H_REG 0x125 +#define MFC_IEC_TA_PLOSS_THRESH1_L_REG 0x126 +#define MFC_IEC_TA_PLOSS_THRESH1_H_REG 0x127 +#define MFC_IEC_TA_PLOSS_THRESH2_L_REG 0x128 +#define MFC_IEC_TA_PLOSS_THRESH2_H_REG 0x129 +#define MFC_IEC_TA_PLOSS_FREQ_THRESH1_REG 0x12A +#define MFC_IEC_TA_PLOSS_FREQ_THRESH2_REG 0x12B +#define MFC_IEC_PLOSS_FOD_ENABLE_REG 0x12C + +#define MIN_DUTY_SETTING_20_DATA 20 +#define MIN_DUTY_SETTING_30_DATA 30 +#define MIN_DUTY_SETTING_50_DATA 50 + +#define MFC_WPC_FOD_0A_REG 0x70 +#define MFC_WPC_FOD_0B_REG 0x71 +#define MFC_WPC_FOD_1A_REG 0x72 +#define MFC_WPC_FOD_1B_REG 0x73 +#define MFC_WPC_FOD_2A_REG 0x74 +#define MFC_WPC_FOD_2B_REG 0x75 +#define MFC_WPC_FOD_3A_REG 0x76 +#define MFC_WPC_FOD_3B_REG 0x77 +#define MFC_WPC_FOD_4A_REG 0x78 +#define MFC_WPC_FOD_4B_REG 0x79 +#define MFC_WPC_FOD_5A_REG 0x7A +#define MFC_WPC_FOD_5B_REG 0x7B +#define MFC_WPC_FOD_6A_REG 0x7C +#define MFC_WPC_FOD_6B_REG 0x7D +#define MFC_WPC_FOD_7A_REG 0x7E +#define MFC_WPC_FOD_7B_REG 0x7F +#define MFC_WPC_FOD_8A_REG 0x80 +#define MFC_WPC_FOD_8B_REG 0x81 +#define MFC_WPC_FOD_9A_REG 0x82 +#define MFC_WPC_FOD_9B_REG 0x83 + +#define MFC_WPC_PARA_MODE_REG 0x8C + +#define MFC_WPC_FWC_FOD_0A_REG 0x100 +#define MFC_WPC_FWC_FOD_0B_REG 0x101 +#define MFC_WPC_FWC_FOD_1A_REG 0x102 +#define MFC_WPC_FWC_FOD_1B_REG 0x103 +#define MFC_WPC_FWC_FOD_2A_REG 0x104 +#define MFC_WPC_FWC_FOD_2B_REG 0x105 +#define MFC_WPC_FWC_FOD_3A_REG 0x106 +#define MFC_WPC_FWC_FOD_3B_REG 0x107 +#define MFC_WPC_FWC_FOD_4A_REG 0x108 +#define MFC_WPC_FWC_FOD_4B_REG 0x109 +#define MFC_WPC_FWC_FOD_5A_REG 0x10A +#define MFC_WPC_FWC_FOD_5B_REG 0x10B +#define MFC_WPC_FWC_FOD_6A_REG 0x10C +#define MFC_WPC_FWC_FOD_6B_REG 0x10D +#define MFC_WPC_FWC_FOD_7A_REG 0x10E +#define MFC_WPC_FWC_FOD_7B_REG 0x10F +#define MFC_WPC_FWC_FOD_8A_REG 0x100 +#define MFC_WPC_FWC_FOD_8B_REG 0x101 +#define MFC_WPC_FWC_FOD_9A_REG 0x102 +#define MFC_WPC_FWC_FOD_9B_REG 0x103 + +#define MFC_WPC_EPP_FOD_0A_REG 0x1A2 +#define MFC_WPC_EPP_FOD_0B_REG 0x1A3 +#define MFC_WPC_EPP_FOD_1A_REG 0x1A4 +#define MFC_WPC_EPP_FOD_1B_REG 0x1A5 +#define MFC_WPC_EPP_FOD_2A_REG 0x1A6 +#define MFC_WPC_EPP_FOD_2B_REG 0x1A7 +#define MFC_WPC_EPP_FOD_3A_REG 0x1A8 +#define MFC_WPC_EPP_FOD_3B_REG 0x1A9 +#define MFC_WPC_EPP_FOD_4A_REG 0x1AA +#define MFC_WPC_EPP_FOD_4B_REG 0x1AB +#define MFC_WPC_EPP_FOD_5A_REG 0x1AC +#define MFC_WPC_EPP_FOD_5B_REG 0x1AD +#define MFC_WPC_EPP_FOD_6A_REG 0x1AE +#define MFC_WPC_EPP_FOD_6B_REG 0x1AF +#define MFC_WPC_EPP_FOD_7A_REG 0x1B0 +#define MFC_WPC_EPP_FOD_7B_REG 0x1B1 +#define MFC_WPC_EPP_FOD_8A_REG 0x1B2 +#define MFC_WPC_EPP_FOD_8B_REG 0x1B3 +#define MFC_WPC_EPP_FOD_9A_REG 0x1B4 +#define MFC_WPC_EPP_FOD_9B_REG 0x1B5 + +#define MFC_PMA_FOD_0A_REG 0x84 +#define MFC_PMA_FOD_0B_REG 0x85 +#define MFC_PMA_FOD_1A_REG 0x86 +#define MFC_PMA_FOD_1B_REG 0x87 +#define MFC_PMA_FOD_2A_REG 0x88 +#define MFC_PMA_FOD_2B_REG 0x89 +#define MFC_PMA_FOD_3A_REG 0x8A +#define MFC_PMA_FOD_3B_REG 0x8B + +#define MFC_ADT_ERROR_CODE_REG 0x8D + +#define MFC_TX_FOD_GAIN_REG 0x8F +#define MFC_TX_FOD_OFFSET_L_REG 0x90 +#define MFC_TX_FOD_OFFSET_H_REG 0x91 +#define MFC_TX_FOD_THRESH1_L_REG 0x92 +#define MFC_TX_FOD_THRESH1_H_REG 0x93 +#define MFC_TX_FOD_TA_THRESH_L_REG 0x98 +#define MFC_TX_FOD_TA_THRESH_H_REG 0x99 + +#define MFX_TX_ID_VALUE_L_REG 0x9C +#define MFX_TX_ID_VALUE_H_REG 0x9D + +#define MFC_DEMOD1_REG 0x9E +#define MFC_DEMOD2_REG 0x9F + +#define MFC_TX_CONFLICT_CURRENT_REG 0xA0 +#define MFC_RECT_MODE_AP_CTRL 0xA2 + +#define MFC_FW_DATA_CODE_0 0xB0 +#define MFC_FW_DATA_CODE_1 0xB1 +#define MFC_FW_DATA_CODE_2 0xB2 +#define MFC_FW_DATA_CODE_3 0xB3 +#define MFC_FW_DATA_CODE_4 0xB4 +#define MFC_FW_DATA_CODE_5 0xB5 +#define MFC_FW_DATA_CODE_6 0xB6 +#define MFC_FW_DATA_CODE_7 0xB7 +#define MFC_FW_DATA_CODE_8 0xB8 +#define MFC_FW_DATA_CODE_9 0xB9 +#define MFC_FW_DATA_CODE_A 0xBA + +#define MFC_RX_PWR_L_REG 0xBC +#define MFC_RX_PWR_H_REG 0xBD +/* Timer code contains ASCII value. (ex. 31 means '1', 3A means ':') */ +#define MFC_FW_TIMER_CODE_0 0xC0 +#define MFC_FW_TIMER_CODE_1 0xC1 +#define MFC_FW_TIMER_CODE_2 0xC2 +#define MFC_FW_TIMER_CODE_3 0xC3 +#define MFC_FW_TIMER_CODE_4 0xC4 +#define MFC_FW_TIMER_CODE_5 0xC5 +#define MFC_FW_TIMER_CODE_6 0xC6 +#define MFC_FW_TIMER_CODE_7 0xC7 + +#define MFC_PWR_HOLD_INTERVAL_REG 0xCF + +//#define MFC_TX_FOD_THRESH2_REG 0xE3 +//#define MFC_TX_DUTY_CYCLE_REG 0xE6 + +//#define MFC_TX_PWR_L_REG 0xEC +//#define MFC_TX_PWR_H_REG 0xED + +#define MFC_RPP_SCALE_COEF_REG 0xF0 +//#define MFC_ACTIVE_LOAD_CONTROL_REG 0xF1 +/* Parameter 1: Major and Minor Version */ +#define MFC_TX_RXID1_READ_REG 0xF2 +/* Parameter 2~3: Manufacturer Code */ +#define MFC_TX_RXID2_READ_REG 0xF3 +#define MFC_TX_RXID3_READ_REG 0xF4 +/* Parameter 4~10: Basic Device Identifier */ +//#define MFC_TX_RXID4_READ_REG 0xF5 +//#define MFC_TX_RXID5_READ_REG 0xF6 +//#define MFC_TX_RXID6_READ_REG 0xF7 +//#define MFC_TX_RXID7_READ_REG 0xF8 +//#define MFC_TX_RXID8_READ_REG 0xF9 +//#define MFC_TX_RXID9_READ_REG 0xFA +//#define MFC_TX_RXID10_READ_REG 0xFB + +/* Target Vrect is ReadOnly register, and updated by every 10ms + * Its default value is 0x1A90(6800mV). + * Target_Vrect (Iout,Vout) = {Vout + 0.05} + { Vrect(Iout,5V)-Vrect(1A,5V) } * 5/9 + */ +#define MFC_TARGET_VRECT_L_REG 0x0164 /* default 0x90 */ +#define MFC_TARGET_VRECT_H_REG 0x0165 /* default 0x1A */ + +#define MFC_MPP_CLOAK_REASON_REG 0x180 +#define MFC_MPP_FULL_MODE_TRANS_TYPE_REG 0x181 +#define MFC_MPP_POWER_LEVEL_SETTING_REG 0x182 +#define MFC_MPP_GP_STATE_REG 0x183 +#define MFC_MPP_EPP_NEGO_DONE_POWER_L_REG 0x184 +#define MFC_MPP_EPP_NEGO_DONE_POWER_H_REG 0x185 +#define MFC_MPP_EPP_POTENTIAL_LOAD_POWER_L_REG 0x186 +#define MFC_MPP_EPP_POTENTIAL_LOAD_POWER_H_REG 0x187 +#define MFC_MPP_EPP_NEGOTIABLE_LOAD_POWER_L_REG 0x188 +#define MFC_MPP_EPP_NEGOTIABLE_LOAD_POWER_H_REG 0x189 +#define MFC_MPP_PTX_EXTENDED_ID0_REG 0x18A +#define MFC_MPP_PTX_EXTENDED_ID1_REG 0x18B +#define MFC_MPP_PTX_EXTENDED_ID2_REG 0x18C +#define MFC_MPP_DC_CURRENT_MOD_BASE_LIGHT_REG 0x18D +#define MFC_MPP_DC_CURRENT_MOD_BASE_HEAVY_REG 0x18E +#define MFC_MPP_DC_CURRENT_MOD_DEPTH_REG 0x18F +#define MFC_MPP_EXIT_CLOAK_REASON_REG 0x190 +#define MFC_MPP_FOD_QF_REG 0x191 +#define MFC_MPP_FOD_RF_REG 0x192 +#define MFC_MPP_ALPHA0_RX_REG 0x193 +#define MFC_MPP_ALPHA1_RX_REG 0x194 +#define MFC_MPP_ALPHA_KTH_RX_REG 0x195 +#define MFC_MPP_RX_COIL_RAC_L_REG 0x196 +#define MFC_MPP_RX_COIL_RAC_H_REG 0x197 +#define MFC_MPP_RX_CAP_ESR_L_REG 0x198 +#define MFC_MPP_RX_CAP_ESR_H_REG 0x199 +#define MFC_MPP_M_COIL_L_REG 0x19A +#define MFC_MPP_M_COIL_H_REG 0x19B +#define MFC_MPP_ALPHA_FM_L_REG 0x19C +#define MFC_MPP_ALPHA_FM_H_REG 0x19D +#define MFC_MPP_ALPHA_FM_DC_L_REG 0x19E +#define MFC_MPP_ALPHA_FM_DC_H_REG 0x19F +#define MFC_MPP_G_COIL_Tx_L_REG 0x1A0 +#define MFC_MPP_G_COIL_Tx_H_REG 0x1A1 +#define MFC_MPP_THERMAL_CTRL_REG 0x1B6 +#define MFC_TX_WPC_AUTH_SUPPORT_REG 0x1B7 + +#define MFC_CEP_TIME_OUT_REG 0x1B8 + +#define SS_ID 0x42 +#define SS_CODE 0x64 + +/* Cloak reason Register, CLOAK_REASON (0x180) */ +#define MFC_TRX_MPP_CLOAK_GENERIC 0x0 +#define MFC_TRX_MPP_CLOAK_FORCED 0x1 +#define MFC_TRX_MPP_CLOAK_THERMALLY_CONSTRAINED 0x2 +#define MFC_TRX_MPP_CLOAK_INSUFFICIENT_POWER 0x3 +#define MFC_TRX_MPP_CLOAK_COEX_MITIGATION 0x4 +#define MFC_TRX_MPP_CLOAK_END_OF_CHARGE 0x5 +#define MFC_TRX_MPP_CLOAK_PTX_INITIATED 0x6 + +/* Cloak exit reason Register, EXIT_CLOAK_REASON (0x190) */ +#define MFC_TRX_MPP_EXIT_CLOAK_NORMAL 0x0 +#define MFC_TRX_MPP_EXIT_CLOAK_FSK_FAIL 0x1 +#define MFC_TRX_MPP_EXIT_CLOAK_DET_PING_TIMEOUT 0x2 +#define MFC_TRX_MPP_EXIT_CLOAK_PING_TIMEOUT 0x3 +#define MFC_TRX_MPP_EXIT_CLOAK_PTX_ID_FAIL 0x4 + +/* full mode transfer type Register, FULL_MODE_TRANS_TYPE (0x181) */ +#define MFC_RX_MPP_FULL_MODE_TRAN_NO_POWER_RESET 0x00 +#define MFC_RX_MPP_FULL_MODE_TRAN_POWER_RESET 0x01 + +/* nego power level Register, POWER_LEVEL_SETTING (0x182) */ +#define MFC_RX_MPP_NEGO_POWER_10W 10 +#define MFC_RX_MPP_NEGO_POWER_15W 15 + +/* ADT type Register, ADT_TYPE (0x800[7:3]) */ +#define MFC_ADT_FWC_EPP_END 0x0 +#define MFC_ADT_FWC_EPP_GENERAL 0x1 +#define MFC_ADT_FWC_EPP_AUTHENTICATION 0x2 +#define MFC_ADT_FWC_EPP_RESET 0x12 + +#define MFC_ADT_MPP_RESET_ALL 0x0 +#define MFC_ADT_MPP_RESET_SINGLE 0x1 +#define MFC_ADT_MPP_ABORT 0x2 +#define MFC_ADT_MPP_COMPLETE 0x3 +#define MFC_ADT_MPP_OPEN 0x4 + +#define MFC_RX_CEP_PACKET_COUNTER0 0x029C +#define MFC_RX_CEP_PACKET_COUNTER1 0x029D +#define MFC_RX_CEP_PACKET_COUNTER2 0x029E +#define MFC_RX_CEP_PACKET_COUNTER3 0x029F +#define MFC_RX_RPP_PACKET_COUNTER0 0x02A0 +#define MFC_RX_RPP_PACKET_COUNTER1 0x02A1 +#define MFC_RX_RPP_PACKET_COUNTER2 0x02A2 +#define MFC_RX_RPP_PACKET_COUNTER3 0x02A3 +#define MFC_RX_CSP_PACKET_COUNTER0 0x02A4 +#define MFC_RX_CSP_PACKET_COUNTER1 0x02A5 +#define MFC_RX_CSP_PACKET_COUNTER2 0x02A6 +#define MFC_RX_CSP_PACKET_COUNTER3 0x02A7 +#define MFC_RX_PPP_PACKET_COUNTER0 0x02A8 +#define MFC_RX_PPP_PACKET_COUNTER1 0x02A9 +#define MFC_RX_PPP_PACKET_COUNTER2 0x02AA +#define MFC_RX_PPP_PACKET_COUNTER3 0x02AB + +/* ADT Buffer Registers, (0x0800 ~ 0x0FFF) */ +#define MFC_ADT_BUFFER_ADT_TYPE_REG 0x0800 +#define MFC_ADT_BUFFER_ADT_MSG_SIZE_REG 0x0801 +#define MFC_ADT_BUFFER_ADT_PARAM_REG 0x0804 +#define MFC_ADT_BUFFER_ADT_PARAM_MAX_REG 0x0FFF + +/* System Operating Mode Register, Sys_Op_Mode (0x2B) */ +#define PAD_MODE_MISSING 0 +#define PAD_MODE_WPC_BASIC 1 +#define PAD_MODE_WPC_ADV 2 +#define PAD_MODE_PMA_SR1 3 +#define PAD_MODE_PMA_SR1E 4 +#define PAD_MODE_UNKNOWN 5 + +/* MFC_RX_DATA_COM_REG (0x51) : RX Data Command VALUE of 0x19 PPP Heaader */ +#define WPC_COM_CLEAR_PACKET_COUNTING 0x01 +#define WPC_COM_START_PACKET_COUNTING 0x02 +#define WPC_COM_DISABLE_PACKET_COUNTING 0x03 + +/* RX Data Value1 Register (Data Sending), RX_Data_VALUE1_Out (0x51) : Function and Description */ +#define WPC_COM_UNKNOWN 0x00 +#define WPC_COM_TX_ID 0x01 +#define WPC_COM_CHG_STATUS 0x05 +#define WPC_COM_AFC_SET 0x06 +#define WPC_COM_AFC_DEBOUNCE 0x07 /* Data Values [ 0~1000mV : 0x0000~0x03E8 ], 2 bytes*/ +#define WPC_COM_SID_TAG 0x08 +#define WPC_COM_SID_TOKEN 0x09 +#define WPC_COM_TX_STANDBY 0x0A +#define WPC_COM_LED_CONTROL 0x0B /* Data Value LED Enable(0x00), LED Disable(0xFF) */ +#define WPC_COM_REQ_AFC_TX 0x0C /* Data Value (0x00) */ +#define WPC_COM_COOLING_CTRL 0x0D /* Data Value ON(0x00), OFF(0xFF) */ +#define WPC_COM_RX_ID 0x0E /* Received RX ID */ +#define WPC_COM_CHG_LEVEL 0x0F /* Battery level */ +#define WPC_COM_ENTER_PHM 0x18 /* GEAR entered PHM */ +#define WPC_COM_DISABLE_TX 0x19 /* Turn off UNO of TX, OFF(0xFF) */ +#define WPC_COM_PAD_LED 0x20 /* PAD LED */ +#define WPC_COM_REQ_PWR_BUDG 0x21 +#define WPC_COM_OP_FREQ_SET 0xD1 +#define WPC_COM_WDT_ERR 0xE7 /* Data Value WDT Error */ + +/* RX Data Value 2~5 Register (Data Sending), RX_Data_Value2_5_Out : Function and Description */ +#define RX_DATA_VAL2_5V 0x05 +#define RX_DATA_VAL2_10V 0x2C +#define RX_DATA_VAL2_12V 0x4B +#define RX_DATA_VAL2_12_5V 0x69 +#define RX_DATA_VAL2_20V 0x9B +#define RX_DATA_VAL2_TA_CONNECT_DURING_WC 0x55 +#define RX_DATA_VAL2_MISALIGN 0xFF +#define RX_DATA_VAL2_ENABLE 0x01 + +#define RX_DATA_VAL2_RXID_ACC_BUDS 0x70 +#define RX_DATA_VAL2_RXID_ACC_BUDS_MAX 0x8F + +/* MFC_TX_DATA_COM_REG (0x58) : TX Command */ +#define WPC_TX_COM_UNKNOWN 0x00 +#define WPC_TX_COM_TX_ID 0x01 +#define WPC_TX_COM_AFC_SET 0x02 +#define WPC_TX_COM_ACK 0x03 +#define WPC_TX_COM_NAK 0x04 +#define WPC_TX_COM_CHG_ERR 0x05 +#define WPC_TX_COM_WPS 0x07 +#define WPC_TX_COM_RX_POWER 0x0A +#define WPC_TX_COM_TX_PWR_BUDG 0x0C + +/* value of WPC_TX_COM_AFC_SET(0x02) */ +#define TX_AFC_SET_5V 0x00 +#define TX_AFC_SET_10V 0x01 +#define TX_AFC_SET_12V 0x02 +#define TX_AFC_SET_18V 0x03 +#define TX_AFC_SET_19V 0x04 +#define TX_AFC_SET_20V 0x05 +#define TX_AFC_SET_24V 0x06 + +/* value of WPC_TX_COM_TX_ID(0x01) */ +#define TX_ID_UNKNOWN 0x00 +#define TX_ID_SNGL_PORT_START 0x01 +#define TX_ID_VEHICLE_PAD 0x11 +#define TX_ID_PG950_D_PAD 0x14 +#define TX_ID_P1100_PAD 0x16 +#define TX_ID_P1300_PAD 0x17 +#define TX_ID_P2400_PAD_NOAUTH 0x18 +#define TX_ID_SNGL_PORT_END 0x1F +#define TX_ID_MULTI_PORT_START 0x20 +#define TX_ID_P4300_PAD 0x25 +#define TX_ID_P5400_PAD_NOAUTH 0x27 +#define TX_ID_MULTI_PORT_END 0x2F +#define TX_ID_STAND_TYPE_START 0x30 +#define TX_ID_PG950_S_PAD 0x31 +#define TX_ID_N3300_V_PAD 0x35 +#define TX_ID_N3300_H_PAD 0xF2 +#define TX_ID_STAND_TYPE_END 0x3F +#define TX_ID_BATT_PACK_TA 0x41 /* 0x40 ~ 0x41 is N/C*/ +#define TX_ID_BATT_PACK_U1200 0x42 +#define TX_ID_BATT_PACK_U3300 0x43 +#define TX_ID_BATT_PACK_END 0x4F /* reserved 0x40 ~ 0x4F for wireless battery pack */ +#define TX_ID_UNO_TX 0x72 +#define TX_ID_UNO_TX_B0 0x80 +#define TX_ID_UNO_TX_B1 0x81 +#define TX_ID_UNO_TX_B2 0x82 +#define TX_ID_UNO_TX_MAX 0x9F + +#define TX_ID_AUTH_PAD 0xA0 +#define TX_ID_P5200_PAD 0xA0 +#define TX_ID_N5200_V_PAD 0xA1 +#define TX_ID_N5200_H_PAD 0xA2 +#define TX_ID_P2400_PAD 0xA3 +#define TX_ID_P5400_PAD 0xA4 +#define TX_ID_AUTH_PAD_ACLASS_END 0xAF +#define TX_ID_AUTH_PAD_END 0xBF /* reserved 0xA1 ~ 0xBF for auth pad */ +#define TX_ID_JIG_PAD 0xED /* for factory */ +#define TX_ID_FG_PAD 0xEF /* Galaxy Friends */ +#define TX_ID_NON_AUTH_PAD 0xF0 +#define TX_ID_NON_AUTH_PAD_END 0xFF + +/* value of WPC_TX_COM_CHG_ERR(0x05) */ +#define TX_CHG_ERR_OTP 0x12 +#define TX_CHG_ERR_OCP 0x13 +#define TX_CHG_ERR_DARKZONE 0x14 +#define TX_CHG_ERR_FOD (0x20 ... 0x27) + +/* value of WPC_TX_COM_WPS 0x07) */ +#define WPS_AICL_RESET 0x01 + +/* value of WPC_TX_COM_RX_POWER(0x0A) */ +#define TX_RX_POWER_0W 0x0 +#define TX_RX_POWER_3W 0x1E +#define TX_RX_POWER_5W 0x32 +#define TX_RX_POWER_6W 0x3C +#define TX_RX_POWER_6_5W 0x41 +#define TX_RX_POWER_7_5W 0x4B +#define TX_RX_POWER_8W 0x50 +#define TX_RX_POWER_10W 0x64 +#define TX_RX_POWER_12W 0x78 +#define TX_RX_POWER_15W 0x96 +#define TX_RX_POWER_17_5W 0xAF +#define TX_RX_POWER_20W 0xC8 + +#define MFC_NUM_FOD_REG 20 + +/* BIT DEFINE of Command Register, COM_L(0x4E) */ +#define MFC_CMD_TOGGLE_PHM_SHIFT 7 +#define MFC_CMD_RESERVED6_SHIFT 6 +#define MFC_CMD_CLEAR_INT_SHIFT 5 +#define MFC_CMD_SEND_CHG_STS_SHIFT 4 +#define MFC_CMD_SEND_EOP_SHIFT 3 +#define MFC_CMD_MCU_RESET_SHIFT 2 +#define MFC_CMD_TOGGLE_LDO_SHIFT 1 +#define MFC_CMD_SEND_TRX_DATA_SHIFT 0 +#define MFC_CMD_TOGGLE_PHM_MASK (1 << MFC_CMD_TOGGLE_PHM_SHIFT) +#define MFC_CMD_RESERVED6_MASK (1 << MFC_CMD_RESERVED6_SHIFT) +#define MFC_CMD_CLEAR_INT_MASK (1 << MFC_CMD_CLEAR_INT_SHIFT) +#define MFC_CMD_SEND_CHG_STS_MASK (1 << MFC_CMD_SEND_CHG_STS_SHIFT) /* MFC MCU sends ChgStatus packet to TX */ +#define MFC_CMD_SEND_EOP_MASK (1 << MFC_CMD_SEND_EOP_SHIFT) +#define MFC_CMD_MCU_RESET_MASK (1 << MFC_CMD_MCU_RESET_SHIFT) +#define MFC_CMD_TOGGLE_LDO_MASK (1 << MFC_CMD_TOGGLE_LDO_SHIFT) +#define MFC_CMD_SEND_TRX_DATA_MASK (1 << MFC_CMD_SEND_TRX_DATA_SHIFT) + +/* Command Register, COM_H(0x4F) */ +#define MFC_CMD2_MPP_EXIT_CLOAK_SHIFT 4 +#define MFC_CMD2_MPP_ENTER_CLOAK_SHIFT 3 +#define MFC_CMD2_MPP_FULL_MODE_SHIFT 2 +#define MFC_CMD2_ADT_SENT_SHIFT 1 +#define MFC_CMD2_WP_ON_SHIFT 0 +#define MFC_CMD2_MPP_EXIT_CLOAK_MASK (1 << MFC_CMD2_MPP_EXIT_CLOAK_SHIFT) +#define MFC_CMD2_MPP_ENTER_CLOAK_MASK (1 << MFC_CMD2_MPP_ENTER_CLOAK_SHIFT) +#define MFC_CMD2_MPP_FULL_MODE_MASK (1 << MFC_CMD2_MPP_FULL_MODE_SHIFT) +#define MFC_CMD2_ADT_SENT_MASK (1 << MFC_CMD2_ADT_SENT_SHIFT) +#define MFC_CMD2_WP_ON_MASK (1 << MFC_CMD2_WP_ON_SHIFT) + +/* Chip Revision and Font Register, Chip_Rev (0x02) */ +#define MFC_CHIP_REVISION_MASK 0xf0 +#define MFC_CHIP_FONT_MASK 0x0f + +/* BIT DEFINE of Status Registers, Status_L (0x20), Status_H (0x21), Status1_L (0x22), Status1_H (0x23) */ +#define MFC_STAT_L_STAT_VOUT_SHIFT 7 +#define MFC_STAT_L_STAT_VRECT_SHIFT 6 +#define MFC_STAT_L_OP_MODE_SHIFT 5 +#define MFC_STAT_L_OVER_VOL_SHIFT 4 +#define MFC_STAT_L_OVER_CURR_SHIFT 3 +#define MFC_STAT_L_OVER_TEMP_SHIFT 2 +#define MFC_STAT_L_TXCONFLICT_SHIFT 1 +#define MFC_STAT_L_ADT_ERROR_SHIFT 0 +#define MFC_STAT_L_STAT_VOUT_MASK (1 << MFC_STAT_L_STAT_VOUT_SHIFT) +#define MFC_STAT_L_STAT_VRECT_MASK (1 << MFC_STAT_L_STAT_VRECT_SHIFT) +#define MFC_STAT_L_OP_MODE_MASK (1 << MFC_STAT_L_OP_MODE_SHIFT) +#define MFC_STAT_L_OVER_VOL_MASK (1 << MFC_STAT_L_OVER_VOL_SHIFT) +#define MFC_STAT_L_OVER_CURR_MASK (1 << MFC_STAT_L_OVER_CURR_SHIFT) +#define MFC_STAT_L_OVER_TEMP_MASK (1 << MFC_STAT_L_OVER_TEMP_SHIFT) +#define MFC_STAT_L_TXCONFLICT_MASK (1 << MFC_STAT_L_TXCONFLICT_SHIFT) +#define MFC_STAT_L_ADT_ERROR_MASK (1 << MFC_STAT_L_ADT_ERROR_SHIFT) + +#define MFC_STAT_H_TRX_DATA_RECEIVED_SHIFT 7 +#define MFC_STAT_H_TX_OCP_SHIFT 6 +#define MFC_STAT_H_TX_MODE_RX_NOT_DET_SHIFT 5 +#define MFC_STAT_H_TX_FOD_SHIFT 4 +#define MFC_STAT_H_TX_CON_DISCON_SHIFT 3 +#define MFC_STAT_H_AC_MISSING_DET_SHIFT 2 +#define MFC_STAT_H_ADT_RECEIVED_SHIFT 1 +#define MFC_STAT_H_ADT_SENT_SHIFT 0 +#define MFC_STAT_H_TRX_DATA_RECEIVED_MASK (1 << MFC_STAT_H_TRX_DATA_RECEIVED_SHIFT) +#define MFC_STAT_H_TX_OCP_MASK (1 << MFC_STAT_H_TX_OCP_SHIFT) +#define MFC_STAT_H_TX_MODE_RX_NOT_DET_MASK (1 << MFC_STAT_H_TX_MODE_RX_NOT_DET_SHIFT) +#define MFC_STAT_H_TX_FOD_MASK (1 << MFC_STAT_H_TX_FOD_SHIFT) +#define MFC_STAT_H_TX_CON_DISCON_MASK (1 << MFC_STAT_H_TX_CON_DISCON_SHIFT) +#define MFC_STAT_H_AC_MISSING_DET_MASK (1 << MFC_STAT_H_AC_MISSING_DET_SHIFT) +#define MFC_STAT_H_ADT_RECEIVED_MASK (1 << MFC_STAT_H_ADT_RECEIVED_SHIFT) +#define MFC_STAT_H_ADT_SENT_MASK (1 << MFC_STAT_H_ADT_SENT_SHIFT) + +#define MFC_STAT1_L_EPP_NEGO_FAIL_SHIFT 7 +#define MFC_STAT1_L_EPP_NEGO_PASS_SHIFT 6 +#define MFC_STAT1_L_EXIT_CLOAK_SHIFT 5 +#define MFC_STAT1_L_DECREASE_POWER_SHIFT 4 +#define MFC_STAT1_L_INCREASE_POWER_SHIFT 3 +#define MFC_STAT1_L_360K_NEGO_PASS_SHIFT 2 +#define MFC_STAT1_L_EPP_SUPPROT_SHIFT 1 +#define MFC_STAT1_L_MPP_SUPPROT_SHIFT 0 +#define MFC_STAT1_L_EPP_NEGO_FAIL_MASK (1 << MFC_STAT1_L_EPP_NEGO_FAIL_SHIFT) +#define MFC_STAT1_L_EPP_NEGO_PASS_MASK (1 << MFC_STAT1_L_EPP_NEGO_PASS_SHIFT) +#define MFC_STAT1_L_EXIT_CLOAK_MASK (1 << MFC_STAT1_L_EXIT_CLOAK_SHIFT) +#define MFC_STAT1_L_DECREASE_POWER_MASK (1 << MFC_STAT1_L_DECREASE_POWER_SHIFT) +#define MFC_STAT1_L_INCREASE_POWER_MASK (1 << MFC_STAT1_L_INCREASE_POWER_SHIFT) +#define MFC_STAT1_L_360K_NEGO_PASS_MASK (1 << MFC_STAT1_L_360K_NEGO_PASS_SHIFT) +#define MFC_STAT1_L_EPP_SUPPROT_MASK (1 << MFC_STAT1_L_EPP_SUPPROT_SHIFT) +#define MFC_STAT1_L_MPP_SUPPROT_MASK (1 << MFC_STAT1_L_MPP_SUPPROT_SHIFT) + +/* BIT DEFINE of Interrupt_A Registers, INTA_L (0x24), INTA_H (0x25), INTB_L (0x26), INTB_H (0x27) */ +#define MFC_INTA_L_STAT_VOUT_SHIFT 7 +#define MFC_INTA_L_STAT_VRECT_SHIFT 6 +#define MFC_INTA_L_OP_MODE_SHIFT 5 +#define MFC_INTA_L_OVER_VOL_SHIFT 4 +#define MFC_INTA_L_OVER_CURR_SHIFT 3 +#define MFC_INTA_L_OVER_TEMP_SHIFT 2 +#define MFC_INTA_L_TXCONFLICT_SHIFT 1 +#define MFC_INTA_L_ADT_ERROR_SHIFT 0 +#define MFC_INTA_L_STAT_VOUT_MASK (1 << MFC_INTA_L_STAT_VOUT_SHIFT) +#define MFC_INTA_L_STAT_VRECT_MASK (1 << MFC_INTA_L_STAT_VRECT_SHIFT) +#define MFC_INTA_L_OP_MODE_MASK (1 << MFC_INTA_L_OP_MODE_SHIFT) +#define MFC_INTA_L_OVER_VOL_MASK (1 << MFC_INTA_L_OVER_VOL_SHIFT) +#define MFC_INTA_L_OVER_CURR_MASK (1 << MFC_STAT_L_OVER_CURR_SHIFT) +#define MFC_INTA_L_OVER_TEMP_MASK (1 << MFC_STAT_L_OVER_TEMP_SHIFT) +#define MFC_INTA_L_TXCONFLICT_MASK (1 << MFC_STAT_L_TXCONFLICT_SHIFT) +#define MFC_INTA_L_ADT_ERROR_MASK (1 << MFC_INTA_L_ADT_ERROR_SHIFT) + +#define MFC_INTA_H_TRX_DATA_RECEIVED_SHIFT 7 +#define MFC_INTA_H_TX_OCP_SHIFT 6 +#define MFC_INTA_H_TX_MODE_RX_NOT_DET 5 +#define MFC_INTA_H_TX_FOD_SHIFT 4 +#define MFC_INTA_H_TX_CON_DISCON_SHIFT 3 +#define MFC_INTA_H_AC_MISSING_DET_SHIFT 2 +#define MFC_INTA_H_ADT_RECEIVED_SHIFT 1 +#define MFC_INTA_H_ADT_SENT_SHIFT 0 +#define MFC_INTA_H_TRX_DATA_RECEIVED_MASK (1 << MFC_INTA_H_TRX_DATA_RECEIVED_SHIFT) +#define MFC_INTA_H_TX_OCP_MASK (1 << MFC_INTA_H_TX_OCP_SHIFT) +#define MFC_INTA_H_TX_MODE_RX_NOT_DET_MASK (1 << MFC_INTA_H_TX_MODE_RX_NOT_DET) +#define MFC_INTA_H_TX_FOD_MASK (1 << MFC_INTA_H_TX_FOD_SHIFT) +#define MFC_INTA_H_TX_CON_DISCON_MASK (1 << MFC_INTA_H_TX_CON_DISCON_SHIFT) +#define MFC_INTA_H_AC_MISSING_DET_MASK (1 << MFC_INTA_H_AC_MISSING_DET_SHIFT) +#define MFC_INTA_H_ADT_RECEIVED_MASK (1 << MFC_INTA_H_ADT_RECEIVED_SHIFT) +#define MFC_INTA_H_ADT_SENT_MASK (1 << MFC_INTA_H_ADT_SENT_SHIFT) + +#define MFC_INTB_L_EPP_NEGO_FAIL_SHIFT 7 +#define MFC_INTB_L_EPP_NEGO_PASS_SHIFT 6 +#define MFC_INTB_L_EXIT_CLOAK_SHIFT 5 +#define MFC_INTB_L_DECREASE_POWER_SHIFT 4 +#define MFC_INTB_L_INCREASE_POWER_SHIFT 3 +#define MFC_INTB_L_360K_NEGO_PASS_SHIFT 2 +#define MFC_INTB_L_EPP_SUPPROT_SHIFT 1 +#define MFC_INTB_L_MPP_SUPPROT_SHIFT 0 +#define MFC_INTB_L_EPP_NEGO_FAIL_MASK (1 << MFC_INTB_L_EPP_NEGO_FAIL_SHIFT) +#define MFC_INTB_L_EPP_NEGO_PASS_MASK (1 << MFC_INTB_L_EPP_NEGO_PASS_SHIFT) +#define MFC_INTB_L_EXIT_CLOAK_MASK (1 << MFC_INTB_L_EXIT_CLOAK_SHIFT) +#define MFC_INTB_L_DECREASE_POWER_MASK (1 << MFC_INTB_L_DECREASE_POWER_SHIFT) +#define MFC_INTB_L_INCREASE_POWER_MASK (1 << MFC_INTB_L_INCREASE_POWER_SHIFT) +#define MFC_INTB_L_360K_NEGO_PASS_MASK (1 << MFC_INTB_L_360K_NEGO_PASS_SHIFT) +#define MFC_INTB_L_EPP_SUPPROT_MASK (1 << MFC_INTB_L_EPP_SUPPROT_SHIFT) +#define MFC_INTB_L_MPP_SUPPROT_MASK (1 << MFC_INTB_L_MPP_SUPPROT_SHIFT) + +/* System Operating Mode Register, Sys_op_mode(0x2B) */ +/* RX MODE[7:5] */ +#define MFC_RX_MODE_AC_MISSING 0x0 +#define MFC_RX_MODE_WPC_BASIC 0x1 +#define MFC_RX_MODE_WPC_ADV 0x2 +#define MFC_RX_MODE_PMA_SR1 0x3 +#define MFC_RX_MODE_PMA_SR1E 0x4 +#define MFC_RX_MODE_RESERVED1 0x5 +#define MFC_RX_MODE_RESERVED2 0x6 +#define MFC_RX_MODE_UNKNOWN 0x7 +#define MFC_RX_MODE_WPC_BPP 0x1 +#define MFC_RX_MODE_WPC_EPP 0x2 +#define MFC_RX_MODE_WPC_MPP_RESTRICT 0x3 +#define MFC_RX_MODE_WPC_MPP_FULL 0x4 +#define MFC_RX_MODE_WPC_MPP_CLOAK 0x5 +#define MFC_RX_MODE_WPC_MPP_NEGO 0x6 +#define MFC_RX_MODE_WPC_EPP_NEGO 0x7 + +//#if defined(CONFIG_WIRELESS_CHARGER_cps4038) +/* TX MODE[3:0] */ +#define MFC_TX_MODE_RX_MODE 0x0 +#define MFC_TX_MODE_MST_MODE1 0x1 +#define MFC_TX_MODE_MST_MODE2 0x2 +#define MFC_TX_MODE_TX_MODE 0x3 +#define MFC_TX_MODE_MST_PCR_MODE1 0x7 +#define MFC_TX_MODE_MST_PCR_MODE2 0xF +//#endif +/* TX MODE[3:0] */ +#define MFC_TX_MODE_BACK_PWR_MISSING 0x0 +#define MFC_TX_MODE_MST_ON 0x4 +#define MFC_TX_MODE_TX_MODE_ON 0x8 +#define MFC_TX_MODE_TX_ERROR 0x9 /* TX FOD, TX conflict */ +#define MFC_TX_MODE_TX_PWR_HOLD 0xA + +/* End of Power Transfer Register, EPT (0x3B) (RX only) */ +#define MFC_WPC_EPT_UNKNOWN 0 +#define MFC_WPC_EPT_END_OF_CHG 1 +#define MFC_WPC_EPT_INT_FAULT 2 +#define MFC_WPC_EPT_OVER_TEMP 3 +#define MFC_WPC_EPT_OVER_VOL 4 +#define MFC_WPC_EPT_OVER_CURR 5 +#define MFC_WPC_EPT_BATT_FAIL 6 +#define MFC_WPC_EPT_RECONFIG 7 +#define MFC_WPC_EPT_NO_RESPONSE 8 + +/* Proprietary Packet Header Register, PPP_Header VALUE(0x50) */ +#define MFC_HEADER_END_SIG_STRENGTH 0x01 /* Message Size 1 */ +#define MFC_HEADER_END_POWER_TRANSFER 0x02 /* Message Size 1 */ +#define MFC_HEADER_END_CTR_ERROR 0x03 /* Message Size 1 */ +#define MFC_HEADER_END_RECEIVED_POWER 0x04 /* Message Size 1 */ +#define MFC_HEADER_END_CHARGE_STATUS 0x05 /* Message Size 1 */ +#define MFC_HEADER_POWER_CTR_HOLD_OFF 0x06 /* Message Size 1 */ +#define MFC_HEADER_PROPRIETARY_1_BYTE 0x18 /* Message Size 1 */ +#define MFC_HEADER_PACKET_COUNTING 0x19 /* Message Size 1 */ +#define MFC_HEADER_CLOAK 0x1E +#define MFC_HEADER_AFC_CONF 0x28 /* Message Size 2 */ +#define MFC_HEADER_CONFIGURATION 0x51 /* Message Size 5 */ +#define MFC_HEADER_IDENTIFICATION 0x71 /* Message Size 7 */ +#define MFC_HEADER_EXTENDED_IDENT 0x81 /* Message Size 8 */ + +/* END CHARGE STATUS CODES IN WPC */ +#define MFC_ECS_CS100 0x64 /* CS 100 */ + +/* TX Data Command Register, TX Data_COM VALUE(0x50) */ +#define MFC_TX_DATA_COM_TX_ID 0x01 + +/* END POWER TRANSFER CODES IN WPC */ +#define MFC_EPT_CODE_UNKNOWN 0x00 +#define MFC_EPT_CODE_CHARGE_COMPLETE 0x01 +#define MFC_EPT_CODE_INTERNAL_FAULT 0x02 +#define MFC_EPT_CODE_OVER_TEMPERATURE 0x03 +#define MFC_EPT_CODE_OVER_VOLTAGE 0x04 +#define MFC_EPT_CODE_OVER_CURRENT 0x05 +#define MFC_EPT_CODE_BATTERY_FAILURE 0x06 +#define MFC_EPT_CODE_RECONFIGURE 0x07 +#define MFC_EPT_CODE_NO_RESPONSE 0x08 + +#define MFC_POWER_MODE_MASK (0x1 << 0) +#define MFC_SEND_USER_PKT_DONE_MASK (0x1 << 7) +#define MFC_SEND_USER_PKT_ERR_MASK (0x3 << 5) +#define MFC_SEND_ALIGN_MASK (0x1 << 3) +#define MFC_SEND_EPT_CC_MASK (0x1 << 0) +#define MFC_SEND_EOC_MASK (0x1 << 0) + +#define MFC_PTK_ERR_NO_ERR 0x00 +#define MFC_PTK_ERR_ERR 0x01 +#define MFC_PTK_ERR_ILLEGAL_HD 0x02 +#define MFC_PTK_ERR_NO_DEF 0x03 + +#define MFC_FW_RESULT_DOWNLOADING 2 +#define MFC_FW_RESULT_PASS 1 +#define MFC_FW_RESULT_FAIL 0 + +#define REQ_AFC_DLY 300 + +#define MFC_FW_MSG "@MFC_FW " + +/* value of TX POWER BUDGET */ +#define MFC_TX_PWR_BUDG_NONE 0x00 +#define MFC_TX_PWR_BUDG_2W 0x14 +#define MFC_TX_PWR_BUDG_5W 0x32 +#define MFC_TX_PWR_BUDG_7_5W 0x4B +#define MFC_TX_PWR_BUDG_10W 0x64 +#define MFC_TX_PWR_BUDG_12W 0x78 +#define MFC_TX_PWR_BUDG_15W 0x96 + +/* CPS F/W Update & Verification Define */ +#define ADDR_BUFFER0 0x20000800 +#define ADDR_BUFFER1 0x20001000 +#define ADDR_CMD 0x20001800 +#define ADDR_FLAG 0x20001804 +#define ADDR_BUF_SIZE 0x20001808 +#define ADDR_FW_VER 0x2000180C + +#define PGM_BUFFER0 0x10 +#define PGM_BUFFER1 0x20 +#define PGM_BUFFER2 0x30 +#define PGM_BUFFER3 0x40 +#define PGM_BUFFER0_1 0x50 +#define PGM_ERASER_0 0x60 +#define PGM_ERASER_1 0x70 +#define PGM_WR_FLAG 0x80 + +#define CACL_CRC_APP 0x90 +#define CACL_CRC_TEST 0xB0 + +#define PGM_ADDR_SET 0xC0 + +#define RUNNING 0x66 +#define PASS 0x55 +#define FAIL 0xAA +#define ILLEGAL 0x40 + +#if defined(CONFIG_MST_V2) +#define MST_MODE_ON 1 // ON Message to MFC ic +#define MST_MODE_OFF 0 // OFF Message to MFC ic +#define DELAY_FOR_MST 100 // S.LSI : 100 ms +#define MFC_MST_LDO_CONFIG_1 0x7400 +#define MFC_MST_LDO_CONFIG_2 0x7409 +#define MFC_MST_LDO_CONFIG_3 0x7418 +#define MFC_MST_LDO_CONFIG_4 0x3014 +#define MFC_MST_LDO_CONFIG_5 0x3405 +#define MFC_MST_LDO_CONFIG_6 0x3010 +#define MFC_MST_LDO_TURN_ON 0x301c +#define MFC_MST_LDO_CONFIG_8 0x343c +#define MFC_MST_OVER_TEMP_INT 0x0024 +#define MFC_ISET_PCR 0x0169 +#define MFC_RES_PCR 0x016A +#define PCR_FIX_MODE 0x0168 +#endif + +/* F/W Update & Verification ERROR CODES */ +enum { + MFC_FWUP_ERR_COMMON_FAIL = 0, + MFC_FWUP_ERR_SUCCEEDED, + MFC_FWUP_ERR_RUNNING, + + MFC_FWUP_ERR_REQUEST_FW_BIN, + + /* F/W update error */ + MFC_FWUP_ERR_WRITE_KEY_ERR, + MFC_FWUP_ERR_CLK_TIMING_ERR1, /* 5 */ + MFC_FWUP_ERR_CLK_TIMING_ERR2, + MFC_FWUP_ERR_CLK_TIMING_ERR3, + MFC_FWUP_ERR_CLK_TIMING_ERR4, + MFC_FWUP_ERR_INFO_PAGE_EMPTY, + MFC_FWUP_ERR_HALT_M0_ERR, /* 10 */ + MFC_FWUP_ERR_FAIL, + MFC_FWUP_ERR_ADDR_READ_FAIL, + MFC_FWUP_ERR_DATA_NOT_MATCH, + MFC_FWUP_ERR_OTP_LOADER_IN_RAM_ERR, + MFC_FWUP_ERR_CLR_MTP_STATUS_BYTE, /* 15 */ + MFC_FWUP_ERR_MAP_RAM_TO_OTP_ERR, + MFC_FWUP_ERR_WRITING_TO_OTP_BUFFER, + MFC_FWUP_ERR_OTF_BUFFER_VALIDATION, + MFC_FWUP_ERR_READING_OTP_BUFFER_STATUS, + MFC_FWUP_ERR_TIMEOUT_ON_BUFFER_TO_OTP, /* 20 */ + MFC_FWUP_ERR_MTP_WRITE_ERR, + MFC_FWUP_ERR_PKT_CHECKSUM_ERR, + MFC_FWUP_ERR_UNKNOWN_ERR, + MFC_FWUP_ERR_BUFFER_WRITE_IN_SECTOR, + MFC_FWUP_ERR_WRITING_FW_VERION, /* 25 */ + + /* F/W verification error */ + MFC_VERIFY_ERR_WRITE_KEY_ERR, + MFC_VERIFY_ERR_HALT_M0_ERR, + MFC_VERIFY_ERR_KZALLOC_ERR, + MFC_VERIFY_ERR_FAIL, + MFC_VERIFY_ERR_ADDR_READ_FAIL, /* 30 */ + MFC_VERIFY_ERR_DATA_NOT_MATCH, + MFC_VERIFY_ERR_MTP_VERIFIER_IN_RAM_ERR, + MFC_VERIFY_ERR_CLR_MTP_STATUS_BYTE, + MFC_VERIFY_ERR_MAP_RAM_TO_OTP_ERR, + MFC_VERIFY_ERR_UNLOCK_SYS_REG_ERR, /* 35 */ + MFC_VERIFY_ERR_LDO_CLK_2MHZ_ERR, + MFC_VERIFY_ERR_LDO_OUTPUT_5_5V_ERR, + MFC_VERIFY_ERR_ENABLE_LDO_ERR, + MFC_VERIFY_ERR_WRITING_TO_MTP_VERIFY_BUFFER, + MFC_VERIFY_ERR_START_MTP_VERIFY_ERR, /* 40 */ + MFC_VERIFY_ERR_READING_MTP_VERIFY_STATUS, + MFC_VERIFY_ERR_CRC_BUSY, + MFC_VERIFY_ERR_READING_MTP_VERIFY_PASS_FAIL, + MFC_VERIFY_ERR_CRC_ERROR, + MFC_VERIFY_ERR_UNKNOWN_ERR, /* 45 */ + MFC_VERIFY_ERR_BUFFER_WRITE_IN_SECTOR, + + MFC_REPAIR_ERR_HALT_M0_ERR, + MFC_REPAIR_ERR_MTP_REPAIR_IN_RAM, + MFC_REPAIR_ERR_CLR_MTP_STATUS_BYTE, + MFC_REPAIR_ERR_START_MTP_REPAIR_ERR, /* 50 */ + MFC_REPAIR_ERR_READING_MTP_REPAIR_STATUS, + MFC_REPAIR_ERR_READING_MTP_REPAIR_PASS_FAIL, + MFC_REPAIR_ERR_BUFFER_WRITE_IN_SECTOR, +}; + +/* PAD Vout */ +enum { + PAD_VOUT_5V = 0, + PAD_VOUT_9V, + PAD_VOUT_10V, + PAD_VOUT_12V, + PAD_VOUT_18V, + PAD_VOUT_19V, + PAD_VOUT_20V, + PAD_VOUT_24V, +}; + +enum { + MFC_ADC_VOUT = 0, + MFC_ADC_VRECT, + MFC_ADC_RX_IOUT, + MFC_ADC_DIE_TEMP, + MFC_ADC_OP_FRQ, + MFC_ADC_TX_MAX_OP_FRQ, + MFC_ADC_TX_MIN_OP_FRQ, + MFC_ADC_PING_FRQ, + MFC_ADC_TX_IOUT, + MFC_ADC_TX_VOUT, +}; + +enum { + MFC_ADDR = 0, + MFC_SIZE, + MFC_DATA, + MFC_PACKET, +}; + +ssize_t cps4038_show_attrs(struct device *dev, + struct device_attribute *attr, char *buf); + +ssize_t cps4038_store_attrs(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +#define CPS4038_ATTR(_name) \ +{ \ + .attr = {.name = #_name, .mode = 0660}, \ + .show = cps4038_show_attrs, \ + .store = cps4038_store_attrs, \ +} + +enum mfc_irq { + MFC_IRQ_STAT_VOUT = 0, + MFC_IRQ_STAT_VRECT, + MFC_IRQ_MODE_CHANGE, + MFC_IRQ_TX_DATA_RECEIVED, + MFC_IRQ_OVER_VOLT, + MFC_IRQ_OVER_CURR, + MFC_IRQ_OVER_TEMP, + MFC_IRQ_TX_OVER_CURR, + MFC_IRQ_TX_OVER_TEMP, + MFC_IRQ_TX_FOD, + MFC_IRQ_TX_CONNECT, + MFC_IRQ_NR, +}; + +enum mfc_firmware_mode { + MFC_RX_FIRMWARE = 0, + MFC_TX_FIRMWARE, +}; + +enum mfc_ic_revision { + MFC_IC_REVISION = 0, + MFC_IC_FONT, +}; + +enum mfc_chip_id { + MFC_CHIP_IDT = 1, + MFC_CHIP_LSI, + MFC_CHIP_CPS, +}; + +enum mfc_headroom { + MFC_HEADROOM_0 = 0, + MFC_HEADROOM_1, /* 0.277V */ + MFC_HEADROOM_2, /* 0.497V */ + MFC_HEADROOM_3, /* 0.650V */ + MFC_HEADROOM_4, /* 0.030V */ + MFC_HEADROOM_5, /* 0.082V */ + MFC_HEADROOM_6, /* 0.097V */ + MFC_HEADROOM_7, /* -0.600V */ +}; + +#if defined(CONFIG_WIRELESS_IC_PARAM) +extern unsigned int wireless_fw_ver_param; +extern unsigned int wireless_chip_id_param; +extern unsigned int wireless_fw_mode_param; +#endif + +struct mfc_charger_platform_data { + int pad_mode; + int wpc_det; + int irq_wpc_det; + int wpc_int; + int mst_pwr_en; + int wpc_en; + int mag_det; + int mpp_sw; + int coil_sw_en; + int wpc_pdrc; + int irq_wpc_pdrc; + int ping_nen; + int irq_wpc_int; + int wpc_pdet_b; + int irq_wpc_pdet_b; + int cs100_status; + int vout_status; + int siop_level; + int cable_type; + bool default_voreg; + int is_charging; + u32 *wireless20_vout_list; + u32 *wireless20_vrect_list; + u32 *wireless20_max_power_list; + u8 len_wc20_list; + bool ic_on_mode; + int hw_rev_changed; /* this is only for noble/zero2 */ + int otp_firmware_result; + int tx_firmware_result; + int wc_ic_grade; + int wc_ic_rev; + int otp_firmware_ver; + int tx_firmware_ver; + int vout; + int vrect; + u8 trx_data_cmd; + u8 trx_data_val; + char *wireless_charger_name; + char *wired_charger_name; + char *fuelgauge_name; + int opfq_cnt; + int mst_switch_delay; + int wc_cover_rpp; + int wc_hv_rpp; + u32 tx_fod_gain; + u32 tx_fod_offset; + u32 phone_fod_thresh1; + u32 phone_fod_ta_thresh; + u32 buds_fod_thresh1; + u32 buds_fod_ta_thresh; + u32 tx_max_op_freq; + u32 tx_min_op_freq; + u32 gear_op_freq; + u32 gear_min_op_freq; + u32 gear_min_op_freq_delay; + u32 cep_timeout; + int no_hv; + bool keep_tx_vout; + u32 wpc_vout_ctrl_full; + bool wpc_headroom_ctrl_full; + bool mis_align_guide; + bool unknown_cmb_ctrl; + bool default_clamp_volt; + u32 mis_align_target_vout; + u32 mis_align_offset; + int tx_conflict_curr; + u32 iec_qfod_enable; + u32 iec_q_thresh_1; + u32 iec_q_thresh_2; + u32 iec_fres_thresh_1; + u32 iec_fres_thresh_2; + u32 iec_power_limit_thresh; + u32 iec_ploss_thresh_1; + u32 iec_ploss_thresh_2; + u32 iec_ploss_freq_thresh_1; + u32 iec_ploss_freq_thresh_2; + u32 iec_ta_power_limit_thresh; + u32 iec_ta_ploss_thresh_1; + u32 iec_ta_ploss_thresh_2; + u32 iec_ta_ploss_freq_thresh_1; + u32 iec_ta_ploss_freq_thresh_2; + u32 iec_ploss_fod_enable; + +#if defined(CONFIG_MST_PCR) + u32 mst_iset_pcr; +#endif + u32 mpp_epp_vout; + u32 mpp_epp_def_power; + u32 mpp_epp_max_count; +}; + +#define mfc_charger_platform_data_t \ + struct mfc_charger_platform_data + +#define MST_MODE_0 0 +#define MST_MODE_2 1 + +#define MFC_BAT_DUMP_SIZE 256 + +struct mfc_charger_data { + struct i2c_client *client; + struct device *dev; + mfc_charger_platform_data_t *pdata; + struct mutex io_lock; + struct mutex wpc_en_lock; + struct mutex fw_lock; + const struct firmware *firm_data_bin; + + u8 det_state; /* ACTIVE HIGH */ + u8 pdrc_state; /* ACTIVE LOW */ + + struct power_supply *psy_chg; + struct wakeup_source *wpc_ws; + struct wakeup_source *wpc_det_ws; + struct wakeup_source *wpc_tx_ws; + struct wakeup_source *wpc_rx_ws; + struct wakeup_source *wpc_update_ws; + struct wakeup_source *wpc_tx_duty_min_ws; + struct wakeup_source *wpc_tx_min_opfq_ws; + struct wakeup_source *wpc_afc_vout_ws; + struct wakeup_source *wpc_vout_mode_ws; + struct wakeup_source *wpc_rx_det_ws; + struct wakeup_source *wpc_tx_phm_ws; + struct wakeup_source *wpc_tx_id_ws; + struct wakeup_source *wpc_tx_pwr_budg_ws; + struct wakeup_source *wpc_pdrc_ws; + struct wakeup_source *align_check_ws; + struct wakeup_source *mode_change_ws; + struct wakeup_source *wpc_cs100_ws; + struct wakeup_source *wpc_check_rx_power_ws; + struct wakeup_source *wpc_pdet_b_ws; + struct wakeup_source *wpc_rx_phm_ws; + struct wakeup_source *wpc_phm_exit_ws; + struct wakeup_source *epp_clear_ws; + struct wakeup_source *epp_count_ws; + struct workqueue_struct *wqueue; + struct work_struct wcin_work; + struct delayed_work wpc_det_work; + struct delayed_work wpc_pdrc_work; + struct delayed_work wpc_isr_work; + struct delayed_work wpc_tx_isr_work; + struct delayed_work wpc_tx_id_work; + struct delayed_work wpc_tx_pwr_budg_work; + struct delayed_work mst_off_work; + struct delayed_work wpc_int_req_work; + struct delayed_work wpc_fw_update_work; + struct delayed_work wpc_afc_vout_work; + struct delayed_work wpc_fw_booting_work; + struct delayed_work wpc_vout_mode_work; + struct delayed_work wpc_i2c_error_work; + struct delayed_work wpc_rx_type_det_work; + struct delayed_work wpc_rx_connection_work; + struct delayed_work wpc_tx_op_freq_work; + struct delayed_work wpc_tx_duty_min_work; + struct delayed_work wpc_tx_min_op_freq_work; + struct delayed_work wpc_tx_phm_work; + struct delayed_work wpc_vrect_check_work; + struct delayed_work wpc_rx_power_work; + struct delayed_work wpc_cs100_work; + struct delayed_work wpc_init_work; + struct delayed_work align_check_work; + struct delayed_work mode_change_work; + struct delayed_work wpc_check_rx_power_work; + struct delayed_work wpc_rx_phm_work; + struct delayed_work wpc_deactivate_work; + struct delayed_work wpc_phm_exit_work; + struct delayed_work epp_clear_timer_work; + struct delayed_work epp_count_work; + + struct alarm phm_alarm; + + struct mfc_fod *fod; + struct mfc_cmfet *cmfet; + + u16 addr; + int size; + int is_afc; + int pad_vout; + int is_mst_on; /* mst */ + int chip_id; + u8 rx_op_mode; + int fw_cmd; + int vout_mode; + u32 vout_by_txid; + u32 vrect_by_txid; + u32 max_power_by_txid; + int is_full_status; + int mst_off_lock; + bool is_otg_on; + int led_cover; + bool is_probed; + bool is_afc_tx; + bool pad_ctrl_by_lcd; + bool tx_id_done; + bool is_suspend; + int tx_id; + int tx_id_cnt; + bool rx_phm_status; + int rx_phm_state; + + int flicker_delay; + int flicker_vout_threshold; + + /* wireless tx */ + int tx_status; + bool initial_wc_check; + bool wc_tx_enable; + int wc_rx_type; + bool wc_rx_connected; + bool wc_rx_fod; + bool wc_ldo_status; + int non_sleep_mode_cnt; + u8 adt_transfer_status; + unsigned int current_rx_power; + u8 tx_pwr_budg; + u8 device_event; + int i2c_error_count; + int gpio_irq_missing_wa_cnt; + int input_current; + int duty_min; + int wpc_en_flag; + bool tx_device_phm; + + bool req_tx_id; + bool afc_tx_done; + int req_afc_delay; + + bool sleep_mode; + bool wc_checking_align; + struct timespec64 wc_align_check_start; + int vout_strength; + u32 mis_align_tx_try_cnt; + bool skip_phm_work_in_sleep; + bool reg_access_lock; + bool check_rx_power; + + int mfc_adc_tx_vout; + int mfc_adc_tx_iout; + int mfc_adc_ping_frq; + int mfc_adc_tx_min_op_frq; + int mfc_adc_tx_max_op_frq; + int mfc_adc_vout; + int mfc_adc_vrect; + int mfc_adc_rx_iout; + int mfc_adc_op_frq; + union mfc_fod_state now_fod_state; + union mfc_cmfet_state now_cmfet_state; + + int mpp_epp_tx_id; + int mpp_epp_nego_done_power; + int mpp_epp_tx_potential_load_power; + int mpp_epp_tx_negotiable_load_power; + int mpp_cloak; + +#if defined(CONFIG_WIRELESS_IC_PARAM) + unsigned int wireless_param_info; + unsigned int wireless_fw_ver_param; + unsigned int wireless_chip_id_param; + unsigned int wireless_fw_mode_param; +#endif + int epp_time; + int epp_count; + + char d_buf[MFC_BAT_DUMP_SIZE]; +}; + +#define fan_ctrl_pad(pad_id) (\ + (pad_id >= 0x14 && pad_id <= 0x1f) || \ + (pad_id >= 0x25 && pad_id <= 0x2f) || \ + (pad_id >= 0x30 && pad_id <= 0x3f) || \ + (pad_id >= 0x46 && pad_id <= 0x4f) || \ + (pad_id >= 0xa1 && pad_id <= 0xcf) || \ + (pad_id >= 0xd0 && pad_id <= 0xff)) + +#define opfreq_ctrl_pad(pad_id) (\ + ((pad_id >= TX_ID_NON_AUTH_PAD) && (pad_id <= TX_ID_NON_AUTH_PAD_END)) || \ + ((pad_id >= TX_ID_N5200_V_PAD) && (pad_id <= TX_ID_AUTH_PAD_ACLASS_END)) || \ + (pad_id == TX_ID_P1300_PAD) || \ + (pad_id == TX_ID_N3300_V_PAD) || \ + (pad_id == TX_ID_N3300_H_PAD) || \ + (pad_id == TX_ID_P4300_PAD)) + +#define volt_ctrl_pad(pad_id) (\ + (pad_id != TX_ID_PG950_S_PAD) && \ + (pad_id != TX_ID_PG950_D_PAD)) + +#define bpp_mode(op_mode) (\ + (op_mode == MFC_RX_MODE_WPC_BPP)) + +#define mpp_mode(op_mode) (\ + (op_mode == MFC_RX_MODE_WPC_MPP_RESTRICT) || \ + (op_mode == MFC_RX_MODE_WPC_MPP_FULL) || \ + (op_mode == MFC_RX_MODE_WPC_MPP_CLOAK) || \ + (op_mode == MFC_RX_MODE_WPC_MPP_NEGO)) + +#define epp_mode(op_mode) (\ + (op_mode == MFC_RX_MODE_WPC_EPP) || \ + (op_mode == MFC_RX_MODE_WPC_EPP_NEGO)) +#endif /* __WIRELESS_CHARGER_CPS4038_H */ diff --git a/drivers/battery/wireless/cps4038_cmfet.c b/drivers/battery/wireless/cps4038_cmfet.c new file mode 100755 index 000000000000..8653124cdbd4 --- /dev/null +++ b/drivers/battery/wireless/cps4038_cmfet.c @@ -0,0 +1,335 @@ +/* + * mfc_cmfet.c + * Samsung Mobile MFC CMFET Module + * + * Copyright (C) 2023 Samsung Electronics + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include "cps4038_charger.h" + +#define cmfet_log(str, ...) pr_info("[MFC-CMFET]:%s: "str, __func__, ##__VA_ARGS__) + +struct mfc_cmfet { + struct device *parent; + mfc_set_cmfet cb_func; + + struct mutex lock; + union mfc_cmfet_state state; + + /* threshold */ + bool unknown_cmb_ctrl; + unsigned int high_swell_cc_cv_thr; +}; + +static int mfc_cmfet_parse_dt(struct device_node *np, struct mfc_cmfet *cmfet) +{ + int ret = 0; + + cmfet->unknown_cmb_ctrl = of_property_read_bool(np, "battery,unknown_cmb_ctrl"); + + ret = of_property_read_u32(np, "high_swell_cc_cv_thr", &cmfet->high_swell_cc_cv_thr); + if (ret < 0) + cmfet->high_swell_cc_cv_thr = 70; + + cmfet_log("unknown_cmb_ctrl = %d, high_swell_cc_cv_thr = %d\n", + cmfet->unknown_cmb_ctrl, cmfet->high_swell_cc_cv_thr); + return 0; +} + +struct mfc_cmfet *mfc_cmfet_init(struct device *dev, mfc_set_cmfet cb_func) +{ + struct mfc_cmfet *cmfet; + int ret = 0; + + if (IS_ERR_OR_NULL(dev) || + (cb_func == NULL)) + return ERR_PTR(-EINVAL); + + cmfet = kzalloc(sizeof(struct mfc_cmfet), GFP_KERNEL); + if (!cmfet) + return ERR_PTR(-ENOMEM); + + ret = mfc_cmfet_parse_dt(dev->of_node, cmfet); + if (ret < 0) { + kfree(cmfet); + return ERR_PTR(ret); + } + + mutex_init(&cmfet->lock); + + cmfet->parent = dev; + cmfet->cb_func = cb_func; + cmfet->state.value = 0; + + cmfet_log("DONE!!\n"); + return cmfet; +} +EXPORT_SYMBOL(mfc_cmfet_init); + +int mfc_cmfet_init_state(struct mfc_cmfet *cmfet) +{ + if (IS_ERR(cmfet)) + return -EINVAL; + + mutex_lock(&cmfet->lock); + if (cmfet->state.value != 0) { + cmfet->state.value = 0; + + cmfet->cb_func(cmfet->parent, &cmfet->state, false, false); + } + mutex_unlock(&cmfet->lock); + return 0; +} +EXPORT_SYMBOL(mfc_cmfet_init_state); + +int mfc_cmfet_refresh(struct mfc_cmfet *cmfet) +{ + if (IS_ERR(cmfet)) + return -EINVAL; + + mutex_lock(&cmfet->lock); + if (cmfet->state.value != 0) + cmfet->cb_func(cmfet->parent, &cmfet->state, cmfet->state.cma, cmfet->state.cmb); + mutex_unlock(&cmfet->lock); + return 0; +} +EXPORT_SYMBOL(mfc_cmfet_refresh); + +static void set_init_state(union mfc_cmfet_state *state, bool cma, bool cmb) +{ + state->cma = cma; + state->cmb = cmb; +} + +static unsigned long long check_tx_default(struct mfc_cmfet *cmfet, union mfc_cmfet_state *state) +{ + if (state->high_swell) + state->cmb = (state->bat_cap > cmfet->high_swell_cc_cv_thr); + + if (state->full) + state->cmb = true; + + if (state->chg_done) + state->cmb = false; + + return state->value; +} + +static unsigned long long check_tx_unknown(struct mfc_cmfet *cmfet, union mfc_cmfet_state *state) +{ + set_init_state(state, true, (cmfet->unknown_cmb_ctrl)); + + return state->value; +} + +static unsigned long long check_tx_p1100(struct mfc_cmfet *cmfet, union mfc_cmfet_state *state) +{ + set_init_state(state, true, true); + + if (state->vout <= 5500) + state->cmb = false; + + if (state->chg_done) + state->cmb = false; + + return state->value; +} + +static unsigned long long check_tx_n5200(struct mfc_cmfet *cmfet, union mfc_cmfet_state *state) +{ + set_init_state(state, true, false); + + return check_tx_default(cmfet, state); +} + +static unsigned long long check_tx_p5200(struct mfc_cmfet *cmfet, union mfc_cmfet_state *state) +{ + set_init_state(state, true, true); + + if (state->auth) + state->cmb = false; + + return check_tx_default(cmfet, state); +} + +static unsigned long long check_cmfet_state(struct mfc_cmfet *cmfet, union mfc_cmfet_state *state) +{ + switch (state->tx_id) { + case TX_ID_UNKNOWN: + return check_tx_unknown(cmfet, state); + case TX_ID_P1100_PAD: + return check_tx_p1100(cmfet, state); + case TX_ID_N5200_V_PAD: + case TX_ID_N5200_H_PAD: + return check_tx_n5200(cmfet, state); + case TX_ID_P5200_PAD: + return check_tx_p5200(cmfet, state); + default: + break; + } + + set_init_state(state, true, true); + return check_tx_default(cmfet, state); +} + +static bool is_changed_state(union mfc_cmfet_state *state1, union mfc_cmfet_state *state2) +{ + return (state1->cma != state2->cma) || (state1->cmb != state2->cmb); +} + +static int update_cmfet_state(struct mfc_cmfet *cmfet, union mfc_cmfet_state *state) +{ + state->value = check_cmfet_state(cmfet, state); + + if (is_changed_state(state, &cmfet->state)) + cmfet->cb_func(cmfet->parent, state, state->cma, state->cmb); + + cmfet->state.value = state->value; + return 0; +} + +int mfc_cmfet_set_tx_id(struct mfc_cmfet *cmfet, int tx_id) +{ + if (IS_ERR(cmfet) || + (tx_id < 0) || (tx_id >= 256)) + return -EINVAL; + + mutex_lock(&cmfet->lock); + if (cmfet->state.tx_id != tx_id) { + union mfc_cmfet_state temp = { cmfet->state.value, }; + + temp.tx_id = tx_id; + update_cmfet_state(cmfet, &temp); + } + mutex_unlock(&cmfet->lock); + return 0; +} +EXPORT_SYMBOL(mfc_cmfet_set_tx_id); + +int mfc_cmfet_set_bat_cap(struct mfc_cmfet *cmfet, int bat_cap) +{ + if (IS_ERR(cmfet) || + (bat_cap < 0) || (bat_cap > 100)) + return -EINVAL; + + mutex_lock(&cmfet->lock); + if (cmfet->state.bat_cap != bat_cap) { + union mfc_cmfet_state temp = { cmfet->state.value, }; + + temp.bat_cap = bat_cap; + update_cmfet_state(cmfet, &temp); + } + mutex_unlock(&cmfet->lock); + return 0; +} +EXPORT_SYMBOL(mfc_cmfet_set_bat_cap); + +int mfc_cmfet_set_vout(struct mfc_cmfet *cmfet, int vout) +{ + if (IS_ERR(cmfet) || + (vout <= 0)) + return -EINVAL; + + mutex_lock(&cmfet->lock); + if (cmfet->state.vout != vout) { + union mfc_cmfet_state temp = { cmfet->state.value, }; + + temp.vout = vout; + update_cmfet_state(cmfet, &temp); + } + mutex_unlock(&cmfet->lock); + return 0; +} +EXPORT_SYMBOL(mfc_cmfet_set_vout); + +int mfc_cmfet_set_high_swell(struct mfc_cmfet *cmfet, bool state) +{ + if (IS_ERR(cmfet)) + return -EINVAL; + + mutex_lock(&cmfet->lock); + if (cmfet->state.high_swell != state) { + union mfc_cmfet_state temp = { cmfet->state.value, }; + + temp.high_swell = state; + update_cmfet_state(cmfet, &temp); + } + mutex_unlock(&cmfet->lock); + return 0; +} +EXPORT_SYMBOL(mfc_cmfet_set_high_swell); + +int mfc_cmfet_set_full(struct mfc_cmfet *cmfet, bool full) +{ + if (IS_ERR(cmfet)) + return -EINVAL; + + mutex_lock(&cmfet->lock); + if (cmfet->state.full != full) { + union mfc_cmfet_state temp = { cmfet->state.value, }; + + temp.full = full; + update_cmfet_state(cmfet, &temp); + } + mutex_unlock(&cmfet->lock); + return 0; +} +EXPORT_SYMBOL(mfc_cmfet_set_full); + +int mfc_cmfet_set_chg_done(struct mfc_cmfet *cmfet, bool chg_done) +{ + if (IS_ERR(cmfet)) + return -EINVAL; + + mutex_lock(&cmfet->lock); + if (cmfet->state.chg_done != chg_done) { + union mfc_cmfet_state temp = { cmfet->state.value, }; + + temp.chg_done = chg_done; + update_cmfet_state(cmfet, &temp); + } + mutex_unlock(&cmfet->lock); + return 0; +} +EXPORT_SYMBOL(mfc_cmfet_set_chg_done); + +int mfc_cmfet_set_auth(struct mfc_cmfet *cmfet, bool auth) +{ + if (IS_ERR(cmfet)) + return -EINVAL; + + mutex_lock(&cmfet->lock); + if (cmfet->state.auth != auth) { + union mfc_cmfet_state temp = { cmfet->state.value, }; + + temp.auth = auth; + update_cmfet_state(cmfet, &temp); + } + mutex_unlock(&cmfet->lock); + return 0; +} +EXPORT_SYMBOL(mfc_cmfet_set_auth); + +int mfc_cmfet_get_state(struct mfc_cmfet *cmfet, union mfc_cmfet_state *state) +{ + if (IS_ERR(cmfet) || + (state == NULL)) + return -EINVAL; + + mutex_lock(&cmfet->lock); + state->value = cmfet->state.value; + mutex_unlock(&cmfet->lock); + return 0; +} +EXPORT_SYMBOL(mfc_cmfet_get_state); diff --git a/drivers/battery/wireless/cps4038_cmfet.h b/drivers/battery/wireless/cps4038_cmfet.h new file mode 100755 index 000000000000..18ae2e4e4ed6 --- /dev/null +++ b/drivers/battery/wireless/cps4038_cmfet.h @@ -0,0 +1,90 @@ +/* + * mfc_cmfet.h + * Samsung Mobile MFC CMFET Header + * + * Copyright (C) 2023 Samsung Electronics, Inc. + * + * + * 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. + * + */ + +#ifndef __MFC_CMFET_H +#define __MFC_CMFET_H __FILE__ + +#include + +struct device; +struct mfc_cmfet; + +union mfc_cmfet_state { + unsigned long long value; + + struct { + unsigned cma : 1, + cmb : 1, + resv : 6, + tx_id : 8, + vout : 16, + bat_cap : 7, + full : 1, + chg_done : 1, + high_swell : 1, + auth : 1; + }; +}; + +typedef int (*mfc_set_cmfet)(struct device *dev, union mfc_cmfet_state *state, bool cma, bool cmb); + +#define MFC_CMFET_DISABLED (-ESRCH) +#if IS_ENABLED(CONFIG_WIRELESS_CHARGING) +struct mfc_cmfet *mfc_cmfet_init(struct device *dev, mfc_set_cmfet cb_func); + +int mfc_cmfet_init_state(struct mfc_cmfet *cmfet); +int mfc_cmfet_refresh(struct mfc_cmfet *cmfet); + +int mfc_cmfet_set_tx_id(struct mfc_cmfet *cmfet, int tx_id); +int mfc_cmfet_set_bat_cap(struct mfc_cmfet *cmfet, int bat_cap); +int mfc_cmfet_set_vout(struct mfc_cmfet *cmfet, int vout); +int mfc_cmfet_set_high_swell(struct mfc_cmfet *cmfet, bool state); +int mfc_cmfet_set_full(struct mfc_cmfet *cmfet, bool full); +int mfc_cmfet_set_chg_done(struct mfc_cmfet *cmfet, bool chg_done); +int mfc_cmfet_set_auth(struct mfc_cmfet *cmfet, bool auth); + +int mfc_cmfet_get_state(struct mfc_cmfet *cmfet, union mfc_cmfet_state *state); +#else +static inline struct mfc_cmfet *mfc_cmfet_init(struct device *dev, mfc_set_cmfet cb_func) +{ return ERR_PTR(MFC_CMFET_DISABLED); } + +static inline int mfc_cmfet_init_state(struct mfc_cmfet *cmfet) +{ return MFC_CMFET_DISABLED; } +static inline int mfc_cmfet_refresh(struct mfc_cmfet *cmfet) +{ return MFC_CMFET_DISABLED; } + +static inline int mfc_cmfet_set_tx_id(struct mfc_cmfet *cmfet, int tx_id) +{ return MFC_CMFET_DISABLED; } +static inline int mfc_cmfet_set_bat_cap(struct mfc_cmfet *cmfet, int bat_cap) +{ return MFC_CMFET_DISABLED; } +static inline int mfc_cmfet_set_vout(struct mfc_cmfet *cmfet, int vout) +{ return MFC_CMFET_DISABLED; } +static inline int mfc_cmfet_set_high_swell(struct mfc_cmfet *cmfet, bool state) +{ return MFC_CMFET_DISABLED; } +static inline int mfc_cmfet_set_full(struct mfc_cmfet *cmfet, bool full) +{ return MFC_CMFET_DISABLED; } +static inline int mfc_cmfet_set_chg_done(struct mfc_cmfet *cmfet, bool chg_done) +{ return MFC_CMFET_DISABLED; } +static inline int mfc_cmfet_set_auth(struct mfc_cmfet *cmfet, bool auth) +{ return MFC_CMFET_DISABLED; } + +static inline int mfc_cmfet_get_state(struct mfc_cmfet *cmfet, union mfc_cmfet_state *state) +{ return MFC_CMFET_DISABLED; } +#endif + +#endif /* __MFC_CMFET_H */ diff --git a/drivers/battery/wireless/cps4038_firmware.h b/drivers/battery/wireless/cps4038_firmware.h new file mode 100755 index 000000000000..03c35cbf83c5 --- /dev/null +++ b/drivers/battery/wireless/cps4038_firmware.h @@ -0,0 +1,293 @@ +/* + * cps4038_firmware.h + * Samsung Mobile CPS4038 FIRMWARE Header + * + * Copyright (C) 2023 Samsung Electronics, Inc. + * + * + * 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. + * + */ + +#ifndef __CPS4038_FIRMWARE_H +#define __CPS4038_FIRMWARE_H __FILE__ + +#define MFC_FW_BIN_VERSION 0x4012 +#define MFC_FW_VER_BIN_CPS 0x00C4 + +#define MFC_FLASH_FW_HEX_PATH "mfc/mfc_fw_flash.bin" +#define MFC_FW_SDCARD_BIN_PATH "wpc_fw_sdcard.bin" + +#define MFC_FLASH_FW_HEX_CPS_PATH "mfc/mfc_fw_flash_cps4038.bin" + +/* for SPU FW update */ +#define MFC_FW_SPU_BIN_PATH "mfc/mfc_fw_spu_cps4038.bin" + +static const unsigned char CPS4038_BL[0x800] = { + // CPS4038_BL_01_07_V0.2_CRC57AE + 0x78, 0x1D, 0x00, 0x20, 0xF9, 0x04, 0x00, 0x20, + 0x55, 0x01, 0x00, 0x20, 0x7D, 0x01, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x59, 0x01, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5B, 0x01, 0x00, 0x20, 0x5D, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x5F, 0x01, 0x00, 0x20, 0x5F, 0x01, 0x00, 0x20, + 0x00, 0x01, 0x01, 0x07, 0x00, 0x02, 0x00, 0x00, + 0x28, 0x05, 0x00, 0x00, 0x00, 0xF0, 0x02, 0xF8, + 0x00, 0xF0, 0x30, 0xF8, 0x0C, 0xA0, 0x30, 0xC8, + 0x08, 0x38, 0x24, 0x18, 0x2D, 0x18, 0xA2, 0x46, + 0x67, 0x1E, 0xAB, 0x46, 0x54, 0x46, 0x5D, 0x46, + 0xAC, 0x42, 0x01, 0xD1, 0x00, 0xF0, 0x22, 0xF8, + 0x7E, 0x46, 0x0F, 0x3E, 0x0F, 0xCC, 0xB6, 0x46, + 0x01, 0x26, 0x33, 0x42, 0x00, 0xD0, 0xFB, 0x1A, + 0xA2, 0x46, 0xAB, 0x46, 0x33, 0x43, 0x18, 0x47, + 0x0C, 0x04, 0x00, 0x00, 0x1C, 0x04, 0x00, 0x00, + 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, + 0x10, 0x3A, 0x01, 0xD3, 0x78, 0xC1, 0xFB, 0xD8, + 0x52, 0x07, 0x00, 0xD3, 0x30, 0xC1, 0x00, 0xD5, + 0x0B, 0x60, 0x70, 0x47, 0x1F, 0xB5, 0x1F, 0xBD, + 0x10, 0xB5, 0x10, 0xBD, 0x00, 0xF0, 0xAD, 0xF9, + 0x11, 0x46, 0xFF, 0xF7, 0xF7, 0xFF, 0x00, 0xF0, + 0x5D, 0xF9, 0x00, 0xF0, 0xC5, 0xF9, 0x03, 0xB4, + 0xFF, 0xF7, 0xF2, 0xFF, 0x03, 0xBC, 0x00, 0xF0, + 0xCB, 0xF9, 0x00, 0x00, 0xFE, 0xE7, 0xFE, 0xE7, + 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, + 0x02, 0x48, 0x03, 0x49, 0x03, 0x4A, 0x04, 0x4B, + 0x70, 0x47, 0x00, 0x00, 0x78, 0x18, 0x00, 0x20, + 0x78, 0x1D, 0x00, 0x20, 0x78, 0x19, 0x00, 0x20, + 0x78, 0x19, 0x00, 0x20, 0xFE, 0xE7, 0xA9, 0x48, + 0x01, 0x68, 0x00, 0x29, 0x08, 0xD1, 0xA8, 0x4A, + 0x0B, 0x21, 0xD1, 0x61, 0xA6, 0x4A, 0xA7, 0x49, + 0xC0, 0x32, 0x91, 0x63, 0x01, 0x21, 0x01, 0x60, + 0x70, 0x47, 0xA2, 0x48, 0x01, 0x68, 0x01, 0x29, + 0x09, 0xD1, 0xA1, 0x49, 0x03, 0x22, 0xCA, 0x61, + 0x0B, 0x22, 0xCA, 0x61, 0x9E, 0x4A, 0x00, 0x21, + 0xC0, 0x32, 0x91, 0x63, 0x01, 0x60, 0x70, 0x47, + 0x30, 0xB5, 0x5F, 0x22, 0x00, 0x23, 0x12, 0x02, + 0x90, 0x42, 0x04, 0xDB, 0x9A, 0x4A, 0x90, 0x42, + 0x01, 0xD0, 0x00, 0x20, 0x30, 0xBD, 0x96, 0x4A, + 0x90, 0x60, 0xD1, 0x60, 0x03, 0x21, 0xD1, 0x61, + 0x07, 0x20, 0xD0, 0x61, 0xD0, 0x61, 0xD0, 0x61, + 0xD0, 0x61, 0xD1, 0x61, 0x93, 0x48, 0x01, 0x24, + 0x15, 0x6A, 0x00, 0x2D, 0x05, 0xD0, 0x40, 0x1E, + 0xFA, 0xD2, 0xD4, 0x61, 0xD1, 0x61, 0x01, 0x23, + 0xF6, 0xE7, 0x08, 0x20, 0x40, 0x1E, 0xFD, 0xD2, + 0x18, 0x46, 0x30, 0xBD, 0x70, 0xB5, 0x04, 0x46, + 0x00, 0x20, 0x8B, 0x4D, 0x02, 0x46, 0x0E, 0xE0, + 0xA3, 0x5C, 0x1B, 0x02, 0x58, 0x40, 0x00, 0x23, + 0x06, 0x04, 0x02, 0xD5, 0x40, 0x00, 0x68, 0x40, + 0x00, 0xE0, 0x40, 0x00, 0x5B, 0x1C, 0x80, 0xB2, + 0x08, 0x2B, 0xF5, 0xDB, 0x52, 0x1C, 0x8A, 0x42, + 0xEE, 0xDB, 0x70, 0xBD, 0x00, 0x25, 0xFF, 0xF7, + 0xA2, 0xFF, 0x69, 0x1E, 0x7C, 0x48, 0xFF, 0xF7, + 0xBB, 0xFF, 0xFF, 0xF7, 0xAA, 0xFF, 0x7D, 0x4F, + 0x7D, 0x49, 0x08, 0x68, 0x00, 0x28, 0xFB, 0xD0, + 0x00, 0x22, 0x0A, 0x60, 0x14, 0x46, 0x01, 0x22, + 0x39, 0x68, 0x52, 0x02, 0x91, 0x42, 0x01, 0xDD, + 0x40, 0x20, 0x7B, 0xE0, 0x66, 0x22, 0x77, 0x49, + 0x90, 0x28, 0x0A, 0x60, 0x24, 0xD0, 0x06, 0xDC, + 0x10, 0x28, 0x41, 0xD0, 0x20, 0x28, 0x5B, 0xD0, + 0x80, 0x28, 0x73, 0xD1, 0x34, 0xE0, 0xB0, 0x28, + 0x02, 0xD0, 0xC0, 0x28, 0x6E, 0xD1, 0x66, 0xE0, + 0xFF, 0xF7, 0x87, 0xFF, 0x6E, 0x48, 0x84, 0x68, + 0x80, 0x14, 0x84, 0x42, 0x02, 0xDD, 0x6D, 0x48, + 0x04, 0x60, 0x45, 0xE0, 0x01, 0x20, 0x21, 0x1F, + 0x40, 0x07, 0xFF, 0xF7, 0xAF, 0xFF, 0x69, 0x4A, + 0x69, 0x49, 0x10, 0x60, 0x61, 0x18, 0xC9, 0x6F, + 0x51, 0x60, 0x88, 0x42, 0x38, 0xD1, 0x50, 0xE0, + 0xFF, 0xF7, 0x6F, 0xFF, 0xC0, 0x20, 0x84, 0x68, + 0x00, 0x2C, 0x31, 0xDB, 0xC0, 0x01, 0x84, 0x42, + 0x2E, 0xDC, 0xC8, 0x2C, 0x2C, 0xDB, 0x21, 0x1F, + 0x00, 0x20, 0xFF, 0xF7, 0x97, 0xFF, 0x01, 0x46, + 0x5C, 0x48, 0x80, 0x3C, 0x01, 0x60, 0xE2, 0x6F, + 0x42, 0x60, 0x91, 0x42, 0x20, 0xD1, 0x38, 0xE0, + 0xFF, 0xF7, 0x49, 0xFF, 0x59, 0x49, 0x50, 0x48, + 0xFF, 0xF7, 0x62, 0xFF, 0x04, 0x46, 0x11, 0xE0, + 0xFF, 0xF7, 0x41, 0xFF, 0x00, 0x26, 0x08, 0xE0, + 0x52, 0x48, 0xB1, 0x00, 0x41, 0x58, 0x28, 0x46, + 0xFF, 0xF7, 0x56, 0xFF, 0x04, 0x43, 0x2D, 0x1D, + 0x76, 0x1C, 0x38, 0x68, 0x86, 0x42, 0xF3, 0xDB, + 0x4C, 0x48, 0x1A, 0xE0, 0xFF, 0xF7, 0x3D, 0xFF, + 0x60, 0x1C, 0x1F, 0xD0, 0x00, 0x2C, 0x18, 0xD0, + 0xAA, 0x21, 0x46, 0x48, 0x01, 0x60, 0x8B, 0xE7, + 0xFF, 0xF7, 0x25, 0xFF, 0x00, 0x26, 0x08, 0xE0, + 0x47, 0x48, 0xB1, 0x00, 0x41, 0x58, 0x28, 0x46, + 0xFF, 0xF7, 0x3A, 0xFF, 0x04, 0x43, 0x2D, 0x1D, + 0x76, 0x1C, 0x38, 0x68, 0x86, 0x42, 0xF3, 0xDB, + 0x41, 0x48, 0x05, 0x60, 0xE2, 0xE7, 0x3D, 0x48, + 0x05, 0x68, 0x55, 0x20, 0x39, 0x49, 0x08, 0x60, + 0x72, 0xE7, 0xFF, 0xE7, 0x40, 0x21, 0xE0, 0xE7, + 0x3D, 0x48, 0x3C, 0x49, 0x01, 0x60, 0x3D, 0x4A, + 0x00, 0x21, 0x91, 0x60, 0x33, 0x22, 0x3C, 0x4B, + 0x92, 0x03, 0x5A, 0x60, 0x01, 0x60, 0x70, 0x47, + 0x01, 0x20, 0x3A, 0x49, 0x80, 0x04, 0x48, 0x63, + 0x38, 0x48, 0x39, 0x49, 0x40, 0x30, 0x81, 0x62, + 0x01, 0x21, 0xC9, 0x05, 0xC1, 0x62, 0x40, 0x21, + 0x41, 0x63, 0xFF, 0x21, 0x35, 0x4A, 0xBD, 0x31, + 0x11, 0x60, 0x35, 0x49, 0x41, 0x62, 0x30, 0x49, + 0x00, 0x14, 0x48, 0x60, 0x70, 0x47, 0x70, 0xB5, + 0x2D, 0x48, 0x00, 0x24, 0x80, 0x30, 0x04, 0x62, + 0x31, 0x4B, 0x30, 0x4D, 0xDD, 0x61, 0x2B, 0x49, + 0x07, 0x20, 0x40, 0x31, 0x48, 0x61, 0xFF, 0xF7, + 0xDB, 0xFF, 0xDC, 0x61, 0x18, 0x48, 0x38, 0x38, + 0xC0, 0x6B, 0x2C, 0x49, 0x88, 0x42, 0x0F, 0xD1, + 0xDD, 0x61, 0xC1, 0x21, 0x23, 0x4A, 0x00, 0x20, + 0xC9, 0x01, 0x80, 0x3A, 0x45, 0x18, 0x2D, 0x68, + 0x86, 0x18, 0x35, 0x60, 0x00, 0x1D, 0x80, 0x28, + 0xF8, 0xDB, 0xDC, 0x61, 0x01, 0x20, 0x70, 0xBD, + 0x00, 0x20, 0x70, 0xBD, 0xFF, 0x21, 0x09, 0x48, + 0x01, 0x31, 0x41, 0x60, 0x0D, 0x4A, 0x49, 0x00, + 0x11, 0x60, 0x0D, 0x4A, 0x00, 0x21, 0x11, 0x60, + 0x0C, 0x4B, 0x55, 0x22, 0x1A, 0x60, 0x01, 0x60, + 0xFF, 0xF7, 0xC9, 0xFF, 0xFF, 0xF7, 0xA4, 0xFF, + 0xFF, 0xF7, 0x08, 0xFF, 0x0C, 0x18, 0x00, 0x20, + 0x00, 0x20, 0x01, 0x40, 0x78, 0x56, 0x00, 0x00, + 0xF8, 0x60, 0x00, 0x00, 0x50, 0xC3, 0x00, 0x00, + 0x21, 0x10, 0x00, 0x00, 0x08, 0x18, 0x00, 0x20, + 0x00, 0x18, 0x00, 0x20, 0x04, 0x18, 0x00, 0x20, + 0xC0, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, + 0x80, 0xFF, 0xFF, 0x1F, 0x85, 0xE1, 0x24, 0x57, + 0x00, 0x10, 0x00, 0x20, 0x51, 0xE5, 0xCC, 0x1A, + 0x00, 0x8C, 0x00, 0x40, 0x00, 0x80, 0x00, 0x40, + 0x00, 0x00, 0x04, 0x40, 0x80, 0xF0, 0x00, 0x40, + 0x2C, 0x00, 0x00, 0x04, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x02, 0xA1, 0x02, 0x50, 0x12, 0x00, 0x00, + 0x40, 0xE7, 0x00, 0x40, 0x4E, 0x87, 0x55, 0x74, + 0x70, 0x47, 0x70, 0x47, 0x70, 0x47, 0x70, 0x47, + 0x70, 0x47, 0x75, 0x46, 0x00, 0xF0, 0x24, 0xF8, + 0xAE, 0x46, 0x05, 0x00, 0x69, 0x46, 0x53, 0x46, + 0xC0, 0x08, 0xC0, 0x00, 0x85, 0x46, 0x18, 0xB0, + 0x20, 0xB5, 0xFF, 0xF7, 0x59, 0xFE, 0x60, 0xBC, + 0x00, 0x27, 0x49, 0x08, 0xB6, 0x46, 0x00, 0x26, + 0xC0, 0xC5, 0xC0, 0xC5, 0xC0, 0xC5, 0xC0, 0xC5, + 0xC0, 0xC5, 0xC0, 0xC5, 0xC0, 0xC5, 0xC0, 0xC5, + 0x40, 0x3D, 0x49, 0x00, 0x8D, 0x46, 0x70, 0x47, + 0x10, 0xB5, 0x04, 0x46, 0xC0, 0x46, 0xC0, 0x46, + 0x20, 0x46, 0xFF, 0xF7, 0x34, 0xFE, 0x10, 0xBD, + 0x00, 0x48, 0x70, 0x47, 0x14, 0x18, 0x00, 0x20, + 0x01, 0x49, 0x18, 0x20, 0xAB, 0xBE, 0xFE, 0xE7, + 0x26, 0x00, 0x02, 0x00, 0x70, 0x47, 0x00, 0x00, + 0x03, 0x49, 0x00, 0x20, 0x48, 0x60, 0x08, 0x60, + 0x02, 0x48, 0x80, 0x47, 0x02, 0x48, 0x00, 0x47, + 0x14, 0xE1, 0x00, 0x40, 0x8B, 0x04, 0x00, 0x20, + 0xCD, 0x00, 0x00, 0x20, 0x24, 0x05, 0x00, 0x20, + 0x00, 0x08, 0x00, 0x20, 0x78, 0x15, 0x00, 0x00, + 0x10, 0x01, 0x00, 0x20, 0x16, 0x2B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#endif /* __CPS4038_FIRMWARE_H */ diff --git a/drivers/battery/wireless/cps4038_fod.c b/drivers/battery/wireless/cps4038_fod.c new file mode 100755 index 000000000000..b91a07b4a4b1 --- /dev/null +++ b/drivers/battery/wireless/cps4038_fod.c @@ -0,0 +1,721 @@ +/* + * mfc_fod.c + * Samsung Mobile MFC FOD Module + * + * Copyright (C) 2023 Samsung Electronics + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "cps4038_fod.h" + +#define fod_log(str, ...) pr_info("[MFC-FOD]:%s: "str, __func__, ##__VA_ARGS__) +#define DEFAULT_TX_IDX 0 +#define DEFAULT_OP_MODE WPC_OP_MODE_BPP +#define DEFAULT_VENDOR_ID 0x42 + +struct mfc_fod_op { + unsigned int flag; + fod_data_t *data[MFC_FOD_BAT_STATE_MAX]; +}; + +struct mfc_fod_tx { + unsigned int id; + struct mfc_fod_op op[WPC_OP_MODE_MAX]; +}; + +struct mfc_fod { + struct device *parent; + mfc_set_fod cb_func; + + struct mutex lock; + union mfc_fod_state state; + + /* fod data */ + struct mfc_fod_tx *list; + unsigned int count; + unsigned int ext[MFC_FOD_EXT_MAX]; + + /* threshold */ + unsigned int bpp_vout; + unsigned int cc_cv_thr; + unsigned int high_swell_cc_cv_thr; + unsigned int vendor_id; +}; + +static int get_op_mode_by_str(const char *str) +{ + if (str == NULL) + return WPC_OP_MODE_NONE; + + if (!strncmp(str, "ppde", 4)) + return WPC_OP_MODE_PPDE; + + if (!strncmp(str, "epp", 3)) + return WPC_OP_MODE_EPP; + + if (!strncmp(str, "mpp", 3)) + return WPC_OP_MODE_MPP; + + return WPC_OP_MODE_BPP; +} + +static const char *get_ext_str(unsigned int ext_type) +{ + switch (ext_type) { + case MFC_FOD_EXT_EPP_REF_QF: + return "epp_ref_qf"; + case MFC_FOD_EXT_EPP_REF_RF: + return "epp_ref_rf"; + } + + return "none"; +} + +static const char *get_bat_state_str(unsigned int bat_state) +{ + switch (bat_state) { + case MFC_FOD_BAT_STATE_CC: + return "cc"; + case MFC_FOD_BAT_STATE_CV: + return "cv"; + case MFC_FOD_BAT_STATE_FULL: + return "full"; + } + + return "none"; +} + +static fod_data_t *mfc_fod_parse_data(struct device_node *np, int bat_state, unsigned int fod_size) +{ + fod_data_t *data; + const u32 *p; + int len = 0, t_size = 0, ret; + + p = of_get_property(np, get_bat_state_str(bat_state), &len); + if (!p) + return NULL; + + t_size = sizeof(fod_data_t); + data = kcalloc(fod_size, t_size, GFP_KERNEL); + if (!data) + return NULL; + + if (fod_size != (len / t_size)) { + fod_log("not match the size(%d, %d, %d)\n", fod_size, len, t_size); + goto err_size; + } + + switch (t_size) { + case sizeof(u64): + ret = of_property_read_u64_array(np, get_bat_state_str(bat_state), + (u64 *)data, fod_size); + break; + case sizeof(u32): + ret = of_property_read_u32_array(np, get_bat_state_str(bat_state), + (u32 *)data, fod_size); + break; + case sizeof(u16): + ret = of_property_read_u16_array(np, get_bat_state_str(bat_state), + (u16 *)data, fod_size); + break; + case sizeof(u8): + ret = of_property_read_u8_array(np, get_bat_state_str(bat_state), + (u8 *)data, fod_size); + break; + default: + fod_log("invalid t size(%d)\n", t_size); + goto err_size; + } + + if (ret < 0) { + fod_log("%s, failed to parse data (ret = %d)\n", get_bat_state_str(bat_state), ret); + goto err_size; + } + + return data; + +err_size: + kfree(data); + return NULL; +} + +static int mfc_fod_parse_op_node(struct device_node *np, + struct mfc_fod *fod, struct mfc_fod_tx *fod_tx, int op_mode, unsigned int fod_size) +{ + struct mfc_fod_op *fod_op = &fod_tx->op[op_mode]; + unsigned int flag = 0; + int ret = 0, i; + + ret = of_property_read_u32(np, "flag", &flag); + if (ret < 0) { + pr_err("%s: failed to get flag of %s\n", __func__, np->name); + return ret; + } + + for (i = MFC_FOD_BAT_STATE_CC; i < MFC_FOD_BAT_STATE_MAX; i++) { + switch ((flag >> (i * 4)) & 0xF) { + case FOD_FLAG_ADD: + fod_op->data[i] = mfc_fod_parse_data(np, i, fod_size); + if (fod_op->data[i] == NULL) { + ret = -1; + goto err_data; + } + break; + case FOD_FLAG_USE_CC: + if (fod_op->data[MFC_FOD_BAT_STATE_CC] == NULL) { + ret = -2; + goto err_data; + } + + fod_op->data[i] = fod_op->data[MFC_FOD_BAT_STATE_CC]; + break; + case FOD_FLAG_USE_CV: + if (fod_op->data[MFC_FOD_BAT_STATE_CV] == NULL) { + ret = -3; + goto err_data; + } + + fod_op->data[i] = fod_op->data[MFC_FOD_BAT_STATE_CV]; + break; + case FOD_FLAG_USE_FULL: + if (fod_op->data[MFC_FOD_BAT_STATE_FULL] == NULL) { + ret = -4; + goto err_data; + } + + fod_op->data[i] = fod_op->data[MFC_FOD_BAT_STATE_FULL]; + break; + case FOD_FLAG_USE_DEF_PAD: + { + struct mfc_fod_tx *def_tx = &fod->list[DEFAULT_TX_IDX]; + + if (def_tx->op[op_mode].data[i] == NULL) { + ret = -5; + goto err_data; + } + + fod_op->data[i] = def_tx->op[op_mode].data[i]; + } + break; + case FOD_FLAG_USE_DEF_OP: + { + struct mfc_fod_op *def_op = &fod_tx->op[DEFAULT_OP_MODE]; + + if (def_op->data[i] == NULL) { + ret = -6; + goto err_data; + } + + fod_op->data[i] = def_op->data[i]; + } + break; + case FOD_FLAG_NONE: + default: + fod_log("%s - %s is not set\n", np->name, get_bat_state_str(i)); + break; + } + } + + fod_op->flag = flag; + return 0; + +err_data: + for (; i >= 0; i--) { + if (((flag >> (i * 4)) & 0xF) == FOD_FLAG_ADD) + kfree(fod_op->data[i]); + } + + return ret; +} + +static int mfc_fod_init_ext_pad(struct mfc_fod_tx *fod_tx, struct mfc_fod_tx *def_tx) +{ + int i, j; + + if (fod_tx->id == DEFAULT_TX_IDX) + return 0; + + for (j = WPC_OP_MODE_NONE; j < WPC_OP_MODE_MAX; j++) { + if (def_tx->op[j].flag == 0) + continue; + + for (i = MFC_FOD_BAT_STATE_CC; i < MFC_FOD_BAT_STATE_MAX; i++) + fod_tx->op[j].data[i] = def_tx->op[j].data[i]; + + fod_tx->op[j].flag = + (SET_FOD_CC(USE_DEF_PAD) | SET_FOD_CV(USE_DEF_PAD) | SET_FOD_FULL(USE_DEF_PAD)); + } + + return 0; +} + +static int mfc_fod_parse_tx_node(struct device_node *np, struct mfc_fod *fod, unsigned int fod_size) +{ + struct device_node *tx_node = NULL; + int ret = 0, tx_idx = 0; + + for_each_child_of_node(np, tx_node) { + struct mfc_fod_tx *fod_tx = NULL; + struct device_node *op_node = NULL; + + if (tx_idx >= fod->count) { + fod_log("out of range(%d <--> %d)\n", tx_idx, fod->count); + break; + } + + fod_tx = &fod->list[tx_idx++]; + if (sscanf(tx_node->name, "pad_0x%X", &fod_tx->id) < 0) { + fod_log("failed to get tx id(%s)\n", tx_node->name); + continue; + } + + mfc_fod_init_ext_pad(fod_tx, &fod->list[DEFAULT_TX_IDX]); + + for_each_child_of_node(tx_node, op_node) { + int op_mode; + + op_mode = get_op_mode_by_str(op_node->name); + if (op_mode == WPC_OP_MODE_NONE) { + fod_log("%s, invalid op name\n", op_node->name); + continue; + } + + ret = mfc_fod_parse_op_node(op_node, fod, fod_tx, op_mode, fod_size); + if (ret < 0) + fod_log("%s, failed to parse data(ret = %d)\n", op_node->name, ret); + } + } + + return 0; +} + +static void mfc_fod_print_data(struct mfc_fod *fod, unsigned int fod_size) +{ + int x, y, z, k; + struct mfc_fod_tx *fod_tx; + + for (x = 0; x < fod->count; x++) { + fod_tx = &fod->list[x]; + + for (y = WPC_OP_MODE_NONE + 1; y < WPC_OP_MODE_MAX; y++) { + + for (z = MFC_FOD_BAT_STATE_CC; z < MFC_FOD_BAT_STATE_MAX; z++) { + char temp_buf[1024] = {0, }; + int size = 1024; + + if (fod_tx->op[y].data[z] == NULL) { + fod_log("PAD_0x%02X:%s:%s is null!!\n", + fod_tx->id, sb_wrl_op_mode_str(y), get_bat_state_str(z)); + continue; + } + + for (k = 0; k < fod_size; k++) { + snprintf(temp_buf + strlen(temp_buf), size, "0x%02X ", fod_tx->op[y].data[z][k]); + size = sizeof(temp_buf) - strlen(temp_buf); + } + + fod_log("PAD_0x%02X:%s:%s - %s\n", + fod_tx->id, sb_wrl_op_mode_str(y), get_bat_state_str(z), temp_buf); + } + } + } +} + +static void mfc_fod_print_ext(struct mfc_fod *fod) +{ + char ext_buf[1024] = {0, }; + int x, ext_size = 1024; + + for (x = 0; x < MFC_FOD_EXT_MAX; x++) { + snprintf(ext_buf + strlen(ext_buf), ext_size, "%02d:0x%X ", x, fod->ext[x]); + ext_size = sizeof(ext_buf) - strlen(ext_buf); + } + fod_log("EXT - %s\n", ext_buf); +} + +static int mfc_fod_parse_dt(struct device_node *np, struct mfc_fod *fod, unsigned int fod_size) +{ + int ret = 0, i; + + np = of_find_node_by_name(np, "fod_list"); + if (!np) { + fod_log("fod list is null!!!!\n"); + return -ENODEV; + } + + ret = of_property_read_u32(np, "count", &fod->count); + if (ret < 0) { + fod_log("count is null(ret = %d)\n", ret); + return ret; + } + + fod->list = kcalloc(fod->count, sizeof(struct mfc_fod_tx), GFP_KERNEL); + if (!fod->list) { + fod_log("failed to alloc fod list\n"); + return -ENOMEM; + } + + ret = mfc_fod_parse_tx_node(np, fod, fod_size); + if (ret < 0) { + kfree(fod->list); + return ret; + } + + /* parse ext */ + for (i = 0; i < MFC_FOD_EXT_MAX; i++) { + ret = of_property_read_u32(np, get_ext_str(i), (unsigned int *)&fod->ext[i]); + if (ret < 0) + fod_log("%s is null(ret = %d)!!\n", get_ext_str(i), ret); + } + + mfc_fod_print_data(fod, fod_size); + mfc_fod_print_ext(fod); + + return 0; +} + +static int mfc_fod_thr_parse_dt(struct device_node *np, struct mfc_fod *fod) +{ + int ret = 0; + + ret = of_property_read_u32(np, "bpp_vout", &fod->bpp_vout); + if (ret < 0) + fod->bpp_vout = 7000; + + ret = of_property_read_u32(np, "cc_cv_thr", &fod->cc_cv_thr); + if (ret < 0) + fod->cc_cv_thr = 85; + + ret = of_property_read_u32(np, "high_swell_cc_cv_thr", &fod->high_swell_cc_cv_thr); + if (ret < 0) + fod->high_swell_cc_cv_thr = 70; + + ret = of_property_read_u32(np, "vendor_id", (unsigned int *)&fod->vendor_id); + if (ret < 0) { + fod_log("vendor_id is null(ret = %d)!!\n", ret); + fod->vendor_id = DEFAULT_VENDOR_ID; + } + + fod_log("bpp_vout = %d, cc_cv_thr = %d, high_swell_cc_cv_thr = %d, vendor_id = 0x%x\n", + fod->bpp_vout, fod->cc_cv_thr, fod->high_swell_cc_cv_thr, fod->vendor_id); + return 0; +} + +static struct mfc_fod_tx *get_fod_tx(struct mfc_fod *fod, unsigned int tx_id) +{ + int i; + + for (i = 0; i < fod->count; i++) { + if (fod->list[i].id == tx_id) + return &fod->list[i]; + } + + return &fod->list[DEFAULT_TX_IDX]; +} + +static bool check_vendor_id_to_set_op_mode_by_vout(union mfc_fod_state *state, int vendor_id) +{ + return (state->vendor_id == vendor_id); +} + +static int check_op_mode_vout(struct mfc_fod *fod, union mfc_fod_state *state) +{ + if (!check_vendor_id_to_set_op_mode_by_vout(state, fod->vendor_id)) + return state->op_mode; + + switch (state->op_mode) { + case WPC_OP_MODE_EPP: + case WPC_OP_MODE_PPDE: + if (state->vout <= fod->bpp_vout) + return WPC_OP_MODE_BPP; + break; + case WPC_OP_MODE_MPP: + break; + default: + /* default op mode */ + return WPC_OP_MODE_BPP; + } + + return state->op_mode; +} + +static fod_data_t *mfc_fod_get_data(struct mfc_fod *fod) +{ + union mfc_fod_state *fod_state = &fod->state; + struct mfc_fod_tx *fod_tx; + + if (fod->count <= 0) + return NULL; + + fod_tx = get_fod_tx(fod, fod_state->tx_id); + fod_state->fake_op_mode = check_op_mode_vout(fod, fod_state); + return fod_tx->op[fod_state->fake_op_mode].data[fod_state->bat_state]; +} + +struct mfc_fod *mfc_fod_init(struct device *dev, unsigned int fod_size, mfc_set_fod cb_func) +{ + struct mfc_fod *fod; + int ret = 0; + + if (IS_ERR_OR_NULL(dev) || + (fod_size <= 0) || + (fod_size > MFC_FOD_MAX_SIZE) || + (cb_func == NULL)) + return ERR_PTR(-EINVAL); + + fod = kzalloc(sizeof(struct mfc_fod), GFP_KERNEL); + if (!fod) + return ERR_PTR(-ENOMEM); + + ret = mfc_fod_parse_dt(dev->of_node, fod, fod_size); + if (ret < 0) { + kfree(fod); + return ERR_PTR(ret); + } + mfc_fod_thr_parse_dt(dev->of_node, fod); + + mutex_init(&fod->lock); + + fod->parent = dev; + fod->cb_func = cb_func; + fod->state.value = 0; + + fod_log("DONE!!\n"); + return fod; +} +EXPORT_SYMBOL(mfc_fod_init); + +int mfc_fod_init_state(struct mfc_fod *fod) +{ + if (IS_ERR(fod)) + return -EINVAL; + + mutex_lock(&fod->lock); + if (fod->state.value != 0) { + fod->state.value = 0; + + fod->cb_func(fod->parent, &fod->state, mfc_fod_get_data(fod)); + } + mutex_unlock(&fod->lock); + return 0; +} +EXPORT_SYMBOL(mfc_fod_init_state); + +int mfc_fod_refresh(struct mfc_fod *fod) +{ + if (IS_ERR(fod)) + return -EINVAL; + + mutex_lock(&fod->lock); + if (fod->state.value != 0) + fod->cb_func(fod->parent, &fod->state, mfc_fod_get_data(fod)); + mutex_unlock(&fod->lock); + return 0; +} +EXPORT_SYMBOL(mfc_fod_refresh); + +int mfc_fod_set_op_mode(struct mfc_fod *fod, int op_mode) +{ + if (IS_ERR(fod) || + (op_mode < WPC_OP_MODE_NONE) || + (op_mode >= WPC_OP_MODE_MAX)) + return -EINVAL; + + mutex_lock(&fod->lock); + switch (fod->state.op_mode) { + case WPC_OP_MODE_EPP: + case WPC_OP_MODE_MPP: + case WPC_OP_MODE_PPDE: + fod_log("prevent op mode(%d)!!\n", op_mode); + break; + case WPC_OP_MODE_BPP: + case WPC_OP_MODE_NONE: + default: + if (fod->state.op_mode != op_mode) { + fod->state.op_mode = op_mode; + + fod->cb_func(fod->parent, &fod->state, mfc_fod_get_data(fod)); + } + break; + } + mutex_unlock(&fod->lock); + return 0; +} +EXPORT_SYMBOL(mfc_fod_set_op_mode); + +int mfc_fod_set_vendor_id(struct mfc_fod *fod, int vendor_id) +{ + if (IS_ERR(fod) || + (vendor_id < 0) || + (vendor_id > 0xFF)) + return -EINVAL; + + mutex_lock(&fod->lock); + if (vendor_id != fod->state.vendor_id) { + fod->state.vendor_id = vendor_id; + + fod->cb_func(fod->parent, &fod->state, mfc_fod_get_data(fod)); + } + mutex_unlock(&fod->lock); + return 0; +} +EXPORT_SYMBOL(mfc_fod_set_vendor_id); + +int mfc_fod_set_tx_id(struct mfc_fod *fod, int tx_id) +{ + if (IS_ERR(fod) || + (tx_id < 0) || (tx_id >= 256)) + return -EINVAL; + + mutex_lock(&fod->lock); + if (fod->state.tx_id != tx_id) { + fod->state.tx_id = tx_id; + + fod->cb_func(fod->parent, &fod->state, mfc_fod_get_data(fod)); + } + mutex_unlock(&fod->lock); + return 0; +} +EXPORT_SYMBOL(mfc_fod_set_tx_id); + +static int check_bat_state(struct mfc_fod *fod) +{ + if (fod->state.high_swell) + return (fod->state.bat_cap > fod->high_swell_cc_cv_thr) ? + MFC_FOD_BAT_STATE_CV : MFC_FOD_BAT_STATE_CC; + + return (fod->state.bat_cap > fod->cc_cv_thr) ? + MFC_FOD_BAT_STATE_CV : MFC_FOD_BAT_STATE_CC; +} + +static int set_bat_state(struct mfc_fod *fod, int bat_state) +{ + switch (fod->state.bat_state) { + case MFC_FOD_BAT_STATE_FULL: + fod_log("prevent bat state(%d)!!\n", bat_state); + break; + case MFC_FOD_BAT_STATE_CC: + case MFC_FOD_BAT_STATE_CV: + default: + if (fod->state.bat_state != bat_state) { + fod->state.bat_state = bat_state; + + fod->cb_func(fod->parent, &fod->state, mfc_fod_get_data(fod)); + } + break; + } + + return 0; +} + +int mfc_fod_set_bat_state(struct mfc_fod *fod, int bat_state) +{ + if (IS_ERR(fod) || + (bat_state < MFC_FOD_BAT_STATE_CC) || + (bat_state >= MFC_FOD_BAT_STATE_MAX)) + return -EINVAL; + + mutex_lock(&fod->lock); + set_bat_state(fod, bat_state); + mutex_unlock(&fod->lock); + return 0; +} +EXPORT_SYMBOL(mfc_fod_set_bat_state); + +int mfc_fod_set_bat_cap(struct mfc_fod *fod, int bat_cap) +{ + if (IS_ERR(fod) || + (bat_cap < 0) || (bat_cap > 100)) + return -EINVAL; + + mutex_lock(&fod->lock); + if (fod->state.bat_cap != bat_cap) { + fod->state.bat_cap = bat_cap; + + set_bat_state(fod, check_bat_state(fod)); + } + mutex_unlock(&fod->lock); + + return 0; +} +EXPORT_SYMBOL(mfc_fod_set_bat_cap); + +int mfc_fod_set_vout(struct mfc_fod *fod, int vout) +{ + if (IS_ERR(fod) || + (vout <= 0)) + return -EINVAL; + + mutex_lock(&fod->lock); + if (fod->state.vout != vout) { + int new_op_mode, old_op_mode; + + old_op_mode = check_op_mode_vout(fod, &fod->state); + + fod->state.vout = vout; + new_op_mode = check_op_mode_vout(fod, &fod->state); + + if (new_op_mode != old_op_mode) + fod->cb_func(fod->parent, &fod->state, mfc_fod_get_data(fod)); + } + mutex_unlock(&fod->lock); + return 0; +} +EXPORT_SYMBOL(mfc_fod_set_vout); + +int mfc_fod_set_high_swell(struct mfc_fod *fod, bool state) +{ + if (IS_ERR(fod)) + return -EINVAL; + + mutex_lock(&fod->lock); + if (fod->state.high_swell != state) { + fod->state.high_swell = state; + + set_bat_state(fod, check_bat_state(fod)); + } + mutex_unlock(&fod->lock); + return 0; +} +EXPORT_SYMBOL(mfc_fod_set_high_swell); + +int mfc_fod_get_state(struct mfc_fod *fod, union mfc_fod_state *state) +{ + if (IS_ERR(fod) || + (state == NULL)) + return -EINVAL; + + mutex_lock(&fod->lock); + state->value = fod->state.value; + mutex_unlock(&fod->lock); + return 0; +} +EXPORT_SYMBOL(mfc_fod_get_state); + +int mfc_fod_get_ext(struct mfc_fod *fod, int ext_type, int *data) +{ + if (IS_ERR(fod) || + (ext_type < MFC_FOD_EXT_EPP_REF_QF) || + (ext_type >= MFC_FOD_EXT_MAX) || + (data == NULL)) + return -EINVAL; + + mutex_lock(&fod->lock); + *data = fod->ext[ext_type]; + mutex_unlock(&fod->lock); + return 0; +} +EXPORT_SYMBOL(mfc_fod_get_ext); diff --git a/drivers/battery/wireless/cps4038_fod.h b/drivers/battery/wireless/cps4038_fod.h new file mode 100755 index 000000000000..d27df0879eaf --- /dev/null +++ b/drivers/battery/wireless/cps4038_fod.h @@ -0,0 +1,108 @@ +/* + * mfc_fod.h + * Samsung Mobile MFC FOD Header + * + * Copyright (C) 2023 Samsung Electronics, Inc. + * + * + * 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. + * + */ + +#ifndef __MFC_FOD_H +#define __MFC_FOD_H __FILE__ + +#include + +struct device; +struct mfc_fod; + +#define MFC_FOD_MAX_SIZE 256 + +enum { + MFC_FOD_BAT_STATE_CC = 0, + MFC_FOD_BAT_STATE_CV, + MFC_FOD_BAT_STATE_FULL, + MFC_FOD_BAT_STATE_MAX +}; + +union mfc_fod_state { + unsigned long long value; + + struct { + unsigned tx_id : 8, + vendor_id : 8, + op_mode : 8, + fake_op_mode : 8, + bat_state : 8, + bat_cap : 7, + high_swell : 1, + vout : 16; + }; +}; + +typedef unsigned int fod_data_t; +typedef int (*mfc_set_fod)(struct device *dev, union mfc_fod_state *state, fod_data_t *data); + +enum { + MFC_FOD_EXT_EPP_REF_QF = 0, + MFC_FOD_EXT_EPP_REF_RF, + + MFC_FOD_EXT_MAX +}; + +#define MFC_FOD_DISABLED (-ESRCH) +#if IS_ENABLED(CONFIG_WIRELESS_CHARGING) +struct mfc_fod *mfc_fod_init(struct device *dev, unsigned int fod_size, mfc_set_fod cb_func); + +int mfc_fod_init_state(struct mfc_fod *fod); +int mfc_fod_refresh(struct mfc_fod *fod); + +int mfc_fod_set_op_mode(struct mfc_fod *fod, int op_mode); +int mfc_fod_set_vendor_id(struct mfc_fod *fod, int vendor_id); +int mfc_fod_set_tx_id(struct mfc_fod *fod, int tx_id); +int mfc_fod_set_bat_state(struct mfc_fod *fod, int bat_state); +int mfc_fod_set_bat_cap(struct mfc_fod *fod, int bat_cap); +int mfc_fod_set_vout(struct mfc_fod *fod, int vout); +int mfc_fod_set_high_swell(struct mfc_fod *fod, bool state); + +int mfc_fod_get_state(struct mfc_fod *fod, union mfc_fod_state *state); +int mfc_fod_get_ext(struct mfc_fod *fod, int ext_type, int *data); +#else +static inline struct mfc_fod *mfc_fod_init(struct device *dev, unsigned int fod_size, mfc_set_fod cb_func) +{ return ERR_PTR(MFC_FOD_DISABLED); } + +static inline int mfc_fod_init_state(struct mfc_fod *fod) +{ return MFC_FOD_DISABLED; } +static inline int mfc_fod_refresh(struct mfc_fod *fod) +{ return MFC_FOD_DISABLED; } + +static inline int mfc_fod_set_op_mode(struct mfc_fod *fod, int op_mode) +{ return MFC_FOD_DISABLED; } +static inline int mfc_fod_set_vendor_id(struct mfc_fod *fod, int vendor_id) +{ return MFC_FOD_DISABLED; } +static inline int mfc_fod_set_tx_id(struct mfc_fod *fod, int tx_id) +{ return MFC_FOD_DISABLED; } +static inline int mfc_fod_set_bat_state(struct mfc_fod *fod, int bat_state) +{ return MFC_FOD_DISABLED; } +static inline int mfc_fod_set_bat_cap(struct mfc_fod *fod, int bat_cap) +{ return MFC_FOD_DISABLED; } +static inline int mfc_fod_set_vout(struct mfc_fod *fod, int vout) +{ return MFC_FOD_DISABLED; } +static inline int mfc_fod_set_high_swell(struct mfc_fod *fod, bool state) +{ return MFC_FOD_DISABLED; } + +static inline int mfc_fod_get_state(struct mfc_fod *fod, union mfc_fod_state *state) +{ return MFC_FOD_DISABLED; } +static inline int mfc_fod_get_ext(struct mfc_fod *fod, int ext_type, int *data) +{ return MFC_FOD_DISABLED; } +#endif + +#endif /* __MFC_FOD_H */