diff --git a/drivers/power/mmi_parallel_charger_iio/Android.mk b/drivers/power/mmi_parallel_charger_iio/Android.mk new file mode 100644 index 000000000000..de1f141bafee --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_parallel_charger.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mmi_parallel_charger_iio/Kbuild b/drivers/power/mmi_parallel_charger_iio/Kbuild new file mode 100644 index 000000000000..3c93b07997cf --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/power/mmi_parallel_charger + +obj-m += mmi_parallel_charger.o +mmi_parallel_charger-objs += mmi_charger_class.o +mmi_parallel_charger-objs += mmi_charger_core.o +mmi_parallel_charger-objs += mmi_charger_pump_policy.o +mmi_parallel_charger-objs += bq2597x_charger.o +mmi_parallel_charger-objs += qpnp_pmic_charger.o + +#ifeq ($(CONFIG_MMI_PL_CP_POLICY), y) +#endif diff --git a/drivers/power/mmi_parallel_charger_iio/Makefile b/drivers/power/mmi_parallel_charger_iio/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.c b/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.c new file mode 100644 index 000000000000..bb0d752b61b1 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" + +static struct class *mmi_charger_class; + +int mmi_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->enable) + return chrg->ops->enable(chrg,en); + + return -ENOTSUPP; +} + +int mmi_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->is_enabled) + return chrg->ops->is_enabled(chrg,en); + + return -ENOTSUPP; +} + +int mmi_get_charing_current(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_charging_current) + return chrg->ops->get_charging_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_set_charing_current(struct mmi_charger_device *chrg, u32 uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->set_charging_current) + return chrg->ops->set_charging_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_input_current_settled) + return chrg->ops->get_input_current_settled(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_get_input_current(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_input_current) + return chrg->ops->get_input_current(chrg,uA); + + return -ENOTSUPP; + +} + +int mmi_set_input_current(struct mmi_charger_device *chrg, u32 uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->set_input_current) + return chrg->ops->set_input_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_update_charger_status(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->update_charger_status) + return chrg->ops->update_charger_status(chrg); + + return -ENOTSUPP; +} + +int mmi_update_charger_error(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->update_charger_error) + return chrg->ops->update_charger_error(chrg); + + return -ENOTSUPP; +} + +int mmi_clear_charger_error(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->clear_charger_error) + return chrg->ops->clear_charger_error(chrg); + + return -ENOTSUPP; +} + +static void mmi_charger_device_release(struct device *dev) +{ + struct mmi_charger_device *charger_dev = to_mmi_charger_device(dev); + + kfree(charger_dev); + return; +} + +static int charger_match_device_by_name(struct device *dev, + const void *data) +{ + const char *name = data; + return strcmp(dev_name(dev), name) == 0; +} + +struct mmi_charger_device *get_charger_by_name(const char *name) +{ + struct device *dev; + + if(!name) + return (struct mmi_charger_device *)NULL; + dev = class_find_device(mmi_charger_class, NULL, name, + charger_match_device_by_name); + + return dev ? to_mmi_charger_device(dev) : NULL; +} + +int is_charger_exist(const char *name) +{ + if (get_charger_by_name(name) == NULL) + return 0; + return 1; +} + +struct mmi_charger_device *mmi_charger_device_register(const char *name, + const char *psy_name,struct device *parent, void *devdata, + const struct mmi_charger_ops *ops) +{ + struct mmi_charger_device *charger_dev; + int rc; + + pr_info("mmi charger device register: name=%s\n",name); + charger_dev = kzalloc(sizeof(struct mmi_charger_device),GFP_KERNEL); + if (!charger_dev) + return ERR_PTR(-ENOMEM); + + mutex_init(&charger_dev->ops_lock); + charger_dev->dev.class = mmi_charger_class; + charger_dev->dev.parent = parent; + charger_dev->dev.release = mmi_charger_device_release; + charger_dev->name = name; + dev_set_name(&charger_dev->dev, "%s", name); + dev_set_drvdata(&charger_dev->dev, devdata); + + rc = device_register(&charger_dev->dev); + if (rc) { + kfree(charger_dev); + return ERR_PTR(rc); + } + charger_dev->chrg_psy = power_supply_get_by_name(psy_name); + charger_dev->ops = ops; + if (!charger_dev->chrg_psy) + return ERR_PTR(-ENODEV); + pr_info("mmi charger device register: name=%s, successfully\n",name); + return charger_dev; +} + +EXPORT_SYMBOL(mmi_charger_device_register); + +void mmi_charger_device_unregister(struct mmi_charger_device* charger_dev) +{ + if (!charger_dev) + return; + + mutex_lock(&charger_dev->ops_lock); + charger_dev->ops = NULL; + mutex_unlock(&charger_dev->ops_lock); + device_unregister(&charger_dev->dev); +} + +EXPORT_SYMBOL(mmi_charger_device_unregister); + +void mmi_charger_class_exit(void) +{ + class_destroy(mmi_charger_class); +} + +int mmi_charger_class_init(void) +{ + mmi_charger_class = class_create(THIS_MODULE, "mmi_charger"); + if (IS_ERR(mmi_charger_class)) { + pr_err("Unable to create mmi charger class; error = %ld\n", + PTR_ERR(mmi_charger_class)); + return PTR_ERR(mmi_charger_class); + } + pr_info("success to create mmi charger class \n"); + return 0; +} diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.h b/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.h new file mode 100644 index 000000000000..1b3cb6503eaa --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #ifndef __MMI_CHRG_CLASS_H_ + #define __MMI_CHRG_CLASS_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chrg_dev_info(chip, fmt, ...) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +struct mmi_charger_info { + bool vbus_pres; + int vbatt_volt; + int vbus_volt; + int ibatt_curr; + int ibus_curr; + int batt_temp; +}; + +#define MMI_BAT_OVP_ALARM_BIT 0 +#define MMI_BAT_OCP_ALARM_BIT 1 +#define MMI_BAT_UCP_ALARM_BIT 2 +#define MMI_BUS_OVP_ALARM_BIT 3 +#define MMI_BUS_OCP_ALARM_BIT 4 +#define MMI_BUS_UCP_ALARM_BIT 5 +#define MMI_BAT_OVP_FAULT_BIT 6 +#define MMI_BAT_OCP_FAULT_BIT 7 +#define MMI_BAT_UCP_FAULT_BIT 8 +#define MMI_BUS_OVP_FAULT_BIT 9 +#define MMI_BUS_OCP_FAULT_BIT 10 +#define MMI_BUS_UCP_FAULT_BIT 11 +#define MMI_CONV_OCP_FAULT_BIT 12 +#define MMI_THERM_ALARM_BIT 13 +#define MMI_THERM_FAULT_BIT 14 + +struct mmi_charger_error_info { + int chrg_err_type; + int bus_ucp_err_cnt; + int bus_ocp_err_cnt; +}; + +struct mmi_charger_device { + const char *name; /*charger device name*/ + struct power_supply *chrg_psy; /*power supply handle*/ + const struct mmi_charger_ops *ops; /*operation function*/ + struct mutex ops_lock; + struct device dev; + struct mmi_charger_info charger_data; + struct mmi_charger_error_info charger_error; + int *debug_mask; + int input_curr_setted; /*save input current*/ + int charging_curr_limited; /*charger current limitation*/ + int charging_curr_min; /*The minimum charging current supported by this device*/ + bool charger_enabled; /*is this charger device enabled*/ + bool charger_limited; /*is the charging current of this device limited*/ +}; + +struct mmi_charger_ops { + int (*enable)(struct mmi_charger_device *chrg, bool en); + int (*is_enabled)(struct mmi_charger_device *chrg, bool *en); + int (*get_charging_current)(struct mmi_charger_device *chrg, u32 *uA); + int (*set_charging_current)(struct mmi_charger_device *chrg, u32 uA); + int (*get_input_current_settled)(struct mmi_charger_device *chrg, u32 *uA); + int (*get_input_current)(struct mmi_charger_device *chrg, u32 *uA); + int (*set_input_current)(struct mmi_charger_device *chrg, u32 uA); + int (*update_charger_status)(struct mmi_charger_device *chrg); + int (*update_charger_error)(struct mmi_charger_device *chrg); + int (*clear_charger_error)(struct mmi_charger_device *chrg); +}; + + +#define to_mmi_charger_device(obj) container_of(obj, struct mmi_charger_device, dev) +#define mmi_chrg_name(x) (dev_name(&(x)->dev)) + +extern struct mmi_charger_device *get_charger_by_name(const char *name); +extern int is_charger_exist(const char *name); +extern int mmi_charger_class_init(void); +extern void mmi_charger_class_exit(void); +extern int mmi_enable_charging(struct mmi_charger_device *chrg, bool en); +extern int mmi_is_charging_enabled(struct mmi_charger_device *chrg, bool *en); +extern int mmi_get_charing_current(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_set_charing_current(struct mmi_charger_device *chrg, u32 uA); +extern int mmi_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_get_input_current(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_set_input_current(struct mmi_charger_device *chrg, u32 uA); +extern int mmi_update_charger_status(struct mmi_charger_device *chrg); +extern int mmi_update_charger_error(struct mmi_charger_device *chrg); +extern int mmi_clear_charger_error(struct mmi_charger_device *chrg); +extern struct mmi_charger_device *mmi_charger_device_register(const char *name, + const char *psy_name,struct device *parent, void *devdata, + const struct mmi_charger_ops *ops); +extern void mmi_charger_device_unregister(struct mmi_charger_device* charger_dev); +#endif diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.c b/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.c new file mode 100644 index 000000000000..5e710ba03849 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.c @@ -0,0 +1,2014 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include "mmi_charger_core.h" +#include "mmi_charger_policy.h" + +static int __debug_mask = 0x85; +module_param_named( + debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR +); + +static int pd_volt_max_init = 0; +static int pd_curr_max_init = 0; +static int *dt_temp_zones; +static struct mmi_chrg_dts_info *chrg_name_list; +static char *charge_rate[] = { + "None", "Normal", "Weak", "Turbo", "Turbo_30W", "Hyper" +}; +#define MIN_TEMP_C -20 +#define MAX_TEMP_C 60 +#define HYSTEREISIS_DEGC 2 +void mmi_chrg_rate_check(struct mmi_charger_manager *chg); +bool mmi_find_temp_zone(struct mmi_charger_manager *chip, int temp_c, bool ignore_hysteresis_degc) +{ + int prev_zone, num_zones; + struct mmi_chrg_temp_zone *zones; + int hotter_t = 0, hotter_fcc = 0; + int colder_t = 0, colder_fcc = 0; + int i; + int max_temp; + + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return false; + } + + zones = chip->temp_zones; + num_zones = chip->num_temp_zones; + prev_zone = chip->pres_temp_zone; + + mmi_chrg_info(chip, "prev zone %d, temp_c %d\n",prev_zone, temp_c); + max_temp = zones[num_zones - 1].temp_c; + + if (prev_zone == ZONE_NONE) { + for (i = num_zones - 1; i >= 0; i--) { + if (temp_c >= zones[i].temp_c) { + if (i == num_zones - 1) + chip->pres_temp_zone = ZONE_HOT; + else + chip->pres_temp_zone = i + 1; + return true; + } + } + chip->pres_temp_zone = ZONE_COLD; + return true; + } + + if (prev_zone == ZONE_COLD) { + if (temp_c >= MIN_TEMP_C + HYSTEREISIS_DEGC) + chip->pres_temp_zone = ZONE_FIRST; + } else if (prev_zone == ZONE_HOT) { + if (temp_c <= max_temp - HYSTEREISIS_DEGC) + chip->pres_temp_zone = num_zones - 1; + } else { + if (prev_zone == ZONE_FIRST) { + hotter_t = zones[prev_zone].temp_c; + colder_t = MIN_TEMP_C; + hotter_fcc = zones[prev_zone + 1].chrg_step_power->chrg_step_curr; + colder_fcc = 0; + } else if (prev_zone == num_zones - 1) { + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + hotter_fcc = 0; + colder_fcc = zones[prev_zone - 1].chrg_step_power->chrg_step_curr; + } else { + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + hotter_fcc = zones[prev_zone + 1].chrg_step_power->chrg_step_curr; + colder_fcc = zones[prev_zone - 1].chrg_step_power->chrg_step_curr; + } + + if (!ignore_hysteresis_degc) { + if (zones[prev_zone].chrg_step_power->chrg_step_curr < hotter_fcc) + hotter_t += HYSTEREISIS_DEGC; + if (zones[prev_zone].chrg_step_power->chrg_step_curr < colder_fcc) + colder_t -= HYSTEREISIS_DEGC; + } + + if (temp_c <= MIN_TEMP_C) + chip->pres_temp_zone = ZONE_COLD; + else if (temp_c >= max_temp) + chip->pres_temp_zone = ZONE_HOT; + else if (temp_c >= hotter_t) + chip->pres_temp_zone++; + else if (temp_c < colder_t) + chip->pres_temp_zone--; + } + + mmi_chrg_info(chip, "batt temp_c %d, prev zone %d, pres zone %d, " + "hotter_fcc %dmA, colder_fcc %dmA, " + "hotter_t %dC, colder_t %dC\n", + temp_c,prev_zone, chip->pres_temp_zone, + hotter_fcc, colder_fcc, hotter_t, colder_t); + + if (prev_zone != chip->pres_temp_zone) { + mmi_chrg_info(chip, + "Entered Temp Zone %d!\n", + chip->pres_temp_zone); + return true; + } + return false; +} + +bool mmi_find_chrg_step(struct mmi_charger_manager *chip, int temp_zone, int vbatt_volt) +{ + int batt_volt, i; + bool find_step = false; + struct mmi_chrg_temp_zone zone; + struct mmi_chrg_step_power *chrg_steps; + struct mmi_chrg_step_info chrg_step_inline; + struct mmi_chrg_step_info prev_step; + + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return false; + } + + if (chip->pres_temp_zone == ZONE_HOT || + chip->pres_temp_zone == ZONE_COLD || + chip->pres_temp_zone < ZONE_FIRST) { + mmi_chrg_err(chip, "pres temp zone is HOT or COLD, " + "can't find chrg step\n"); + return false; + } + + zone = chip->temp_zones[chip->pres_temp_zone]; + chrg_steps = zone.chrg_step_power; + prev_step = chip->chrg_step; + + batt_volt = vbatt_volt; + chrg_step_inline.temp_c = zone.temp_c; + + mmi_chrg_info(chip, "batt_volt %d, chrg step %d, step nums %d\n", + batt_volt, prev_step.pres_chrg_step, + chip->chrg_step_nums); + + /*In the first search cycle, find out the vbatt is less than step volt*/ + + for (i = 0; i < chip->chrg_step_nums; i++) { + mmi_chrg_info(chip, + "first cycle,i %d, step volt %d, batt_volt %d\n", + i, chrg_steps[i].chrg_step_volt, batt_volt); + if (chrg_steps[i].chrg_step_volt > 0 + && batt_volt < chrg_steps[i].chrg_step_volt) { + if ( (i + 1) < chip->chrg_step_nums + && chrg_steps[i + 1].chrg_step_volt > 0) { + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i + 1].chrg_step_curr; + } else + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cc_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cv_volt = + chrg_steps[i].chrg_step_volt; + chrg_step_inline.pres_chrg_step = i; + find_step = true; + mmi_chrg_info(chip, "find chrg step\n"); + break; + } + } + + if (find_step) { + mmi_chrg_info(chip, "chrg step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chrg_step_inline.pres_chrg_step, + chrg_step_inline.chrg_step_cc_curr, + chrg_step_inline.chrg_step_cv_volt, + chrg_step_inline.chrg_step_cv_tapper_curr); + chip->chrg_step = chrg_step_inline; + } else { + + /*If can't find out any chrg step in the first search cycle,*/ + /*it means that vbatt is already greater than all chrg step volt, */ + /*therefore, start to enter second search cycle, */ + /*to find out the maximal chrg step volt*/ + for (i = 0; i < chip->chrg_step_nums; i++) { + mmi_chrg_info(chip, + "second cycle, i %d, step volt %d, batt_volt %d\n", + i, chrg_steps[i].chrg_step_volt, batt_volt); + if (chrg_steps[i].chrg_step_volt > 0 + && batt_volt > chrg_steps[i].chrg_step_volt) { + if ( (i + 1) < chip->chrg_step_nums + && chrg_steps[i + 1].chrg_step_volt > 0) { + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i + 1].chrg_step_curr; + } else + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cc_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cv_volt = + chrg_steps[i].chrg_step_volt; + chrg_step_inline.pres_chrg_step = i; + find_step = true; + mmi_chrg_info(chip, "find chrg step\n"); + } + } + + if (find_step) { + mmi_chrg_info(chip, "chrg step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chrg_step_inline.pres_chrg_step, + chrg_step_inline.chrg_step_cc_curr, + chrg_step_inline.chrg_step_cv_volt, + chrg_step_inline.chrg_step_cv_tapper_curr); + chip->chrg_step = chrg_step_inline; + } + } + + + if (find_step) { + if (chip->chrg_step.chrg_step_cc_curr == + chip->chrg_step.chrg_step_cv_tapper_curr) + chip->chrg_step.last_step = true; + else + chip->chrg_step.last_step = false; + + mmi_chrg_info(chip,"Temp zone %d, " + "select chrg step %d, step cc curr %d," + "step cv volt %d, step cv tapper curr %d, " + "is the last chrg step %d\n", + chip->pres_temp_zone, + chip->chrg_step.pres_chrg_step, + chip->chrg_step.chrg_step_cc_curr, + chip->chrg_step.chrg_step_cv_volt, + chip->chrg_step.chrg_step_cv_tapper_curr, + chip->chrg_step.last_step); + + if (prev_step.pres_chrg_step != chip->chrg_step.pres_chrg_step) { + mmi_chrg_info(chip, "Find the next chrg step\n"); + return true; + } + } + return false; +} + +void mmi_set_pps_result_history(struct mmi_charger_manager *chip, int pps_result) +{ + if (chip->pps_result_history_idx >= PPS_RET_HISTORY_SIZE -1) + chip->pps_result_history_idx = 0; + + if (pps_result < 0) + chip->pps_result_history[chip->pps_result_history_idx] = 1; + else + chip->pps_result_history[chip->pps_result_history_idx] = 0; + + chip->pps_result_history_idx++; +} + +bool mmi_get_pps_result_history(struct mmi_charger_manager *chip) +{ + int i = 0; + int result = 0; + for (i = 0; i < PPS_RET_HISTORY_SIZE; i++) + result += chip->pps_result_history[i]; + + if (result >= PPS_RET_HISTORY_SIZE) + return RESET_POWER; + else if (result >= PPS_RET_HISTORY_SIZE / 2) + return BLANCE_POWER; + else + return NO_ERROR; +} + +void mmi_clear_pps_result_history(struct mmi_charger_manager *chip) +{ + int i = 0;; + for (i = 0; i < PPS_RET_HISTORY_SIZE; i++) + chip->pps_result_history[i] = 0; +} + +int mmi_calculate_delta_volt(int pps_voltage, int pps_current, int delta_curr) +{ + u64 power; + int delta_volt; + u64 pps_volt; + u64 pps_curr; + + pps_volt = pps_voltage; + pps_curr = pps_current; + power = pps_volt * pps_curr; + + delta_volt = (int)(power / (pps_curr - delta_curr) - pps_volt); + delta_volt -= delta_volt % 20000; + + if (delta_volt > 0) + return delta_volt; + else + return 0; +} + +void mmi_update_all_charger_status(struct mmi_charger_manager *chip) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + + chrg_num = chip->mmi_chrg_dev_num; + for (i = 0; i < chrg_num; i++) { + chrg_dev = chip->chrg_list[i]; + mmi_update_charger_status(chrg_dev); + } + return; +} + +void mmi_update_all_charger_error(struct mmi_charger_manager *chip) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + + chrg_num = chip->mmi_chrg_dev_num; + for (i = 0; i < chrg_num; i++) { + chrg_dev = chip->chrg_list[i]; + mmi_update_charger_error(chrg_dev); + } + return; +} + +void mmi_dump_charger_error(struct mmi_charger_manager *chip, + struct mmi_charger_device *chrg_dev) +{ + if (!chip || !chrg_dev) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + mmi_chrg_dbg(chip, PR_MOTO, "%s: charger error type %d, " + "bus ucp err cnt %d, bus ocp err cnt %d\n", + chrg_dev->name, + chrg_dev->charger_error.chrg_err_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chrg_dev->charger_error.bus_ocp_err_cnt); + return; +} +#if 0 +static enum power_supply_property batt_props[] = { + POWER_SUPPLY_PROP_INPUT_SUSPEND, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGER_TEMP, + POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_QNOVO, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_QNOVO, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_SW_JEITA_ENABLED, + POWER_SUPPLY_PROP_CHARGE_DONE, + POWER_SUPPLY_PROP_PARALLEL_DISABLE, + POWER_SUPPLY_PROP_SET_SHIP_MODE, + POWER_SUPPLY_PROP_DIE_HEALTH, + POWER_SUPPLY_PROP_RERUN_AICL, + POWER_SUPPLY_PROP_DP_DM, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_RECHARGE_SOC, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_FORCE_RECHARGE, + POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, + POWER_SUPPLY_PROP_CHARGE_RATE, + POWER_SUPPLY_PROP_AGE, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static int batt_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + union power_supply_propval prop = {0,}; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + rc = power_supply_get_property(chip->extrn_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d from extrn_psy rc = %d\n", + psp, rc); + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + } + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + if (chip->use_batt_age) { + val->intval = chip->cycles; + } else { + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + rc = 0; + val->intval = -EINVAL; + } + if (!rc) + val->intval = prop.intval; + } + break; + case POWER_SUPPLY_PROP_STATUS: + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + rc = 0; + val->intval = -EINVAL; + } else { + val->intval = prop.intval; + if (prop.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) { + rc = power_supply_get_property(chip->extrn_psy, + psp, &prop); + if (!rc && (prop.intval == POWER_SUPPLY_STATUS_CHARGING + || prop.intval == POWER_SUPPLY_STATUS_FULL)) + val->intval = prop.intval; + } + } + break; + case POWER_SUPPLY_PROP_CHARGE_RATE: + mmi_chrg_rate_check(chip); + val->intval = chip->charger_rate; + break; + default: + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + rc = 0; + val->intval = -EINVAL; + } + if (!rc) + val->intval = prop.intval; + break; + + } + return rc; +} + +static int batt_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (prop) { + case POWER_SUPPLY_PROP_CYCLE_COUNT: + chip->cycles = val->intval; + break; + default: + rc = power_supply_set_property(chip->qcom_psy, + prop, val); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", prop, rc); + rc = 0; + } + break; + } + return rc; +} + +static int batt_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_INPUT_SUSPEND: + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_PARALLEL_DISABLE: + case POWER_SUPPLY_PROP_DP_DM: + case POWER_SUPPLY_PROP_RERUN_AICL: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: + case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: + case POWER_SUPPLY_PROP_DIE_HEALTH: + case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + return 1; + default: + break; + } + return 0; +} +static const struct power_supply_desc batt_psy_desc = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = batt_props, + .num_properties = ARRAY_SIZE(batt_props), + .get_property = batt_get_property, + .set_property = batt_set_property, + .property_is_writeable = batt_is_writeable, +}; +static int batt_psy_register(struct mmi_charger_manager *chip) +{ + struct power_supply_config batt_cfg = {}; + + batt_cfg.drv_data = chip; + batt_cfg.of_node = chip->dev->of_node; + chip->batt_psy = power_supply_register(chip->dev, + &batt_psy_desc, + &batt_cfg); + if (IS_ERR(chip->batt_psy)) { + pr_err("Couldn't register batt_psy power supply\n"); + return PTR_ERR(chip->batt_psy); + } + + pr_info("power supply register batt_psy successfully\n"); + return 0; +} +#endif +static enum power_supply_property mmi_chrg_mgr_props[] = { + POWER_SUPPLY_PROP_PD_CURRENT_MAX, + POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS, + POWER_SUPPLY_PROP_CP_ENABLE, +}; + +static int mmi_chrg_mgr_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + union power_supply_propval prop = {0,}; + int rc, i, chrg_cnt = 0; + + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return -ENODEV; + } + + switch (psp) { + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + val->intval = chip->pd_request_curr_prev; + break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + val->intval = chip->pd_request_volt_prev; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED: + val->intval = chip->pd_target_curr; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED: + val->intval = chip->pd_target_volt; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + if (chip->mmi_chrg_dev_num) { + for (i = 0; i < chip->mmi_chrg_dev_num; i++) { + if (chip->chrg_list[i]->charger_enabled) + chrg_cnt++; + } + val->intval = chrg_cnt; + } else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + val->intval = chip->system_thermal_level; + break; + case POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS: + val->intval = chip->thermal_levels; + break; + case POWER_SUPPLY_PROP_CP_ENABLE: + val->intval = !chip->cp_disable; + break; + default: + return -EINVAL; + + } + return 0; +} + +static int mmi_chrg_mgr_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + if (val->intval < 0) + return -EINVAL; + + if (chip->thermal_levels <= 0) + return -EINVAL; + + if (val->intval >= chip->thermal_levels) + chip->system_thermal_level = + chip->thermal_levels - 1; + else + chip->system_thermal_level = val->intval; + + break; + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + if (val->intval > chip->pd_curr_max) + return -EINVAL; + chip->pd_curr_max = val->intval; + break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + if (val->intval > chip->pd_volt_max) + return -EINVAL; + chip->pd_volt_max= val->intval; + break; + case POWER_SUPPLY_PROP_CP_ENABLE: + if (chip->pd_pps_support + && !chip->factory_mode) + mmi_chrg_enable_all_cp(chip, val->intval); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mmi_chrg_mgr_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + case POWER_SUPPLY_PROP_CP_ENABLE: + ret = 1; + break; + default: + ret = 0; + break; + } + return ret; +} + +static const struct power_supply_desc mmi_chrg_mgr_psy_desc = { + .name = "mmi_chrg_manager", + .type = POWER_SUPPLY_TYPE_PARALLEL, + .properties = mmi_chrg_mgr_props, + .num_properties = ARRAY_SIZE(mmi_chrg_mgr_props), + .get_property = mmi_chrg_mgr_get_property, + .set_property = mmi_chrg_mgr_set_property, + .property_is_writeable = mmi_chrg_mgr_is_writeable, +}; + +static int mmi_chrg_mgr_psy_register(struct mmi_charger_manager *chip) +{ + struct power_supply_config mmi_chrg_mgr_cfg = {}; + + mmi_chrg_mgr_cfg.drv_data = chip; + mmi_chrg_mgr_cfg.of_node = chip->dev->of_node; + chip->mmi_chrg_mgr_psy = power_supply_register(chip->dev, + &mmi_chrg_mgr_psy_desc, + &mmi_chrg_mgr_cfg); + if (IS_ERR(chip->mmi_chrg_mgr_psy)) { + pr_err("Couldn't register mmi_chrg_mgr_psy power supply\n"); + return PTR_ERR(chip->mmi_chrg_mgr_psy); + } + + pr_info("power supply register mmi_chrg_mgr_psy successfully\n"); + return 0; +} + +static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) +{ + struct mmi_charger_manager *chip = + container_of(nb, struct mmi_charger_manager, psy_nb); + + if (!chip->usb_psy) { + mmi_chrg_err(chip, "usb psy is NULL, direct return\n"); + return NOTIFY_OK; + } + + if (ptr == chip->usb_psy && evt == PSY_EVENT_PROP_CHANGED) { + schedule_work(&chip->psy_changed_work); + + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(1000)); + + } + + return NOTIFY_OK; +} + +static int get_prop_charger_present(struct mmi_charger_manager *chg, + union power_supply_propval *val) +{ + int rc = -EINVAL; + val->intval = 0; + + if (chg->usb_psy) + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, val); + if (rc < 0) { + mmi_chrg_err(chg, "Couldn't read TypeC Mode rc=%d\n", rc); + return rc; + } + + switch (val->intval) { + case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: + case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: + case POWER_SUPPLY_TYPEC_SOURCE_HIGH: + val->intval = 1; + break; + default: + val->intval = 0; + break; + } + + return rc; +} + +#define WEAK_CHRG_THRSH 450 +#define TURBO_CHRG_THRSH 2500 +#define TURBO_30W_CHRG_THRSH_UW 25000000 +#define HYPER_CHRG_THRSH_UW 40000000 + +void mmi_chrg_rate_check(struct mmi_charger_manager *chg) +{ + union power_supply_propval val; + int chrg_cm_ma = 0; + int chrg_cs_ma = 0; + int charger_power = 0; + int prev_chg_rate = chg->charger_rate; + int rc = -EINVAL; + + if (!chg->usb_psy) { + mmi_chrg_err(chg, "No usb PSY\n"); + return; + } + + val.intval = 0; + rc = get_prop_charger_present(chg, &val); + if (rc < 0) { + mmi_chrg_err(chg, "Error getting Charger Present rc = %d\n", rc); + return; + } + + if (val.intval) { + val.intval = 0; + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, &val); + if (rc < 0) { + mmi_chrg_info(chg, "Error getting HW Current Max rc = %d\n", rc); + return; + } + chrg_cm_ma = val.intval / 1000; + + val.intval = 0; + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &val); + if (rc < 0) { + mmi_chrg_err(chg, "Error getting ICL Settled rc = %d\n", rc); + return; + } + chrg_cs_ma = val.intval / 1000; + } else { + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NONE; + goto end_rate_check; + } + + mmi_chrg_info(chg, "SMBMMI: cm %d, cs %d\n", chrg_cm_ma, chrg_cs_ma); + if (chrg_cm_ma >= TURBO_CHRG_THRSH) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO; + else if ((chrg_cm_ma > WEAK_CHRG_THRSH) && (chrg_cs_ma < WEAK_CHRG_THRSH)) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_WEAK; + else if (prev_chg_rate == POWER_SUPPLY_CHARGE_RATE_NONE) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NORMAL; + + if (chg->pd_pps_support) { + charger_power = (chg->pd_curr_max / 1000) * (chg->pd_volt_max / 1000); + if (charger_power >= HYPER_CHRG_THRSH_UW) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_HYPER; + else if (charger_power >= TURBO_30W_CHRG_THRSH_UW) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO_30W; + } +end_rate_check: + if (prev_chg_rate != chg->charger_rate) + mmi_chrg_info(chg, "%s Charger Detected\n", + charge_rate[chg->charger_rate]); + +} +#if 0 +#define CHG_SHOW_MAX_SIZE 50 +static ssize_t factory_image_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid factory image mode value = %lu\n", mode); + return -EINVAL; + } + + mmi_set_factory_image_mode(mode); + + return r ? r : count; +} + +static ssize_t factory_image_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + state = mmi_get_factory_image_mode() ? 1 : 0; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_image_mode, 0644, + factory_image_mode_show, + factory_image_mode_store); + +static ssize_t factory_charge_upper_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + state = mmi_get_factory_charge_upper(); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_charge_upper, 0444, + factory_charge_upper_show, + NULL); + +static ssize_t force_demo_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid demo mode value = %lu\n", mode); + return -EINVAL; + } + + mmi_set_demo_mode(mode); + + return r ? r : count; +} + +static ssize_t force_demo_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + state = mmi_get_demo_mode(); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_demo_mode, 0644, + force_demo_mode_show, + force_demo_mode_store); + +static ssize_t force_max_chrg_temp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid max temp value = %lu\n", mode); + return -EINVAL; + } + + mmi_set_max_chrg_temp(mode); + + return r ? r : count; +} + +static ssize_t force_max_chrg_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + state = mmi_get_max_chrg_temp(); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_max_chrg_temp, 0644, + force_max_chrg_temp_show, + force_max_chrg_temp_store); +#endif +static bool mmi_factory_check(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory = false; + + if (np) + factory = of_property_read_bool(np, "mmi,factory-cable"); + + of_node_put(np); + + return factory; +} + + +static void kick_sm(struct mmi_charger_manager *chip, int ms) +{ + union power_supply_propval val; + int ret; + + if (!chip->sm_work_running) { + mmi_chrg_dbg(chip, PR_INTERRUPT, + "launch mmi chrg sm work\n"); + mmi_chrg_policy_clear(chip); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(ms)); + chip->sm_work_running = true; + val.intval = true; + ret = power_supply_set_property(chip->usb_psy, + POWER_SUPPLY_PROP_CP_ENABLE, &val); + if (ret) + mmi_chrg_err(chip, "Unable to enable CP: %d\n", ret); + } else + mmi_chrg_dbg(chip, PR_INTERRUPT, + "mmi chrg sm work already existed\n"); +} + +static void cancel_sm(struct mmi_charger_manager *chip) +{ + union power_supply_propval val; + int ret; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + flush_delayed_work(&chip->mmi_chrg_sm_work); + mmi_chrg_policy_clear(chip); + chip->sm_work_running = false; + val.intval = false; + ret = power_supply_set_property(chip->usb_psy, + POWER_SUPPLY_PROP_CP_ENABLE, &val); + if (ret) + mmi_chrg_err(chip, "Unable to disable CP: %d\n", ret); + chip->pd_volt_max = pd_volt_max_init; + chip->pd_curr_max = pd_curr_max_init; + mmi_chrg_dbg(chip, PR_INTERRUPT, + "cancel sync and flush mmi chrg sm work\n"); +} + +void clear_chg_manager(struct mmi_charger_manager *chip) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "clear mmi chrg manager!\n"); + chip->pd_request_volt = 0; + chip->pd_request_curr = 0; + chip->pd_request_volt_prev = 0; + chip->pd_request_curr_prev = 0; + chip->pd_target_curr = 0; + chip->pd_target_volt = 0; + chip->pd_batt_therm_volt = 0; + chip->pd_batt_therm_curr = 0; + chip->pd_sys_therm_volt = 0; + chip->pd_sys_therm_curr = 0; + chip->recovery_pmic_chrg = false; + chip->sys_therm_cooling= false; + chip->sys_therm_force_pmic_chrg = false; + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + + memset(chip->mmi_pdo_info, 0, + sizeof(struct usbpd_pdo_info) * PD_MAX_PDO_NUM); +} + +static void mmi_awake_vote(struct mmi_charger_manager *chip, bool awake) +{ + if (awake == chip->awake) + return; + + chip->awake = awake; + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); +} + +static void mmi_batt_age_init(struct mmi_charger_manager *chip) +{ + int rc = 0; + union power_supply_propval pval; + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_STATUS, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt status\n"); + return; + } + chip->pres_batt_status = pval.intval; + chip->prev_batt_status = chip->pres_batt_status; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt capacity\n"); + return; + } + + chip->soc_cycles_start= pval.intval; + chip->batt_cap_delta = 0; + return; +} + +static void mmi_cycle_counts(struct mmi_charger_manager *chip) +{ + int rc = 0; + int batt_design_capacity = 0, new_cycles = 0; + int soc_report = 0, soc_delta = 0, batt_delta_capacity = 0; + union power_supply_propval pval; + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt design capacity\n"); + return; + } + batt_design_capacity = pval.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_STATUS, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt status\n"); + return; + } + chip ->pres_batt_status = pval.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt capacity\n"); + return; + } + soc_report = pval.intval; + + mmi_chrg_dbg(chip, PR_MOTO, "cycle_count %d, pres %d, prev %d, soc_report %d, " + "soc_cycles_start %d, batt_cap_delta %d, " + "design_capcaity %d, batt_soc_delta %d\n", + chip->cycles, chip->pres_batt_status, + chip->prev_batt_status, + soc_report, chip->soc_cycles_start, + chip->batt_cap_delta, batt_design_capacity, + chip->batt_soc_delta); + + if (chip->prev_batt_status == POWER_SUPPLY_STATUS_CHARGING + && chip->pres_batt_status != POWER_SUPPLY_STATUS_CHARGING) { + chip->soc_cycles_start = soc_report; + } else if (chip->prev_batt_status != POWER_SUPPLY_STATUS_CHARGING + && chip->pres_batt_status == POWER_SUPPLY_STATUS_CHARGING) { + + soc_delta = chip->soc_cycles_start - soc_report; + if (soc_delta > 0) { + chip->batt_soc_delta += soc_delta; + + batt_delta_capacity = (chip->batt_soc_delta * batt_design_capacity) / 100 + + chip->batt_cap_delta; + + new_cycles = batt_delta_capacity / batt_design_capacity; + if (new_cycles > 0) { + chip->cycles += batt_delta_capacity / batt_design_capacity; + chip->batt_cap_delta = batt_delta_capacity % batt_design_capacity; + chip->batt_soc_delta = 0; + } + } + chip->soc_cycles_start = soc_report; + } + + chip->prev_batt_status = chip->pres_batt_status; +} + +static bool __mmi_ps_is_supplied_by(struct power_supply *supplier, + struct power_supply *supply) +{ + int i; + + if (!supply->supplied_from && !supplier->supplied_to) + return false; + + /* Support both supplied_to and supplied_from modes */ + if (supply->supplied_from) { + if (!supplier->desc->name) + return false; + for (i = 0; i < supply->num_supplies; i++) + if (!strcmp(supplier->desc->name, + supply->supplied_from[i])) + return true; + } else { + if (!supply->desc->name) + return false; + for (i = 0; i < supplier->num_supplicants; i++) + if (!strcmp(supplier->supplied_to[i], + supply->desc->name)) + return true; + } + + return false; +} + +static int __mmi_ps_changed(struct device *dev, void *data) +{ + struct power_supply *psy = data; + struct power_supply *pst = dev_get_drvdata(dev); + + if (__mmi_ps_is_supplied_by(psy, pst)) { + if (pst->desc->external_power_changed) + pst->desc->external_power_changed(pst); + } + + return 0; +} + +static void mmi_power_supply_changed(struct power_supply *psy, + char *envp_ext[]) +{ + dev_err(&psy->dev, "%s: %s\n", __func__, envp_ext[0]); + + class_for_each_device(power_supply_class, NULL, psy, + __mmi_ps_changed); + atomic_notifier_call_chain(&power_supply_notifier, + PSY_EVENT_PROP_CHANGED, psy); + kobject_uevent_env(&psy->dev.kobj, KOBJ_CHANGE, envp_ext); +} + +static enum alarmtimer_restart mmi_heartbeat_alarm_cb(struct alarm *alarm, + ktime_t now) +{ + struct mmi_charger_manager *chip = container_of(alarm, + struct mmi_charger_manager, + heartbeat_alarm); + + mmi_chrg_info(chip, "MMI: HB alarm fired\n"); + + __pm_stay_awake(&chip->mmi_hb_wake_source); + cancel_delayed_work(&chip->heartbeat_work); + /* Delay by 500 ms to allow devices to resume. */ + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(500)); + + return ALARMTIMER_NORESTART; +} + +#define HEARTBEAT_DELAY_MS 60000 +#define SMBCHG_HEARTBEAT_INTRVAL_NS 70000000000 +#define CHG_SHOW_MAX_SIZE 50 +static void mmi_heartbeat_work(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, heartbeat_work.work); + int hb_resch_time = 0, ret = 0, i = 0; + char *chrg_rate_string = NULL; + char *envp[2]; + union power_supply_propval val; + bool pd_active; + + mmi_chrg_info(chip, "MMI: Heartbeat!\n"); + /* Have not been resumed so wait another 100 ms */ + if (chip->suspended) { + mmi_chrg_err(chip, "SMBMMI: HB running before Resume\n"); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(100)); + return; + } + + mmi_awake_vote(chip, true); + alarm_try_to_cancel(&chip->heartbeat_alarm); + + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_chrg_err(chip, + "Could not get USB power_supply, deferring probe\n"); + return; + } + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read USB PRESENT: %d\n", ret); + return; + } + chip->vbus_present = val.intval; + + hb_resch_time = HEARTBEAT_DELAY_MS; + + if (chip->vbus_present) + alarm_start_relative(&chip->heartbeat_alarm, + ns_to_ktime(SMBCHG_HEARTBEAT_INTRVAL_NS)); + + if (!chip->vbus_present) + mmi_awake_vote(chip, false); + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PD_ACTIVE, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read PD ACTIVE: %d\n", ret); + goto schedule_work; + } + pd_active = val.intval; + + if (!chip->pd_handle) { + chip->pd_handle = devm_usbpd_get_by_phandle(chip->dev, + "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + mmi_chrg_err(chip, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + goto schedule_work; + } + } + + if (!chip->sm_work_running && chip->vbus_present + && pd_active) { + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + mmi_chrg_info(chip, "check all effective pdo info\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED) + && chip->mmi_pdo_info[i].uv_max >= PUMP_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua >= chip->typec_middle_current) { + chip->mmi_pd_pdo_idx = chip->mmi_pdo_info[i].pdo_pos; + mmi_chrg_info(chip, + "pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pd_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max < + chip->pd_volt_max) { + chip->pd_volt_max = + chip->mmi_pdo_info[i].uv_max; + } + if (chip->mmi_pdo_info[i].ua < + chip->pd_curr_max) { + chip->pd_curr_max = + chip->mmi_pdo_info[i].ua; + } + break; + } + } + + if (chip->pd_pps_support + && !chip->factory_mode) { + mmi_chrg_info(chip, "MMI: Heartbeat!, launch sm work\n"); + kick_sm(chip, 100); + } + } + + if (chip->use_batt_age) + mmi_cycle_counts(chip); + + mmi_chrg_rate_check(chip); + if (!chip->extrn_fg) { + val.intval = chip->charger_rate; + ret = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_RATE, &val); + if (ret) + mmi_chrg_err(chip, "Unable to set charge rate: %d\n", ret); + } + + chrg_rate_string = kmalloc(CHG_SHOW_MAX_SIZE, GFP_KERNEL); + if (!chrg_rate_string) { + mmi_chrg_err(chip, "SMBMMI: Failed to Get Uevent Mem\n"); + envp[0] = NULL; + } else { + scnprintf(chrg_rate_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_CHARGE_RATE=%s", + charge_rate[chip->charger_rate]); + envp[0] = chrg_rate_string; + envp[1] = NULL; + } + + if (chip->qcom_psy) { + mmi_power_supply_changed(chip->batt_psy, envp); + } + + kfree(chrg_rate_string); + + __pm_relax(&chip->mmi_hb_wake_source); + +schedule_work: + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(hb_resch_time)); +} + +static void psy_changed_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, psy_changed_work); + union power_supply_propval val; + bool pd_active; + int ret, i; + + mmi_chrg_info(chip, "kick psy changed work.\n"); + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_chrg_err(chip, + "Could not get USB power_supply, deferring probe\n"); + return; + } + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read USB PRESENT: %d\n", ret); + return; + } + chip->vbus_present = val.intval; + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PD_ACTIVE, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read PD ACTIVE: %d\n", ret); + return; + } + pd_active = val.intval; + + if (!chip->pd_handle) { + chip->pd_handle = devm_usbpd_get_by_phandle(chip->dev, + "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + mmi_chrg_err(chip, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + return; + } + } + + if (pd_active && chip->vbus_present) { + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + mmi_chrg_info(chip, "check all effective pdo info\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED) + && chip->mmi_pdo_info[i].uv_max >= PUMP_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua >= chip->typec_middle_current) { + chip->mmi_pd_pdo_idx = chip->mmi_pdo_info[i].pdo_pos; + mmi_chrg_info(chip, + "pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pd_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max < + chip->pd_volt_max) { + chip->pd_volt_max = + chip->mmi_pdo_info[i].uv_max; + } + + if (chip->mmi_pdo_info[i].ua < + chip->pd_curr_max) { + chip->pd_curr_max = + chip->mmi_pdo_info[i].ua; + } + + break; + } + } + } + + mmi_chrg_info(chip, "vbus present %d, pd pps support %d, " + "pps max voltage %d, pps max curr %d\n", + chip->vbus_present, + chip->pd_pps_support, + chip->pd_volt_max, + chip->pd_curr_max); + + if (chip->vbus_present + && chip->pd_pps_support + && !chip->factory_mode) { + kick_sm(chip, 100); + } else { + cancel_sm(chip); + clear_chg_manager(chip); + chip->pd_pps_support = false; + } + return; +} + +#define DEFAULT_BATT_OVP_LMT 4475000 +#define DEFAULT_PL_CHRG_VBATT_MIN 3600000 +#define DEFAULT_PPS_VOLT_STEPS 20000 +#define DEFAULT_PPS_CURR_STEPS 50000 +#define DEFAULT_PPS_VOLT_MAX 11000000 +#define DEFAULT_PPS_CURR_MAX 4000000 +#define MMI_TEMP_ZONES 5 +static int mmi_chrg_manager_parse_dt(struct mmi_charger_manager *chip) +{ + struct device_node *node = chip->dev->of_node, *child; + int rc, byte_len, i, j, chrg_idx = 0, step_cnt = 0, idx = 0; + const int *ptr; + + rc = of_property_read_u32(node, + "mmi,batt-ovp-lmt", + &chip->batt_ovp_lmt); + if (rc < 0) + chip->batt_ovp_lmt = + DEFAULT_BATT_OVP_LMT; + + rc = of_property_read_u32(node, + "mmi,pl-chrg-vbatt-min", + &chip->pl_chrg_vbatt_min); + if (rc < 0) + chip->pl_chrg_vbatt_min = + DEFAULT_PL_CHRG_VBATT_MIN; + + chip->use_batt_age = of_property_read_bool(node, + "mmi,use-batt-age"); + + chip->extrn_fg = of_property_read_bool(node, + "mmi,extrn-fg"); + + if (chip->extrn_fg) { + byte_len = of_property_count_strings(node, "mmi,extrn-fg-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse mmi, extrn-fg: %d\n", byte_len); + return byte_len; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(node, "mmi,extrn-fg-name", + i, &chip->extrn_fg_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse extrn-fg-name\n"); + return rc; + } + } + } + + chip->extrn_sense = of_property_read_bool(node, + "mmi,extrn-sense"); + + chip->dont_rerun_aicl= of_property_read_bool(node, + "mmi,dont-rerun-aicl"); + + rc = of_property_read_u32(node, + "mmi,typec-middle-current", + &chip->typec_middle_current); + if (rc < 0) + chip->typec_middle_current = + TYPEC_MIDDLE_CURRENT_UA; + + rc = of_property_read_u32(node, + "mmi,step-first-current-comp", + &chip->step_first_curr_comp); + if (rc < 0) + chip->step_first_curr_comp = + STEP_FIREST_CURR_COMP; + + rc = of_property_read_u32(node, + "mmi,pps-volt-steps", + &chip->pps_volt_steps); + if (rc < 0) + chip->pps_volt_steps = + DEFAULT_PPS_VOLT_STEPS; + + rc = of_property_read_u32(node, + "mmi,pps-curr-steps", + &chip->pps_curr_steps); + if (rc < 0) + chip->pps_curr_steps = + DEFAULT_PPS_CURR_STEPS; + + rc = of_property_read_u32(node, + "mmi,pd-volt-max", + &chip->pd_volt_max); + if (rc < 0) + chip->pd_volt_max = + DEFAULT_PPS_VOLT_MAX; + pd_volt_max_init = chip->pd_volt_max; + rc = of_property_read_u32(node, + "mmi,pd-curr-max", + &chip->pd_curr_max); + if (rc < 0) + chip->pd_curr_max = + DEFAULT_PPS_CURR_MAX; + pd_curr_max_init = chip->pd_curr_max; + + for_each_child_of_node(node, child) + chip->mmi_chrg_dev_num++; + + if (!chip->mmi_chrg_dev_num) { + mmi_chrg_err(chip,"No mmi_chrg_dev list !\n"); + return -ENODEV; + } + + chrg_name_list = (struct mmi_chrg_dts_info *)devm_kzalloc(chip->dev, + sizeof(struct mmi_chrg_dts_info) * + chip->mmi_chrg_dev_num, GFP_KERNEL); + if (!chrg_name_list) { + mmi_chrg_err(chip,"No memory for mmi charger device name list !\n"); + goto cleanup; + } + + chip->chrg_list = (struct mmi_charger_device **)devm_kzalloc(chip->dev, + sizeof(struct mmi_charger_device *) * + chip->mmi_chrg_dev_num, GFP_KERNEL); + if (!chip->chrg_list) { + mmi_chrg_err(chip,"No memory for mmi charger device list !\n"); + goto cleanup; + } + + for_each_child_of_node(node, child) { + byte_len = of_property_count_strings(child, "chrg-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse chrg-name: %d\n", byte_len); + goto cleanup; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(child, "chrg-name", + i, &chrg_name_list[chrg_idx].chrg_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse chrg-name\n"); + goto cleanup; + } + } + + byte_len = of_property_count_strings(child, "psy-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse psy-name: %d\n", byte_len); + goto cleanup; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(child, "psy-name", + i, &chrg_name_list[chrg_idx].psy_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse psy-name\n"); + goto cleanup; + } + } + + mmi_chrg_info(chip, "mmi,chrg-name: %s, psy-name: %s\n", + chrg_name_list[chrg_idx].chrg_name, + chrg_name_list[chrg_idx].psy_name); + + of_property_read_u32(child, "charging-curr-limited", + &chrg_name_list[chrg_idx].charging_curr_limited); + + of_property_read_u32(child, "charging-curr-min", + &chrg_name_list[chrg_idx].charging_curr_min); + + chrg_idx++; + } + + rc = of_property_read_u32(node, + "mmi,chrg-temp-zones-num", + &chip->num_temp_zones); + if (rc < 0) + chip->num_temp_zones = + MMI_TEMP_ZONES; + + chip->temp_zones = (struct mmi_chrg_temp_zone *) + devm_kzalloc(chip->dev, + sizeof(struct mmi_chrg_temp_zone ) + * chip->num_temp_zones, + GFP_KERNEL); + if (chip->temp_zones == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + if (of_find_property(node, "mmi,mmi-chrg-temp-zones", &byte_len)) { + if (byte_len <= 0) { + mmi_chrg_err(chip, + "Cannot parse mmi-chrg-temp-zones %d\n",byte_len); + goto cleanup; + } + + step_cnt = (byte_len / chip->num_temp_zones - sizeof(u32)) + / (sizeof(u32) * 2); + mmi_chrg_info(chip, "mmi chrg step number is %d\n", step_cnt); + + dt_temp_zones = (u32 *) devm_kzalloc(chip->dev, byte_len, GFP_KERNEL); + if (dt_temp_zones == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + rc = of_property_read_u32_array(node, + "mmi,mmi-chrg-temp-zones", + (u32 *)dt_temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_chrg_err(chip, + "Couldn't read mmi chrg temp zones rc = %d\n", rc); + goto zones_failed; + } + + mmi_chrg_err(chip, "temp zone nums: %d , byte_len %d, " + "num %d, row size %d\n", + chip->num_temp_zones, byte_len, + (int)(byte_len / sizeof(u32)), + (int)(sizeof(struct mmi_chrg_step_power) * step_cnt)); + ptr = dt_temp_zones; + + for (i = 0; i < byte_len / sizeof(u32); i++) { + if (!(i % 9)) { + printk("\n"); + } + printk("%u,", ptr[i]); + } + printk("\n"); + + for (i = 0; i < chip->num_temp_zones; i++) { + idx = (int)((sizeof(u32) + + sizeof(struct mmi_chrg_step_power) * step_cnt ) + * i / sizeof(u32)); + chip->temp_zones[i].temp_c = dt_temp_zones[idx]; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + idx = (int)(((sizeof(u32) + + sizeof(struct mmi_chrg_step_power) * step_cnt ) + * i + sizeof(u32)) / sizeof(u32)); + chip->temp_zones[i].chrg_step_power = + (struct mmi_chrg_step_power *)&dt_temp_zones[idx]; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + + printk( "mmi temp zones: Zone: %d, Temp: %d C " , i, + chip->temp_zones[i].temp_c); + for (j = 0; j < step_cnt; j++) { + chip->temp_zones[i].chrg_step_power[j].chrg_step_curr *= 1000; + chip->temp_zones[i].chrg_step_power[j].chrg_step_volt *= 1000; + + printk("step_volt %dmV, step_curr %dmA, ", + chip->temp_zones[i].chrg_step_power[j].chrg_step_volt, + chip->temp_zones[i].chrg_step_power[j].chrg_step_curr); + } + printk("\n"); + } + + chip->pres_temp_zone = ZONE_NONE; + chip->chrg_step_nums = step_cnt; + } + + if (of_find_property(node, "mmi,thermal-mitigation", &byte_len)) { + chip->thermal_mitigation = devm_kzalloc(chip->dev, byte_len, + GFP_KERNEL); + + if (chip->thermal_mitigation == NULL) { + rc = -ENOMEM; + goto zones_failed; + } + chip->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "mmi,thermal-mitigation", + chip->thermal_mitigation, + chip->thermal_levels); + if (rc < 0) { + mmi_chrg_err(chip, + "Couldn't parse thermal-mitigation rc = %d\n", rc); + goto thermal_failed; + } + } + + return rc; +thermal_failed: + devm_kfree(chip->dev,chip->thermal_mitigation); +zones_failed: + devm_kfree(chip->dev,chip->temp_zones); +cleanup: + chip->mmi_chrg_dev_num = 0; + devm_kfree(chip->dev,chip->chrg_list); + return rc; +} + +static int mmi_chrg_manager_probe(struct platform_device *pdev) +{ + int ret = 0; + struct mmi_charger_manager *chip; + + if (!pdev) + return -ENODEV; + + chip = devm_kzalloc(&pdev->dev, sizeof(struct mmi_charger_manager), + GFP_KERNEL); + if (!chip) { + dev_err(&pdev->dev, + "Unable to alloc memory for mmi_charger_manager\n"); + return -ENOMEM; + } + + chip->dev = &pdev->dev; + chip->name = "mmi_chrg_manager"; + chip->debug_mask = &__debug_mask; + chip->suspended = false; + + ret = mmi_charger_class_init(); + if (ret < 0) { + dev_err(&pdev->dev, + "mmi charger class init failed\n"); + goto cleanup; + } + + ret = mmi_chrg_manager_parse_dt(chip); + if (ret < 0) { + dev_err(&pdev->dev, + "parse dt failed\n"); + goto cleanup; + } + + chip->factory_mode = mmi_factory_check(); + + chip->qcom_psy = power_supply_get_by_name("qcom_battery"); +#if 0 + if (chip->qcom_psy && chip->extrn_fg) { + + chip->extrn_psy = power_supply_get_by_name(chip->extrn_fg_name); + if (!chip->extrn_psy) { + mmi_chrg_err(chip, "Get extrn psy failed\n"); + goto cleanup; + } + ret = batt_psy_register(chip); + if (ret) { + mmi_chrg_err(chip, "Batt psy register failed\n"); + goto cleanup; + } + + ret = device_create_file(chip->dev, + &dev_attr_factory_image_mode); + if (ret) { + mmi_chrg_err(chip, "couldn't create factory_image_mode\n"); + } + + ret = device_create_file(chip->dev, + &dev_attr_force_demo_mode); + if (ret) { + mmi_chrg_err(chip, "couldn't create force_demo_mode\n"); + } + + ret = device_create_file(chip->dev, + &dev_attr_force_max_chrg_temp); + if (ret) { + mmi_chrg_err(chip, "couldn't create force_max_chrg_temp\n"); + } + + ret = device_create_file(chip->dev, + &dev_attr_factory_charge_upper); + if (ret) + mmi_chrg_err(chip, "couldn't create factory_charge_upper\n"); + + } else +#endif + chip->batt_psy = power_supply_get_by_name("battery"); + + if (!chip->batt_psy) { + mmi_chrg_err(chip, "Could not get battery power_supply\n"); + goto cleanup; + } + + + ret = mmi_chrg_policy_init(chip, chrg_name_list, + chip->mmi_chrg_dev_num); + if (ret < 0) { + dev_err(&pdev->dev, + "mmi chrg policy init failed\n"); + goto cleanup; + } + + chip->pd_handle = + devm_usbpd_get_by_phandle(chip->dev, "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + dev_err(&pdev->dev, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + } + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) + mmi_chrg_err(chip, "Could not get USB power_supply\n"); + } + + INIT_WORK(&chip->psy_changed_work, psy_changed_work_func); + INIT_DELAYED_WORK(&chip->heartbeat_work, mmi_heartbeat_work); + wakeup_source_init_internal(&chip->mmi_hb_wake_source, + "mmi_hb_wake"); + alarm_init(&chip->heartbeat_alarm, ALARM_BOOTTIME, + mmi_heartbeat_alarm_cb); + + + ret = mmi_chrg_mgr_psy_register(chip); + if (ret) + goto cleanup; + + chip->psy_nb.notifier_call = psy_changed; + ret = power_supply_reg_notifier(&chip->psy_nb); + if (ret) + goto cleanup; + + mmi_batt_age_init(chip); + + init_completion(&chip->sm_completion); + platform_set_drvdata(pdev, chip); + + //create_sysfs_entries(chip); + schedule_work(&chip->psy_changed_work); + mmi_chrg_info(chip, "mmi chrg manager initialized successfully, ret %d\n", ret); + return 0; +cleanup: + mmi_charger_class_exit(); + devm_kfree(&pdev->dev, chip); + return ret; +} + +static int mmi_chrg_manager_remove(struct platform_device *pdev) +{ + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + //remove_sysfs_entries(chip); + //cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + power_supply_unreg_notifier(&chip->psy_nb); + power_supply_unregister(chip->mmi_chrg_mgr_psy ); +// wakeup_source_trash_internal(&chip->mmi_hb_wake_source); + platform_set_drvdata(pdev, NULL); + devm_kfree(&pdev->dev, chip); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mmi_chrg_suspend(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + chip->suspended = true; + + return 0; +} + +static int mmi_chrg_resume(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + chip->suspended = false; + + return 0; +} +#else +#define smb_mmi_suspend NULL +#define smb_mmi_resume NULL +#endif + +static const struct dev_pm_ops mmi_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mmi_chrg_suspend, mmi_chrg_resume) +}; + +static const struct of_device_id mmi_chrg_manager_match_table[] = { + {.compatible = "mmi,chrg-manager"}, + {}, +}; +MODULE_DEVICE_TABLE(of, mmi_chrg_manager_match_table); + +static struct platform_driver mmi_chrg_manager_driver = { + .probe = mmi_chrg_manager_probe, + .remove = mmi_chrg_manager_remove, + .driver = { + .name = "mmi_chrg_manager", + .owner = THIS_MODULE, + .pm = &mmi_dev_pm_ops, + .of_match_table = mmi_chrg_manager_match_table, + }, +}; + +static int __init mmi_chrg_manager_init(void) +{ + int ret; + ret = platform_driver_register(&mmi_chrg_manager_driver); + if (ret) { + pr_err("mmi_chrg_manager failed to register driver\n"); + return ret; + } + return 0; +} + +static void __exit mmi_chrg_manager_exit(void) +{ + platform_driver_unregister(&mmi_chrg_manager_driver); +} + +module_init(mmi_chrg_manager_init); +module_exit(mmi_chrg_manager_exit); + +MODULE_ALIAS("platform:mmi parallel charger"); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("Motorola Mobility parallel charger"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.h b/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.h new file mode 100644 index 000000000000..4c7b435ce3fb --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.h @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MMI_CHRG_CORE_H_ +#define __MMI_CHRG_CORE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include + +#define mmi_chrg_err(chip, fmt, ...) \ + pr_err("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define mmi_chrg_info(chip, fmt, ...) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define mmi_chrg_dbg(chip, reason, fmt, ...) \ + do { \ + if (*chip->debug_mask & (reason)) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + else \ + pr_debug("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + } while (0) + +enum print_reason { + PR_INTERRUPT = BIT(0), + PR_MISC = BIT(2), + PR_MOTO = BIT(7), +}; + +enum { + POWER_SUPPLY_CHARGE_RATE_NONE = 0, + POWER_SUPPLY_CHARGE_RATE_NORMAL, + POWER_SUPPLY_CHARGE_RATE_WEAK, + POWER_SUPPLY_CHARGE_RATE_TURBO, + POWER_SUPPLY_CHARGE_RATE_TURBO_30W, + POWER_SUPPLY_CHARGE_RATE_HYPER, +}; + +static inline void wakeup_source_init_internal(struct wakeup_source *ws, + const char *name) +{ + if(ws) { + memset(ws, 0, sizeof(*ws)); + ws->name = name; + } + + wakeup_source_add(ws); +} +/* +static inline void wakeup_source_trash_internal(struct wakeup_source *ws) +{ + + wakeup_source_remove(ws); + if(!ws) + return; + __pm_relex(ws); +}*/ + +#define MAX_NUM_STEPS 10 +enum mmi_chrg_temp_zones { + ZONE_FIRST = 0, + /* states 0-9 are reserved for zones */ + ZONE_LAST = MAX_NUM_STEPS + ZONE_FIRST - 1, + ZONE_HOT, + ZONE_COLD, + ZONE_NONE = 0xFF, +}; + +enum mmi_chrg_steps { + STEP_FIRST = 0, + STEP_LAST = MAX_NUM_STEPS + STEP_FIRST - 1, + STEP_NONE = 0xFF, +}; + +enum mmi_pd_pps_result { + NO_ERROR, + BLANCE_POWER, + RESET_POWER, +}; + +struct mmi_chrg_step_power { + u32 chrg_step_volt; + u32 chrg_step_curr; +}; + +struct mmi_chrg_temp_zone { + int temp_c; /*temperature*/ + struct mmi_chrg_step_power *chrg_step_power; +}; + +struct mmi_chrg_step_info { + enum mmi_chrg_steps pres_chrg_step; + int temp_c; + int chrg_step_cc_curr; + int chrg_step_cv_volt; + int chrg_step_cv_tapper_curr; + bool last_step; +}; + +struct mmi_chrg_dev_ops { + const char *dev_name; + struct mmi_charger_ops *ops; +}; + +struct mmi_chrg_dts_info { + const char *chrg_name; + const char *psy_name; + int charging_curr_limited; + int charging_curr_min; +}; + +#define PPS_RET_HISTORY_SIZE 8 +#define PD_SRC_PDO_TYPE_FIXED 0 +#define PD_SRC_PDO_TYPE_BATTERY 1 +#define PD_SRC_PDO_TYPE_VARIABLE 2 +#define PD_SRC_PDO_TYPE_AUGMENTED 3 +#define STEP_FIREST_CURR_COMP 300000 +#define TYPEC_HIGH_CURRENT_UA 3000000 +#define TYPEC_MIDDLE_CURRENT_UA 2000000 +#define SWITCH_CHARGER_PPS_VOLT 5000000 +#define PUMP_CHARGER_PPS_MIN_VOLT 8000000 +#define COOLING_HYSTERISIS_DEGC 2 +struct mmi_charger_manager { + const char *name; + struct device *dev; + struct power_supply *batt_psy; + struct power_supply *qcom_psy; + struct power_supply *extrn_psy; + struct power_supply *usb_psy; + struct power_supply *mmi_chrg_mgr_psy; + struct usbpd *pd_handle; + struct usbpd_pdo_info mmi_pdo_info[PD_MAX_PDO_NUM]; + struct notifier_block psy_nb; + + struct wakeup_source mmi_hb_wake_source; + struct alarm heartbeat_alarm; + bool factory_mode; + bool suspended; + bool awake; + bool cp_disable; + + int *debug_mask; + int mmi_pd_pdo_idx; /*request the pdo idx of PD*/ + int pps_volt_steps; /*PPS voltage, programming step size*/ + int pps_curr_steps; /*pps current, programming step size*/ + + int pd_volt_max; /*the Maximum request PD Voltage*/ + int pd_curr_max; /*the Maximum request PD current*/ + int batt_ovp_lmt; /*the battery over current limitation*/ + int pl_chrg_vbatt_min; /*the minimum battery voltage to enable parallel charging*/ + + int typec_middle_current; + int step_first_curr_comp; + int pps_volt_comp; + int pd_request_volt; + int pd_request_curr; + /*the request PD power*/ + int pd_request_volt_prev; + int pd_request_curr_prev; + /*the previous request PD power*/ + int pd_sys_therm_volt; + int pd_sys_therm_curr; + /*the thermal PD power*/ + + int pd_batt_therm_volt; + int pd_batt_therm_curr; + + int pd_target_volt; + int pd_target_curr; + /*the final commited request PD power*/ + + int pps_result; + int pps_result_history[PPS_RET_HISTORY_SIZE]; + int pps_result_history_idx; + /*save the result of the return from PD request*/ + + int charger_rate; + bool vbus_present; + bool pd_pps_support; + bool pd_pps_balance; + bool freeze_pd_power; + bool extrn_fg; + const char *extrn_fg_name; + bool extrn_sense; + bool recovery_pmic_chrg; + bool dont_rerun_aicl; + + bool sys_therm_cooling; + bool sys_therm_force_pmic_chrg; + bool batt_therm_cooling; + int batt_therm_cooling_cnt; + + bool use_batt_age; + int cycles; + int batt_cap_delta; + int batt_soc_delta; + int soc_cycles_start; + int pres_batt_status; + int prev_batt_status; + + struct delayed_work mmi_chrg_sm_work; /*mmi charger state machine work*/ + struct delayed_work heartbeat_work; /*cycle trig heartbeat work*/ + struct completion sm_completion; + struct work_struct psy_changed_work; /*the change notification of power supply*/ + bool sm_work_running; + int num_temp_zones; + struct mmi_chrg_temp_zone *temp_zones; /*the temperature zone of charging*/ + enum mmi_chrg_temp_zones pres_temp_zone; /*the present zone idx*/ + + int *thermal_mitigation; /*thermal mitigation array*/ + int thermal_levels; + int system_thermal_level; /*thermal level setting*/ + + int chrg_step_nums; + struct mmi_chrg_step_info chrg_step; /*step charger info*/ + + int mmi_chrg_dev_num; + struct mmi_charger_device **chrg_list; /*charger device list*/ +}; + +extern bool mmi_get_pps_result_history(struct mmi_charger_manager *chip); +extern void mmi_set_pps_result_history(struct mmi_charger_manager *chip, int pps_result); +extern void mmi_clear_pps_result_history(struct mmi_charger_manager *chip); +extern int mmi_calculate_delta_volt(int pps_voltage, int pps_current, int delta_curr); +extern bool mmi_find_chrg_step(struct mmi_charger_manager *chip, int temp_zone, int vbatt_volt); +extern bool mmi_find_temp_zone(struct mmi_charger_manager *chip, int temp_c, bool ignore_hysteresis_degc); +extern void clear_chg_manager(struct mmi_charger_manager *chip); +extern void mmi_update_all_charger_status(struct mmi_charger_manager *chip); +extern void mmi_update_all_charger_error(struct mmi_charger_manager *chip); +extern void mmi_dump_charger_error(struct mmi_charger_manager *chip, + struct mmi_charger_device *chrg_dev); +extern ssize_t mmi_get_factory_image_mode(void); +extern ssize_t mmi_set_factory_image_mode(int mode); +extern ssize_t mmi_get_factory_charge_upper(void); +extern ssize_t mmi_get_demo_mode(void); +extern ssize_t mmi_set_demo_mode(int mode); +extern ssize_t mmi_get_max_chrg_temp(void); +extern ssize_t mmi_set_max_chrg_temp(int value); +#endif diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_policy.h b/drivers/power/mmi_parallel_charger_iio/mmi_charger_policy.h new file mode 100644 index 000000000000..e3dfc285caa8 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_policy.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #ifndef __MMI_CHRG_POLICY_H_ + #define __MMI_CHRG_POLICY_H_ +extern int mmi_chrg_policy_init(struct mmi_charger_manager *chip, + struct mmi_chrg_dts_info *chrg_dts, + int chrg_cnt); +extern void mmi_chrg_policy_clear(struct mmi_charger_manager *chip); +extern void mmi_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val); +#endif diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_pump_policy.c b/drivers/power/mmi_parallel_charger_iio/mmi_charger_pump_policy.c new file mode 100644 index 000000000000..063521b5dd68 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_pump_policy.c @@ -0,0 +1,1663 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_core.h" + +extern struct mmi_charger_ops bq2597x_charger_ops; +extern struct mmi_charger_ops qpnp_pmic_charger_ops; + +static const struct mmi_chrg_dev_ops dev_ops[] = { + { + .dev_name = "pmic-sw", + .ops = &qpnp_pmic_charger_ops, + }, + { + .dev_name = "cp-master", + .ops = &bq2597x_charger_ops, + }, + { + .dev_name = "cp-slave", + .ops = &bq2597x_charger_ops, + }, +}; + +enum mmi_chrg_dev { + PMIC_SW = 0, + CP_MASTER, + CP_SLAVE, + CHRG_NUM, +}; + +struct mmi_cp_policy_dev { + bool pmic_sw; + bool cp_master; + bool cp_slave; + bool cp_clave_later; + struct mmi_charger_device *chrg_dev[CHRG_NUM]; +}; + +typedef enum { + PM_STATE_DISCONNECT, + PM_STATE_ENTRY, + PM_STATE_SW_ENTRY, + PM_STATE_SW_LOOP, + PM_STATE_CHRG_PUMP_ENTRY, + PM_STATE_SINGLE_CP_ENTRY, + PM_STATE_DULE_CP_ENTRY, + PM_STATE_PPS_TUNNING_CURR, + PM_STATE_PPS_TUNNING_VOLT, + PM_STATE_CP_CC_LOOP, + PM_STATE_CP_CV_LOOP, + PM_STATE_CP_QUIT, + PM_STATE_RECOVERY_SW, + PM_STATE_STOP_CHARGE, + PM_STATE_COOLING_LOOP, +} pm_sm_state_t; + +const unsigned char *pm_state_str[] = { + "PM_STATE_DISCONNECT", + "PM_STATE_ENTRY", + "PM_STATE_SW_ENTRY", + "PM_STATE_SW_LOOP", + "PM_STATE_CHRG_PUMP_ENTRY", + "PM_STATE_SINGLE_CP_ENTRY", + "PM_STATE_DULE_CP_ENTRY", + "PM_STATE_PPS_TUNNING_CURR", + "PM_STATE_PPS_TUNNING_VOLT", + "PM_STATE_CP_CC_LOOP", + "PM_STATE_CP_CV_LOOP", + "PM_STATE_CP_QUIT", + "PM_STATE_RECOVERY_SW", + "PM_STATE_STOP_CHARGE", + "PM_STATE_COOLING_LOOP", +}; + +static pm_sm_state_t sm_state = PM_STATE_DISCONNECT; +static int chrg_cc_power_tunning_cnt = 0; +static int chrg_cv_taper_tunning_cnt = 0; +static int chrg_cv_delta_volt = 0; +static int quit_slave_chrg_cnt = 0; +static int batt_curr_roof = 0; +static int pd_constant_power_cnt = 0; +static struct mmi_cp_policy_dev g_chrg_list = {0}; + +static void mmi_chrg_sm_move_state(struct mmi_charger_manager *chip, pm_sm_state_t state) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "pm_state change:%s -> %s\n", + pm_state_str[sm_state], pm_state_str[state]); + sm_state = state; + pd_constant_power_cnt = 0; + batt_curr_roof = 0; +} + +static void chrg_dev_init(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num, i; + chrg_num = chip->mmi_chrg_dev_num; + mmi_chrg_err(chip,"runing in chrg dev init!\n"); + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + if (is_charger_exist(dev_ops[PMIC_SW].dev_name)) { + chrg_list->pmic_sw = true; + chrg_list->chrg_dev[PMIC_SW] = chip->chrg_list[PMIC_SW]; + } + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_list->cp_master = true; + chrg_list->chrg_dev[CP_MASTER] = chip->chrg_list[CP_MASTER]; + } + break; + case CP_SLAVE: + if (is_charger_exist(dev_ops[CP_SLAVE].dev_name)) { + chrg_list->cp_slave = true; + chrg_list->cp_clave_later = false; + chrg_list->chrg_dev[CP_SLAVE] = chip->chrg_list[CP_SLAVE]; + } + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +static void clear_chrg_dev_error_cnt(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + if (is_charger_exist(dev_ops[i].dev_name)) { + chrg_dev = chrg_list->chrg_dev[i]; + chrg_dev->charger_error.chrg_err_type = 0; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + } + return; +} + +#define PPS_INIT_VOLT_COMP 500000 +#define PPS_VOLT_COMP_DELTA 300000 +static void chrg_policy_error_recovery(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0, chrg_error_type= 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + chrg_error_type = chrg_dev->charger_error.chrg_err_type; + if (chrg_error_type & (1 << MMI_BUS_UCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_UCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master bus ucp error %d, cnt %d," + "pps volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chip->pps_volt_comp); + + if (chrg_dev->charger_error.bus_ucp_err_cnt > 3) { + if (chrg_list->cp_slave) { + chrg_list->cp_slave = false; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + } else { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + } else if (chrg_dev->charger_error.bus_ucp_err_cnt > 6) { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + + chrg_dev->charger_error.bus_ucp_err_cnt++; + chip->pps_volt_comp += PPS_VOLT_COMP_DELTA; + mmi_chrg_info(chip,"Restart charging, " + "increase pps volt comp %dmV\n", + chip->pps_volt_comp); + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + }else if (chrg_error_type & (1 << MMI_BUS_OCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_OCP_FAULT_BIT) || + chrg_error_type & (1 << MMI_CONV_OCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master ocp error %d, cnt %d," + "pps volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ocp_err_cnt, + chip->pps_volt_comp); + if (chrg_dev->charger_error.bus_ocp_err_cnt > 3) { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + chrg_dev->charger_error.bus_ocp_err_cnt++; + chip->pps_volt_comp -= PPS_VOLT_COMP_DELTA; + if (chip->pps_volt_comp < 0) + chip->pps_volt_comp = 0; + mmi_chrg_info(chip,"Restart charging, " + "decrease pps volt comp %dmV\n", + chip->pps_volt_comp); + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + } + } + break; + case CP_SLAVE: + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +static void chrg_policy_error_clear(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + switch (i) { + case PMIC_SW: + break; + + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + mmi_clear_charger_error(chrg_dev); + } + break; + + case CP_SLAVE: + break; + + default: + mmi_chrg_err(chip,"mmi_chrg_dev not found %d !\n",i); + break; + } + } + + return; +} + +void mmi_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + bool enable = !!val; + + mmi_chrg_dbg(chip, PR_MOTO,"enable all cp = %d\n", enable); + if (enable) { + if (chip->cp_disable == false) + return; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + chip->cp_disable = false; + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(0)); + + } else { + if(chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) + return; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + + chip->cp_disable = true; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + mmi_chrg_sm_move_state(chip, PM_STATE_RECOVERY_SW); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(0)); + } +} + +#define HEARTBEAT_SHORT_DELAY_MS 1000 +#define HEARTBEAT_lOOP_WAIT_MS 3000 +#define HEARTBEAT_PPS_TUNNING_MS 100 +#define HEARTBEAT_NEXT_STATE_MS 100 +#define HEARTBEAT_CANCEL -1 +#define CC_CURR_DEBOUNCE 100000 +#define CV_TAPPER_COUNT 3 +#define CC_POWER_COUNT 3 +#define CV_DELTA_VOLT 100000 +#define THERMAL_TUNNING_CURR 100000 +#define COOLING_DELTA_POWER 100000 +#define COOLING_MAX_CNT 5 +#define PPS_SELECT_PDO_RETRY_COUNT 3 +#define DISABLE_CHRG_LIMIT -1 +#define CP_CHRG_SOC_LIMIT 90 +#define PD_CONT_PWR_CNT 5 + +void mmi_chrg_policy_clear(struct mmi_charger_manager *chip) { + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + chrg_dev_init(chip, &g_chrg_list); + clear_chrg_dev_error_cnt(chip, &g_chrg_list);; + clear_chg_manager(chip); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + sm_state = PM_STATE_DISCONNECT; + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + quit_slave_chrg_cnt = 0; + chrg_cc_power_tunning_cnt = 0; + chrg_cv_taper_tunning_cnt = 0; + chrg_cv_delta_volt = 0; + pd_constant_power_cnt = 0; + batt_curr_roof = 0; + return; +} + +static void mmi_chrg_sm_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, mmi_chrg_sm_work.work); + int i = 0, rc = 0; + int ibatt_curr = 0, vbatt_volt = 0, batt_temp = 0, vbus_pres = 0; + int batt_soc = 0; + int heartbeat_dely_ms = 0; + int cooling_curr = 0; + int cooling_volt = 0; + int pmic_sys_therm_level = 0; + bool zone_change = false; + bool ignore_hysteresis_degc = false; + struct mmi_chrg_step_info *chrg_step; + union power_supply_propval prop = {0,}; + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + + mmi_chrg_dbg(chip, PR_MOTO, "\n\n\n"); + + mmi_chrg_dbg(chip, PR_MOTO, "schedule SM work, sm state %s \n", + pm_state_str[sm_state]); + + mmi_chrg_dbg(chip, PR_MOTO, "pmic-sw is exist: %d " + "cp-master is exist: %d " + "cp-slave is exist: %d\n", + chrg_list->pmic_sw, + chrg_list->cp_master, + chrg_list->cp_slave); + if (!chrg_list->pmic_sw) { + mmi_chrg_err(chip,"PMIC-SW isn't exist, force quite mmi chrg sm work !\n"); + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, &prop); + if (!rc) + pmic_sys_therm_level = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + vbatt_volt = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + ibatt_curr = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + batt_temp = prop.intval / 10; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &prop); + if (!rc) + batt_soc = prop.intval; + + + if (ibatt_curr < 0) + ibatt_curr *= -1; + + + mmi_update_all_charger_status(chip); + mmi_update_all_charger_error(chip); + + if (chip->extrn_sense) { + ibatt_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibatt_curr; + ibatt_curr *= 1000; + if (ibatt_curr < 0) + ibatt_curr *= -1; + vbatt_volt = chrg_list->chrg_dev[CP_MASTER]->charger_data.vbatt_volt; + vbatt_volt *= 1000; + } + + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + if (!vbus_pres) { + for (i = 0; i < 3; i++) { + mmi_update_all_charger_status(chip); + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + mmi_chrg_info(chip, "Retry check charger status, vbus %d\n", vbus_pres); + if (vbus_pres) { + mmi_chrg_info(chip, "Get vbus present , continue to charging\n"); + break; + } + msleep(100); + } + } + + mmi_chrg_info(chip, "battery current %d\n", ibatt_curr); + mmi_chrg_info(chip, "battery voltage %d\n", vbatt_volt); + mmi_chrg_info(chip, "battery temp %d\n", batt_temp); + mmi_chrg_info(chip, "battery capacity %d\n", batt_soc); + + if (vbus_pres && + (sm_state == PM_STATE_PPS_TUNNING_CURR + || sm_state == PM_STATE_PPS_TUNNING_VOLT + || sm_state == PM_STATE_CP_CC_LOOP + || sm_state == PM_STATE_CP_CV_LOOP)) { + mmi_dump_charger_error(chip, chrg_list->chrg_dev[CP_MASTER]); + chrg_policy_error_recovery(chip, chrg_list); + } + + if (sm_state == PM_STATE_CP_CV_LOOP) + ignore_hysteresis_degc = true; + zone_change = mmi_find_temp_zone(chip, batt_temp, ignore_hysteresis_degc); + chrg_step = &chip->chrg_step; + + if (chip->pres_temp_zone == ZONE_COLD + || chip->pres_temp_zone == ZONE_HOT + || !chrg_list->chrg_dev[PMIC_SW]->charger_enabled + || vbatt_volt > chip->batt_ovp_lmt) { + + mmi_chrg_info(chip, "Force stop charging, " + "pres_temp_zone %d, " + "pmic charger enabled %d, " + "vbatt_volt %dmv, " + "batt ovp limit %dmv\n", + chip->pres_temp_zone, + chrg_list->chrg_dev[PMIC_SW]->charger_enabled, + vbatt_volt, chip->batt_ovp_lmt); + mmi_chrg_sm_move_state(chip, PM_STATE_STOP_CHARGE); + } + + if (!vbus_pres) { + mmi_chrg_sm_move_state(chip, PM_STATE_DISCONNECT); + } else if (sm_state == PM_STATE_ENTRY + || sm_state == PM_STATE_STOP_CHARGE) { + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + } else if (sm_state == PM_STATE_DISCONNECT) { + mmi_get_input_current_settled(chrg_list->chrg_dev[PMIC_SW], + &chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } else if (zone_change && + chip->pres_temp_zone != ZONE_COLD && + chip->pres_temp_zone != ZONE_HOT) { + + if (batt_temp >= chrg_step->temp_c) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Enter into COOLING LOOP !\n", + batt_temp, chrg_step->temp_c); + chip->batt_therm_cooling = true; + chip->pd_batt_therm_volt = chip->pd_request_volt_prev; + chip->pd_batt_therm_curr = chip->pd_request_curr_prev; + } else if (!chip->batt_therm_cooling && + !ignore_hysteresis_degc) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Restart select chrg step and temp zone !\n", + batt_temp, chrg_step->temp_c); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + } + + mmi_chrg_dbg(chip, PR_MOTO, "temp zone %d, is_changed %d, " + "chrg_step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chip->pres_temp_zone, zone_change, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + switch (sm_state) { + case PM_STATE_DISCONNECT: + mmi_chrg_info(chip,"vbus disconnect !, jump to PM_STATE_DISCONNECT," + "recovery PMIC-SW limitation, and close CP charg\n"); + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + heartbeat_dely_ms = HEARTBEAT_CANCEL; + break; + case PM_STATE_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chip->pd_pps_support + && chrg_list->cp_master + && vbatt_volt > chip->pl_chrg_vbatt_min + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && batt_soc < CP_CHRG_SOC_LIMIT) { + + mmi_chrg_dbg(chip, PR_MOTO, "Enter into CHRG PUMP, " + "vbatt %d uV, " + "pps support %d, " + "chrg step %d, " + "chrg step cc curr %d uA, " + "CP master charging curr min %d uA\n", + vbatt_volt, + chip->pd_pps_support, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Enter into PMIC switch charging, " + "the reason is : vbatt %d uV, " + "pl chrg vbatt min %d uV, " + "pps support %d, " + "chrg step %d\n", + vbatt_volt, chip->pl_chrg_vbatt_min, + chip->pd_pps_support, + chrg_step->pres_chrg_step); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + + } + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt= 0; + chip->pd_sys_therm_volt= chip->pd_request_volt_prev; + chip->pd_sys_therm_curr = chip->pd_request_curr_prev; + chip->pd_batt_therm_volt = chip->pd_request_volt_prev; + chip->pd_batt_therm_curr = chip->pd_request_curr_prev; + chip->sys_therm_cooling = false; + chip->sys_therm_force_pmic_chrg = false; + chip->recovery_pmic_chrg = false; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip, "Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip, "Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + mmi_chrg_info(chip, "Check all effective pdo info again\n"); + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + mmi_chrg_info(chip, "Select FIXED pdo for switch charging !\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + mmi_chrg_info(chip,"find pdo %d, max volt %d, max curr %d\n", + chip->mmi_pdo_info[i].type, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + if (chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_FIXED + && chip->mmi_pdo_info[i].uv_max >= SWITCH_CHARGER_PPS_VOLT + && chip->mmi_pdo_info[i].ua >= TYPEC_HIGH_CURRENT_UA) { + mmi_chrg_dbg(chip, PR_MOTO, "select 5V/3A pps, pdo %d\n", i); + chip->mmi_pd_pdo_idx = + chip->mmi_pdo_info[i].pdo_pos; + break; + } + } + + chip->pd_request_volt = SWITCH_CHARGER_PPS_VOLT; + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + mmi_chrg_info(chip,"Select pdo %d, pd request curr %d, volt %d\n", + chip->mmi_pd_pdo_idx, + chip->pd_request_curr, + chip->pd_request_volt); + + mmi_chrg_sm_move_state(chip, PM_STATE_SW_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_LOOP: + if (chip->pd_pps_support + && chip->cp_disable == false + && vbatt_volt > chip->pl_chrg_vbatt_min + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && !chip->recovery_pmic_chrg + && !chip->sys_therm_force_pmic_chrg + && batt_soc < CP_CHRG_SOC_LIMIT) { + mmi_chrg_info(chip, "Enter CP, the reason is : " + "pd pps support %d, " + "vbatt %duV, chrg step %d\n", + chip->pd_pps_support, + vbatt_volt, chrg_step->pres_chrg_step); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Continue to SW charging, " + "vbatt %d uV, ibatt %d uA\n", + vbatt_volt, ibatt_curr); + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + } + break; + case PM_STATE_CHRG_PUMP_ENTRY: + mmi_chrg_info(chip,"CP master exist %d, CP slave exist %d !\n", + chrg_list->cp_master, + chrg_list->cp_slave); + if (chrg_list->cp_slave) { + mmi_chrg_info(chip,"CP slave is exist !\n"); + mmi_chrg_info(chip,"chrg step cc curr %d uA, " + "CP slave charging curr min %d uA\n", + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + if (chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) { + mmi_chrg_sm_move_state(chip, + PM_STATE_DULE_CP_ENTRY); + } else + mmi_chrg_sm_move_state(chip, + PM_STATE_SINGLE_CP_ENTRY); + } else { + mmi_chrg_info(chip,"CP slave isn't exist !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SINGLE_CP_ENTRY); + } + + mmi_chrg_info(chip,"Set PMIC SW FCC limits!\n"); + if (!chrg_list->chrg_dev[PMIC_SW]->charger_limited + && chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited > 0) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + mmi_chrg_info(chip,"Set PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = true; + } + + mmi_chrg_info(chip, "Check all effective pdo info again\n"); + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED) + && chip->mmi_pdo_info[i].uv_max >= PUMP_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua >= chip->typec_middle_current) { + chip->mmi_pd_pdo_idx = chip->mmi_pdo_info[i].pdo_pos; + mmi_chrg_info(chip, + "Pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pd_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max < + chip->pd_volt_max) { + chip->pd_volt_max = + chip->mmi_pdo_info[i].uv_max; + } + + if (chip->mmi_pdo_info[i].ua < + chip->pd_curr_max) { + chip->pd_curr_max = + chip->mmi_pdo_info[i].ua; + } + + break; + } + } + + /*Initial setup pps request power by the battery voltage*/ + chip->pd_request_volt = (2 * vbatt_volt) % 20000; + chip->pd_request_volt = 2 * vbatt_volt - chip->pd_request_volt + + chip->pps_volt_comp; + chip->pd_request_curr = + min(chip->pd_curr_max, chip->typec_middle_current); + mmi_chrg_info(chip,"pps init , volt %dmV, curr %dmA, volt comp %dmv\n", + chip->pd_request_volt, chip->pd_request_curr, chip->pps_volt_comp); + chrg_policy_error_clear(chip, chrg_list); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SINGLE_CP_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_chrg_info(chip,"Disable Slave Charger Pump !\n"); + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_CURR); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_DULE_CP_ENTRY: + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + } + + if (chrg_list->cp_slave) { + chrg_list->cp_clave_later = true; + mmi_chrg_info(chip,"For ibus UCP, " + "delay start Slave Charger Pump in TUNNING_VOLT stage !\n"); + } + + if (chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_CURR); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_PPS_TUNNING_CURR: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into " + "SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + }else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chip->pd_request_curr - chip->pps_curr_steps + > chip->typec_middle_current) + chip->pd_request_curr -= chip->pps_curr_steps; + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"During the curr going up process, " + "the chrg step was changed," + "stop increase pps curr and Enter into " + "CC stage as soon!\n"); + } else if (chip->pps_result < 0) { + if (mmi_get_pps_result_history(chip) != NO_ERROR) { + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Too many pdo request failed," + " Enter into CC stage directly!\n"); + mmi_clear_pps_result_history(chip); + } + chip->pd_request_curr = chip->pd_request_curr_prev; + goto schedule; + } else if (chip->pd_request_curr + chip->pps_curr_steps + <= chip->pd_curr_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < chrg_step->chrg_step_cc_curr) { + chip->pd_request_curr += chip->pps_curr_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase pps curr %d\n", + chip->pd_request_curr); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into tunning pps volt\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_VOLT); + } + break; + case PM_STATE_PPS_TUNNING_VOLT: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_slave + && chrg_list->cp_clave_later + && !chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], true); + mmi_chrg_info(chip,"Enable Slave Charger Pump !\n"); + } + + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, " + "Enter into SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + } else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + chip->pd_request_volt -= chip->pps_volt_steps; + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Duing the volt going up process, " + "the chrg step was changed," + "stop increase pps volt and" + " Enter into CC stage as soon!\n"); + } else if (chip->pps_result < 0) { + if (mmi_get_pps_result_history(chip) != NO_ERROR) { + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_clear_pps_result_history(chip); + mmi_chrg_info(chip,"Too many pdo request failed," + " Enter into CC stage directly!\n"); + } + chip->pd_request_volt = chip->pd_request_volt_prev; + goto schedule; + } else if (chip->pd_request_volt + chip->pps_volt_steps + <= chip->pd_volt_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < ((chrg_step->pres_chrg_step == STEP_FIRST) ? + chrg_step->chrg_step_cc_curr + chip->step_first_curr_comp: + chrg_step->chrg_step_cc_curr)) { + chip->pd_request_volt += chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase pps volt %d\n", + chip->pd_request_volt); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into CC loop stage !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CC_LOOP); + } + + if (ibatt_curr > batt_curr_roof) { + batt_curr_roof = ibatt_curr; + } + if (ibatt_curr < batt_curr_roof) + pd_constant_power_cnt++; + else + pd_constant_power_cnt = 0; + + if (pd_constant_power_cnt > PD_CONT_PWR_CNT) { + mmi_chrg_info(chip,"PD adapter was ready in constant power state, " + "Enter into CC loop stage !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CC_LOOP); + } + mmi_chrg_info(chip, "pd_constant_power_cnt %d, " + "batt_curr_roof %d\n", + pd_constant_power_cnt, + batt_curr_roof); + break; + case PM_STATE_CP_CC_LOOP: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CC target curr %dmA, " + "next CV target volt %dmV\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt); + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (chip->pps_result < 0) { + mmi_chrg_err(chip, "Last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->pd_pps_balance = true; + mmi_chrg_err(chip, "Enable pps power balance\n"); + mmi_clear_pps_result_history(chip); + break; + case RESET_POWER: + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + mmi_chrg_err(chip, "Hard reset charger policy to recovery power," + "Since too many pdo failed\n"); + mmi_clear_pps_result_history(chip); + break; + default: + break; + } + + chip->pd_request_volt = chip->pd_request_volt_prev; + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max) + chrg_cc_power_tunning_cnt = + CC_POWER_COUNT; + goto schedule; + } + + if (!chip->sys_therm_cooling + && !chip->batt_therm_cooling + && pd_constant_power_cnt <= PD_CONT_PWR_CNT) { + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max + && chrg_cc_power_tunning_cnt >= + CC_POWER_COUNT) { + if (chip->pd_pps_balance + && (chip->pd_request_curr - chip->pps_curr_steps) + > chip->typec_middle_current) { + chip->pd_request_curr -= + chip->pps_curr_steps; + chip->pd_request_volt += + mmi_calculate_delta_volt(chip->pd_request_volt_prev, + chip->pd_request_curr_prev, + chip->pps_curr_steps); + mmi_chrg_dbg(chip, PR_MOTO, + "Request curr decreass %dmA, " + "Request volt increase %dmV \n", + chip->pd_request_curr, + chip->pd_request_volt); + } else { + if (chip->pd_request_curr + chip->pps_curr_steps + < chip->pd_curr_max) { + chip->pd_request_curr += + chip->pps_curr_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request curr decreass %dmA\n ", + chip->pd_request_curr); + } else if (chip->pd_request_volt + chip->pps_volt_steps + < chip->pd_volt_max) { + chip->pd_request_volt += + chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request volt decreass %duV\n ", + chip->pd_request_volt); + } + } + chrg_cc_power_tunning_cnt = 0; + mmi_chrg_dbg(chip, PR_MOTO, + "For keeping CC chrg POWER, " + "Have to adjust pd input volt or curr" + "Request volt %dmV, " + "Request curr %dmA\n", + chip->pd_request_volt, + chip->pd_request_curr); + }else if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max) { + chrg_cc_power_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC tunning cnt %d\n", + chrg_cc_power_tunning_cnt); + } else if (ibatt_curr > chrg_step->chrg_step_cc_curr + + CC_CURR_DEBOUNCE) { + chip->pd_request_volt -= chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "In the CC step , the ibatt is greater than CC curr, " + "Request volt decreass %duV to remain CC step\n ", + chip->pd_request_volt); + } else + chrg_cc_power_tunning_cnt = 0; + + if(ibatt_curr < chrg_step->chrg_step_cc_curr) + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chrg_cv_taper_tunning_cnt > + CV_TAPPER_COUNT) { + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CV_LOOP); + chrg_cv_taper_tunning_cnt = 0; + chrg_cv_delta_volt = CV_DELTA_VOLT; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + chrg_cv_taper_tunning_cnt = 0; + + break; + case PM_STATE_CP_CV_LOOP: + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CV target volt %dmV, " + "CV taper curr %dmA\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (chip->pps_result < 0) { + mmi_chrg_err(chip, "Last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->pd_request_curr -= + chip->pps_curr_steps; + mmi_chrg_err(chip, "Reduce pps curr,for pps power balance\n"); + mmi_clear_pps_result_history(chip); + break; + case RESET_POWER: + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + mmi_chrg_err(chip, "Hard reset charger policy to recovery power," + "Since too many pdo failed\n"); + mmi_clear_pps_result_history(chip); + break; + default: + break; + } + chip->pd_request_volt = chip->pd_request_volt_prev; + goto schedule; + } + + if (vbatt_volt >= chrg_step->chrg_step_cv_volt + && ((!chrg_step->last_step && + ibatt_curr < chrg_step->chrg_step_cv_tapper_curr) + || ibatt_curr < chrg_list->chrg_dev[CP_MASTER]->charging_curr_min)) { + if (chrg_cv_taper_tunning_cnt >= CV_TAPPER_COUNT) { + if (ibatt_curr < + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min) { + mmi_chrg_info(chip, "Ready quite CP chrg stage, " + "and Enter into PMIC switch chrg stage, " + "chrg step %d, ibatt %dmA\n", + chrg_step->pres_chrg_step, ibatt_curr); + mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_QUIT); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + if (mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt)) { + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + mmi_chrg_info(chip,"Jump to next chrg step\n"); + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + } else { + mmi_chrg_info(chip,"Can't find next chrg step\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_QUIT); + } + } + chrg_cv_taper_tunning_cnt = 0; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, "chrg cv taper cnt ++, %d\n", + chrg_cv_taper_tunning_cnt); + } + + }else if (!chip->sys_therm_cooling + && !chip->batt_therm_cooling) { + + chrg_cv_taper_tunning_cnt = 0; + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (chrg_cv_delta_volt > 20000) + chip->pd_request_volt -= chrg_cv_delta_volt; + else + chip->pd_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else if (vbatt_volt < chrg_step->chrg_step_cv_volt - 10000) { + chrg_cv_delta_volt -= 20000; + chip->pd_request_volt += 20000; + mmi_chrg_info(chip, + "For keeping CV stage, increase volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep pd power, volt %dmV, curr %dmA\n", + chip->pd_request_volt, chip->pd_request_curr); + } + + }else { + /*In this case, sys_therm_cooling or batt_therm_cooling is ture*/ + + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (chrg_cv_delta_volt > 20000) + chip->pd_request_volt -= chrg_cv_delta_volt; + else + chip->pd_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep pd power, volt %dmV, curr %dmA\n", + chip->pd_request_volt, chip->pd_request_curr); + } + } + + if (chrg_list->cp_slave) { + if (quit_slave_chrg_cnt > 3 + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + mmi_chrg_info(chip,"Quit slave chrg, the reason is :ibatt %duA, " + "CP slave charging curr min %d uA\n", + ibatt_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Restart CP master again\n"); + } + + if (ibatt_curr < + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) + quit_slave_chrg_cnt++; + else + quit_slave_chrg_cnt = 0; + } + break; + case PM_STATE_CP_QUIT: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + mmi_chrg_sm_move_state(chip, PM_STATE_RECOVERY_SW); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_RECOVERY_SW: + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip,"Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + chip->recovery_pmic_chrg = true; + chrg_cv_taper_tunning_cnt = 0; + } + + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + mmi_chrg_info(chip,"ibatt : %dmA, step cc curr : %dmA\n", + ibatt_curr, chrg_step->chrg_step_cc_curr); + if (ibatt_curr > chrg_step->chrg_step_cc_curr) { + chip->pd_request_volt -= CV_DELTA_VOLT; + + mmi_chrg_dbg(chip, PR_MOTO, "Reduce pps volt %dmV, curr %dmA\n ", + chip->pd_request_volt, chip->pd_request_curr); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + chrg_cv_taper_tunning_cnt = 0; + } else { + chrg_cv_taper_tunning_cnt++; + + } + + if (chrg_cv_taper_tunning_cnt > CV_TAPPER_COUNT){ + mmi_chrg_sm_move_state(chip, PM_STATE_SW_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_STOP_CHARGE: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (chip->pres_temp_zone != ZONE_COLD + && chip->pres_temp_zone != ZONE_HOT + && chrg_list->chrg_dev[PMIC_SW]->charger_enabled + && chrg_step->chrg_step_cc_curr > 0) { + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + chip->pd_request_volt = SWITCH_CHARGER_PPS_VOLT; + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + break; + case PM_STATE_COOLING_LOOP: + mmi_chrg_info(chip,"In cooling loop, batt temp %d, cmp temp %d\n", + batt_temp, chrg_step->temp_c); + + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_curr = + min(chip->pd_curr_max, chip->typec_middle_current); + if (chip->pd_request_curr > cooling_curr) { + chip->pd_request_curr -= COOLING_DELTA_POWER; + } else { + mmi_chrg_info(chip, "pd request curr %dmA, " + "battery temp %d, " + "cooling failed, Restart PM_STATE_ENTRY !\n", + chip->pd_request_curr, batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + } + + } else if (batt_temp < chrg_step->temp_c - COOLING_DELTA_POWER) { + + mmi_chrg_info(chip,"Batt temp %d, successful exit from COOLing loop, " + "and Enter int TUNNING_CURR again !\n", + batt_temp); + mmi_chrg_sm_move_state(chip, + PM_STATE_PPS_TUNNING_CURR); + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chrg_cv_taper_tunning_cnt > + CV_TAPPER_COUNT) { + + mmi_chrg_info(chip, "vbatt_volt %dmV, " + "battery temp %d, " + "chrg step cv volt %dmV" + "cooling failed, Restart PM_STATE_ENTRY !\n", + vbatt_volt, batt_temp, chrg_step->chrg_step_cv_volt); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + chrg_cv_taper_tunning_cnt = 0; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + chrg_cv_taper_tunning_cnt = 0; + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + break; + } + +schedule: + + chip->pd_target_volt = min(chip->pd_request_volt, chip->pd_volt_max); + chip->pd_target_curr = min(chip->pd_request_curr, chip->pd_curr_max); + + if (chip->system_thermal_level == 0) { + chip->sys_therm_cooling= false; + chip->sys_therm_force_pmic_chrg = false; + } else if ((chip->system_thermal_level == chip->thermal_levels - 1) + && !chip->sys_therm_force_pmic_chrg) { + chip->sys_therm_cooling = true; + chip->sys_therm_force_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + mmi_chrg_info(chip, "Thermal is the highest, level %d, " + "Force enter into single pmic charging !\n", + chip->system_thermal_level); + + } else if (chip->system_thermal_level > 0 && + (sm_state == PM_STATE_CP_CC_LOOP || + sm_state == PM_STATE_CP_CV_LOOP)) { + + mmi_chrg_dbg(chip, PR_MOTO, "Thermal level is %d\n", + chip->system_thermal_level); + if (!chip->sys_therm_cooling) { + chip->sys_therm_cooling = true; + chip->pd_sys_therm_volt = chip->pd_request_volt_prev; + chip->pd_sys_therm_curr = chip->pd_request_curr_prev; + } + + if (ibatt_curr > + chip->thermal_mitigation[chip->system_thermal_level] + + CC_CURR_DEBOUNCE) { + if (chip->pd_sys_therm_curr - THERMAL_TUNNING_CURR >= + chip->typec_middle_current) { + chip->pd_sys_therm_curr -= THERMAL_TUNNING_CURR; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, decrease pps curr %d\n", + chip->pd_sys_therm_curr); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, + "pd_sys_therm_curr %dmA was less than %dmA, " + "Give up thermal mitigation!", + chip->pd_sys_therm_curr - THERMAL_TUNNING_CURR, + chip->typec_middle_current); + } + } else if (ibatt_curr < + chip->thermal_mitigation[chip->system_thermal_level] + - CC_CURR_DEBOUNCE) { + if (chip->pd_sys_therm_curr + THERMAL_TUNNING_CURR <= + chip->pd_curr_max) { + chip->pd_sys_therm_curr += THERMAL_TUNNING_CURR; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, increase pps curr %d\n", + chip->pd_sys_therm_curr); + } + } + + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + } else if (chip->system_thermal_level > 0 && + chip->system_thermal_level != chip->thermal_levels - 1 && + sm_state == PM_STATE_SW_LOOP && + chip->sys_therm_force_pmic_chrg) { + mmi_chrg_dbg(chip, PR_MOTO, "Try to recovery charger pump!\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + + if (chip->sys_therm_cooling) { + chip->pd_target_volt = min(chip->pd_target_volt, chip->pd_sys_therm_volt); + chip->pd_target_curr = min(chip->pd_target_curr, chip->pd_sys_therm_curr); + } + + if (chip->batt_therm_cooling && !chip->sys_therm_force_pmic_chrg) { + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt= 0; + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_curr = + min(chip->pd_curr_max, chip->typec_middle_current); + cooling_volt = (2 * vbatt_volt) % 20000; + cooling_volt = 2 * vbatt_volt - cooling_volt + + chip->pps_volt_comp; + if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->pd_batt_therm_curr > cooling_curr) { + + if (chip->pd_batt_therm_curr - COOLING_DELTA_POWER >= + chip->typec_middle_current) + chip->pd_batt_therm_curr -= COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "pd_batt_therm_curr %dmA, " + "battery temp %d\n", + chip->pd_batt_therm_curr, batt_temp); + } else if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->pd_batt_therm_volt > cooling_volt) { + chip->pd_batt_therm_volt -= COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "pd request volt %dmA, " + "battery temp %d\n", + chip->pd_batt_therm_volt, batt_temp); + + } else { + if (chip->batt_therm_cooling_cnt > COOLING_MAX_CNT) { + + mmi_chrg_info(chip, "Do chrg power cooling failed" + "pd_batt_therm_currr %dmA, " + "pd_batt_therm_volt %dmV" + "battery temp %d, " + "Restart PM_STATE_ENTRY !\n", + chip->pd_batt_therm_curr, + chip->pd_batt_therm_volt, + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else { + mmi_chrg_info(chip, "It's already the lowest cooling chrg power" + "waiting for a while, cooling cnt %d, " + "battery temp %d\n", + batt_temp, chip->batt_therm_cooling); + chip->batt_therm_cooling_cnt++; + } + } + + } else if (batt_temp < chrg_step->temp_c - COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, " + "Exit successfully from COOLing loop!\n", + batt_temp); + + if (sm_state == PM_STATE_CP_CC_LOOP) { + mmi_chrg_info(chip,"Jump into CURR tunning" + "for chrg power poerformance!\n"); + mmi_chrg_sm_move_state(chip, + PM_STATE_PPS_TUNNING_CURR); + } + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + } + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + } + + if (chip->batt_therm_cooling) { + chip->pd_target_volt = min(chip->pd_target_volt, chip->pd_batt_therm_volt); + chip->pd_target_curr = min(chip->pd_target_curr, chip->pd_batt_therm_curr); + } + + mmi_chrg_dbg(chip, PR_MOTO, "chrg sm work,%s, " + "battery soc %d, " + "battery temp %d, " + "battery current %d, " + "battery voltage %d, " + "sys therm level %d, " + "pmic therm level %d, " + "sys therm cooling %d, " + "batt therm cooling %d, " + "sys therm force pmic chrg %d, " + "recovery pmic chrg %d\n", + pm_state_str[sm_state], + batt_soc, batt_temp, + ibatt_curr, vbatt_volt, + chip->system_thermal_level, + pmic_sys_therm_level, + chip->sys_therm_cooling, + chip->batt_therm_cooling, + chip->sys_therm_force_pmic_chrg, + chip->recovery_pmic_chrg); + + mmi_chrg_dbg(chip, PR_MOTO, "pd request volt %dmV, " + "pd request curr %dmA, " + "pd target volt %dmV, " + "pd target curr %dmA, " + "sys therm volt %dmV, " + "sys therm curr %dmA, " + "batt therm volt %dmV, " + "batt therm curr %dmA\n", + chip->pd_request_volt, + chip->pd_request_curr, + chip->pd_target_volt, + chip->pd_target_curr, + chip->pd_sys_therm_volt, + chip->pd_sys_therm_curr, + chip->pd_batt_therm_volt, + chip->pd_batt_therm_curr); + + if (chip->pd_target_volt < SWITCH_CHARGER_PPS_VOLT + || chip->pd_target_curr < chip->typec_middle_current) { + + if (sm_state == PM_STATE_PPS_TUNNING_CURR + || sm_state == PM_STATE_PPS_TUNNING_VOLT + || sm_state == PM_STATE_CP_CC_LOOP + || sm_state == PM_STATE_CP_CV_LOOP) { + + mmi_chrg_err(chip, "%s, wrong pd voltage or current , " + "request pd voltage %d, " + "request pd current %d\n", + pm_state_str[sm_state], + chip->pd_target_volt, + chip->pd_target_curr); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + goto skip_pd_select; + } + + chip->pps_result = usbpd_select_pdo(chip->pd_handle, + chip->mmi_pd_pdo_idx, + chip->pd_target_volt, + chip->pd_target_curr); + mmi_set_pps_result_history(chip, chip->pps_result); + if (!chip->pps_result) { + chip->pd_request_volt_prev = chip->pd_target_volt; + chip->pd_request_curr_prev = chip->pd_target_curr; + } + +skip_pd_select: + + if (heartbeat_dely_ms > 0) { + mmi_chrg_err(chip, "schedule work timer %dms\n", heartbeat_dely_ms); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(heartbeat_dely_ms)); + } else { + mmi_chrg_policy_clear(chip); + chip->sm_work_running = false; + chip->pd_pps_support = false; + mmi_chrg_err(chip, "exit sm work\n"); + } + + return; +} + +int mmi_chrg_policy_init(struct mmi_charger_manager *chip, + struct mmi_chrg_dts_info *chrg_dts, + int chrg_cnt) { + int i = 0, ops_cnt = 0, chrg_idx = 0; + struct mmi_charger_device *chrg_dev; + + if (!chip ||!chrg_dts) { + mmi_chrg_err(chip, "invalid input param!\n"); + return -EINVAL; + } + + ops_cnt = ARRAY_SIZE(dev_ops); + if (chrg_cnt > ops_cnt || + chrg_cnt > chip->mmi_chrg_dev_num) { + mmi_chrg_err(chip, "invalid input param!, chrg_cnt %d , ops_cnt %d, " + "mmi_chrg_dev_num %d\n", + chrg_cnt, ops_cnt, chip->mmi_chrg_dev_num); + return -EINVAL; + } + + mmi_chrg_err(chip, "chrg_cnt %d\n", chrg_cnt); + for (i = 0; i < chrg_cnt; i++) { + if (!strcmp(chrg_dts[i].chrg_name, dev_ops[i].dev_name)) { + + chrg_dev = mmi_charger_device_register(chrg_dts[i].chrg_name, + chrg_dts[i].psy_name, chip->dev, chip, + dev_ops[i].ops); + if (IS_ERR_OR_NULL(chrg_dev) + || !is_charger_exist(chrg_dts[i].chrg_name)) { + mmi_chrg_err(chip, + "register mmi charger %s failed\n", + chrg_dts[i].chrg_name); + return -EINVAL; + } else { + mmi_chrg_info(chip, + "register mmi charger %s successfully, i %d, chrg_idx %d\n", + chrg_dts[i].chrg_name, i, chrg_idx); + chrg_dev->charging_curr_limited = + chrg_dts[i].charging_curr_limited; + mmi_chrg_err(chip, "charging_curr_limited %d\n", + chrg_dts[i].charging_curr_limited); + chrg_dev->charging_curr_min = + chrg_dts[i].charging_curr_min; + mmi_chrg_err(chip, "charging_curr_min %d, chrg_id %d\n", + chrg_dts[i].charging_curr_min, chrg_idx); + chip->chrg_list[chrg_idx] = chrg_dev; + mmi_chrg_err(chip, "--- over ----\n"); + chrg_idx++; + } + } + } + + mmi_chrg_info(chip,"chrg_cnt %d, ops_cnt %d, mmi_chrg_dev_num %d, " + "chrg_idx %d\n", + chrg_cnt, ops_cnt, chip->mmi_chrg_dev_num, chrg_idx); + + if (chrg_idx != chip->mmi_chrg_dev_num + && chrg_idx > 0) { + + mmi_chrg_err(chip, "chrg_id %d != charger num %d\n", + chrg_idx, chip->mmi_chrg_dev_num); + for (i = 0; i < chrg_idx; i++) + mmi_charger_device_unregister(chip->chrg_list[i]); + return -EINVAL; + } + + chrg_dev_init(chip, &g_chrg_list); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + INIT_DELAYED_WORK(&chip->mmi_chrg_sm_work, mmi_chrg_sm_work_func); + return 0; +} + diff --git a/drivers/power/mmi_parallel_charger_iio/qpnp_pmic_charger.c b/drivers/power/mmi_parallel_charger_iio/qpnp_pmic_charger.c new file mode 100644 index 000000000000..5c2083dfb4bf --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/qpnp_pmic_charger.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include +#include "mmi_charger_core.h" + +static int qpnp_pmic_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + prop.intval = en; + rc = power_supply_set_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + + if (!rc) { + chrg->charger_enabled = !!prop.intval; + } else + chrg->charger_enabled = false; + + return rc; +} + +static int qpnp_pmic_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + if (!rc) { + chrg->charger_enabled = !!prop.intval; + } else + chrg->charger_enabled = false; + + *en = chrg->charger_enabled; + + return rc; +} + +static int qpnp_pmic_get_charging_current(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + *uA = !!prop.intval; + + return rc; +} + +static int qpnp_pmic_set_charging_current(struct mmi_charger_device *chrg, u32 uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + prop.intval = uA; + rc = power_supply_set_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &prop); +// if (!rc) +// chrg->charger_limited = true; + + return rc; +} + +static int qpnp_pmic_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + struct power_supply *chrg_psy; + union power_supply_propval prop = {0,}; + + chrg_psy = power_supply_get_by_name("usb"); + if (!chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg_psy, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, &prop); + if (!rc) + chrg->input_curr_setted = prop.intval; + *uA = chrg->input_curr_setted; + + return rc; +} + +static int qpnp_pmic_update_charger_status(struct mmi_charger_device *chrg) +{ + int rc; + struct power_supply *usb_psy; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + usb_psy = power_supply_get_by_name("usb"); + if (!usb_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbatt_volt = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibatt_curr = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + chrg->charger_data.batt_temp = prop.intval / 10; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbus_volt = prop.intval; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibus_curr = prop.intval; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chrg->charger_data.vbus_pres = !!prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + if (!rc) + chrg->charger_enabled= !!prop.intval; + + chrg_dev_info(chrg, "QPNP SW chrg: status update: --- info---"); + chrg_dev_info(chrg, "vbatt %d\n", chrg->charger_data.vbatt_volt); + chrg_dev_info(chrg, "ibatt %d\n", chrg->charger_data.ibatt_curr); + chrg_dev_info(chrg, "batt temp %d\n", chrg->charger_data.batt_temp); + chrg_dev_info(chrg, "vbus %d\n", chrg->charger_data.vbus_volt); + chrg_dev_info(chrg, "ibus %d\n", chrg->charger_data.ibus_curr); + chrg_dev_info(chrg, "vbus pres %d\n", chrg->charger_data.vbus_pres); + chrg_dev_info(chrg, "charger_enabled %d\n", chrg->charger_enabled); + chrg_dev_info(chrg, "charger_limited %d\n", chrg->charger_limited); + return rc; +} + +struct mmi_charger_ops qpnp_pmic_charger_ops = { + .enable = qpnp_pmic_enable_charging, + .is_enabled = qpnp_pmic_is_charging_enabled, + .get_charging_current = qpnp_pmic_get_charging_current, + .set_charging_current = qpnp_pmic_set_charging_current, + .get_input_current_settled = qpnp_pmic_get_input_current_settled, + .update_charger_status = qpnp_pmic_update_charger_status, +};