// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2022 MediaTek Inc. */ #include #include /* For init/exit macros */ #include /* For MODULE_ marcros */ #include #include #include #include #include #ifdef CONFIG_OF #include #include #include #include #endif #include #include #include "sgm41516d.h" /********************************************************** * * [I2C Slave Setting] * *********************************************************/ //extern int hw_charging_get_charger_type(void); int g_chg_info; int led_ctl_gpio; /*SMG41516 start*/ enum chg_info { BQ25601D = 1, BQ25601, SGM41516, SGM41516D, CHG_INFO_MAX, }; enum cv_fine_val { CV_NORMAL=0, CV_POST_8MV, CV_NEG_8MV, CV_NEG_16MV, CV_MAX, }; enum usb_val { USB_HIZ=0, USB_0V, USB_0_6V, USB_3_3V, USB_VAL_MAX, }; enum wdt_value { DISABLE_WDT=0, WDT_40S, WDT_80S, WDT_160S, }; const unsigned int VBAT_CV_FINE[] = {32000,8000}; /*SMG41516 end*/ enum sgm41516d_pmu_chg_type { SGM41516D_CHG_TYPE_NOVBUS = 0, SGM41516D_CHG_TYPE_SDP, SGM41516D_CHG_TYPE_CDP, SGM41516D_CHG_TYPE_DCP, SGM41516D_CHG_TYPE_UNKNOWN=5, SGM41516D_CHG_TYPE_SDPNSTD, SGM41516D_CHG_TYPE_OTG, SGM41516D_CHG_TYPE_MAX, }; static enum charger_type bq_chg_type; #define BQ_DET_COUNT_MAX 16 #define GETARRAYNUM(array) (ARRAY_SIZE(array)) const unsigned int VBAT_CV_VTH[] = { 3856000, 3888000, 3920000, 3952000, 3984000, 4016000, 4048000, 4080000, 4112000, 4144000, 4176000, 4208000, 4240000, 4272000, 4304000, 4336000, 4368000, 4400000, 4432000, 4464000, 4496000, 4528000, 4560000, 4592000, 4624000 }; const unsigned int CS_VTH[] = { 0, 6000, 12000, 18000, 24000, 30000, 36000, 42000, 48000, 54000, 60000, 66000, 72000, 78000, 84000, 90000, 96000, 102000, 108000, 114000, 120000, 126000, 132000, 138000, 144000, 150000, 156000, 162000, 168000, 174000, 180000, 186000, 192000, 198000, 204000, 210000, 216000, 222000, 228000, 234000, 240000, 246000, 252000, 258000, 264000, 270000, 276000, 282000, 288000, 294000, 300000, 306000 }; const unsigned int INPUT_CS_VTH[] = { 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, 100000, 110000, 120000, 130000, 140000, 150000, 160000, 170000, 180000, 190000, 200000, 210000, 220000, 230000, 250000, 260000, 270000, 280000, 290000, 300000, 310000, 320000 }; const unsigned int VCDT_HV_VTH[] = { 4200000, 4250000, 4300000, 4350000, 4400000, 4450000, 4500000, 4550000, 4600000, 6000000, 6500000, 7000000, 7500000, 8500000, 9500000, 10500000 }; const unsigned int VINDPM_REG[] = { 3900, 4000, 4100, 4200, 4300, 4400, 4500, 4600, 4700, 4800, 4900, 5000, 5100, 5200, 5300, 5400, 5500, 5600, 5700, 5800, 5900, 6000, 6100, 6200, 6300, 6400 }; /* SGM41516D REG0A BOOST_LIM[2:0], mA */ const unsigned int BOOST_CURRENT_LIMIT[] = { 500, 1200 }; DEFINE_MUTEX(g_input_current_mutex); static const struct i2c_device_id sgm41516d_i2c_id[] = { {"sgm41516d", 0}, {} }; static int sgm41516d_driver_probe(struct i2c_client *client, const struct i2c_device_id *id); unsigned int charging_value_to_parameter(const unsigned int *parameter, const unsigned int array_size, const unsigned int val) { if (val < array_size) return parameter[val]; pr_info("Can't find the parameter\n"); return parameter[0]; } unsigned int charging_parameter_to_value(const unsigned int *parameter, const unsigned int array_size, const unsigned int val) { unsigned int i; pr_debug_ratelimited("array_size = %d\n", array_size); for (i = 0; i < array_size; i++) { if (val == *(parameter + i)) return i; } pr_info("NO register value match\n"); /* TODO: ASSERT(0); // not find the value */ return 0; } static unsigned int bmt_find_closest_level(const unsigned int *pList, unsigned int number, unsigned int level) { unsigned int i; unsigned int max_value_in_last_element; if (pList[0] < pList[1]) max_value_in_last_element = 1; else max_value_in_last_element = 0; if (max_value_in_last_element == 1) { for (i = (number - 1); i != 0; i--) { /* max value in the last element */ if (pList[i] <= level) { pr_debug_ratelimited("zzf_%d<=%d, i=%d\n", pList[i], level, i); return pList[i]; } } pr_info("Can't find closest level\n"); return pList[0]; /* return CHARGE_CURRENT_0_00_MA; */ } else { /* max value in the first element */ for (i = 0; i < number; i++) { if (pList[i] <= level) return pList[i]; } pr_info("Can't find closest level\n"); return pList[number - 1]; /* return CHARGE_CURRENT_0_00_MA; */ } } static unsigned int sgm41516d_closest_reg(u32 min, u32 max, u32 step, u32 target) { if (target < min) return 0; if (target >= max) target = max; return (target - min) / step; } /********************************************************** * * [I2C Function For Read/Write sgm41516d] * *********************************************************/ #ifdef CONFIG_MTK_I2C_EXTENSION unsigned int sgm41516d_read_byte(struct sgm41516d_info *info, unsigned char cmd, unsigned char *returnData) { char cmd_buf[1] = { 0x00 }; char readData = 0; int ret = 0; mutex_lock(&info->sgm41516d_i2c_access); /* info->client->addr = ((info->client->addr) & I2C_MASK_FLAG) | * I2C_WR_FLAG; */ info->client->ext_flag = ((info->client->ext_flag) & I2C_MASK_FLAG) | I2C_WR_FLAG | I2C_DIRECTION_FLAG; cmd_buf[0] = cmd; ret = i2c_master_send(info->client, &cmd_buf[0], (1 << 8 | 1)); if (ret < 0) { /* info->client->addr = info->client->addr & I2C_MASK_FLAG; */ info->client->ext_flag = 0; mutex_unlock(&info->sgm41516d_i2c_access); return 0; } readData = cmd_buf[0]; *returnData = readData; /* info->client->addr = info->client->addr & I2C_MASK_FLAG; */ info->client->ext_flag = 0; mutex_unlock(&info->sgm41516d_i2c_access); return 1; } unsigned int sgm41516d_write_byte(struct sgm41516d_info *info, unsigned char cmd, unsigned char writeData) { char write_data[2] = { 0 }; int ret = 0; mutex_lock(&info->sgm41516d_i2c_access); write_data[0] = cmd; write_data[1] = writeData; info->client->ext_flag = ((info->client->ext_flag) & I2C_MASK_FLAG) | I2C_DIRECTION_FLAG; ret = i2c_master_send(info->client, write_data, 2); if (ret < 0) { info->client->ext_flag = 0; mutex_unlock(&info->sgm41516d_i2c_access); return 0; } info->client->ext_flag = 0; mutex_unlock(&info->sgm41516d_i2c_access); return 1; } #else unsigned int sgm41516d_read_byte(struct sgm41516d_info *info, unsigned char cmd, unsigned char *returnData) { unsigned char xfers = 2; int ret, retries = 1; mutex_lock(&info->sgm41516d_i2c_access); do { struct i2c_msg msgs[2] = { { .addr = info->client->addr, .flags = 0, .len = 1, .buf = &cmd, }, { .addr = info->client->addr, .flags = I2C_M_RD, .len = 1, .buf = returnData, } }; /* * Avoid sending the segment addr to not upset non-compliant * DDC monitors. */ ret = i2c_transfer(info->client->adapter, msgs, xfers); if (ret == -ENXIO) { pr_info("skipping non-existent adapter %s\n", info->client->adapter->name); break; } } while (ret != xfers && --retries); mutex_unlock(&info->sgm41516d_i2c_access); return ret == xfers ? 1 : -1; } unsigned int sgm41516d_write_byte(struct sgm41516d_info *info, unsigned char cmd, unsigned char writeData) { unsigned char xfers = 1; int ret, retries = 1; unsigned char buf[8]; mutex_lock(&info->sgm41516d_i2c_access); buf[0] = cmd; memcpy(&buf[1], &writeData, 1); do { struct i2c_msg msgs[1] = { { .addr = info->client->addr, .flags = 0, .len = 1 + 1, .buf = buf, }, }; /* * Avoid sending the segment addr to not upset non-compliant * DDC monitors. */ ret = i2c_transfer(info->client->adapter, msgs, xfers); if (ret == -ENXIO) { pr_info("skipping non-existent adapter %s\n", info->client->adapter->name); break; } } while (ret != xfers && --retries); mutex_unlock(&info->sgm41516d_i2c_access); return ret == xfers ? 1 : -1; } #endif /********************************************************** * * [Read / Write Function] * *********************************************************/ unsigned int sgm41516d_read_interface(struct sgm41516d_info *info, unsigned char RegNum, unsigned char *val, unsigned char MASK, unsigned char SHIFT) { unsigned char sgm41516d_reg = 0; unsigned int ret = 0; mutex_lock(&info->sgm41516d_access_lock); ret = sgm41516d_read_byte(info, RegNum, &sgm41516d_reg); if (ret < 0) pr_info("[%s] read Reg[%x] failed ret = %d\n", __func__, RegNum, ret); pr_info("[%s] Reg[%x]=0x%x\n", __func__, RegNum, sgm41516d_reg); sgm41516d_reg &= (MASK << SHIFT); *val = (sgm41516d_reg >> SHIFT); pr_info("[%s] val=0x%x\n", __func__, *val); mutex_unlock(&info->sgm41516d_access_lock); return ret; } unsigned int sgm41516d_config_interface(struct sgm41516d_info *info, unsigned char RegNum, unsigned char val, unsigned char MASK, unsigned char SHIFT) { unsigned char sgm41516d_reg = 0; unsigned char sgm41516d_reg_ori = 0; unsigned int ret = 0; mutex_lock(&info->sgm41516d_access_lock); ret = sgm41516d_read_byte(info, RegNum, &sgm41516d_reg); if (ret < 0) pr_info("[%s] read Reg[%x] failed ret = %d\n", __func__, RegNum, ret); sgm41516d_reg_ori = sgm41516d_reg; sgm41516d_reg &= ~(MASK << SHIFT); sgm41516d_reg |= (val << SHIFT); ret = sgm41516d_write_byte(info, RegNum, sgm41516d_reg); if (ret < 0) pr_info("[%s] write Reg[%x] failed ret = %d\n", __func__, RegNum, ret); mutex_unlock(&info->sgm41516d_access_lock); pr_info("[%s] write Reg[%x]=0x%x from 0x%x\n", __func__, RegNum, sgm41516d_reg, sgm41516d_reg_ori); return ret; } /********************************************************** * * [platform_driver API] * *********************************************************/ unsigned char g_reg_value_sgm41516d; static ssize_t show_sgm41516d_access(struct device *dev, struct device_attribute *attr, char *buf) { pr_info("[%s] 0x%x\n", __func__, g_reg_value_sgm41516d); return sprintf(buf, "0x%x\n", (unsigned int)g_reg_value_sgm41516d); } static ssize_t store_sgm41516d_access(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret = 0; char *pvalue = NULL, *addr, *val; unsigned int reg_value = 0; unsigned int reg_address = 0; struct sgm41516d_info *info = dev_get_drvdata(dev); pr_info("[%s]\n", __func__); if (buf != NULL && size != 0) { pr_info("[%s] buf is %s and size is %zu\n", __func__, buf, size); pvalue = (char *)buf; if (size > 5) { addr = strsep(&pvalue, " "); ret = kstrtou32(addr, 16, (unsigned int *)®_address); } else ret = kstrtou32(pvalue, 16, (unsigned int *)®_address); if (size > 5) { val = strsep(&pvalue, " "); ret = kstrtou32(val, 16, (unsigned int *)®_value); pr_info( "[%s] write sgm41516d reg 0x%x with value 0x%x !\n", __func__, (unsigned int) reg_address, reg_value); //ret = sgm41516d_write_byte(info, reg_address, reg_value); ret = sgm41516d_config_interface(info, reg_address, reg_value, 0xFF, 0x0); } else { ret = sgm41516d_read_byte(info, reg_address, &g_reg_value_sgm41516d); pr_info( "[%s] read sgm41516d reg 0x%x with value 0x%x !\n", __func__, (unsigned int) reg_address, g_reg_value_sgm41516d); pr_info( "[%s] use \"cat sgm41516d_access\" to get value\n", __func__); } } return size; } static DEVICE_ATTR(sgm41516d_access, 0664, show_sgm41516d_access, store_sgm41516d_access); /* 664 */ /********************************************************** * * [Internal Function] * *********************************************************/ /* CON0---------------------------------------------------- */ void sgm41516d_set_en_hiz(struct sgm41516d_info *info, bool val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON0), (unsigned char) (val), (unsigned char) (CON0_EN_HIZ_MASK), (unsigned char) (CON0_EN_HIZ_SHIFT) ); } void sgm41516d_set_iinlim(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON0), (unsigned char) (val), (unsigned char) (CON0_IINLIM_MASK), (unsigned char) (CON0_IINLIM_SHIFT) ); } void sgm41516d_set_stat_ctrl(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON0), (unsigned char) (val), (unsigned char) (CON0_STAT_IMON_CTRL_MASK), (unsigned char) (CON0_STAT_IMON_CTRL_SHIFT) ); } /* CON1---------------------------------------------------- */ /*It can only be used once during boot initialization*/ void sgm41516d_set_reg_rst(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON11), (unsigned char) (val), (unsigned char) (CON11_REG_RST_MASK), (unsigned char) (CON11_REG_RST_SHIFT) ); } void sgm41516d_set_pfm(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON1), (unsigned char) (val), (unsigned char) (CON1_PFM_MASK), (unsigned char) (CON1_PFM_SHIFT) ); } void sgm41516d_set_wdt_rst(struct sgm41516d_info *info, bool val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON1), (unsigned char) (val), (unsigned char) (CON1_WDT_RST_MASK), (unsigned char) (CON1_WDT_RST_SHIFT) ); } void sgm41516d_set_otg_config(struct sgm41516d_info *info, bool val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON1), (unsigned char) (val), (unsigned char) (CON1_OTG_CONFIG_MASK), (unsigned char) (CON1_OTG_CONFIG_SHIFT) ); } void sgm41516d_set_chg_config(struct sgm41516d_info *info, bool val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON1), (unsigned char) (val), (unsigned char) (CON1_CHG_CONFIG_MASK), (unsigned char) (CON1_CHG_CONFIG_SHIFT) ); } void sgm41516d_set_sys_min(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON1), (unsigned char) (val), (unsigned char) (CON1_SYS_MIN_MASK), (unsigned char) (CON1_SYS_MIN_SHIFT) ); } void sgm41516d_set_batlowv(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON1), (unsigned char) (val), (unsigned char) (CON1_MIN_VBAT_SEL_MASK), (unsigned char) (CON1_MIN_VBAT_SEL_SHIFT) ); } /* CON2---------------------------------------------------- */ void sgm41516d_set_rdson(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON2), (unsigned char) (val), (unsigned char) (CON2_Q1_FULLON_MASK), (unsigned char) (CON2_Q1_FULLON_SHIFT) ); } void sgm41516d_set_boost_lim(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON2), (unsigned char) (val), (unsigned char) (CON2_BOOST_LIM_MASK), (unsigned char) (CON2_BOOST_LIM_SHIFT) ); } void sgm41516d_set_ichg(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON2), (unsigned char) (val), (unsigned char) (CON2_ICHG_MASK), (unsigned char) (CON2_ICHG_SHIFT) ); } /* CON3---------------------------------------------------- */ void sgm41516d_set_iprechg(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON3), (unsigned char) (val), (unsigned char) (CON3_IPRECHG_MASK), (unsigned char) (CON3_IPRECHG_SHIFT) ); } void sgm41516d_set_iterm(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON3), (unsigned char) (val), (unsigned char) (CON3_ITERM_MASK), (unsigned char) (CON3_ITERM_SHIFT) ); } /* CON4---------------------------------------------------- */ void sgm41516d_set_vreg(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON4), (unsigned char) (val), (unsigned char) (CON4_VREG_MASK), (unsigned char) (CON4_VREG_SHIFT) ); } void sgm41516d_set_topoff_timer(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON4), (unsigned char) (val), (unsigned char) (CON4_TOPOFF_TIMER_MASK), (unsigned char) (CON4_TOPOFF_TIMER_SHIFT) ); } void sgm41516d_set_vrechg(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON4), (unsigned char) (val), (unsigned char) (CON4_VRECHG_MASK), (unsigned char) (CON4_VRECHG_SHIFT) ); } /* CON5---------------------------------------------------- */ void sgm41516d_set_en_term(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON5), (unsigned char) (val), (unsigned char) (CON5_EN_TERM_MASK), (unsigned char) (CON5_EN_TERM_SHIFT) ); } void sgm41516d_set_watchdog(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON5), (unsigned char) (val), (unsigned char) (CON5_WATCHDOG_MASK), (unsigned char) (CON5_WATCHDOG_SHIFT) ); } void sgm41516d_set_en_timer(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON5), (unsigned char) (val), (unsigned char) (CON5_EN_TIMER_MASK), (unsigned char) (CON5_EN_TIMER_SHIFT) ); } void sgm41516d_set_chg_timer(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON5), (unsigned char) (val), (unsigned char) (CON5_CHG_TIMER_MASK), (unsigned char) (CON5_CHG_TIMER_SHIFT) ); } void sgm41516d_set_chg_thermal(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON5), (unsigned char) (val), (unsigned char) (CON5_TREG_MASK), (unsigned char) (CON5_TREG_SHIFT) ); } /* CON6---------------------------------------------------- */ void sgm41516d_set_treg(struct sgm41516d_info *info, unsigned int val) { #if 0 unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON6), (unsigned char) (val), (unsigned char) (CON6_BOOSTV_MASK), (unsigned char) (CON6_BOOSTV_SHIFT) ); #endif } void sgm41516d_set_vindpm(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON6), (unsigned char) (val), (unsigned char) (CON6_VINDPM_MASK), (unsigned char) (CON6_VINDPM_SHIFT) ); } unsigned char sgm41516d_get_vindpm(struct sgm41516d_info *info) { unsigned int ret = 0; unsigned char val = 0; ret = sgm41516d_read_interface(info, (unsigned char) (sgm41516d_CON6), &val, (unsigned char) (CON6_VINDPM_MASK), (unsigned char) (CON6_VINDPM_SHIFT) ); return val; } void sgm41516d_set_ovp(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON6), (unsigned char) (val), (unsigned char) (CON6_OVP_MASK), (unsigned char) (CON6_OVP_SHIFT) ); } void sgm41516d_set_boostv(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON6), (unsigned char) (val), (unsigned char) (CON6_BOOSTV_MASK), (unsigned char) (CON6_BOOSTV_SHIFT) ); } /* CON7---------------------------------------------------- */ void sgm41516d_set_tmr2x_en(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON7), (unsigned char) (val), (unsigned char) (CON7_TMR2X_EN_MASK), (unsigned char) (CON7_TMR2X_EN_SHIFT) ); } void sgm41516d_set_batfet_disable(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON7), (unsigned char) (val), (unsigned char) (CON7_BATFET_Disable_MASK), (unsigned char) (CON7_BATFET_Disable_SHIFT) ); } void sgm41516d_set_batfet_delay(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON7), (unsigned char) (val), (unsigned char) (CON7_BATFET_DLY_MASK), (unsigned char) (CON7_BATFET_DLY_SHIFT) ); } void sgm41516d_set_batfet_reset_enable(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON7), (unsigned char) (val), (unsigned char) (CON7_BATFET_RST_EN_MASK), (unsigned char) (CON7_BATFET_RST_EN_SHIFT) ); } /* CON8---------------------------------------------------- */ unsigned int sgm41516d_get_system_status(struct sgm41516d_info *info) { unsigned int ret = 0; unsigned char val = 0; ret = sgm41516d_read_interface(info, (unsigned char) (sgm41516d_CON8), (&val), (unsigned char) (0xFF), (unsigned char) (0x0) ); return val; } unsigned int sgm41516d_get_vbus_stat(struct sgm41516d_info *info) { unsigned int i, j, ret = 0; unsigned char val = 0; const int retry_count = 5; unsigned int unknown_count = 0,sdp_count = 0,cdp_count = 0, dcp_count = 0,sdpnstd_count = 0,otg_count = 0; struct chg_type_record sgm41516d_chg_type_record[] = { {SGM41516D_CHG_TYPE_SDP, sdp_count}, {SGM41516D_CHG_TYPE_CDP, cdp_count}, {SGM41516D_CHG_TYPE_DCP, dcp_count}, {SGM41516D_CHG_TYPE_SDPNSTD, sdpnstd_count}, {SGM41516D_CHG_TYPE_OTG, otg_count}, {SGM41516D_CHG_TYPE_UNKNOWN, unknown_count}, }; for (i = 0; i < retry_count; i++){ ret = sgm41516d_read_interface(info, (unsigned char) (sgm41516d_CON8), (&val), (unsigned char) (CON8_VBUS_STAT_MASK), (unsigned char) (CON8_VBUS_STAT_SHIFT) ); for (j = 0; j < GETARRAYNUM(sgm41516d_chg_type_record); j++) { if (val == sgm41516d_chg_type_record[j].chg_type){ sgm41516d_chg_type_record[j].chg_type_count ++; if (sgm41516d_chg_type_record[j].chg_type_count >= retry_count-2) return sgm41516d_chg_type_record[j].chg_type; break; } } } return SGM41516D_CHG_TYPE_NOVBUS; } unsigned int sgm41516d_get_chrg_stat(struct sgm41516d_info *info) { unsigned int ret = 0; unsigned char val = 0; ret = sgm41516d_read_interface(info, (unsigned char) (sgm41516d_CON8), (&val), (unsigned char) (CON8_CHRG_STAT_MASK), (unsigned char) (CON8_CHRG_STAT_SHIFT) ); return val; } unsigned int sgm41516d_get_vsys_stat(struct sgm41516d_info *info) { unsigned int ret = 0; unsigned char val = 0; ret = sgm41516d_read_interface(info, (unsigned char) (sgm41516d_CON8), (&val), (unsigned char) (CON8_VSYS_STAT_MASK), (unsigned char) (CON8_VSYS_STAT_SHIFT) ); return val; } unsigned int sgm41516d_get_pg_stat(struct sgm41516d_info *info) { unsigned int ret = 0; unsigned char val = 0; ret = sgm41516d_read_interface(info, (unsigned char) (sgm41516d_CON8), (&val), (unsigned char) (CON8_PG_STAT_MASK), (unsigned char) (CON8_PG_STAT_SHIFT) ); return val; } /*CON10----------------------------------------------------------*/ void sgm41516d_set_int_mask(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON10), (unsigned char) (val), (unsigned char) (CON10_INT_MASK_MASK), (unsigned char) (CON10_INT_MASK_SHIFT) ); } /*CON13 for SMG41516-----------------------------------------------------*/ void sgm41516d_set_dp(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON13), (unsigned char) (val), (unsigned char) (CON13_REG_DP_VSET_MASK), (unsigned char) (CON13_REG_DP_VSET_SHIFT) ); } void sgm41516d_set_dm(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON13), (unsigned char) (val), (unsigned char) (CON13_REG_DM_VSET_MASK), (unsigned char) (CON13_REG_DM_VSET_SHIFT) ); } /*CON15 for SMG41516-----------------------------------------------------*/ void sgm41516d_set_vindpm_os(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON15), (unsigned char) (val), (unsigned char) (CON15_REG_VINDPM_OS_MASK), (unsigned char) (CON15_REG_VINDPM_OS_SHIFT) ); } unsigned int sgm41516d_get_vindpm_os(struct sgm41516d_info *info) { unsigned int ret = 0; unsigned char val = 0; ret = sgm41516d_read_interface(info, (unsigned char) (sgm41516d_CON15), (&val), (unsigned char) (CON15_REG_VINDPM_OS_MASK), (unsigned char) (CON15_REG_VINDPM_OS_SHIFT) ); return val; } void sgm41516d_set_cv_fine_tuning(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON15), (unsigned char) (val), (unsigned char) (CON15_REG_FINE_TUNING_MASK), (unsigned char) (CON15_REG_FINE_TUNING_SHIFT) ); } /********************************************************** * * [Internal Function] * *********************************************************/ static int sgm41516d_dump_register(struct charger_device *chg_dev) { struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); unsigned char i = 0; unsigned int ret = 0; unsigned char sgm41516d_reg[sgm41516d_REG_NUM] = { 0 }; pr_info("[sgm41516d] "); for (i = 0; i < sgm41516d_REG_NUM; i++) { ret = sgm41516d_read_byte(info, i, &sgm41516d_reg[i]); if (ret == 0) { pr_info("[sgm41516d] i2c transfor error\n"); return 1; } pr_info("[0x%x]=0x%x ", i, sgm41516d_reg[i]); } pr_debug("\n"); return 0; } /********************************************************** * * [Internal Function] * *********************************************************/ static int sgm41516d_enable_charging(struct charger_device *chg_dev, bool en) { int status = 0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); pr_info("pass value = %d\n", en); if (en) { /* enable charging */ sgm41516d_set_en_hiz(info, false); sgm41516d_set_chg_config(info, en); } else { /* disable charging */ sgm41516d_set_chg_config(info, en); pr_info("[charging_enable] under test mode: disable charging\n"); } return status; } static int sgm41516d_get_current(struct charger_device *chg_dev, u32 *ichg) { unsigned char ret_val = 0; unsigned int ret = 0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); pr_info("pass value = %d\n", *ichg); /* Get current level */ ret=sgm41516d_read_interface(info, sgm41516d_CON2, &ret_val, CON2_ICHG_MASK, CON2_ICHG_SHIFT); /* Parsing */ *ichg = CS_VTH[ret_val]*10; return ret; } static int sgm41516d_set_current(struct charger_device *chg_dev, u32 current_value) { unsigned int status = true; unsigned int set_chr_current; unsigned int array_size; unsigned int register_value; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); pr_info("&&&& charge_current_value = %d\n", current_value); current_value /= 10; array_size = GETARRAYNUM(CS_VTH); set_chr_current = bmt_find_closest_level(CS_VTH, array_size, current_value); register_value = charging_parameter_to_value(CS_VTH, array_size, set_chr_current); //pr_info("&&&& charge_register_value = %d\n",register_value); pr_info("&&&& %s register_value = %d\n", __func__, register_value); sgm41516d_set_ichg(info, register_value); return status; } static int sgm41516d_get_input_current(struct charger_device *chg_dev, u32 *aicr) { int ret = 0; unsigned char val = 0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); ret=sgm41516d_read_interface(info, sgm41516d_CON0, &val, CON0_IINLIM_MASK, CON0_IINLIM_SHIFT); *aicr = INPUT_CS_VTH[val]*10; pr_info("read value = %d\n", *aicr); return ret; } static int sgm41516d_set_input_current(struct charger_device *chg_dev, u32 current_value) { unsigned int status = true; unsigned int set_chr_current; unsigned int array_size; unsigned int register_value; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); pr_info("pass value = %d\n", current_value); current_value /= 10; pr_info("&&&& current_value = %d\n", current_value); array_size = GETARRAYNUM(INPUT_CS_VTH); set_chr_current = bmt_find_closest_level(INPUT_CS_VTH, array_size, current_value); register_value = charging_parameter_to_value(INPUT_CS_VTH, array_size, set_chr_current); pr_info("&&&& %s register_value = %d\n", __func__, register_value); sgm41516d_set_iinlim(info, register_value); return status; } static int sgm41516d_set_cv_voltage(struct charger_device *chg_dev, u32 cv) { unsigned int status = true; unsigned int array_size; unsigned int set_cv_voltage; unsigned short register_value; unsigned int smg41516_fine=0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); pr_info("pass value = %d\n", cv); array_size = GETARRAYNUM(VBAT_CV_VTH); set_cv_voltage = bmt_find_closest_level(VBAT_CV_VTH, array_size, cv); register_value = charging_parameter_to_value(VBAT_CV_VTH, array_size,set_cv_voltage); if (g_chg_info == SGM41516 || g_chg_info == SGM41516D) { if (cv == SPECIAL_CV_VAL) { register_value=SPECIAL_CV_BIT; smg41516_fine=CV_NORMAL; goto out; } smg41516_fine=((cv-CV_OFFSET)%VBAT_CV_FINE[0])/VBAT_CV_FINE[1]; switch (smg41516_fine) { case 0: smg41516_fine=CV_NORMAL; break; case 1: smg41516_fine=CV_POST_8MV; break; case 2: register_value+=1; smg41516_fine=CV_NEG_16MV; break; case 3: register_value+=1; smg41516_fine=CV_NEG_8MV; break; default: break; } } out: sgm41516d_set_vreg(info, register_value); if (g_chg_info == SGM41516 || g_chg_info == SGM41516D) sgm41516d_set_cv_fine_tuning(info, smg41516_fine); pr_info("cv reg value = %d %d %d smg41516_fine=%d\n", register_value, cv, set_cv_voltage, smg41516_fine); return status; } static int sgm41516d_reset_watch_dog_timer(struct charger_device *chg_dev) { unsigned int status = true; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("charging_reset_watch_dog_timer\n"); sgm41516d_set_wdt_rst(info, true); /* Kick watchdog */ //sgm41516d_set_watchdog(info, WDT_160S); return status; } static int sgm41516d_set_vindpm_voltage(struct charger_device *chg_dev, u32 vindpm) { int status = 0; unsigned int vindpm_os=0; unsigned int set_value=0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); pr_info("pass value = %d\n", vindpm); if(g_chg_info!=SGM41516 && g_chg_info!=SGM41516D) return status; vindpm /= 1000; if(vindpm<=VINDPM_OS0_MIVR_MIN) return status; if(vindpm>=VINDPM_OS3_MIVR_MAX) vindpm=VINDPM_OS3_MIVR_MAX; switch (vindpm) { case 3900 ... 5400: vindpm_os=0; set_value=sgm41516d_closest_reg(VINDPM_OS0_MIVR_MIN, VINDPM_OS0_MIVR_MAX, VINDPM_OS_MIVR_STEP, vindpm); break; case 5500 ... 5800: vindpm_os=0; vindpm=VINDPM_OS0_MIVR_MAX; set_value=sgm41516d_closest_reg(VINDPM_OS0_MIVR_MIN, VINDPM_OS0_MIVR_MAX, VINDPM_OS_MIVR_STEP, vindpm); break; case 5900 ... 7400: vindpm_os=1; set_value=sgm41516d_closest_reg(VINDPM_OS1_MIVR_MIN, VINDPM_OS1_MIVR_MAX, VINDPM_OS_MIVR_STEP, vindpm); break; case 7500 ... 9000: vindpm_os=2; set_value=sgm41516d_closest_reg(VINDPM_OS2_MIVR_MIN, VINDPM_OS2_MIVR_MAX, VINDPM_OS_MIVR_STEP, vindpm); break; case 10000 ... 10400: vindpm_os=2; vindpm=VINDPM_OS2_MIVR_MAX; set_value=sgm41516d_closest_reg(VINDPM_OS2_MIVR_MIN, VINDPM_OS2_MIVR_MAX, VINDPM_OS_MIVR_STEP, vindpm); break; case 10500 ... 12000: vindpm_os=3; set_value=sgm41516d_closest_reg(VINDPM_OS3_MIVR_MIN, VINDPM_OS3_MIVR_MAX, VINDPM_OS_MIVR_STEP, vindpm); break; default: return false; } pr_info("%s vindpm =%d vindpm_os=%d set_value=%d\r\n", __func__, vindpm, vindpm_os, set_value); sgm41516d_set_vindpm_os(info, vindpm_os); sgm41516d_set_vindpm(info, set_value); return status; } static int sgm41516d_get_mivr_state(struct charger_device *chg_dev, bool *in_loop) { unsigned int ret = 0,is_in_vindpm = 0, i = 0; unsigned char val = 0; const int retry_count = 5; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); for (i = 0;i < retry_count; i++){ ret = sgm41516d_read_interface(info, (unsigned char) (sgm41516d_CON10), (&val), (unsigned char) (CON10_VINDPM_STAT_MASK), (unsigned char) (CON10_VINDPM_STAT_SHIFT) ); if (val == 0x1) is_in_vindpm++; } pr_info("pass value = 0x%x\n", val); if (is_in_vindpm >= retry_count-1) return 1; else return 0; } static int sgm41516d_get_mivr(struct charger_device *chg_dev, u32 *uV) { unsigned char val = 0; unsigned char vindpm_bit = 0; unsigned int vindpm_offset = 0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); if(g_chg_info!=SGM41516 && g_chg_info!=SGM41516D) return -1; val=sgm41516d_get_vindpm_os(info); vindpm_bit=sgm41516d_get_vindpm(info); switch (val) { case 0: vindpm_offset=VINDPM_OS0_MIVR_MIN; break; case 1: vindpm_offset=VINDPM_OS1_MIVR_MIN; break; case 2: vindpm_offset=VINDPM_OS2_MIVR_MIN; break; case 3: vindpm_offset=VINDPM_OS3_MIVR_MIN; break; default: return false; }; *uV=(vindpm_offset+VINDPM_OS_MIVR_STEP*vindpm_bit)*1000; pr_info("%s val =%d vindpm_bit=%d vindpm_offset=%d *uV=%d\r\n", __func__, val,vindpm_bit,vindpm_offset,*uV); return true; } static int sgm41516d_get_charging_status(struct charger_device *chg_dev, bool *is_done) { unsigned int status = true; unsigned int ret_val, i; unsigned int is_done_count = 0; const int retry_count = 5; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); for (i = 0;i < retry_count; i++){ ret_val = sgm41516d_get_chrg_stat(info); if (ret_val == 0x3) is_done_count++; } if (is_done_count >= retry_count-1) *is_done = true; else *is_done = false; return status; } static int sgm41516d_enable_otg(struct charger_device *chg_dev, bool en) { int ret = 0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s en = %d\n", __func__, en); if (en) { sgm41516d_set_chg_config(info, false); sgm41516d_set_otg_config(info, en); sgm41516d_set_watchdog(info, DISABLE_WDT); /*default WDT_160S WDT 160s*/ sgm41516d_set_pfm(info, 0x0);//enable pfm } else { sgm41516d_set_otg_config(info, en); sgm41516d_set_chg_config(info, true); sgm41516d_set_watchdog(info, DISABLE_WDT); sgm41516d_set_pfm(info, 0x1);//disable pfm } return ret; } static int sgm41516d_set_boost_current_limit(struct charger_device *chg_dev, u32 uA) { int ret = 0; u32 array_size = 0; u32 boost_ilimit = 0; u8 boost_reg = 0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); pr_info("pass value = %d\n", uA); uA /= 1000; array_size = ARRAY_SIZE(BOOST_CURRENT_LIMIT); boost_ilimit = bmt_find_closest_level(BOOST_CURRENT_LIMIT, array_size, uA); boost_reg = charging_parameter_to_value(BOOST_CURRENT_LIMIT, array_size, boost_ilimit); sgm41516d_set_boost_lim(info, boost_reg); return ret; } static int sgm41516d_enable_safetytimer(struct charger_device *chg_dev, bool en) { int status = 0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); pr_info("pass value = %d\n", en); if (en) sgm41516d_set_en_timer(info, 0x1); else sgm41516d_set_en_timer(info, 0x0); return status; } static int sgm41516d_get_is_safetytimer_enable(struct charger_device *chg_dev, bool *en) { unsigned char val = 0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); sgm41516d_read_interface(info, sgm41516d_CON5, &val, CON5_EN_TIMER_MASK, CON5_EN_TIMER_SHIFT); *en = (bool)val; pr_info("pass value = %d\n", val); return val; } void sgm41516d_force_dpdm_enable(struct sgm41516d_info *info, unsigned int val) { unsigned int ret = 0; ret = sgm41516d_config_interface(info, (unsigned char) (sgm41516d_CON7), (unsigned char) (val), (unsigned char) (CON7_FORCE_DPDM_MASK), (unsigned char) (CON7_FORCE_DPDM_SHIFT) ); } unsigned int sgm41516d_get_iidet_status(struct sgm41516d_info *info) { unsigned char val = 0; unsigned int ret = 0; ret = sgm41516d_read_byte(info, sgm41516d_CON7, &val); val &= (CON7_FORCE_DPDM_MASK << CON7_FORCE_DPDM_SHIFT); val = (val >> CON7_FORCE_DPDM_SHIFT); return val; } static int mtk_ext_chgdet(struct charger_device *chg_dev) { unsigned int usb_status = 0,bq_detect_count=0; unsigned int iidet_bit=1; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); sgm41516d_dump_register(chg_dev); pr_info( "kernel_sgm41516d_chg_type detect]\n"); Charger_Detect_Init(); bq_chg_type = CHARGER_UNKNOWN; sgm41516d_force_dpdm_enable(info, 1); //usb_status = sgm41516d_get_vbus_stat(); //pr_info("%s: usb_stats1 = 0x%X\n", __func__, usb_status); do{ msleep(50); iidet_bit =sgm41516d_get_iidet_status(info); bq_detect_count++; pr_info("%s: count_max=%d,iidet_bit=%d,usb_status =%d\n", __func__,bq_detect_count,iidet_bit, sgm41516d_get_vbus_stat(info)); if(bq_detect_count>BQ_DET_COUNT_MAX) iidet_bit = 0; }while (iidet_bit); usb_status = sgm41516d_get_vbus_stat(info); pr_info("%s: usb_stats2 = 0x%X\n", __func__, usb_status); switch (usb_status) { case SGM41516D_CHG_TYPE_SDP: bq_chg_type = STANDARD_HOST; break; case SGM41516D_CHG_TYPE_UNKNOWN: bq_chg_type = NONSTANDARD_CHARGER; break; case SGM41516D_CHG_TYPE_CDP: bq_chg_type = CHARGING_HOST; break; case SGM41516D_CHG_TYPE_DCP: bq_chg_type = STANDARD_CHARGER; break; case SGM41516D_CHG_TYPE_SDPNSTD: bq_chg_type = APPLE_2_1A_CHARGER; break; default: bq_chg_type = CHARGER_UNKNOWN; break; } pr_info("%s: bq_chg_type = %d\n", __func__, bq_chg_type); Charger_Detect_Release(); return bq_chg_type; } static int sgm41516d_set_term_current(struct charger_device *chg_dev, u32 current_value) { struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); unsigned int iterm_reg = 0x2; pr_info("%s enter!\n", __func__); pr_info("pass value = %d\n", current_value); if (current_value > 960000) iterm_reg = 0xF; /*Termination current = 960ma */ else if (current_value < 60000) iterm_reg = 0x0; /*Termination current = 60ma */ else iterm_reg = (current_value/60000)-1; sgm41516d_set_iterm(info, iterm_reg); return 0; } static unsigned int charging_hw_init(struct sgm41516d_info *info) { unsigned int status = 0; sgm41516d_set_en_hiz(info, false); sgm41516d_set_vindpm(info, 0x6); /* VIN DPM check 4.5V */ sgm41516d_set_wdt_rst(info, true); /* Kick watchdog */ sgm41516d_set_sys_min(info, 0x5); /* Minimum system voltage 3.5V */ sgm41516d_set_iprechg(info, 0x8); /* Precharge current 540mA */ sgm41516d_set_iterm(info, 0x2); /* Termination current 180mA */ sgm41516d_set_vreg(info, 0x11); /* VREG 4.4V */ sgm41516d_set_pfm(info, 0x1);//disable pfm sgm41516d_set_rdson(info, 0x0); /*close rdson*/ sgm41516d_set_batlowv(info, 0x1); /* BATLOWV 3.0V */ sgm41516d_set_vrechg(info, 0x0); /* VRECHG 0.1V */ sgm41516d_set_en_term(info, 0x1); /* Enable termination */ sgm41516d_set_watchdog(info, 0x3); /* WDT 160s */ sgm41516d_set_en_timer(info, 0x1); /* Enable charge timer */ sgm41516d_set_int_mask(info, 0x0); /* Disable fault interrupt */ sgm41516d_set_batfet_reset_enable(info, 0x0);/*disable batfet*/ #if defined(CONFIG_MTK_PUMP_EXPRESS_PLUS_SUPPORT) \ || defined(CONFIG_MTK_PUMP_EXPRESS_PLUS_20_SUPPORT) sgm41516d_set_ovp(info, 0x2); sgm41516d_set_chg_thermal(info, 0x1);//SGM41516 thermal to 120 #endif sgm41516d_set_boostv(info,0x3); pr_info("%s: hw_init down!\n", __func__); return status; } static int sgm41516d_parse_dt(struct sgm41516d_info *info, struct device *dev) { int ret = 0; struct device_node *np = dev->of_node; //int sgm41516d_en_pin = 0; pr_info("%s\n", __func__); if (!np) { pr_info("%s: no of node\n", __func__); return -ENODEV; } if (of_property_read_string(np, "charger_name", &info->chg_dev_name) < 0) { info->chg_dev_name = "primary_chg"; pr_info("%s: no charger name\n", __func__); } if (of_property_read_string(np, "alias_name", &(info->chg_props.alias_name)) < 0) { info->chg_props.alias_name = "sgm41516d"; pr_info("%s: no alias name\n", __func__); } if (strcmp(info->chg_dev_name, "primary_chg") == 0) { led_ctl_gpio = of_get_named_gpio(np, "led_ctl_gpio", 0); if (led_ctl_gpio < 0) { led_ctl_gpio = U32_MAX; pr_info("%s led_ctl_gpio=%d get fail\n", __func__,led_ctl_gpio); } if(led_ctl_gpio != U32_MAX) { ret = gpio_request(led_ctl_gpio, "led_ctl_gpio"); if (ret < 0) pr_info("[%s]:led gpio request failed!\n", __func__); gpio_direction_output(led_ctl_gpio, 1); } } return 0; } static int sgm41516d_do_event(struct charger_device *chg_dev, u32 event, u32 args) { if (chg_dev == NULL) return -EINVAL; pr_info("%s: event = %d\n", __func__, event); switch (event) { case EVENT_EOC: charger_dev_notify(chg_dev, CHARGER_DEV_NOTIFY_EOC); break; case EVENT_RECHARGE: charger_dev_notify(chg_dev, CHARGER_DEV_NOTIFY_RECHG); break; default: break; } return 0; } static int sgm41516d_set_pep20_efficiency_table(struct charger_device *chg_dev) { struct charger_manager *chg_mgr = NULL; chg_mgr = charger_dev_get_drvdata(chg_dev); if (!chg_mgr) return -EINVAL; chg_mgr->pe2.profile[0].vchr = 9000000; chg_mgr->pe2.profile[1].vchr = 9000000; chg_mgr->pe2.profile[2].vchr = 9000000; chg_mgr->pe2.profile[3].vchr = 9000000; chg_mgr->pe2.profile[4].vchr = 9000000; chg_mgr->pe2.profile[5].vchr = 9000000; chg_mgr->pe2.profile[6].vchr = 9000000; chg_mgr->pe2.profile[7].vchr = 9000000; chg_mgr->pe2.profile[8].vchr = 9000000; chg_mgr->pe2.profile[9].vchr = 9000000; return 0; } struct timespec ptime[13]; static int cptime[13][2]; static int dtime(int i) { struct timespec time; time = timespec_sub(ptime[i], ptime[i-1]); return time.tv_nsec/1000000; } #define PEOFFTIME 40 #define PEONTIME 90 static int sgm41516d_set_pep20_current_pattern(struct charger_device *chg_dev, u32 uV) { int value; int i, j = 0; int flag; int errcnt = 0; sgm41516d_set_current(chg_dev,2000000); // bq25892_set_ico_en_start(0); mdelay(20); value = (uV - 5500000) / 500000; sgm41516d_set_input_current(chg_dev,0); mdelay(150); get_monotonic_boottime(&ptime[j++]); for (i = 4; i >= 0; i--) { flag = value & (1 << i); if (flag == 0) { sgm41516d_set_input_current(chg_dev,800000); mdelay(PEOFFTIME); get_monotonic_boottime(&ptime[j]); cptime[j][0] = PEOFFTIME; cptime[j][1] = dtime(j); if (cptime[j][1] < 30 || cptime[j][1] > 65) { errcnt = 1; return -1; } j++; sgm41516d_set_input_current(chg_dev,0); mdelay(PEONTIME); get_monotonic_boottime(&ptime[j]); cptime[j][0] = PEONTIME; cptime[j][1] = dtime(j); if (cptime[j][1] < 90 || cptime[j][1] > 115) { errcnt = 1; return -1; } j++; } else { sgm41516d_set_input_current(chg_dev,800000); mdelay(PEONTIME); get_monotonic_boottime(&ptime[j]); cptime[j][0] = PEONTIME; cptime[j][1] = dtime(j); if (cptime[j][1] < 90 || cptime[j][1] > 115) { errcnt = 1; return -1; } j++; sgm41516d_set_input_current(chg_dev,0); mdelay(PEOFFTIME); get_monotonic_boottime(&ptime[j]); cptime[j][0] = PEOFFTIME; cptime[j][1] = dtime(j); if (cptime[j][1] < 30 || cptime[j][1] > 65) { errcnt = 1; return -1; } j++; } } sgm41516d_set_input_current(chg_dev,800000); mdelay(200); get_monotonic_boottime(&ptime[j]); cptime[j][0] = 200; cptime[j][1] = dtime(j); if (cptime[j][1] < 180 || cptime[j][1] > 240) { errcnt = 1; return -1; } j++; sgm41516d_set_input_current(chg_dev,0); mdelay(150); sgm41516d_set_input_current(chg_dev,800000); //sgm41516d_set_input_current(chg_dev,2000000); mdelay(300); if (errcnt == 0) return 0; return -1; } int sgm41516d_enable_hiz_mode(struct charger_device *chg_dev, bool enable) { int status = 0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); pr_info("pass value = %d\n", enable); sgm41516d_set_en_hiz(info, enable); return status; } int sgm41516d_get_dev_id(struct sgm41516d_info *info) { unsigned int ret = 0; unsigned char val = 0; ret = sgm41516d_read_interface(info, (unsigned char) (sgm41516d_CON11), (&val), (unsigned char) (CON11_PN_MASK), (unsigned char) (CON11_PN_SHIFT) ); return val; } static int sgm41516d_reset_ta(struct charger_device *chg_dev) { pr_info("%s enter!\n", __func__); sgm41516d_set_current(chg_dev,800000); //512mA sgm41516d_set_input_current(chg_dev,0); msleep(250);//250ms sgm41516d_set_input_current(chg_dev,2000000); return 1; } static int sgm41516d_plug_in(struct charger_device *chg_dev) { int ret = 0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); sgm41516d_set_watchdog(info, WDT_160S); ret = sgm41516d_enable_charging(chg_dev, true); if (ret < 0) { pr_info( "%s en fail(%d)\n", __func__, ret); return ret; } return 0; } static int sgm41516d_plug_out(struct charger_device *chg_dev) { int ret = 0; struct sgm41516d_info *info = dev_get_drvdata(&chg_dev->dev); pr_info("%s enter!\n", __func__); /* Disable charging */ ret = sgm41516d_enable_charging(chg_dev, false); if (ret < 0) { pr_info("%s en chg fail(%d)\n", __func__, ret); return ret; } ret = sgm41516d_enable_hiz_mode(chg_dev, false); if (ret < 0) pr_info("%s en hz fail(%d)\n", __func__, ret); /* Disable WDT */ sgm41516d_set_watchdog(info, DISABLE_WDT); #if defined(CONFIG_MTK_HVDCP20_SUPPORT)||defined(CONFIG_MTK_TC30_SUPPORT) sgm41516d_set_dp_dm_recover(chg_dev); #endif return 0; } #if defined(CONFIG_MTK_PUMP_EXPRESS_50_SUPPORT) static int sgm41516d_get_pwr_rdy_status(struct charger_device *chg_dev) { pr_info("%s\n", __func__); return true; } #endif static struct charger_ops sgm41516d_chg_ops = { .plug_in = sgm41516d_plug_in, .plug_out = sgm41516d_plug_out, /* Normal charging */ .set_eoc_current = sgm41516d_set_term_current, .dump_registers = sgm41516d_dump_register, .enable = sgm41516d_enable_charging, .get_charging_current = sgm41516d_get_current, .set_charging_current = sgm41516d_set_current, .get_input_current = sgm41516d_get_input_current, .set_input_current = sgm41516d_set_input_current, /*.get_constant_voltage = sgm41516d_get_battery_voreg,*/ .set_constant_voltage = sgm41516d_set_cv_voltage, .kick_wdt = sgm41516d_reset_watch_dog_timer, .set_mivr = sgm41516d_set_vindpm_voltage, .get_mivr = sgm41516d_get_mivr, .get_mivr_state = sgm41516d_get_mivr_state, .is_charging_done = sgm41516d_get_charging_status, /* Safety timer */ .enable_safety_timer = sgm41516d_enable_safetytimer, .is_safety_timer_enabled = sgm41516d_get_is_safetytimer_enable, /* PE+20/HVDCP */ .set_pe20_efficiency_table = sgm41516d_set_pep20_efficiency_table, .send_ta20_current_pattern = sgm41516d_set_pep20_current_pattern, .reset_ta = sgm41516d_reset_ta, #if defined(CONFIG_MTK_HVDCP20_SUPPORT) /*.set_dm_0_6_v = sgm41516d_hvdcp20_set_dm_0_6_v,*/ /*.set_dp_0_6_v = sgm41516d_hvdcp20_set_dp_0_6_v,*/ /*.set_dp_3_0_v = sgm41516d_hvdcp20_set_dp_3_0_v,*/ /*.set_dp_dm_recover = sgm41516d_set_dp_dm_recover,*/ #endif #if defined(CONFIG_MTK_TC30_SUPPORT) /*.set_tc30_gpio_enable = sgm41516d_tc30_enable,*/ #endif .enable_hz = sgm41516d_enable_hiz_mode, .enable_powerpath = sgm41516d_enable_hiz_mode, .get_ext_chgtyp = mtk_ext_chgdet, #if defined(CONFIG_MTK_PUMP_EXPRESS_50_SUPPORT) .get_pwr_rdy_status = sgm41516d_get_pwr_rdy_status, #endif /* OTG */ .enable_otg = sgm41516d_enable_otg, .set_boost_current_limit = sgm41516d_set_boost_current_limit, .event = sgm41516d_do_event, /*LED*/ /*.set_stat_pin = sgm41516d_set_stat_pin_val,*/ }; static int sgm41516d_hw_component_detect(struct sgm41516d_info *info) { unsigned char vendor_id = 0; vendor_id = sgm41516d_get_dev_id(info); pr_info("sgm41516d vendor_id=%d!!!\r\n",vendor_id); switch (vendor_id) { case bq25600d_VENDOR_ID: g_chg_info=BQ25601D; break; case bq25601_VENDOR_ID: g_chg_info=BQ25601; break; case SGM41516_VENDOR_ID: g_chg_info=SGM41516; break; case SGM41516D_VENDOR_ID: g_chg_info=SGM41516D; break; default: return false; } pr_info("sgm41516d g_chg_info=%d!!!\r\n",g_chg_info); return true; } static int sgm41516d_driver_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret = 0; struct sgm41516d_info *info = NULL; pr_info("[%s]\n", __func__); info = devm_kzalloc(&client->dev, sizeof(struct sgm41516d_info), GFP_KERNEL); if (!info) return -ENOMEM; info->dev = &client->dev; info->client = client; i2c_set_clientdata(client, info); mutex_init(&info->sgm41516d_i2c_access); mutex_init(&info->sgm41516d_access_lock); ret = sgm41516d_parse_dt(info, &client->dev); if (ret < 0) goto err_parse_dt; if(!sgm41516d_hw_component_detect(info)){ pr_info("sgm41516d component not exist!!!\r\n"); goto err_parse_dt; } ret = charging_hw_init(info); if (ret < 0) goto err_parse_dt; /* Register charger device */ info->chg_dev = charger_device_register(info->chg_dev_name, &client->dev, info, &sgm41516d_chg_ops, &info->chg_props); if (IS_ERR_OR_NULL(info->chg_dev)) { pr_info("%s: register charger device failed\n", __func__); ret = PTR_ERR(info->chg_dev); goto err_register_chg_dev; } ret = device_create_file(&client->dev, &dev_attr_sgm41516d_access); if (ret < 0) { pr_info( "%s create file fail(%d)\n", __func__, ret); goto err_create_file; } sgm41516d_dump_register(info->chg_dev); return 0; err_create_file: err_register_chg_dev: charger_device_unregister(info->chg_dev); err_parse_dt: mutex_destroy(&info->sgm41516d_i2c_access); mutex_destroy(&info->sgm41516d_access_lock); devm_kfree(info->dev, info); return ret; } #ifdef CONFIG_OF static const struct of_device_id sgm41516d_of_match[] = { {.compatible = "mediatek,sgm41516d"}, {}, }; #else static struct i2c_board_info i2c_sgm41516d __initdata = { I2C_BOARD_INFO("sgm41516d", (sgm41516d_SLAVE_ADDR_WRITE >> 1)) }; #endif static struct i2c_driver sgm41516d_driver = { .driver = { .name = "sgm41516d", .owner = THIS_MODULE, #ifdef CONFIG_OF .of_match_table = sgm41516d_of_match, #endif }, .probe = sgm41516d_driver_probe, .id_table = sgm41516d_i2c_id, }; static int __init sgm41516d_init(void) { //int ret = 0; /* i2c registeration using DTS instead of boardinfo*/ #ifdef CONFIG_OF pr_info("[%s] init start with i2c DTS", __func__); #else pr_info("[%s] init start. ch=%d\n", __func__, sgm41516d_BUSNUM); i2c_register_board_info(sgm41516d_BUSNUM, &i2c_sgm41516d, 1); #endif if (i2c_add_driver(&sgm41516d_driver) != 0) { pr_info( "[%s] failed to register sgm41516d i2c driver.\n", __func__); } else { pr_info( "[%s] Success to register sgm41516d i2c driver.\n", __func__); } return 0; } static void __exit sgm41516d_exit(void) { i2c_del_driver(&sgm41516d_driver); } module_init(sgm41516d_init); module_exit(sgm41516d_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("I2C sgm41516d Driver"); MODULE_AUTHOR("will cai ");