diff --git a/device/testing/device-xiaomi-pipa/APKBUILD b/device/testing/device-xiaomi-pipa/APKBUILD index e39262b3a..e1ed0bcf4 100644 --- a/device/testing/device-xiaomi-pipa/APKBUILD +++ b/device/testing/device-xiaomi-pipa/APKBUILD @@ -2,7 +2,7 @@ maintainer="bluebunny " pkgname=device-xiaomi-pipa pkgdesc="Xiaomi Pad 6" -pkgver=9 +pkgver=10 pkgrel=0 url="https://postmarketos.org" license="MIT" @@ -77,6 +77,6 @@ sha512sums=" d0e8f2e378130130ac72703aed957f7638e2dd75acf4aa421e1b4592c6cb6d296b652db0d9c242be4e56326760ce18d28c1e36ae553d0b7a7f91e12ab0353f22 81-libssc-xiaomi-pipa.rules 13c0c6639af2ac7103b2a30612f8a8b4bf1cfd875da527a2bccb786a846d948b3f30f79b01485402bdc9fa2db6c2ab2a449669199fb1e4b425c6604a7fe0eed5 HiFi.conf 9ce5336d2815de6dfe8477daa326abf8c79640d3fe519ef1978b18b49e489d70371215d3c0bbfb55068afde4b01b166b0a2a1a8d57b81355e465f542a43e361f deviceinfo -a92842d4f9d92af8ca14f4e86c74ea7dec446abd8a2a2b7dccbf6771065d8a7eea7f4a41adbd68647b49f81787a43fdb5b5337df0032ff3fbcb8e88b17f5ebf1 modules-initfs +a3f84d9636e907e3434ac952a554b771a083de8b62f2ea525234632b157ec2b5155a73856c01769bafa8fe9468216aea712769ca4d21ce388ba65491e96d2bee modules-initfs 007908c49db107dd8289a96049ed4d77a55c7846e21f76fa7f97306df953ccdec7d821cbc7732a3ff7c532285175ebf0d6a4e3f224740ac002b23f3608cbf76d pipa.conf " diff --git a/device/testing/device-xiaomi-pipa/modules-initfs b/device/testing/device-xiaomi-pipa/modules-initfs index ba1f62332..004607cd1 100644 --- a/device/testing/device-xiaomi-pipa/modules-initfs +++ b/device/testing/device-xiaomi-pipa/modules-initfs @@ -1,5 +1,6 @@ ktz8866 nt36523_ts +nu1665 panel_novatek_nt36532 qcom_common qcom_pil_info diff --git a/device/testing/linux-xiaomi-pipa/0002-power-supply-Add-driver-for-Qualcomm-PMIC-fuel-gauge.patch b/device/testing/linux-xiaomi-pipa/0002-power-supply-Add-driver-for-Qualcomm-PMIC-fuel-gauge.patch index 4106e7447..555522520 100644 --- a/device/testing/linux-xiaomi-pipa/0002-power-supply-Add-driver-for-Qualcomm-PMIC-fuel-gauge.patch +++ b/device/testing/linux-xiaomi-pipa/0002-power-supply-Add-driver-for-Qualcomm-PMIC-fuel-gauge.patch @@ -58,7 +58,7 @@ power: supply: qcom_pm8150b_charger: adds charging control to pm8150b_charger drivers/power/supply/Kconfig | 17 + drivers/power/supply/Makefile | 2 + drivers/power/supply/power_supply_sysfs.c | 1 + - drivers/power/supply/qcom_fg.c | 1365 +++++++++++++++++++ + drivers/power/supply/qcom_fg.c | 1371 +++++++++++++++++++ drivers/power/supply/qcom_pm8150b_charger.c | 964 +++++++++++++ include/linux/power_supply.h | 1 + 7 files changed, 2390 insertions(+) @@ -192,7 +192,7 @@ new file mode 100644 index 000000000000..921ef52fcc60 --- /dev/null +++ b/drivers/power/supply/qcom_fg.c -@@ -0,0 +1,1365 @@ +@@ -0,0 +1,1371 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */ + @@ -913,6 +913,12 @@ index 000000000000..921ef52fcc60 + temp = (s16)(readval[1] << 8 | readval[0]); + *val = div_s64((s64)temp * 488281, 1000); + ++ /* ++ * The power supply API expects charging batteries to report a ++ * positive current, which is inverted from what the PMIC reports. ++ */ ++ *val = -*val; ++ + return 0; +} + @@ -1100,9 +1106,9 @@ index 000000000000..921ef52fcc60 + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } -+ if (temp < 0) ++ if (temp > 0) + val->intval = POWER_SUPPLY_STATUS_CHARGING; -+ else if (temp > 0) ++ else if (temp < 0) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; diff --git a/device/testing/linux-xiaomi-pipa/0003-Input-Add-nt36523-touchscreen-driver.patch b/device/testing/linux-xiaomi-pipa/0003-Input-Add-nt36523-touchscreen-driver.patch index 53ae876e8..837338bcf 100644 --- a/device/testing/linux-xiaomi-pipa/0003-Input-Add-nt36523-touchscreen-driver.patch +++ b/device/testing/linux-xiaomi-pipa/0003-Input-Add-nt36523-touchscreen-driver.patch @@ -78,7 +78,7 @@ new file mode 100644 index 000000000000..83eddb597544 --- /dev/null +++ b/drivers/input/touchscreen/nt36523/nt36xxx.c -@@ -0,0 +1,1934 @@ +@@ -0,0 +1,1977 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * Copyright (C) 2021 XiaoMi, Inc. @@ -106,10 +106,14 @@ index 000000000000..83eddb597544 +#include +#include +#include ++#include +#include + +#include "nt36xxx.h" + ++extern int pen_charge_state_notifier_register_client(struct notifier_block *nb); ++extern int pen_charge_state_notifier_unregister_client(struct notifier_block *nb); ++ +#if NVT_TOUCH_ESD_PROTECT +#include +#endif /* #if NVT_TOUCH_ESD_PROTECT */ @@ -1216,6 +1220,30 @@ index 000000000000..83eddb597544 + } +} + ++static void nvt_pen_charge_state_change_work(struct work_struct *work) ++{ ++ struct nvt_ts_data *ts = container_of(work, struct nvt_ts_data, ++ pen_charge_state_change_work); ++ ++ mutex_lock(&ts->lock); ++ disable_pen_input_device(false); ++ mutex_unlock(&ts->lock); ++} ++ ++static int nvt_pen_charge_state_notifier_call(struct notifier_block *nb, ++ unsigned long val, void *v) ++{ ++ struct nvt_ts_data *ts = container_of(nb, struct nvt_ts_data, ++ pen_charge_state_notifier); ++ ++ ts->pen_is_charge = !!val; ++ NVT_LOG("pen charge state changed: %s\n", ++ ts->pen_is_charge ? "charging" : "not charging"); ++ schedule_work(&ts->pen_charge_state_change_work); ++ ++ return NOTIFY_OK; ++} ++ +int32_t disable_pen_input_device(bool disable) { + uint8_t buf[8] = {0}; + int32_t ret = 0; @@ -1577,6 +1605,13 @@ index 000000000000..83eddb597544 + } + INIT_WORK(&ts->resume_work, nvt_resume_work); + INIT_WORK(&ts->suspend_work, nvt_suspend_work); ++ INIT_WORK(&ts->pen_charge_state_change_work, nvt_pen_charge_state_change_work); ++ ts->pen_charge_state_notifier.notifier_call = nvt_pen_charge_state_notifier_call; ++ ret = pen_charge_state_notifier_register_client(&ts->pen_charge_state_notifier); ++ if (ret) { ++ NVT_ERR("register pen charge notifier failed. ret=%d\n", ret); ++ goto err_register_pen_charge_notifier_failed; ++ } + + bTouchIsAwake = 1; + NVT_LOG("end\n"); @@ -1585,6 +1620,11 @@ index 000000000000..83eddb597544 + + return 0; + ++err_register_pen_charge_notifier_failed: ++ if (ts->event_wq) { ++ destroy_workqueue(ts->event_wq); ++ ts->event_wq = NULL; ++ } +err_alloc_work_thread_failed: + +#if NVT_TOUCH_ESD_PROTECT @@ -1663,6 +1703,9 @@ index 000000000000..83eddb597544 +{ + NVT_LOG("Removing driver...\n"); + ++ pen_charge_state_notifier_unregister_client(&ts->pen_charge_state_notifier); ++ cancel_work_sync(&ts->pen_charge_state_change_work); ++ +#if NVT_TOUCH_ESD_PROTECT + if (nvt_esd_check_wq) { + cancel_delayed_work_sync(&nvt_esd_check_work); @@ -2018,7 +2061,7 @@ new file mode 100644 index 000000000000..beb5fc07dd6c --- /dev/null +++ b/drivers/input/touchscreen/nt36523/nt36xxx.h -@@ -0,0 +1,266 @@ +@@ -0,0 +1,267 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * Copyright (C) 2021 XiaoMi, Inc. @@ -2043,6 +2086,7 @@ index 000000000000..beb5fc07dd6c +#include +#include +#include ++#include +#include +#include +#include diff --git a/device/testing/linux-xiaomi-pipa/0016-power-supply-add-nuvolta-rx1665-wireless-charger.patch b/device/testing/linux-xiaomi-pipa/0016-power-supply-add-nuvolta-rx1665-wireless-charger.patch new file mode 100644 index 000000000..74d894783 --- /dev/null +++ b/device/testing/linux-xiaomi-pipa/0016-power-supply-add-nuvolta-rx1665-wireless-charger.patch @@ -0,0 +1,3126 @@ +diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig +index e981b8bbdfdf..2ae1024a021a 100644 +--- a/drivers/power/supply/Kconfig ++++ b/drivers/power/supply/Kconfig +@@ -919,6 +919,17 @@ config CHARGER_SC2731 + Say Y here to enable support for battery charging with SC2731 + PMIC chips. + ++config FUDA_1665 ++ tristate "Nuvolta RX1665 wireless charger" ++ depends on I2C ++ help ++ Say Y to include support for the Nuvolta RX1665 wireless power ++ transmitter used to charge a stylus on some Xiaomi tablets. ++ ++ The driver reports stylus charge information through the power ++ supply framework and controls the charger over I2C using device ++ tree bindings. ++ + config FUEL_GAUGE_SC27XX + tristate "Spreadtrum SC27XX fuel gauge driver" + depends on MFD_SC27XX_PMIC || COMPILE_TEST +diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile +index 6334ab4c3555..8145a3d8c88a 100644 +--- a/drivers/power/supply/Makefile ++++ b/drivers/power/supply/Makefile +@@ -108,6 +108,7 @@ obj-$(CONFIG_CHARGER_CROS_CONTROL) += cros_charge-control.o + obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o + obj-$(CONFIG_CHARGER_CROS_PCHG) += cros_peripheral_charger.o + obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o ++obj-$(CONFIG_FUDA_1665) += nu1665.o + obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o + obj-$(CONFIG_FUEL_GAUGE_STC3117) += stc3117_fuel_gauge.o + obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o +diff --git a/drivers/power/supply/nu1665.c b/drivers/power/supply/nu1665.c +new file mode 100644 +index 000000000000..a83472b91be2 +--- /dev/null ++++ b/drivers/power/supply/nu1665.c +@@ -0,0 +1,2624 @@ ++/* Copyright (c) 2022 The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++/* this driver is compatible for nuvolta wireless charge ic */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "nu1665.h" ++ ++static struct nuvolta_1665_chg *g_chip; ++static int last_valid_pen_soc = -1; ++static int pen_soc_count = 0; ++static u8 sram_buffer[256]; ++static int curr_count = 0; ++ ++static int nuvolta_1665_set_enable_mode(struct nuvolta_1665_chg *chip, ++ bool enable); ++static int nuvolta_1665_set_reverse_chg_mode(struct nuvolta_1665_chg *chip, ++ int enable); ++static int tx_info_update(struct nuvolta_1665_chg *chip, u8 *buff); ++static int fw_crc_chk(struct nuvolta_1665_chg *chip); ++static int read_fw_version(struct nuvolta_1665_chg *chip, u8 *version); ++static int nuvolta_1665_download_fw(struct nuvolta_1665_chg *chip, ++ bool power_on, bool force); ++static int nuvolta_1665_get_reverse_soc(struct nuvolta_1665_chg *chip); ++ ++int pen_charge_state_notifier_register_client(struct notifier_block *nb); ++int pen_charge_state_notifier_unregister_client(struct notifier_block *nb); ++void pen_charge_state_notifier_call_chain(unsigned long val); ++ ++static struct regmap_config nuvolta_1665_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 8, ++ .max_register = 0xFFFF, ++}; ++ ++static int nuvolta_1665_get_gpio(struct device *dev, const char *con_id) ++{ ++ struct gpio_desc *desc; ++ int gpio; ++ ++ desc = fwnode_gpiod_get_index(dev_fwnode(dev), con_id, 0, ++ GPIOD_ASIS, con_id); ++ if (IS_ERR(desc)) ++ return PTR_ERR(desc); ++ ++ gpio = desc_to_gpio(desc); ++ gpiod_put(desc); ++ ++ return gpio; ++} ++ ++struct delayed_work *pen_notifier_work; ++ ++static BLOCKING_NOTIFIER_HEAD(pen_charge_state_notifier_list); ++ ++static void pen_charge_notifier_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, pen_notifier_work.work); ++ blocking_notifier_call_chain(&pen_charge_state_notifier_list, ++ chip->pen_val, chip->pen_v); ++ return; ++} ++ ++int pen_charge_state_notifier_register_client(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&pen_charge_state_notifier_list, ++ nb); ++} ++EXPORT_SYMBOL(pen_charge_state_notifier_register_client); ++ ++int pen_charge_state_notifier_unregister_client(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_unregister( ++ &pen_charge_state_notifier_list, nb); ++} ++EXPORT_SYMBOL(pen_charge_state_notifier_unregister_client); ++ ++void pen_charge_state_notifier_call_chain(unsigned long val) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ pen_notifier_work, struct nuvolta_1665_chg, pen_notifier_work); ++ ++ chip->pen_v = NULL; ++ chip->pen_val = val; ++ schedule_delayed_work(&chip->pen_notifier_work, msecs_to_jiffies(0)); ++} ++EXPORT_SYMBOL(pen_charge_state_notifier_call_chain); ++ ++static int rx1665_read(struct nuvolta_1665_chg *chip, u8 *val, u16 addr) ++{ ++ unsigned int temp; ++ int rc = 0; ++ ++ rc = regmap_read(chip->regmap, addr, &temp); ++ if (rc < 0) ++ dev_err(chip->dev, "i2c read error: %d, address:%x\n", rc, addr); ++ else ++ *val = (u8)temp; ++ ++ return rc; ++} ++ ++static int rx1665_read_buffer(struct nuvolta_1665_chg *chip, u8 *buf, u16 addr, ++ int size) ++{ ++ int rc = 0; ++ ++ while (size--) { ++ rc = rx1665_read(chip, buf++, addr++); ++ if (rc < 0) { ++ dev_err(chip->dev, "[%s]i2c read error: %d\n", __func__, rc); ++ return rc; ++ } ++ } ++ ++ return rc; ++} ++ ++static int rx1665_write(struct nuvolta_1665_chg *chip, u8 val, u16 addr) ++{ ++ int rc = 0; ++ ++ rc = regmap_write(chip->regmap, addr, val); ++ if (rc < 0) ++ dev_err(chip->dev, "i2c write error: %d, address:%x\n", rc, addr); ++ ++ return rc; ++} ++ ++static bool nuvolta_1665_check_cmd_free(struct nuvolta_1665_chg *chip, u16 reg) ++{ ++ u8 rx_cmd_busy = 0, retry = 0; ++ ++ while (retry++ < 100) { ++ rx1665_read(chip, &rx_cmd_busy, reg); ++ if (rx_cmd_busy != 0x55) ++ return true; ++ } ++ ++ dev_info(chip->dev, "%s reg: %x always busy\n", __func__, reg); ++ return false; ++} ++ ++static bool nuvolta_1665_check_buffer_ready(struct nuvolta_1665_chg *chip) ++{ ++ return nuvolta_1665_check_cmd_free(chip, 0x0024); ++} ++ ++static bool nuvolta_1665_check_rx_ready(struct nuvolta_1665_chg *chip) ++{ ++ return nuvolta_1665_check_cmd_free(chip, 0x0025); ++} ++ ++static int nuvolta_1665_start_tx_function(struct nuvolta_1665_chg *chip, ++ bool en) ++{ ++ int ret = 0; ++ ++ dev_err(chip->dev, "%s enable:%d\n", __func__, en); ++ if (!chip->fw_update) { ++ if (en) { ++ ret = rx1665_write(chip, 0x01, TRX_MODE_EN); ++ if (ret >= 0) { ++ dev_info(chip->dev, ++ "ic work on rtx mode,start reverse charging,ret:%d\n", ++ ret); ++ chip->is_reverse_mode = 1; ++ return 1; ++ } ++ } else ++ ret = rx1665_write(chip, 0x00, TRX_MODE_EN); ++ ++ dev_info(chip->dev, "[%s] ret = %d\n", __func__, ret); ++ } ++ ++ dev_info(chip->dev, "Not open reverse charging start:%d\n", en); ++ ret = nuvolta_1665_set_reverse_chg_mode(chip, false); ++ return 0; ++} ++ ++static int rx_set_reverse_boost_enable_gpio(struct nuvolta_1665_chg *chip, ++ int enable) ++{ ++ int ret = 0; ++ if (gpio_is_valid(chip->reverse_boost_gpio)) { ++ ret = gpio_request(chip->reverse_boost_gpio, ++ "reverse-boost-enable-gpio"); ++ if (ret) { ++ dev_err(chip->dev, ++ "%s: unable to reverse_boost_enable_gpio [%d]\n", ++ __func__, chip->reverse_boost_gpio); ++ } ++ ++ ret = gpio_direction_output(chip->reverse_boost_gpio, !!enable); ++ if (ret) { ++ dev_err(chip->dev, ++ "%s: cannot set direction for reverse_boost_enable_gpio gpio [%d]\n", ++ __func__, chip->reverse_boost_gpio); ++ } ++ gpio_free(chip->reverse_boost_gpio); ++ } else ++ dev_err(chip->dev, "%s: unable to set reverse_boost_enable_gpio\n", ++ __func__); ++ ++ return ret; ++} ++ ++static int nuvolta_1665_set_reverse_gpio(struct nuvolta_1665_chg *chip, ++ int enable) ++{ ++ int ret = 0; ++ union power_supply_propval val = { ++ 0, ++ }; ++ ++ val.intval = !!enable; ++ ++ if (gpio_is_valid(chip->tx_on_gpio)) { ++ if (!enable) { ++ rx_set_reverse_boost_enable_gpio(chip, enable); ++ msleep(100); ++ } ++ ++ ret = gpio_request(chip->tx_on_gpio, "tx-on-gpio"); ++ if (ret) { ++ dev_err(chip->dev, "%s: unable to request tx_on gpio\n", ++ __func__); ++ } ++ ret = gpio_direction_output(chip->tx_on_gpio, enable); ++ if (ret) { ++ dev_err(chip->dev, "%s: cannot set direction for tx_on gpio\n", ++ __func__); ++ } ++ ++ ret = gpio_get_value(chip->tx_on_gpio); ++ dev_info(chip->dev, "txon gpio: %d\n", ret); ++ dev_err(chip->dev, "%s-2 chip->tx_on_gpio:%d, gpio is valid:%d,\n", ++ __func__, chip->tx_on_gpio, ++ gpio_is_valid(chip->tx_on_gpio)); ++ gpio_free(chip->tx_on_gpio); ++ if (enable) { ++ msleep(100); ++ rx_set_reverse_boost_enable_gpio(chip, enable); ++ } ++ } else ++ dev_err(chip->dev, "%s: unable to set tx_on gpio\n", __func__); ++ ++ return ret; ++} ++ ++static int nuvolta_1665_set_reverse_chg_mode(struct nuvolta_1665_chg *chip, ++ int enable) ++{ ++ int rc = 0; ++ ++ if (chip->fw_update) { ++ goto out; ++ } ++ ++ nuvolta_1665_set_reverse_gpio(chip, enable); ++ ++ if (enable) { ++ chip->is_boost_mode = 1; ++ dev_info(chip->dev, "enable reverse charging\n"); ++ chip->reverse_chg_en = true; ++ ++ msleep(100); ++ rc = nuvolta_1665_start_tx_function(chip, true); ++ ++ } else { ++ chip->is_boost_mode = 0; ++ dev_info(chip->dev, "disable reverse charging\n"); ++ chip->reverse_chg_en = false; ++ chip->alarm_flag = false; ++ chip->is_reverse_mode = 0; ++ chip->reverse_pen_soc = 255; ++ pen_soc_count = 0; ++ curr_count = 0; ++ ++ msleep(100); ++ cancel_delayed_work(&chip->reverse_chg_state_work); ++ cancel_delayed_work(&chip->reverse_dping_state_work); ++ cancel_delayed_work(&chip->reverse_chg_work); ++ cancel_delayed_work(&chip->pen_check_work); ++ pm_relax(chip->dev); ++ } ++ ++ schedule_delayed_work(&chip->pen_check_work, msecs_to_jiffies(1500)); ++ ++out: ++ return 0; ++} ++ ++static int nuvolta_1665_get_cep(struct nuvolta_1665_chg *chip, int *cep) ++{ ++ int ret = 0; ++ bool status = true; ++ u8 read_buf[128]; ++ ++ if (!chip->power_good_flag) { ++ *cep = 0; ++ return ret; ++ } ++ ++ status = nuvolta_1665_check_rx_ready(chip); ++ if (!status) ++ return 0; ++ ++ ret = rx1665_write(chip, 0x88, 0x0062); ++ if (ret < 0) ++ return ret; ++ ++ status = nuvolta_1665_check_buffer_ready(chip); ++ if (!status) ++ return 0; ++ ++ ret = rx1665_read_buffer(chip, read_buf, RX_DATA_INFO, 30); ++ if (ret < 0) ++ return ret; ++ ++ *cep = read_buf[10]; ++ dev_info(chip->dev, "get rx cep: %d\n", *cep); ++ ++ return ret; ++} ++ ++static int nuvolta_1665_set_vout(struct nuvolta_1665_chg *chip, int vout) ++{ ++ int ret = 0; ++ bool status = true; ++ u8 vout_l = 0, vout_h = 0; ++ int max_vol = 19500; ++ int cep = 0; ++ ++ if (!chip->power_good_flag) { ++ dev_info(chip->dev, "power good disonline, don't set vout\n"); ++ return 0; ++ } ++ ++ if (chip->parallel_charge) { ++ ret = nuvolta_1665_get_cep(chip, &cep); ++ if (ret < 0) { ++ dev_info(chip->dev, "get cep failed : %d\n", ret); ++ return ret; ++ } else if (ABS(cep) > ABS_CEP_VALUE) { ++ dev_info(chip->dev, "[%s] vout: %d, cep: %d, not set vout\n", ++ __func__, vout, cep); ++ return 0; ++ } ++ } ++ ++ if (vout < 4000) { ++ vout = 6000; ++ } else if (vout > max_vol) { ++ vout = max_vol; ++ } ++ ++ vout_h = (u8)(vout >> 8); ++ vout_l = (u8)(vout & 0xFF); ++ ++ status = nuvolta_1665_check_rx_ready(chip); ++ if (!status) ++ return 0; ++ ++ ret = rx1665_write(chip, 0x31, 0x0000); ++ if (ret < 0) ++ return ret; ++ ++ ret = rx1665_write(chip, 0x02, 0x0001); ++ if (ret < 0) ++ return ret; ++ ++ ret = rx1665_write(chip, vout_l, 0x0002); ++ if (ret < 0) ++ return ret; ++ ++ ret = rx1665_write(chip, vout_h, 0x0003); ++ if (ret < 0) ++ return ret; ++ ++ ret = rx1665_write(chip, 0x04, 0x0060); ++ if (ret < 0) ++ return ret; ++ ++ chip->vout_setted = vout; ++ dev_info(chip->dev, "set rx vout: %d\n", vout); ++ ++ return ret; ++} ++ ++static int nuvolta_1665_get_vrect(struct nuvolta_1665_chg *chip, int *vrect) ++{ ++ int ret = 0; ++ bool status = true; ++ u8 read_buf[128]; ++ ++ if (!chip->power_good_flag) { ++ *vrect = 0; ++ return ret; ++ } ++ ++ status = nuvolta_1665_check_rx_ready(chip); ++ if (!status) ++ return 0; ++ ++ ret = rx1665_write(chip, 0x88, 0x0062); ++ if (ret < 0) ++ return ret; ++ ++ status = nuvolta_1665_check_buffer_ready(chip); ++ if (!status) ++ return 0; ++ ++ ret = rx1665_read_buffer(chip, read_buf, RX_DATA_INFO, 30); ++ if (ret < 0) ++ return ret; ++ ++ *vrect = read_buf[19] * 256 + read_buf[18]; ++ dev_info(chip->dev, "get rx vrect: %d\n", *vrect); ++ ++ return ret; ++} ++ ++static int nuvolta_1665_get_vout(struct nuvolta_1665_chg *chip, int *vout) ++{ ++ int ret = 0; ++ bool status = true; ++ u8 read_buf[128]; ++ s8 cep = 0; ++ ++ if (!chip->power_good_flag) { ++ *vout = 0; ++ return ret; ++ } ++ ++ status = nuvolta_1665_check_rx_ready(chip); ++ if (!status) ++ return 0; ++ ++ ret = rx1665_write(chip, 0x88, 0x0062); ++ if (ret < 0) ++ return ret; ++ ++ status = nuvolta_1665_check_buffer_ready(chip); ++ if (!status) ++ return 0; ++ ++ ret = rx1665_read_buffer(chip, read_buf, RX_DATA_INFO, 30); ++ if (ret < 0) ++ return ret; ++ ++ *vout = read_buf[21] * 256 + read_buf[20]; ++ cep = read_buf[10]; ++ dev_info(chip->dev, "get rx vout: %d, cep: %d\n", *vout, cep); ++ chip->reverse_vout = *vout; ++ ++ return ret; ++} ++ ++static int nuvolta_1665_get_iout(struct nuvolta_1665_chg *chip, int *iout) ++{ ++ int ret = 0; ++ bool status = true; ++ u8 read_buf[128]; ++ ++ if (!chip->power_good_flag) { ++ *iout = 0; ++ return ret; ++ } ++ ++ status = nuvolta_1665_check_rx_ready(chip); ++ if (!status) ++ return 0; ++ ++ ret = rx1665_write(chip, 0x88, 0x0062); ++ if (ret < 0) ++ return ret; ++ ++ status = nuvolta_1665_check_buffer_ready(chip); ++ if (!status) ++ return 0; ++ ++ ret = rx1665_read_buffer(chip, read_buf, RX_DATA_INFO, 30); ++ if (ret < 0) ++ return ret; ++ ++ *iout = read_buf[17] * 256 + read_buf[16]; ++ ++ dev_info(chip->dev, "get rx iout: %d\n", *iout); ++ chip->reverse_iout = *iout; ++ return ret; ++} ++ ++static int nuvolta_1665_get_temp(struct nuvolta_1665_chg *chip, int *temp) ++{ ++ int ret = 0; ++ bool status = true; ++ u8 read_buf[128]; ++ ++ if (!chip->power_good_flag) { ++ *temp = 0; ++ return ret; ++ } ++ ++ status = nuvolta_1665_check_rx_ready(chip); ++ if (!status) ++ return 0; ++ ++ ret = rx1665_write(chip, 0x88, 0x0062); ++ if (ret < 0) ++ return 0; ++ ++ status = nuvolta_1665_check_buffer_ready(chip); ++ if (!status) ++ return 0; ++ ++ ret = rx1665_read_buffer(chip, read_buf, RX_DATA_INFO, 30); ++ if (ret < 0) ++ return ret; ++ ++ *temp = read_buf[15] * 256 + read_buf[14]; ++ dev_info(chip->dev, "get rx temp: %d\n", *temp); ++ ++ return ret; ++} ++ ++static int tx_info_update(struct nuvolta_1665_chg *chip, u8 *buff) ++{ ++ int ret = 0; ++ ++ ret = rx1665_write(chip, 0x88, 0x0000); ++ if (ret < 0) ++ return ret; ++ ++ ret = rx1665_write(chip, 0x01, 0x0060); ++ if (ret < 0) ++ return ret; ++ ++ msleep(20); ++ ++ ret = rx1665_read_buffer(chip, buff, 0x1200, 256); ++ if (ret < 0) { ++ dev_err(chip->dev, "Update %s failed!\n", __func__); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int nuvolta_1665_get_reverse_vout(struct nuvolta_1665_chg *chip, ++ int *vout) ++{ ++ int ret = 0; ++ ++ *vout = 256 * sram_buffer[19] + sram_buffer[18]; ++ ++ dev_info(chip->dev, "get tx reverse vout: %d\n", *vout); ++ chip->reverse_vout = *vout; ++ ++ return ret; ++} ++ ++static int nuvolta_1665_get_reverse_iout(struct nuvolta_1665_chg *chip, ++ int *iout) ++{ ++ int ret = 0; ++ ++ *iout = 256 * sram_buffer[17] + sram_buffer[16]; ++ ++ dev_info(chip->dev, "get tx reverse iout: %d\n", *iout); ++ chip->reverse_iout = *iout; ++ ++ if (chip->reverse_iout > 500 || chip->reverse_iout < 50) { ++ curr_count++; ++ } ++ if (curr_count >= 5) { ++ curr_count = 0; ++ dev_info(chip->dev, "The pen position is out of the right place.\n"); ++ nuvolta_1665_set_reverse_chg_mode(chip, false); ++ chip->is_reverse_mode = 0; ++ chip->is_reverse_chg = 2; ++ } ++ return ret; ++} ++ ++static int nuvolta_1665_get_reverse_temp(struct nuvolta_1665_chg *chip, ++ int *temp) ++{ ++ int ret = 0; ++ ++ *temp = 256 * sram_buffer[15] + sram_buffer[14]; ++ ++ dev_info(chip->dev, "get tx reverse temperature: %d\n", *temp); ++ chip->reverse_temp = *temp; ++ ++ return ret; ++} ++ ++#define SOC_100_RETRY 20 ++static int soc_count; ++static int nuvolta_1665_get_reverse_soc(struct nuvolta_1665_chg *chip) ++{ ++ int ret = 0; ++ u8 soc = 0xFF; ++ static int last_soc; ++ ++ soc = sram_buffer[9]; ++ ++ if ((soc < 0) || (soc > 0x64)) { ++ if (soc == 0xFF) { ++ dev_info(chip->dev, "[reverse] soc is default 0xFF\n"); ++ chip->reverse_pen_soc = 0xFF; ++ return ret; ++ } else { ++ dev_info(chip->dev, "[reverse] soc illegal: %d\n", soc); ++ return ret; ++ } ++ } ++ ++ chip->reverse_pen_soc = soc + 1; ++ if (chip->reverse_pen_soc > 100) ++ chip->reverse_pen_soc = 100; ++ dev_info(chip->dev, "get tx reverse raw_soc: %d, UI_soc:%d\n", soc, ++ chip->reverse_pen_soc); ++ ++ if ((soc == 100) && (pen_soc_count < SOC_100_RETRY)) { ++ dev_info(chip->dev, "[reverse] soc is 100 count: %d\n", pen_soc_count); ++ pen_soc_count++; ++ } else { ++ pen_soc_count = 0; ++ } ++ ++ if ((soc - last_soc) == 0) ++ soc_count++; ++ else { ++ dev_info(chip->dev, "pen soc is change soc_count=%d!\n", soc_count); ++ soc_count = 0; ++ } ++ ++ if (pen_soc_count == SOC_100_RETRY) { ++ dev_info(chip->dev, ++ "[reverse] soc is 100 exceed 6 times, disable reverse chg!\n"); ++ nuvolta_1665_set_reverse_chg_mode(chip, false); ++ chip->is_reverse_mode = 0; ++ chip->is_reverse_chg = 2; ++ pen_soc_count = 0; ++ } ++ ++ if (soc_count >= 180) { ++ dev_info(chip->dev, ++ "Happen pen lock, need disable/enable reverse chg!\n"); ++ soc_count = 0; ++ ret = nuvolta_1665_set_reverse_chg_mode(chip, false); ++ chip->is_reverse_mode = 0; ++ chip->is_reverse_chg = 2; ++ msleep(30); ++ ret = nuvolta_1665_set_reverse_chg_mode(chip, true); ++ } ++ ++ last_soc = soc; ++ ++ return ret; ++} ++ ++static u8 nuvolta_1665_get_fastchg_result(struct nuvolta_1665_chg *chip) ++{ ++ u8 fastchg_result = 0; ++ bool status = true; ++ int ret = 0; ++ ++ status = nuvolta_1665_check_rx_ready(chip); ++ if (!status) ++ return fastchg_result; ++ ++ ret = rx1665_write(chip, 0x88, 0x0062); ++ if (ret < 0) ++ return fastchg_result; ++ ++ status = nuvolta_1665_check_buffer_ready(chip); ++ if (!status) ++ return fastchg_result; ++ ++ ret = rx1665_read(chip, &fastchg_result, RX_FASTCHG_RESULT); ++ if (ret < 0) ++ return fastchg_result; ++ ++ dev_info(chip->dev, "[%s] fastch result: %d\n", __func__, fastchg_result); ++ ++ return fastchg_result; ++} ++ ++static void nuvolta_1665_power_off_err(struct nuvolta_1665_chg *chip) ++{ ++ int ret = 0; ++ u8 err_code = 0; ++ ++ ret = rx1665_read(chip, &err_code, RX_POWER_OFF_ERR); ++ if (ret < 0) ++ return; ++ ++ /*unknown:0x00 otp:0x03 ovp:0x04 ocp:0x05 sc:0x06 hop:0x10 ++ *sop:0x11 sleep:0x0B ovl:0x13 vup:0x14 rect_err:0x15 ++ */ ++ dev_info(chip->dev, "[%s] power off err = 0x%x\n", __func__, err_code); ++ return; ++} ++ ++static void nuvolta_1665_adapter_handle(struct nuvolta_1665_chg *chip) ++{ ++ dev_info(chip->dev, "[%s] adapter: %d, epp: %d\n", __func__, ++ chip->adapter_type, chip->epp); ++ ++ dev_info(chip->dev, "set icl for SDP/CDP/DCP/QC2 adapter\n"); ++ chip->pre_curr = 750; ++ ++ schedule_delayed_work(&chip->chg_monitor_work, msecs_to_jiffies(1000)); ++ return; ++} ++ ++static void nuvolta_1665_clear_int(struct nuvolta_1665_chg *chip) ++{ ++ int ret = 0; ++ ++ ret = rx1665_write(chip, 0x68, 0x0000); ++ ret = rx1665_write(chip, 0x01, 0x0060); ++ ++ dev_info(chip->dev, "[%s] ret: %d\n", __func__, ret); ++ return; ++} ++ ++static void reverse_chg_state_set_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, reverse_chg_state_work.work); ++ int ret; ++ ++ dev_info(chip->dev, "no rx found and disable reverse charging\n"); ++ mutex_lock(&chip->reverse_op_lock); ++ ret = nuvolta_1665_set_reverse_chg_mode(chip, false); ++ chip->is_reverse_chg = REVERSE_STATE_TIMEOUT; ++ chip->is_reverse_mode = 0; ++ mutex_unlock(&chip->reverse_op_lock); ++ ++ return; ++} ++ ++static void reverse_dping_state_set_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, reverse_dping_state_work.work); ++ int ret; ++ ++ dev_info(chip->dev, "tx mode fault and disable reverse charging\n"); ++ mutex_lock(&chip->reverse_op_lock); ++ ret = nuvolta_1665_set_reverse_chg_mode(chip, false); ++ chip->is_reverse_mode = 0; ++ chip->is_reverse_chg = REVERSE_STATE_ENDTRANS; ++ mutex_unlock(&chip->reverse_op_lock); ++ ++ return; ++} ++ ++static void pen_check_worker(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of(work, ++ struct nuvolta_1665_chg, pen_check_work.work); ++ ++ static bool pen_charge_en = false; ++ bool enable = (chip->reverse_pen_soc >= 0 && chip->reverse_pen_soc <= 100); ++ ++ if (pen_charge_en != enable) ++ pen_charge_state_notifier_call_chain(enable); ++ ++ if (chip->reverse_chg_en != enable) ++ nuvolta_1665_set_reverse_chg_mode(chip, enable); ++ ++ pen_charge_en = enable; ++} ++ ++static int nuvolta_1665_reverse_enable_fod(struct nuvolta_1665_chg *chip, ++ bool enable) ++{ ++ int ret = 0; ++ bool status = true; ++ u8 gain = REVERSE_FOD_GAIN; ++ u8 offset = REVERSE_FOD_OFFSET; ++ ++ if (!enable) ++ return ret; ++ ++ status = nuvolta_1665_check_rx_ready(chip); ++ if (!status) ++ return -1; ++ ++ ret = rx1665_write(chip, 0x23, 0x0000); ++ if (ret < 0) ++ return ret; ++ ++ ret = rx1665_write(chip, 0x01, 0x0001); ++ if (ret < 0) ++ return ret; ++ ++ ret = rx1665_write(chip, gain, 0x0002); ++ if (ret < 0) ++ return ret; ++ ++ ret = rx1665_write(chip, offset, 0x0003); ++ if (ret < 0) ++ return ret; ++ ++ ret = rx1665_write(chip, 0x04, 0x0060); ++ if (ret < 0) ++ return ret; ++ ++ dev_info(chip->dev, "[%s] gain: %d, offset: %d\n", __func__, gain, offset); ++ return ret; ++} ++ ++static void nuvolta_1665_reverse_chg_handler(struct nuvolta_1665_chg *chip, ++ u16 int_flag) ++{ ++ if (int_flag & RTX_INT_EPT) { ++ if (tx_info_update(chip, sram_buffer) >= 0) ++ dev_info(chip->dev, "tx mode ept. the code:0x%02x\n", ++ sram_buffer[10]); ++ goto out; ++ } ++ ++ if (int_flag & INT_GET_DPING) { ++ dev_info(chip->dev, "TRX get dping and disable reverse charging \n"); ++ nuvolta_1665_set_reverse_chg_mode(chip, false); ++ chip->is_reverse_mode = 0; ++ chip->is_reverse_chg = 2; ++ goto out; ++ } ++ ++ if (int_flag & RTX_INT_START_DPING) { ++ pm_relax(chip->dev); ++ dev_info(chip->dev, "tx mode ping\n"); ++ } ++ ++ if (int_flag & RTX_INT_GET_CFG) { ++ pm_stay_awake(chip->dev); ++ ++ /* set reverse charging state to started*/ ++ nuvolta_1665_reverse_enable_fod(chip, true); ++ msleep(50); ++ ++ //start reverse chg infor work ++ cancel_delayed_work_sync(&chip->reverse_chg_work); ++ msleep(10); ++ schedule_delayed_work(&chip->reverse_chg_work, 0); ++ /* set reverse charging state to started */ ++ if (chip->is_reverse_mode || chip->is_boost_mode) { ++ chip->is_reverse_chg = 4; ++ } ++ dev_info(chip->dev, "tx mode get rx\n"); ++ } ++ ++ if (int_flag & INT_GET_SS) ++ dev_info(chip->dev, "TRX get ss\n"); ++ if (int_flag & INT_GET_ID) ++ dev_info(chip->dev, "TRX get id\n"); ++ if (int_flag & INT_INIT_TX) ++ dev_info(chip->dev, "TRX reset done\n"); ++ ++ if (int_flag & INT_GET_PPP) { ++ dev_info(chip->dev, "INT_GET_PPP.\n"); ++ if (tx_info_update(chip, sram_buffer) >= 0) { ++ dev_info(chip->dev, ++ "receive dates: 0x%02x: ,0x%02x: ,0x%02x: ,0x%02x: ,0x%02x: ,0x%02x: ,0x%02x: ,0x%02x:\n", ++ sram_buffer[42], sram_buffer[43], ++ sram_buffer[44], sram_buffer[45], ++ sram_buffer[46], sram_buffer[47], ++ sram_buffer[48], sram_buffer[49]); ++ } ++ } ++out: ++ return; ++} ++ ++static void nuvolta_1665_chg_handler(struct nuvolta_1665_chg *chip, ++ u16 int_flag) ++{ ++ switch (int_flag) { ++ case RX_INT_FAST_CHARGE: ++ dev_info(chip->dev, "[%s] fastchg finish!\n", __func__); ++ chip->fc_flag = nuvolta_1665_get_fastchg_result(chip); ++ if (chip->fc_flag) { ++ nuvolta_1665_adapter_handle(chip); ++ } else if (chip->set_fastcharge_vout_cnt++ < 3) { ++ dev_info(chip->dev, "set fastchg vol failed, retry %d\n", ++ chip->set_fastcharge_vout_cnt); ++ msleep(2000); ++ } else { ++ dev_info(chip->dev, "set fastchg vol failed finally\n"); ++ nuvolta_1665_adapter_handle(chip); ++ } ++ break; ++ case RX_INT_OCP_OTP_ALARM: ++ dev_info(chip->dev, "[%s] OCP OR OTP trigger\n", __func__); ++ schedule_delayed_work(&chip->rx_alarm_work, ++ msecs_to_jiffies(500)); ++ break; ++ case RX_INT_POWER_OFF: ++ dev_info(chip->dev, "[%s] POWER OFF INT trigger\n", __func__); ++ nuvolta_1665_power_off_err(chip); ++ break; ++ default: ++ break; ++ } ++ ++ return; ++} ++ ++static void nu1665_dump_regs(struct nuvolta_1665_chg *chip) ++{ ++ u8 int_l = 0; ++ int ret = 0; ++ ++ ret = rx1665_read(chip, &int_l, REG_RX_REV_CMD); //0x0020 ++ if (ret < 0) { ++ dev_err(chip->dev, "%s read int 0x20 error\n", __func__); ++ goto exit; ++ } ++ ++ ret = rx1665_read(chip, &int_l, 0x0021); //0x0021 ++ if (ret < 0) { ++ dev_err(chip->dev, "%s read int 0x21 error\n", __func__); ++ goto exit; ++ } ++ ++ ret = rx1665_read(chip, &int_l, 0x0022); //0x0022 ++ if (ret < 0) { ++ dev_err(chip->dev, "%s read int 0x22 error\n", __func__); ++ goto exit; ++ } ++ ++ ret = rx1665_read(chip, &int_l, 0x0023); //0x0022 ++ if (ret < 0) { ++ dev_err(chip->dev, "%s read int 0x23 error\n", __func__); ++ goto exit; ++ } ++exit: ++ return; ++} ++ ++static void nuvolta_1665_wireless_int_work(struct work_struct *work) ++{ ++ u16 int_flags = 0; ++ u8 tmp = 0; //int_l = 0, int_h = 0, ++ u8 int_trx_mode = RTX_MODE; ++ int ret = 0; ++ int irq_level; ++ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, wireless_int_work.work); ++ ++ if (gpio_is_valid(chip->irq_gpio)) ++ irq_level = gpio_get_value(chip->irq_gpio); ++ else { ++ dev_err(chip->dev, "%s: irq gpio not provided\n", __func__); ++ irq_level = -1; ++ pm_relax(chip->dev); ++ return; ++ } ++ dev_info(chip->dev, "irq gpio status: %d\n", irq_level); ++ if (irq_level) { ++ dev_info(chip->dev, "irq is high level, ignore%d\n", irq_level); ++ pm_relax(chip->dev); ++ return; ++ } ++ mutex_lock(&chip->wireless_chg_int_lock); ++ ++ ret = rx1665_read(chip, &tmp, REG_RX_REV_CMD); //0x0020 ++ if (ret < 0) { ++ dev_err(chip->dev, "%s read int 0x20 error\n", __func__); ++ goto exit; ++ } ++ int_flags |= tmp; ++ tmp = 0; ++ ++ ret = rx1665_read(chip, &tmp, REG_RX_REV_DATA1); //0x0021 ++ if (ret < 0) { ++ dev_err(chip->dev, "%s read int 0x21 error\n", __func__); ++ goto exit; ++ } ++ int_flags |= (tmp << 8); ++ tmp = 0; ++ ++ if (rx1665_read(chip, &tmp, 0x0022) < 0) { ++ dev_err(chip->dev, "%s read int 0x22 error\n", __func__); ++ goto exit; ++ } ++ int_flags |= (tmp << 16); ++ tmp = 0; ++ ++ if (rx1665_read(chip, &tmp, 0x0023) < 0) { ++ dev_err(chip->dev, "%s read int 0x23 error\n", __func__); ++ goto exit; ++ } ++ int_flags |= (tmp << 24); ++ ++ nu1665_dump_regs(chip); ++ dev_info(chip->dev, "int_flag: 0x%x\n", int_flags); ++ nuvolta_1665_clear_int(chip); ++ ++ dev_info(chip->dev, "ic work only rtx mode\n"); ++ if (int_trx_mode == RTX_MODE) { ++ dev_info(chip->dev, "ic work on rtx mode\n"); ++ nuvolta_1665_reverse_chg_handler(chip, int_flags); ++ } else { ++ dev_info(chip->dev, "ic work on rx mode\n"); ++ nuvolta_1665_chg_handler(chip, int_flags); ++ } ++ ++exit: ++ mutex_unlock(&chip->wireless_chg_int_lock); ++ return; ++} ++ ++static irqreturn_t nuvolta_1665_interrupt_handler(int irq, void *dev_id) ++{ ++ struct nuvolta_1665_chg *chip = dev_id; ++ ++ dev_info(chip->dev, "[%s]\n", __func__); ++ pm_stay_awake(chip->dev); ++ schedule_delayed_work(&chip->wireless_int_work, 0); ++ ++ return IRQ_HANDLED; ++} ++ ++static void nuvolta_1665_reset_parameters(struct nuvolta_1665_chg *chip) ++{ ++ dev_info(chip->dev, "%s\n", __func__); ++ ++ chip->power_good_flag = 0; ++ chip->ss = 2; ++ chip->epp = 0; ++ chip->qc_enable = false; ++ chip->chg_phase = NORMAL_MODE; ++ chip->adapter_type = 0; ++ chip->fc_flag = 0; ++ chip->set_fastcharge_vout_cnt = 0; ++ chip->parallel_charge = false; ++ chip->reverse_chg_en = false; ++ chip->alarm_flag = false; ++ ++ return; ++} ++ ++static void nuvolta_1665_pg_det_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, wireless_pg_det_work.work); ++ int ret = 0, wls_switch_usb = 0; ++ ++ if (gpio_is_valid(chip->power_good_gpio)) { ++ ret = gpio_get_value(chip->power_good_gpio); ++ if (ret) { ++ dev_info(chip->dev, "power_good high, wireless attached\n"); ++ chip->power_good_flag = 1; ++ chip->adapter_type = ADAPTER_SDP; ++ cancel_delayed_work(&chip->delay_report_status_work); ++ cancel_delayed_work(&chip->rx_enable_usb_work); ++ } else { ++ dev_info(chip->dev, "power_good low, wireless detached\n"); ++ nuvolta_1665_reset_parameters(chip); ++ ++ cancel_delayed_work(&chip->chg_monitor_work); ++ cancel_delayed_work(&chip->max_power_control_work); ++ cancel_delayed_work(&chip->rx_alarm_work); ++ schedule_delayed_work(&chip->rx_enable_usb_work, ++ msecs_to_jiffies(500)); ++ schedule_delayed_work(&chip->delay_report_status_work, ++ msecs_to_jiffies(2000)); ++ ++ dev_info(chip->dev, "wireless switch to usb: %d\n", ++ wls_switch_usb); ++ } ++ } ++} ++ ++static void nuvolta_1665_get_charge_phase(struct nuvolta_1665_chg *chip, ++ int *chg_phase) ++{ ++ switch (*chg_phase) { ++ case NORMAL_MODE: ++ if (chip->batt_soc == 100) { ++ *chg_phase = TAPER_MODE; ++ dev_info(chip->dev, "change normal mode to tapter mode"); ++ } ++ break; ++ case TAPER_MODE: ++ if ((chip->batt_soc == 100) && ++ (chip->chg_status == POWER_SUPPLY_STATUS_FULL)) { ++ *chg_phase = FULL_MODE; ++ dev_info(chip->dev, "change taper mode to full mode"); ++ } else if (chip->batt_soc < 99) { ++ *chg_phase = NORMAL_MODE; ++ dev_info(chip->dev, "change taper mode to normal mode"); ++ } ++ break; ++ case FULL_MODE: ++ if ((chip->chg_status == POWER_SUPPLY_STATUS_CHARGING) && ++ (chip->batt_soc < 100)) { ++ *chg_phase = RECHG_MODE; ++ dev_info(chip->dev, "change full mode to recharge mode"); ++ } ++ break; ++ case RECHG_MODE: ++ if (chip->chg_status == POWER_SUPPLY_STATUS_FULL) { ++ *chg_phase = FULL_MODE; ++ dev_info(chip->dev, "change recharge mode to full mode"); ++ } ++ break; ++ default: ++ break; ++ } ++ return; ++} ++ ++static void nuvolta_1665_get_charging_info(struct nuvolta_1665_chg *chip) ++{ ++ int vout, iout, vrect; ++ union power_supply_propval val = { ++ 0, ++ }; ++ int ret = 0; ++ ++ if (!chip) ++ return; ++ ++ if (!chip->batt_psy) ++ chip->batt_psy = power_supply_get_by_name("battery"); ++ if (!chip->batt_psy) ++ dev_err(chip->dev, "failed to get batt_psy\n"); ++ else { ++ power_supply_get_property(chip->batt_psy, ++ POWER_SUPPLY_PROP_CAPACITY, &val); ++ chip->batt_soc = val.intval; ++ power_supply_get_property(chip->batt_psy, ++ POWER_SUPPLY_PROP_STATUS, &val); ++ chip->chg_status = val.intval; ++ nuvolta_1665_get_charge_phase(chip, &chip->chg_phase); ++ } ++ ++ ret = nuvolta_1665_get_iout(chip, &iout); ++ if (ret < 0) { ++ dev_err(chip->dev, "get iout failed\n"); ++ iout = 0; ++ } ++ ret = nuvolta_1665_get_vout(chip, &vout); ++ if (ret < 0) { ++ dev_err(chip->dev, "get vout failed\n"); ++ vout = 0; ++ } ++ ret = nuvolta_1665_get_vrect(chip, &vrect); ++ if (ret < 0) { ++ dev_err(chip->dev, "get vrect failed\n"); ++ vrect = 0; ++ } ++ ++ dev_info(chip->dev, ++ "%s:Vout:%d, Iout:%d, Vrect:%d, soc: %d, status: %d, chg_phase: %d\n", ++ __func__, vout, iout, vrect, chip->batt_soc, chip->chg_status, ++ chip->chg_phase); ++} ++ ++static void nuvolta_1665_standard_epp_work(struct nuvolta_1665_chg *chip) ++{ ++ dev_info(chip->dev, "run standard epp work\n"); ++ if (chip->chg_phase == FULL_MODE) ++ chip->target_curr = 250; ++ if (chip->chg_phase == RECHG_MODE) ++ chip->target_curr = 550; ++ ++ if (chip->target_vol != chip->pre_vol) { ++ dev_info(chip->dev, "set new vout: %d, pre vout: %d\n", ++ chip->target_vol, chip->pre_vol); ++ nuvolta_1665_set_vout(chip, chip->target_vol); ++ chip->pre_vol = chip->target_vol; ++ } ++ ++ if (chip->target_curr != chip->pre_curr) { ++ dev_info(chip->dev, "set new icl: %d, pre icl: %d\n", ++ chip->target_curr, chip->pre_curr); ++ chip->pre_curr = chip->target_curr; ++ } ++ ++ return; ++} ++ ++static void nuvolta_1665_monitor_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, chg_monitor_work.work); ++ nuvolta_1665_get_charging_info(chip); ++ ++ nuvolta_1665_standard_epp_work(chip); ++ ++ schedule_delayed_work(&chip->chg_monitor_work, msecs_to_jiffies(5000)); ++} ++ ++static void nuvolta_1665_delay_report_status_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, delay_report_status_work.work); ++ ++ //TODO:delay report discharging ++ dev_info(chip->dev, "just for use chip: %d\n", chip->power_good_flag); ++ return; ++} ++ ++static void nuvolta_1665_check_rx_alarm(struct nuvolta_1665_chg *chip, ++ bool *ocp_flag, bool *otp_flag) ++{ ++ int iout = 0, temp = 0; ++ int ret = 0; ++ ++ ret = nuvolta_1665_get_iout(chip, &iout); ++ if (ret < 0) ++ *ocp_flag = false; ++ else ++ *ocp_flag = (iout >= RX_MAX_IOUT); ++ ++ ret = nuvolta_1665_get_temp(chip, &temp); ++ if (ret < 0) ++ *otp_flag = false; ++ else ++ *otp_flag = (temp >= RX_MAX_TEMP); ++ ++ return; ++} ++ ++static void nuvolta_1665_rx_alarm_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = ++ container_of(work, struct nuvolta_1665_chg, rx_alarm_work.work); ++ ++ bool ocp_flag = false, otp_flag = false; ++ ++ nuvolta_1665_check_rx_alarm(chip, &ocp_flag, &otp_flag); ++ if ((!ocp_flag) && (!otp_flag)) ++ return; ++ ++ schedule_delayed_work(&chip->rx_alarm_work, msecs_to_jiffies(4000)); ++ return; ++} ++ ++static void nu1665_hall3_irq_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, hall3_irq_work.work); ++ ++ if (chip->fw_update) { ++ dev_info(chip->dev, "[hall3] fw updating, don't enable reverse chg\n"); ++ return; ++ } ++ ++ if (chip->hall3_online) ++ nuvolta_1665_set_reverse_chg_mode(chip, true); ++ else if (!chip->hall4_online) { ++ nuvolta_1665_set_reverse_chg_mode(chip, false); ++ chip->is_reverse_mode = 0; ++ chip->is_reverse_chg = 2; ++ } else ++ dev_info(chip->dev, ++ "[hall3] hall4 online, don't disable reverse charge\n"); ++ ++ return; ++} ++ ++static void nu1665_hall4_irq_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, hall4_irq_work.work); ++ ++ if (chip->fw_update) { ++ dev_info(chip->dev, "[hall4] fw updating, don't enable reverse chg\n"); ++ return; ++ } ++ ++ if (chip->hall4_online) ++ nuvolta_1665_set_reverse_chg_mode(chip, true); ++ else if (!chip->hall3_online) { ++ nuvolta_1665_set_reverse_chg_mode(chip, false); ++ chip->is_reverse_mode = 0; ++ chip->is_reverse_chg = 2; ++ } else ++ dev_info(chip->dev, ++ "[hall4] hall3 online, don't disable reverse charge\n"); ++ ++ return; ++} ++ ++static void nu_reverse_chg_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, reverse_chg_work.work); ++ int temp = 0, rc = 0; ++ ++ if (chip->is_reverse_mode || chip->is_boost_mode) { ++ rc = tx_info_update(chip, sram_buffer); ++ if (rc < 0) ++ goto exit; ++ nuvolta_1665_get_reverse_vout(chip, &temp); ++ nuvolta_1665_get_reverse_iout(chip, &temp); ++ nuvolta_1665_get_reverse_temp(chip, &temp); ++ nuvolta_1665_get_reverse_soc(chip); ++ } else { ++ dev_info(chip->dev, "reverse chg closed, return\n"); ++ chip->reverse_pen_soc = 255; ++ chip->reverse_vout = 0; ++ chip->reverse_iout = 0; ++ return; ++ } ++exit: ++ if (chip->reverse_pen_soc == 255) ++ schedule_delayed_work(&chip->reverse_chg_work, 100); ++ else ++ schedule_delayed_work(&chip->reverse_chg_work, 10 * HZ); ++ ++ return; ++} ++ ++static void nu1665_probe_fw_download_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, probe_fw_download_work.work); ++ bool crc_ok = false; ++ int rc = 0; ++ bool fw_ok = false; ++ u8 fw_version = 0; ++ ++ dev_info(chip->dev, "[nuvo] enter %s\n", __func__); ++ ++ if (chip->fw_update) { ++ dev_info(chip->dev, "[nu1665] [%s] FW Update is on going!\n", ++ __func__); ++ return; ++ } ++ ++ pm_stay_awake(chip->dev); ++ chip->fw_update = true; ++ rc = nuvolta_1665_set_reverse_gpio(chip, true); ++ msleep(100); ++ ++ rc = fw_crc_chk(chip); ++ if (rc < 0) { ++ dev_err(chip->dev, "update crc verify failed.\n"); ++ crc_ok = false; ++ } else { ++ dev_info(chip->dev, "update crc verify success.\n"); ++ crc_ok = true; ++ } ++ ++ rc = read_fw_version(chip, &fw_version); ++ dev_info(chip->dev, "%s, FW Version:0x%02x\n", __func__, fw_version); ++ chip->fw_version = fw_version; ++ ++ if (rc < 0) ++ chip->chip_ok = 0; ++ else ++ chip->chip_ok = 1; ++ ++ rc = nuvolta_1665_set_reverse_gpio(chip, false); ++ msleep(1000); ++ ++ if (fw_version >= FW_VERSION) ++ fw_ok = true; ++ if (crc_ok && fw_ok) ++ dev_info(chip->dev, "Don't need update FW,so skip.\n"); ++ else { ++ rc = nuvolta_1665_set_reverse_gpio(chip, true); ++ msleep(100); ++ ++ dev_info(chip->dev, "%s: FW download start\n", __func__); ++ rc = nuvolta_1665_download_fw(chip, false, true); ++ if (rc < 0) { ++ dev_err(chip->dev, "[%s] fw download failed!\n", __func__); ++ } else { ++ dev_info(chip->dev, "%s: FW download end\n", __func__); ++ } ++ ++ rc = nuvolta_1665_set_reverse_gpio(chip, false); ++ msleep(1000); ++ ++ // start crc verify ++ rc = nuvolta_1665_set_reverse_gpio(chip, true); ++ msleep(100); ++ ++ if (fw_crc_chk(chip) < 0) ++ dev_err(chip->dev, "[%s] fw crc failed!\n", __func__); ++ ++ rc = nuvolta_1665_set_reverse_gpio(chip, false); ++ } ++ ++ chip->fw_update = false; ++ pm_relax(chip->dev); ++ if (chip->hall3_online) ++ schedule_delayed_work(&chip->hall3_irq_work, ++ msecs_to_jiffies(2000)); ++ else if (chip->hall4_online) ++ schedule_delayed_work(&chip->hall4_irq_work, ++ msecs_to_jiffies(2000)); ++ else ++ return; ++} ++ ++static irqreturn_t nuvolta_1665_power_good_handler(int irq, void *dev_id) ++{ ++ struct nuvolta_1665_chg *chip = dev_id; ++ ++ if (chip->fw_update) ++ return IRQ_HANDLED; ++ schedule_delayed_work(&chip->wireless_pg_det_work, msecs_to_jiffies(0)); ++ ++ return IRQ_HANDLED; ++} ++ ++static void nuvolta_1665_report_pen_detached(struct nuvolta_1665_chg *chip) ++{ ++ if (chip->hall3_online || chip->hall4_online) ++ return; ++ ++ chip->reverse_pen_soc = 255; ++ pen_charge_state_notifier_call_chain(false); ++ if (chip->nuvo_psy) ++ power_supply_changed(chip->nuvo_psy); ++} ++ ++static irqreturn_t nuvolta_1665_hall3_irq_handler(int irq, void *dev_id) ++{ ++ struct nuvolta_1665_chg *chip = dev_id; ++ ++ if (gpio_is_valid(chip->hall3_gpio)) { ++ if (gpio_get_value(chip->hall3_gpio)) { ++ dev_err(chip->dev, "hall3_irq_handler: pen detach\n"); ++ chip->hall3_online = 0; ++ nuvolta_1665_report_pen_detached(chip); ++ if (chip->nuvo_psy) ++ power_supply_changed(chip->nuvo_psy); ++ if (chip->hall4_online) { ++ dev_err(chip->dev, ++ "hall3_irq_handler: hall4 online, return\n"); ++ return IRQ_HANDLED; ++ } ++ schedule_delayed_work(&chip->hall3_irq_work, ++ msecs_to_jiffies(0)); ++ return IRQ_HANDLED; ++ } else { ++ dev_err(chip->dev, "hall3_irq_handler: pen attach\n"); ++ chip->hall3_online = 1; ++ if (chip->nuvo_psy) ++ power_supply_changed(chip->nuvo_psy); ++ } ++ } ++ ++ if (chip->hall4_online) { ++ dev_err(chip->dev, ++ "[hall3] reverse charging already running, return\n"); ++ return IRQ_HANDLED; ++ } else ++ schedule_delayed_work(&chip->hall3_irq_work, ++ msecs_to_jiffies(10)); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t nuvolta_1665_hall4_irq_handler(int irq, void *dev_id) ++{ ++ struct nuvolta_1665_chg *chip = dev_id; ++ ++ if (gpio_is_valid(chip->hall4_gpio)) { ++ if (gpio_get_value(chip->hall4_gpio)) { ++ dev_err(chip->dev, "hall4_irq_handler: pen detach\n"); ++ chip->hall4_online = 0; ++ nuvolta_1665_report_pen_detached(chip); ++ if (chip->nuvo_psy) ++ power_supply_changed(chip->nuvo_psy); ++ if (chip->hall3_online) { ++ dev_err(chip->dev, ++ "hall4_irq_handler: hall3 online, return\n"); ++ return IRQ_HANDLED; ++ } ++ schedule_delayed_work(&chip->hall4_irq_work, ++ msecs_to_jiffies(0)); ++ return IRQ_HANDLED; ++ } else { ++ dev_err(chip->dev, "hall4_irq_handler: pen attach\n"); ++ chip->hall4_online = 1; ++ if (chip->nuvo_psy) ++ power_supply_changed(chip->nuvo_psy); ++ } ++ } ++ ++ if (chip->hall3_online) { ++ dev_err(chip->dev, ++ "[hall4] reverse charging already running, return\n"); ++ return IRQ_HANDLED; ++ } else ++ schedule_delayed_work(&chip->hall4_irq_work, ++ msecs_to_jiffies(10)); ++ ++ return IRQ_HANDLED; ++} ++ ++static int nuvolta_1665_parse_dt(struct nuvolta_1665_chg *chip) ++{ ++ struct device_node *node = chip->dev->of_node; ++ ++ if (!node) { ++ dev_err(chip->dev, "%s:No DT data Failing Probe\n", __func__); ++ return -EINVAL; ++ } ++ ++ chip->tx_on_gpio = nuvolta_1665_get_gpio(chip->dev, "reverse-chg-ovp"); ++ dev_err(chip->dev, "[%s] print tx_on gpio %d\n", __func__, chip->tx_on_gpio); ++ if (!gpio_is_valid(chip->tx_on_gpio)) { ++ dev_err(chip->dev, "[%s] fail_tx_on gpio %d\n", __func__, ++ chip->tx_on_gpio); ++ return -EINVAL; ++ } ++ ++ chip->irq_gpio = nuvolta_1665_get_gpio(chip->dev, "rx-irq"); ++ dev_err(chip->dev, "[%s] print irq_gpio %d\n", __func__, chip->irq_gpio); ++ if (!gpio_is_valid(chip->irq_gpio)) { ++ dev_err(chip->dev, "[%s] fail_irq_gpio %d\n", __func__, ++ chip->irq_gpio); ++ return -EINVAL; ++ } ++ ++ chip->reverse_boost_gpio = ++ nuvolta_1665_get_gpio(chip->dev, "reverse-boost"); ++ dev_err(chip->dev, "[%s] print reverse_boost_gpio %d\n", __func__, ++ chip->reverse_boost_gpio); ++ if (!gpio_is_valid(chip->reverse_boost_gpio)) { ++ dev_err(chip->dev, "[%s] fail reverse_boost_gpio %d\n", __func__, ++ chip->reverse_boost_gpio); ++ return -EINVAL; ++ } ++ ++ chip->hall3_gpio = nuvolta_1665_get_gpio(chip->dev, "hall-int3"); ++ dev_err(chip->dev, "[%s] print chip->hall3_gpio %d\n", __func__, ++ chip->hall3_gpio); ++ if ((!gpio_is_valid(chip->hall3_gpio))) { ++ dev_err(chip->dev, "[%s] chip->hall3_gpio %d\n", __func__, ++ chip->hall3_gpio); ++ return -EINVAL; ++ } ++ ++ chip->hall4_gpio = nuvolta_1665_get_gpio(chip->dev, "hall-int4"); ++ dev_err(chip->dev, "[%s] print chip->hall4_gpio %d\n", __func__, ++ chip->hall4_gpio); ++ if ((!gpio_is_valid(chip->hall4_gpio))) { ++ dev_err(chip->dev, "[%s] chip->hall4_gpio %d\n", __func__, ++ chip->hall4_gpio); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int nuvolta_rx1665_gpio_init(struct nuvolta_1665_chg *chip) ++{ ++ int ret = 0; ++ int irqn = 0; ++ ++ chip->idt_pinctrl = devm_pinctrl_get(chip->dev); ++ if (IS_ERR(chip->idt_pinctrl)) { ++ ret = PTR_ERR(chip->idt_pinctrl); ++ dev_err(chip->dev, "Failed to get pinctrl config: %d\n", ret); ++ return ret; ++ } ++ ++ chip->idt_gpio_active = pinctrl_lookup_state(chip->idt_pinctrl, "idt_active"); ++ if (IS_ERR(chip->idt_gpio_active)) { ++ ret = PTR_ERR(chip->idt_gpio_active); ++ dev_err(chip->dev, "Failed to get active pinctrl state: %d\n", ret); ++ return ret; ++ } ++ ++ chip->idt_gpio_suspend = pinctrl_lookup_state(chip->idt_pinctrl, "idt_suspend"); ++ if (IS_ERR(chip->idt_gpio_suspend)) { ++ ret = PTR_ERR(chip->idt_gpio_suspend); ++ dev_err(chip->dev, "Failed to get suspend pinctrl state: %d\n", ret); ++ return ret; ++ } ++ ++ /* Set the pinctrl state to active initially */ ++ ret = pinctrl_select_state(chip->idt_pinctrl, chip->idt_gpio_active); ++ if (ret) { ++ dev_err(chip->dev, "Failed to select active pinctrl state: %d\n", ret); ++ return ret; ++ } ++ ++ if (gpio_is_valid(chip->irq_gpio)) { ++ irqn = gpio_to_irq(chip->irq_gpio); ++ if (irqn < 0) { ++ dev_err(chip->dev, "[%s] gpio_to_irq Fail!, irq_gpio:%d \n", ++ __func__, chip->irq_gpio); ++ ret = -1; ++ goto fail_irq_gpio; ++ } ++ chip->irq = irqn; ++ } else { ++ dev_err(chip->dev, "%s: irq gpio not provided\n", __func__); ++ ret = -1; ++ goto fail_irq_gpio; ++ } ++ ++ if (gpio_is_valid(chip->hall3_gpio)) { ++ irqn = gpio_to_irq(chip->hall3_gpio); ++ if (irqn < 0) { ++ ret = irqn; ++ dev_err(chip->dev, "%s:hall3 irq gpio failed\n", __func__); ++ goto fail_irq_gpio; ++ } ++ chip->hall3_irq = irqn; ++ } else { ++ dev_err(chip->dev, "%s:hall3 irq gpio not provided\n", __func__); ++ goto err_hall3_irq_gpio; ++ } ++ ++ if (gpio_is_valid(chip->hall4_gpio)) { ++ irqn = gpio_to_irq(chip->hall4_gpio); ++ if (irqn < 0) { ++ ret = irqn; ++ dev_err(chip->dev, "%s:hall4 irq gpio failed\n", __func__); ++ goto fail_irq_gpio; ++ } ++ chip->hall4_irq = irqn; ++ } else { ++ dev_err(chip->dev, "%s:hall4 irq gpio not provided\n", __func__); ++ goto err_hall4_irq_gpio; ++ } ++ ++ return ret; ++ ++fail_irq_gpio: ++ gpio_free(chip->irq_gpio); ++err_hall4_irq_gpio: ++ gpio_free(chip->hall4_gpio); ++err_hall3_irq_gpio: ++ gpio_free(chip->hall3_gpio); ++ return ret; ++} ++ ++static int nuvolta_rx1665_irq_request(struct nuvolta_1665_chg *chip) ++{ ++ int ret; ++ ++ // config irq ++ if (!chip->irq) { ++ dev_err(chip->dev, "irq is wrong = %s \n", __func__); ++ return -EINVAL; ++ } ++ ++ ret = request_irq(chip->irq, nuvolta_1665_interrupt_handler, ++ (IRQF_TRIGGER_FALLING | IRQF_ONESHOT), ++ "nuvolta_1665_chg_stat_irq", chip); ++ if (ret) { ++ dev_err(chip->dev, "Failed irq = %d ret = %d\n", chip->irq, ret); ++ return ret; ++ } ++ ret = enable_irq_wake(chip->irq); ++ if (ret) { ++ dev_err(chip->dev, "%s: enable request irq is failed\n", __func__); ++ return ret; ++ } ++ ++ // config hall3 irq ++ if (!chip->hall3_irq) { ++ dev_err(chip->dev, "hall3 irq is wrong = %s \n", __func__); ++ return -EINVAL; ++ } ++ ++ ret = request_irq(chip->hall3_irq, nuvolta_1665_hall3_irq_handler, ++ (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), ++ "hall3_irq", chip); ++ if (ret) { ++ dev_err(chip->dev, "Failed hall3-irq = %d ret = %d\n", chip->hall3_irq, ++ ret); ++ return ret; ++ } ++ ++ enable_irq_wake(chip->hall3_irq); ++ if (ret) { ++ dev_err(chip->dev, "%s: enable request hall3 irq is failed\n", ++ __func__); ++ return ret; ++ } ++ // config hall4 irq ++ if (!chip->hall4_irq) { ++ dev_err(chip->dev, "hall4 irq is wrong = %s \n", __func__); ++ return -EINVAL; ++ } ++ ++ ret = request_irq(chip->hall4_irq, nuvolta_1665_hall4_irq_handler, ++ (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), ++ "hall4_irq", chip); ++ if (ret) { ++ dev_err(chip->dev, "Failed hall4-irq = %d ret = %d\n", chip->hall4_irq, ++ ret); ++ return ret; ++ } ++ ++ enable_irq_wake(chip->hall4_irq); ++ if (ret) { ++ dev_err(chip->dev, "%s: enable request hall4 irq is failed\n", ++ __func__); ++ return ret; ++ } ++ ++ return 0; ++ // config power good irq ++ if (!chip->power_good_irq) { ++ dev_err(chip->dev, "power good irq is wrong = %s \n", __func__); ++ return -EINVAL; ++ } ++ ++ ret = devm_request_threaded_irq( ++ &chip->client->dev, chip->power_good_irq, NULL, ++ nuvolta_1665_power_good_handler, ++ (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT), ++ "nuvolta_1665_power_good_irq", chip); ++ if (ret) { ++ dev_err(chip->dev, "Failed irq = %d ret = %d\n", chip->power_good_irq, ++ ret); ++ return ret; ++ } ++ ++ enable_irq_wake(chip->power_good_irq); ++ if (ret) { ++ dev_err(chip->dev, "%s: enable request power good irq is failed\n", ++ __func__); ++ return ret; ++ } ++ return -EINVAL; ++} ++ ++static ssize_t chip_vrect_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int vrect = 0, ret = 0; ++ ++ ret = nuvolta_1665_get_vrect(g_chip, &vrect); ++ if (ret < 0) { ++ dev_err(dev, "get vrect failed\n"); ++ vrect = 0; ++ } ++ ++ return scnprintf(buf, PAGE_SIZE, "rx1665 Vrect : %d mV\n", vrect); ++} ++ ++static ssize_t chip_iout_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ int iout = 0, ret = 0; ++ ++ ret = nuvolta_1665_get_iout(g_chip, &iout); ++ if (ret < 0) { ++ dev_err(dev, "get iout failed\n"); ++ iout = 0; ++ } ++ ++ return scnprintf(buf, PAGE_SIZE, "%d\n", iout); ++} ++ ++static ssize_t chip_vout_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ int vout = 0, ret = 0; ++ ++ ret = nuvolta_1665_get_vout(g_chip, &vout); ++ if (ret < 0) { ++ dev_err(dev, "get vout failed\n"); ++ vout = 0; ++ } ++ ++ return scnprintf(buf, PAGE_SIZE, "%d\n", vout); ++} ++ ++static ssize_t chip_vout_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t count) ++{ ++ int index; ++ ++ index = (int)simple_strtoul(buf, NULL, 10); ++ dev_info(dev, "[rx1665] [%s] --Store output_voltage = %d\n", __func__, ++ index); ++ if ((index < 4000) || (index > 21000)) { ++ dev_err(dev, "[rx1665] [%s] Store Voltage %s is invalid\n", ++ __func__, buf); ++ nuvolta_1665_set_vout(g_chip, 0); ++ return count; ++ } ++ ++ nuvolta_1665_set_vout(g_chip, index); ++ ++ return count; ++} ++ ++static int nuvolta_1665_check_i2c(struct nuvolta_1665_chg *chip) ++{ ++ int ret = 0; ++ u8 data = 0; ++ ++ ret = rx1665_write(chip, 0x88, 0x0000); ++ if (ret < 0) ++ return ret; ++ msleep(10); ++ ++ ret = rx1665_read(chip, &data, 0x0000); ++ if (ret < 0) ++ return ret; ++ ++ if (data == 0x88) { ++ dev_info(chip->dev, "[%s] i2c check ok!\n", __func__); ++ return 1; ++ } else { ++ dev_info(chip->dev, "[%s] i2c check failed!\n", __func__); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++static int nuvolta_1665_download_fw_data(struct nuvolta_1665_chg *chip, ++ const unsigned char *fw_data, ++ int fw_data_length) ++{ ++ int ret = 0; ++ u8 read_data = 0; //, wrfail = 0, busy = 0; ++ int i = 0, j = 0; ++ u8 __1st_word = 1; ++ ++ dev_info(chip->dev, "[%s] start\n", __func__); ++ ++ //ret = nuvolta_1665_enter_dtm_mode(chip); ++ if (rx1665_write(chip, 0x41, 0x0090) < 0) ++ goto exit; ++ if (rx1665_write(chip, 0xC0, 0x1000) < 0) ++ goto exit; ++ if (rx1665_write(chip, 0xFF, 0x0012) < 0) ++ goto exit; ++ if (ret < 0) { ++ dev_err(chip->dev, "[%s] failed to enter dtm mode\n", __func__); ++ return ret; ++ } ++ ++ for (i = 0; i < fw_data_length; i += 4) { ++ if (__1st_word) { ++ __1st_word = 0; ++ ++ if (rx1665_write(chip, 0x01, 0x0017) < 0) ++ goto exit; ++ ++ if (rx1665_write(chip, fw_data[i + 3], 0x001C) < 0) ++ goto exit; ++ if (rx1665_write(chip, fw_data[i + 2], 0x001D) < 0) ++ goto exit; ++ if (rx1665_write(chip, fw_data[i + 1], 0x001E) < 0) ++ goto exit; ++ if (rx1665_write(chip, fw_data[i + 0], 0x001F) < 0) ++ goto exit; ++ ++ if (rx1665_write(chip, 0x01, 0x0019) < 0) ++ goto exit; ++ if (rx1665_write(chip, 0x00, 0x0019) < 0) ++ goto exit; ++ ++ if (rx1665_write(chip, 0x5A, 0x001A) < 0) ++ goto exit; ++ msleep(20); ++ } ++ ++ if (rx1665_write(chip, fw_data[i + 3], 0x001C) < 0) ++ goto exit; ++ if (rx1665_write(chip, fw_data[i + 2], 0x001D) < 0) ++ goto exit; ++ if (rx1665_write(chip, fw_data[i + 1], 0x001E) < 0) ++ goto exit; ++ if (rx1665_write(chip, fw_data[i + 0], 0x001F) < 0) ++ goto exit; ++ ++ for (j = 0; j < 250; j++) { ++ if (rx1665_read(chip, &read_data, 0x001b) < 0) ++ goto exit; ++ if (!(read_data & (1 << 7))) ++ break; ++ if (read_data & (1 << 6)) { ++ dev_err(chip->dev, "[%s] write failed \n", __func__); ++ goto exit; ++ } ++ msleep(1); ++ } ++ ++ if (j == 250) { ++ dev_err(chip->dev, "[%s] write timeout \n", __func__); ++ goto exit; ++ } ++ } ++ if (rx1665_write(chip, 0x00, 0x001A) < 0) ++ goto exit; ++ ++ return ret; ++ ++exit: ++ msleep(100); ++ dev_err(chip->dev, "[%s] wrfail, MTP error\n", __func__); ++ return -1; ++} ++ ++static int key_open(struct nuvolta_1665_chg *chip) ++{ ++ if (rx1665_write(chip, 0x00, 0x2017) < 0) ++ goto exit; ++ if (rx1665_write(chip, 0x2D, 0x2017) < 0) ++ goto exit; ++ if (rx1665_write(chip, 0xD2, 0x2017) < 0) ++ goto exit; ++ if (rx1665_write(chip, 0x22, 0x2017) < 0) ++ goto exit; ++ if (rx1665_write(chip, 0xDD, 0x2017) < 0) ++ goto exit; ++ return 0; ++exit: ++ dev_err(chip->dev, "[%s] failed \n", __func__); ++ return -1; ++} ++ ++static int write_key0(struct nuvolta_1665_chg *chip) ++{ ++ if (rx1665_write(chip, 0x00, 0x2018) < 0) { ++ dev_err(chip->dev, "[%s] failed \n", __func__); ++ return -1; ++ } ++ return 0; ++} ++ ++static int write_key1(struct nuvolta_1665_chg *chip) ++{ ++ if (rx1665_write(chip, 0x00, 0x2019) < 0) { ++ dev_err(chip->dev, "[%s] failed \n", __func__); ++ return -1; ++ } ++ return 0; ++} ++ ++static int exit_key0(struct nuvolta_1665_chg *chip) ++{ ++ if (rx1665_write(chip, 0xFF, 0x2018) < 0) { ++ dev_err(chip->dev, "[%s] failed \n", __func__); ++ return -1; ++ } ++ return 0; ++} ++ ++static int exit_key1(struct nuvolta_1665_chg *chip) ++{ ++ if (rx1665_write(chip, 0xFF, 0x2019) < 0) { ++ dev_err(chip->dev, "[%s] failed \n", __func__); ++ return -1; ++ } ++ return 0; ++} ++ ++static int exit_key_open(struct nuvolta_1665_chg *chip) ++{ ++ if (rx1665_write(chip, 0x00, 0x2017) < 0) { ++ dev_err(chip->dev, "[%s] failed \n", __func__); ++ return -1; ++ } ++ return 0; ++} ++ ++static int nuvolta_1665_download_fw(struct nuvolta_1665_chg *chip, ++ bool power_on, bool force) ++{ ++ int ret = 0; ++ const unsigned char *fw_data = NULL; ++ int fw_data_length = 0; ++ const struct firmware *fw; ++ ++ if (power_on) { ++ dev_info(chip->dev, "[%s]auto update when power on\n", __func__); ++ } ++ ++ ret = request_firmware(&fw, "nuvolta/rx1665.bin", chip->dev); ++ if (ret) { ++ dev_err(chip->dev, "Failed to load firmware: %d\n", ret); ++ return ret; ++ } ++ ++ fw_data = fw->data; ++ fw_data_length = fw->size; ++ ++ dev_info(chip->dev, "Firmware loaded successfully: %zu bytes\n", fw->size); ++ ++ dev_info(chip->dev, "[%s] fw data length: %d\n", __func__, fw_data_length); ++ ++ if (key_open(chip) < 0) ++ return -1; ++ ++ if (write_key0(chip) < 0) ++ return -1; ++ ++ if (write_key1(chip) < 0) ++ return -1; ++ ++ ret = nuvolta_1665_download_fw_data(chip, fw_data, fw_data_length); ++ release_firmware(fw); ++ if (ret < 0) { ++ dev_err(chip->dev, "[] nuvolta_1665_download_fw_data failed \n"); ++ return -1; ++ } ++ ++ if (exit_key0(chip) < 0) ++ return -1; ++ ++ if (exit_key1(chip) < 0) ++ return -1; ++ ++ if (exit_key_open(chip) < 0) ++ return -1; ++ ++ return 0; ++} ++ ++static int fw_crc_chk(struct nuvolta_1665_chg *chip) ++{ ++ u8 read_data = 0; ++ ++ if (rx1665_write(chip, 0x02, 0x0063) < 0) ++ goto exit; ++ msleep(100); ++ if (rx1665_read(chip, &read_data, 0x0028) < 0) ++ goto exit; ++ if (read_data == 0x66) { ++ dev_info(chip->dev, "fw crc chk good \n"); ++ return 0; ++ } ++ dev_info(chip->dev, "fw crc chk res %x \n", read_data); ++exit: ++ dev_err(chip->dev, "[%s] failed \n", __func__); ++ return -1; ++} ++ ++static int read_fw_version(struct nuvolta_1665_chg *chip, u8 *version) ++{ ++ if (!version) ++ return -1; ++ ++ *version = 0xFF; ++ ++ if (rx1665_read(chip, version, 0x002C) < 0) { ++ dev_err(chip->dev, "[%s] failed \n", __func__); ++ return -1; ++ } ++ dev_info(chip->dev, "fw chk version %x \n", *version); ++ return 0; ++} ++ ++static int nuvolta_1665_firmware_update_func(struct nuvolta_1665_chg *chip, ++ u8 cmd) ++{ ++ int ret = 0; ++ u8 fw_version = 0; ++ ++ //TODO1 disable reverse charge if it run ++ chip->fw_update = true; ++ //TODO2 sleep rx before start download and resume after ++ nuvolta_1665_set_reverse_gpio(chip, true); ++ msleep(100); ++ ++ ret = nuvolta_1665_check_i2c(chip); ++ if (ret < 0) ++ goto exit; ++ ++ switch (cmd) { ++ case FW_UPDATE_CHECK: ++ ret = nuvolta_1665_download_fw(chip, false, false); ++ if (ret < 0) { ++ dev_err(chip->dev, "[%s] fw download failed! cmd: %d\n", ++ __func__, cmd); ++ goto exit; ++ } ++ break; ++ case FW_UPDATE_FORCE: ++ ret = nuvolta_1665_download_fw(chip, false, true); ++ if (ret < 0) { ++ dev_err(chip->dev, "[%s] fw download failed! cmd: %d\n", ++ __func__, cmd); ++ goto exit; ++ } ++ break; ++ case FW_UPDATE_FROM_BIN: ++ //TODO add fw download from bin ++ break; ++ case FW_UPDATE_ERASE: ++ //TODO add erase func ++ break; ++ case FW_UPDATE_AUTO: ++ ret = nuvolta_1665_download_fw(chip, true, false); ++ if (ret < 0) { ++ dev_err(chip->dev, "[%s] fw download failed! cmd: %d\n", ++ __func__, cmd); ++ goto exit; ++ } ++ break; ++ default: ++ dev_err(chip->dev, "[%s] unknown cmd: %d\n", __func__, cmd); ++ break; ++ } ++ ++ nuvolta_1665_set_reverse_gpio(chip, false); ++ msleep(1000); ++ nuvolta_1665_set_reverse_gpio(chip, true); ++ ++ msleep(100); ++ ++ if (fw_crc_chk(chip) < 0) ++ ret = -1; ++ else { ++ if (read_fw_version(chip, &fw_version) < 0) ++ ret = -1; ++ else ++ chip->fw_version = fw_version; ++ } ++ dev_info(chip->dev, "check fw version %x \n", chip->fw_version); ++exit: ++ chip->fw_update = false; ++ nuvolta_1665_set_reverse_gpio(chip, false); ++ return ret; ++} ++ ++static ssize_t chip_firmware_update_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int cmd = 0, ret = 0; ++ ++ if (g_chip->fw_update) { ++ dev_info(dev, "[%s] Firmware Update is on going!\n", __func__); ++ return count; ++ } ++ ++ cmd = (int)simple_strtoul(buf, NULL, 10); ++ dev_info(dev, "[%s] value %d\n", __func__, cmd); ++ ++ if ((cmd > FW_UPDATE_NONE) && (cmd < FW_UPDATE_MAX)) { ++ ret = nuvolta_1665_firmware_update_func(g_chip, cmd); ++ if (ret < 0) { ++ dev_err(dev, "[%s] Firmware Update:failed!\n", __func__); ++ return count; ++ } else { ++ dev_info(dev, "[%s] Firmware Update:Success!\n", ++ __func__); ++ return count; ++ } ++ } else { ++ dev_err(dev, "[%s] Firmware Update:invalid cmd\n", __func__); ++ } ++ ++ return count; ++} ++ ++static ssize_t chip_version_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int check_result = 0; ++ u8 default_FW_Ver = 0xFE; ++ ++ if (g_chip->fw_update) { ++ dev_info(dev, "[%s] fw update going, can not show version\n", ++ __func__); ++ return scnprintf(buf, PAGE_SIZE, "updating\n"); ++ } else { ++ nuvolta_1665_set_reverse_gpio(g_chip, true); ++ msleep(100); ++ ++ check_result = fw_crc_chk(g_chip); ++ if (check_result >= 0) { ++ read_fw_version(g_chip, &default_FW_Ver); ++ } ++ nuvolta_1665_set_reverse_gpio(g_chip, false); ++ ++ return scnprintf(buf, PAGE_SIZE, "fw_ver:%02x\n", ++ default_FW_Ver); ++ } ++} ++ ++static ssize_t chip_fw_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ int ret = 0; ++ ++ if (g_chip->fw_update) { ++ dev_info(dev, "[%s] Firmware Update is on going!\n", __func__); ++ return snprintf(buf, PAGE_SIZE, ++ "Firmware Update is on going!\n"); ++ } ++ ++ dev_info(dev, "[%s] Start fireware update process\n", __func__); ++ ret = nuvolta_1665_firmware_update_func(g_chip, 2); ++ if (ret < 0) { ++ dev_err(dev, "[%s] Firmware Update:failed!\n", __func__); ++ return snprintf(buf, PAGE_SIZE, "Firmware Update:Failed\n"); ++ } else { ++ dev_info(dev, "[%s] Firmware Update:Success!\n", __func__); ++ return snprintf(buf, PAGE_SIZE, "Firmware Update:Success\n"); ++ } ++} ++ ++static DEVICE_ATTR(chip_vrect, S_IRUGO, chip_vrect_show, NULL); ++static DEVICE_ATTR(chip_firmware_update, S_IWUSR, NULL, ++ chip_firmware_update_store); ++static DEVICE_ATTR(chip_version, S_IRUGO, chip_version_show, NULL); ++static DEVICE_ATTR(chip_vout, S_IWUSR | S_IRUGO, chip_vout_show, ++ chip_vout_store); ++static DEVICE_ATTR(chip_iout, S_IRUGO, chip_iout_show, NULL); ++static DEVICE_ATTR(chip_fw, S_IWUSR | S_IRUGO, chip_fw_show, NULL); ++ ++static struct attribute *rx1665_sysfs_attrs[] = { ++ &dev_attr_chip_vrect.attr, ++ &dev_attr_chip_version.attr, ++ &dev_attr_chip_vout.attr, ++ &dev_attr_chip_iout.attr, ++ &dev_attr_chip_firmware_update.attr, ++ &dev_attr_chip_fw.attr, ++ NULL, ++}; ++ ++static const struct attribute_group rx1665_sysfs_group_attrs = { ++ .attrs = rx1665_sysfs_attrs, ++}; ++ ++static int nuvolta_1665_set_enable_mode(struct nuvolta_1665_chg *chip, ++ bool enable) ++{ ++ int ret = 0; ++ int gpio_enable_val = 0; ++ int en = !!enable; ++ ++ if (gpio_is_valid(chip->enable_gpio)) { ++ ret = gpio_request(chip->enable_gpio, "rx-enable-gpio"); ++ if (ret) { ++ dev_err(chip->dev, "%s: unable to request enable gpio [%d]\n", ++ __func__, chip->enable_gpio); ++ } ++ ++ ret = gpio_direction_output(chip->enable_gpio, !en); ++ if (ret) { ++ dev_err(chip->dev, ++ "%s: cannot set direction for idt enable gpio [%d]\n", ++ __func__, chip->enable_gpio); ++ } ++ gpio_enable_val = gpio_get_value(chip->enable_gpio); ++ dev_info(chip->dev, "nuvolta enable gpio val is :%d\n", ++ gpio_enable_val); ++ gpio_free(chip->enable_gpio); ++ } ++ ++ return ret; ++} ++static enum power_supply_property nu1665_props[] = { ++ POWER_SUPPLY_PROP_PRESENT, ++ POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_CAPACITY, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_CURRENT_NOW, ++}; ++ ++static int nu1665_get_prop(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct nuvolta_1665_chg *chip = power_supply_get_drvdata(psy); ++ int temp = 0; ++ int rc = 0; ++ switch (psp) { ++ case POWER_SUPPLY_PROP_PRESENT: ++ val->intval = chip->hall3_online || chip->hall4_online; ++ break; ++ case POWER_SUPPLY_PROP_CAPACITY: ++ if (chip->is_reverse_mode || chip->is_boost_mode) { ++ if (chip->reverse_pen_soc != 255) { ++ last_valid_pen_soc = chip->reverse_pen_soc; ++ val->intval = chip->reverse_pen_soc; ++ } else { ++ val->intval = last_valid_pen_soc; ++ dev_err(chip->dev, "print last valid pen soc: %d\n", ++ val->intval); ++ } ++ } else { ++ val->intval = last_valid_pen_soc; ++ if (val->intval) ++ dev_err(chip->dev, "report last valid pen soc: %d\n", ++ val->intval); ++ } ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ if (chip->is_reverse_mode || chip->is_boost_mode) ++ val->intval = chip->reverse_vout * 1000; ++ else ++ val->intval = 0; ++ break; ++ case POWER_SUPPLY_PROP_CURRENT_NOW: ++ if (chip->is_reverse_mode || chip->is_boost_mode) ++ val->intval = chip->reverse_iout * 1000; ++ else ++ val->intval = 0; ++ break; ++ case POWER_SUPPLY_PROP_TEMP: ++ if (chip->is_reverse_mode || chip->is_boost_mode) { ++ val->intval = chip->reverse_temp * 10; ++ break; ++ } ++ rc = nuvolta_1665_get_temp(chip, &temp); ++ if (!rc) ++ val->intval = temp * 10; ++ else ++ val->intval = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int nu1665_set_prop(struct power_supply *psy, ++ enum power_supply_property psp, ++ const union power_supply_propval *val) ++{ ++ switch (psp) { ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int nu1665_prop_is_writeable(struct power_supply *psy, ++ enum power_supply_property psp) ++{ ++ return 0; ++} ++ ++static const struct power_supply_desc nuvo_psy_desc = { ++ .name = "nuvolta-1665", ++ .type = POWER_SUPPLY_TYPE_WIRELESS, ++ .properties = nu1665_props, ++ .num_properties = ARRAY_SIZE(nu1665_props), ++ .get_property = nu1665_get_prop, ++ .set_property = nu1665_set_prop, ++ .property_is_writeable = nu1665_prop_is_writeable, ++}; ++ ++static void nuvolta_1665_init_detect_work(struct work_struct *work) ++{ ++ struct nuvolta_1665_chg *chip = container_of( ++ work, struct nuvolta_1665_chg, init_detect_work.work); ++ int ret = 0; ++ ++ if (gpio_is_valid(chip->power_good_gpio)) { ++ ret = gpio_get_value(chip->power_good_gpio); ++ dev_info(chip->dev, "[%s]init power good: %d\n", __func__, ret); ++ if (ret) { ++ nuvolta_1665_set_enable_mode(chip, false); ++ usleep_range(20000, 25000); ++ nuvolta_1665_set_enable_mode(chip, true); ++ } ++ } ++ return; ++} ++ ++extern char *saved_command_line; ++ ++ ++static const struct i2c_device_id nuvolta_1665_id[] = { ++ { "nuvolta_1665", 0 }, ++ {}, ++}; ++ ++static int nuvolta_1665_probe(struct i2c_client *client) ++{ ++ ++ int ret = 0; ++ int hall3_val = 1, hall4_val = 1; ++ struct nuvolta_1665_chg *chip; ++ ++ struct power_supply_config nuvo_cfg = {}; ++ ++ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); ++ if (!chip) { ++ dev_err(chip->dev, "Failed to allocate memory\n"); ++ return -ENOMEM; ++ } ++ ++ chip->regmap = ++ devm_regmap_init_i2c(client, &nuvolta_1665_regmap_config); ++ if (IS_ERR(chip->regmap)) { ++ dev_err(chip->dev, "failed to allocate register map\n"); ++ return PTR_ERR(chip->regmap); ++ } ++ ++ chip->client = client; ++ chip->dev = &client->dev; ++ chip->fw_update = false; ++ chip->fw_version = 0; ++ chip->ss = 2; ++ chip->wlsdev_name = NUVOLTA_1665_DRIVER_NAME; ++ chip->chg_phase = NORMAL_MODE; ++ g_chip = chip; ++ chip->reverse_pen_soc = 255; ++ ++ device_init_wakeup(&client->dev, true); ++ i2c_set_clientdata(client, chip); ++ ++ mutex_init(&chip->wireless_chg_int_lock); ++ mutex_init(&chip->reverse_op_lock); ++ ++ INIT_DELAYED_WORK(&chip->wireless_int_work, ++ nuvolta_1665_wireless_int_work); ++ INIT_DELAYED_WORK(&chip->wireless_pg_det_work, ++ nuvolta_1665_pg_det_work); ++ INIT_DELAYED_WORK(&chip->chg_monitor_work, nuvolta_1665_monitor_work); ++ INIT_DELAYED_WORK(&chip->reverse_chg_state_work, ++ reverse_chg_state_set_work); ++ INIT_DELAYED_WORK(&chip->reverse_dping_state_work, ++ reverse_dping_state_set_work); ++ INIT_DELAYED_WORK(&chip->init_detect_work, ++ nuvolta_1665_init_detect_work); ++ INIT_DELAYED_WORK(&chip->delay_report_status_work, ++ nuvolta_1665_delay_report_status_work); ++ INIT_DELAYED_WORK(&chip->rx_alarm_work, nuvolta_1665_rx_alarm_work); ++ INIT_DELAYED_WORK(&chip->hall3_irq_work, nu1665_hall3_irq_work); ++ INIT_DELAYED_WORK(&chip->hall4_irq_work, nu1665_hall4_irq_work); ++ INIT_DELAYED_WORK(&chip->pen_notifier_work, pen_charge_notifier_work); ++ pen_notifier_work = &chip->pen_notifier_work; ++ INIT_DELAYED_WORK(&chip->reverse_chg_work, nu_reverse_chg_work); ++ INIT_DELAYED_WORK(&chip->probe_fw_download_work, ++ nu1665_probe_fw_download_work); ++ INIT_DELAYED_WORK(&chip->pen_check_work, pen_check_worker); ++ ++ ret = nuvolta_1665_parse_dt(chip); ++ if (ret < 0) { ++ dev_err(chip->dev, "device tree init is failed = %s\n", __func__); ++ goto error_sysfs; ++ } ++ ++ ret = nuvolta_rx1665_gpio_init(chip); ++ if (ret < 0) { ++ dev_err(chip->dev, "gpio init is failed = %s\n", __func__); ++ goto error_sysfs; ++ } ++ ++ ret = nuvolta_rx1665_irq_request(chip); ++ if (ret < 0) { ++ dev_err(chip->dev, "irq init is failed = %s\n", __func__); ++ goto error_sysfs; ++ } ++ ++ schedule_delayed_work(&chip->reverse_dping_state_work, 0); ++ schedule_delayed_work(&chip->reverse_chg_state_work, 5000); ++ ++ ret = sysfs_create_group(&chip->dev->kobj, &rx1665_sysfs_group_attrs); ++ if (ret < 0) { ++ dev_err(chip->dev, "sysfs_create_group fail %d\n", ret); ++ goto error_sysfs; ++ } ++ nuvo_cfg.drv_data = chip; ++ chip->nuvo_psy = ++ power_supply_register(chip->dev, &nuvo_psy_desc, &nuvo_cfg); ++ ++ /* reset wls charge when power good online */ ++ schedule_delayed_work(&chip->init_detect_work, msecs_to_jiffies(20000)); ++ ++ if (gpio_is_valid(chip->hall3_gpio)) { ++ hall3_val = gpio_get_value(chip->hall3_gpio); ++ if (!hall3_val) { ++ dev_info(chip->dev, "pen online, start reverse charge\n"); ++ chip->hall3_online = 1; ++ schedule_delayed_work(&chip->hall3_irq_work, ++ msecs_to_jiffies(6000)); ++ } ++ } else ++ dev_err(chip->dev, "%s: hall3 gpio not provided\n", __func__); ++ ++ if (gpio_is_valid(chip->hall4_gpio)) { ++ hall4_val = gpio_get_value(chip->hall4_gpio); ++ if (!hall4_val) { ++ dev_info(chip->dev, "pen online, start reverse charge\n"); ++ chip->hall4_online = 1; ++ schedule_delayed_work(&chip->hall4_irq_work, ++ msecs_to_jiffies(6000)); ++ } ++ } else ++ dev_err(chip->dev, "%s: hall4 gpio not provided\n", __func__); ++ ++ if (!chip->power_off_mode) ++ schedule_delayed_work(&chip->probe_fw_download_work, 10 * HZ); ++ ++ return 0; ++ ++error_sysfs: ++ sysfs_remove_group(&chip->dev->kobj, &rx1665_sysfs_group_attrs); ++ if (chip->irq_gpio > 0) ++ gpio_free(chip->irq_gpio); ++ if (chip->power_good_gpio > 0) ++ gpio_free(chip->power_good_gpio); ++ cancel_delayed_work_sync(&chip->hall3_irq_work); ++ cancel_delayed_work_sync(&chip->hall4_irq_work); ++ return 0; ++} ++ ++static void nuvolta_1665_remove(struct i2c_client *client) ++{ ++ struct nuvolta_1665_chg *chip = i2c_get_clientdata(client); ++ ++ if (chip->irq_gpio > 0) ++ gpio_free(chip->irq_gpio); ++ if (chip->power_good_gpio > 0) ++ gpio_free(chip->power_good_gpio); ++} ++ ++static int nuvolta_1665_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct nuvolta_1665_chg *chip = i2c_get_clientdata(client); ++ ++ return enable_irq_wake(chip->irq); ++} ++ ++static int nuvolta_1665_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct nuvolta_1665_chg *chip = i2c_get_clientdata(client); ++ ++ return disable_irq_wake(chip->irq); ++} ++ ++static const struct dev_pm_ops nuvolta_1665_pm_ops = { ++ .suspend = nuvolta_1665_suspend, ++ .resume = nuvolta_1665_resume, ++}; ++ ++static void nuvolta_1665_shutdown(struct i2c_client *client) ++{ ++ struct nuvolta_1665_chg *chip = i2c_get_clientdata(client); ++ ++ if (chip->power_good_flag) { ++ nuvolta_1665_set_enable_mode(chip, false); ++ usleep_range(20000, 25000); ++ nuvolta_1665_set_enable_mode(chip, true); ++ } ++ ++ dev_info(chip->dev, "%s: shutdown: %s\n", __func__, chip->wlsdev_name); ++ return; ++} ++ ++static const struct of_device_id nuvolta_1665_match_table[] = { ++ { ++ .compatible = "nuvolta,rx1665", ++ }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(i2c, nuvolta_1665_id); ++MODULE_DEVICE_TABLE(of, nuvolta_1665_match_table); ++ ++static struct i2c_driver nuvolta_1665_driver = { ++ .driver = { ++ .name = "nuvolta_1665", ++ .owner = THIS_MODULE, ++ .of_match_table = nuvolta_1665_match_table, ++ .pm = &nuvolta_1665_pm_ops, ++ }, ++ .probe = nuvolta_1665_probe, ++ .remove = nuvolta_1665_remove, ++ .id_table = nuvolta_1665_id, ++ .shutdown = nuvolta_1665_shutdown, ++}; ++ ++module_i2c_driver(nuvolta_1665_driver); ++ ++MODULE_AUTHOR("anxufeng "); ++MODULE_DESCRIPTION("nuvolta wireless charge driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/power/supply/nu1665.h b/drivers/power/supply/nu1665.h +new file mode 100644 +index 000000000000..a39c2815520c +--- /dev/null ++++ b/drivers/power/supply/nu1665.h +@@ -0,0 +1,247 @@ ++/** ++ * @file nuvolta_1665.h ++ * @author ++ * @data April 6 2022 ++ * @brief ++ * nuvolta 1665 wireless charge! ++*/ ++#ifndef __NU1665_HEADER__ ++#define __NU1665_HEADER__ ++ ++#include ++#include ++#include ++#include ++ ++//registers define ++#define REG_RX_REV_CMD 0x0020 ++#define REG_RX_REV_DATA1 0x0021 ++#define RX_POWER_OFF_ERR 0x0028 ++#define TRX_MODE_EN 0x0063 ++#define RX_RTX_MODE 0x002f ++#define RX_DATA_INFO 0x1200 ++#define RX_POWER_MODE 0x1203 ++#define RX_FASTCHG_RESULT 0x1209 ++#define TX_MANU_ID_L 0x1224 ++#define TX_MANU_ID_H 0x1225 ++ ++//vout definition ++#define BPP_DEFAULT_VOUT 6000 ++#define BPP_QC2_VOUT 6500 ++#define BPP_PLUS_VOUT 9000 ++#define EPP_DEFAULT_VOUT 11000 ++#define EPP_PLUS_VOUT 15000 ++ ++//protect threshold definition ++#define RX_MAX_IOUT 2650 ++#define RX_MAX_TEMP 84 ++ ++// interrupts foward definition ++#define RX_INT_LDO_ON 0x0001 ++#define RX_INT_FAST_CHARGE 0x0002 ++#define RX_INT_AUTHEN_FINISH 0x0004 ++#define RX_INT_RENEGO_DONE 0x0008 ++#define RX_INT_ALARM_SUCCESS 0x0010 ++#define RX_INT_ALARM_FAIL 0x0020 ++#define RX_INT_OOB_GOOD 0x0040 ++#define RX_INT_RPP 0x0080 ++#define RX_INT_TRANSPARENT_SUCCESS 0x0100 ++#define RX_INT_TRANSPARENT_FAIL 0x0200 ++#define RX_INT_FACTORY_TEST 0x0400 ++#define RX_INT_OCP_OTP_ALARM 0x1000 ++#define RX_INT_POWER_OFF 0x4000 ++#define RX_INT_POWER_ON 0x8000 ++ ++#define RTX_INT_EPT (1 << 0) ++#define RTX_INT_START_DPING (1 << 1) ++#define INT_GET_SS (1 << 2) ++#define INT_GET_ID (1 << 3) ++#define RTX_INT_GET_CFG (1 << 4) ++#define INT_GET_PPP (1 << 5) ++#define INT_GET_DPING (1 << 6) ++#define INT_INIT_TX (1 << 7) ++#define INT_GET_BLE_ADDR (1 << 8) ++#define FW_VERSION 0x11 ++ ++//reverse charge timer ++#define REVERSE_CHG_CHECK_DELAY_MS 100000 ++#define REVERSE_DPING_CHECK_DELAY_MS 10000 ++ ++//reverse charge fod setting ++#define REVERSE_FOD_GAIN 94 ++#define REVERSE_FOD_OFFSET 0 ++ ++//driver name definition ++#define NUVOLTA_1665_DRIVER_NAME "nuvolta_1665" ++#define WLS_CHG_VOTER "WLS_CHG_VOTER" ++ ++//firmware check result ++#define RX_CHECK_SUCCESS (1 << 0) ++#define TX_CHECK_SUCCESS (1 << 1) ++#define BOOT_CHECK_SUCCESS (1 << 2) ++ ++#ifndef ABS ++#define ABS(x) ((x) > 0 ? (x) : (-x)) ++#endif ++#define ABS_CEP_VALUE 1 ++ ++enum FW_UPDATE_CMD { ++ FW_UPDATE_NONE, ++ FW_UPDATE_CHECK, ++ FW_UPDATE_FORCE, ++ FW_UPDATE_FROM_BIN, ++ FW_UPDATE_ERASE, ++ FW_UPDATE_AUTO, ++ FW_UPDATE_MAX, ++}; ++ ++enum reverse_chg_state { ++ REVERSE_STATE_OPEN, ++ REVERSE_STATE_TIMEOUT, ++ REVERSE_STATE_ENDTRANS, ++ REVERSE_STATE_FORWARD, ++ REVERSE_STATE_TRANSFER, ++ REVERSE_STATE_WAITPING, ++}; ++ ++enum fod_param_id { ++ FOD_PARAM_20V, ++ FOD_PARAM_27V, ++ FOD_PARAM_BPP_PLUS, ++ FOD_PARAM_MAX, ++}; ++ ++struct params_t { ++ s8 gain; ++ s8 offset; ++}; ++ ++enum wls_chg_stage { ++ NORMAL_MODE = 1, ++ TAPER_MODE, ++ FULL_MODE, ++ RECHG_MODE, ++}; ++ ++enum wls_work_mode { ++ RX_MODE, ++ RTX_MODE, ++}; ++ ++enum wls_adapter_type { ++ ADAPTER_NONE, ++ ADAPTER_SDP, ++}; ++ ++struct nuvolta_1665_chg { ++ struct i2c_client *client; ++ struct device *dev; ++ struct regmap *regmap; ++ // irq and gpio ++ unsigned int tx_on_gpio; ++ unsigned int reverse_boost_gpio; ++ unsigned int irq_gpio; ++ unsigned int power_good_gpio; ++ unsigned int power_good_irq; ++ unsigned int irq; ++ unsigned int enable_gpio; ++ unsigned int hall3_irq; ++ unsigned int hall4_irq; ++ unsigned int hall3_gpio; ++ unsigned int hall4_gpio; ++ int hall3_online; ++ int hall4_online; ++ unsigned long pen_val; ++ void *pen_v; ++ int is_reverse_mode; ++ int is_boost_mode; ++ int reverse_vout; ++ int reverse_iout; ++ int reverse_temp; ++ int reverse_pen_soc; ++ u8 pen_mac_data[6]; ++ int power_off_mode; ++ int chip_ok; ++ struct pinctrl *idt_pinctrl; ++ struct pinctrl_state *idt_gpio_active; ++ struct pinctrl_state *idt_gpio_suspend; ++ // delay works ++ struct delayed_work wireless_int_work; ++ struct delayed_work wireless_pg_det_work; ++ struct delayed_work chg_monitor_work; ++ struct delayed_work reverse_chg_state_work; ++ struct delayed_work reverse_dping_state_work; ++ struct delayed_work init_detect_work; ++ struct delayed_work factory_reverse_start_work; ++ struct delayed_work factory_reverse_stop_work; ++ struct delayed_work delay_report_status_work; ++ struct delayed_work rx_alarm_work; ++ struct delayed_work rx_enable_usb_work; ++ struct delayed_work max_power_control_work; ++ struct delayed_work fw_state_work; ++ struct delayed_work hall3_irq_work; ++ struct delayed_work hall4_irq_work; ++ struct delayed_work pen_notifier_work; ++ struct delayed_work reverse_sent_state_work; ++ struct delayed_work reverse_chg_work; ++ struct delayed_work probe_fw_download_work; ++ struct delayed_work pen_check_work; ++ // lock ++ struct mutex wireless_chg_int_lock; ++ struct mutex reverse_op_lock; ++ // alarm ++ struct alarm reverse_dping_alarm; ++ struct alarm reverse_chg_alarm; ++ //vote ++ struct votable *fcc_votable; ++ struct votable *icl_votable; ++ // wireless charge device ++ struct wireless_charger_device *wlschgdev; ++ struct charger_device *master_cp_dev; ++ const char *wlsdev_name; ++ // charger device ++ struct charger_device *cp_master_dev; ++ // power supply ++ struct power_supply *batt_psy; ++ struct regulator *pmic_boost; ++ struct power_supply *nuvo_psy; ++ // driver parameters ++ u8 epp; ++ u8 epp_tx_id_h; ++ u8 epp_tx_id_l; ++ u8 tx_manu_id_l; ++ u8 tx_manu_id_h; ++ u8 fc_flag; ++ u8 uuid[4]; ++ u8 wait_for_reverse_test_status; ++ u8 power_good_flag; ++ u8 set_fastcharge_vout_cnt; ++ u8 ss; ++ u16 adapter_type; ++ int batt_soc; ++ int target_vol; ++ int target_curr; ++ int pre_curr; ++ int pre_vol; ++ int vout_setted; ++ int chg_status; ++ int chg_phase; ++ int is_reverse_chg; ++ bool fw_update; ++ int fw_version; ++ bool parallel_charge; ++ bool wait_for_reverse_test; ++ bool qc_enable; ++ bool reverse_chg_en; ++ bool alarm_flag; ++}; ++ ++struct wls_fw_parameters { ++ u8 fw_rx_id; ++ u8 fw_tx_id; ++ u8 fw_boot_id; ++ u8 hw_id_h; ++ u8 hw_id_l; ++}; ++ ++#endif +diff --git a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-pipa-common.dtsi b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-pipa-common.dtsi +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-pipa-common.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-pipa-common.dtsi +@@ -640,6 +640,25 @@ &i2c6 { + }; + }; + ++&i2c8 { ++ clock-frequency = <400000>; ++ status = "okay"; ++ ++ nuvolta_1665: nuvolta_1665@41 { ++ compatible = "nuvolta,rx1665"; ++ reg = <0x41>; ++ rx-irq-gpios = <&tlmm 113 0x00>; ++ hall-int3-gpios = <&tlmm 136 0x00>; ++ hall-int4-gpios = <&tlmm 138 0x00>; ++ reverse-chg-ovp-gpios = <&tlmm 47 0x00>; ++ reverse-boost-gpios = <&tlmm 22 0x00>; ++ wpc-det-gpios = <&tlmm 145 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "idt_active", "idt_suspend"; ++ pinctrl-0 = <&idt_int_active &hall_intr3_active &hall_intr4_active &txon_enable_active &power_good_active &reverse_booset_enable_active>; ++ pinctrl-1 = <&idt_int_suspend &hall_intr3_suspend &hall_intr4_suspend &txon_enable_suspend &power_good_suspend &reverse_booset_enable_suspend>; ++ }; ++}; ++ + &i2c11 { + clock-frequency = <400000>; + status = "okay"; +@@ -1275,6 +1294,178 @@ &tlmm { + input-enable; + }; + ++ hall { ++ hall_intr3_active: hall_intr3-active-state { ++ mux { ++ pins = "gpio136"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio136"; ++ drive-strength = <2>; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ hall_intr3_suspend: hall_intr3-suspend-state { ++ mux { ++ pins = "gpio136"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio136"; ++ drive-strength = <2>; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ hall_intr4_active: hall_intr4-active-state { ++ mux { ++ pins = "gpio138"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio138"; ++ drive-strength = <2>; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ hall_intr4_suspend: hall_intr4-suspend-state { ++ mux { ++ pins = "gpio138"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio138"; ++ drive-strength = <2>; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ }; ++ ++ idt { ++ idt_int_active: idt-int-active-state { ++ mux { ++ pins = "gpio113"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio113"; ++ drive-strength = <2>; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ idt_int_suspend: idt-int-suspend-state { ++ mux { ++ pins = "gpio113"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio113"; ++ drive-strength = <2>; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ txon_enable_active: txon-enable-active-state { ++ mux { ++ pins = "gpio47"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio47"; ++ drive-strength = <16>; ++ bias-pull-down; ++ output-low; ++ }; ++ }; ++ ++ txon_enable_suspend: txon-enable-suspend-state { ++ mux { ++ pins = "gpio47"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio47"; ++ drive-strength = <16>; ++ bias-pull-down; ++ output-low; ++ }; ++ }; ++ ++ power_good_active: power-good-active-state { ++ mux { ++ pins = "gpio145"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio145"; ++ drive-strength = <2>; ++ bias-pull-down; ++ input-enable; ++ }; ++ }; ++ ++ power_good_suspend: power-good-suspend-state { ++ mux { ++ pins = "gpio145"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio145"; ++ drive-strength = <2>; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ reverse_booset_enable_active: reverse-boost-enable-active-state { ++ mux { ++ pins = "gpio22"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio22"; ++ drive-strength = <16>; ++ bias-pull-down; ++ output-low; ++ }; ++ }; ++ ++ reverse_booset_enable_suspend: reverse-boost-enable-suspend-state { ++ mux { ++ pins = "gpio22"; ++ function = "gpio"; ++ }; ++ ++ config { ++ pins = "gpio22"; ++ drive-strength = <16>; ++ bias-pull-down; ++ output-low; ++ }; ++ }; ++ }; ++ + mclk0_active: mclk0_active-state { + pins = "gpio94"; + function = "cam_mclk"; diff --git a/device/testing/linux-xiaomi-pipa/APKBUILD b/device/testing/linux-xiaomi-pipa/APKBUILD index 2297f988b..c0402075c 100644 --- a/device/testing/linux-xiaomi-pipa/APKBUILD +++ b/device/testing/linux-xiaomi-pipa/APKBUILD @@ -58,6 +58,7 @@ source=" 0012-HACK-ASoC-qcom-qdsp6-q6afe-pretend-the-AFE-vote-didn.patch 0013-Input-keyboard-add-Xiaomi-Nanosic-803-keyboard.patch 0014-UPSTREAM-libbpf-Fix-UAF-in-strset__add_str.patch + 0016-power-supply-add-nuvolta-rx1665-wireless-charger.patch " builddir="$srcdir/linux-$pkgver" @@ -91,8 +92,8 @@ sha512sums=" 753b53b3f9a240b5838fe1156ad61f2356e2042941e571450492b5c8c46fd32140aede34fd4b074705650bac503deccec20ad9b8caee9d4c5fdc575401c76cc4 linux-7.1.4.tar.xz 73fa3eab2ab9e420cd0eb8a366dd928e58ce17fc7c05a2a531b28cca8c0eb6fa0fd0b0708a74d2171e758f7c257095996875f14ea2f2a6fba68b22074fff2765 config-xiaomi-pipa.aarch64 cdc7d83d87fe632a3589fdfd117a045f2da0873c1d78ed117b38649b83adee518f099f3dcf7908b1729d75535ba25a1685dc37fd1a5d14c64bbe0d41dd87a2d3 0001-arm64-dts-qcom-sm8250-xiaomi-pipa-Add-device-tree-fo.patch -e20fa947eada70ff7a83ad76ecb0402e3a6d4b3d471066065dbe580bd45851a59006c53daf85009e59686a888b24d7f8b27595e7bb49c6f564e29ea70640893e 0002-power-supply-Add-driver-for-Qualcomm-PMIC-fuel-gauge.patch -522af2aede3ef917035bfca7c9c24f43407118540c6bbe93bce73bce41066fe83e9f83e848264182188a5f47da2a1b94a84040f192e5af390054fced89a7e1d8 0003-Input-Add-nt36523-touchscreen-driver.patch +34eab30dde870322ad10ab7babee8acf7c5da2b2d5d38d5e4f67fad87be4e7d1426950b46c7d7344627ba034643f030b8f4656cbf941bb2f04b1d649530842d3 0002-power-supply-Add-driver-for-Qualcomm-PMIC-fuel-gauge.patch +f5132c2e1c6d130843bc98f649609ab3fbec7fb011a608f4aa0a2bbd933962650713e0b6524ee7bcfd62b16cbaa18cb47b8858833f89c62a06a54f920527bf2d 0003-Input-Add-nt36523-touchscreen-driver.patch 6db0ea6e585e50054457b0d959efe691c7fcb6d68dc7144b456f7f04f140bd02a422008bfe40680d3d7b799d7192b007d94df97c7288644f2ffd321221d1a881 0004-drm-msm-dsi-change-sync-mode-to-sync-on-DSI0-rather-.patch 9ac34413200b8bcb4e8d72441fff9161d0966081e817700127dc607ffcea215a689f4a482663d422b06b4e3e40aa0e11a607efad3310a31a3e81299390e83dc3 0005-drm-msm-dsi-support-DSC-configurations-with-slice_pe.patch cb6265f0dbb4f35753c6cda74a78bebea6aa18294175498556d2cb089ac6c5da26563bd47a50ea531a643cf967dc5bcefe341a951f7af9e2d74f5b832430b242 0006-drm-panel-Add-support-for-Novatek-NT36532-panel.patch @@ -103,4 +104,5 @@ f98be67fa1826cc7f29a5ab07ff6457701701a13fd563bdae1b3eac47c32e74a08be18faeae7a57d c6683c513af7a98269a7a686c6ba24ff9387c4e50968bf0de64c1f4eaa9a01b65e17318a2b9cae52085b3e4afabf47aacc4f6fa5bb1a5d12d92cff1eab7bdd65 0012-HACK-ASoC-qcom-qdsp6-q6afe-pretend-the-AFE-vote-didn.patch f763ecb3c565d2f5c330ad25e8193e89c1de207db8f460b71c17bb7f68e98bec3f4a22e78ac422329db33f139f0fddd0aef6ffa4f8659248c150df951c4a2f19 0013-Input-keyboard-add-Xiaomi-Nanosic-803-keyboard.patch b4fa58b77ea1cf16e594efaaee800ee8790cad4fbe3662bd22d5be71d8cb6fd4fe95f8c9184c0c53c42a46aa3e2999e1053f8e8b532ac1e9c7b9b0fcb674dba9 0014-UPSTREAM-libbpf-Fix-UAF-in-strset__add_str.patch +8589f3100677b35e0af9d72d4c44c399248df0dbaec4a9654e2c4eab65acafa1b1f1c52a47ed9df2d3f3dc6f8687c3cee82047ec6e1ebcad9d0e7731608c0688 0016-power-supply-add-nuvolta-rx1665-wireless-charger.patch "