| /* |
| * mfc_s2miw04_charger.c |
| * Samsung MFC IC Charger Driver |
| * |
| * Copyright (C) 2016 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 "include/charger/mfc_s2miw04_charger.h" |
| #include <linux/errno.h> |
| #include <linux/version.h> |
| #include <linux/device.h> |
| #include <linux/pm.h> |
| #include <linux/gpio.h> |
| #include <linux/interrupt.h> |
| #include <linux/i2c.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/pm_runtime.h> |
| #include <linux/irqdomain.h> |
| #include <linux/of.h> |
| #include <linux/of_gpio.h> |
| #include <linux/kernel.h> |
| #include <asm/uaccess.h> |
| #include <linux/sysctl.h> |
| #include <linux/proc_fs.h> |
| #include <linux/vmalloc.h> |
| #include <linux/ctype.h> |
| #include <linux/firmware.h> |
| #include <linux/sec_batt.h> |
| #include <linux/sec_ext.h> |
| #include <linux/spu-verify.h> |
| |
| #define ENABLE 1 |
| #define DISABLE 0 |
| #define CMD_CNT 3 |
| #define MAX_I2C_ERROR_COUNT 30 |
| |
| #define MAX_BUF 255 |
| static u8 ADT_buffer_rdata[MAX_BUF] = {0, }; |
| static u8 adt_readSize; |
| static bool is_shutdn = false; |
| |
| #if defined(CONFIG_WIRELESS_CHARGER_HAL_MFC) |
| extern unsigned int mfc_chip_id_now; |
| #endif |
| |
| extern bool sleep_mode; |
| extern unsigned int lpcharge; |
| extern bool mfc_fw_update; |
| extern bool boot_complete; |
| |
| static char *rx_device_type_str[] = { |
| "No Dev", |
| "Other Dev", |
| "Gear", |
| "Phone", |
| "Buds", |
| }; |
| |
| static char *mfc_vout_control_mode_str[] = { |
| "Set VOUT Off", |
| "Set VOUT NV", |
| "Set Vout Rsv", |
| "Set Vout HV", |
| "Set Vout CV", |
| "Set Vout Call", |
| "Set Vout 5V", |
| "Set Vout 9V", |
| "Set Vout 10V", |
| "Set Vout 11V", |
| "Set Vout 12V", |
| "Set Vout 12.5V", |
| "Set Vout 5V Step", |
| "Set Vout 5.5V Step", |
| "Set Vout 9V Step", |
| "Set Vout 10V Step", |
| }; |
| |
| static char *rx_vout_str[] = { |
| "Vout 5V", |
| "Vout 5.5V", |
| "Vout 6V", |
| "Vout 7V", |
| "Vout 8V", |
| "Vout 9V", |
| "Vout 10V", |
| "Vout 11V", |
| "Vout 12V", |
| "Vout 12.5V", |
| }; |
| |
| static struct device_attribute mfc_attrs[] = { |
| MFC_S2MIW04_ATTR(mfc_addr), |
| MFC_S2MIW04_ATTR(mfc_size), |
| MFC_S2MIW04_ATTR(mfc_data), |
| MFC_S2MIW04_ATTR(mfc_packet), |
| }; |
| |
| static enum power_supply_property mfc_charger_props[] = { |
| }; |
| |
| static int mfc_get_firmware_version(struct mfc_charger_data *charger, int firm_mode); |
| 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_check_i2c_error(struct mfc_charger_data *charger, bool is_error) |
| { |
| charger->i2c_error_count = |
| (charger->wc_w_state && gpio_get_value(charger->pdata->wpc_det) && is_error) ? |
| (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); |
| } |
| } |
| |
| 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]; |
| |
| 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 ret; |
| } |
| *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]; |
| |
| 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 ret; |
| } |
| |
| 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 }; |
| |
| //pr_info("%s reg=0x%x, val=0x%x", __func__, reg, 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 (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; |
| |
| 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; |
| } |
| |
| #if 0 |
| static void mfc_itoh(u8 *dest, char *hex, int len) |
| { |
| int i = 0; |
| |
| while (i < len) { |
| snprintf(hex + i*2, len*2, "%x", dest[i]); |
| i++; |
| } |
| } |
| |
| static u8 htoi(const char *hexa) |
| { |
| char ch = 0; |
| u8 deci = 0; |
| const char *sp = hexa; |
| while (*sp) { |
| deci *= 16; |
| if ('0' <= *sp && *sp <= '9') |
| ch = *sp - '0'; |
| if ('A' <= *sp && *sp <= 'F') |
| ch = *sp - 'A' + 10; |
| if ('a' <= *sp && *sp <= 'f') |
| ch = *sp - 'a' + 10; |
| deci += ch; |
| sp++; |
| } |
| return deci; |
| } |
| |
| static int mfc_str_to_deci(const char *data, int size, u8 *dest) |
| { |
| int ret = 0; |
| int i = 0, str_len = 0; |
| char temp_str[3] = {0, }; |
| |
| if (data == NULL) { |
| pr_info("%s: given data is not valid !\n", __func__); |
| return -1; |
| } |
| |
| pr_info("%s : data = %s, size = %d \n", __func__, data, size); |
| |
| str_len = size * 2; |
| while(i < str_len) { |
| strncpy(temp_str, (char *)data + i, 2); |
| dest[i / 2] = htoi(temp_str); |
| pr_info("%s: 0x%x, %d\n", __func__, dest[i / 2], dest[i / 2]); |
| i += 2; |
| } |
| |
| for(i=0; i < size; i++) |
| pr_info("%s: 0x%x, %d\n", __func__, dest[i], dest[i]); |
| |
| return ret; |
| } |
| #endif |
| |
| #if defined(CONFIG_WIRELESS_IC_PARAM) |
| static int mfc_set_wireless_param(u8 chip_id, int fw_ver) |
| { |
| u32 param_data = 0; |
| int ret = 0; |
| #if defined(CONFIG_SEC_PARAM) |
| param_data = (chip_id & 0xFF) << 24; |
| param_data |= (fw_ver & 0xFFFF) << 8; |
| param_data |= (wireless_fw_mode_param & 0xF) << 4; |
| |
| pr_info("%s: param data 0x%08X\n", __func__, param_data); |
| ret = sec_set_param_u32(wireless_offset, param_data); |
| if (ret < 0) |
| pr_err("%s: sec_set_param_u32 failed\n", __func__); |
| #else |
| pr_err("%s: config is undefined, not called sec_set_param_u32\n", __func__); |
| #endif |
| return ret; |
| } |
| |
| static int mfc_set_wireless_ic_param(struct mfc_charger_data *charger, u8 chip_id, int fw_ver) |
| { |
| u32 param_data = 0; |
| int ret = 0; |
| |
| if (boot_complete) { |
| #if defined(CONFIG_SEC_PARAM) |
| if (chip_id == 0) { |
| pr_info("%s: chip_id(0x%02X) is abnormal, so changed chip_id to (0x%02X, param)\n", |
| __func__, chip_id, wireless_chip_id_param); |
| chip_id = wireless_chip_id_param; |
| } |
| |
| if (fw_ver <= 0) { |
| pr_info("%s: fw_ver(0x%04X) is abnormal, so changed fw_ver to (0x%04X, param)\n", |
| __func__, fw_ver, wireless_fw_ver_param); |
| fw_ver = wireless_fw_ver_param; |
| } |
| |
| if (chip_id == wireless_chip_id_param && fw_ver == wireless_fw_ver_param) { |
| pr_info("%s: no changed, chip id(0x%02X), fw_ver(0x%04X)\n", |
| __func__, chip_id, fw_ver); |
| return 0; |
| } |
| |
| param_data = (chip_id & 0xFF) << 24; |
| param_data |= (fw_ver & 0xFFFF) << 8; |
| param_data |= (wireless_fw_mode_param & 0xF) << 4; |
| pr_info("%s: param data 0x%08X\n", __func__, param_data); |
| ret = sec_set_param_u32(wireless_offset, param_data); |
| if (ret < 0) { |
| pr_err("%s: sec_set_param_u32 failed\n", __func__); |
| goto out; |
| } else { |
| wireless_chip_id_param = chip_id & 0xFF; |
| wireless_fw_ver_param = fw_ver & 0xFFFF; |
| pr_info("%s: succeed. chip_id(0x%02X), fw_ver(0x%04X)\n", |
| __func__, wireless_chip_id_param, wireless_fw_ver_param); |
| } |
| #else |
| pr_err("%s: config is undefined, not called sec_set_param_u32\n", __func__); |
| #endif |
| } else if (!lpcharge) { |
| pr_info("%s: not yet boot_complete\n", __func__); |
| } |
| |
| out: |
| return ret; |
| } |
| #endif |
| |
| static int mfc_get_firmware_version(struct mfc_charger_data *charger, int firm_mode) |
| { |
| int version = -1; |
| int ret; |
| u8 fw_major[2] = {0,}; |
| u8 fw_minor[2] = {0,}; |
| |
| pr_info("%s: called by (%ps)\n", __func__, __builtin_return_address(0)); |
| switch (firm_mode) { |
| case MFC_RX_FIRMWARE: |
| ret = mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_L_REG, &fw_major[0]); |
| ret = mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_H_REG, &fw_major[1]); |
| if (ret >= 0) { |
| version = fw_major[0] | (fw_major[1] << 8); |
| } |
| pr_info("%s rx major firmware version 0x%x\n", __func__, version); |
| |
| ret = mfc_reg_read(charger->client, MFC_FW_MINOR_REV_L_REG, &fw_minor[0]); |
| ret = mfc_reg_read(charger->client, MFC_FW_MINOR_REV_H_REG, &fw_minor[1]); |
| if (ret >= 0) { |
| version = fw_minor[0] | (fw_minor[1] << 8); |
| } |
| pr_info("%s rx minor firmware version 0x%x\n", __func__, version); |
| break; |
| default: |
| pr_err("%s Wrong firmware mode\n", __func__); |
| version = -1; |
| break; |
| } |
| |
| #if defined(CONFIG_WIRELESS_IC_PARAM) |
| if (version > 0 && wireless_fw_ver_param != version) { |
| pr_info("%s: fw_ver (param:0x%04X, version:0x%04X)\n", |
| __func__, wireless_fw_ver_param, version); |
| mfc_set_wireless_ic_param(charger, charger->chip_id_now, version); |
| } |
| #endif |
| |
| return version; |
| } |
| |
| static int mfc_get_chip_id(struct mfc_charger_data *charger) |
| { |
| u8 chip_id = 0; |
| int ret = 0; |
| |
| ret = mfc_reg_read(charger->client, MFC_CHIP_ID_L_REG, &chip_id); |
| if (ret >= 0) { |
| if (chip_id == 0x04) { |
| charger->chip_id = MFC_CHIP_LSI; |
| pr_info("%s: LSI CHIP(0x%x)\n", __func__, chip_id); |
| } else { /* 0x20 */ |
| charger->chip_id = MFC_CHIP_IDT; |
| pr_info("%s: IDT CHIP(0x%x)\n", __func__, chip_id); |
| } |
| charger->chip_id_now = chip_id; |
| } else |
| return -1; |
| return charger->chip_id; |
| } |
| |
| static int mfc_get_ic_revision(struct mfc_charger_data *charger, int read_mode) |
| { |
| u8 temp; |
| int ret; |
| |
| 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; |
| } |
| else |
| ret = -1; |
| 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; |
| } |
| else |
| ret = -1; |
| break; |
| default : |
| ret = -1; |
| break; |
| } |
| return ret; |
| } |
| |
| static int mfc_get_adc(struct mfc_charger_data *charger, int adc_type) |
| { |
| int ret = 0; |
| u8 data[2] = {0,}; |
| |
| switch (adc_type) { |
| case MFC_ADC_VOUT: |
| ret = mfc_reg_read(charger->client, MFC_ADC_VOUT_L_REG, &data[0]); |
| ret = mfc_reg_read(charger->client, MFC_ADC_VOUT_H_REG, &data[1]); |
| if (ret >= 0) { |
| ret = (data[0] | (data[1] << 8)); |
| } else |
| ret = -1; |
| break; |
| case MFC_ADC_VRECT: |
| ret = mfc_reg_read(charger->client, MFC_ADC_VRECT_L_REG, &data[0]); |
| ret = mfc_reg_read(charger->client, MFC_ADC_VRECT_H_REG, &data[1]); |
| if (ret >= 0) { |
| ret = (data[0] | (data[1] << 8)); |
| } else |
| ret = -1; |
| break; |
| case MFC_ADC_RX_IOUT: |
| ret = mfc_reg_read(charger->client, MFC_ADC_IOUT_L_REG, &data[0]); |
| ret = mfc_reg_read(charger->client, MFC_ADC_IOUT_H_REG, &data[1]); |
| if (ret >= 0) { |
| ret = (data[0] | (data[1] << 8)); |
| } else |
| ret = -1; |
| break; |
| case MFC_ADC_DIE_TEMP: |
| /* only 4 MSB[3:0] field is used, Celsius */ |
| ret = mfc_reg_read(charger->client, MFC_ADC_DIE_TEMP_L_REG, &data[0]); |
| ret = mfc_reg_read(charger->client, MFC_ADC_DIE_TEMP_H_REG, &data[1]); |
| if (ret >= 0) { |
| data[1] &= 0x0f; |
| ret = (data[0] | (data[1] << 8)); |
| } else |
| ret = -1; |
| break; |
| case MFC_ADC_OP_FRQ: /* kHz */ |
| ret = mfc_reg_read(charger->client, MFC_TRX_OP_FREQ_L_REG, &data[0]); |
| ret = mfc_reg_read(charger->client, MFC_TRX_OP_FREQ_H_REG, &data[1]); |
| if (ret >= 0) { |
| ret = (data[0] | (data[1] << 8)) / 10; |
| } else |
| ret = -1; |
| break; |
| case MFC_ADC_TX_OP_FRQ: |
| ret = mfc_reg_read(charger->client, MFC_TX_MAX_OP_FREQ_L_REG, &data[0]); |
| ret = mfc_reg_read(charger->client, MFC_TX_MAX_OP_FREQ_H_REG, &data[1]); |
| if (ret >= 0) { |
| ret = (int)(60000 / (data[0] | (data[1] << 8))); |
| } else |
| ret = -1; |
| break; |
| case MFC_ADC_PING_FRQ: |
| ret = mfc_reg_read(charger->client, MFC_RX_PING_FREQ_L_REG, &data[0]); |
| ret = mfc_reg_read(charger->client, MFC_RX_PING_FREQ_H_REG, &data[0]); |
| if (ret >= 0) { |
| ret = (data[0] | (data[1] << 8)); |
| } else |
| ret = -1; |
| break; |
| case MFC_ADC_TX_VOUT: |
| ret = mfc_reg_read(charger->client, MFC_ADC_VOUT_L_REG, &data[0]); |
| ret = mfc_reg_read(charger->client, MFC_ADC_VOUT_H_REG, &data[1]); |
| if (ret >= 0) { |
| ret = (data[0] | (data[1] << 8)); |
| } else |
| ret = -1; |
| break; |
| case MFC_ADC_TX_IOUT: |
| ret = mfc_reg_read(charger->client, MFC_ADC_IOUT_L_REG, &data[0]); |
| ret = mfc_reg_read(charger->client, MFC_ADC_IOUT_H_REG, &data[1]); |
| if (ret >= 0) { |
| ret = (data[0] | (data[1] << 8)); |
| } else |
| ret = -1; |
| 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; |
| |
| mutex_lock(&charger->wpc_en_lock); |
| |
| if (on) |
| 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_CHARGING | WPC_EN_MST | WPC_EN_TX))) |
| enable = 0; |
| else |
| enable = 1; |
| |
| if (charger->pdata->wpc_en) { |
| 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("%s: before(0x%x), after(0x%x), en(%d), val(%d->%d)\n", |
| __func__, temp, charger->wpc_en_flag, enable, old_val, new_val); |
| |
| if (old_val != new_val) { |
| value.intval = enable; |
| psy_do_property("battery", set, |
| POWER_SUPPLY_EXT_PROP_WPC_EN, value); |
| } |
| } else { |
| pr_info("%s: there`s no wpc_en\n", __func__); |
| } |
| mutex_unlock(&charger->wpc_en_lock); |
| } |
| |
| static void mfc_set_vout(struct mfc_charger_data *charger, int vout) |
| { |
| u8 data[2] = {0,}; |
| |
| data[0] = mfc_lsi_vout_val16[vout] & 0xff; |
| data[1] = (mfc_lsi_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__, |
| rx_vout_str[vout], (data[0] | (data[1] << 8)), mfc_get_adc(charger, MFC_ADC_VOUT)); |
| charger->pdata->vout_status = vout; |
| } |
| |
| static int mfc_get_vout(struct mfc_charger_data *charger) |
| { |
| u8 data[2] = {0,}; |
| int ret; |
| |
| ret = mfc_reg_read(charger->client, MFC_VOUT_SET_L_REG, &data[0]); |
| ret = mfc_reg_read(charger->client, MFC_VOUT_SET_H_REG, &data[1]); |
| |
| if (ret < 0) { |
| pr_err("%s: fail to read vout. (%d)\n", __func__, ret); |
| } else { |
| ret = (data[0] | (data[1] << 8)); |
| pr_info("%s: vout(0x%04x)\n", __func__, ret); |
| } |
| |
| return ret; |
| } |
| |
| static void mfc_uno_on(struct mfc_charger_data *charger, bool on) |
| { |
| union power_supply_propval value = {0, }; |
| |
| if (charger->wc_tx_enable && on) { /* UNO ON */ |
| value.intval = SEC_BAT_CHG_MODE_UNO_ONLY; |
| psy_do_property(charger->pdata->wired_charger_name, set, |
| POWER_SUPPLY_PROP_CHARGE_UNO_CONTROL, value); |
| pr_info("%s: ENABLE\n", __func__); |
| } else if (on) { /* UNO ON */ |
| value.intval = 1; |
| psy_do_property(charger->pdata->wired_charger_name, set, |
| POWER_SUPPLY_PROP_CHARGE_UNO_CONTROL, value); |
| pr_info("%s: ENABLE\n", __func__); |
| } else { /* UNO OFF */ |
| value.intval = 0; |
| psy_do_property(charger->pdata->wired_charger_name, set, |
| POWER_SUPPLY_PROP_CHARGE_UNO_CONTROL, value); |
| |
| if (delayed_work_pending(&charger->wpc_tx_op_freq_work)) { |
| wake_unlock(&charger->wpc_tx_opfq_lock); |
| cancel_delayed_work(&charger->wpc_tx_op_freq_work); |
| } |
| pr_info("%s: DISABLE\n", __func__); |
| } |
| } |
| |
| static void mfc_rpp_set(struct mfc_charger_data *charger) |
| { |
| u8 data; |
| int ret; |
| |
| if (charger->led_cover) { |
| pr_info("%s: LED cover exists. RPP 3/4 (0x%x)\n", __func__, charger->pdata->wc_cover_rpp); |
| mfc_reg_write(charger->client, MFC_RPP_SCALE_COEF_REG, charger->pdata->wc_cover_rpp); |
| } else { |
| pr_info("%s: LED cover not exists. RPP 1/2 (0x%x)\n", __func__, charger->pdata->wc_hv_rpp); |
| mfc_reg_write(charger->client, MFC_RPP_SCALE_COEF_REG, charger->pdata->wc_hv_rpp); |
| } |
| msleep(5); |
| ret = mfc_reg_read(charger->client, MFC_RPP_SCALE_COEF_REG, &data); |
| if (ret < 0) { |
| pr_err("%s: fail to read RPP scaling coefficient. (%d)\n", __func__, ret); |
| } else |
| pr_info("%s: RPP scaling coefficient(0x%x)\n", __func__, data); |
| } |
| |
| static void mfc_fod_set(struct mfc_charger_data *charger) |
| { |
| int i = 0; |
| |
| pr_info("%s\n", __func__); |
| |
| if (charger->pdata->cable_type != SEC_WIRELESS_PAD_NONE) { |
| for (i = 0; i < MFC_NUM_FOD_REG; i++) |
| mfc_reg_write(charger->client, MFC_WPC_FOD_0A_REG+i, charger->pdata->fod_data[i]); |
| } |
| } |
| |
| static void mfc_fod_set_cv(struct mfc_charger_data *charger) |
| { |
| int i = 0; |
| |
| pr_info("%s\n", __func__); |
| |
| if (charger->pdata->cable_type != SEC_WIRELESS_PAD_NONE) { |
| for (i = 0; i < MFC_NUM_FOD_REG; i++) |
| mfc_reg_write(charger->client, MFC_WPC_FOD_0A_REG+i, charger->pdata->fod_data_cv[i]); |
| } |
| } |
| |
| static void mfc_fod_set_cs100(struct mfc_charger_data *charger) |
| { |
| int i = 0; |
| |
| pr_info("%s\n", __func__); |
| |
| if (charger->pdata->cable_type != SEC_WIRELESS_PAD_NONE) { |
| for (i = 0; i < MFC_NUM_FOD_REG; i++) |
| mfc_reg_write(charger->client, MFC_WPC_FOD_0A_REG+i, 0x7f); |
| } |
| } |
| |
| static void mfc_set_tx_op_freq(struct mfc_charger_data *charger, unsigned int op_feq) |
| { |
| u8 data[2] = {0,}; |
| |
| pr_info("%s op feq = %d KHz\n", __func__, op_feq); |
| |
| op_feq = (int)(60000 / op_feq); |
| data[0] = op_feq & 0xff; |
| data[1] = (op_feq & 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]); |
| |
| msleep(500); |
| pr_info("%s op feq = %d KHz\n", __func__, mfc_get_adc(charger, MFC_ADC_TX_OP_FRQ)); |
| } |
| |
| static void mfc_set_min_duty(struct mfc_charger_data *charger, unsigned int duty) |
| { |
| u8 data = 0; |
| |
| data = duty; |
| |
| pr_info("%s min duty = %d%%(0x%x)\n", __func__, duty, data); |
| |
| mfc_reg_write(charger->client, MFC_TX_MIN_DUTY_SETTING_REG, data); |
| } |
| |
| #if 0 |
| static void mfc_fod_set_hero_5v(struct mfc_charger_data *charger) |
| { |
| int i = 0; |
| u8 data = 0; |
| char str[(MFC_NUM_FOD_REG * 5) + 1] = {0,}; |
| |
| pr_info("%s\n", __func__); |
| |
| if (charger->pdata->fod_hero_5v_data) { |
| for (i = 0; i < MFC_NUM_FOD_REG; i++) |
| mfc_reg_write(charger->client, MFC_WPC_FOD_0A_REG+i, charger->pdata->fod_hero_5v_data[i]); |
| msleep(2); |
| for (i = 0; i < MFC_NUM_FOD_REG; i++) { |
| mfc_reg_read(charger->client, MFC_WPC_FOD_0A_REG+i, &data); |
| sprintf(str + strlen(str), "0x%02X ", data); |
| } |
| |
| pr_info("%s: HERO 5V FOD(%s)\n", __func__, str); |
| } |
| } |
| |
| |
| static void mfc_fod_set_dream(struct mfc_charger_data *charger) |
| { |
| int i = 0; |
| u8 data = 0; |
| char str[(MFC_NUM_FOD_REG * 5) + 1] = {0,}; |
| |
| pr_info("%s\n", __func__); |
| |
| if (charger->pdata->fod_dream_data) { |
| for (i = 0; i < MFC_NUM_FOD_REG; i++) |
| mfc_reg_write(charger->client, MFC_WPC_FOD_0A_REG+i, charger->pdata->fod_dream_data[i]); |
| msleep(2); |
| for (i = 0; i < MFC_NUM_FOD_REG; i++) { |
| mfc_reg_read(charger->client, MFC_WPC_FOD_0A_REG+i, &data); |
| sprintf(str + strlen(str), "0x%02X ", data); |
| } |
| |
| pr_info("%s: DreamPad FOD(%s)\n", __func__, str); |
| } |
| } |
| static void mfc_fod_set_dream_cv(struct mfc_charger_data *charger) |
| { |
| int i = 0; |
| u8 data = 0; |
| char str[(MFC_NUM_FOD_REG * 5) + 1] = {0,}; |
| |
| pr_info("%s\n", __func__); |
| |
| if (charger->pdata->fod_dream_cv_data) { |
| for (i = 0; i < MFC_NUM_FOD_REG; i++) |
| mfc_reg_write(charger->client, MFC_WPC_FOD_0A_REG+i, charger->pdata->fod_dream_cv_data[i]); |
| msleep(2); |
| for (i = 0; i < MFC_NUM_FOD_REG; i++) { |
| mfc_reg_read(charger->client, MFC_WPC_FOD_0A_REG+i, &data); |
| sprintf(str + strlen(str), "0x%02X ", data); |
| } |
| |
| pr_info("%s: DreamPad CV FOD(%s)\n", __func__, str); |
| } |
| } |
| #endif |
| |
| 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) { |
| msleep(10); |
| 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)); |
| } |
| |
| #if 0 |
| static void mfc_set_cmd_h_reg(struct mfc_charger_data *charger, u8 val, u8 mask) |
| { |
| u8 temp = 0; |
| int ret = 0, i = 0; |
| |
| do { |
| pr_info("%s\n", __func__); |
| ret = mfc_reg_update(charger->client, MFC_AP2MFC_CMD_H_REG, val, mask); // command |
| if (ret >= 0) { |
| msleep(250); |
| ret = mfc_reg_read(charger->client, MFC_AP2MFC_CMD_H_REG, &temp); // check out set bit exists |
| if (ret < 0 || i > 3) |
| break; |
| } |
| i++; |
| } while ((temp != 0) && (i < 3)); |
| } |
| #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_HEALTH_OVERHEATLIMIT: |
| case POWER_SUPPLY_HEALTH_COLD: |
| pr_info("%s: ept-ot\n", __func__); |
| if (charger->pdata->cable_type == SEC_WIRELESS_PAD_PMA) { |
| 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) { |
| mfc_set_cmd_l_reg(charger, MFC_CMD_SEND_EOP_MASK, MFC_CMD_SEND_EOP_MASK); |
| msleep(250); |
| } else |
| break; |
| } |
| } 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) { |
| mfc_set_cmd_l_reg(charger, MFC_CMD_SEND_EOP_MASK, MFC_CMD_SEND_EOP_MASK); |
| msleep(250); |
| } else |
| break; |
| } |
| } |
| break; |
| case POWER_SUPPLY_HEALTH_UNDERVOLTAGE: |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void mfc_send_packet(struct mfc_charger_data *charger, u8 header, u8 rx_data_com, u8 *data_val, int data_size) |
| { |
| int i; |
| |
| if (data_size > MFC_WPC_RTX_DATA_SIZE_MAX) { |
| pr_info("%s: data_size is over(%d)\n", __func__, data_size); |
| data_size = MFC_WPC_RTX_DATA_SIZE_MAX; |
| } |
| |
| mfc_reg_write(charger->client, MFC_WPC_PPP_HEADER_REG, header); |
| mfc_reg_write(charger->client, MFC_WPC_RTX_RX_DATA_COM_REG, rx_data_com); |
| |
| for (i = 0; i < data_size; i++) { |
| mfc_reg_write(charger->client, MFC_WPC_RTX_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_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); |
| msleep(120); |
| |
| charger->vout_mode = WIRELESS_VOUT_5V; |
| cancel_delayed_work(&charger->wpc_vout_mode_work); |
| wake_lock(&charger->wpc_vout_mode_lock); |
| 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_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_COM_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_VALUE0_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_AP2MFC_CMD_L_REG, &data_val[0]); |
| break; |
| case MFC_AFC_CONF_10V: |
| pr_info("%s: WPC set 10V\n", __func__); |
| //trigger 10V vout work after 8sec |
| wake_lock(&charger->wpc_afc_vout_lock); |
| #if defined(CONFIG_SEC_FACTORY) |
| queue_delayed_work(charger->wqueue, &charger->wpc_afc_vout_work, msecs_to_jiffies(3000)); |
| #else |
| if (sleep_mode && (charger->non_sleep_mode_cnt >= 2)) |
| 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); |
| mfc_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_COM_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_VALUE0_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_AP2MFC_CMD_L_REG, &data_val[0]); |
| 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); |
| mfc_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_COM_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_VALUE0_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_AP2MFC_CMD_L_REG, &data_val[0]); |
| 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_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_COM_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_VALUE0_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_AP2MFC_CMD_L_REG, &data_val[0]); |
| msleep(100); |
| } |
| break; |
| case MFC_AFC_CONF_12_5V_TX: |
| for (i = 0; i < CMD_CNT; i++) { |
| cmd = WPC_COM_AFC_SET; |
| data_val[0] = RX_DATA_VAL2_12_5V; /* Value for WPC AFC_SET 12V */ |
| pr_info("%s set 12.5V to TX, cnt = %d \n", __func__, i); |
| mfc_send_packet(charger, MFC_HEADER_AFC_CONF, cmd, data_val, 1); |
| mfc_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_COM_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_VALUE0_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_AP2MFC_CMD_L_REG, &data_val[0]); |
| msleep(100); |
| } |
| 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_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_COM_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_WPC_RTX_RX_DATA_VALUE0_REG, &data_val[0]); |
| mfc_reg_read(charger->client, MFC_AP2MFC_CMD_L_REG, &data_val[0]); |
| 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); |
| 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); |
| 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); |
| 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); |
| 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); |
| 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; |
| default: |
| break; |
| } |
| } |
| |
| static void mfc_send_fsk(struct mfc_charger_data *charger, u8 header, u8 tx_data_com, u8 *data_val, int data_size) |
| { |
| int i, j; |
| |
| if (data_size > MFC_WPC_RTX_DATA_SIZE_MAX) { |
| pr_info("%s: data_size is over(%d)\n", __func__, data_size); |
| data_size = MFC_WPC_RTX_DATA_SIZE_MAX; |
| } |
| |
| for(j = 0; j < 3; j++) { |
| mfc_reg_write(charger->client, MFC_WPC_PPP_HEADER_REG, header); |
| mfc_reg_write(charger->client, MFC_WPC_RTX_TX_DATA_COM_REG, tx_data_com); |
| |
| for (i = 0; i < data_size; i++) { |
| mfc_reg_write(charger->client, MFC_WPC_RTX_TX_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); |
| 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) |
| { |
| int i = 0; |
| |
| switch (set) { |
| case MFC_HEADROOM_0: |
| for (i = 0; i < 6; i++) { |
| mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x0); |
| msleep(50); |
| } |
| break; |
| case MFC_HEADROOM_1: |
| for (i = 0; i < 6; i++) { |
| mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x36); |
| msleep(50); |
| } |
| break; |
| case MFC_HEADROOM_2: |
| for (i = 0; i < 6; i++) { |
| mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x61); |
| msleep(50); |
| } |
| break; |
| case MFC_HEADROOM_3: |
| for (i = 0; i < 6; i++) { |
| mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x7f); |
| msleep(50); |
| } |
| break; |
| case MFC_HEADROOM_4: |
| for (i = 0; i < 6; i++) { |
| mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x06); |
| msleep(50); |
| } |
| break; |
| case MFC_HEADROOM_5: |
| for (i = 0; i < 6; i++) { |
| mfc_reg_write(charger->client, MFC_VRECT_ADJ_REG, 0x10); |
| msleep(50); |
| } |
| break; |
| default: |
| pr_info("%s no headroom mode\n", __func__); |
| break; |
| } |
| } |
| |
| static bool is_sleep_mode_active(int pad_id) |
| { |
| if ((pad_id >= 0x17 && pad_id <= 0x1f) || |
| (pad_id >= 0x25 && pad_id <= 0x2f) || |
| (pad_id >= 0x34 && pad_id <= 0x3f) || |
| (pad_id >= 0x46 && pad_id <= 0x4f) || |
| (pad_id >= 0x90 && pad_id <= 0x9f) || |
| (pad_id >= 0xa1 && pad_id <= 0xbf) || |
| (pad_id >= 0xc0 && pad_id <= 0xef)) { |
| pr_info("%s: this pad applies auto mode\n", __func__); |
| return 1; |
| } else { |
| pr_info("%s: this pad applies no auto mode\n", __func__); |
| return 0; |
| } |
| } |
| |
| static void mfc_mis_align(struct mfc_charger_data *charger) |
| { |
| pr_info("%s: Reset M0\n", __func__); |
| if (charger->pdata->cable_type == SEC_WIRELESS_PAD_WPC_HV || |
| charger->pdata->cable_type == SEC_WIRELESS_PAD_PMA) |
| /* reset MCU of MFC IC */ |
| mfc_set_cmd_l_reg(charger, MFC_CMD_MCU_RESET_MASK, MFC_CMD_MCU_RESET_MASK); |
| } |
| |
| static bool mfc_tx_function_check(struct mfc_charger_data *charger) |
| { |
| u8 reg_f2, reg_f4; |
| |
| mfc_reg_read(charger->client, MFC_TX_RXID1_READ_REG, ®_f2); |
| mfc_reg_read(charger->client, MFC_TX_RXID3_READ_REG, ®_f4); |
| |
| pr_info("@Tx_Mode %s: 0x%x 0x%x\n", __func__, reg_f2, reg_f4); |
| |
| if ((reg_f2 == 0x42) && (reg_f4 == 0x64)) |
| return true; |
| else |
| return false; |
| } |
| |
| static void mfc_set_tx_ioffset(struct mfc_charger_data *charger, int ioffset) |
| { |
| u8 ioffset_data_l = 0, ioffset_data_h = 0; |
| |
| ioffset_data_l = 0xFF & ioffset; |
| ioffset_data_h = 0xFF & (ioffset >> 8); |
| |
| mfc_reg_write(charger->client, MFC_TX_IUNO_OFFSET_L_REG, ioffset_data_l); |
| mfc_reg_write(charger->client, MFC_TX_IUNO_OFFSET_H_REG, ioffset_data_h); |
| |
| pr_info("@Tx_Mode %s: Tx Iout set %d(0x%x 0x%x)\n", __func__, ioffset, ioffset_data_h, ioffset_data_l); |
| |
| |
| } |
| |
| static void mfc_set_tx_iout(struct mfc_charger_data *charger, unsigned int iout) |
| { |
| u8 iout_data_l = 0, iout_data_h = 0; |
| |
| iout_data_l = 0xFF & iout; |
| iout_data_h = 0xFF & (iout >> 8); |
| |
| mfc_reg_write(charger->client, MFC_TX_IUNO_LIMIT_L_REG, iout_data_l); |
| mfc_reg_write(charger->client, MFC_TX_IUNO_LIMIT_H_REG, iout_data_h); |
| |
| pr_info("@Tx_Mode %s: Tx Iout set %d(0x%x 0x%x)\n", __func__, iout, iout_data_h, iout_data_l); |
| |
| if (iout == 1100) |
| mfc_set_tx_ioffset(charger, 100); |
| else |
| mfc_set_tx_ioffset(charger, 150); |
| } |
| |
| static void mfc_print_buffer(struct mfc_charger_data *charger, u8 * buffer, u8 size) |
| { |
| u8 i; |
| |
| for (i = 0; i < size; i++) |
| pr_info("0x%x", buffer[i]); |
| |
| pr_info("\n"); |
| } |
| |
| static int mfc_auth_adt_read(struct mfc_charger_data *charger, u8 * readData) |
| { |
| int buffAddr = MFC_ADT_BUFFER_ADT_PARAM_REG; |
| union power_supply_propval value = {0, }; |
| const int sendsz = 16; |
| int i = 0, size = 0; |
| int ret = 0; |
| |
| mfc_reg_read(charger->client, MFC_ADT_BUFFER_ADT_MSG_SIZE_REG, &adt_readSize); |
| pr_info("%s %s read size = %d byte\n", WC_AUTH_MSG, __func__, 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__); |
| //charger->adt_transfer_status = WIRELESS_AUTH_FAIL; |
| /* notify auth service to send TX PAD a request key */ |
| value.intval = WIRELESS_AUTH_FAIL; |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS, value); |
| 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); |
| |
| value.intval = WIRELESS_AUTH_RECEIVED; |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS, value); |
| |
| charger->adt_transfer_status = 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, u8 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; |
| } |
| |
| wdata = 0x08; /* Authentication purpose */ |
| 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; |
| } else { |
| 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; |
| u8 irq_src[2]; |
| |
| mfc_reg_read(charger->client, MFC_INT_A_L_REG, &irq_src[0]); |
| mfc_reg_read(charger->client, MFC_INT_A_H_REG, &irq_src[1]); |
| |
| 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_H_REG, |
| MFC_STAT_H_ADT_SENT_MASK, MFC_STAT_H_ADT_SENT_MASK); |
| |
| /* clear intterupt */ |
| mfc_reg_write(charger->client, MFC_INT_A_CLEAR_L_REG, irq_src[0]); // clear int |
| mfc_reg_write(charger->client, MFC_INT_A_CLEAR_H_REG, irq_src[1]); // clear int |
| mfc_set_cmd_l_reg(charger, 0x20, MFC_CMD_CLEAR_INT_MASK); // command |
| } |
| |
| /* 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) { |
| pr_info("@Tx_Mode %s: Turn On TX Power\n", __func__); |
| mfc_uno_on(charger, true); |
| msleep(200); |
| |
| charger->pdata->otp_firmware_ver = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE); |
| |
| cancel_delayed_work(&charger->wpc_tx_op_freq_work); |
| wake_lock(&charger->wpc_tx_opfq_lock); |
| queue_delayed_work(charger->wqueue, |
| &charger->wpc_tx_op_freq_work, msecs_to_jiffies(5000)); |
| charger->wc_rx_fod = false; |
| pr_info("%s : tx op freq = %dKhz\n", __func__, mfc_get_adc(charger, MFC_ADC_TX_OP_FRQ)); |
| mfc_set_min_duty(charger, 30); |
| } else { |
| pr_info("@Tx_Mode %s: Turn Off TX Power, and reset UNO config\n", __func__); |
| |
| mfc_uno_on(charger, false); |
| |
| value.intval = WC_TX_VOUT_5_0V; |
| psy_do_property(charger->pdata->wired_charger_name, set, |
| POWER_SUPPLY_EXT_PROP_WIRELESS_TX_VOUT, value); |
| |
| charger->wc_rx_connected = false; |
| charger->gear_start_time = 0; |
| |
| cancel_delayed_work(&charger->wpc_rx_det_work); |
| cancel_delayed_work(&charger->wpc_tx_isr_work); |
| cancel_delayed_work(&charger->wpc_tx_phm_work); |
| alarm_cancel(&charger->phm_alarm); |
| wake_unlock(&charger->wpc_tx_phm_lock); |
| wake_unlock(&charger->wpc_rx_det_lock); |
| wake_unlock(&charger->wpc_tx_wake_lock); |
| |
| charger->tx_status = SEC_TX_OFF; |
| } |
| } |
| |
| /* determine rx connection status with tx sharing mode */ |
| static void mfc_tx_check_rx_connection(struct mfc_charger_data *charger, bool enable) |
| { |
| union power_supply_propval value = {0, }; |
| |
| if (!charger->wc_tx_enable) |
| return; |
| |
| if(enable) { |
| pr_info("@Tx_Mode %s : Rx(%s) connected\n", __func__, mfc_tx_function_check(charger) ? "Samsung" : "Other"); |
| |
| cancel_delayed_work(&charger->wpc_rx_det_work); |
| wake_lock(&charger->wpc_rx_det_lock); |
| queue_delayed_work(charger->wqueue, |
| &charger->wpc_rx_det_work, msecs_to_jiffies(1000)); |
| charger->wc_rx_connected = true; |
| } else { |
| pr_info("@Tx_Mode %s : Rx disconnected\n", __func__); |
| charger->wc_rx_connected = false; |
| charger->wc_rx_fod = false; |
| charger->gear_start_time = 0; |
| charger->wc_rx_type = NO_DEV; |
| |
| if (delayed_work_pending(&charger->wpc_rx_det_work)) { |
| wake_unlock(&charger->wpc_rx_det_lock); |
| cancel_delayed_work(&charger->wpc_rx_det_work); |
| } |
| |
| if (delayed_work_pending(&charger->wpc_tx_op_freq_work)) { |
| wake_unlock(&charger->wpc_tx_opfq_lock); |
| cancel_delayed_work(&charger->wpc_tx_op_freq_work); |
| } |
| } |
| |
| /* set rx connection condition */ |
| value.intval = charger->wc_rx_connected; |
| psy_do_property("wireless", set, POWER_SUPPLY_EXT_PROP_WIRELESS_RX_CONNECTED, value); |
| } |
| |
| /* 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_DATA_COM_REG, &cmd_data); |
| mfc_reg_read(charger->client, MFC_WPC_TRX_DATA_VALUE0_REG, &val_data); |
| mfc_reg_read(charger->client, MFC_WPC_TRX_DATA_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) { |
| switch (val_data) { |
| case WPC_COM_DISABLE_TX: |
| if(val2_data == 0xFF) { |
| pr_info("@Tx_Mode %s: Gear send TX off packet by Misalign or darkzone issue\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 == 0x55) { |
| pr_info("@Tx_Mode %s: this TX device should turn off TX power sharing(uno) since RX device has a 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: |
| if (val2_data == 0x01) { |
| if(charger->wc_rx_type == SS_GEAR) { |
| /* start 3min alarm timer */ |
| pr_info("%s: Start PHM 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 3min timer\n", __func__); |
| } |
| } |
| break; |
| case WPC_COM_RX_ID: |
| if (val2_data >= 0x70 && val2_data <= 0x8f) { |
| 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 == 0x64) { |
| pr_info("%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); |
| } |
| } |
| } |
| |
| 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; |
| const int sendsz = 16; |
| int cnt = 0; |
| int retry_cnt = 0; |
| unsigned char data[sendsz+2]; |
| u8 rdata[sendsz+2]; |
| |
| // dev_dbg(&client->dev, "%s: size: 0x%x\n", __func__, size); |
| while(size > sendsz) { |
| data[0] = (reg+cnt) >>8; |
| data[1] = (reg+cnt) & 0xff; |
| memcpy(data+2, val+cnt, sendsz); |
| // 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 int mfc_reg_multi_write(struct i2c_client *client, u16 reg, const u8 * val, int size) |
| { |
| struct mfc_charger_data *charger = i2c_get_clientdata(client); |
| int ret = 0; |
| const int sendsz = 16; |
| unsigned char data[sendsz+2]; |
| int cnt = 0; |
| |
| pr_err("%s: size: 0x%x\n", |
| __func__, size); |
| while(size > sendsz) { |
| data[0] = (reg+cnt) >>8; |
| data[1] = (reg+cnt) & 0xff; |
| memcpy(data+2, val+cnt, sendsz); |
| mutex_lock(&charger->io_lock); |
| ret = i2c_master_send(client, data, sendsz+2); |
| mutex_unlock(&charger->io_lock); |
| if (ret < sendsz+2) { |
| pr_err("%s: i2c write error, reg: 0x%x\n", |
| __func__, reg); |
| return ret < 0 ? ret : -EIO; |
| } |
| cnt = cnt + sendsz; |
| size = size - sendsz; |
| } |
| if (size > 0) { |
| data[0] = (reg+cnt) >>8; |
| data[1] = (reg+cnt) & 0xff; |
| memcpy(data+2, val+cnt, size); |
| mutex_lock(&charger->io_lock); |
| ret = i2c_master_send(client, data, size+2); |
| mutex_unlock(&charger->io_lock); |
| if (ret < size+2) { |
| dev_err(&client->dev, "%s: i2c write error, reg: 0x%x\n", |
| __func__, reg); |
| return ret < 0 ? ret : -EIO; |
| } |
| } |
| |
| return ret; |
| } |
| |
| #if 0 |
| static int mfc_firmware_verify(struct mfc_charger_data *charger) |
| { |
| int ret = 0; |
| const u16 sendsz = 16; |
| size_t i = 0; |
| int block_len = 0; |
| int block_addr = 0; |
| u8 rdata[sendsz+2]; |
| |
| /* I2C WR to prepare boot-loader write */ |
| |
| if (mfc_reg_write(charger->client, 0x3000, 0x5a) < 0) { |
| pr_err("%s: key error\n", __func__); |
| return 0; |
| } |
| |
| if (mfc_reg_write(charger->client, 0x3040, 0x11) < 0) { |
| pr_err("%s: halt M0, OTP_I2C_EN set error\n", __func__); |
| return 0; |
| } |
| |
| dev_err(&charger->client->dev, "%s, request_firmware\n", __func__); |
| ret = request_firmware(&charger->firm_data_bin, MFC_FLASH_FW_HEX_PATH, |
| &charger->client->dev); |
| if ( ret < 0) { |
| dev_err(&charger->client->dev, "%s: failed to request firmware %s (%d)\n", |
| __func__, MFC_FLASH_FW_HEX_PATH, ret); |
| return 0; |
| } |
| ret = 1; |
| wake_lock(&charger->wpc_update_lock); |
| for (i = 0; i < charger->firm_data_bin->size; i += sendsz) { |
| block_len = (i + sendsz) > charger->firm_data_bin->size ? charger->firm_data_bin->size - i : sendsz; |
| block_addr = 0x8000 + i; |
| |
| if (mfc_reg_multi_read(charger->client, block_addr, rdata, block_len) < 0) { |
| pr_err("%s, read failed\n", __func__); |
| ret = 0; |
| break; |
| } |
| if (datacmp(charger->firm_data_bin->data + i, rdata, block_len)) { |
| pr_err("%s, verify data is not matched. block_len(%d), block_addr(%d)\n", |
| __func__, block_len, block_addr); |
| ret = -1; |
| break; |
| } |
| } |
| release_firmware(charger->firm_data_bin); |
| |
| wake_unlock(&charger->wpc_update_lock); |
| return ret; |
| } |
| #endif |
| |
| static int mfc_write_fw_flash_LSI(struct mfc_charger_data *charger, u8 addr_l, u8 addr_h, const u8 *wData, int length) |
| { |
| int i; |
| u8 ucTmpBuf[36]; |
| |
| if (length <= 0) { |
| pr_err("%s: skip. length is 0\n", __func__); |
| return -1; |
| } |
| |
| ucTmpBuf[0] = length / 4; |
| ucTmpBuf[1] = addr_l; |
| ucTmpBuf[2] = addr_h; |
| |
| for (i = 0; i < length; i++) |
| ucTmpBuf[i + 3] = wData[i]; |
| |
| if (mfc_reg_multi_write(charger->client, 0x1F11, ucTmpBuf, length + 3) < 0) { |
| pr_err("%s: failed to write ucTmpBuf(%d bytes) at 0x1F11\n", __func__, length + 3); |
| return -1; |
| } |
| |
| if (mfc_reg_write(charger->client, 0x1F10, 0x52) < 0) { |
| pr_err("%s: failed to write 0x52 at 0x1F10\n", __func__); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| //#define LSI_MFC_FW_BOOT_CODE_INCLUDE |
| #define LSI_MFC_FW_FLASH_START_ADDR 0x0C00 |
| static int PgmOTPwRAM_LSI(struct mfc_charger_data *charger, unsigned short OtpAddr, |
| const u8 * srcData, int srcOffs, int size) |
| { |
| int addr; |
| int start_addr; |
| u8 wdata[4] = {0,}; |
| // static int startAddr; |
| u16 temp; |
| u8 addr_l, addr_h; |
| char chksum; |
| int i; |
| u8 fw_ver[4] = {0,}; |
| u8 fw_ver_bin[4] = {0,}; |
| |
| mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_L_REG, &fw_ver[0]); |
| mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_H_REG, &fw_ver[1]); |
| pr_info("%s BEFORE rx major firmware version 0x%x\n", |
| __func__, fw_ver[0] | (fw_ver[1] << 8)); |
| |
| mfc_reg_read(charger->client, MFC_FW_MINOR_REV_L_REG, &fw_ver[2]); |
| mfc_reg_read(charger->client, MFC_FW_MINOR_REV_H_REG, &fw_ver[3]); |
| pr_info("%s BEFORE rx minor firmware version 0x%x\n", |
| __func__, fw_ver[2] | (fw_ver[3] << 8)); |
| |
| memcpy(fw_ver_bin, &srcData[MFC_FW_VER_BIN_LSI], 4); |
| pr_info("%s NEW rx major firmware version 0x%x\n", |
| __func__, fw_ver_bin[0] | (fw_ver_bin[1] << 8)); |
| pr_info("%s NEW rx minor firmware version 0x%x\n", |
| __func__, fw_ver_bin[2] | (fw_ver_bin[3] << 8)); |
| |
| pr_info("%s: Enter the flash mode (0x1F10)\n", __func__); |
| if (mfc_reg_write(charger->client, 0x1F10, 0x10) < 0) { |
| pr_err("%s: failed to enter the flash mode\n", __func__); |
| return MFC_FWUP_ERR_FAIL; |
| } |
| msleep(100); |
| pr_info("%s: Erase the flash memory\n", __func__); |
| if (mfc_reg_write(charger->client, 0x1F10, 0x44) < 0) { |
| pr_err("%s: failed to erase flash\n", __func__); |
| return MFC_FWUP_ERR_FAIL; |
| } |
| |
| msleep(1000); /* erasing flash needs 200ms delay at least */ |
| |
| #ifdef LSI_MFC_FW_BOOT_CODE_INCLUDE |
| pr_info("%s: Erase boot code (0x1F10)\n", __func__); |
| if (mfc_reg_write(charger->client, 0x1F10, 0x55) < 0) { |
| pr_err("%s: failed to erase boot code\n", __func__); |
| return MFC_FWUP_ERR_FAIL; |
| } |
| msleep(300); |
| #endif |
| pr_info("%s: write fwimg by 32 bytes \n", __func__); |
| |
| #ifdef LSI_MFC_FW_BOOT_CODE_INCLUDE |
| start_addr = 0x00; |
| #else |
| start_addr = LSI_MFC_FW_FLASH_START_ADDR; |
| #endif |
| chksum = 0; |
| for (i = start_addr; i < size; i++) |
| chksum += srcData[i]; |
| |
| for (addr = start_addr; addr < size; addr += 32) // program pages of 32bytes |
| { |
| temp = addr & 0xff; |
| addr_l = (u8)temp; |
| temp = ((addr & 0xff00) >> 8); |
| addr_h = (u8)temp; |
| if (size - addr >= 32) |
| mfc_write_fw_flash_LSI(charger, addr_l, addr_h, srcData + addr, 32); |
| else |
| mfc_write_fw_flash_LSI(charger, addr_l, addr_h, srcData + addr, size - addr); |
| } |
| |
| pr_info("%s: write fw length --------------------\n", __func__); |
| wdata[0] = (u8)(size & 0x00FF); |
| wdata[1] = (u8)((size>>8) & 0x00FF); |
| wdata[2] = 0x00; |
| wdata[3] = 0x00; |
| mfc_write_fw_flash_LSI(charger, 0xf4, 0x7e, wdata, 4); |
| |
| pr_info("%s: write fw checksum --------------------\n", __func__); |
| chksum = -chksum; |
| wdata[0] = chksum; /* checksum */ |
| wdata[1] = 0x00; |
| wdata[2] = 0x00; |
| wdata[3] = 0x00; |
| mfc_write_fw_flash_LSI(charger, 0xf8, 0x7e, wdata, 4); |
| |
| pr_info("%s: write flash done flag --------------------*\n", __func__); |
| wdata[0] = 0x01; |
| wdata[1] = 0x00; |
| wdata[2] = 0x00; |
| wdata[3] = 0x00; |
| mfc_write_fw_flash_LSI(charger, 0xfc, 0x7e, wdata, 4); |
| |
| msleep(10); |
| pr_info("%s: Enter the normal mode\n", __func__); |
| if (mfc_reg_write(charger->client, 0x1F10, 0x20) < 0) { |
| pr_err("%s: failed to enter the normal mode\n", __func__); |
| return MFC_FWUP_ERR_FAIL; |
| } |
| msleep(10); |
| |
| mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_L_REG, &fw_ver[0]); |
| mfc_reg_read(charger->client, MFC_FW_MAJOR_REV_H_REG, &fw_ver[1]); |
| pr_info("%s NOW rx major firmware version 0x%x\n", |
| __func__, fw_ver[0] | (fw_ver[1] << 8)); |
| |
| mfc_reg_read(charger->client, MFC_FW_MINOR_REV_L_REG, &fw_ver[2]); |
| mfc_reg_read(charger->client, MFC_FW_MINOR_REV_H_REG, &fw_ver[3]); |
| pr_info("%s NOW rx minor firmware version 0x%x\n", |
| __func__, fw_ver[2] | (fw_ver[3] << 8)); |
| |
| for (i = 0; i < 4; i++) |
| if (fw_ver[i] != fw_ver_bin[i]) |
| return MFC_FWUP_ERR_COMMON_FAIL; |
| |
| return MFC_FWUP_ERR_SUCCEEDED; |
| } |
| |
| static void mfc_reset_rx_power(struct mfc_charger_data *charger, u8 rx_power) |
| { |
| if(charger->adt_transfer_status == WIRELESS_AUTH_PASS) { |
| union power_supply_propval value = {0, }; |
| switch(rx_power) { |
| case TX_RX_POWER_7_5W: |
| value.intval = SEC_WIRELESS_RX_POWER_7_5W; |
| break; |
| case TX_RX_POWER_12W: |
| value.intval = SEC_WIRELESS_RX_POWER_12W; |
| break; |
| case TX_RX_POWER_15W: |
| value.intval = SEC_WIRELESS_RX_POWER_15W; |
| break; |
| case TX_RX_POWER_17_5W: |
| value.intval = SEC_WIRELESS_RX_POWER_17_5W; |
| break; |
| case TX_RX_POWER_20W: |
| value.intval = SEC_WIRELESS_RX_POWER_20W; |
| break; |
| default: |
| value.intval = SEC_WIRELESS_RX_POWER_12W; |
| pr_info("%s %s : undefine rx power reset\n", WC_AUTH_MSG, __func__); |
| break; |
| } |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_WIRELESS_RX_POWER, value); |
| } 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_cm_fet_work(struct work_struct *work) |
| { |
| struct mfc_charger_data *charger = |
| container_of(work, struct mfc_charger_data, wpc_cm_fet_work.work); |
| u8 tmp = 0; |
| |
| /* disable all CM FETs for MST operation */ |
| mfc_reg_write(charger->client, MFC_RX_COMM_MOD_FET_REG, 0xf0); |
| mfc_reg_read(charger->client, MFC_RX_COMM_MOD_FET_REG, &tmp); |
| pr_info("%s: disable CM FET (0x%x)\n", __func__, tmp); |
| } |
| |
| 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); |
| union power_supply_propval value = {0, }; |
| |
| pr_info("%s start, current cable(%d)\n", __func__, charger->pdata->cable_type); |
| |
| /* change cable type */ |
| if (charger->pdata->cable_type == SEC_WIRELESS_PAD_PREPARE_HV) |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_WPC_HV; |
| else |
| value.intval = charger->pdata->cable_type; |
| |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_ONLINE, value); |
| |
| #if defined(CONFIG_BATTERY_SWELLING) |
| /* check swelling mode */ |
| psy_do_property("battery", get, |
| POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, value); |
| pr_info("%s: check swelling mode(%d)\n", __func__, value.intval); |
| |
| if (value.intval) |
| goto skip_set_afc_vout; |
| #endif |
| |
| if (sleep_mode) |
| goto skip_set_afc_vout; |
| |
| pr_info("%s: check OTG(%d), full(%d) tx_id(%d)\n", |
| __func__, charger->is_otg_on, charger->is_full_status, charger->tx_id); |
| |
| if (charger->is_otg_on) |
| goto skip_set_afc_vout; |
| |
| if (charger->is_full_status && |
| (charger->tx_id != TX_ID_DREAM_STAND && |
| charger->tx_id != TX_ID_DREAM_DOWN)) |
| goto skip_set_afc_vout; |
| |
| // need to fix here to get vout setting |
| if (charger->pdata->cable_type == SEC_WIRELESS_PAD_WPC_HV_20) |
| mfc_send_command(charger, charger->vrect_by_txid); |
| else |
| mfc_send_command(charger, MFC_AFC_CONF_10V_TX); |
| |
| charger->is_afc_tx = true; |
| pr_info("%s: is_afc_tx = %d vout read = %d\n", |
| __func__, charger->is_afc_tx, mfc_get_adc(charger, MFC_ADC_VOUT)); |
| |
| pr_info("%s: check state(Vmode:%d, Vstatus:%d, Otg:%d)\n", __func__, |
| charger->vout_mode, charger->pdata->vout_status, charger->is_otg_on); |
| |
| if (!charger->is_full_status && |
| charger->vout_mode != WIRELESS_VOUT_5V && |
| charger->vout_mode != WIRELESS_VOUT_5V_STEP && |
| charger->vout_mode != WIRELESS_VOUT_5_5V_STEP) { |
| // need to fix here |
| if (charger->pdata->cable_type == SEC_WIRELESS_PAD_WPC_HV_20) |
| charger->vout_mode = charger->vout_by_txid; |
| else |
| charger->vout_mode = WIRELESS_VOUT_10V; |
| cancel_delayed_work(&charger->wpc_vout_mode_work); |
| wake_lock(&charger->wpc_vout_mode_lock); |
| queue_delayed_work(charger->wqueue, |
| &charger->wpc_vout_mode_work, 0); |
| } |
| |
| skip_set_afc_vout: |
| wake_unlock(&charger->wpc_afc_vout_lock); |
| } |
| |
| 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); |
| |
| int ret = 0; |
| int i = 0; |
| char fwtime[8] = {32, 32, 32, 32, 32, 32, 32, 32}; |
| char fwdate[8] = {32, 32, 32, 32, 32, 32, 32, 32}; |
| u8 data = 32; /* ascii space */ |
| #if defined(CONFIG_WIRELESS_IC_PARAM) |
| u32 param_data = 0; |
| #endif |
| |
| 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_RX_SPU_MODE: |
| case SEC_WIRELESS_RX_SDCARD_MODE: |
| mfc_uno_on(charger, true); |
| msleep(200); |
| if (mfc_get_chip_id(charger) < 0 || |
| charger->chip_id != MFC_CHIP_LSI) { |
| pr_info("%s: current IC's chip_id(0x%x) is not matching to driver's chip_id(0x%x)\n", |
| __func__, charger->chip_id, MFC_CHIP_LSI); |
| break; |
| } |
| charger->pdata->otp_firmware_result = MFC_FWUP_ERR_RUNNING; |
| disable_irq(charger->pdata->irq_wpc_int); |
| disable_irq(charger->pdata->irq_wpc_det); |
| #if defined(CONFIG_WIRELESS_IC_PARAM) |
| mfc_set_wireless_param(MFC_CHIP_ID_S2MIW04, 0); |
| #endif |
| ret = PgmOTPwRAM_LSI(charger, 0, charger->fw_img, 0, charger->fw_size); |
| |
| charger->pdata->otp_firmware_ver = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE); |
| charger->pdata->wc_ic_rev = mfc_get_ic_revision(charger, MFC_IC_REVISION); |
| |
| if (ret == MFC_FWUP_ERR_SUCCEEDED) |
| charger->pdata->otp_firmware_result = MFC_FW_RESULT_PASS; |
| else |
| charger->pdata->otp_firmware_result = ret; |
| |
| enable_irq(charger->pdata->irq_wpc_int); |
| enable_irq(charger->pdata->irq_wpc_det); |
| break; |
| case SEC_WIRELESS_RX_INIT: |
| case SEC_WIRELESS_RX_BUILT_IN_MODE: |
| mfc_uno_on(charger, true); |
| msleep(200); |
| if (mfc_get_chip_id(charger) < 0 || |
| charger->chip_id != MFC_CHIP_LSI) { |
| pr_info("%s: current IC's chip_id(0x%x) is not matching to driver's chip_id(0x%x)\n", |
| __func__, charger->chip_id, MFC_CHIP_LSI); |
| break; |
| } |
| if (charger->fw_cmd == SEC_WIRELESS_RX_INIT) { |
| charger->pdata->otp_firmware_ver = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE); |
| #if defined(CONFIG_WIRELESS_IC_PARAM) |
| if (wireless_fw_mode_param == SEC_WIRELESS_RX_SPU_MODE && |
| charger->pdata->otp_firmware_ver > MFC_FW_BIN_VERSION) { |
| pr_info("%s: current version (0x%x) is higher than BIN_VERSION by SPU(0x%x)\n", |
| __func__, charger->pdata->otp_firmware_ver, MFC_FW_BIN_VERSION); |
| break; |
| } |
| #endif |
| if (charger->pdata->otp_firmware_ver == MFC_FW_BIN_VERSION) { |
| pr_info("%s: current version (0x%x) is same to BIN_VERSION (0x%x)\n", |
| __func__, charger->pdata->otp_firmware_ver, MFC_FW_BIN_VERSION); |
| break; |
| } |
| } |
| charger->pdata->otp_firmware_result = MFC_FWUP_ERR_RUNNING; |
| |
| disable_irq(charger->pdata->irq_wpc_int); |
| disable_irq(charger->pdata->irq_wpc_det); |
| dev_err(&charger->client->dev, "%s, request_firmware\n", __func__); |
| |
| ret = request_firmware(&charger->firm_data_bin, MFC_FLASH_FW_HEX_LSI_PATH, |
| &charger->client->dev); |
| if ( ret < 0) { |
| dev_err(&charger->client->dev, "%s: failed to request firmware %s (%d)\n", __func__, MFC_FLASH_FW_HEX_LSI_PATH, ret); |
| charger->pdata->otp_firmware_result = MFC_FWUP_ERR_REQUEST_FW_BIN; |
| goto fw_err; |
| } |
| wake_lock(&charger->wpc_update_lock); |
| #if defined(CONFIG_WIRELESS_IC_PARAM) |
| mfc_set_wireless_param(MFC_CHIP_ID_S2MIW04, 0); |
| #endif |
| pr_info("%s data size = %ld\n", __func__, charger->firm_data_bin->size); |
| ret = PgmOTPwRAM_LSI(charger, 0 ,charger->firm_data_bin->data, 0, charger->firm_data_bin->size); |
| |
| release_firmware(charger->firm_data_bin); |
| |
| for (i = 0; i < 8; i++) { |
| if (mfc_reg_read(charger->client, MFC_FW_DATA_CODE_0+i, &data) > 0) |
| fwdate[i] = (char)data; |
| } |
| for (i = 0; i < 8; i++) { |
| if (mfc_reg_read(charger->client, MFC_FW_TIMER_CODE_0+i, &data) > 0) |
| fwtime[i] = (char)data; |
| } |
| pr_info("%s: %d%d%d%d%d%d%d%d, %d%d%d%d%d%d%d%d\n", __func__, |
| fwdate[0], fwdate[1], fwdate[2],fwdate[3], fwdate[4], fwdate[5], fwdate[6], fwdate[7], |
| fwtime[0], fwtime[1], fwtime[2],fwtime[3], fwtime[4], fwtime[5], fwtime[6], fwtime[7]); |
| |
| charger->pdata->otp_firmware_ver = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE); |
| charger->pdata->wc_ic_rev = mfc_get_ic_revision(charger, MFC_IC_REVISION); |
| |
| if (ret == MFC_FWUP_ERR_SUCCEEDED) { |
| charger->pdata->otp_firmware_result = MFC_FW_RESULT_PASS; |
| #if defined(CONFIG_WIRELESS_IC_PARAM) |
| ret = sec_get_param_u32(wireless_offset, ¶m_data); |
| if (ret < 0) { |
| pr_err("%s: sec_get_param failed\n", __func__); |
| } else { |
| param_data &= 0xFFFFFF0F; |
| param_data |= (charger->fw_cmd & 0xF) << 4; |
| pr_info("%s: param data 0x%08X\n", __func__, param_data); |
| ret = sec_set_param_u32(wireless_offset, param_data); |
| if (ret < 0) { |
| pr_err("%s: sec_set_param failed\n", __func__); |
| } else { |
| wireless_fw_mode_param = charger->fw_cmd & 0xF; |
| pr_info("%s: succeed. fw_mode(0x%01X)\n", __func__, wireless_fw_mode_param); |
| } |
| } |
| #endif |
| } else |
| charger->pdata->otp_firmware_result = ret; |
| |
| for (i = 0; i < 8; i++) { |
| if (mfc_reg_read(charger->client, MFC_FW_DATA_CODE_0+i, &data) > 0) |
| fwdate[i] = (char)data; |
| } |
| for (i = 0; i < 8; i++) { |
| if (mfc_reg_read(charger->client, MFC_FW_TIMER_CODE_0+i, &data) > 0) |
| fwtime[i] = (char)data; |
| } |
| pr_info("%s: %d%d%d%d%d%d%d%d, %d%d%d%d%d%d%d%d\n", __func__, |
| fwdate[0], fwdate[1], fwdate[2],fwdate[3], fwdate[4], fwdate[5], fwdate[6], fwdate[7], |
| fwtime[0], fwtime[1], fwtime[2],fwtime[3], fwtime[4], fwtime[5], fwtime[6], fwtime[7]); |
| |
| enable_irq(charger->pdata->irq_wpc_int); |
| enable_irq(charger->pdata->irq_wpc_det); |
| wake_unlock(&charger->wpc_update_lock); |
| break; |
| case SEC_WIRELESS_TX_ON_MODE: |
| charger->pdata->cable_type = SEC_WIRELESS_PAD_TX; |
| break; |
| case SEC_WIRELESS_TX_OFF_MODE: |
| /* need to check */ |
| break; |
| default: |
| break; |
| } |
| |
| msleep(200); |
| mfc_uno_on(charger, false); |
| pr_info("%s ---------------------------------------------------------------\n", __func__); |
| mfc_fw_update = false; |
| if (charger->fw_img) |
| kfree(charger->fw_img); |
| charger->fw_img = NULL; |
| |
| mfc_set_wpc_en(charger, WPC_EN_FW, false); |
| |
| return; |
| fw_err: |
| mfc_uno_on(charger, false); |
| mfc_set_wpc_en(charger, WPC_EN_FW, false); |
| end_of_fw_work: |
| mfc_fw_update = false; |
| } |
| |
| static int mfc_chg_get_property(struct power_supply *psy, |
| enum power_supply_property psp, |
| union power_supply_propval *val) |
| { |
| struct mfc_charger_data *charger = power_supply_get_drvdata(psy); |
| enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp; |
| // union power_supply_propval value; |
| u8 mst_mode; |
| u8 reg_data; |
| |
| switch (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_TECHNOLOGY: |
| val->intval = mfc_reg_read(charger->client, MFC_MST_MODE_SEL_REG, &mst_mode); |
| if (val->intval < 0) { |
| pr_info("%s mst mode(0x2) i2c write failed, val->intval = %d\n", |
| __func__, val->intval); |
| break; |
| } |
| |
| val->intval = mfc_reg_read(charger->client, MFC_SYS_OP_MODE_REG, ®_data); |
| if (val->intval < 0) { |
| pr_info("%s mst mode change irq(0x4) read failed, val->intval = %d\n", |
| __func__, val->intval); |
| break; |
| } |
| reg_data &= 0x0C; /* use only [3:2]bit of sys_op_mode register for MST */ |
| |
| pr_info("%s mst mode check: mst_mode = %d, reg_data = %d\n", |
| __func__, mst_mode, reg_data); |
| val->intval = 0; |
| if (reg_data == 0x4) |
| val->intval = mst_mode; |
| 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_OTG_CONTROL: |
| case POWER_SUPPLY_PROP_CHARGE_POWERED_OTG_CONTROL: |
| 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_INPUT_VOLTAGE_REGULATION: |
| val->intval = charger->pdata->vout_status; |
| break; |
| case POWER_SUPPLY_PROP_MANUFACTURER: |
| pr_info("%s: POWER_SUPPLY_PROP_MANUFACTURER, intval(0x%x), called by(%ps)\n", __func__, val->intval, __builtin_return_address(0)); |
| if (val->intval == SEC_WIRELESS_OTP_FIRM_RESULT) { |
| pr_info("%s otp firmware result = %d,\n", __func__, charger->pdata->otp_firmware_result); |
| val->intval = charger->pdata->otp_firmware_result; |
| } else if (val->intval == SEC_WIRELESS_IC_REVISION) { |
| pr_info("%s: check ic revision\n", __func__); |
| val->intval = mfc_get_ic_revision(charger, MFC_IC_REVISION); |
| } else if (val->intval == SEC_WIRELESS_IC_CHIP_ID) { |
| pr_info("%s: check ic chip_id(0x%02X)\n", __func__, charger->chip_id); |
| val->intval = charger->chip_id; |
| } else if (val->intval == SEC_WIRELESS_OTP_FIRM_VER_BIN) { |
| /* update latest kernl f/w version */ |
| val->intval = MFC_FW_BIN_VERSION; |
| } else if (val->intval == SEC_WIRELESS_OTP_FIRM_VER) { |
| val->intval = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE); |
| pr_info("%s: check f/w revision (0x%x)\n", __func__, val->intval); |
| if (val->intval < 0 && charger->pdata->otp_firmware_ver > 0) |
| val->intval = charger->pdata->otp_firmware_ver; |
| } else if (val->intval == SEC_WIRELESS_TX_FIRM_RESULT) { |
| val->intval = charger->pdata->tx_firmware_result; |
| } else if (val->intval == SEC_WIRELESS_TX_FIRM_VER) { |
| val->intval = charger->pdata->tx_firmware_ver; |
| } else if (val->intval == SEC_TX_FIRMWARE) { |
| val->intval = charger->tx_status; |
| } else if (val->intval == SEC_WIRELESS_OTP_FIRM_VERIFY) { |
| pr_info("%s: LSI FIRM_VERIFY is not implemented\n", __func__); |
| val->intval = 1; |
| } else if (val->intval == SEC_WIRELESS_MST_SWITCH_VERIFY) { |
| if (gpio_is_valid(charger->pdata->mst_pwr_en)) { |
| gpio_direction_output(charger->pdata->mst_pwr_en, 1); |
| msleep(charger->pdata->mst_switch_delay); |
| val->intval = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE); |
| pr_info("%s: check f/w revision, mst power on (0x%x)\n", __func__, val->intval); |
| gpio_direction_output(charger->pdata->mst_pwr_en, 0); |
| } else { |
| pr_info("%s: MST_SWITCH_VERIFY, invalid gpio(mst_pwr_en)\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_MAX ... 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_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_RX_CONNECTED: |
| val->intval = charger->wc_rx_connected; |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_TYPE: |
| val->intval = charger->wc_rx_type; |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_UNO_VIN: |
| val->intval = mfc_get_adc(charger, MFC_ADC_TX_VOUT); |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_UNO_IIN: |
| val->intval = mfc_get_adc(charger, MFC_ADC_TX_IOUT); |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_POWER: |
| val->intval = charger->current_rx_power; |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS: |
| val->intval = charger->adt_transfer_status; |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_DATA: |
| { |
| //int i = 0; |
| //u8 *p_data; |
| |
| if(charger->adt_transfer_status == WIRELESS_AUTH_RECEIVED) { |
| pr_info("%s %s : MFC_ADT_RECEIVED (%d)\n", WC_AUTH_MSG, __func__, charger->adt_transfer_status); |
| val->strval = (u8 *)ADT_buffer_rdata; |
| //p_data = ADT_buffer_rdata; |
| //for(i=0; i< adt_readSize; i++) |
| // pr_info("%s: auth read data = %x", __func__, p_data[i]); |
| //pr_info("\n", __func__); |
| } else { |
| pr_info("%s: data hasn't been received yet\n", __func__); |
| return -ENODATA; |
| } |
| } |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_SIZE: |
| val->intval = adt_readSize; |
| pr_info("%s %s : MFC_ADT_RECEIVED (%d), DATA SIZE(%d)\n", WC_AUTH_MSG, __func__, charger->adt_transfer_status, val->intval); |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_VOUT: |
| val->intval = charger->vout_mode; |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_INITIAL_WC_CHECK: |
| val->intval = charger->initial_wc_check; |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_CHECK_FW_VER: |
| #if defined(CONFIG_WIRELESS_IC_PARAM) |
| pr_info("%s: fw_ver (param:0x%04X, build:0x%04X)\n", |
| __func__, wireless_fw_ver_param, MFC_FW_BIN_VERSION); |
| if (wireless_fw_ver_param == MFC_FW_BIN_VERSION) |
| val->intval = 1; |
| else |
| val->intval = 0; |
| #else |
| val->intval = 0; |
| #endif |
| break; |
| case POWER_SUPPLY_EXT_PROP_WPC_EN: |
| val->intval = gpio_get_value(charger->pdata->wpc_en); |
| break; |
| case POWER_SUPPLY_EXT_PROP_MONITOR_WORK: |
| if (gpio_get_value(charger->pdata->wpc_en)) |
| pr_info("%s: charger->wpc_en_flag(0x%x)\n", __func__, charger->wpc_en_flag); |
| 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; |
| union power_supply_propval value; |
| |
| if (is_shutdn) { |
| pr_err("%s: Escape by shtudown\n", __func__); |
| return; |
| } |
| pr_info("%s: start - vout_mode(%s), vout_status(%s)\n", |
| __func__, mfc_vout_control_mode_str[charger->vout_mode], rx_vout_str[charger->pdata->vout_status]); |
| switch (charger->vout_mode) { |
| case WIRELESS_VOUT_5V: |
| mfc_set_vout(charger, MFC_VOUT_5V); |
| break; |
| case WIRELESS_VOUT_9V: |
| mfc_set_vout(charger, MFC_VOUT_9V); |
| break; |
| case WIRELESS_VOUT_10V: |
| mfc_set_vout(charger, MFC_VOUT_10V); |
| /* reset AICL */ |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_CURRENT_MAX, value); |
| break; |
| case WIRELESS_VOUT_11V: |
| mfc_set_vout(charger, MFC_VOUT_11V); |
| /* reset AICL */ |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_CURRENT_MAX, value); |
| break; |
| case WIRELESS_VOUT_12V: |
| mfc_set_vout(charger, MFC_VOUT_12V); |
| /* reset AICL */ |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_CURRENT_MAX, value); |
| break; |
| case WIRELESS_VOUT_12_5V: |
| mfc_set_vout(charger, MFC_VOUT_12_5V); |
| /* reset AICL */ |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_CURRENT_MAX, value); |
| break; |
| case WIRELESS_VOUT_5V_STEP: |
| vout_step--; |
| if (vout_step >= MFC_VOUT_5V) { |
| mfc_set_vout(charger, vout_step); |
| cancel_delayed_work(&charger->wpc_vout_mode_work); |
| queue_delayed_work(charger->wqueue, |
| &charger->wpc_vout_mode_work, msecs_to_jiffies(250)); |
| return; |
| } |
| break; |
| case WIRELESS_VOUT_5_5V_STEP: |
| vout_step--; |
| if (vout_step >= MFC_VOUT_5_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; |
| 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; |
| default: |
| break; |
| } |
| #if !defined(CONFIG_SEC_FACTORY) |
| if ((is_hv_wireless_pad_type(charger->pdata->cable_type)) && |
| (charger->pdata->vout_status <= MFC_VOUT_5_5V && (charger->is_full_status || sleep_mode))) { |
| mfc_send_command(charger, MFC_AFC_CONF_5V_TX); |
| pr_info("%s: set TX 5V after cs100\n", __func__); |
| } |
| #endif |
| pr_info("%s: finish - vout_mode(%s), vout_status(%s)\n", |
| __func__, mfc_vout_control_mode_str[charger->vout_mode], rx_vout_str[charger->pdata->vout_status]); |
| wake_unlock(&charger->wpc_vout_mode_lock); |
| } |
| |
| 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->wc_w_state && |
| gpio_get_value(charger->pdata->wpc_det)) { |
| union power_supply_propval value; |
| |
| psy_do_property("battery", set, |
| POWER_SUPPLY_EXT_PROP_WC_CONTROL, value); |
| } |
| } |
| |
| static void mfc_set_tx_fod_with_gear(struct mfc_charger_data *charger) |
| { |
| struct timespec ts = {0, }; |
| |
| pr_info("%s: FOD1 1300mA, FOD2 700mA\n", __func__); |
| /* FOD1 LIMIT 1300mA */ |
| mfc_reg_write(charger->client, MFC_TX_OC_FOD1_LIMIT_L_REG, 0x14); |
| mfc_reg_write(charger->client, MFC_TX_OC_FOD1_LIMIT_H_REG, 0x5); |
| /* FOD2 LIMIT 700mA */ |
| mfc_reg_write(charger->client, MFC_TX_OC_FOD2_LIMIT_L_REG, 0xBC); |
| mfc_reg_write(charger->client, MFC_TX_OC_FOD2_LIMIT_H_REG, 0x2); |
| |
| if (charger->gear_start_time == 0) { |
| ts = ktime_to_timespec(ktime_get_boottime()); |
| charger->gear_start_time = ts.tv_sec; |
| } |
| } |
| |
| static void mfc_wpc_rx_det_work(struct work_struct *work) |
| { |
| struct mfc_charger_data *charger = |
| container_of(work, struct mfc_charger_data, wpc_rx_det_work.work); |
| u8 reg_data; |
| u8 data[2] = {0,}; |
| int ret = 0, prmc_id = 0; |
| union power_supply_propval value; |
| |
| if (!charger->wc_tx_enable) { |
| wake_unlock(&charger->wpc_rx_det_lock); |
| return; |
| } |
| |
| mfc_reg_read(charger->client, MFC_STARTUP_EPT_COUNTER, ®_data); |
| |
| ret = mfc_reg_read(charger->client, MFC_TX_RXID1_READ_REG, &data[0]); |
| // ret = mfc_reg_read(charger->client, MFC_PRMC_ID_H_REG, &data[1]); |
| if (ret >= 0) { |
| prmc_id = data[0] | (data[1] << 8); |
| } |
| 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; |
| |
| mfc_set_tx_fod_with_gear(charger); |
| } else if (prmc_id == 0x42) { |
| pr_info("@Tx_Mode %s : Samsung Phone Connected\n", __func__); |
| charger->wc_rx_type = SS_PHONE; |
| } 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); |
| |
| wake_unlock(&charger->wpc_rx_det_lock); |
| } |
| |
| static void mfc_tx_op_freq_work(struct work_struct *work) |
| { |
| struct mfc_charger_data *charger = |
| container_of(work, struct mfc_charger_data, wpc_tx_op_freq_work.work); |
| |
| pr_info("%s \n", __func__); |
| |
| /* recover tx op freq */ |
| mfc_set_min_duty(charger, 20); |
| pr_info("%s : tx op freq = %dKhz\n", __func__, mfc_get_adc(charger, MFC_ADC_TX_OP_FRQ)); |
| |
| wake_unlock(&charger->wpc_tx_opfq_lock); |
| |
| } |
| |
| static void mfc_check_tx_gear_time(struct mfc_charger_data *charger) |
| { |
| struct timespec ts = {0, }; |
| unsigned long gear_charging_time; |
| |
| get_monotonic_boottime(&ts); |
| |
| if (ts.tv_sec >= charger->gear_start_time) { |
| gear_charging_time = ts.tv_sec - charger->gear_start_time; |
| } else { |
| gear_charging_time = 0xFFFFFFFF - charger->gear_start_time |
| + ts.tv_sec; |
| } |
| |
| if ((gear_charging_time >= 90) && charger->gear_start_time) { |
| pr_info("@Tx_Mode %s: set OC FOD1 900mA for Gear, gear_charging_time(%ld)\n", __func__, gear_charging_time); |
| /* FOD1 LIMIT 900mA */ |
| mfc_reg_write(charger->client, MFC_TX_OC_FOD1_LIMIT_L_REG, 0x84); |
| mfc_reg_write(charger->client, MFC_TX_OC_FOD1_LIMIT_H_REG, 0x3); |
| charger->gear_start_time = 0; |
| } |
| } |
| |
| 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); |
| |
| /* noirq resume of devices complete after 200 msecs */ |
| if(charger->is_suspend) |
| msleep(200); |
| |
| pr_info("%s \n", __func__); |
| mfc_set_cmd_l_reg(charger,MFC_CMD_TOGGLE_PHM_MASK, MFC_CMD_TOGGLE_PHM_MASK); |
| |
| wake_unlock(&charger->wpc_tx_phm_lock); |
| } |
| |
| #if defined(CONFIG_UPDATE_BATTERY_DATA) |
| static int mfc_chg_parse_dt(struct device *dev, mfc_charger_platform_data_t *pdata); |
| #endif |
| static int mfc_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 vout, vrect, iout, freq, tx_freq, i = 0; |
| u8 tmp = 0; |
| /* int ret; */ |
| u8 fod[MFC_NUM_FOD_REG] = {0, }; |
| |
| switch (psp) { |
| case POWER_SUPPLY_PROP_STATUS: |
| if (val->intval == POWER_SUPPLY_STATUS_FULL) { |
| pr_info("%s set cs100\n", __func__); |
| if (is_wireless_pad_type(charger->pdata->cable_type)) { |
| /* set fake FOD values before send cs100, need to tune */ |
| mfc_fod_set_cs100(charger); |
| } |
| charger->pdata->cs100_status = mfc_send_cs100(charger); |
| #if !defined(CONFIG_SEC_FACTORY) |
| if (!charger->is_full_status) { |
| charger->is_full_status = 1; |
| if (is_hv_wireless_pad_type(charger->pdata->cable_type) && |
| (charger->tx_id != TX_ID_DREAM_STAND && |
| charger->tx_id != TX_ID_DREAM_DOWN)) { |
| charger->vout_mode = WIRELESS_VOUT_5_5V_STEP; |
| cancel_delayed_work(&charger->wpc_vout_mode_work); |
| wake_lock(&charger->wpc_vout_mode_lock); |
| queue_delayed_work(charger->wqueue, |
| &charger->wpc_vout_mode_work, msecs_to_jiffies(250)); |
| } |
| } |
| #endif |
| |
| } else if (val->intval == POWER_SUPPLY_STATUS_NOT_CHARGING) { |
| mfc_mis_align(charger); |
| } else if (val->intval == POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE) { |
| #if 0 |
| if (charger->tx_id == TX_ID_DREAM_STAND) { |
| mfc_fod_set_dream_cv(charger); |
| } else |
| #endif |
| mfc_fod_set_cv(charger); |
| |
| } |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_TYPE: |
| { |
| union power_supply_propval value; |
| value.intval = charger->pdata->cable_type; |
| psy_do_property("wireless", set, POWER_SUPPLY_PROP_ONLINE, value); |
| |
| if (charger->is_otg_on) { |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value); |
| } |
| } |
| break; |
| case POWER_SUPPLY_PROP_HEALTH: |
| if (val->intval == POWER_SUPPLY_HEALTH_OVERHEAT || |
| val->intval == POWER_SUPPLY_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__); |
| /* disable CM FETs to avoid MST/WPC crash situation */ |
| queue_delayed_work(charger->wqueue, |
| &charger->wpc_cm_fet_work, msecs_to_jiffies(1000)); |
| } else { |
| pr_info("%s: set MST mode off\n", __func__); |
| charger->is_mst_on = MST_MODE_0; |
| } |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: |
| charger->pdata->siop_level = val->intval; |
| if (charger->pdata->siop_level == 100) { |
| pr_info("%s vrect headroom set ROOM 2, siop = %d\n", __func__, charger->pdata->siop_level); |
| mfc_set_vrect_adjust(charger, MFC_HEADROOM_2); |
| } else if (charger->pdata->siop_level < 100) { |
| pr_info("%s vrect headroom set ROOM 0, siop = %d\n", __func__, charger->pdata->siop_level); |
| mfc_set_vrect_adjust(charger, MFC_HEADROOM_0); |
| } |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL: |
| if (val->intval) { |
| pr_info("%s: mfc otg on\n", __func__); |
| charger->is_otg_on = true; |
| } else { |
| pr_info("%s: mfc otg off\n", __func__); |
| charger->is_otg_on = false; |
| } |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_POWERED_OTG_CONTROL: |
| { |
| unsigned int work_state; |
| struct file *fp; |
| mm_segment_t old_fs; |
| |
| 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__); |
| mutex_unlock(&charger->fw_lock); |
| return 0; |
| } |
| |
| charger->fw_cmd = val->intval; |
| if (charger->fw_cmd == SEC_WIRELESS_RX_SPU_MODE || |
| charger->fw_cmd == SEC_WIRELESS_RX_SDCARD_MODE) { |
| long nread; |
| const char *fw_path = MFC_FW_SDCARD_BIN_PATH; |
| |
| if (charger->fw_cmd == SEC_WIRELESS_RX_SPU_MODE) |
| fw_path = MFC_FW_SPU_BIN_PATH; |
| |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| |
| fp = filp_open(fw_path, O_RDONLY, S_IRUSR); |
| |
| if (IS_ERR(fp)) { |
| pr_err("%s: failed to open %s, (%d)\n", __func__, fw_path, (int)PTR_ERR(fp)); |
| set_fs(old_fs); |
| mutex_unlock(&charger->fw_lock); |
| return 0; |
| } |
| |
| charger->fw_size = fp->f_path.dentry->d_inode->i_size; |
| pr_err("%s: start, file path %s, size %ld Bytes\n", |
| __func__, fw_path, charger->fw_size); |
| |
| charger->fw_img = kmalloc(charger->fw_size, GFP_KERNEL); |
| if (charger->fw_img == NULL) { |
| pr_err("%s, kmalloc failed\n", __func__); |
| goto out; |
| } |
| |
| nread = vfs_read(fp, (char __user *)charger->fw_img, |
| charger->fw_size, &fp->f_pos); |
| pr_err("nread %ld Bytes\n", nread); |
| if (nread != charger->fw_size) { |
| pr_err("failed to read firmware file, nread %ld Bytes\n", nread); |
| goto out; |
| } |
| |
| if (charger->fw_cmd == SEC_WIRELESS_RX_SPU_MODE) { |
| if (spu_firmware_signature_verify("MFC", charger->fw_img, charger->fw_size) == (charger->fw_size - SPU_METADATA_SIZE(MFC))) { |
| pr_err("%s, spu_firmware_signature_verify success\n", __func__); |
| charger->fw_size -= SPU_METADATA_SIZE(MFC); |
| } else { |
| pr_err("%s, spu_firmware_signature_verify failed\n", __func__); |
| goto out; |
| } |
| } |
| |
| filp_close(fp, current->files); |
| set_fs(old_fs); |
| } |
| 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); |
| return 0; |
| |
| out: |
| filp_close(fp, current->files); |
| set_fs(old_fs); |
| if (charger->fw_img) |
| kfree(charger->fw_img); |
| charger->fw_img = NULL; |
| mutex_unlock(&charger->fw_lock); |
| } |
| break; |
| case POWER_SUPPLY_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); |
| wake_lock(&charger->wpc_vout_mode_lock); |
| 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) { |
| int def_delay = 0; |
| charger->vout_mode = val->intval; |
| if (is_hv_wireless_pad_type(charger->pdata->cable_type)) { |
| def_delay = 250; |
| } |
| cancel_delayed_work(&charger->wpc_vout_mode_work); |
| wake_lock(&charger->wpc_vout_mode_lock); |
| 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) { |
| if (!charger->is_full_status && !is_shutdn) { |
| if (!charger->is_afc_tx) { |
| u8 cmd = 0; |
| pr_info("%s: need to set afc tx before vout control\n", __func__); |
| |
| if (charger->pdata->cable_type == SEC_WIRELESS_PAD_WPC_HV_20) |
| mfc_send_command(charger, charger->vrect_by_txid); |
| else |
| mfc_send_command(charger, MFC_AFC_CONF_10V_TX); |
| charger->is_afc_tx = true; |
| pr_info("%s: is_afc_tx = %d vout read = %d \n", |
| __func__, charger->is_afc_tx, mfc_get_adc(charger, MFC_ADC_VOUT)); |
| |
| /* use all CM FETs for 10V wireless charging */ |
| /* used when VOUT <= 8.5V OR VOUT > 8.5V AND IOUT >= 320mA */ |
| mfc_reg_write(charger->client, MFC_RX_COMM_MOD_FET_REG, 0x00); |
| mfc_reg_read(charger->client, MFC_RX_COMM_MOD_FET_REG, &cmd); |
| pr_info("%s: CM FET setting(0x%x) \n", __func__, cmd); |
| } |
| charger->vout_mode = val->intval; |
| cancel_delayed_work(&charger->wpc_vout_mode_work); |
| wake_lock(&charger->wpc_vout_mode_lock); |
| queue_delayed_work(charger->wqueue, |
| &charger->wpc_vout_mode_work, msecs_to_jiffies(250)); |
| } else { |
| pr_info("%s: block to set high vout level(vs=%s) because full status(%d), shutdn(%d)\n", |
| __func__, rx_vout_str[charger->pdata->vout_status], charger->is_full_status, is_shutdn); |
| } |
| } else 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__); |
| //default enabled state. no need to config. |
| //mfc_reg_update(charger->client, MFC_RX_COMM_MOD_FET_REG, 0x00, 0x00); |
| } else if (val->intval == WIRELESS_SLEEP_MODE_ENABLE) { |
| if (is_sleep_mode_active(charger->tx_id)) { |
| pr_info("%s: sleep_mode enable\n", __func__); |
| |
| pr_info("%s: led dimming\n", __func__); |
| mfc_led_control(charger, MFC_LED_CONTROL_DIMMING); |
| |
| 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__); |
| |
| pr_info("%s: led on\n", __func__); |
| mfc_led_control(charger, MFC_LED_CONTROL_ON); |
| |
| 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; |
| 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; |
| #if defined(CONFIG_UPDATE_BATTERY_DATA) |
| case POWER_SUPPLY_PROP_POWER_DESIGN: |
| mfc_chg_parse_dt(charger->dev, charger->pdata); |
| break; |
| #endif |
| case POWER_SUPPLY_PROP_ENERGY_NOW: |
| charger->pdata->capacity = val->intval; |
| if (charger->wc_tx_enable) { |
| vout = mfc_get_adc(charger, MFC_ADC_TX_VOUT); |
| iout = mfc_get_adc(charger, MFC_ADC_TX_IOUT); |
| freq = mfc_get_adc(charger, MFC_ADC_OP_FRQ); |
| tx_freq = mfc_get_adc(charger, MFC_ADC_OP_FRQ); |
| pr_info("@Tx_Mode %s Firmware Ver = %x TX_VOUT = %dmV, TX_IOUT = %dmA, OP_FREQ = %dKHz, TX_OP_FREQ = %dKHZ, %s connected\n", |
| __func__, charger->pdata->otp_firmware_ver, vout, iout, freq, tx_freq, rx_device_type_str[charger->wc_rx_type]); |
| } else if (charger->pdata->cable_type != SEC_WIRELESS_PAD_NONE) { |
| vout = mfc_get_adc(charger, MFC_ADC_VOUT); |
| vrect = mfc_get_adc(charger, MFC_ADC_VRECT); |
| iout = mfc_get_adc(charger, MFC_ADC_RX_IOUT); |
| freq = mfc_get_adc(charger, MFC_ADC_OP_FRQ); |
| pr_info("%s Firmware Ver = %x, RX_VOUT = %dmV, RX_VRECT = %dmV, RX_IOUT = %dmA, OP_FREQ = %dKHz, IC Rev = 0x%x, cable=%d\n", |
| __func__, charger->pdata->otp_firmware_ver, vout, vrect, iout, freq, |
| charger->pdata->wc_ic_rev, charger->pdata->cable_type); |
| |
| if ((vout < 6500) && (charger->pdata->capacity >= 85)) { |
| mfc_reg_read(charger->client, MFC_RX_COMM_MOD_FET_REG, &tmp); |
| if (tmp != 0x00) { |
| /* use all CM FETs for 5V wireless charging */ |
| /* used when VOUT <= 8.5V OR VOUT > 8.5V AND IOUT >= 320mA */ |
| mfc_reg_write(charger->client, MFC_RX_COMM_MOD_FET_REG, 0x00); |
| mfc_reg_read(charger->client, MFC_RX_COMM_MOD_FET_REG, &tmp); |
| pr_info("%s: CM FET setting(0x%x)\n", __func__, tmp); |
| } |
| } |
| |
| for (i = 0; i < MFC_NUM_FOD_REG; i++) |
| mfc_reg_read(charger->client, MFC_WPC_FOD_0A_REG+i, &fod[i]); |
| pr_info("%s: FOD(0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X" |
| "0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X" |
| "0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X)\n", __func__, |
| fod[0], fod[1], fod[2], fod[3], fod[4], fod[5], fod[6], |
| fod[7], fod[8], fod[9], fod[10], fod[11], fod[12], fod[13], |
| fod[14], fod[15], fod[16], fod[17], fod[18], fod[19]); |
| } |
| break; |
| case POWER_SUPPLY_PROP_CAPACITY: |
| charger->pdata->capacity = val->intval; |
| if (charger->wc_rx_type == SS_GEAR) { |
| mfc_check_tx_gear_time(charger); |
| } |
| break; |
| case POWER_SUPPLY_PROP_FILTER_CFG: |
| charger->led_cover = val->intval; |
| pr_info("%s: LED_COVER(%d)\n", __func__, charger->led_cover); |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_EMPTY: |
| { |
| int vout = 0, vrect = 0; |
| u8 is_vout_on = 0; |
| |
| mfc_reg_read(charger->client, MFC_STATUS_L_REG, &is_vout_on); |
| is_vout_on = is_vout_on >> 7; |
| vout = mfc_get_adc(charger, MFC_ADC_VOUT); |
| vrect = mfc_get_adc(charger, MFC_ADC_VRECT); |
| pr_info("%s: SET MFC LDO (%s), Current VOUT STAT (%d), RX_VOUT = %dmV, RX_VRECT = %dmV\n", __func__, (val->intval == MFC_LDO_ON ? "ON" : "OFF"), is_vout_on, vout, vrect); |
| if ((val->intval == MFC_LDO_ON) && !is_vout_on) { /* LDO ON */ |
| pr_info("%s: MFC LDO ON toggle ------------ cable_work\n", __func__); |
| mfc_set_cmd_l_reg(charger, MFC_CMD_TOGGLE_LDO_MASK, MFC_CMD_TOGGLE_LDO_MASK); |
| msleep(400); |
| mfc_reg_read(charger->client, MFC_STATUS_L_REG, &is_vout_on); |
| is_vout_on = is_vout_on >> 7; |
| vout = mfc_get_adc(charger, MFC_ADC_VOUT); |
| vrect = mfc_get_adc(charger, MFC_ADC_VRECT); |
| pr_info("%s: LDO Should ON -> MFC LDO STAT(%d), RX_VOUT = %dmV, RX_VRECT = %dmV\n", __func__, is_vout_on, vout, vrect); |
| charger->wc_ldo_status = MFC_LDO_ON; |
| } else if ((val->intval == MFC_LDO_OFF) && is_vout_on) { /* LDO OFF */ |
| pr_info("%s: MFC LDO OFF toggle ------------ cable_work\n", __func__); |
| mfc_set_cmd_l_reg(charger, MFC_CMD_TOGGLE_LDO_MASK, MFC_CMD_TOGGLE_LDO_MASK); |
| msleep(400); |
| mfc_reg_read(charger->client, MFC_STATUS_L_REG, &is_vout_on); |
| is_vout_on = is_vout_on >> 7; |
| vout = mfc_get_adc(charger, MFC_ADC_VOUT); |
| vrect = mfc_get_adc(charger, MFC_ADC_VRECT); |
| pr_info("%s: LDO Should OFF -> MFC LDO STAT(%d), RX_VOUT = %dmV, RX_VRECT = %dmV\n", __func__, is_vout_on, vout, vrect); |
| charger->wc_ldo_status = MFC_LDO_OFF; |
| } |
| } |
| break; |
| case POWER_SUPPLY_PROP_SCOPE: |
| return -ENODATA; |
| case POWER_SUPPLY_PROP_MAX ... 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_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_WIRELESS_PAD_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, NULL, 0); |
| } |
| 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: |
| mfc_tx_check_rx_connection(charger, val->intval); |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS: /* it has only PASS and FAIL */ |
| #if !defined(CONFIG_SEC_FACTORY) |
| if((charger->pdata->cable_type != SEC_WIRELESS_PAD_NONE) && |
| !lpcharge && |
| (charger->adt_transfer_status != WIRELESS_AUTH_WAIT)) { |
| if(charger->adt_transfer_status != val->intval) { |
| charger->adt_transfer_status = val->intval; |
| |
| if(charger->adt_transfer_status == WIRELESS_AUTH_PASS) { |
| /* this cable type will call a proper RX power having done vout_work */ |
| //charger->pdata->cable_type = SEC_WIRELESS_PAD_WPC_HV_20_LIMIT; |
| charger->pdata->cable_type = SEC_WIRELESS_PAD_WPC_HV_20; |
| pr_info("%s %s: type = %d \n", WC_AUTH_MSG, __func__, charger->pdata->cable_type); |
| pr_info("%s %s: pass\n", WC_AUTH_MSG, __func__); |
| } else if (charger->adt_transfer_status == WIRELESS_AUTH_FAIL) { |
| /* when auth is failed, cable type is HV wireless */ |
| charger->pdata->cable_type = SEC_WIRELESS_PAD_WPC_HV; |
| pr_info("%s %s: fail\n", WC_AUTH_MSG, __func__); |
| } else |
| pr_info("%s %s: undefined pass/fail result\n", WC_AUTH_MSG, __func__); |
| wake_lock(&charger->wpc_afc_vout_lock); |
| queue_delayed_work(charger->wqueue, &charger->wpc_afc_vout_work, msecs_to_jiffies(0)); |
| } else |
| pr_info("%s %s: skip a same pass/fail result\n", WC_AUTH_MSG, __func__); |
| }else |
| pr_info("%s %s: auth service sent wrong cmd \n", WC_AUTH_MSG, __func__); |
| #endif |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_DATA: /* data from auth service will be sent */ |
| if(charger->pdata->cable_type != SEC_WIRELESS_PAD_NONE && !lpcharge) { |
| u8 *p_data; |
| int i; |
| 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_WIRELESS_PAD_NONE) |
| adt_readSize = val->intval; |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_TYPE: |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_VOUT: |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_IOUT: |
| mfc_set_tx_iout(charger, val->intval); |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_TIMER_ON: |
| pr_info("%s %s : TX receiver detecting timer enable(%d)\n", WC_AUTH_MSG, __func__, val->intval); |
| if (charger->wc_tx_enable) { |
| if (val->intval) { |
| pr_info("%s %s : enable TX OFF timer (90sec)", WC_AUTH_MSG, __func__); |
| mfc_reg_update(charger->client, MFC_INT_A_ENABLE_H_REG, (0x1<<5), (0x1<<5)); |
| } else { |
| pr_info("%s %s : disable TX OFF timer (90sec)", WC_AUTH_MSG, __func__); |
| mfc_reg_update(charger->client, MFC_INT_A_ENABLE_H_REG, 0x0, (0x1<<5)); |
| } |
| } else |
| pr_info("%s %s : Don't need to set TX 90sec timer, on TX OFF state\n", WC_AUTH_MSG, __func__); |
| break; |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_MIN_DUTY: |
| cancel_delayed_work(&charger->wpc_tx_op_freq_work); |
| if (val->intval == 50) { |
| mfc_set_tx_op_freq(charger, 1100); |
| } else { |
| mfc_set_tx_op_freq(charger, 1480); |
| } |
| 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; |
| |
| /* call in is after wireless connection */ |
| if(charger->pdata->cable_type == SEC_WIRELESS_PAD_WPC_PACK || |
| charger->pdata->cable_type == SEC_WIRELESS_PAD_WPC_PACK_HV || |
| charger->pdata->cable_type == SEC_WIRELESS_PAD_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); |
| } |
| } else if (val->intval == BATT_EXT_EVENT_NONE) { |
| charger->device_event &= ~BATT_EXT_EVENT_CALL; |
| } |
| 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; |
| #if defined(CONFIG_WIRELESS_IC_PARAM) |
| case POWER_SUPPLY_EXT_PROP_WIRELESS_PARAM_INFO: |
| mfc_set_wireless_ic_param(charger, charger->chip_id_now, charger->pdata->otp_firmware_ver); |
| break; |
| #endif |
| default: |
| return -ENODATA; |
| } |
| break; |
| default: |
| return -ENODATA; |
| } |
| |
| return 0; |
| } |
| |
| #define FREQ_OFFSET 384000 /* 64*6000 */ |
| static void mfc_wpc_opfq_work(struct work_struct *work) |
| { |
| struct mfc_charger_data *charger = |
| container_of(work, struct mfc_charger_data, wpc_opfq_work.work); |
| #if 0 |
| u16 op_fq; |
| u8 pad_mode; |
| union power_supply_propval value; |
| |
| mfc_reg_read(charger->client, MFC_SYS_OP_MODE_REG, &pad_mode); |
| if ((pad_mode == PAD_MODE_WPC_BASIC) ||\ |
| (pad_mode == PAD_MODE_WPC_ADV)) { |
| op_fq = mfc_get_adc(charger, MFC_ADC_OP_FRQ); |
| pr_info("%s: Operating FQ %dkHz(0x%x)\n", __func__, op_fq, op_fq); |
| if (op_fq > 230) { /* wpc threshold 230kHz */ |
| pr_info("%s: Reset M0\n", __func__); |
| mfc_reg_write(charger->client, 0x3040, 0x80); /*restart M0 */ |
| |
| charger->pdata->opfq_cnt++; |
| if (charger->pdata->opfq_cnt <= CMD_CNT) { |
| queue_delayed_work(charger->wqueue, &charger->wpc_opfq_work, msecs_to_jiffies(10000)); |
| return; |
| } |
| } |
| } else if ((pad_mode == PAD_MODE_PMA_SR1) ||\ |
| (pad_mode == PAD_MODE_PMA_SR1E)) { |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_PMA; |
| psy_do_property("wireless", set, POWER_SUPPLY_PROP_ONLINE, value); |
| } |
| charger->pdata->opfq_cnt = 0; |
| #endif |
| wake_unlock(&charger->wpc_opfq_lock); |
| |
| } |
| |
| static u32 mfc_get_wireless20_vout_by_txid(struct mfc_charger_data *charger, u8 txid) |
| { |
| u32 vout = 0, i = 0; |
| |
| if(txid < TX_ID_AUTH_PAD || txid > TX_ID_AUTH_PAD_END) { |
| pr_info("%s : wrong tx id(0x%x) for wireless 2.0\n", __func__, txid); |
| i = 0; /* defalut value is WIRELESS_VOUT_10V */ |
| } else if(txid >= TX_ID_AUTH_PAD + charger->pdata->len_wc20_list) { |
| pr_info("%s : undefined tx id(0x%x) of the device\n", __func__, txid); |
| i = charger->pdata->len_wc20_list-1; |
| } else |
| i = txid - TX_ID_AUTH_PAD; |
| |
| vout = charger->pdata->wireless20_vout_list[i]; |
| pr_info("%s vout = %d\n", __func__, vout); |
| return vout; |
| } |
| |
| static u32 mfc_get_wireless20_vrect_by_txid(struct mfc_charger_data *charger, u8 txid) |
| { |
| u32 vrect = 0, i = 0; |
| |
| if(txid < TX_ID_AUTH_PAD || txid > TX_ID_AUTH_PAD_END) { |
| pr_info("%s : wrong tx id(0x%x) for wireless 2.0\n", __func__, txid); |
| i = 0; /* defalut value is MFC_AFC_CONF_10V_TX */ |
| } else if(txid >= TX_ID_AUTH_PAD + charger->pdata->len_wc20_list) { |
| pr_info("%s : undefined tx id(0x%x) of the device\n", __func__, txid); |
| i = charger->pdata->len_wc20_list-1; |
| } else |
| i = txid - TX_ID_AUTH_PAD; |
| |
| vrect = charger->pdata->wireless20_vrect_list[i]; |
| pr_info("%s vrect = %d\n", __func__, vrect); |
| return vrect; |
| } |
| |
| static u32 mfc_get_wireless20_max_power_by_txid(struct mfc_charger_data *charger, u8 txid) |
| { |
| u32 max_power = 0, i = 0; |
| |
| if(txid < TX_ID_AUTH_PAD || txid > TX_ID_AUTH_PAD_END) { |
| pr_info("%s : wrong tx id(0x%x) for wireless 2.0\n", __func__, txid); |
| i = 0; /* defalut value is SEC_WIRELESS_RX_POWER_12W */ |
| } else if(txid >= TX_ID_AUTH_PAD + charger->pdata->len_wc20_list) { |
| pr_info("%s : undefined tx id(0x%x) of the device\n", __func__, txid); |
| i = charger->pdata->len_wc20_list-1; |
| } else |
| i = txid - TX_ID_AUTH_PAD; |
| |
| max_power = charger->pdata->wireless20_max_power_list[i]; |
| pr_info("%s max rx power = %d\n", __func__, max_power); |
| return max_power; |
| } |
| |
| static void mfc_wpc_det_work(struct work_struct *work) |
| { |
| struct mfc_charger_data *charger = |
| container_of(work, struct mfc_charger_data, wpc_det_work.work); |
| int wc_w_state; |
| union power_supply_propval value; |
| u8 pad_mode; |
| u8 vrect; |
| |
| mfc_get_chip_id(charger); |
| 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 MST RETURN!\n", __func__); |
| return; |
| } |
| |
| if (charger->is_mst_on == MST_MODE_2) { |
| pr_info("%s: check wpc-state(%d - %d)\n", __func__, |
| charger->wc_w_state, gpio_get_value(charger->pdata->wpc_det)); |
| |
| if (charger->wc_w_state == 0) { |
| pr_info("%s: skip wpc_det_work for MST operation\n", __func__); |
| return; |
| } |
| } |
| |
| wake_lock(&charger->wpc_wake_lock); |
| pr_info("%s\n", __func__); |
| wc_w_state = gpio_get_value(charger->pdata->wpc_det); |
| |
| if ((charger->wc_w_state == 0) && (wc_w_state == 1)) { |
| charger->initial_wc_check = true; |
| |
| charger->pdata->otp_firmware_ver = mfc_get_firmware_version(charger, MFC_RX_FIRMWARE); |
| charger->pdata->wc_ic_rev = mfc_get_ic_revision(charger, MFC_IC_REVISION); |
| |
| if (charger->is_otg_on) |
| charger->pdata->vout_status = MFC_VOUT_5V; |
| else |
| charger->pdata->vout_status = MFC_VOUT_5_5V; |
| mfc_set_vout(charger, charger->pdata->vout_status); |
| |
| charger->wc_tx_enable = false; |
| charger->gear_start_time = 0; |
| |
| /* enable Mode Change INT */ |
| mfc_reg_update(charger->client, MFC_INT_A_ENABLE_L_REG, |
| MFC_STAT_L_OP_MODE_MASK, MFC_STAT_L_OP_MODE_MASK); |
| |
| mfc_reg_update(charger->client, MFC_INT_A_ENABLE_L_REG, |
| MFC_STAT_H_ADT_RECEIVED_MASK, MFC_STAT_H_ADT_RECEIVED_MASK); |
| |
| /* 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__); |
| |
| /* default value of CMA and CMB all enable |
| use all CM FETs for wireless charging */ |
| #if 0 //S2MIW04 not support |
| mfc_reg_write(charger->client, MFC_RX_COMM_MOD_AFC_FET_REG, 0x00); |
| #endif |
| mfc_reg_write(charger->client, MFC_RX_COMM_MOD_FET_REG, 0x30); |
| |
| /* read pad mode */ |
| mfc_reg_read(charger->client, MFC_SYS_OP_MODE_REG, &pad_mode); |
| pad_mode = pad_mode >> 5; |
| pr_info("%s: Pad type (0x%x)\n", __func__, pad_mode); |
| if ((pad_mode == PAD_MODE_PMA_SR1) || |
| (pad_mode == PAD_MODE_PMA_SR1E)) { |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_PMA; |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_ONLINE, value); |
| } else { /* WPC */ |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_WPC; |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_ONLINE, value); |
| wake_lock(&charger->wpc_opfq_lock); |
| queue_delayed_work(charger->wqueue, &charger->wpc_opfq_work, msecs_to_jiffies(10000)); |
| } |
| |
| /* set fod value */ |
| if (charger->pdata->fod_data_check) |
| mfc_fod_set(charger); |
| #if !defined(CONFIG_WIRELESS_NO_HV) |
| if (!sleep_mode) { |
| 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); |
| if (vrect_level >= 8500 && vout_level >= 8500) { |
| /* re-set vout level */ |
| charger->pad_vout = PAD_VOUT_10V; |
| mfc_set_vout(charger, MFC_VOUT_10V); |
| |
| /* change cable type */ |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_WPC_HV; |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_ONLINE, value); |
| wake_lock(&charger->wpc_tx_id_lock); |
| queue_delayed_work(charger->wqueue, &charger->wpc_tx_id_work, msecs_to_jiffies(500)); |
| } else { |
| /* send request afc_tx */ |
| msleep(200); |
| mfc_send_command(charger, MFC_REQUEST_AFC_TX); |
| } |
| } else { |
| wake_lock(&charger->wpc_tx_id_lock); |
| queue_delayed_work(charger->wqueue, &charger->wpc_tx_id_work, msecs_to_jiffies(500)); |
| } |
| #endif |
| /* set rpp scaling factor for LED cover */ |
| mfc_rpp_set(charger); |
| #if 0 |
| /* set request TX_ID */ |
| mfc_send_command(charger, MFC_REQUEST_TX_ID); |
| #endif |
| charger->pdata->is_charging = 1; |
| } else if ((charger->wc_w_state == 1) && (wc_w_state == 0)) { |
| /* Send last tx_id to battery to cound tx_id */ |
| value.intval = charger->tx_id; |
| psy_do_property("wireless", set, POWER_SUPPLY_PROP_AUTHENTIC, value); |
| charger->pdata->cable_type = SEC_WIRELESS_PAD_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->pdata->capacity = 101; |
| charger->is_afc_tx = false; |
| charger->tx_id = TX_ID_UNKNOWN; |
| charger->i2c_error_count = 0; |
| charger->non_sleep_mode_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; |
| |
| value.intval = SEC_WIRELESS_PAD_NONE; |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_ONLINE, value); |
| pr_info("%s: wpc deactivated, set V_INT as PD\n", __func__); |
| |
| if (delayed_work_pending(&charger->wpc_opfq_work)) { |
| wake_unlock(&charger->wpc_opfq_lock); |
| cancel_delayed_work(&charger->wpc_opfq_work); |
| } |
| if (delayed_work_pending(&charger->wpc_afc_vout_work)) { |
| wake_unlock(&charger->wpc_afc_vout_lock); |
| cancel_delayed_work(&charger->wpc_afc_vout_work); |
| } |
| if (delayed_work_pending(&charger->wpc_vout_mode_work)) { |
| wake_unlock(&charger->wpc_vout_mode_lock); |
| cancel_delayed_work(&charger->wpc_vout_mode_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_i2c_error_work); |
| wake_unlock(&charger->wpc_rx_wake_lock); |
| wake_unlock(&charger->wpc_tx_wake_lock); |
| wake_unlock(&charger->wpc_tx_id_lock); |
| } |
| |
| pr_info("%s: w(%d to %d)\n", __func__, |
| charger->wc_w_state, wc_w_state); |
| |
| charger->wc_w_state = wc_w_state; |
| wake_unlock(&charger->wpc_wake_lock); |
| } |
| |
| /* 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) { |
| wake_unlock(&charger->wpc_tx_wake_lock); |
| return; |
| } |
| |
| ret = mfc_reg_read(charger->client, MFC_STATUS_L_REG, &status_l); |
| ret = mfc_reg_read(charger->client, MFC_STATUS_H_REG, &status_h); |
| |
| pr_info("@Tx_Mode %s: status(0x%x) \n", __func__, status_h << 8 | status_l); |
| |
| pr_info("@Tx_Mode %s DATA RECEIVED\n", __func__); |
| mfc_tx_handle_rx_packet(charger); |
| |
| wake_unlock(&charger->wpc_tx_wake_lock); |
| } |
| |
| /* 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; |
| |
| union power_supply_propval value; |
| |
| if (!charger->wc_w_state) { |
| pr_info("%s: charger->wc_w_state is 0. exit wpc_isr_work.\n", __func__); |
| wake_unlock(&charger->wpc_rx_wake_lock); |
| return; |
| } |
| |
| pr_info("%s: cable_type (0x%x)\n", __func__, charger->pdata->cable_type); |
| pr_info("%s\n", __func__); |
| |
| mfc_reg_read(charger->client, MFC_WPC_TRX_DATA_COM_REG, &cmd_data); |
| mfc_reg_read(charger->client, MFC_WPC_TRX_DATA_VALUE0_REG, &val_data); |
| charger->pdata->trx_data_cmd = cmd_data; |
| charger->pdata->trx_data_val = val_data; |
| |
| pr_info("%s: WPC Interrupt Occured, CMD : 0x%x, DATA : 0x%x\n", |
| __func__, cmd_data, val_data); |
| |
| /* 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); |
| if (!gpio_get_value(charger->pdata->wpc_det)) { |
| pr_err("%s Wireless charging is paused during set high voltage.\n", __func__); |
| wake_unlock(&charger->wpc_rx_wake_lock); |
| return; |
| } else if (sleep_mode) { |
| if(charger->non_sleep_mode_cnt >= 2) |
| pr_info("%s: will have 10V at sleep_mode, cnt = %d \n", __func__, charger->non_sleep_mode_cnt); |
| else { |
| charger->non_sleep_mode_cnt++; |
| pr_info("%s: does not needs to check afc mode at sleep_mode, cnt = %d \n", __func__, charger->non_sleep_mode_cnt); |
| charger->pad_vout = PAD_VOUT_5V; |
| break; |
| } |
| } |
| #if !defined(CONFIG_WIRELESS_NO_HV) |
| if (is_hv_wireless_pad_type(charger->pdata->cable_type) || |
| charger->pdata->cable_type == SEC_WIRELESS_PAD_PREPARE_HV) { |
| pr_err("%s: Is is already HV wireless cable. No need to set again\n", __func__); |
| wake_unlock(&charger->wpc_rx_wake_lock); |
| return; |
| } |
| |
| /* send AFC_SET */ |
| mfc_send_command(charger, MFC_AFC_CONF_10V); |
| msleep(500); |
| |
| /* change cable type */ |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_PREPARE_HV; |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_PROP_ONLINE, value); |
| |
| charger->pad_vout = PAD_VOUT_10V; |
| #endif |
| 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; |
| } |
| |
| wake_lock(&charger->wpc_tx_id_lock); |
| queue_delayed_work(charger->wqueue, &charger->wpc_tx_id_work, msecs_to_jiffies(500)); |
| } else if (cmd_data == WPC_TX_COM_TX_ID) { |
| if(!charger->tx_id_done) { |
| switch (val_data) { |
| case TX_ID_UNKNOWN: |
| break; |
| case TX_ID_VEHICLE_PAD: |
| if (charger->pad_vout == PAD_VOUT_10V) { |
| if (charger->pdata->cable_type == SEC_WIRELESS_PAD_PREPARE_HV) { |
| charger->pdata->cable_type = SEC_WIRELESS_PAD_VEHICLE_HV; |
| value.intval = SEC_WIRELESS_PAD_PREPARE_HV; |
| } else { |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_VEHICLE_HV; |
| } |
| } else { |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_VEHICLE; |
| } |
| pr_info("%s: VEHICLE Wireless Charge PAD %s\n", __func__, |
| charger->pad_vout == PAD_VOUT_10V ? "HV" : ""); |
| |
| break; |
| case TX_ID_STAND_TYPE_START: |
| if (charger->pad_vout == PAD_VOUT_10V) { |
| if (charger->pdata->cable_type == SEC_WIRELESS_PAD_PREPARE_HV) { |
| charger->pdata->cable_type = SEC_WIRELESS_PAD_WPC_STAND_HV; |
| value.intval = SEC_WIRELESS_PAD_PREPARE_HV; |
| } else { |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_WPC_STAND_HV; |
| } |
| } else { |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_WPC_STAND; |
| //mfc_fod_set_hero_5v(charger); |
| } |
| pr_info("%s: STAND Wireless Charge PAD %s\n", __func__, |
| charger->pad_vout == PAD_VOUT_10V ? "HV" : ""); |
| pr_info("%s: cable_type(%d)\n", __func__, charger->pdata->cable_type); |
| break; |
| case TX_ID_MULTI_PORT_START ... TX_ID_MULTI_PORT_END: |
| value.intval = charger->pdata->cable_type; |
| pr_info("%s: MULTI PORT PAD : 0x%x\n", __func__, val_data); |
| break; |
| case TX_ID_BATT_PACK ... TX_ID_BATT_PACK_END: |
| if (charger->pad_vout == PAD_VOUT_10V) { |
| if (charger->pdata->cable_type == SEC_WIRELESS_PAD_PREPARE_HV) { |
| charger->pdata->cable_type = SEC_WIRELESS_PAD_WPC_PACK_HV; |
| value.intval = SEC_WIRELESS_PAD_PREPARE_HV; |
| pr_info("%s: WIRELESS HV BATTERY PACK (PREP) \n", __func__); |
| } else { |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_WPC_PACK_HV; |
| pr_info("%s: WIRELESS HV BATTERY PACK\n", __func__); |
| } |
| } else { |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_WPC_PACK; |
| pr_info("%s: WIRELESS BATTERY PACK\n", __func__); |
| } |
| 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); |
| } |
| break; |
| case TX_ID_UNO_TX: |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_TX; |
| pr_info("@Tx_Mode %s: TX by UNO\n", __func__); |
| 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); |
| } |
| break; |
| case TX_ID_AUTH_PAD ... TX_ID_AUTH_PAD_END: |
| 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) |
| /* do not process during lpm and wired charging */ |
| if(!lpcharge) { |
| if(charger->adt_transfer_status == WIRELESS_AUTH_WAIT) { |
| if (delayed_work_pending(&charger->wpc_afc_vout_work)) { |
| wake_unlock(&charger->wpc_afc_vout_lock); |
| cancel_delayed_work(&charger->wpc_afc_vout_work); |
| } |
| charger->adt_transfer_status = WIRELESS_AUTH_START; |
| /* notify auth service to send TX PAD a request key */ |
| value.intval = WIRELESS_AUTH_START; |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS, value); |
| |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_WPC_PREPARE_HV_20; |
| pr_info("%s %s : AUTH PAD for WIRELESS 2.0 : 0x%x\n", WC_AUTH_MSG, __func__, val_data); |
| } 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 { |
| value.intval = charger->pdata->cable_type; |
| pr_info("%s %s : It is AUTH PAD for WIRELESS 2.0 but LPM, %d\n", WC_AUTH_MSG, __func__, charger->pdata->cable_type); |
| } |
| #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)) { |
| wake_unlock(&charger->wpc_afc_vout_lock); |
| cancel_delayed_work(&charger->wpc_afc_vout_work); |
| } |
| charger->adt_transfer_status = WIRELESS_AUTH_PASS; |
| charger->pdata->cable_type = value.intval = SEC_WIRELESS_PAD_WPC_HV_20; |
| wake_lock(&charger->wpc_afc_vout_lock); |
| queue_delayed_work(charger->wqueue, &charger->wpc_afc_vout_work, msecs_to_jiffies(0)); |
| } |
| pr_info("%s %s : AUTH PAD for WIRELESS 2.0 but FACTORY MODE\n", WC_AUTH_MSG, __func__); |
| #endif |
| break; |
| case TX_ID_DREAM_STAND: |
| pr_info("%s: Dream pad FOD, SOC(%d)\n", __func__, charger->pdata->capacity); |
| if (charger->pdata->fod_dream_data) { |
| //if (charger->pdata->capacity > 85) |
| // mfc_fod_set_dream_cv(charger); |
| //else |
| // mfc_fod_set_dream(charger); |
| } |
| case TX_ID_DREAM_DOWN: |
| default: |
| value.intval = charger->pdata->cable_type; |
| pr_info("%s: UNDEFINED PAD : 0x%x\n", __func__, val_data); |
| break; |
| } |
| |
| if (value.intval != SEC_WIRELESS_PAD_PREPARE_HV && |
| value.intval != SEC_WIRELESS_PAD_WPC_HV_20) { |
| pr_info("%s: change cable type\n", __func__); |
| psy_do_property("wireless", set, POWER_SUPPLY_PROP_ONLINE, value); |
| } |
| |
| /* send rx power informaion of this pad such as SEC_WIRELESS_RX_POWER_12W, SEC_WIRELESS_RX_POWER_17_5W */ |
| value.intval = charger->max_power_by_txid; |
| psy_do_property("wireless", set, POWER_SUPPLY_PROP_WIRELESS_RX_POWER, value); |
| |
| /* send max vout informaion of this pad such as WIRELESS_VOUT_10V, WIRELESS_VOUT_12_5V */ |
| value.intval = charger->vout_by_txid; |
| psy_do_property("wireless", set, POWER_SUPPLY_PROP_WIRELESS_MAX_VOUT, value); |
| |
| charger->tx_id_done = true; |
| charger->tx_id = val_data; |
| 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: |
| 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) { |
| if(charger->current_rx_power != val_data) { |
| switch (val_data) { |
| case TX_RX_POWER_3W: |
| pr_info("%s %s : RX Power is 3W\n", WC_AUTH_MSG, __func__); |
| charger->current_rx_power = TX_RX_POWER_3W; |
| break; |
| case TX_RX_POWER_5W: |
| pr_info("%s %s : RX Power is 5W\n", WC_AUTH_MSG, __func__); |
| charger->current_rx_power = TX_RX_POWER_5W; |
| break; |
| case TX_RX_POWER_6_5W: |
| pr_info("%s %s : RX Power is 6.5W\n", WC_AUTH_MSG, __func__); |
| charger->current_rx_power = TX_RX_POWER_6_5W; |
| break; |
| case TX_RX_POWER_7_5W: |
| pr_info("%s %s : RX Power is 7.5W\n", WC_AUTH_MSG, __func__); |
| mfc_reset_rx_power(charger, TX_RX_POWER_7_5W); |
| charger->current_rx_power = TX_RX_POWER_7_5W; |
| break; |
| case TX_RX_POWER_10W: |
| pr_info("%s %s : RX Power is 10W\n", WC_AUTH_MSG, __func__); |
| charger->current_rx_power = TX_RX_POWER_10W; |
| break; |
| case TX_RX_POWER_12W: |
| pr_info("%s %s : RX Power is 12W\n", WC_AUTH_MSG, __func__); |
| mfc_reset_rx_power(charger, TX_RX_POWER_12W); |
| charger->current_rx_power = TX_RX_POWER_12W; |
| break; |
| case TX_RX_POWER_15W: |
| pr_info("%s %s : RX Power is 15W\n", WC_AUTH_MSG, __func__); |
| mfc_reset_rx_power(charger, TX_RX_POWER_15W); |
| charger->current_rx_power = TX_RX_POWER_15W; |
| break; |
| case TX_RX_POWER_17_5W: |
| pr_info("%s %s : RX Power is 17.5W\n", WC_AUTH_MSG, __func__); |
| mfc_reset_rx_power(charger, TX_RX_POWER_17_5W); |
| charger->current_rx_power = TX_RX_POWER_17_5W; |
| break; |
| case TX_RX_POWER_20W: |
| pr_info("%s %s : RX Power is 20W\n", WC_AUTH_MSG, __func__); |
| mfc_reset_rx_power(charger, TX_RX_POWER_20W); |
| charger->current_rx_power = TX_RX_POWER_20W; |
| break; |
| default: |
| pr_info("%s %s : Undefined RX Power(0x%x)\n", WC_AUTH_MSG, __func__, val_data); |
| break; |
| } |
| }else |
| pr_info("%s: skip same RX Power\n", __func__); |
| } else if (cmd_data == WPC_TX_COM_WPS) { |
| switch (val_data) { |
| case WPS_AICL_RESET: |
| value.intval = 1; |
| pr_info("@Tx_mode %s Rx devic AICL Reset\n", __func__); |
| psy_do_property("wireless", set, POWER_SUPPLY_PROP_CURRENT_MAX, value); |
| break; |
| default: |
| pr_info("%s %s : Undefined RX Power(0x%x)\n", WC_AUTH_MSG, __func__, val_data); |
| break; |
| } |
| } |
| wake_unlock(&charger->wpc_rx_wake_lock); |
| } |
| |
| 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); |
| |
| pr_info("%s\n", __func__); |
| |
| mfc_send_command(charger, MFC_REQUEST_TX_ID); |
| charger->tx_id_cnt++; |
| |
| if ((charger->tx_id_cnt <= 10) && !charger->tx_id) { |
| pr_info("%s: request TX ID (%d)\n", __func__, charger->tx_id_cnt); |
| queue_delayed_work(charger->wqueue, &charger->wpc_tx_id_work, msecs_to_jiffies(1500)); |
| return; |
| } else { |
| if (charger->tx_id) { |
| pr_info("%s: TX ID (0x%x)\n", __func__, charger->tx_id); |
| } else { |
| pr_info("%s: TX ID not Received, cable_type(%d)\n", |
| __func__, charger->pdata->cable_type); |
| if (is_hv_wireless_pad_type(charger->pdata->cable_type)) { |
| /* SET OV 13V */ |
| mfc_reg_write(charger->client, MFC_RX_OV_CLAMP_REG, 0x1); |
| } else { |
| /* SET OV 11V */ |
| mfc_reg_write(charger->client, MFC_RX_OV_CLAMP_REG, 0x0); |
| } |
| } |
| charger->tx_id_cnt = 0; |
| } |
| wake_unlock(&charger->wpc_tx_id_lock); |
| } |
| |
| static irqreturn_t mfc_wpc_det_irq_thread(int irq, void *irq_data) |
| { |
| struct mfc_charger_data *charger = irq_data; |
| |
| pr_info("%s !\n", __func__); |
| |
| if (charger->is_probed) { |
| queue_delayed_work(charger->wqueue, &charger->wpc_det_work, 0); |
| } |
| else |
| pr_info("%s: prevent work thread before device is probed.\n", __func__); |
| |
| 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; |
| mfc_reg_write(charger->client, MFC_MST_MODE_SEL_REG, 0x02); /* set MST mode2 */ |
| pr_info("%s 2AC Missing ! : MST on REV : %d\n", __func__, charger->pdata->wc_ic_rev); |
| |
| /* clear intterupt */ |
| mfc_reg_write(charger->client, MFC_INT_A_CLEAR_L_REG, irq_src_l); // clear int |
| mfc_reg_write(charger->client, MFC_INT_A_CLEAR_H_REG, irq_src_h); // clear int |
| mfc_set_cmd_l_reg(charger, 0x20, MFC_CMD_CLEAR_INT_MASK); // command |
| } else if (charger->wc_tx_enable) { |
| mfc_reg_read(charger->client,MFC_STATUS_H_REG, &data); |
| data &= 0x4; /* AC MISSING DETECT */ |
| msleep(100); |
| pr_info("@Tx_Mode %s: 0x21 Register AC Missing(%d)\n", __func__, data); |
| if (data) { |
| 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 void mfc_check_sys_op_mode(struct mfc_charger_data *charger, u8 mode) |
| { |
| mode &= 0xF; |
| |
| if (mode == MFC_TX_MODE_TX_PWR_HOLD) { |
| if(charger->wc_rx_type == SS_GEAR) { |
| /* start 3min alarm timer */ |
| pr_info("@Tx_Mode %s: Start PHM 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 3min timer\n", __func__); |
| } |
| } else { |
| 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); |
| wake_unlock(&charger->wpc_tx_phm_lock); |
| alarm_cancel(&charger->phm_alarm); |
| } |
| } |
| } |
| |
| static int mfc_clear_irq(struct mfc_charger_data *charger, u8 *src_l, u8 *src_h, u8 *status_l, u8 *status_h) |
| { |
| int wc_w_state_irq = 0; |
| u8 new_src_l = 0, new_src_h = 0; |
| u8 new_status_l = 0, new_status_h = 0; |
| int ret = 0, rerun_ret = 1; |
| |
| pr_info("%s start\n", __func__); |
| |
| //ret = mfc_reg_write(charger->client, MFC_INT_A_CLEAR_L_REG, *src_l); // clear int |
| //ret = mfc_reg_write(charger->client, MFC_INT_A_CLEAR_H_REG, *src_h); // clear int |
| ret = mfc_reg_write(charger->client, MFC_INT_A_CLEAR_L_REG, 0xff); // clear int |
| ret = mfc_reg_write(charger->client, MFC_INT_A_CLEAR_H_REG, 0xff); // clear int |
| mfc_set_cmd_l_reg(charger, 0x20, MFC_CMD_CLEAR_INT_MASK); // command |
| |
| //msleep(2); |
| wc_w_state_irq = gpio_get_value(charger->pdata->wpc_int); |
| pr_info("%s wc_w_state_irq = %d\n", __func__, wc_w_state_irq); |
| |
| if (!wc_w_state_irq && (ret >= 0)) { |
| pr_info("%s wc_w_state_irq is not cleared\n", __func__); |
| ret = mfc_reg_read(charger->client, MFC_INT_A_L_REG, &new_src_l); |
| ret = mfc_reg_read(charger->client, MFC_INT_A_H_REG, &new_src_h); |
| |
| ret = mfc_reg_read(charger->client, MFC_STATUS_L_REG, &new_status_l); |
| ret = mfc_reg_read(charger->client, MFC_STATUS_H_REG, &new_status_h); |
| |
| pr_info("%s src_l[0x%x -> 0x%x], src_h[0x%x -> 0x%x], status_l[0x%x -> 0x%x], status_h[0x%x -> 0x%x]\n", |
| __func__, *src_l, new_src_l, *src_h, new_src_h, *status_l, new_status_l, *status_h, new_status_h); |
| |
| rerun_ret = 0; // re-run isr |
| |
| /* do not try irq again with i2c fail status, need to end up the irq */ |
| if ((*src_l != new_src_l || |
| *src_h != new_src_h || |
| *status_l != new_status_l || |
| *status_h != new_status_h) && |
| (ret >= 0)) { |
| *src_l = new_src_l; |
| *src_h = new_src_h; |
| *status_l = new_status_l; |
| *status_h = new_status_h; |
| pr_info("%s re-run isr\n", __func__); |
| } else if (ret < 0) { |
| rerun_ret = 1; // do not re-run isr |
| pr_info("%s i2c fail, do not re-run isr\n", __func__); |
| } else { |
| pr_info("%s re-run isr with same src, status\n", __func__); |
| } |
| } |
| |
| pr_info("%s end (%d)\n", __func__, rerun_ret); |
| return rerun_ret; |
| } |
| |
| static irqreturn_t mfc_wpc_irq_thread(int irq, void *irq_data) |
| { |
| struct mfc_charger_data *charger = irq_data; |
| int wc_w_state_irq = 0; |
| int ret = 0; |
| u8 irq_src_l = 0, irq_src_h = 0; |
| u8 status_l = 0, status_h = 0; |
| u8 reg_data = 0; |
| bool end_irq = false; |
| union power_supply_propval value; |
| |
| pr_info("%s start!\n", __func__); |
| |
| wc_w_state_irq = gpio_get_value(charger->pdata->wpc_int); |
| pr_info("%s wc_w_state_irq = %d\n", __func__, wc_w_state_irq); |
| |
| wake_lock(&charger->wpc_wake_lock); |
| |
| ret = mfc_reg_read(charger->client, MFC_INT_A_L_REG, &irq_src_l); |
| ret = mfc_reg_read(charger->client, MFC_INT_A_H_REG, &irq_src_h); |
| |
| ret = mfc_reg_read(charger->client, MFC_STATUS_L_REG, &status_l); |
| ret = mfc_reg_read(charger->client, MFC_STATUS_H_REG, &status_h); |
| |
| pr_info("%s: interrupt source(0x%x), status(0x%x) \n", __func__, irq_src_h << 8 | irq_src_l, status_h << 8 | status_l); |
| |
| if (ret < 0) { |
| pr_err("%s: Failed to read interrupt source: %d\n", |
| __func__, ret); |
| wake_unlock(&charger->wpc_wake_lock); |
| return IRQ_HANDLED; |
| } |
| |
| if (irq_src_h & MFC_INTA_H_AC_MISSING_DET_MASK) { |
| pr_info("%s 1AC Missing ! : MFC is on\n", __func__); |
| mfc_mst_routine(charger, irq_src_l, irq_src_h); |
| } |
| |
| if (irq_src_l & MFC_INTA_L_OP_MODE_MASK) { |
| pr_info("%s MODE CHANGE IRQ !\n", __func__); |
| if(charger->wc_tx_enable) { |
| ret = mfc_reg_read(charger->client, MFC_SYS_OP_MODE_REG, ®_data); |
| mfc_check_sys_op_mode(charger, reg_data); |
| } |
| } |
| |
| if ((irq_src_l & MFC_INTA_L_OVER_VOL_MASK) || |
| (irq_src_l & MFC_INTA_L_OVER_CURR_MASK)) { |
| pr_info("%s ABNORMAL STAT IRQ !\n", __func__); |
| } |
| |
| if (irq_src_l & MFC_INTA_L_OVER_TEMP_MASK) { |
| pr_info("%s OVER TEMP IRQ !\n", __func__); |
| if (charger->wc_tx_enable) { |
| value.intval = BATT_TX_EVENT_WIRELESS_RX_UNSAFE_TEMP; |
| end_irq = true; |
| goto INT_END; |
| } |
| } |
| |
| if (irq_src_l & MFC_INTA_L_STAT_VRECT_MASK) { |
| pr_info("%s: Vrect IRQ !\n", __func__); |
| if (charger->is_mst_on == MST_MODE_2 || charger->wc_tx_enable) { |
| pr_info("%s: Noise made false Vrect IRQ !\n", __func__); |
| if (charger->wc_tx_enable) { |
| value.intval = BATT_TX_EVENT_WIRELESS_TX_ETC; |
| end_irq = true; |
| goto INT_END; |
| } |
| } |
| } |
| |
| if (irq_src_l & MFC_INTA_L_TXCONFLICT_MASK) { |
| pr_info("@Tx_Mode %s TXCONFLICT IRQ !\n", __func__); |
| if (charger->wc_tx_enable && (status_l & MFC_STAT_L_TXCONFLICT_MASK)) { |
| value.intval = BATT_TX_EVENT_WIRELESS_TX_ETC; |
| end_irq = true; |
| goto INT_END; |
| } |
| } |
| |
| if (irq_src_h & MFC_INTA_H_TRX_DATA_RECEIVED_MASK) { |
| pr_info("%s TX RECEIVED IRQ !\n", __func__); |
| if (charger->wc_tx_enable && !delayed_work_pending(&charger->wpc_tx_isr_work)) { |
| wake_lock(&charger->wpc_tx_wake_lock); |
| queue_delayed_work(charger->wqueue, &charger->wpc_tx_isr_work, msecs_to_jiffies(1000)); |
| } |
| else if (charger->pdata->cable_type == SEC_WIRELESS_PAD_WPC_STAND || |
| charger->pdata->cable_type == SEC_WIRELESS_PAD_WPC_STAND_HV) |
| pr_info("%s Don't run ISR_WORK for NO ACK !\n", __func__); |
| else if (!delayed_work_pending(&charger->wpc_isr_work)) { |
| wake_lock(&charger->wpc_rx_wake_lock); |
| queue_delayed_work(charger->wqueue, &charger->wpc_isr_work, msecs_to_jiffies(500)); |
| } |
| } |
| |
| if (irq_src_h & MFC_INTA_H_TX_CON_DISCON_MASK) { |
| pr_info("@Tx_Mode %s TX CONNECT IRQ !\n", __func__); |
| charger->tx_status = SEC_TX_POWER_TRANSFER; |
| if (status_h & MFC_STAT_H_TX_CON_DISCON_MASK) { |
| /* determine rx connection status with tx sharing mode */ |
| if (!charger->wc_rx_connected) |
| mfc_tx_check_rx_connection(charger, true); |
| } 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 { |
| mfc_tx_check_rx_connection(charger, false); |
| } |
| } |
| } |
| |
| /* when rx is not detacted in 3 mins */ |
| if (irq_src_h & MFC_INTA_H_TX_MODE_RX_NOT_DET_MASK) { |
| pr_info("@Tx_Mode %s Receiver NOT DETECTED IRQ !\n", __func__); |
| if (charger->wc_tx_enable) { |
| value.intval = BATT_TX_EVENT_WIRELESS_TX_ETC; |
| end_irq = true; |
| goto INT_END; |
| } |
| } |
| |
| if (irq_src_h & MFC_STAT_H_ADT_RECEIVED_MASK) { |
| pr_info("%s %s ADT Received IRQ !\n", WC_AUTH_MSG, __func__); |
| mfc_auth_adt_read(charger, ADT_buffer_rdata); |
| } |
| |
| if (irq_src_h & MFC_STAT_H_ADT_SENT_MASK) { |
| pr_info("%s %s ADT Sent successful IRQ !\n", WC_AUTH_MSG, __func__); |
| mfc_reg_update(charger->client, MFC_INT_A_ENABLE_H_REG, |
| 0, MFC_STAT_H_ADT_SENT_MASK); |
| } |
| |
| if (irq_src_h & MFC_INTA_H_TX_FOD_MASK) { |
| pr_info("%s TX FOD IRQ !\n", __func__); |
| // this code will move to wpc_tx_isr_work |
| if (charger->wc_tx_enable) { |
| if (status_h & MFC_STAT_H_TX_FOD_MASK) { |
| charger->wc_rx_fod = true; |
| value.intval = BATT_TX_EVENT_WIRELESS_TX_FOD; |
| end_irq = true; |
| goto INT_END; |
| } |
| } |
| } |
| |
| if (irq_src_h & MFC_INTA_H_TX_OCP_MASK) { |
| pr_info("%s TX Over Current IRQ !\n", __func__); |
| } |
| |
| INT_END: |
| /* clear intterupt */ |
| mfc_clear_irq(charger, &irq_src_l, &irq_src_h, &status_l, &status_h); |
| |
| /* 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__); |
| |
| wake_unlock(&charger->wpc_wake_lock); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int mfc_chg_parse_dt(struct device *dev, |
| mfc_charger_platform_data_t *pdata) |
| { |
| int ret = 0; |
| struct device_node *np = dev->of_node; |
| enum of_gpio_flags irq_gpio_flags; |
| int len,i; |
| const u32 *p; |
| |
| if (!np) { |
| pr_err("%s np NULL\n", __func__); |
| return 1; |
| } else { |
| p = of_get_property(np, "battery,fod_data", &len); |
| if (p) { |
| len = len / sizeof(u32); |
| pdata->fod_data = kzalloc(sizeof(*pdata->fod_data) * len, GFP_KERNEL); |
| ret = of_property_read_u32_array(np, "battery,fod_data", |
| pdata->fod_data, len); |
| pdata->fod_data_check = 1; |
| |
| for (i = 0; i < len; i++) |
| pr_info("%s fod data = %d ", __func__, pdata->fod_data[i]); |
| } else { |
| pdata->fod_data_check = 0; |
| pr_err("%s there is not fod_data\n", __func__); |
| } |
| |
| p = of_get_property(np, "battery,fod_data_cv", &len); |
| if (p) { |
| len = len / sizeof(u32); |
| pdata->fod_data_cv = kzalloc(sizeof(*pdata->fod_data_cv) * len, GFP_KERNEL); |
| ret = of_property_read_u32_array(np, "battery,fod_data_cv", |
| pdata->fod_data_cv, len); |
| pdata->fod_data_check = 1; |
| |
| for (i = 0; i < len; i++) |
| pr_info("%s fod data_cv = %d ", __func__, pdata->fod_data_cv[i]); |
| } else { |
| pdata->fod_data_check = 0; |
| pr_err("%s there is not fod_data_cv\n", __func__); |
| } |
| |
| p = of_get_property(np, "battery,fod_hero_5v_data", &len); |
| if (p) { |
| len = len / sizeof(u32); |
| pdata->fod_hero_5v_data = kzalloc(sizeof(*pdata->fod_hero_5v_data) * len, GFP_KERNEL); |
| ret = of_property_read_u32_array(np, "battery,fod_hero_5v_data", |
| pdata->fod_hero_5v_data, len); |
| |
| for (i = 0; i < len; i++) |
| pr_info("%s fod Hero 5V data = 0x%x ", __func__, pdata->fod_hero_5v_data[i]); |
| } else { |
| pr_err("%s there is not fod_hero_5v_data\n", __func__); |
| } |
| |
| p = of_get_property(np, "battery,fod_dream_data", &len); |
| if (p) { |
| len = len / sizeof(u32); |
| pdata->fod_dream_data = kzalloc(sizeof(*pdata->fod_dream_data) * len, GFP_KERNEL); |
| ret = of_property_read_u32_array(np, "battery,fod_dream_data", |
| pdata->fod_dream_data, len); |
| |
| for (i = 0; i < len; i++) |
| pr_info("%s fod Dream data = 0x%x ", __func__, pdata->fod_dream_data[i]); |
| } else { |
| pr_err("%s there is not fod_dream_data\n", __func__); |
| } |
| |
| p = of_get_property(np, "battery,fod_dream_cv_data", &len); |
| if (p) { |
| len = len / sizeof(u32); |
| pdata->fod_dream_cv_data = kzalloc(sizeof(*pdata->fod_dream_cv_data) * len, GFP_KERNEL); |
| ret = of_property_read_u32_array(np, "battery,fod_dream_cv_data", |
| pdata->fod_dream_cv_data, len); |
| |
| for (i = 0; i < len; i++) |
| pr_info("%s fod Dream CV data = 0x%x ", __func__, pdata->fod_dream_cv_data[i]); |
| } else { |
| pr_err("%s there is not fod_dream_cv_data\n", __func__); |
| } |
| |
| 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,mst_switch_delay", |
| &pdata->mst_switch_delay); |
| if (ret < 0) { |
| pr_info("%s: mst_switch_delay is Empty\n", __func__); |
| pdata->mst_switch_delay = 1000; /* set default value (dream) */ |
| } |
| |
| ret = of_property_read_u32(np, "battery,wc_cover_rpp", |
| &pdata->wc_cover_rpp); |
| if (ret < 0) { |
| pr_info("%s: fail to read wc_cover_rpp.\n", __func__); |
| pdata->wc_cover_rpp = 0x55; |
| } |
| |
| ret = of_property_read_u32(np, "battery,wc_hv_rpp", |
| &pdata->wc_hv_rpp); |
| if (ret < 0) { |
| pr_info("%s: fail to read wc_hv_rpp.\n", __func__); |
| pdata->wc_hv_rpp = 0x40; |
| } |
| |
| /* wpc_det */ |
| ret = pdata->wpc_det = of_get_named_gpio_flags(np, "battery,wpc_det", |
| 0, &irq_gpio_flags); |
| if (ret < 0) { |
| dev_err(dev, "%s : can't get wpc_det\r\n", __FUNCTION__); |
| } else { |
| pdata->irq_wpc_det = gpio_to_irq(pdata->wpc_det); |
| pr_info("%s wpc_det = 0x%x, irq_wpc_det = 0x%x\n", __func__, pdata->wpc_det, pdata->irq_wpc_det); |
| } |
| /* wpc_int (This GPIO means MFC_AP_INT) */ |
| ret = pdata->wpc_int = of_get_named_gpio_flags(np, "battery,wpc_int", |
| 0, &irq_gpio_flags); |
| if (ret < 0) { |
| dev_err(dev, "%s : can't wpc_int\r\n", __FUNCTION__); |
| } 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); |
| } |
| |
| /* vrect_int */ |
| ret = pdata->vrect_int = of_get_named_gpio_flags(np, "battery,vrect_int", |
| 0, &irq_gpio_flags); |
| if (ret < 0) { |
| dev_err(dev, "%s : can't vrect_int\r\n", __FUNCTION__); |
| } else { |
| pdata->irq_vrect_int = gpio_to_irq(pdata->vrect_int); |
| pr_info("%s vrect_int = 0x%x, irq_vrect_int = 0x%x\n", __func__, pdata->vrect_int, pdata->irq_vrect_int); |
| } |
| |
| /* mst_pwr_en (MST PWR EN) */ |
| ret = pdata->mst_pwr_en = of_get_named_gpio_flags(np, "battery,mst_pwr_en", |
| 0, &irq_gpio_flags); |
| if (ret < 0) { |
| dev_err(dev, "%s : can't mst_pwr_en\r\n", __FUNCTION__); |
| } |
| |
| /* wpc_en (MFC EN) */ |
| ret = pdata->wpc_en = of_get_named_gpio_flags(np, "battery,wpc_en", |
| 0, &irq_gpio_flags); |
| if (ret < 0) { |
| dev_err(dev, "%s : can't wpc_en\r\n", __FUNCTION__); |
| } |
| |
| /* ping_nen (PING nEN) */ |
| ret = pdata->ping_nen = of_get_named_gpio_flags(np, "battery,ping_nen", |
| 0, &irq_gpio_flags); |
| if (ret < 0) { |
| dev_err(dev, "%s : can't ping_nen\r\n", __FUNCTION__); |
| } |
| |
| 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 = kzalloc(sizeof(*pdata->wireless20_vout_list) * len, 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 = kzalloc(sizeof(*pdata->wireless20_vrect_list) * len, 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 = kzalloc(sizeof(*pdata->wireless20_max_power_list) * len, 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 ", __func__, pdata->wireless20_max_power_list[i]); |
| } else { |
| pr_err("%s there is no wireless20_max_power_list\n", __func__); |
| } |
| |
| 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 mfc_s2miw04_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 += sprintf(buf, "0x%x\n", charger->addr); |
| break; |
| case MFC_SIZE: |
| i += sprintf(buf, "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 += sprintf(buf + i, "addr: 0x%x read fail\n", charger->addr + j); |
| continue; |
| } |
| i += sprintf(buf + i, "addr: 0x%x, data: 0x%x\n", charger->addr + j, data); |
| } |
| } |
| break; |
| case MFC_PACKET: |
| break; |
| default: |
| return -EINVAL; |
| } |
| return i; |
| } |
| |
| ssize_t mfc_s2miw04_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: |
| if (sscanf(buf, "0x%4x 0x%4x 0x%4x\n", &header, &data_com, &data_val) == 3) { |
| dev_info(charger->dev, "%s 0x%x, 0x%x, 0x%x \n", __func__, header, data_com, data_val); |
| mfc_send_packet(charger, header, data_com, &data_val, 1); |
| } |
| ret = count; |
| break; |
| default: |
| ret = -EINVAL; |
| } |
| return ret; |
| } |
| |
| static const struct power_supply_desc mfc_charger_power_supply_desc = { |
| .name = "mfc-charger", |
| .type = POWER_SUPPLY_TYPE_UNKNOWN, |
| .properties = mfc_charger_props, |
| .num_properties = ARRAY_SIZE(mfc_charger_props), |
| .get_property = mfc_chg_get_property, |
| .set_property = mfc_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, |
| NULL, mfc_wpc_irq_thread, |
| IRQF_TRIGGER_FALLING | |
| IRQF_ONESHOT, |
| "wpc-irq", charger); |
| if (ret) { |
| pr_err("%s: Failed to Reqeust 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__); |
| wake_lock(&charger->wpc_tx_phm_lock); |
| queue_delayed_work(charger->wqueue, &charger->wpc_tx_phm_work, 0); |
| |
| return ALARMTIMER_NORESTART; |
| } |
| |
| static int mfc_s2miw04_charger_probe( |
| struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| struct device_node *of_node = client->dev.of_node; |
| struct mfc_charger_data *charger; |
| mfc_charger_platform_data_t *pdata = client->dev.platform_data; |
| struct power_supply_config mfc_cfg = {}; |
| int ret = 0; |
| int wc_w_state_irq; |
| |
| dev_info(&client->dev, |
| "%s: MFC s2miw04 Charger Driver Loading\n", __func__); |
| |
| #if defined(CONFIG_WIRELESS_CHARGER_HAL_MFC) |
| if (client->addr != 0x3b) |
| client->addr = 0x3b; |
| #endif |
| |
| if (of_node) { |
| pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); |
| if (!pdata) { |
| dev_err(&client->dev, "Failed to allocate memory\n"); |
| 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 = kzalloc(sizeof(*charger), GFP_KERNEL); |
| if (charger == NULL) { |
| dev_err(&client->dev, "Memory is not enough.\n"); |
| 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 = -ENOSYS; |
| 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); |
| |
| charger->pdata->cable_type = SEC_WIRELESS_PAD_NONE; |
| charger->pdata->is_charging = 0; |
| charger->tx_status = 0; |
| charger->pdata->cs100_status = 0; |
| charger->pdata->capacity = 101; |
| charger->pdata->vout_status = MFC_VOUT_5V; |
| charger->pdata->opfq_cnt = 0; |
| |
| charger->is_mst_on = MST_MODE_0; |
| charger->chip_id = MFC_CHIP_LSI; |
| charger->chip_id_now = MFC_CHIP_ID_S2MIW04; |
| 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->wc_tx_enable = false; |
| charger->initial_wc_check = false; |
| charger->wc_rx_connected = false; |
| charger->wc_rx_fod = false; |
| charger->non_sleep_mode_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->wc_rx_type = NO_DEV; |
| charger->is_suspend = false; |
| charger->device_event = 0; |
| charger->wpc_en_flag = (WPC_EN_SYSFS | WPC_EN_CHARGING | WPC_EN_CCIC); |
| |
| mutex_init(&charger->io_lock); |
| mutex_init(&charger->wpc_en_lock); |
| mutex_init(&charger->fw_lock); |
| |
| /* wpc_det */ |
| if (charger->pdata->irq_wpc_det) { |
| INIT_DELAYED_WORK(&charger->wpc_det_work, mfc_wpc_det_work); |
| INIT_DELAYED_WORK(&charger->wpc_opfq_work, mfc_wpc_opfq_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_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_cm_fet_work, mfc_wpc_cm_fet_work); |
| INIT_DELAYED_WORK(&charger->wpc_i2c_error_work, mfc_wpc_i2c_error_work); |
| INIT_DELAYED_WORK(&charger->wpc_rx_det_work, mfc_wpc_rx_det_work); |
| INIT_DELAYED_WORK(&charger->wpc_tx_op_freq_work, mfc_tx_op_freq_work); |
| INIT_DELAYED_WORK(&charger->wpc_tx_phm_work, mfc_tx_phm_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 ((void *)charger->psy_chg < 0) { |
| pr_err("%s: Failed to Register psy_chg\n", __func__); |
| goto err_supply_unreg; |
| } |
| |
| charger->wqueue = create_singlethread_workqueue("mfc_workqueue"); |
| if (!charger->wqueue) { |
| pr_err("%s: Fail to Create Workqueue\n", __func__); |
| goto err_pdata_free; |
| } |
| |
| wake_lock_init(&charger->wpc_wake_lock, WAKE_LOCK_SUSPEND, |
| "wpc_wakelock"); |
| wake_lock_init(&charger->wpc_rx_wake_lock, WAKE_LOCK_SUSPEND, |
| "wpc_rx_wakelock"); |
| wake_lock_init(&charger->wpc_tx_wake_lock, WAKE_LOCK_SUSPEND, |
| "wpc_tx_wakelock"); |
| wake_lock_init(&charger->wpc_update_lock, WAKE_LOCK_SUSPEND, |
| "wpc_update_lock"); |
| wake_lock_init(&charger->wpc_opfq_lock, WAKE_LOCK_SUSPEND, |
| "wpc_opfq_lock"); |
| wake_lock_init(&charger->wpc_tx_opfq_lock, WAKE_LOCK_SUSPEND, |
| "wpc_tx_opfq_lock"); |
| wake_lock_init(&charger->wpc_afc_vout_lock, WAKE_LOCK_SUSPEND, |
| "wpc_afc_vout_lock"); |
| wake_lock_init(&charger->wpc_vout_mode_lock, WAKE_LOCK_SUSPEND, |
| "wpc_vout_mode_lock"); |
| wake_lock_init(&charger->wpc_rx_det_lock, WAKE_LOCK_SUSPEND, |
| "wpc_rx_det_lock"); |
| wake_lock_init(&charger->wpc_tx_phm_lock, WAKE_LOCK_SUSPEND, |
| "wpc_tx_phm_lock"); |
| wake_lock_init(&charger->wpc_tx_id_lock, WAKE_LOCK_SUSPEND, |
| "wpc_tx_id_lock"); |
| |
| /* Enable interrupts after battery driver load */ |
| /* wpc_det */ |
| if (charger->pdata->irq_wpc_det) { |
| ret = request_threaded_irq(charger->pdata->irq_wpc_det, |
| NULL, mfc_wpc_det_irq_thread, |
| IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | |
| IRQF_ONESHOT, |
| "wpd-det-irq", charger); |
| if (ret) { |
| pr_err("%s: Failed to Reqeust IRQ\n", __func__); |
| goto err_irq_wpc_det; |
| } |
| } |
| |
| /* wpc_irq */ |
| queue_delayed_work(charger->wqueue, &charger->wpc_int_req_work, msecs_to_jiffies(100)); |
| |
| wc_w_state_irq = gpio_get_value(charger->pdata->wpc_int); |
| pr_info("%s wc_w_state_irq = %d\n", __func__, wc_w_state_irq); |
| if (gpio_get_value(charger->pdata->wpc_det)) { |
| u8 irq_src[2]; |
| pr_info("%s: Charger interrupt occured during lpm\n", __func__); |
| |
| mfc_reg_read(charger->client, MFC_INT_A_L_REG, &irq_src[0]); |
| mfc_reg_read(charger->client, MFC_INT_A_H_REG, &irq_src[1]); |
| /* clear intterupt */ |
| mfc_reg_write(charger->client, MFC_INT_A_CLEAR_L_REG, irq_src[0]); // clear int |
| mfc_reg_write(charger->client, MFC_INT_A_CLEAR_H_REG, irq_src[1]); // clear int |
| mfc_set_cmd_l_reg(charger, 0x20, MFC_CMD_CLEAR_INT_MASK); // command |
| queue_delayed_work(charger->wqueue, &charger->wpc_det_work, 0); |
| if (!wc_w_state_irq && !delayed_work_pending(&charger->wpc_isr_work)) { |
| wake_lock(&charger->wpc_rx_wake_lock); |
| queue_delayed_work(charger->wqueue, &charger->wpc_isr_work, msecs_to_jiffies(2000)); |
| } |
| } |
| |
| ret = mfc_create_attrs(&charger->psy_chg->dev); |
| if (ret) { |
| dev_err(charger->dev, |
| "%s : Failed to create_attrs\n", __func__); |
| } |
| |
| charger->is_probed = true; |
| dev_info(&client->dev, |
| "%s: MFC s2miw04 Charger Driver Loaded\n", __func__); |
| |
| device_init_wakeup(charger->dev, 1); |
| return 0; |
| |
| err_irq_wpc_det: |
| wake_lock_destroy(&charger->wpc_wake_lock); |
| wake_lock_destroy(&charger->wpc_rx_wake_lock); |
| wake_lock_destroy(&charger->wpc_tx_wake_lock); |
| wake_lock_destroy(&charger->wpc_update_lock); |
| wake_lock_destroy(&charger->wpc_opfq_lock); |
| wake_lock_destroy(&charger->wpc_tx_opfq_lock); |
| wake_lock_destroy(&charger->wpc_afc_vout_lock); |
| wake_lock_destroy(&charger->wpc_vout_mode_lock); |
| wake_lock_destroy(&charger->wpc_rx_det_lock); |
| wake_lock_destroy(&charger->wpc_tx_phm_lock); |
| wake_lock_destroy(&charger->wpc_tx_id_lock); |
| err_pdata_free: |
| power_supply_unregister(charger->psy_chg); |
| err_supply_unreg: |
| 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; |
| } |
| |
| static int mfc_s2miw04_charger_remove(struct i2c_client *client) |
| { |
| struct mfc_charger_data *charger = i2c_get_clientdata(client); |
| |
| alarm_cancel(&charger->phm_alarm); |
| |
| return 0; |
| } |
| |
| #if defined(CONFIG_PM) |
| static int mfc_charger_suspend(struct device *dev) |
| { |
| struct mfc_charger_data *charger = dev_get_drvdata(dev); |
| |
| pr_info("%s\n", __func__); |
| |
| 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); |
| } |
| disable_irq(charger->pdata->irq_wpc_int); |
| disable_irq(charger->pdata->irq_wpc_det); |
| |
| return 0; |
| } |
| |
| static int mfc_charger_resume(struct device *dev) |
| { |
| struct mfc_charger_data *charger = dev_get_drvdata(dev); |
| |
| pr_info("%s\n", __func__); |
| |
| 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); |
| } |
| enable_irq(charger->pdata->irq_wpc_int); |
| enable_irq(charger->pdata->irq_wpc_det); |
| |
| return 0; |
| } |
| #else |
| #define mfc_charger_suspend NULL |
| #define mfc_charger_resume NULL |
| #endif |
| |
| static void mfc_s2miw04_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__); |
| mfc_set_vrect_adjust(charger, MFC_HEADROOM_1); |
| mfc_set_vout(charger, MFC_VOUT_5V); |
| } |
| } |
| |
| static const struct i2c_device_id mfc_s2miw04_charger_id_table[] = { |
| { "mfc-s2miw04-charger", 0 }, |
| { }, |
| }; |
| MODULE_DEVICE_TABLE(i2c, mfc_s2miw04_charger_id_table); |
| |
| #ifdef CONFIG_OF |
| static struct of_device_id mfc_s2miw04_charger_match_table[] = { |
| { .compatible = "lsi,mfc-s2miw04-charger",}, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, mfc_s2miw04_charger_match_table); |
| #else |
| #define mfc_s2miw04_charger_match_table NULL |
| #endif |
| |
| const struct dev_pm_ops mfc_s2miw04_pm = { |
| SET_SYSTEM_SLEEP_PM_OPS(mfc_charger_suspend,mfc_charger_resume) |
| }; |
| |
| static struct i2c_driver mfc_s2miw04_charger_driver = { |
| .driver = { |
| .name = "mfc-s2miw04-charger", |
| .owner = THIS_MODULE, |
| #if defined(CONFIG_PM) |
| .pm = &mfc_s2miw04_pm, |
| #endif /* CONFIG_PM */ |
| .of_match_table = mfc_s2miw04_charger_match_table, |
| }, |
| .shutdown = mfc_s2miw04_charger_shutdown, |
| .probe = mfc_s2miw04_charger_probe, |
| .remove = mfc_s2miw04_charger_remove, |
| .id_table = mfc_s2miw04_charger_id_table, |
| }; |
| |
| static int __init mfc_s2miw04_charger_init(void) |
| { |
| #if defined(CONFIG_WIRELESS_CHARGER_HAL_MFC) |
| if (mfc_chip_id_now != 0xFF && mfc_chip_id_now != MFC_CHIP_ID_S2MIW04) |
| return -ENODEV; |
| #endif |
| |
| pr_info("%s\n", __func__); |
| return i2c_add_driver(&mfc_s2miw04_charger_driver); |
| } |
| |
| static void __exit mfc_s2miw04_charger_exit(void) |
| { |
| pr_info("%s\n", __func__); |
| i2c_del_driver(&mfc_s2miw04_charger_driver); |
| } |
| |
| module_init(mfc_s2miw04_charger_init); |
| module_exit(mfc_s2miw04_charger_exit); |
| |
| MODULE_DESCRIPTION("Samsung MFC Charger Driver"); |
| MODULE_AUTHOR("Samsung Electronics"); |
| MODULE_LICENSE("GPL"); |