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