/** * @file wireless_power_mt5728.c * @author * @date Mon May 13 18:11:39 2019 * @brief Maxictech Proprietary and Confidential * Copyright (c) 2017 Maxic Technology Corporation * All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wireless_power_mt5728.h" #ifdef MT5728_USE_WAKELOCK // #include #include #endif #include #include #include #include #include // #include #include #include "mtk_charger_intf.h" #include "mtk_intf.h" //#include #include "MT5728_mtp_array.h" #include extern bool otg_plugin; #define DEVICE_NAME "mt5728" #define DRIVER_FIRMWARE_VERSION "0.0.1" #define FALSE 0 #define TRUE 1 #define UP_MT5728_WIRELESS_TRX_MODE_SWITCH 1 #define UP_MT5728_WIRELESS_ADD_OTG 1 #ifdef __CUST_REVERSE_TIMEOUT_CNT__ #define REVERSE_TIMEOUT_CNT __CUST_REVERSE_TIMEOUT_CNT__ #else #define REVERSE_TIMEOUT_CNT 1200 #endif static bool vbat_temp_hot = false; int wls_work_online = 0; //wireless charge working int reserse_charge_online = 0; int usb_otg_online = 0; int usbchip_otg_detect = 0; // int MT5728_rt_mode_n = 0; //GPOD5,rxmode - 0 volatile unsigned int AfcSendTimeout; //afc vout set time out volatile unsigned char AfcIntFlag; volatile int rx_vout_max = 5000; volatile int rx_iout_max = 1000; volatile int rx_iout_limit = 100; #ifdef MT5728_USE_WAKELOCK // static struct wake_lock mt5728_wls_wake_lock; static struct wakeup_source mt5728_wls_wake_lock; #endif static int cur_last = 0; static int last_ichg = 0; static int powergood_err_cnt = 0; volatile int setup_iout_start_cnt = 0; volatile int reverse_timeout_cnt = 0; volatile int current_change_interval_cnt = 0; static int mt5728_charging_otgin_flag = 0; static int input_current_limit = 0; #ifndef MT5728_CHIP_AUTO_AFC9V static volatile int vout_change_timecnt = 0; #endif static volatile int current_reduce_flag = 0; static int mt5728_vout_old; static int mt5728_vrect_old; static int mt5728_mtp_write_flag; static volatile int mt5728_epp_ctrl_vout_flag; static int mt5728_ldo_on_flag; static bool first_boot = true; static struct charger_device *primary_charger; static volatile int mt5728_epp_ptpower; static unsigned int boot_mode; #ifdef CONFIG_FG_SD77561 extern struct charger_manager *pinfo; #endif static volatile int mt5728_tx_ping_cnt = 0; static volatile int mt5728_tx_cep_cnt_old = 0; static volatile int mt5728_tx_cep_cnt_timeout = 0; typedef struct { unsigned char G; s8 Offs; } FodType; FodType mt5728fod[8]={{250,127},{250,127},{250,127},{250,127},{250,127},{250,127},{250,127},{250,127}}; typedef union { u16 value; u8 ptr[2]; } vuc; struct mt5728_dev *mte = NULL, *chip = NULL; //struct delayed_work MT5728_int_delayed_work; typedef struct Pgm_Type { u16 status; u16 addr; u16 length; u16 cs; u8 data[MTP_BLOCK_SIZE]; } Pgm_Type_t; struct pinctrl* mt5728_pinctrl; struct mt5728_func { int (*read)(struct mt5728_dev* di, u16 reg, u8* val); int (*write)(struct mt5728_dev* di, u16 reg, u8 val); int (*read_buf)(struct mt5728_dev* di, u16 reg, u8* buf, unsig32 size); int (*write_buf)(struct mt5728_dev* di, u16 reg, u8* buf, unsig32 size); }; struct mt5728_dev { char *name; struct i2c_client* client; struct device* dev; struct regmap* regmap; struct mt5728_func bus; unsigned int state; int resume_vbatt; int powergood_gpio; int online_back; int irq_gpio; struct device_node *irq_nd; /* node */ int charger_en_gpio; //int otg_ctrl_gpio; int ldo_ctrl_gpio; int trx_mode_gpio; int chip_en_gpio; int chip_en; int rxdetect_flag; int rxremove_flag; int wls_afc_cmd; struct mutex slock; struct delayed_work eint_work; struct delayed_work charger_work; struct delayed_work reverse_charge_work; //struct delayed_work mt5728_fwcheck_work; int ldo_status; int is_samsung_charge; struct device_node* irq_node1; int fsk_status; int otg_flag; int otp_flag; int reverse_charger; //power_supply struct power_supply *wl_psy; struct power_supply *batt_psy; struct power_supply *usb_psy; struct power_supply *dc_psy; struct power_supply_desc wl_psd; struct power_supply_config wl_psc; struct power_supply_desc batt_psd; struct power_supply_desc usb_psd; struct power_supply_desc dc_psd; }; int wls_work_int_pin = 0; #define REG_NONE_ACCESS 0 #define REG_RD_ACCESS (1 << 0) #define REG_WR_ACCESS (1 << 1) #define REG_BIT_ACCESS (1 << 2) #define REG_MAX 0x0F struct reg_attr { const char* name; u16 addr; u8 flag; }; enum REG_INDEX { CHIPID = 0, FWVERSION, VOUT, INT_FLAG, INTCTLR, VOUTSET, VFC, CMD, RXPERI, TXPERI, INDEX_MAX, }; static struct reg_attr reg_access[INDEX_MAX]={ [CHIPID] = { "CHIPID", REG_CHIPID, REG_RD_ACCESS }, [FWVERSION] = { "FWVERSION", REG_FW_VER, REG_RD_ACCESS }, [VOUT] = { "VOUT", REG_VOUT, REG_RD_ACCESS }, [INT_FLAG] = { "INT_FLAG", REG_INTFLAG, REG_RD_ACCESS }, [INTCTLR] = { "INTCLR", REG_INTCLR, REG_WR_ACCESS }, [VOUTSET] = { "VOUTSET", REG_VOUTSET, REG_RD_ACCESS | REG_WR_ACCESS }, [VFC] = { "VFC", REG_VFC, REG_RD_ACCESS | REG_WR_ACCESS }, [CMD] = { "CMD", REG_CMD, REG_RD_ACCESS | REG_WR_ACCESS | REG_BIT_ACCESS }, [RXPERI] = { "RXPERI", REG_RXD_PERI, REG_RD_ACCESS | REG_WR_ACCESS | REG_BIT_ACCESS }, [TXPERI] = { "TXPERI", RED_TXD_PERI, REG_RD_ACCESS | REG_WR_ACCESS | REG_BIT_ACCESS }, }; #define SET_GPIO_WIRELESS_EN_H() gpio_set_value(chip->chip_en_gpio, 1) #define SET_GPIO_WIRELESS_EN_L() gpio_set_value(chip->chip_en_gpio, 0) #define GPIO_WIRELESS_EN_INPUT() gpio_direction_input(chip->chip_en_gpio) #define GPIO_WIRELESS_EN_OUTPUT() gpio_direction_output(chip->chip_en_gpio, 0) #define GET_GPIO_WIRELESS_EN() gpio_get_value(chip->chip_en_gpio) #if 0 #define SET_GPIO_OTG_CTRL_H() gpio_set_value(chip->otg_ctrl_gpio, 1) #define SET_GPIO_OTG_CTRL_L() gpio_set_value(chip->otg_ctrl_gpio, 0) #define GPIO_OTG_CTRL_INPUT() gpio_direction_input(chip->otg_ctrl_gpio) #define GPIO_OTG_CTRL_OUTPUT() gpio_direction_output(chip->otg_ctrl_gpio, 0) #endif #define SET_GPIO_WPC_RTMODE_H() gpio_set_value(chip->trx_mode_gpio, 1) #define SET_GPIO_WPC_RTMODE_L() gpio_set_value(chip->trx_mode_gpio, 0) #define GPIO_WPC_RTMODE_OUTPUT() gpio_direction_output(chip->trx_mode_gpio, 0) #define SET_GPIO_LDO_CTRL_H() /*gpio_set_value(mte->ldo_ctrl_gpio, 1)*/ #define SET_GPIO_LDO_CTRL_L() /*gpio_set_value(mte->ldo_ctrl_gpio, 0) */ #define GPIO_LDO_CTRL_OUTPUT() gpio_direction_output(chip->ldo_ctrl_gpio, 0) #define GET_GPIO_POWERGOOD() gpio_get_value(chip->powergood_gpio) #define GPIO_POWERGOOD_INPUT() gpio_direction_input(chip->powergood_gpio) #if 0 void mt5728_otgen_gpio_ctrl(bool en) { if (en){ GPIO_OTG_CTRL_OUTPUT(); SET_GPIO_OTG_CTRL_L(); } else{ GPIO_OTG_CTRL_INPUT(); } } #endif static ssize_t Mt5728_set_vout(struct device* cd, struct device_attribute *attr, const char* buf, size_t len); static ssize_t brushfirmware(struct device* dev, struct device_attribute* attr, const char* buf, size_t count); static ssize_t get_reg(struct device* cd, struct device_attribute* attr, char* buf); static ssize_t set_reg(struct device* cd, struct device_attribute* attr, const char* buf, size_t len); int Get_adaptertype(void); static ssize_t get_adapter(struct device* cd, struct device_attribute* attr, char* buf); static void mt5728_set_pmic_input_current_ichg(int input_current); static ssize_t fast_charging_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count); static ssize_t updata_txFW(struct device* cd, struct device_attribute* attr, char* buf); static ssize_t mt5728_get_reverse_charger(struct device* cd,struct device_attribute *attr, char* buf); static ssize_t mt5728_set_reverse_charger(struct device* cd, struct device_attribute *attr, const char* buf, size_t len); static ssize_t mt5728_get_otg(struct device* cd,struct device_attribute *attr, char* buf); static ssize_t mt5728_set_otg(struct device* cd, struct device_attribute *attr, const char* buf, size_t len); static ssize_t mt5728_get_hwen(struct device* cd,struct device_attribute *attr, char* buf); static ssize_t mt5728_set_hwen(struct device* cd, struct device_attribute *attr, const char* buf, size_t len); static ssize_t mt5728_show_flag(struct device* cd,struct device_attribute *attr, char* buf); static ssize_t mt5728_set_flag(struct device* cd, struct device_attribute *attr, const char* buf, size_t len); static ssize_t Mt5728_set_vout(struct device* cd, struct device_attribute *attr, const char* buf, size_t len); static ssize_t mt5728_get_rxdetect(struct device* cd,struct device_attribute *attr, char* buf); void fast_vfc(int vol); static DEVICE_ATTR(fast_charging, S_IRUGO | S_IWUSR, NULL, fast_charging_store); static DEVICE_ATTR(reverse_charger, 0660, mt5728_get_reverse_charger, mt5728_set_reverse_charger); static DEVICE_ATTR(otg, 0660, mt5728_get_otg, mt5728_set_otg); // static DEVICE_ATTR(otp, 0660, mt5728_get_otp, mt5728_set_otp); static DEVICE_ATTR(mt5728_en, 0660, mt5728_get_hwen, mt5728_set_hwen); static DEVICE_ATTR(mt5728_flag, 0660, mt5728_show_flag, mt5728_set_flag); static DEVICE_ATTR(epp_set_vout, 0660, NULL, Mt5728_set_vout); static DEVICE_ATTR(rxdetect, 0660, mt5728_get_rxdetect, NULL); static DEVICE_ATTR(reg, 0660, get_reg, set_reg); static DEVICE_ATTR(adapter_type,S_IRUGO | S_IWUSR,get_adapter,NULL); static DEVICE_ATTR(brushFW,S_IRUGO | S_IWUSR,NULL,brushfirmware); static DEVICE_ATTR(TxFirmware,S_IRUGO | S_IWUSR,updata_txFW,NULL); static ssize_t Mt5728_get_vout(void); static u16 crc_ccitt(u16 crc, u8 const *buffer, size_t len); static inline u16 crc_firmware(u16 poly1, u16 sed, u8 *buf, u32 n); static void ech_wls_chrg_set_psy_info(struct power_supply *psy, enum power_supply_property psp, int data); static int set_charger_type(void); // static int ech_wls_chrg_get_psy_info(struct power_supply *psy, // enum power_supply_property psp, int *data); static int ech_wls_chrg_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val); extern void mt_usb_vbus_on(int delay); extern void mt_usb_vbus_off(int delay); static void mt5728_ap_open_otg_boost (bool en) { //mt_usb_vbus_on(10); // charger_dev_enable_otg(primary_charger, en); if(en){ mt_usb_vbus_on(0); } else{ mt_usb_vbus_off(0); } } static void wireless_charge_wake_lock(void) { if (!mt5728_wls_wake_lock.active) { __pm_stay_awake(&mt5728_wls_wake_lock); } } static void wireless_charge_wake_unlock(void) { if (mt5728_wls_wake_lock.active) { __pm_relax(&mt5728_wls_wake_lock); } } /** * [mt5728_display_slow_wls_icon android java app display slow wireless icon] * @param en [1:display ,0:put out] */ static void mt5728_display_slow_wls_icon(int en) { if(en) { set_charger_type(); printk(KERN_ALERT "[%s] mt5728_display_slow_wls_icon on\n", __func__); } else { set_charger_type(); printk(KERN_ALERT "[%s] mt5728_display_slow_wls_icon off\n", __func__); } } /** * [mt5728_display_qucik_wls_icon android java app display quick wireless icon] * @param en [1:display ,0:put out] */ static void mt5728_display_qucik_wls_icon(int en) { if(en) { printk(KERN_ALERT "[%s] mt5728_display_qucik_wls_icon on\n", __func__); } else { printk(KERN_ALERT "[%s] mt5728_display_qucik_wls_icon off\n", __func__); } } /** * [mt5728_is_otg_plugin] * @return [1:otg in ,0:otg out] */ //extern up_otg_flag; int mt5728_is_otg_plugin(bool otg_plugin) { return otg_plugin;//up_otg_flag;//usb_otg_online && rt1711_otg_detect; } void ech_wls_set_chrg_current(int input_current, int cur) { // if(input_current == cur_last){ // return; // } ech_wls_chrg_set_psy_info(chip->wl_psy, POWER_SUPPLY_PROP_CURRENT_MAX, input_current); ech_wls_chrg_set_psy_info(chip->wl_psy, POWER_SUPPLY_PROP_CURRENT_NOW, cur); //ech_wls_log(ECH_LOG_DEBG, "[%s] Set Input Current: %d mA.\n", __func__, cur); // cur_last = input_current; } /** * [mt5728_set_pmic_input_current_ichg description] * @param input_current [mA] */ static void mt5728_set_pmic_input_current_ichg(int input_current) { printk(KERN_ALERT "%s() input_current:%d, charger_current:%d\n", __func__,input_current, chrg_info.charger_current); if((input_current == cur_last)&&(chrg_info.charger_current == last_ichg)){ return; } primary_charger = get_charger_by_name("primary_chg"); if (!primary_charger) { pr_notice("%s: get primary charger device failed\n", __func__); } //customer realizes PMIC input current control here // ech_wls_chrg_set_psy_info(chip->wl_psy, POWER_SUPPLY_PROP_CURRENT_MAX, input_current); // ech_wls_chrg_set_psy_info(chip->wl_psy, POWER_SUPPLY_PROP_CURRENT_NOW, input_current); chrg_info.input_current_max = input_current; ech_wls_set_chrg_current(chrg_info.input_current_max,chrg_info.charger_current); charger_dev_set_input_current(primary_charger,input_current * 1000); charger_dev_set_charging_current(primary_charger,chrg_info.charger_current * 1000); printk(KERN_ALERT "%s() set input_current:%d, charger_current:%d\n", __func__,input_current, chrg_info.charger_current); cur_last = input_current; last_ichg = chrg_info.charger_current; } // static int cur_last = 0; static void ech_wls_chrg_set_psy_info(struct power_supply *psy, enum power_supply_property psp, int data) { union power_supply_propval val; int ret; val.intval = data; ret = power_supply_set_property(psy, psp, &val); if(ret < 0){ ech_wls_log(ECH_LOG_ERR, "set psy failed.\n"); } } int is_wireless_chipen_pin_high(void) { return GET_GPIO_WIRELESS_EN(); } static enum power_supply_property ech_wls_chrg_props[] = { POWER_SUPPLY_PROP_CURRENT_MAX, // POWER_SUPPLY_PROP_CHARGING_ENABLED, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, // POWER_SUPPLY_PROP_VOUT_NOW, // POWER_SUPPLY_PROP_VRECT, // POWER_SUPPLY_PROP_IRECT, // POWER_SUPPLY_PROP_TEMP_NTC, // POWER_SUPPLY_PROP_TEMP_WARM_NTC, // POWER_SUPPLY_PROP_TEMP_HOT_NTC, // POWER_SUPPLY_PROP_TEMP_WARM_BATTERY, // POWER_SUPPLY_PROP_TEMP_HOT_BATTERY, POWER_SUPPLY_PROP_CALL_STATUS, POWER_SUPPLY_PROP_CURRENT_NOW, }; static int ech_wls_chrg_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { printk(KERN_ALERT, "[%s] psp = %d.\n", __func__, psp); switch(psp){ case POWER_SUPPLY_PROP_CURRENT_MAX: power_supply_changed(chip->wl_psy); break; case POWER_SUPPLY_PROP_CURRENT_NOW: power_supply_changed(chip->wl_psy); break; case POWER_SUPPLY_PROP_CHARGING_ENABLED: //enable_charging(val->intval); power_supply_changed(chip->wl_psy); break; case POWER_SUPPLY_PROP_CALL_STATUS: if(val->intval > 0) chrg_info.call_status = 1; else chrg_info.call_status = 0; break; default: return -EPERM; } return 0; } static int ech_wls_chrg_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { ech_wls_log(ECH_LOG_DEBG, "[%s] psp = %d.\n", __func__, psp); //if (!psy || !(&psy->dev) || !val) { // ech_wls_log(ECH_LOG_ERR, "[%s] Not find any psy!\n", __func__); // return -ENODEV; // } val->intval = 0; switch (psp) { case POWER_SUPPLY_PROP_CURRENT_MAX: val->intval = chrg_info.input_current_max; break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = chrg_info.charger_current; break; case POWER_SUPPLY_PROP_CHARGING_ENABLED: val->intval = chrg_info.work_en; break; case POWER_SUPPLY_PROP_PRESENT: // val->intval = chrg_info.wls_online; val->intval = wls_work_online; break; case POWER_SUPPLY_PROP_ONLINE: // val->intval = chrg_info.wls_online; printk("wls_work_online = %d\n",wls_work_online); val->intval = wls_work_online; //modify break; /* case POWER_SUPPLY_PROP_VOUT_NOW: val->intval = chrg_info.vout; break; case POWER_SUPPLY_PROP_VRECT: val->intval = chrg_info.vrect; break; case POWER_SUPPLY_PROP_IRECT: val->intval = chrg_info.irect; break; case POWER_SUPPLY_PROP_TEMP_NTC: val->intval = chrg_info.temp_ntc; break; case POWER_SUPPLY_PROP_TEMP_WARM_NTC: //val->intval = chip->temp_warm_ntc; break; case POWER_SUPPLY_PROP_TEMP_HOT_NTC: //val->intval = chip->temp_hot_ntc; break; case POWER_SUPPLY_PROP_TEMP_WARM_BATTERY: //val->intval = chip->temp_warm_battery; break; case POWER_SUPPLY_PROP_TEMP_HOT_BATTERY: //val->intval = chip->temp_hot_battery; break; */ case POWER_SUPPLY_PROP_CALL_STATUS: val->intval = chrg_info.call_status; break; default: return -EINVAL; } return 0; } static void ech_wls_charger_external_power_changed(struct power_supply *psy) { printk(KERN_ALERT "ech_wls_charger_external_power_changed.\n"); return; } static int ech_wls_chrg_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp){ case POWER_SUPPLY_PROP_CURRENT_MAX: // case POWER_SUPPLY_PROP_CHARGING_ENABLED: // case POWER_SUPPLY_PROP_TEMP_WARM_NTC: // case POWER_SUPPLY_PROP_TEMP_HOT_NTC: // case POWER_SUPPLY_PROP_TEMP_WARM_BATTERY: // case POWER_SUPPLY_PROP_TEMP_HOT_BATTERY: case POWER_SUPPLY_PROP_CALL_STATUS: case POWER_SUPPLY_PROP_CURRENT_NOW: return 1; default: break; } return 0; } static char *ech_supplicants[] = { "battery", "usb", "charger", //"dc", }; static char *ech_supplied_from[] = { "battery", "usb", }; static void ech_wls_chrg_get_psys(struct mt5728_dev *chip) { if (!chip) { ech_wls_log(ECH_LOG_ERR, "[%s] Chip not ready.\n", __func__); return; } chip->batt_psy = power_supply_get_by_name("battery"); if (!chip->batt_psy) ech_wls_log(ECH_LOG_ERR, "[%s] BATT PSY Not Found.\n", __func__); chip->usb_psy = power_supply_get_by_name("usb"); if (!chip->usb_psy) ech_wls_log(ECH_LOG_ERR, "[%s] USB PSY Not Found.\n", __func__); } void ech_wls_chrg_info_init(void) { // return; memset(&chrg_info, 0, sizeof(chrg_info)); chip->state = ECH_WLS_CHRG_WAIT; chip->resume_vbatt = 4300; ech_wls_chrg_get_psys(chip); chrg_info.work_en = 1; } void updata_wireless_online(void) { // wls_work_online = wls_work_online; // if(MT5725_wls_get_online() == 1) // { // wls_work_online = 1; // } // else // { // wls_work_online = 0; // } } // static void ech_wls_create_device_node(struct device *dev) // { // device_create_file(dev, &dev_attr_ver_drv); // device_create_file(dev, &dev_attr_ver_fw); // device_create_file(dev, &dev_attr_reg_addr); // device_create_file(dev, &dev_attr_reg_data); // device_create_file(dev, &dev_attr_pwr_src); // device_create_file(dev, &dev_attr_protocol); // } static int set_charger_type(void) { int ret; union power_supply_propval val; struct power_supply *psy = power_supply_get_by_name("charger"); pr_info("mt5728 set_charger_type psy: %d.\n", psy); if (psy == NULL) { pr_info("mt5728 power_supply_get_by_name error.\n"); return -1; } val.intval = wls_work_online; pr_info("mt5728 set_charger_type: %d.\n", val.intval); ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_ONLINE, &val); if(val.intval) { val.intval = WIRELESS_CHARGER; ret = power_supply_set_property(psy,POWER_SUPPLY_PROP_CHARGE_TYPE, &val); if (!ret) { pr_info("mt5728 POWER_SUPPLY_PROP_CHARGE_TYPE: %d.\n", val.intval); return val.intval; } else { return 0; } }else if(GET_GPIO_WIRELESS_EN() == 0) { ret = power_supply_set_property(psy,POWER_SUPPLY_PROP_CHARGE_TYPE, &val); }else if(GET_GPIO_WIRELESS_EN() == 1) { val.intval = CHARGER_UNKNOWN; ret = power_supply_set_property(psy,POWER_SUPPLY_PROP_CHARGE_TYPE, &val); } return 0; } static int mt5728_gpio_init(void) { int ret = 0; //trx struct device_node *usb_node = NULL; usb_node = of_find_compatible_node(NULL, NULL, "mediatek,mt5728"); if (usb_node != NULL) { //-------------------chip en------------------ chip->chip_en_gpio = of_get_named_gpio(usb_node, "chip_en_gpio", 0); if (!gpio_is_valid(chip->chip_en_gpio)) { printk(KERN_ALERT "gpio_is_valid error:%d",chip->chip_en_gpio); } if (chip->chip_en_gpio < 0) { printk(KERN_ALERT "%s get chip->chip_en_gpio failed:%d\n", __func__,chip->chip_en_gpio); } ret = gpio_request(chip->chip_en_gpio, "chip_en_gpio"); /*request gpio*/ if (ret < 0) { printk(KERN_ALERT "%s chip->chip_en_gpio gpio_request failed, gpio=%d\n", __func__, chip->chip_en_gpio); }else { printk(KERN_ALERT "%s set chip->chip_en_gpio success, gpio=%d\n", __func__, chip->chip_en_gpio); } gpio_direction_input(chip->chip_en_gpio); /*set direction is input*/ //-------------------eint_wpc------------------ chip->irq_gpio= of_get_named_gpio(usb_node, "eint_wpc", 0); if (!gpio_is_valid(chip->irq_gpio)) { printk(KERN_ALERT "gpio_is_valid error:%d",chip->irq_gpio); } if (chip->irq_gpio < 0) { printk(KERN_ALERT "%s get chip->irq_gpio failed:%d\n", __func__,chip->irq_gpio); } ret = gpio_request(chip->irq_gpio, "eint_wpc"); if (ret < 0) { printk(KERN_ALERT "%s chip->irq_gpio_request failed, gpio=%d\n", __func__, chip->irq_gpio); } else { printk(KERN_ALERT "%s set chip->irq_gpio success, gpio=%d\n", __func__, chip->irq_gpio); } gpio_direction_input(chip->irq_gpio); //-----------------------powergood_gpio------------------------- chip->powergood_gpio = of_get_named_gpio(usb_node, "powergood_gpio", 0); if (!gpio_is_valid(chip->powergood_gpio)) { printk(KERN_ALERT "powergood_gpio_is_valid error:%d",chip->powergood_gpio); } if (chip->powergood_gpio < 0) { printk(KERN_ALERT "%s get chip->powergood_gpio failed:%d\n", __func__,chip->powergood_gpio); } ret = gpio_request(chip->powergood_gpio, "powergood_gpio"); /*request gpio*/ if (ret < 0) { printk(KERN_ALERT "%s chip->powergood_gpio gpio_request failed, gpio=%d\n", __func__, chip->powergood_gpio); } else { printk(KERN_ALERT "%s set chip->powergood_gpio success, gpio=%d\n", __func__, chip->powergood_gpio); } gpio_direction_input(chip->powergood_gpio); /*set direction is input*/ gpio_set_value(chip->powergood_gpio, 1); //powergood input high //-------------------otg_ctrl_gpio------------------ #if 0 chip->otg_ctrl_gpio = of_get_named_gpio(usb_node, "otg_ctrl_gpio", 0); if (!gpio_is_valid(chip->otg_ctrl_gpio)) { printk(KERN_ALERT "otg_ctrl_gpio_is_valid error:%d",chip->otg_ctrl_gpio); } if (chip->otg_ctrl_gpio < 0) { printk(KERN_ALERT "%s get chip->otg_ctrl_gpio failed:%d\n", __func__,chip->otg_ctrl_gpio); } ret = gpio_request(chip->otg_ctrl_gpio, "otg_ctrl_gpio"); /*request gpio*/ if (ret < 0) { printk(KERN_ALERT "%s chip->otg_ctrl_gpio gpio_request failed, gpio=%d\n", __func__, chip->otg_ctrl_gpio); } else { printk(KERN_ALERT "%s set chip->otg_ctrl_gpio success, gpio=%d\n", __func__, chip->otg_ctrl_gpio); } gpio_direction_input(chip->otg_ctrl_gpio); /*set direction is input*/ printk(KERN_ALERT "%s set chip->otg_ctrl_gpio input success, gpio=%d\n", __func__, chip->otg_ctrl_gpio); gpio_set_value(chip->otg_ctrl_gpio,0); printk(KERN_ALERT "%s get chip->otg_ctrl_gpio status =%d\n", __func__,gpio_get_value(chip->otg_ctrl_gpio)); #endif //-------------------ldo_ctrl_gpio------------------ chip->ldo_ctrl_gpio = of_get_named_gpio(usb_node, "ldo_ctrl_gpio", 0); if (!gpio_is_valid(chip->ldo_ctrl_gpio)) { printk(KERN_ALERT "ldo_ctrl_gpio_is_valid error:%d",chip->ldo_ctrl_gpio); } if (chip->ldo_ctrl_gpio < 0) { printk(KERN_ALERT "%s get chip->ldo_ctrl_gpio failed:%d\n", __func__,chip->ldo_ctrl_gpio); } ret = gpio_request(chip->ldo_ctrl_gpio, "ldo_ctrl_gpio"); /*request gpio*/ if (ret < 0) { printk(KERN_ALERT "%s ldo_ctrl_gpio gpio_request failed, gpio=%d\n", __func__, chip->ldo_ctrl_gpio); return ret; }else{ printk(KERN_ALERT "%s set chip->ldo_ctrl_gpio success, gpio=%d\n", __func__, chip->ldo_ctrl_gpio); } gpio_direction_input(chip->ldo_ctrl_gpio); /*set direction is input*/ //-------------------trx_mode_gpio------------------ chip->trx_mode_gpio = of_get_named_gpio(usb_node, "trx_mode_gpio", 0); if (!gpio_is_valid(chip->trx_mode_gpio)) { printk(KERN_ALERT "trx_mode_gpio_is_valid error:%d",chip->trx_mode_gpio); } if (chip->trx_mode_gpio < 0) { printk(KERN_ALERT "%s get chip->trx_mode_gpio failed:%d\n", __func__,chip->trx_mode_gpio); } ret = gpio_request(chip->trx_mode_gpio, "trx_mode_gpio"); /*request trx gpio*/ if (ret < 0) { printk(KERN_ALERT "%s chip->trx_mode_gpio gpio_request failed, gpio=%d\n", __func__, chip->trx_mode_gpio); } else { printk(KERN_ALERT "%s set chip->trx_mode_gpio success, gpio=%d\n", __func__, chip->trx_mode_gpio); } gpio_direction_output(chip->trx_mode_gpio, 0); /*set direction is output*/ printk(KERN_ALERT "%s set gpio done !\n",__func__); } else { printk(KERN_ALERT "error gpio\n"); } return ret; } static unsig32 SizeofPkt(u8 hdr) { if (hdr < 0x20) return 1; if (hdr < 0x80) return (2 + ((hdr - 0x20) >> 4)); if (hdr < 0xe0) return (8 + ((hdr - 0x80) >> 3)); return (20 + ((hdr - 0xe0) >> 2)); } static int mt5728_read(struct mt5728_dev* di, u16 reg, u8* val) { unsigned int temp; int rc; rc = regmap_read(di->regmap, reg, &temp); if (rc >= 0) *val = (u8)temp; return rc; } static int mt5728_write(struct mt5728_dev* di, u16 reg, u8 val) { int rc = 0; rc = regmap_write(di->regmap, reg, val); if (rc < 0) dev_err(di->dev, "%s error: %d\n",__func__, rc); return rc; } static int mt5728_read_buffer(struct mt5728_dev* di, u16 reg, u8* buf, unsig32 size) { return regmap_bulk_read(di->regmap, reg, buf, size); } static int mt5728_write_buffer(struct mt5728_dev* di, u16 reg, u8* buf, unsig32 size) { int rc = 0; rc = regmap_bulk_write(di->regmap, reg, buf, size); // while (size--) { // rc = di->bus.write(di, reg++, *buf++); // if (rc < 0) { // dev_err(di->dev, "%s error: %d\n",__func__, rc); // return rc; // } // } return rc; } static void mt5728_removed_form_tx(void) { wls_work_online = 0; chrg_info.wls_online = 0; mt5728_ldo_on_flag = 0; // set_charger_type(); mt5728_display_slow_wls_icon(0); mt5728_display_qucik_wls_icon(0); enable_vbus_ovp(true); printk(KERN_ALERT "mt5728_removed_form_tx\n"); } static void mt5728_sram_write(unsig32 addr, u8 *data,unsig32 len) { unsig32 offset,length,size; offset = 0; length = 0; size = len; printk(KERN_ALERT "[%s] Length to write:%d\n",__func__, len); while(size > 0) { if(size > SRAM_PAGE_SIZE) { length = SRAM_PAGE_SIZE; } else { length = size; } printk(KERN_ALERT "[%s] Length of this write :%d\n",__func__,length); mt5728_write_buffer(mte, addr + offset ,data+offset, length); size -= length; offset += length; msleep(2); } printk(KERN_ALERT "[%s] Write completion\n",__func__); } static void mt5728_run_pgm_fw(void) { vuc val; val.value = MT5728_WDG_DISABLE; mt5728_write_buffer(mte, MT5728_PMU_WDGEN_REG, val.ptr, 2); mt5728_write_buffer(mte, MT5728_PMU_WDGEN_REG, val.ptr, 2); mt5728_write_buffer(mte, MT5728_PMU_WDGEN_REG, val.ptr, 2); val.value = MT5728_WDT_INTFALG; mt5728_write_buffer(mte, MT5728_PMU_FLAG_REG, val.ptr, 2); val.value = MT5728_KEY; mt5728_write_buffer(mte, MT5728_SYS_KEY_REG, val.ptr, 2); val.value = 0X08; mt5728_write_buffer(mte, MT5728_CODE_REMAP_REG, val.ptr, 2); val.value = 0x0FFF; mt5728_write_buffer(mte, MT5728_SRAM_REMAP_REG, val.ptr, 2); msleep(50); mt5728_sram_write(0x1800,(u8 *)mt5728_pgm_bin,sizeof(mt5728_pgm_bin)); msleep(50); val.value = MT5728_KEY; mt5728_write_buffer(mte, MT5728_SYS_KEY_REG, val.ptr, 2); val.value = MT5728_M0_RESET; mt5728_write_buffer(mte, MT5728_M0_CTRL_REG, val.ptr, 2); msleep(50); printk(KERN_ALERT "[%s] finish \n",__func__); } static u8 mt5728_mtp_read(unsig32 addr, u8 * buf , unsig32 size, u8 mode) { unsig32 length ,i,status,times; vuc val; Pgm_Type_t pgm; printk(KERN_ALERT "[%s] parameter size :%d\n",__func__,size); length = (size+(MTP_BLOCK_SIZE-1))/MTP_BLOCK_SIZE*MTP_BLOCK_SIZE; mt5728_run_pgm_fw(); printk(KERN_ALERT "[%s] Calculate the length to read:%d\n",__func__,length); for (i = 0; i < length/MTP_BLOCK_SIZE; ++i) { pgm.length = MTP_BLOCK_SIZE; pgm.addr = addr+i*MTP_BLOCK_SIZE; pgm.status = PGM_STATUS_READY; val.value = pgm.status; mt5728_write_buffer(mte,PGM_STATUS_ADDR,val.ptr,2); val.value = pgm.addr; mt5728_write_buffer(mte,PGM_ADDR_ADDR,val.ptr,2); val.value = pgm.length; mt5728_write_buffer(mte,PGM_LENGTH_ADDR,val.ptr,2); val.value = PGM_STATUS_READ; mt5728_write_buffer(mte,PGM_STATUS_ADDR,val.ptr,2); msleep(50); mt5728_read_buffer(mte, PGM_STATUS_ADDR, val.ptr, 2); status = val.ptr[0]; times = 0; while(status == PGM_STATUS_READ) { msleep(50); mt5728_read_buffer(mte, PGM_STATUS_ADDR, val.ptr, 2); printk(KERN_ALERT "[%s] Program reading",__func__); status = val.ptr[0]; times+=1; if (times>100) { printk(KERN_ALERT "[%s] error! Read OTP TImeout\n",__func__); return FALSE; } } if (status == PGM_STATUS_PROGOK) { printk(KERN_ALERT "[%s] PGM_STATUS_PROGOK\n",__func__); mt5728_read_buffer(mte, PGM_DATA_ADDR, &buf[MTP_BLOCK_SIZE*i], MTP_BLOCK_SIZE); } else { printk(KERN_ALERT "[%s] OtpRead error , status = 0x%02x\n",__func__,status); return FALSE; } if (mode == TRUE) { } } return TRUE; } static u8 mt5728_mtp_write(unsig32 addr, u8 * buf , unsig32 len) { unsig32 offset; unsig32 write_size ,status,times; unsig32 write_retrycnt; s32 size; int i; vuc val; Pgm_Type_t pgm; size = len; offset = 0; write_size = 0; printk(KERN_ALERT "[%s] Size to write:%d\n",__func__,size); mt5728_run_pgm_fw(); write_retrycnt = 8; while(size>0) { if (size>MTP_BLOCK_SIZE) { pgm.length = MTP_BLOCK_SIZE; write_size = MTP_BLOCK_SIZE; } else { pgm.length = size; write_size = size; } pgm.addr = addr+offset; pgm.cs = pgm.addr; pgm.status = PGM_STATUS_READY; for (i = 0; i < pgm.length; ++i) { pgm.data[i] = buf[offset + i]; pgm.cs += pgm.data[i]; } // zero_cnt = 0; // for (i = (pgm.length -1); i >= 0; --i) { // if (pgm.data[i] != 0x00) { // break; // } // zero_cnt+=1; // } // if(zero_cnt == pgm.length) { // size-=write_size; // offset+=write_size; // continue; // } // pgm.length-=zero_cnt; pgm.cs+=pgm.length; val.value = pgm.status; mt5728_write_buffer(mte,PGM_STATUS_ADDR,val.ptr,2); val.value = pgm.addr; mt5728_write_buffer(mte,PGM_ADDR_ADDR,val.ptr,2); val.value = pgm.length; mt5728_write_buffer(mte,PGM_LENGTH_ADDR,val.ptr,2); val.value = pgm.cs; mt5728_write_buffer(mte,PGM_CHECKSUM_ADDR,val.ptr,2); mt5728_write_buffer(mte, PGM_DATA_ADDR, pgm.data, pgm.length); val.value = PGM_STATUS_WMTP; mt5728_write_buffer(mte,PGM_STATUS_ADDR,val.ptr,2); msleep(50); mt5728_read_buffer(mte, PGM_STATUS_ADDR, val.ptr, 2); status = val.ptr[0]; times = 0; while(status == PGM_STATUS_WMTP) { msleep(50); mt5728_read_buffer(mte, PGM_STATUS_ADDR, val.ptr, 2); status = val.ptr[0]; printk(KERN_ALERT "[%s] Program writing\n",__func__); times+=1; if (times > 100) { printk(KERN_ALERT "[%s] Program write timeout\n",__func__); return FALSE; } } if (status == PGM_STATUS_PROGOK) { size-=write_size; offset+=write_size; printk(KERN_ALERT "[%s] PGM_STATUS_PROGOK\n",__func__); } else if (status == PGM_STATUS_ERRCS) { if (write_retrycnt > 0) { write_retrycnt--; printk(KERN_ALERT "[%s] ERRCS write_retrycnt:%d\n",__func__,write_retrycnt); continue; } else { printk(KERN_ALERT "[%s] PGM_STATUS_ERRCS\n",__func__); return FALSE; } } else if (status == PGM_STATUS_ERRPGM) { if (write_retrycnt > 0) { write_retrycnt--; printk(KERN_ALERT "[%s] ERRPGM write_retrycnt:%d\n",__func__,write_retrycnt); continue; } else { printk(KERN_ALERT "[%s] PGM_STATUS_ERRPGM\n",__func__); return FALSE; } } else { if (write_retrycnt > 0) { write_retrycnt--; printk(KERN_ALERT "[%s] NUKNOWN write_retrycnt:%d\n",__func__,write_retrycnt); continue; } else { printk(KERN_ALERT "[%s] PGM_STATUS_NUKNOWN \n",__func__); return FALSE; } } } return TRUE; } static u8 mt5728_mtp_write_check(unsig32 flagaddr,u16 crc) { u8 i; u8 otpwrite_flagdata[4]; u8 *otpwrite_flagread; otpwrite_flagread = kmalloc(1064, GFP_KERNEL); otpwrite_flagdata[0] = crc % 256; otpwrite_flagdata[1] = crc / 256; otpwrite_flagdata[2] = crc % 256; otpwrite_flagdata[3] = crc / 256; mt5728_mtp_read(flagaddr, otpwrite_flagread,4,1); for (i = 0; i < 4; ++i) { if (otpwrite_flagread[i] != otpwrite_flagdata[i]) { printk(KERN_ALERT "[%s] MT5728 MTP Flag not written: %d\n",__func__,i); return FALSE; } } printk(KERN_ALERT "[%s] MT5728 MTP Flag has been written",__func__); kfree(otpwrite_flagread); return TRUE; } void mt5728_write_mtpok_flag(unsig32 flagaddr,u16 crc) { u8 otpwrite_flagdata[4] ; otpwrite_flagdata[0] = crc % 256; otpwrite_flagdata[1] = crc / 256; otpwrite_flagdata[2] = crc % 256; otpwrite_flagdata[3] = crc / 256; if (flagaddr < 0x3700) { printk(KERN_ALERT "[%s] Address out of range\n",__func__); return; } mt5728_mtp_write(flagaddr,otpwrite_flagdata,4); } static u8 mt5728_mtp_verify(u32 addr,u8 * data,u32 len) { #if 0 // u8 *mtp_read_temp; // u32 i; // mtp_read_temp = kmalloc(1024*17, GFP_KERNEL); // if(mtp_read_temp == NULL){ // printk(KERN_ALERT "[%s] devm_kzalloc Error\n",__func__); // return FALSE; // } // if (len > 1024*17) { // printk(KERN_ALERT "[%s] Wrong parameter length\n",__func__); #else vuc val; int waitTimeOutCnt; int status; u16 crcvlaue; u16 crcvlaue_chip; Pgm_Type_t pgm; // crcvlaue = crc_ccitt(0xFFFF,(u8 *)MT5728_mtp_bin,sizeof(MT5728_mtp_bin)); crcvlaue = crc_firmware(0x1021, 0xFFFF, (u8*)MT5728_mtp_bin, sizeof(MT5728_mtp_bin)) & 0xffff; val.value = 0; mt5728_write_buffer(mte,PGM_ADDR_ADDR,val.ptr,2); val.value = sizeof(MT5728_mtp_bin); mt5728_write_buffer(mte,PGM_LENGTH_ADDR,val.ptr,2); pgm.cs = crcvlaue; val.value = pgm.cs; mt5728_write_buffer(mte,PGM_CHECKSUM_ADDR,val.ptr,2); val.value = (1 << 6); mt5728_write_buffer(mte,PGM_STATUS_ADDR,val.ptr,2); waitTimeOutCnt = 100; while(waitTimeOutCnt--) { mt5728_read_buffer(mte, PGM_STATUS_ADDR, val.ptr, 2); status = val.ptr[0] | (val.ptr[1] << 8); if (status & (1 << 7)) { mt5728_read_buffer(mte, PGM_DATA_ADDR, val.ptr, 2); crcvlaue_chip = val.value; printk(KERN_ALERT "[%s] mt5728_mtp_verify error,crcvlaue_chip:%x,crcvlaue:%x",__func__,crcvlaue_chip,crcvlaue); return FALSE; } //VERIFYOK if (status & (1 << 8)) { mt5728_read_buffer(mte, PGM_DATA_ADDR, val.ptr, 2); crcvlaue_chip = val.value; printk(KERN_ALERT "[%s] mt5728_mtp_verify success,crcvlaue_chip:%x,crcvlaue:%x",__func__,crcvlaue_chip,crcvlaue); return TRUE; } msleep(60); }/**/ printk(KERN_ALERT "[%s] TimeOut cal_crc :0x%04x\n",__func__,crcvlaue); return FALSE; #endif } static inline u16 crc_ccitt_byte(u16 crc, const u8 c) { return (crc >> 8) ^ mt5728_crc_ccitt_table[(crc ^ c) & 0xff]; } static u16 crc_ccitt(u16 crc, u8 const *buffer, size_t len) { while (len--) crc = crc_ccitt_byte(crc, *buffer++); return crc; } static inline u16 crc_firmware(u16 poly1, u16 sed, u8 *buf, u32 n) { u32 i = 0; u32 j = 0; u16 poly = 0x1021; u16 crc = 0xffff; // u32 addr = 0x20000070; for (j = 0; j < n; j+=2) { crc ^= (buf[j+1] << 8); for (i = 0;i < 8; i++) { crc = (crc & 0x8000) ? (((crc << 1) & 0xffff) ^ poly) : (crc << 1); } /* *(u8*)addr = buf[j+1]; */ /* addr ++; */ crc ^= (buf[j] << 8); for (i = 0;i < 8; i++) { crc = (crc & 0x8000) ? (((crc << 1) & 0xffff) ^ poly) : (crc << 1); } /* *(u8*)addr = buf[j]; */ /* addr ++; */ /* *(u32*)0x200000a0 = addr; */ } return crc; } /* Send proprietary packet to Tx Step 1: write data into REG_PPP Step 2: write REG_CMD */ void mt5728_send_ppp(PktType *pkt) { vuc val; mt5728_write_buffer(mte, REG_PPP, (u8 *)pkt, SizeofPkt(pkt->header)+1); mte->fsk_status = FSK_WAITTING; val.value = SEND_PPP; mt5728_write_buffer(mte, REG_CMD, val.ptr, 2); } EXPORT_SYMBOL(mt5728_send_ppp); int Get_adaptertype(void){ PktType eptpkt; int count = 0; u8 fsk_msg[10]; eptpkt.header = PP18; eptpkt.cmd = CMD_ADAPTER_TYPE; mt5728_send_ppp(&eptpkt); while(mte->fsk_status == FSK_WAITTING){ msleep(20); if((count++) > 50 ){ printk(KERN_ALERT "[%s] AP system judgement:FSK receive timeout \n",__func__); return (-1); } } if(mte->fsk_status == FSK_FAILED){ printk(KERN_ALERT "[%s] Wireless charging system judgement:FSK receive timeout \n",__func__); return (-1); } if(mte->fsk_status == FSK_SUCCESS){ mt5728_read_buffer(mte,REG_BC,fsk_msg,10); printk(KERN_ALERT "[%s] Information received : 0x%02x 0x%02x 0x%02x \n",__func__,fsk_msg[0],fsk_msg[1],fsk_msg[2]); } return fsk_msg[2]; } static ssize_t get_reg(struct device* cd, struct device_attribute* attr, char* buf) { vuc val; ssize_t len = 0; int i = 0; for (i = 0; i < INDEX_MAX; i++) { if (reg_access[i].flag & REG_RD_ACCESS) { mt5728_read_buffer(mte, reg_access[i].addr, val.ptr, 2); len += snprintf(buf + len, PAGE_SIZE - len, "reg:%s 0x%04x=0x%04x,%d\n", reg_access[i].name, reg_access[i].addr, val.value,val.value); } } return len; } static ssize_t set_reg(struct device* cd, struct device_attribute* attr, const char* buf, size_t len) { unsigned int databuf[2]; vuc val; u8 tmp[2]; u16 regdata; int i = 0; int ret = 0; ret = sscanf(buf, "%x %x", &databuf[0], &databuf[1]); if (2 == ret) { for (i = 0; i < INDEX_MAX; i++) { if (databuf[0] == reg_access[i].addr) { if (reg_access[i].flag & REG_WR_ACCESS) { // val.ptr[0] = (databuf[1] & 0xff00) >> 8; val.value = databuf[1]; // val.ptr[1] = databuf[1] & 0x00ff; //big endian if (reg_access[i].flag & REG_BIT_ACCESS) { mt5728_read_buffer(mte, databuf[0], tmp, 2); regdata = tmp[0] << 8 | tmp[1]; val.value |= regdata; printk(KERN_ALERT "get reg: 0x%04x set reg: 0x%04x \n", regdata, val.value); mt5728_write_buffer(mte, databuf[0], val.ptr, 2); } else { printk(KERN_ALERT "Set reg : [0x%04x] 0x%x \n", databuf[0], val.value); mt5728_write_buffer(mte, databuf[0], val.ptr, 2); } } break; } } }else{ printk(KERN_ALERT "Error \n"); } return len; } static ssize_t fast_charging_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) { vuc val; int error; unsigned int a; error = kstrtouint(buf, 10, &a); val.value = (unsigned short)a; if (error) return error; if ((val.value < 0) || (val.value > 20000)) { printk(KERN_ALERT "[%s] MT5728 Parameter error\n",__func__); return count; } // get_charger_by_name("primary_chg"); // charger_dev_set_input_current(info->chg1_dev, pdata->input_current_limit); // charger_dev_set_charging_current(info->chg1_dev,pdata->charging_current_limit); // fast_vfc(val.value); return count; } static ssize_t get_adapter(struct device* cd, struct device_attribute* attr, char* buf) { ssize_t len = 0; int rc; rc = Get_adaptertype(); if(rc == (-1)){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Failed to read adapter type\n", __func__); }else if(rc == ADAPTER_NONE ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : Unknown\n", __func__); }else if(rc == ADAPTER_SDP ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : SDP\n", __func__); }else if(rc == ADAPTER_CDP ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : CDP\n", __func__); }else if(rc == ADAPTER_DCP ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : DCP\n", __func__); }else if(rc == ADAPTER_QC20 ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : QC2.0\n", __func__); }else if(rc == ADAPTER_QC30 ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : QC3.0\n", __func__); }else if(rc == ADAPTER_PD ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : PD\n", __func__); }else if(rc == ADAPTER_FCP ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : FCP\n", __func__); }else if(rc == ADAPTER_SCP ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : SCP\n", __func__); }else if(rc == ADAPTER_DCS ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : DC source\n", __func__); }else if(rc == ADAPTER_AFC ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : AFC\n", __func__); }else if(rc == ADAPTER_PDPPS ){ len += snprintf(buf + len, PAGE_SIZE - len, "[%s] Adapter type : PD PPS\n", __func__); } return len; } static ssize_t brushfirmware(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) { int error; int mt5728VoutTemp; unsigned int pter; error = kstrtouint(buf, 10, &pter); mt5728_mtp_write_flag = 1; mt5728VoutTemp = Mt5728_get_vout(); if (mt5728VoutTemp < 0) { mt5728_ap_open_otg_boost(true); } msleep(200); //Wait for Vout voltage to stabilize if(pter == 1){ printk(KERN_ALERT "[%s] brush MTP program\n",__func__); if(mt5728_mtp_write(0x0000,(u8 *)MT5728_mtp_bin,sizeof(MT5728_mtp_bin))){ printk(KERN_ALERT "[%s] Write complete, start verification \n",__func__); if (mt5728_mtp_verify(0x0000,(u8 *)MT5728_mtp_bin,sizeof(MT5728_mtp_bin))) { printk(KERN_ALERT "[%s] mt5728_mtp_verify OK \n",__func__); } else { printk(KERN_ALERT "[%s] mt5728_mtp_verify check program failed \n",__func__); } } }else if(pter == 2){ u16 crcvlaue; crcvlaue = crc_ccitt(0xFFFF,(u8 *)MT5728_mtp_bin,sizeof(MT5728_mtp_bin)); printk(KERN_ALERT "[%s] cal_crc :0x%04x\n",__func__,crcvlaue); if(mt5728_mtp_write_check(MTP_WRITE_FLAG_ADDR,crcvlaue)==TRUE){ printk(KERN_ALERT "[%s] mt5728_mtp_write exit \n",__func__); }else{ if(mt5728_mtp_write(0x0000,(u8 *)MT5728_mtp_bin,sizeof(MT5728_mtp_bin))){ printk(KERN_ALERT "[%s] Write complete, start verification \n",__func__); if (mt5728_mtp_verify(0x0000,(u8 *)MT5728_mtp_bin,sizeof (MT5728_mtp_bin))) { printk(KERN_ALERT "[%s] mt5728_mtp_verify OK \n",__func__); mt5728_write_mtpok_flag(MTP_WRITE_FLAG_ADDR,crcvlaue); printk(KERN_ALERT "[%s] MT5728_write_mtp_flag \n",__func__); } else { printk(KERN_ALERT "[%s] mt5728_mtp_verify check program failed \n",__func__); } } } } if (mt5728VoutTemp < 0) { mt5728_ap_open_otg_boost(false); } printk(KERN_ALERT "[%s] Exit this operation \n",__func__); mt5728_mtp_write_flag = 0; return count; } static void download_txSRAM_code(void) { vuc val; val.value = MT5728_WDG_DISABLE; mt5728_write_buffer(mte, MT5728_PMU_WDGEN_REG, val.ptr, 2); mt5728_write_buffer(mte, MT5728_PMU_WDGEN_REG, val.ptr, 2); mt5728_write_buffer(mte, MT5728_PMU_WDGEN_REG, val.ptr, 2); val.value = MT5728_WDT_INTFALG; mt5728_write_buffer(mte,MT5728_PMU_FLAG_REG,val.ptr,2); val.value = MT5728_KEY; mt5728_write_buffer(mte,MT5728_SYS_KEY_REG,val.ptr,2); val.value = MT5728_M0_HOLD | LBIT(9); mt5728_write_buffer(mte,MT5728_M0_CTRL_REG,val.ptr,2); msleep(50); mt5728_sram_write(0x800,(u8 *)MT572x_TxSRAM_bin,sizeof(MT572x_TxSRAM_bin)); val.value = 0x02; mt5728_write_buffer(mte,MT5728_CODE_REMAP_REG,val.ptr,2); val.value = 0xff0f; mt5728_write_buffer(mte,MT5728_SRAM_REMAP_REG,val.ptr,2); val.value = MT5728_KEY; mt5728_write_buffer(mte,MT5728_SYS_KEY_REG,val.ptr,2); val.value = MT5728_M0_RESET ; mt5728_write_buffer(mte,MT5728_M0_CTRL_REG,val.ptr,2); msleep(50); printk(KERN_ALERT "[%s] finish\n",__func__); } static ssize_t updata_txFW(struct device* cd, struct device_attribute* attr, char* buf) { ssize_t len = 0; download_txSRAM_code(); return len; } void fastcharge_afc(void) { vuc val; vuc temp, fclr, scmd; scmd.value = 0; mt5728_read_buffer(mte, REG_INTFLAG, val.ptr, 2); fclr.value = FAST_CHARGE; if (val.value & INT_AFC_SUPPORT) { printk(KERN_ALERT "MT5728 %s ,version 0.1 Tx support samsung_afc\n", __func__); temp.value = 9000; mt5728_write_buffer(mte, REG_VFC, temp.ptr, 2); scmd.value |= FAST_CHARGE; scmd.value |= CLEAR_INT; mt5728_write_buffer(mte, REG_INTCLR, fclr.ptr, 2); printk(KERN_ALERT "%s,version 0.1 write reg_clr : 0x%04x,\n", __func__, fclr.value); mt5728_write_buffer(mte, REG_CMD, scmd.ptr, 2); printk(KERN_ALERT "%s,version 0.1 write reg_cmd : 0x%04x,\n", __func__, scmd.value); } } static struct attribute* mt5728_sysfs_attrs[] = { &dev_attr_fast_charging.attr, &dev_attr_reg.attr, &dev_attr_adapter_type.attr, &dev_attr_brushFW.attr, &dev_attr_TxFirmware.attr, &dev_attr_otg.attr, // &dev_attr_otp.attr, &dev_attr_mt5728_en.attr, &dev_attr_mt5728_flag.attr, &dev_attr_epp_set_vout.attr, &dev_attr_rxdetect.attr, &dev_attr_reverse_charger.attr, NULL, }; static const struct attribute_group mt5728_sysfs_group = { .name = "mt5728group", .attrs = mt5728_sysfs_attrs, }; static const struct regmap_config mt5728_regmap_config = { .reg_bits = 16, .val_bits = 8, .max_register = 0xFFFF, }; void mt5728_typec_host_otg_detect(int detect) { if (detect) { usbchip_otg_detect = 1; } else { usbchip_otg_detect = 0; } } int is_reverse_charger_online(void) { return (reserse_charge_online); } int is_maxic_wls_online(void) { return wls_work_online; } static ssize_t mt5728_get_reverse_charger(struct device* cd,struct device_attribute *attr, char* buf) { ssize_t len = 0; len += snprintf(buf+len, PAGE_SIZE-len, "reverse_charger en : %d\n", chip->reverse_charger); return len; } void mt5728_soft_reset(void) { vuc val; val.value = MT5728_KEY; mt5728_write_buffer(mte, MT5728_SYS_KEY_REG, val.ptr, 1); val.ptr[0] = MT5728_M0_RESET; mt5728_write_buffer(mte, MT5728_M0_CTRL_REG, val.ptr, 1); printk(KERN_ALERT "%s\n", __func__); msleep(20); } //extern int mt6360_set_vchg(void); //extern int mt6360_get_vchg(void); void mt5728_reverse_charge(bool en) { #ifdef __GFTK_REVERSE_CHARGER_PERIOD__ vuc val; #endif if(en) { chip->reverse_charger = 1; reserse_charge_online = 1; reverse_timeout_cnt = 0; //mt5728_otgen_gpio_ctrl(true); printk(KERN_ALERT "zzt[%s] start en =%d",__func__, en); // msleep(20000); //debug,use adb shell on reverse charger SET_GPIO_WPC_RTMODE_H(); printk(KERN_ALERT "zzt[%s] trx_mode_gpio is high",__func__); mt5728_ap_open_otg_boost(true); charger_dev_set_boost_current_limit(primary_charger, 2000000); printk("up drv add wiress start !\n"); printk(KERN_ALERT "zzt[%s] open_otg_boost",__func__); msleep(500); //Wait for Vout voltage to stabilize mt5728_soft_reset(); #ifdef MT5728_USE_WAKELOCK // wake_lock(&mt5728_wls_wake_lock); wireless_charge_wake_lock(); #endif schedule_delayed_work(&chip->reverse_charge_work, msecs_to_jiffies(100)); chip->rxdetect_flag = 0; chip->rxremove_flag = 0; mt5728_tx_ping_cnt = 0; //val.value = (80000/125) - 2; //mt5728_write_buffer(mte, 0x0050, val.ptr, 2); //ping 125k,20210517 //msleep(200); // //val.value = 0x280;//80000/f 125Khz // //val.value = 0x29a;//80000/f 120Khz // val.value = 0x2d7;//80000/f 110Khz // mt5728_write_buffer(mte, 0x0040, val.ptr, 2); // //mt5728_read_buffer(mte, 0x0040, val.ptr, 2); // //printk(KERN_ALERT " 0x40 val = 0x%04x\n", val.value); // // val.value = 0x2f9; //105KHz // val.value = 0x378; //90KHz // mt5728_write_buffer(mte, 0x003e, val.ptr, 2); // val.value = 0x0; // mt5728_read_buffer(mte, 0x003e, val.ptr, 2); // printk(KERN_ALERT " 0x40 valw = 0x%04x\n", val.value); #ifdef __GFTK_REVERSE_CHARGER_PERIOD__ msleep(3000); mt5728_read_buffer(mte, REG_RXD_PERI, val.ptr, 2); printk("dz-drv: 0x004e old value is %d\n", val.value); val.value = 0x320;//f (period = 80000/f) mt5728_write_buffer(mte, REG_RXD_PERI, val.ptr, 2); mt5728_read_buffer(mte, REG_RXD_PERI, val.ptr, 2); printk("dz-drv: 0x004e new value is %d\n", val.value); #endif } else { //mt5728_otgen_gpio_ctrl(false); printk(KERN_ALERT "zzt[%s] reverse_charge close by driver\n",__func__); SET_GPIO_WPC_RTMODE_L(); mt5728_ap_open_otg_boost(false); chip->reverse_charger = 0; reserse_charge_online = 0; mt5728_tx_cep_cnt_timeout = 0; #ifdef MT5728_USE_WAKELOCK // wake_unlock(&mt5728_wls_wake_lock); wireless_charge_wake_unlock(); #endif } } // static ssize_t mt5728_get_reverse_charger{ // static ssize_t mt5728_set_reverse_charger(struct device* cd, struct device_attribute *attr, const char* buf, size_t len) // } static ssize_t mt5728_set_reverse_charger(struct device* cd, struct device_attribute *attr, const char* buf, size_t len) { unsigned int databuf[16]; sscanf(buf,"%d",&databuf[0]); if(databuf[0] == 0) { // OFF mt5728_reverse_charge(0); } else { // ON mt5728_reverse_charge(1); } return len; } static ssize_t mt5728_get_hwen(struct device* cd,struct device_attribute *attr, char* buf) { return 0; //return sprintf(buf, "%u\n", !gpio_get_value(mte->chip_en)); } ssize_t mt5728_force_chipen_disable(void) { GPIO_WIRELESS_EN_OUTPUT(); SET_GPIO_WIRELESS_EN_H(); return 0; } ssize_t mt5728_chipen_ctrl_by_hardware(void) { GPIO_WIRELESS_EN_INPUT(); return 0; } static ssize_t mt5728_set_hwen(struct device* cd, struct device_attribute *attr, const char* buf, size_t len) { unsigned int databuf[16]; sscanf(buf,"%d",&databuf[0]); if(databuf[0] == 0) { // OFF //gpio_set_value(mte->chip_en, 1); // chrg_info.work_en = 0; } else { // ON // gpio_set_value(mte->chip_en ,0); // chrg_info.work_en = 1; } return len; } static ssize_t Mt5728_set_vout(struct device* cd, struct device_attribute *attr, const char* buf, size_t len) { vuc val; int error; unsigned int a; error = kstrtouint(buf, 10, &a); val.value = (unsigned short)a; printk(KERN_ALERT "%s,write reg_cmd : 0x%04x,\n", __func__, val.value); mt5728_write_buffer(mte, REG_VOUTSET, val.ptr, 2); val.value = VOUT_CHANGE; mt5728_write_buffer(mte, REG_CMD, val.ptr, 2); printk(KERN_ALERT "%s,write reg_cmd : 0x%04x,\n", __func__, val.value); return 0; } static ssize_t Mt5728_get_vout(void) { vuc vout; if(mt5728_read_buffer(mte,REG_VOUT,vout.ptr,2) < 0) { return -1; } //printk(KERN_ALERT "%s,Mt5728_get_vout_old : %d !\n", __func__,mt5728_vout_old); if (abs(vout.value - mt5728_vout_old) > 1000) { mt5728_vout_old = vout.value; printk(KERN_ALERT "%s,Mt5728_get_vout : %dmV !\n", __func__, vout.value); } return vout.value; // return battery_get_vbus(); } static ssize_t Mt5728_get_vrect(void) { vuc vout; if(mt5728_read_buffer(mte,REG_VRECT,vout.ptr,2) < 0) { return -1; } //printk(KERN_ALERT "%s,Mt5728_get_vrect_old : %d !\n", __func__,mt5728_vout_old); if (abs(vout.value - mt5728_vrect_old) > 1000) { mt5728_vrect_old = vout.value; printk(KERN_ALERT "%s,Mt5728_get_vrect : %dmV !\n", __func__, vout.value); } return vout.value; // return battery_get_vbus(); } static ssize_t mt5728_get_rxdetect(struct device* cd,struct device_attribute *attr, char* buf) { return sprintf(buf, "%u\n", chip->rxdetect_flag); } static ssize_t mt5728_get_otg(struct device* cd,struct device_attribute *attr, char* buf) { return sprintf(buf, "%u\n", chip->otg_flag); } static ssize_t mt5728_set_otg(struct device* cd, struct device_attribute *attr, const char* buf, size_t len) { unsigned int databuf[16]; sscanf(buf,"%d",&databuf[0]); if(databuf[0] == 0) { // OFF mt5728_ap_open_otg_boost(false); chip->otg_flag = 0; } else { // ON mt5728_ap_open_otg_boost(true); chip->otg_flag = 1; } return len; } ssize_t mt5728_show_flag(struct device* cd,struct device_attribute *attr, char* buf) { int ret = 0; ret += sprintf(buf + ret, "%d%d\n", reserse_charge_online, wls_work_online); ret += sprintf(buf + ret, "powergood_gpio : %d\n", gpio_get_value(chip->powergood_gpio)); ret += sprintf(buf + ret, "wpc_int_gpio : %d\n", gpio_get_value(chip->irq_gpio)); ret += sprintf(buf + ret, "wireless_en : %d\n", gpio_get_value(chip->chip_en_gpio)); ret += sprintf(buf + ret, "en1_gpio : %d\n", gpio_get_value(chip->charger_en_gpio)); ret += sprintf(buf + ret, "\n"); ret += sprintf(buf + ret, "reverse_charger : %d\n", reserse_charge_online); ret += sprintf(buf + ret, "wireless_online : %d\n", wls_work_online); ret += sprintf(buf + ret, "usb_otg_online : %d\n", usb_otg_online); ret += sprintf(buf + ret, "usbchip_otg_detect : %d\n", usbchip_otg_detect); return ret; } ssize_t mt5728_set_flag(struct device* cd, struct device_attribute *attr, const char* buf, size_t len) { unsigned int value = 0; switch (value) { case 1: gpio_set_value(mte->ldo_ctrl_gpio, 1); break; case 2: gpio_set_value(mte->ldo_ctrl_gpio, 0); break; case 3: gpio_set_value(mte->chip_en_gpio, 1); break; case 4: gpio_set_value(mte->chip_en_gpio, 0); break; case 5: //gpio_set_value(mte->otg_ctrl_gpio, 1); break; case 6: //gpio_set_value(mte->otg_ctrl_gpio, 0); break; case 20: gpio_direction_input(chip->chip_en_gpio); break; case 21: gpio_direction_output(chip->chip_en_gpio, 0); break; case 22: //gpio_direction_input(chip->otg_ctrl_gpio); break; case 23: //gpio_direction_output(chip->otg_ctrl_gpio, 0); break; case 10: mt5728_reverse_charge(0); break; case 11: mt5728_reverse_charge(1); break; } return len; } //samsung tx send vout to 9v void fast_vfc(int vol) { vuc val; val.value = vol; mt5728_write_buffer(mte, REG_VFC, val.ptr, 2); val.value = FAST_CHARGE; mt5728_write_buffer(mte, REG_CMD, val.ptr, 2); printk(KERN_ALERT "%s,write reg_cmd : 0x%04x,\n", __func__, val.value); } #if 1//def CONFIG_FG_SD77561 void mt5728_send_EndPowerPacket(u8 endreson) { PktType eptpkt; eptpkt.header = ENDPOWERXFERPACKET; eptpkt.msg[0] = endreson; mt5728_send_ppp(&eptpkt); } #endif //wireless charge ,vout ,iout ,PMIC set,100ms extern void wireless_chg_type_switch(bool otg_on); #ifdef CONFIG_FG_SD77561 extern int sd77561_get_soc(void); #endif static void mt5728_charger_work_func(struct work_struct* work) { int vbus_now = 0; int vbus_read_temp = 0; vuc val; int vbat_temp; int ui_soc; #if 1//def CONFIG_FG_SD77561 bool chg_done = false; #endif #ifndef MT5728_CHIP_AUTO_AFC9V vuc vout; #endif #if UP_MT5728_WIRELESS_ADD_OTG if((mt5728_is_otg_plugin(otg_plugin) == 1) && (mt5728_charging_otgin_flag == 0)) { printk(KERN_ALERT "mt5728 ,when wireless charging,otg in 01 "); mt5728_force_chipen_disable(); msleep(100); //wait Vout lower than 5V printk(KERN_ALERT "mt5728 ,when wireless charging,otg in 02 "); // while(Mt5728_get_vout() > 4500) { // msleep(50); //} mt5728_removed_form_tx(); mt5728_charging_otgin_flag = 1; //mt5728_ap_open_otg_boost(true); //pmic boost otg power on wireless_chg_type_switch(true);//true schedule_delayed_work(&chip->charger_work, msecs_to_jiffies(100)); printk(KERN_ALERT "mt5728 ,when wireless charging,otg in 04,,,wls_work_online:%d",wls_work_online); return; } if(mt5728_charging_otgin_flag == 1) { if(mt5728_is_otg_plugin(otg_plugin) == 0) { printk(KERN_ALERT "mt5728 ,when wireless charging,otg in 05,,,wls_work_online:%d,otg_plugin:%d",wls_work_online,otg_plugin); mt5728_charging_otgin_flag = 0; mt5728_ap_open_otg_boost(false); //pmic boost otg power off msleep(100); //wait Vbus to 0V SET_GPIO_WIRELESS_EN_L(); wireless_chg_type_switch(false);//wwx printk(KERN_ALERT "mt5728 ,when wireless charging,otg in 05,,,wls_work_online:%d",wls_work_online); return; } else { schedule_delayed_work(&chip->charger_work, msecs_to_jiffies(100)); return; } } #endif //check if usb line charge plug in. // printk(KERN_ALERT "mt5728 get gpio wireless en !GET_GPIO_WIRELESS_EN:%d\n",GET_GPIO_WIRELESS_EN()); if (GET_GPIO_WIRELESS_EN() == 1) { printk(KERN_ALERT "mt5728 get gpio wireless en !\n"); //close mt5825 ldo SET_GPIO_LDO_CTRL_H(); mt5728_removed_form_tx(); #ifdef MT5728_USE_WAKELOCK // wake_unlock(&mt5728_wls_wake_lock); wireless_charge_wake_unlock(); #endif return; } Mt5728_get_vrect(); vbus_read_temp = Mt5728_get_vout(); if ((vbus_read_temp >= 0) && (vbus_read_temp <= 20000)) { vbus_now = Mt5728_get_vout(); } //check if rx put on tx surface,if 1 rx removed from tx //if(GET_GPIO_POWERGOOD() == 1) { if(((vbus_now < 1000) && (mt5728_ldo_on_flag == 1))|| (vbus_read_temp == -1)) { powergood_err_cnt++; if(powergood_err_cnt > 20) { //Cancel wireless charging icon display mt5728_removed_form_tx(); #ifdef MT5728_USE_WAKELOCK // wake_unlock(&mt5728_wls_wake_lock); wireless_charge_wake_unlock(); #endif return; } else { schedule_delayed_work(&chip->charger_work, msecs_to_jiffies(100)); return; } } else { powergood_err_cnt = 0; } #ifndef MT5728_CHIP_AUTO_AFC9V //rx , afc set up mt5728 vout to 9v if(vout_change_timecnt++ > 10) { vout_change_timecnt = 0; if(AfcSendTimeout > 0) { AfcSendTimeout--; } else { mt5728_read_buffer(mte,REG_VOUT,vout.ptr,2); //if tx support afc,set vout to 9v if((AfcIntFlag == 1) && (vout.value < 6000)) { vout.value = 9000; mt5728_write_buffer(mte, REG_VFC, vout.ptr, 2); vout.value = FAST_CHARGE; mt5728_write_buffer(mte, REG_CMD, vout.ptr, 2); printk(KERN_ALERT " mt5728_write_buffer(mte, REG_VFC, vout.ptr, 2)\n"); } } } #endif #ifdef REDUCE_CURRENT_ACCORD_VOUT if(rx_vout_max > (vbus_now + 500)) { current_reduce_flag |= VOUT_LOW; if(input_current_limit > 100) { input_current_limit -= 100; } } else { current_reduce_flag &= ~VOUT_LOW; } #endif vbat_temp = battery_get_bat_temperature(); #ifdef CONFIG_FG_SD77561 ui_soc = sd77561_get_soc(); #else ui_soc = battery_get_uisoc(); #endif if(vbus_now > 11000) { printk("%d - vbat_temp = %d\n", __LINE__, vbat_temp); if(vbat_temp >= 46){ if(vbat_temp > 56){ chrg_info.charger_current = 0; }else if(vbat_temp >= 49){ chrg_info.charger_current = 1000; input_current_limit = 500; rx_iout_max = 500; }else{ chrg_info.charger_current = 2000; input_current_limit = 1000; rx_iout_max = 1000; } vbat_temp_hot = true; }else if(vbat_temp > 42 && vbat_temp_hot == true){ chrg_info.charger_current = 2000; input_current_limit = 1000; rx_iout_max = 1000; }else{ chrg_info.charger_current = 2900; input_current_limit = 1100; rx_iout_max = 1100; vbat_temp_hot = false; } } else if(vbus_now > 8000) { if(vbat_temp >= 49 && vbat_temp <= 56){ chrg_info.charger_current = 1000; rx_iout_max = 500; }else if(vbat_temp >= 46 && vbat_temp<49){ chrg_info.charger_current = 2000; rx_iout_max = 1000; }else if(vbat_temp > 56){ chrg_info.charger_current = 0; }else{ chrg_info.charger_current = 2900; rx_iout_max = 1100; } } else { rx_iout_max = 1000; chrg_info.charger_current = 1000; } //After waiting for 10 seconds, change the PMIC input current if(setup_iout_start_cnt < 100) { setup_iout_start_cnt++; if (setup_iout_start_cnt == 70) { if (mt5728_epp_ctrl_vout_flag) { val.value = rx_vout_max; printk(KERN_ALERT "mt5728 epp set Vout to:%d,ptpower%d\n",rx_vout_max,mt5728_epp_ptpower); mt5728_write_buffer(mte, REG_VOUTSET, val.ptr, 2); val.value = VOUT_CHANGE; mt5728_write_buffer(mte, REG_CMD, val.ptr, 2); mt5728_epp_ctrl_vout_flag = 0; } } } else { //Change one step every 500ms if(current_change_interval_cnt++ > 15) { current_change_interval_cnt = 0; if((input_current_limit < rx_iout_max) && (current_reduce_flag == 0)) { input_current_limit += 100; } mt5728_set_pmic_input_current_ichg(input_current_limit); #if 1//def CONFIG_FG_SD77561 charger_dev_is_charging_done(primary_charger, &chg_done); printk(KERN_ALERT "mt5728 chg_done=%d,ui_soc=%d\n",chg_done,ui_soc); if (chg_done) { printk(KERN_ALERT "mt5728 charging full!\n"); mt5728_send_EndPowerPacket(EPT_CHGCOMPLETE); } if(ui_soc >= 99){ val.value = 5000; printk(KERN_ALERT "mt5728 epp set Vout to 5v.\n"); mt5728_write_buffer(mte, REG_VOUTSET, val.ptr, 2); val.value = VOUT_CHANGE; mt5728_write_buffer(mte, REG_CMD, val.ptr, 2); mt5728_epp_ctrl_vout_flag = 0; } #endif } } schedule_delayed_work(&chip->charger_work, msecs_to_jiffies(100)); } //mt5728_tx_cep_cnt_old static ssize_t mt5728_get_tx_cep_cnt_close_tx(void) { vuc cepcnt; mt5728_read_buffer(mte, 0x00c0, cepcnt.ptr, 2); if(cepcnt.value != mt5728_tx_cep_cnt_old) { mt5728_tx_cep_cnt_timeout = 0; mt5728_tx_cep_cnt_old = cepcnt.value; } else { mt5728_tx_cep_cnt_timeout++; } if(mt5728_tx_cep_cnt_timeout > (REVERSE_TIMEOUT_CNT)) { return 1; } return 0; } //reverse charge delay work static void mt5728_reverse_work_func(struct work_struct* work) { if(chip->rxdetect_flag == 1) { if(chip->rxremove_flag == 1) { chip->rxdetect_flag = 0; chip->rxremove_flag = 0; reverse_timeout_cnt = 0; schedule_delayed_work(&chip->reverse_charge_work, msecs_to_jiffies(100)); return; } else { //Check the remaining battery capacity // //Check battery temperature } } else { reverse_timeout_cnt++; if(reverse_timeout_cnt > REVERSE_TIMEOUT_CNT) { //RX put on timeout, active shutdown mt5728_reverse_charge(0); return; } } if (mt5728_tx_ping_cnt > (REVERSE_TIMEOUT_CNT*100 / 230)) { printk(" %s: chip->rxdetect_flag=%d,mt5728_tx_ping_cnt=%d \n",__func__, chip->rxdetect_flag, mt5728_tx_ping_cnt); mt5728_reverse_charge(0); return; } if(mt5728_get_tx_cep_cnt_close_tx()) { printk(" %s: chip->rxdetect_flag=%d,mt5728_tx_cep_cnt_timeout=%d \n",__func__, chip->rxdetect_flag, mt5728_tx_cep_cnt_timeout); mt5728_reverse_charge(0); return; } schedule_delayed_work(&chip->reverse_charge_work, msecs_to_jiffies(100)); } #if 0 static void mt5728_fwcheck_work_func(struct work_struct* work) { int mt5728VoutTemp; u8 fwver[2]; if(wls_work_online ==1 ) { mt5728_read_buffer(mte, REG_FW_VER, fwver, 2); printk(KERN_ALERT "MT5728 fw_version_in_chip : 0x%x%x\n",fwver[0], fwver[1]); return; } mt5728VoutTemp = Mt5728_get_vout(); if (mt5728VoutTemp < 0) { reserse_charge_online = 1; mt5728_ap_open_otg_boost(true); } msleep(200); //Wait for Vout voltage to stabilize mt5728VoutTemp = Mt5728_get_vout(); mt5728_read_buffer(mte, REG_FW_VER, fwver, 2); fwver[0] ^= fwver[1]; fwver[1] ^= fwver[0]; fwver[0] ^= fwver[1]; printk(KERN_ALERT "MT5728 fw_version_in_chip : 0x%x%x\n",fwver[1], fwver[0]); if(((fwver[1] << 8)|fwver[0]) != MT5728_FWVERSION) { mt5728_mtp_write_flag = 1; printk(KERN_ALERT "MT5728 fw_version not match : 0x%x\n",MT5728_FWVERSION); printk(KERN_ALERT "[%s] brush MTP program\n",__func__); if(mt5728_mtp_write(0x0000,(u8 *)MT5728_mtp_bin,sizeof(MT5728_mtp_bin))){ printk(KERN_ALERT "[%s] Write complete, start verification \n",__func__); if (mt5728_mtp_verify(0x0000,(u8 *)MT5728_mtp_bin,sizeof(MT5728_mtp_bin))) { printk(KERN_ALERT "[%s] mt5728_mtp_verify OK \n",__func__); mt5728_read_buffer(mte, REG_FW_VER, fwver, 2); printk(KERN_ALERT "MT5728 fw_version_in_chip222 : 0x%x%x\n",fwver[1], fwver[0]); } else { printk(KERN_ALERT "[%s] mt5728_mtp_verify check program failed \n",__func__); mt5728_read_buffer(mte, REG_FW_VER, fwver, 2); printk(KERN_ALERT "MT5728 fw_version_in_chip333 : 0x%x%x\n",fwver[1], fwver[0]); } } //if (mt5728VoutTemp < 0) { mt5728_ap_open_otg_boost(false); // } mt5728_mtp_write_flag = 0; } } #endif /** * [mt5728_send_EPT End Power Transfer Packet] * @param endreson [end powr Reson] */ void mt5728_send_EPT(u8 endreson) { PktType eptpkt; eptpkt.header = PP18; eptpkt.cmd = ENDPOWERXFERPACKET; eptpkt.data[0] = endreson; mt5728_send_ppp(&eptpkt); } void mt5728_SetFodPara(void) { mt5728_write_buffer(mte, REG_FOD, (unsigned char *)mt5728fod, 16); mt5728_write_buffer(mte, REG_FOD_EPP, (unsigned char *)mt5728fod, 16); } void mt5728_irq_handle(void) { vuc val; vuc temp, fclr, scmd, ptpower; #ifdef MT5728_AFC9V_IN_IRQ vuc voutset; #endif int iic_rf = 0; scmd.value = 0; fclr.value = 0; printk(KERN_ALERT "----------------MT5728_delayed_work-----------------------\n"); if (mt5728_mtp_write_flag == 1) { return; } temp.value = MT5728ID; iic_rf = mt5728_write_buffer(mte,REG_CHIPID,temp.ptr,2); if(iic_rf < 0){ printk(KERN_ALERT "[%s] Chip may not be working\n",__func__); //20210330 if(gpio_get_value(chip->irq_gpio)){ printk(KERN_ALERT "[%s] irq_gpio high return irq handle\n",__func__); return; } } mt5728_read_buffer(mte,0x04,temp.ptr,2); //20210330 if((temp.ptr[1] & RXMODE) && (chip->reverse_charger == 0) && (iic_rf >= 0) && (!is_wireless_chipen_pin_high())){ printk(KERN_ALERT "[%s] The chip works in Rx mode\n",__func__); mt5728_read_buffer(mte, REG_INTFLAG, val.ptr, 2); fclr.value = val.value; printk(KERN_ALERT "[%s] REG_INTFLAG value:0x%04x\n", __func__, val.value); if(val.value == 0){ printk(KERN_ALERT "[%s] There's no interruption here\n", __func__); cancel_delayed_work(&mte->eint_work); return; } if (val.value & INT_POWER_ON) { printk(KERN_ALERT "[%s] val.value & INT_POWER_ON\n",__func__); AfcSendTimeout = 0; rx_vout_max = 5000; rx_iout_max = 1000; rx_iout_limit = 100; AfcIntFlag = 0; mt5728_ldo_on_flag = 0; mt5728_SetFodPara(); if(mt5728_is_otg_plugin(otg_plugin) == 1) { SET_GPIO_LDO_CTRL_H(); //OTG is working. Put it on TX and LDO is close } else { #ifdef MT5728_USE_WAKELOCK // wake_lock(&mt5728_wls_wake_lock); wireless_charge_wake_lock(); #endif SET_GPIO_LDO_CTRL_H(); } powergood_err_cnt = 0; setup_iout_start_cnt = 0; mt5728_vout_old = 0; mt5728_vrect_old = 0; chrg_info.wls_online = 1; wls_work_online = 1; input_current_limit = 100; cur_last = 0; mt5728_display_slow_wls_icon(1); mt5728_set_pmic_input_current_ichg(100); enable_vbus_ovp(false); schedule_delayed_work(&chip->charger_work, msecs_to_jiffies(100)); printk(KERN_ALERT "[%s] Interrupt signal: PowerON\n", __func__); // printk(KERN_ALERT, "[%s] PWR_SRC_CHG.\n", __func__); // pwr_src_chg = 1; // power_supply_changed(chip->wl_psy); } if (val.value & INT_LDO_ON) { mt5728_ldo_on_flag = 1; printk(KERN_ALERT "[%s] Interrupt signal:LDO ON\n", __func__); } if (val.value & INT_RX_READY) { if(mt5728_is_otg_plugin(otg_plugin) == 1) { SET_GPIO_LDO_CTRL_H(); //OTG is working. Put it on TX and LDO is close } else { powergood_err_cnt = 0; setup_iout_start_cnt = 0; wls_work_online = 1; } printk(KERN_ALERT "[%s] Interrupt signal:MT5728 is Ready\n", __func__); } if (val.value & INT_LDO_OFF) { printk(KERN_ALERT "[%s] Interrupt signal:MT5728 LDO_OFF\n", __func__); } if (val.value & INT_AFC_SUPPORT) { printk(KERN_ALERT "[%s] Interrupt signal:Tx support samsung_afc\n", __func__); #ifdef MT5728_AFC9V_IN_IRQ voutset.value = 9000; mt5728_write_buffer(mte, REG_VFC, voutset.ptr, 2); scmd.value |= FAST_CHARGE; #endif AfcIntFlag = 1; AfcSendTimeout = 6; rx_vout_max = 9000; rx_iout_max = 1200; mt5728_display_qucik_wls_icon(1); } if (val.value & INT_FSK_RECV) { printk(KERN_ALERT "[%s] Interrupt signal:FSK received successfully\n", __func__); mte->fsk_status = FSK_SUCCESS; //read REG_BC } if (val.value & INT_FSK_SUCCESS) { printk(KERN_ALERT "[%s] Interrupt signal:FSK received successfully\n", __func__); mte->fsk_status = FSK_SUCCESS; //read REG_BC } if (val.value & INT_FSK_TIMEOUT) { printk(KERN_ALERT "[%s] Interrupt signal:Failed to receive FSK\n", __func__); mte->fsk_status = FSK_FAILED; //read REG_BC } if (val.value & INT_EPP) { mt5728_read_buffer(mte,REG_POT_POWER,ptpower.ptr,2); printk(KERN_ALERT "[%s] REG_POT_POWER:%d\n", __func__ ,ptpower.value); #ifdef MT5728_EPP_AP_CTRL_VOUT mt5728_epp_ctrl_vout_flag = 1; mt5728_epp_ptpower = ptpower.value; if(ptpower.value < MAX_POWER(15)) { rx_vout_max = 9000; rx_iout_max = 1200; } else { rx_vout_max = 12000; rx_iout_max = 1000; } // voutset.value = rx_vout_max; // mt5728_write_buffer(mte, REG_VOUTSET, voutset.ptr, 2); // scmd.value |= VOUT_CHANGE; #endif #ifdef __GFTK_WIRELESS_REG_MAX_POWER_VALUE__ mt5728_read_buffer(mte,REG_MAX_POWER,ptpower.ptr,2); printk(KERN_ALERT "[%s] OLD REG_MAX_POWER:%d\n", __func__ ,ptpower.value); ptpower.value = __GFTK_WIRELESS_REG_MAX_POWER_VALUE__; mt5728_write_buffer(mte,REG_MAX_POWER,ptpower.ptr,2); mt5728_read_buffer(mte,REG_MAX_POWER,ptpower.ptr,2); printk(KERN_ALERT "[%s] NEW REG_MAX_POWER:%d\n", __func__ ,ptpower.value); #endif mt5728_display_qucik_wls_icon(1); printk(KERN_ALERT "[%s] Interrupt signal:Tx support EPP\n", __func__); } } if((temp.ptr[1] & TXMODE) && (iic_rf >= 0)){ printk(KERN_ALERT "[%s] The chip works in Tx mode\n",__func__); mt5728_read_buffer(mte, REG_INTFLAG, val.ptr, 2); fclr.value = val.value; printk(KERN_ALERT "[%s] REG_INTFLAG value:0x%04x\n", __func__, val.value); if(val.value == 0){ printk(KERN_ALERT "[%s] There's no interruption here\n", __func__); cancel_delayed_work(&mte->eint_work); return; } if (val.value & BIT(14)) { //INT_TX_PING,interval = 230ms if (reserse_charge_online == 1) { mt5728_tx_ping_cnt++; printk(KERN_ALERT "MT5728 %s ,INT_TX_PING\n", __func__); } } if (val.value & INT_POWER_TRANS) { if (reserse_charge_online == 1) { chip->rxdetect_flag = 1; reverse_timeout_cnt = 0; mt5728_tx_ping_cnt = 0; printk(KERN_ALERT "MT5728 %s ,rxdetect: INT_POWER_TRANS\n", __func__); } } if (val.value & INT_REMOVE_POWER) { if (reserse_charge_online == 1) { chip->rxdetect_flag = 0; printk(KERN_ALERT "MT5728 %s ,rxdetect: INT_REMOVE_POWER\n", __func__); } } } if(iic_rf >= 0) { scmd.value |= CLEAR_INT; //---clrintflag //mt5728_write_buffer(mte, REG_INTCLR, fclr.ptr, 2); mt5728_write_buffer(mte, REG_INTCLR, fclr.ptr, 2); printk(KERN_ALERT "[%s] write REG_INTCLR : 0x%04x,\n", __func__, fclr.value); mt5728_write_buffer(mte, REG_CMD, scmd.ptr, 2); printk(KERN_ALERT "[%s] write REG_CMD : 0x%04x,\n", __func__, scmd.value); } if(fclr.value){ schedule_delayed_work(&mte->eint_work,100); //Callback check if the interrupt is cleared printk(KERN_ALERT "MT5728 %s ,schedule_delayed_work\n", __func__); } } EXPORT_SYMBOL(mt5728_irq_handle); static void mt5728_int_delayed_work_func(struct work_struct* work) { if(first_boot){ if(boot_mode == KERNEL_POWER_OFF_CHARGING_BOOT || boot_mode == LOW_POWER_OFF_CHARGING_BOOT){ printk(KERN_ALERT "MT5728 kernel power off chargin\n"); }else{ printk(KERN_ALERT "MT5728 reset chip while normal boot\n"); mt5728_force_chipen_disable(); msleep(100); mt5728_chipen_ctrl_by_hardware(); } first_boot = false; } mt5728_irq_handle(); } static irqreturn_t mt5728_irq(int irq,void * data){ struct mt5728_dev * mt5728 = data; printk(KERN_ALERT "mt5728_gpio_irq\n"); schedule_delayed_work(&mt5728->eint_work,300); return IRQ_HANDLED; } #if UP_MT5728_WIRELESS_TRX_MODE_SWITCH int up_trx_mode_flag = 0; static char wireless_trx_mode_value[5] = {}; static ssize_t show_wireless_trx_mode_value(struct device* dev, struct device_attribute *attr, char *buf) { printk("show wireless_trx_mode status is %s \n",wireless_trx_mode_value); return scnprintf(buf, 64, "%s\n", wireless_trx_mode_value); } static ssize_t store_wireless_trx_mode_value(struct device* dev, struct device_attribute *attr, const char *buf, size_t count) { printk("show wireless_trx_mode status before : %d (1->tx / 0->rx) !\n",up_trx_mode_flag); if(!strncmp(buf, "tx", 2)) { sprintf(wireless_trx_mode_value,"tx");//tx mode gpio_set_value(chip->trx_mode_gpio, 1); //gpio_direction_output(chip->powergood_gpio, 0); gpio_set_value(chip->powergood_gpio, 1); //gpio_set_value(chip->powergood_gpio, 0); //gpio_set_value(chip->powergood_gpio, 1); up_trx_mode_flag = 1;//status --- on } else { sprintf(wireless_trx_mode_value,"rx");//rx mode gpio_set_value(chip->trx_mode_gpio, 0); //gpio_direction_output(chip->powergood_gpio, 0); gpio_set_value(chip->powergood_gpio, 0); //gpio_set_value(chip->powergood_gpio, 1); //gpio_set_value(chip->powergood_gpio, 0); up_trx_mode_flag = 0;//status --- off } printk("show wireless_trx_mode status after : %d 1->tx / 0->rx !\n",up_trx_mode_flag); return count; } static DEVICE_ATTR(wireless_trx_mode, 0664, show_wireless_trx_mode_value, store_wireless_trx_mode_value); static struct attribute *wireless_trx_mode_attrs[] = { &dev_attr_wireless_trx_mode.attr, NULL, }; static struct attribute_group wireless_trx_mode_attr_grp = { .attrs = wireless_trx_mode_attrs, }; #endif extern int battery_get_boot_mode(void); static int mt5728_probe(struct i2c_client* client, const struct i2c_device_id* id) { int rc = 0; vuc chipid; int irq_num = 0; u8 fwver[2]; int ret=0; //int i; // struct device_node *mt5728gpio_node; boot_mode = battery_get_boot_mode(); printk(KERN_ALERT "MT5728 probe boot_mode=%d.\n",boot_mode); printk(KERN_ALERT DRIVER_FIRMWARE_VERSION); chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; primary_charger = get_charger_by_name("primary_chg"); if (!primary_charger) { pr_notice("%s: get primary charger device failed\n", __func__); //return -1; } mt5728_gpio_init(); printk(KERN_ALERT "mt5728_gpio_init done!\n"); // GPIO_OTG_CTRL_INPUT(); // mt5728_otgen_gpio_ctrl(false); GPIO_WIRELESS_EN_INPUT(); //chip en,hardware contrl //gpio26 GPIO_WPC_RTMODE_OUTPUT(); //trx gpio output //gpio46 SET_GPIO_WPC_RTMODE_L(); //default rx mode //low //GPIO_LDO_CTRL_OUTPUT(); //SET_GPIO_LDO_CTRL_L(); //ldo on mutex_init(&chip->slock); #ifdef MT5728_USE_WAKELOCK //wake_lock_init(&mt5728_wls_wake_lock, WAKE_LOCK_SUSPEND, "mt5728_wls_wake_lock"); //mt5728_wls_wake_lock = wakeup_source_register("mt5728_wls_wake_lock"); wakeup_source_init(&mt5728_wls_wake_lock, "mt5728_wls_wake_lock"); #endif printk(KERN_ALERT "MT5728 chip.\n"); chip->regmap = regmap_init_i2c(client, &mt5728_regmap_config); if (!chip->regmap) { printk(KERN_ALERT "MT5728 parent regmap is missing\n"); return -EINVAL; } printk(KERN_ALERT "MT5728 regmap.\n"); chip->client = client; chip->dev = &client->dev; chip->bus.read = mt5728_read; chip->bus.write = mt5728_write; chip->bus.read_buf = mt5728_read_buffer; chip->bus.write_buf = mt5728_write_buffer; chip->wl_psd.name = "mt5728_wireless"; chip->wl_psd.type = POWER_SUPPLY_TYPE_WIRELESS; chip->wl_psd.properties = ech_wls_chrg_props; chip->wl_psd.num_properties = ARRAY_SIZE(ech_wls_chrg_props); chip->wl_psd.get_property = ech_wls_chrg_get_property; chip->wl_psd.set_property = ech_wls_chrg_set_property; chip->wl_psd.external_power_changed = ech_wls_charger_external_power_changed; chip->wl_psd.property_is_writeable= ech_wls_chrg_property_is_writeable; chip->wl_psc.drv_data = chip; chip->wl_psc.of_node = chip->dev->of_node; chip->wl_psc.supplied_to = ech_supplicants; chip->wl_psc.num_supplicants = ARRAY_SIZE(ech_supplicants); chip->wl_psy = power_supply_register(chip->dev, &chip->wl_psd, NULL); if (IS_ERR(chip->wl_psy)) { printk(KERN_ALERT "[%s] power_supply_register wireless failed, ret = %d.\n", __func__, ret); } chip->wl_psy->supplied_from = ech_supplied_from; chip->wl_psy->num_supplies = ARRAY_SIZE(ech_supplied_from); printk(KERN_ALERT "wireless charger power supply register successful.\n"); ech_wls_chrg_info_init(); //ech_wls_create_device_node(&(client->dev)); INIT_DELAYED_WORK(&chip->eint_work,mt5728_int_delayed_work_func); INIT_DELAYED_WORK(&chip->charger_work, mt5728_charger_work_func); INIT_DELAYED_WORK(&chip->reverse_charge_work, mt5728_reverse_work_func); //INIT_DELAYED_WORK(&chip->mt5728_fwcheck_work, mt5728_fwcheck_work_func); irq_num = gpio_to_irq(chip->irq_gpio); rc = devm_request_threaded_irq(&client->dev,irq_num, \ NULL,mt5728_irq,IRQF_TRIGGER_FALLING|IRQF_ONESHOT, \ "mt5728",chip); if(rc != 0){ printk(KERN_ALERT "%s:failed to request IRQ %d: %d\n",__func__,gpio_to_irq(chip->irq_gpio),rc); goto Err; } #ifdef MT5728_USE_WAKELOCK device_init_wakeup(chip->dev, true); #endif rc = sysfs_create_group(&client->dev.kobj, &mt5728_sysfs_group); printk(KERN_ALERT "MT5728 probed successfully\n"); mte = chip; #if UP_MT5728_WIRELESS_TRX_MODE_SWITCH if (sysfs_create_group(&client->dev.kobj, &wireless_trx_mode_attr_grp)) { pr_err("%s : sysfs_create_group wireless_trx_mode failed\n", __FILE__); goto Err; } #endif #if 0//test for(ret=0;ret++;ret<10) { mt5728_read_buffer(mte, REG_CHIPID, chipid.ptr, 2); } #endif mt5728_read_buffer(mte, REG_CHIPID, chipid.ptr, 2); if ((chipid.ptr[1] << 8 | chipid.ptr[0]) == MT5728ID) { printk(KERN_ALERT "MT5728 ID 0x%x Correct query !\n",chipid.value); } else { printk(KERN_ALERT "MT5728 ID error : 0x%x \n ", chipid.value); } // schedule_delayed_work(&chip->mt5728_fwcheck_work, msecs_to_jiffies(100)); mt5728_read_buffer(mte, REG_FW_VER, fwver, 2); printk(KERN_ALERT "MT5728 fw_version : 0x%x%x\n",fwver[1], fwver[0]); schedule_delayed_work(&mte->eint_work,msecs_to_jiffies(5000)); Err: return rc; } static int mt5728_remove(struct i2c_client* client) { sysfs_remove_group(&client->dev.kobj, &mt5728_sysfs_group); #if UP_MT5728_WIRELESS_TRX_MODE_SWITCH sysfs_remove_group(&client->dev.kobj, &wireless_trx_mode_attr_grp); #endif return 0; } static void mt5728_shutdown(struct i2c_client *client) { mt5728_force_chipen_disable(); msleep(100); mt5728_chipen_ctrl_by_hardware(); printk(KERN_ALERT "%S \n ", __func__); } static const struct i2c_device_id mt5728_dev_id[] = { {"mt5728", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, mt5728_dev_id); #ifdef CONFIG_OF static const struct of_device_id mt5728_of_match[] = { {.compatible = "mediatek,mt5728"}, {}, }; MODULE_DEVICE_TABLE(of, mt5728_of_match); #endif static struct i2c_driver mt5728_driver = { .driver = { .name = "mt5728", .owner = THIS_MODULE, .of_match_table = of_match_ptr(mt5728_of_match), }, .probe = mt5728_probe, .remove = mt5728_remove, .id_table = mt5728_dev_id, .shutdown = mt5728_shutdown, }; static int __init mt5728_driver_init(void) { #ifdef CONFIG_OF printk(KERN_ALERT "mt5728_driver_init start\n "); #endif return i2c_add_driver(&mt5728_driver); } late_initcall(mt5728_driver_init); static void __exit mt5728_driver_exit(void) { printk(KERN_ALERT "mt5728_driver_exit\n"); return i2c_del_driver(&mt5728_driver); } module_exit(mt5728_driver_exit); MODULE_AUTHOR("Yangwl@maxictech.com"); MODULE_DESCRIPTION("MT5728 Wireless Power Receiver"); MODULE_LICENSE("GPL");