diff --git a/drivers/power/mmi_discrete_turbo_charger/Android.mk b/drivers/power/mmi_discrete_turbo_charger/Android.mk new file mode 100644 index 000000000000..dd1795de2357 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_parallel_charger_iio.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_discrete_turbo_charger/Kbuild b/drivers/power/mmi_discrete_turbo_charger/Kbuild new file mode 100644 index 000000000000..6e171daa0029 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/Kbuild @@ -0,0 +1,16 @@ +# 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_iio + +obj-m += mmi_parallel_charger_iio.o +mmi_parallel_charger_iio-objs += mmi_charger_class.o +mmi_parallel_charger_iio-objs += mmi_charger_core.o +mmi_parallel_charger_iio-objs += mmi_charger_pump_policy.o +mmi_parallel_charger_iio-objs += mmi_cp_charger.o +mmi_parallel_charger_iio-objs += qpnp_pmic_charger.o +mmi_parallel_charger_iio-objs += mmi_qc3p.o +mmi_parallel_charger_iio-objs += mmi_qc3p_cp_policy.o + +#ifeq ($(CONFIG_MMI_PL_CP_POLICY), y) +#endif diff --git a/drivers/power/mmi_discrete_turbo_charger/Makefile b/drivers/power/mmi_discrete_turbo_charger/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/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_discrete_turbo_charger/mmi_charger_class.c b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_class.c new file mode 100644 index 000000000000..fc44c0df0935 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_class.c @@ -0,0 +1,222 @@ +/* + * 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_vbus(struct mmi_charger_device *chrg, u32 *mv) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_vbus) + return chrg->ops->get_vbus(chrg,mv); + + 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; +} + +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); +} + +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_discrete_turbo_charger/mmi_charger_class.h b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_class.h new file mode 100644 index 000000000000..f39ea60fe51f --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_class.h @@ -0,0 +1,134 @@ +/* + * 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 +#define MMI_CP_SWITCH_BIT 18 + +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_vbus)(struct mmi_charger_device *chrg, u32 *mv); + 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 int mmi_get_vbus(struct mmi_charger_device *chrg, u32 *mv); +extern struct mmi_charger_ops cp_charger_ops; +extern struct mmi_charger_ops qpnp_pmic_charger_ops; +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_discrete_turbo_charger/mmi_charger_core.c b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core.c new file mode 100644 index 000000000000..a08383eff5c2 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core.c @@ -0,0 +1,1761 @@ +/* + * 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_core_iio.h" +#include "mmi_charger_policy.h" +#include "mmi_qc3p.h" +#include +#include + +static int __debug_mask = 0x85; +module_param_named( + debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR +); + +struct mmi_cp_policy_dev g_chrg_list = {0}; +const struct mmi_chrg_dev_ops dev_ops[] = { + { + .dev_name = "pmic-sw", + .ops = &qpnp_pmic_charger_ops, + }, + { + .dev_name = "cp-master", + .ops = &cp_charger_ops, + }, + { + .dev_name = "cp-slave", + .ops = &cp_charger_ops, + }, +}; + +bool is_chan_valid(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan) +{ + int rc; + + if (IS_ERR(chip->ext_iio_chans[chan])) + return false; + + if (!chip->ext_iio_chans[chan]) { + chip->ext_iio_chans[chan] = iio_channel_get(chip->dev, + mmi_charger_ext_iio_chan_name[chan]); + if (IS_ERR(chip->ext_iio_chans[chan])) { + rc = PTR_ERR(chip->ext_iio_chans[chan]); + if (rc == -EPROBE_DEFER) + chip->ext_iio_chans[chan] = NULL; + + mmi_chrg_err(chip, "Failed to get IIO channel %s, rc=%d\n", + mmi_charger_ext_iio_chan_name[chan], rc); + return false; + } + } + + return true; +} + +int mmi_charger_read_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int *val) +{ + int rc; + + if (is_chan_valid(chip, chan)) { + rc = iio_read_channel_processed( + chip->ext_iio_chans[chan], val); + return (rc < 0) ? rc : 0; + } + + return -EINVAL; +} + +int mmi_charger_write_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int val) +{ + if (is_chan_valid(chip, chan)) + return iio_write_channel_raw(chip->ext_iio_chans[chan], + val); + + return -EINVAL; +} + +static int mmi_charger_iio_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val1, + int val2, long mask) +{ + struct mmi_charger_manager *chip = iio_priv(indio_dev); + int rc = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + if (!chip->factory_mode) { + if (chip->pd_pps_support) + mmi_chrg_enable_all_cp(chip, val1); + else if (chip->qc3p_active) + mmi_qc3p_chrg_enable_all_cp(chip, val1); + } + break; + default: + pr_err("Unsupported mmi_charger IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) + pr_err("Couldn't write IIO channel %d, rc = %d\n", + chan->channel, rc); + + return rc; +} + +static int mmi_charger_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val1, + int *val2, long mask) +{ + struct mmi_charger_manager *chip = iio_priv(indio_dev); + int rc = 0; + + *val1 = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + *val1 = !chip->cp_disable; + break; + default: + pr_err("Unsupported mmi_charger IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) { + pr_err("Couldn't read IIO channel %d, rc = %d\n", + chan->channel, rc); + return rc; + } + + return IIO_VAL_INT; +} + +static int mmi_charger_iio_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct mmi_charger_manager *chip = iio_priv(indio_dev); + struct iio_chan_spec *iio_chan = chip->iio_chan; + int i; + + for (i = 0; i < ARRAY_SIZE(mmi_charger_iio_psy_channels); + i++, iio_chan++) + if (iio_chan->channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info mmi_charger_iio_info = { + .read_raw = mmi_charger_iio_read_raw, + .write_raw = mmi_charger_iio_write_raw, + .of_xlate = mmi_charger_iio_of_xlate, +}; + +static int mmi_charger_init_iio_psy(struct mmi_charger_manager *chip, + struct platform_device *pdev) +{ + struct iio_dev *indio_dev = chip->indio_dev; + struct iio_chan_spec *chan; + int mmi_charger_num_iio_channels = ARRAY_SIZE(mmi_charger_iio_psy_channels); + int rc, i; + + chip->iio_chan = devm_kcalloc(chip->dev, mmi_charger_num_iio_channels, + sizeof(*chip->iio_chan), GFP_KERNEL); + if (!chip->iio_chan) + return -ENOMEM; + + chip->int_iio_chans = devm_kcalloc(chip->dev, + mmi_charger_num_iio_channels, + sizeof(*chip->int_iio_chans), + GFP_KERNEL); + if (!chip->int_iio_chans) + return -ENOMEM; + + indio_dev->info = &mmi_charger_iio_info; + indio_dev->dev.parent = chip->dev; + indio_dev->dev.of_node = chip->dev->of_node; + indio_dev->name = "mmi-parallel-charger"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip->iio_chan; + indio_dev->num_channels = mmi_charger_num_iio_channels; + + for (i = 0; i < mmi_charger_num_iio_channels; i++) { + chip->int_iio_chans[i].indio_dev = indio_dev; + chan = &chip->iio_chan[i]; + chip->int_iio_chans[i].channel = chan; + chan->address = i; + chan->channel = mmi_charger_iio_psy_channels[i].channel_num; + chan->type = mmi_charger_iio_psy_channels[i].type; + chan->datasheet_name = + mmi_charger_iio_psy_channels[i].datasheet_name; + chan->extend_name = + mmi_charger_iio_psy_channels[i].datasheet_name; + chan->info_mask_separate = + mmi_charger_iio_psy_channels[i].info_mask; + } + + rc = devm_iio_device_register(chip->dev, indio_dev); + if (rc) { + pr_err("Failed to register cp IIO device, rc=%d\n", rc); + return rc; + } + + chip->ext_iio_chans = devm_kcalloc(chip->dev, + ARRAY_SIZE(mmi_charger_ext_iio_chan_name), + sizeof(*chip->ext_iio_chans), + GFP_KERNEL); + if (!chip->ext_iio_chans) + return -ENOMEM; + + return 0; +} + +static int qc3p_volt_max_init = 0; +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; + +#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; +} + +static enum power_supply_property mmi_chrg_mgr_props[] = { + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX +}; + +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_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_CHARGE_CONTROL_LIMIT: + val->intval = chip->system_thermal_level; + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: + val->intval = chip->thermal_levels; + 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_CHARGE_CONTROL_LIMIT: + 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; + 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_CHARGE_CONTROL_LIMIT: + 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_MAINS, + .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; +} + +#define REV_CP_THRESH_UV 4400000 +#define CP_IBUS_THRESH_MA 20 +#define IBAT_THRESH_UA 10000 +#define REV_CP_DOUBLE_THRESH_UV 100000 +bool mmi_is_cable_plugout(struct mmi_charger_manager *chip) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + int vbus_volt, ibatt_curr, ibus_curr, vbatt_volt; + bool cp_chrg_enable, cp_sw_en; + + if (chip->extrn_sense) { + ibatt_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibatt_curr; + ibatt_curr *= 1000; + vbatt_volt = chrg_list->chrg_dev[CP_MASTER]->charger_data.vbatt_volt; + vbatt_volt *= 1000; + } else { + ibatt_curr= chrg_list->chrg_dev[PMIC_SW]->charger_data.ibatt_curr; + vbatt_volt = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbatt_volt; + } + vbus_volt = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_volt; + ibus_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibus_curr; + cp_sw_en = chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT); + cp_chrg_enable = chrg_list->chrg_dev[CP_MASTER]->charger_enabled; + mmi_chrg_info(chip, "ibat:%d, vbat:%d, vbus:%d, ibus:%d, cp_chg_en:%d, cp_sw_en:%d\n", + ibatt_curr, vbatt_volt, vbus_volt, ibus_curr, cp_chrg_enable, cp_sw_en); + + if (!cp_chrg_enable ||ibus_curr > CP_IBUS_THRESH_MA || ibatt_curr > IBAT_THRESH_UA) + return false; + + if (vbatt_volt >= vbus_volt + && vbus_volt < REV_CP_THRESH_UV) { + mmi_chrg_info(chip, "cable plug out: cp reverse\n"); + return true; + } + + if (cp_sw_en && vbus_volt < 2 * vbatt_volt - REV_CP_DOUBLE_THRESH_UV) { + mmi_chrg_info(chip, "cable plug out: cp double reverse\n"); + return true; + } + + return false; +} + +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) +{ + 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; + ret = mmi_charger_write_iio_chan(chip, MMI_CP_ENABLE_STATUS, true); + if (ret) + mmi_chrg_err(chip, "Unable to write CP enable status: %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) +{ + 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; + ret = mmi_charger_write_iio_chan(chip, MMI_CP_ENABLE_STATUS, false); + if (ret) + mmi_chrg_err(chip, "Unable to write CP disable status: %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 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 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; +} + +static void kick_qc3p_sm(struct mmi_charger_manager *chip, int ms) +{ + + + if (!chip->qc3p_sm_work_running) { + mmi_chrg_dbg(chip, PR_INTERRUPT, + "launch mmi qc3p chrg sm work\n"); + mmi_qc3p_chrg_policy_clear(chip); //todo + schedule_delayed_work(&chip->mmi_qc3p_chrg_sm_work, // + msecs_to_jiffies(ms)); //todo + chip->qc3p_sm_work_running = true; + } else + mmi_chrg_dbg(chip, PR_INTERRUPT, + "mmi chrg qc3p sm work already existed\n"); +} + +static void cancel_qc3p_sm(struct mmi_charger_manager *chip) +{ + cancel_delayed_work_sync(&chip->mmi_qc3p_chrg_sm_work); + flush_delayed_work(&chip->mmi_qc3p_chrg_sm_work); + mmi_qc3p_chrg_policy_clear(chip); + chip->qc3p_sm_work_running = false; + chip->qc3p_volt_max = qc3p_volt_max_init; + mmi_chrg_dbg(chip, PR_INTERRUPT, + "cancel sync and flush mmi chrg qc3p sm work\n"); +} + +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); +} + +#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; + 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); + + 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) + mmi_awake_vote(chip, false); + + ret = mmi_charger_read_iio_chan(chip, SMB5_USB_PD_ACTIVE, &val.intval); + 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->qc3p_sm_work_running && chip->vbus_present) + chip->qc3p_active = mmi_qc3p_power_active(chip); + + if (chip->pd_pps_support + && !chip->factory_mode) { + mmi_chrg_info(chip, "MMI: Heartbeat!, launch sm work\n"); + kick_sm(chip, 100); + } else if(chip->qc3p_active + && !chip->factory_mode) { + mmi_chrg_info(chip, "MMI: Heartbeat!, launch qc3p sm work\n"); + kick_qc3p_sm(chip, 100); + } + +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 = mmi_charger_read_iio_chan(chip, SMB5_USB_PD_ACTIVE, &val.intval); + 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->qc3p_active = mmi_qc3p_power_active(chip); + + if (chip->vbus_present + && chip->pd_pps_support + && !chip->factory_mode) { + kick_sm(chip, 100); + } else if (chip->vbus_present + && chip->qc3p_active + && !chip->factory_mode) { + kick_qc3p_sm(chip, 100); + } else { + cancel_sm(chip); + cancel_qc3p_sm(chip); + chip->pd_pps_support = false; + chip->qc3p_active = false; + } + + return; +} +#define DEFAULT_IBUS_MAX_MA 3000 +#define DEFAULT_QC3P_VOLT_STEPS 20000 +#define DEFAULT_QC3P_VOLT_MAX 11000000 +#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->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,pd-allow-min-current", + &chip->pd_allow_min_current); + if (rc < 0) + chip->pd_allow_min_current = + PD_ALLOW_MIN_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; + + rc = of_property_read_u32(node, + "mmi,qc3p-max-ibus-ma", + &chip->qc3p_max_ibus_ma); + if (rc < 0) + chip->qc3p_max_ibus_ma = + DEFAULT_IBUS_MAX_MA; + rc = of_property_read_u32(node, + "mmi,qc3p-volt-steps", + &chip->qc3p_volt_steps); + if (rc < 0) + chip->qc3p_volt_steps = + DEFAULT_QC3P_VOLT_STEPS; + + rc = of_property_read_u32(node, + "mmi,qc3p-volt-max", + &chip->qc3p_volt_max); + if (rc < 0) + chip->qc3p_volt_max = + DEFAULT_QC3P_VOLT_MAX; + qc3p_volt_max_init = chip->qc3p_volt_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; +} + +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; +} + +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); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + INIT_DELAYED_WORK(&chip->mmi_qc3p_chrg_sm_work, mmi_qc3p_chrg_sm_work_func); + return 0; +} + +static int mmi_chrg_manager_probe(struct platform_device *pdev) +{ + int ret = 0; + struct mmi_charger_manager *chip; + struct iio_dev *indio_dev; + + if (!pdev) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + chip = iio_priv(indio_dev); + if (!chip) { + dev_err(&pdev->dev, + "Unable to alloc memory for mmi_charger_manager\n"); + return -ENOMEM; + } + chip->indio_dev = indio_dev; + + 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; + } + + ret = mmi_charger_init_iio_psy(chip, pdev); + if (ret) { + dev_err(&pdev->dev, + "mmi charger iio psy init failed\n"); + goto cleanup; + } + + chip->factory_mode = mmi_factory_check(); + + chip->qcom_psy = power_supply_get_by_name("qcom_battery"); + 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); + + 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; + + 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); + + power_supply_unreg_notifier(&chip->psy_nb); + power_supply_unregister(chip->mmi_chrg_mgr_psy ); + 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_discrete_turbo_charger/mmi_charger_core.h b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core.h new file mode 100644 index 000000000000..449ad5a9d62b --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core.h @@ -0,0 +1,319 @@ +/* + * 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 +#include +#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); +} + +#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; +}; + +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]; +}; + +#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 PD_ALLOW_MIN_CURRENT_UA 1500000 +#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 *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 iio_channel **ext_iio_chans; + struct iio_dev *indio_dev; + struct iio_chan_spec *iio_chan; + struct iio_channel *int_iio_chans; + + 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 pd_allow_min_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*/ + + 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; + + 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*/ + + /*qc3p*/ + int qc3p_max_ibus_ma; + int qc3p_power; + bool qc3p_active; + bool qc3p_sm_work_running; + struct delayed_work mmi_qc3p_chrg_sm_work; + int qc3p_request_volt; + int qc3p_request_volt_prev; + int qc3p_target_volt; + int qc3p_batt_therm_volt; + int qc3p_sys_therm_volt; + bool qc3p_recovery_pmic_chrg; + bool qc3p_sys_therm_cooling; + bool qc3p_sys_therm_force_pmic_chrg; + bool qc3p_batt_therm_cooling; + int qc3p_batt_therm_cooling_cnt; + int qc3p_volt_comp; + int qc3p_ibus_max_volt; + int qc3p_volt_steps; + int qc3p_volt_max; +}; + +#define PPS_INIT_VOLT_COMP 500000 +#define QC3P_INIT_VOLT_COMP 500000 +extern void mmi_qc3p_chrg_sm_work_func(struct work_struct *work); +extern struct mmi_cp_policy_dev g_chrg_list; +extern const struct mmi_chrg_dev_ops dev_ops[]; +extern void clear_chrg_dev_error_cnt(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list); +extern void chrg_policy_error_clear(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list); +extern void mmi_chrg_sm_work_func(struct work_struct *work); +extern void chrg_dev_init(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_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 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 bool mmi_is_cable_plugout(struct mmi_charger_manager *chip); +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_discrete_turbo_charger/mmi_charger_core_iio.h b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core_iio.h new file mode 100644 index 000000000000..e4f049f53428 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core_iio.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#ifndef __MMI_CHRG_CORE_IIO_H +#define __MMI_CHRG_CORE_IIO_H + +#include +#include + +struct mmi_charger_iio_channels { + const char *datasheet_name; + int channel_num; + enum iio_chan_type type; + long info_mask; +}; + +#define MMI_CHARGER_IIO_CHAN(_name, _num, _type, _mask) \ + { \ + .datasheet_name = _name, \ + .channel_num = _num, \ + .type = _type, \ + .info_mask = _mask, \ + }, + +#define MMI_CHARGER_CHAN_INDEX(_name, _num) \ + MMI_CHARGER_IIO_CHAN(_name, _num, IIO_INDEX, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +static const struct mmi_charger_iio_channels mmi_charger_iio_psy_channels[] = { + MMI_CHARGER_CHAN_INDEX("cp_charging_enabled", PSY_IIO_CP_ENABLE) +}; +enum mmi_charger_ext_iio_channels { + /*smb5*/ + SMB5_HW_CURRENT_MAX, + SMB5_CHARGING_ENABLED, + SMB5_TYPEC_MODE, + SMB5_USB_INPUT_CURRENT_SETTLED, + SMB5_USB_PD_ACTIVE, + /*qc3p*/ + SMB5_USB_REAL_TYPE, + SMB5_DP_DM, + SMB5_QC3P_POWER, + /*cp*/ + CP_ENABLE, + CP_INPUT_CURRENT_NOW, + CP_INPUT_VOLTAGE_NOW, + CP_STATUS1, + CP_CLEAR_ERROR, + /*mmi-smb5charger-iio*/ + MMI_CP_ENABLE_STATUS, +}; + +static const char * const mmi_charger_ext_iio_chan_name[] = { + /*smb5*/ + [SMB5_HW_CURRENT_MAX] = "usb_hw_current_max", + [SMB5_CHARGING_ENABLED] = "charging_enabled", + [SMB5_TYPEC_MODE] = "usb_typec_mode", + [SMB5_USB_INPUT_CURRENT_SETTLED] = "usb_input_current_settled", + [SMB5_USB_PD_ACTIVE] = "usb_pd_active", + /*qc3p*/ + [SMB5_USB_REAL_TYPE] = "usb_real_type", + [SMB5_DP_DM] = "battery_dp_dm", + [SMB5_QC3P_POWER] = "usb_qc3p_power", + /*cp*/ + [CP_ENABLE] = "cp_enable", + [CP_INPUT_CURRENT_NOW] = "cp_input_current_now", + [CP_INPUT_VOLTAGE_NOW] = "cp_input_voltage_now", + [CP_STATUS1] = "cp_status1", + [CP_CLEAR_ERROR] = "cp_clear_error", + /*mmi-smb5charger-iio*/ + [MMI_CP_ENABLE_STATUS] = "mmi_cp_enabled_status", +}; + +int mmi_charger_read_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int *val); + +int mmi_charger_write_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int val); + +#endif diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_charger_policy.h b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_policy.h new file mode 100644 index 000000000000..62db9d556e9a --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_policy.h @@ -0,0 +1,34 @@ +/* + * 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 void mmi_chrg_policy_clear(struct mmi_charger_manager *chip); +extern void mmi_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val); +extern void mmi_qc3p_chrg_policy_clear(struct mmi_charger_manager *chip); +extern void mmi_qc3p_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val); +#endif diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_charger_pump_policy.c b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_pump_policy.c new file mode 100644 index 000000000000..cb20daa15cac --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_pump_policy.c @@ -0,0 +1,1507 @@ +/* + * 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" + +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 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; +} + +#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; +} + +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 + +static 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); +} + +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); + if (chrg_list->cp_slave) + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + if (chrg_list->cp_master) + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + 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; +} + +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_CHARGE_CONTROL_LIMIT, &prop); + if (!rc) + pmic_sys_therm_level = 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 && mmi_is_cable_plugout(chip)) + vbus_pres = 0; + + 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 + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into " + "SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + 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 + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, " + "Enter into SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + 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 + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + 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->pd_allow_min_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 + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + 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->pd_allow_min_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->pd_allow_min_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->pd_allow_min_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->pd_allow_min_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->pd_allow_min_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->pd_allow_min_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; +} + + diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_cp_charger.c b/drivers/power/mmi_discrete_turbo_charger/mmi_cp_charger.c new file mode 100644 index 000000000000..2d6f4939781e --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_cp_charger.c @@ -0,0 +1,225 @@ +/* + * 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" +#include "mmi_charger_core_iio.h" + +static int cp_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + int rc; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_write_iio_chan(chip, CP_ENABLE, en); + + if (!rc) { + chrg->charger_enabled = !!en; + } else + chrg->charger_enabled = false; + + return rc; +} + +static int cp_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + int rc; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, CP_ENABLE, &value); + + if (!rc) { + chrg->charger_enabled = !!value; + } else + chrg->charger_enabled = false; + + *en = chrg->charger_enabled; + + return rc; +} + +static int cp_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 cp_get_vbus(struct mmi_charger_device *chrg, u32 *mv) +{ + int rc, val; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_VOLTAGE_NOW, &val); + if (!rc) + *mv = val; + + return rc; +} + +static int cp_get_input_current(struct mmi_charger_device *chrg, u32 *uA) //ibus +{ + int rc; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_CURRENT_NOW, &value); + + if (!rc) + *uA = value; + + return rc; +} + +static int cp_update_charger_status(struct mmi_charger_device *chrg) +{ + int rc; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &prop); + if (!rc) + chrg->charger_data.ibus_curr= prop.intval; + + 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; + + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_VOLTAGE_NOW, &prop.intval); + if (!rc) + chrg->charger_data.vbus_volt = prop.intval; + + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_CURRENT_NOW, &prop.intval); + if (!rc) + chrg->charger_data.ibus_curr = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chrg->charger_data.vbus_pres = !!prop.intval; + + rc = mmi_charger_read_iio_chan(chip, CP_ENABLE, &prop.intval); + if (!rc) + chrg->charger_enabled = !!prop.intval; + + chrg_dev_info(chrg, "CP chrg: %s status update: --- info---\n",chrg->name); + 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); + + return rc; +} + +static int cp_update_charger_error_status(struct mmi_charger_device *chrg) +{ + int rc; + int value = 0; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, CP_STATUS1, &value); + if (!rc) { + chrg->charger_error.chrg_err_type = value; + } + return rc; +} + +static int cp_clear_charger_error(struct mmi_charger_device *chrg) +{ + int rc; + int value = 0; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_write_iio_chan(chip, CP_CLEAR_ERROR, value); + + return rc; +} + +struct mmi_charger_ops cp_charger_ops = { + .enable = cp_enable_charging, + .is_enabled = cp_is_charging_enabled, + .get_charging_current = cp_get_charging_current, + .get_vbus = cp_get_vbus, + .get_input_current = cp_get_input_current, + .update_charger_status = cp_update_charger_status, + .update_charger_error = cp_update_charger_error_status, + .clear_charger_error = cp_clear_charger_error, +}; + diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.c b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.c new file mode 100644 index 000000000000..bc465469c5f1 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2012 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 "mmi_charger_core.h" +#include "mmi_charger_core_iio.h" +#include + +bool mmi_qc3p_power_active(struct mmi_charger_manager *chg) +{ + int ret, iio_val; + + ret = mmi_charger_read_iio_chan(chg, SMB5_USB_REAL_TYPE, &iio_val); + if (ret < 0) { + mmi_chrg_err(chg, "Couldn't read charger type rc=%d\n", ret); + return false; + } + if (iio_val != QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5) + return false; + + ret = mmi_charger_read_iio_chan(chg, SMB5_QC3P_POWER, &iio_val); + if (ret < 0) { + mmi_chrg_err(chg, "Couldn't read qc3p power rc=%d\n", ret); + return false; + } + chg->qc3p_power = iio_val; + mmi_chrg_info(chg, "qc3p_power =%d\n", chg->qc3p_power); + + if (chg->qc3p_power == QTI_POWER_SUPPLY_QC3P_27W || + chg->qc3p_power == QTI_POWER_SUPPLY_QC3P_45W) + return true; + else + return false; +} + +#define QC3P_PULSE_COUNT_MAX ((11000 - 5000) / 20 + 10) +int mmi_qc3p_set_vbus_voltage(struct mmi_charger_manager *chg, int target_mv) +{ + int rc = -EINVAL; + union power_supply_propval val = {0, }; + int pulse_cnt, curr_vbus_mv, step, i; + + rc = mmi_charger_read_iio_chan(chg, SMB5_DP_DM, &val.intval); + if (rc < 0) { + mmi_chrg_err(chg, "Couldn't read dpdm pulse count rc=%d\n", rc); + return -EINVAL; + } else { + mmi_chrg_info(chg, "DP DM pulse count = %d\n", val.intval); + pulse_cnt = val.intval; + } + + rc = mmi_get_vbus(chg->chrg_list[CP_MASTER], + &curr_vbus_mv); + if (rc) { + mmi_chrg_err(chg, "Unable to read USB voltage: %d\n", rc); + return -EINVAL; + } + + if(target_mv < curr_vbus_mv) { + step = (curr_vbus_mv - target_mv) / 20; + val.intval = QTI_POWER_SUPPLY_DP_DM_DM_PULSE; + if (pulse_cnt <= step) + step = pulse_cnt; + } else { + step = (target_mv - curr_vbus_mv) / 20; + val.intval = QTI_POWER_SUPPLY_DP_DM_DP_PULSE; + if (step + pulse_cnt > QC3P_PULSE_COUNT_MAX) + step = QC3P_PULSE_COUNT_MAX - pulse_cnt; + } + + mmi_chrg_info(chg, "curr_vbus= %d, target_vbus = %d, step = %d\n",curr_vbus_mv, target_mv, step); + for (i = 0; i < step; i++) { + rc = mmi_charger_write_iio_chan(chg, SMB5_DP_DM, val.intval); + if (rc < 0) { + mmi_chrg_err(chg, "Couldn't set dpdm pulse rc=%d\n", rc); + break; + } + + if (i < step - 1) + udelay(5000); + } + + rc = mmi_get_vbus(chg->chrg_list[CP_MASTER], + &curr_vbus_mv); + if (rc) { + mmi_chrg_err(chg, "Unable to read VBUS: %d\n", rc); + return -EINVAL; + } + mmi_chrg_info(chg, "result_vbus= %d\n",curr_vbus_mv); + return curr_vbus_mv; +} diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.h b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.h new file mode 100644 index 000000000000..7b3af65832c8 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 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. + */ + +extern bool mmi_qc3p_power_active(struct mmi_charger_manager *chg); +extern int mmi_qc3p_set_vbus_voltage(struct mmi_charger_manager *chg, int target_mv); + diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p_cp_policy.c b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p_cp_policy.c new file mode 100644 index 000000000000..1415edf45995 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p_cp_policy.c @@ -0,0 +1,1200 @@ +/* + * Copyright (c) 2021 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 "mmi_charger_core.h" +#include "mmi_qc3p.h" + +#define SWITCH_CHARGER_QC3P_VOLT 5000000 +typedef enum { + PM_QC3P_STATE_DISCONNECT, + PM_QC3P_STATE_ENTRY, + PM_QC3P_STATE_SW_ENTRY, + PM_QC3P_STATE_SW_LOOP, + PM_QC3P_STATE_CHRG_PUMP_ENTRY, + PM_QC3P_STATE_SINGLE_CP_ENTRY, + PM_QC3P_STATE_DULE_CP_ENTRY, + PM_QC3P_STATE_TUNNING_VOLT, + PM_QC3P_STATE_CP_CC_LOOP, + PM_QC3P_STATE_CP_CV_LOOP, + PM_QC3P_STATE_CP_QUIT, + PM_QC3P_STATE_RECOVERY_SW, + PM_QC3P_STATE_STOP_CHARGE, +} pm_sm_qc3p_state_t; + +const unsigned char *pm_qc3p_state_str[] = { + "PM_QC3P_STATE_DISCONNECT", + "PM_QC3P_STATE_ENTRY", + "PM_QC3P_STATE_SW_ENTRY", + "PM_QC3P_STATE_SW_LOOP", + "PM_QC3P_STATE_CHRG_PUMP_ENTRY", + "PM_QC3P_STATE_SINGLE_CP_ENTRY", + "PM_QC3P_STATE_DULE_CP_ENTRY", + "PM_QC3P_STATE_TUNNING_VOLT", + "PM_QC3P_STATE_CP_CC_LOOP", + "PM_QC3P_STATE_CP_CV_LOOP", + "PM_QC3P_STATE_CP_QUIT", + "PM_QC3P_STATE_RECOVERY_SW", + "PM_QC3P_STATE_STOP_CHARGE", +}; + +static pm_sm_qc3p_state_t qc3p_sm_state = PM_QC3P_STATE_DISCONNECT; +static int qc3p_chrg_cc_power_tunning_cnt = 0; +static int qc3p_chrg_cv_taper_tunning_cnt = 0; +static int qc3p_chrg_cv_delta_volt = 0; +static int qc3p_quit_slave_chrg_cnt = 0; +static int qc3p_batt_curr_roof = 0; +static int qc3p_constant_power_cnt = 0; + +static void mmi_chrg_qc3p_sm_move_state(struct mmi_charger_manager *chip, pm_sm_qc3p_state_t state) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "pm_state change:%s -> %s\n", + pm_qc3p_state_str[qc3p_sm_state], pm_qc3p_state_str[state]); + qc3p_sm_state = state; + qc3p_constant_power_cnt = 0; + qc3p_batt_curr_roof = 0; +} + +#define QC3P_VOLT_COMP_DELTA 300000 +static void qc3p_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," + "qc3p volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chip->qc3p_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_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CHRG_PUMP_ENTRY); + } else { + chip->qc3p_recovery_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + } else if (chrg_dev->charger_error.bus_ucp_err_cnt > 6) { + chip->qc3p_recovery_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + + chrg_dev->charger_error.bus_ucp_err_cnt++; + chip->qc3p_volt_comp += QC3P_VOLT_COMP_DELTA; + mmi_chrg_info(chip,"Restart charging, " + "increase qc3p volt comp %dmV\n", + chip->qc3p_volt_comp); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_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," + "qc3p volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ocp_err_cnt, + chip->qc3p_volt_comp); + if (chrg_dev->charger_error.bus_ocp_err_cnt > 3) { + chip->qc3p_recovery_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + chrg_dev->charger_error.bus_ocp_err_cnt++; + chip->qc3p_volt_comp -= QC3P_VOLT_COMP_DELTA; + if (chip->qc3p_volt_comp < 0) + chip->qc3p_volt_comp = 0; + mmi_chrg_info(chip,"Restart charging, " + "decrease qc3p volt comp %dmV\n", + chip->qc3p_volt_comp); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_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; +} + +void mmi_qc3p_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_qc3p_chrg_sm_work); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CHRG_PUMP_ENTRY); + chip->cp_disable = false; + schedule_delayed_work(&chip->mmi_qc3p_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_qc3p_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_qc3p_sm_move_state(chip, PM_QC3P_STATE_RECOVERY_SW); + schedule_delayed_work(&chip->mmi_qc3p_chrg_sm_work, + msecs_to_jiffies(0)); + } +} + +#define QC3P_HEARTBEAT_SHORT_DELAY_MS 1000 +#define QC3P_HEARTBEAT_lOOP_WAIT_MS 3000 +#define QC3P_HEARTBEAT_TUNNING_MS 100 +#define QC3P_HEARTBEAT_NEXT_STATE_MS 100 +#define QC3P_HEARTBEAT_CANCEL -1 +#define QC3P_CC_CURR_DEBOUNCE 100000 +#define QC3P_CV_TAPPER_COUNT 3 +#define QC3P_CC_POWER_COUNT 3 +#define QC3P_CV_DELTA_VOLT 100000 +#define QC3P_COOLING_DELTA_POWER 100000 +#define QC3P_COOLING_MAX_CNT 5 +#define QC3P_DISABLE_CHRG_LIMIT -1 +#define QC3P_CP_CHRG_SOC_LIMIT 90 +#define QC3P_CONT_PWR_CNT 5 + +void qc3p_clear_chg_manager(struct mmi_charger_manager *chip) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "clear mmi qc3p chrg manager!\n"); + chip->qc3p_request_volt = 0; + chip->qc3p_request_volt_prev = 0; + chip->qc3p_target_volt = 0; + chip->qc3p_batt_therm_volt = 0; + chip->qc3p_sys_therm_volt = 0; + chip->qc3p_recovery_pmic_chrg = false; + chip->qc3p_sys_therm_cooling= false; + chip->qc3p_sys_therm_force_pmic_chrg = false; + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + +} + +void mmi_qc3p_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); + qc3p_clear_chg_manager(chip); + if (chrg_list->cp_slave) + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + if (chrg_list->cp_master) + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + qc3p_sm_state = PM_QC3P_STATE_DISCONNECT; + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + qc3p_quit_slave_chrg_cnt = 0; + qc3p_chrg_cc_power_tunning_cnt = 0; + qc3p_chrg_cv_taper_tunning_cnt = 0; + qc3p_chrg_cv_delta_volt = 0; + qc3p_constant_power_cnt = 0; + qc3p_batt_curr_roof = 0; + return; +} + +void mmi_qc3p_chrg_sm_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, mmi_qc3p_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 pmic_sys_therm_level = 0; + bool zone_change = false; + int ibus_total_ma = 0; + int cooling_volt = 0; + 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_qc3p_state_str[qc3p_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_CHARGE_CONTROL_LIMIT, &prop); + if (!rc) + pmic_sys_therm_level = 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 && mmi_is_cable_plugout(chip)) + vbus_pres = 0; + + if (vbus_pres && + (qc3p_sm_state == PM_QC3P_STATE_TUNNING_VOLT + || qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP + || qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP)) { + mmi_dump_charger_error(chip, chrg_list->chrg_dev[CP_MASTER]); + qc3p_chrg_policy_error_recovery(chip, chrg_list); + } + + if (qc3p_sm_state == PM_QC3P_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_qc3p_sm_move_state(chip, PM_QC3P_STATE_STOP_CHARGE); + } + + if (!vbus_pres) { + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_DISCONNECT); + } else if (qc3p_sm_state == PM_QC3P_STATE_ENTRY + || qc3p_sm_state == PM_QC3P_STATE_STOP_CHARGE) { + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + } else if (qc3p_sm_state == PM_QC3P_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_qc3p_sm_move_state(chip, PM_QC3P_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->qc3p_batt_therm_cooling = true; + chip->qc3p_batt_therm_volt = chip->qc3p_request_volt_prev; + } else if (!chip->qc3p_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_qc3p_sm_move_state(chip, PM_QC3P_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 (qc3p_sm_state) { + case PM_QC3P_STATE_DISCONNECT: + mmi_chrg_info(chip,"vbus disconnect !, jump to PM_QC3P_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], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + heartbeat_dely_ms = QC3P_HEARTBEAT_CANCEL; + break; + case PM_QC3P_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->qc3p_active + && 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 < QC3P_CP_CHRG_SOC_LIMIT) { + + mmi_chrg_dbg(chip, PR_MOTO, "Enter into CHRG PUMP, " + "vbatt %d uV, " + "qc3p support %d, " + "chrg step %d, " + "chrg step cc curr %d uA, " + "CP master charging curr min %d uA\n", + vbatt_volt, + chip->qc3p_active, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_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, " + "qc3p support %d, " + "chrg step %d\n", + vbatt_volt, chip->pl_chrg_vbatt_min, + chip->qc3p_active, + chrg_step->pres_chrg_step); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + + } + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt= 0; + chip->qc3p_sys_therm_volt= chip->qc3p_request_volt_prev; + chip->qc3p_batt_therm_volt = chip->qc3p_request_volt_prev; + chip->qc3p_ibus_max_volt = chip->qc3p_request_volt_prev; + chip->qc3p_sys_therm_cooling = false; + chip->qc3p_sys_therm_force_pmic_chrg = false; + chip->qc3p_recovery_pmic_chrg = false; + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_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], + QC3P_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->qc3p_request_volt = SWITCH_CHARGER_QC3P_VOLT; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_LOOP); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_STATE_SW_LOOP: + if (chip->qc3p_active + && 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->qc3p_recovery_pmic_chrg + && !chip->qc3p_sys_therm_force_pmic_chrg + && batt_soc < QC3P_CP_CHRG_SOC_LIMIT) { + mmi_chrg_info(chip, "Enter CP, the reason is : " + "qc3p support %d, " + "vbatt %duV, chrg step %d\n", + chip->qc3p_active, + vbatt_volt, chrg_step->pres_chrg_step); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CHRG_PUMP_ENTRY); + heartbeat_dely_ms = QC3P_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 = QC3P_HEARTBEAT_lOOP_WAIT_MS; + } + break; + case PM_QC3P_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_qc3p_sm_move_state(chip, + PM_QC3P_STATE_DULE_CP_ENTRY); + } else + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SINGLE_CP_ENTRY); + } else { + mmi_chrg_info(chip,"CP slave isn't exist !\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_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; + } + + /*Initial setup qc3p request power by the battery voltage*/ + chip->qc3p_request_volt = (2 * vbatt_volt) % 20000; + chip->qc3p_request_volt = 2 * vbatt_volt - chip->qc3p_request_volt + + chip->qc3p_volt_comp; + mmi_chrg_info(chip,"qc3p init , volt %dmV, volt comp %dmv\n", + chip->qc3p_request_volt, chip->qc3p_volt_comp); + chrg_policy_error_clear(chip, chrg_list); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_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_qc3p_sm_move_state(chip, PM_QC3P_STATE_TUNNING_VOLT); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_QC3P_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_qc3p_sm_move_state(chip, PM_QC3P_STATE_TUNNING_VOLT); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_QC3P_STATE_TUNNING_VOLT: + heartbeat_dely_ms = QC3P_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 + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, " + "Enter into SW directly\n"); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + } else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + chip->qc3p_request_volt -= chip->qc3p_volt_steps; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Duing the volt going up process, " + "the chrg step was changed," + "stop increase qc3p volt and" + " Enter into CC stage as soon!\n"); + } else if (chip->qc3p_request_volt + chip->qc3p_volt_steps + <= chip->qc3p_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->qc3p_request_volt += chip->qc3p_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase qc3p volt %d\n", + chip->qc3p_request_volt); + heartbeat_dely_ms = QC3P_HEARTBEAT_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into CC loop stage !\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_CC_LOOP); + } + + if (ibatt_curr > qc3p_batt_curr_roof) { + qc3p_batt_curr_roof = ibatt_curr; + } + if (ibatt_curr < qc3p_batt_curr_roof) + qc3p_constant_power_cnt++; + else + qc3p_constant_power_cnt = 0; + + if (qc3p_constant_power_cnt > QC3P_CONT_PWR_CNT) { + mmi_chrg_info(chip,"QC3P adapter was ready in constant power state, " + "Enter into CC loop stage !\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_CC_LOOP); + } + mmi_chrg_info(chip, "qc3p_constant_power_cnt %d, " + "qc3p_batt_curr_roof %d\n", + qc3p_constant_power_cnt, + qc3p_batt_curr_roof); + break; + case PM_QC3P_STATE_CP_CC_LOOP: + heartbeat_dely_ms = QC3P_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 + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (!chip->qc3p_sys_therm_cooling + && !chip->qc3p_batt_therm_cooling + && qc3p_constant_power_cnt <= QC3P_CONT_PWR_CNT) { + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->qc3p_request_volt < chip->qc3p_volt_max + && qc3p_chrg_cc_power_tunning_cnt >= + QC3P_CC_POWER_COUNT) { + if (chip->qc3p_request_volt + chip->qc3p_volt_steps + < chip->qc3p_volt_max) { + chip->qc3p_request_volt += + chip->qc3p_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request volt increass %duV\n ", + chip->qc3p_request_volt); + } + qc3p_chrg_cc_power_tunning_cnt = 0; + }else if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->qc3p_request_volt < chip->qc3p_volt_max) { + qc3p_chrg_cc_power_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC tunning cnt %d\n", + qc3p_chrg_cc_power_tunning_cnt); + } else if (ibatt_curr > chrg_step->chrg_step_cc_curr + + QC3P_CC_CURR_DEBOUNCE) { + chip->qc3p_request_volt -= chip->qc3p_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->qc3p_request_volt); + } else + qc3p_chrg_cc_power_tunning_cnt = 0; + + if(ibatt_curr < chrg_step->chrg_step_cc_curr) + heartbeat_dely_ms = QC3P_HEARTBEAT_SHORT_DELAY_MS; + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (qc3p_chrg_cv_taper_tunning_cnt > + QC3P_CV_TAPPER_COUNT) { + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_CV_LOOP); + qc3p_chrg_cv_taper_tunning_cnt = 0; + qc3p_chrg_cv_delta_volt = QC3P_CV_DELTA_VOLT; + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } else { + qc3p_chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + qc3p_chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + qc3p_chrg_cv_taper_tunning_cnt = 0; + + break; + case PM_QC3P_STATE_CP_CV_LOOP: + heartbeat_dely_ms = QC3P_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 + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + 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 (qc3p_chrg_cv_taper_tunning_cnt >= QC3P_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_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_QUIT); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } else { + if (mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt)) { + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + mmi_chrg_info(chip,"Jump to next chrg step\n"); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CP_CC_LOOP); + } else { + mmi_chrg_info(chip,"Can't find next chrg step\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_QUIT); + } + } + qc3p_chrg_cv_taper_tunning_cnt = 0; + } else { + qc3p_chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, "chrg cv taper cnt ++, %d\n", + qc3p_chrg_cv_taper_tunning_cnt); + } + + }else if (!chip->qc3p_sys_therm_cooling + && !chip->qc3p_batt_therm_cooling) { + + qc3p_chrg_cv_taper_tunning_cnt = 0; + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (qc3p_chrg_cv_delta_volt > 20000) + chip->qc3p_request_volt -= qc3p_chrg_cv_delta_volt; + else + chip->qc3p_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->qc3p_request_volt, qc3p_chrg_cv_delta_volt); + } else if (vbatt_volt < chrg_step->chrg_step_cv_volt - 10000) { + qc3p_chrg_cv_delta_volt -= 20000; + chip->qc3p_request_volt += 20000; + mmi_chrg_info(chip, + "For keeping CV stage, increase volt %dmV, " + "cv delta volt %dmV\n", + chip->qc3p_request_volt, qc3p_chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep qc3p power, volt %dmV\n", + chip->qc3p_request_volt); + } + + }else { + /*In this case, qc3p_sys_therm_cooling or qc3p_batt_therm_cooling is ture*/ + + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (qc3p_chrg_cv_delta_volt > 20000) + chip->qc3p_request_volt -= qc3p_chrg_cv_delta_volt; + else + chip->qc3p_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->qc3p_request_volt, qc3p_chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep qc3p power, volt %dmV\n", + chip->qc3p_request_volt); + } + } + + if (chrg_list->cp_slave) { + if (qc3p_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) + qc3p_quit_slave_chrg_cnt++; + else + qc3p_quit_slave_chrg_cnt = 0; + } + break; + case PM_QC3P_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->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_RECOVERY_SW); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_STATE_RECOVERY_SW: + heartbeat_dely_ms = QC3P_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], + QC3P_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->qc3p_recovery_pmic_chrg = true; + qc3p_chrg_cv_taper_tunning_cnt = 0; + } + + 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->qc3p_request_volt -= QC3P_CV_DELTA_VOLT; + + mmi_chrg_dbg(chip, PR_MOTO, "Reduce qc3p volt %dmV\n ", + chip->qc3p_request_volt); + heartbeat_dely_ms = QC3P_HEARTBEAT_TUNNING_MS; + qc3p_chrg_cv_taper_tunning_cnt = 0; + } else { + qc3p_chrg_cv_taper_tunning_cnt++; + + } + + if (qc3p_chrg_cv_taper_tunning_cnt > QC3P_CV_TAPPER_COUNT){ + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_LOOP); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_QC3P_STATE_STOP_CHARGE: + heartbeat_dely_ms = QC3P_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], + QC3P_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_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + chip->qc3p_request_volt = SWITCH_CHARGER_QC3P_VOLT; + break; + } + +schedule: + + chip->qc3p_target_volt = min(chip->qc3p_request_volt, chip->qc3p_volt_max); + + if (chip->system_thermal_level == 0) { + chip->qc3p_sys_therm_cooling= false; + chip->qc3p_sys_therm_force_pmic_chrg = false; + } else if ((chip->system_thermal_level == chip->thermal_levels - 1) + && !chip->qc3p_sys_therm_force_pmic_chrg) { + chip->qc3p_sys_therm_cooling = true; + chip->qc3p_sys_therm_force_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_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 && + (qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP || + qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP)) { + + mmi_chrg_dbg(chip, PR_MOTO, "Thermal level is %d\n", + chip->system_thermal_level); + if (!chip->qc3p_sys_therm_cooling) { + chip->qc3p_sys_therm_cooling = true; + chip->qc3p_sys_therm_volt = chip->qc3p_request_volt_prev; + } + + if (ibatt_curr > + chip->thermal_mitigation[chip->system_thermal_level] + QC3P_CC_CURR_DEBOUNCE) { + if (ibatt_curr - chip->thermal_mitigation[chip->system_thermal_level] > 300000) + chip->qc3p_sys_therm_volt -= chip->qc3p_volt_steps *3; + else + chip->qc3p_sys_therm_volt -= chip->qc3p_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, decrease qc3 volt %d\n", + chip->qc3p_sys_therm_volt); + } else if ((ibatt_curr < + chip->thermal_mitigation[chip->system_thermal_level])) { + if (chip->thermal_mitigation[chip->system_thermal_level] - ibatt_curr > 300000) + chip->qc3p_sys_therm_volt += chip->qc3p_volt_steps *3; + else + chip->qc3p_sys_therm_volt += chip->qc3p_volt_steps; + chip->qc3p_sys_therm_volt = min(chip->qc3p_volt_max, chip->qc3p_sys_therm_volt); + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, increase qc3 volt %d\n", + chip->qc3p_sys_therm_volt); + } + + heartbeat_dely_ms = QC3P_HEARTBEAT_TUNNING_MS; + } else if (chip->system_thermal_level > 0 && + chip->system_thermal_level != chip->thermal_levels - 1 && + qc3p_sm_state == PM_QC3P_STATE_SW_LOOP && + chip->qc3p_sys_therm_force_pmic_chrg) { + mmi_chrg_dbg(chip, PR_MOTO, "Try to recovery charger pump!\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } + + if (chip->qc3p_sys_therm_cooling) + chip->qc3p_target_volt = min(chip->qc3p_target_volt, chip->qc3p_sys_therm_volt); + + if (chip->qc3p_batt_therm_cooling && !chip->qc3p_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_QC3P_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt= 0; + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_volt = (2 * vbatt_volt) % 20000; + cooling_volt = 2 * vbatt_volt - cooling_volt + + chip->qc3p_volt_comp; + if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->qc3p_batt_therm_volt > cooling_volt) { + chip->qc3p_batt_therm_volt -= QC3P_COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "qc3p request volt %dmA, " + "battery temp %d\n", + chip->qc3p_batt_therm_volt, batt_temp); + } else { + if (chip->qc3p_batt_therm_cooling_cnt > QC3P_COOLING_MAX_CNT) { + mmi_chrg_info(chip, "Do chrg power cooling failed" + "qc3p_batt_therm_volt %dmV" + "battery temp %d, " + "Restart PM_STATE_ENTRY !\n", + chip->qc3p_batt_therm_volt, + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_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->qc3p_batt_therm_cooling); + chip->qc3p_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 (qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP) { + mmi_chrg_info(chip,"Jump into CURR tunning" + "for chrg power poerformance!\n"); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_TUNNING_VOLT); + } + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + } + heartbeat_dely_ms = QC3P_HEARTBEAT_lOOP_WAIT_MS; + } + + if (chip->qc3p_batt_therm_cooling) + chip->qc3p_target_volt = min(chip->qc3p_target_volt, chip->qc3p_batt_therm_volt); + + ibus_total_ma = chrg_list->chrg_dev[PMIC_SW]->charger_data.ibus_curr / 1000 + chrg_list->chrg_dev[CP_MASTER]->charger_data.ibus_curr; + if (chrg_list->cp_slave && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + ibus_total_ma += chrg_list->chrg_dev[CP_SLAVE]->charger_data.ibus_curr; + } + + if(ibus_total_ma > chip->qc3p_max_ibus_ma) { + chip->qc3p_ibus_max_volt -= 2 * chip->qc3p_volt_steps; + chip->qc3p_target_volt = min(chip->qc3p_target_volt, chip->qc3p_ibus_max_volt); + chip->qc3p_request_volt = chip->qc3p_target_volt; + mmi_chrg_info(chip,"Limit ibus current: ibus is %dmA, max allow is %dmA, ibus volt is %duV, target volt is %duV \n", + ibus_total_ma, chip->qc3p_max_ibus_ma, chip->qc3p_ibus_max_volt, chip->qc3p_target_volt); + } + + 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", + pm_qc3p_state_str[qc3p_sm_state], + batt_soc, batt_temp, + ibatt_curr, vbatt_volt, + chip->system_thermal_level, + pmic_sys_therm_level, + chip->qc3p_sys_therm_cooling, + chip->qc3p_batt_therm_cooling, + chip->qc3p_sys_therm_force_pmic_chrg, + chip->qc3p_recovery_pmic_chrg); + + mmi_chrg_dbg(chip, PR_MOTO, "qc3p request volt %dmV, " + "qc3p target volt %dmV, " + "sys therm volt %dmV, " + "batt therm volt %dmV," + "qc3p_ibus_max_volt %dmV," + "qc3p_max_ibus_ma %dmA," + "ibus total %dmA\n", + chip->qc3p_request_volt, + chip->qc3p_target_volt, + chip->qc3p_sys_therm_volt, + chip->qc3p_batt_therm_volt, + chip->qc3p_ibus_max_volt, + chip->qc3p_max_ibus_ma, + ibus_total_ma); + + if (chip->qc3p_target_volt < SWITCH_CHARGER_QC3P_VOLT) { + + if (qc3p_sm_state == PM_QC3P_STATE_TUNNING_VOLT + || qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP + || qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP) { + + mmi_chrg_err(chip, "%s, wrong qc3p voltage or current , " + "request qc3p voltage %d, ", + pm_qc3p_state_str[qc3p_sm_state], + chip->qc3p_target_volt); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } + goto skip_pd_select; + } + + rc = mmi_qc3p_set_vbus_voltage(chip, chip->qc3p_target_volt / 1000); + if (rc > 0) { + chip->qc3p_request_volt_prev = chip->qc3p_target_volt; + chip->qc3p_ibus_max_volt = chip->qc3p_target_volt; + } +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_qc3p_chrg_sm_work, + msecs_to_jiffies(heartbeat_dely_ms)); + } else { + mmi_qc3p_chrg_policy_clear(chip); + chip->sm_work_running = false; + chip->qc3p_active = false; + mmi_chrg_err(chip, "exit sm work\n"); + } + + return; +} + + diff --git a/drivers/power/mmi_discrete_turbo_charger/qpnp_pmic_charger.c b/drivers/power/mmi_discrete_turbo_charger/qpnp_pmic_charger.c new file mode 100644 index 000000000000..7a831e37cf6d --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/qpnp_pmic_charger.c @@ -0,0 +1,222 @@ +/* + * 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" +#include "mmi_charger_core_iio.h" + +static int qpnp_pmic_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + int rc = 0; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_write_iio_chan(chip, SMB5_CHARGING_ENABLED, en); + if (!rc) { + chrg->charger_enabled = en; + } else { + chrg->charger_enabled = false; + } + + return rc; +} + +static int qpnp_pmic_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + int rc = 0; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, SMB5_CHARGING_ENABLED, &value); + if (!rc) { + chrg->charger_enabled = !!value; + } 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_get_ibus(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + static struct power_supply *usb_psy; + union power_supply_propval prop = {0,}; + + if (!usb_psy) + usb_psy = power_supply_get_by_name("usb"); + if (!usb_psy) + return -ENODEV; + + rc = power_supply_get_property(usb_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 = 0; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, SMB5_HW_CURRENT_MAX, &value); + if (!rc) + chrg->input_curr_setted = value; + *uA = chrg->input_curr_setted; + + return rc; +} + +static int qpnp_pmic_update_charger_status(struct mmi_charger_device *chrg) +{ + int rc; + bool value = 0; + static struct power_supply *usb_psy = NULL; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + if (!usb_psy) + usb_psy = power_supply_get_by_name("usb"); + if (!usb_psy) + return -ENODEV; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibus_curr= prop.intval; + + 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_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 = qpnp_pmic_is_charging_enabled(chrg, &value); + if (!rc) + chrg->charger_enabled= !!value; + + 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, + .get_input_current = qpnp_pmic_get_ibus, + .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, +};