blob: 665e03bd4f4bfcdfa53e7ac2ba87cc70ab9a8940 [file] [log] [blame]
/*
* muic_max77865_hv.c
*
* Copyright (C) 2018 Samsung Electronics
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define pr_fmt(fmt) "[MUIC_HV] " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/mfd/max77865.h>
#include <linux/mfd/max77865-private.h>
/* MUIC header file */
#include <linux/muic/muic.h>
#include "muic-internal.h"
#include "muic_state.h"
#include "muic_i2c.h"
#include "muic_vps.h"
#include "muic_apis.h"
#include "muic_regmap.h"
#include "muic_hv.h"
#include "muic_hv_max77865.h"
#if defined(CONFIG_MUIC_NOTIFIER)
#include <linux/muic/muic_notifier.h>
#endif /* CONFIG_MUIC_NOTIFIER */
#if defined(CONFIG_MUIC_SUPPORT_CCIC)
#include "muic_ccic.h"
#include <linux/ccic/s2mm005.h>
#endif
static bool debug_en_checklist = false;
/* temp function for function pointer (TODO) */
enum act_function_num {
FUNC_TA_TO_PREPARE = 0,
FUNC_PREPARE_TO_PREPARE_DUPLI,
FUNC_PREPARE_TO_AFC_5V,
FUNC_PREPARE_TO_QC_PREPARE,
FUNC_PREPARE_DUPLI_TO_PREPARE_DUPLI,
FUNC_PREPARE_DUPLI_TO_AFC_5V,
FUNC_PREPARE_DUPLI_TO_AFC_ERR_V,
FUNC_PREPARE_DUPLI_TO_AFC_9V,
#if defined(CONFIG_MUIC_HV_12V)
FUNC_PREPARE_DUPLI_TO_AFC_12V,
#endif
FUNC_PREPARE_DUPLI_TO_QC_PREPARE,
FUNC_AFC_5V_TO_AFC_5V_DUPLI,
FUNC_AFC_5V_TO_AFC_ERR_V,
FUNC_AFC_5V_TO_AFC_9V,
#if defined(CONFIG_MUIC_HV_12V)
FUNC_AFC_5V_TO_AFC_12V,
#endif
FUNC_AFC_5V_TO_QC_PREPARE,
FUNC_AFC_5V_DUPLI_TO_AFC_5V_DUPLI,
FUNC_AFC_5V_DUPLI_TO_AFC_ERR_V,
FUNC_AFC_5V_DUPLI_TO_AFC_9V,
#if defined(CONFIG_MUIC_HV_12V)
FUNC_AFC_5V_DUPLI_TO_AFC_12V,
#endif
FUNC_AFC_5V_DUPLI_TO_QC_PREPARE,
FUNC_AFC_ERR_V_TO_AFC_ERR_V_DUPLI,
FUNC_AFC_ERR_V_TO_AFC_5V,
FUNC_AFC_ERR_V_TO_AFC_9V,
FUNC_AFC_ERR_V_TO_QC_PREPARE,
#if defined(CONFIG_MUIC_HV_12V)
FUNC_AFC_ERR_V_TO_AFC_12V,
#endif
FUNC_AFC_ERR_V_DUPLI_TO_AFC_ERR_V_DUPLI,
FUNC_AFC_ERR_V_DUPLI_TO_AFC_5V,
FUNC_AFC_ERR_V_DUPLI_TO_AFC_9V,
#if defined(CONFIG_MUIC_HV_12V)
FUNC_AFC_ERR_V_DUPLI_TO_AFC_12V,
#endif
FUNC_AFC_ERR_V_DUPLI_TO_QC_PREPARE,
FUNC_AFC_9V_TO_AFC_9V_DUPLI,
FUNC_AFC_9V_TO_AFC_ERR_V,
FUNC_AFC_9V_TO_AFC_5V,
#if defined(CONFIG_MUIC_HV_12V)
FUNC_AFC_9V_TO_AFC_12V,
#endif
FUNC_AFC_9V_TO_QC_PREPARE,
FUNC_AFC_9V_DUPLI_TO_AFC_ERR_V,
FUNC_AFC_9V_DUPLI_TO_AFC_5V,
#if defined(CONFIG_MUIC_HV_12V)
FUNC_AFC_9V_DUPLI_TO_AFC_12V,
#endif
FUNC_AFC_9V_DUPLI_TO_AFC_9V_DUPLI,
FUNC_AFC_9V_DUPLI_TO_QC_PREPARE,
#if defined(CONFIG_MUIC_HV_12V)
FUNC_AFC_12V_TO_AFC_12V_DUPLI,
FUNC_AFC_12V_TO_AFC_ERR_V,
FUNC_AFC_12V_TO_AFC_5V,
FUNC_AFC_12V_TO_AFC_9V,
FUNC_AFC_12V_TO_QC_PREPARE,
FUNC_AFC_12V_DUPLI_TO_AFC_ERR_V,
FUNC_AFC_12V_DUPLI_TO_AFC_5V,
FUNC_AFC_12V_DUPLI_TO_AFC_9V,
FUNC_AFC_12V_DUPLI_TO_AFC_12V_DUPLI,
FUNC_AFC_12V_DUPLI_TO_QC_PREPARE,
#endif
FUNC_QC_PREPARE_TO_QC_5V,
FUNC_QC_PREPARE_TO_QC_9V,
FUNC_QC_5V_TO_QC_9V,
FUNC_QC_9V_TO_QC_5V,
};
static struct hv_data hv_afc;
/* afc_condition_checklist[ATTACHED_DEV_TA_MUIC] */
muic_afc_data_t ta_to_prepare = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC,
.afc_name = "AFC charger Prepare",
.afc_irq = MUIC_AFC_IRQ_VDNMON,
.bccontrol2_dpdnman = DPDNVDEN_ENABLE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_LOW,
.function_num = FUNC_TA_TO_PREPARE,
.next = &ta_to_prepare,
};
/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC] */
muic_afc_data_t prepare_to_qc_prepare = {
.new_dev = ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
.afc_name = "QC charger Prepare",
.afc_irq = MUIC_AFC_IRQ_MPNACK,
.bccontrol2_dpdnman = DPDNVDEN_DONTCARE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_PREPARE_TO_QC_PREPARE,
.next = &prepare_to_qc_prepare,
};
muic_afc_data_t prepare_to_afc_5v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
.afc_name = "AFC charger 5V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_PREPARE_TO_AFC_5V,
.next = &prepare_to_qc_prepare,
};
muic_afc_data_t prepare_to_prepare_dupli = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC,
.afc_name = "AFC charger prepare (mrxrdy)",
.afc_irq = MUIC_AFC_IRQ_MRXRDY,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_PREPARE_TO_PREPARE_DUPLI,
.next = &prepare_to_afc_5v,
};
/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC] */
muic_afc_data_t prepare_dupli_to_qc_prepare = {
.new_dev = ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
.afc_name = "QC charger Prepare",
.afc_irq = MUIC_AFC_IRQ_MPNACK,
.bccontrol2_dpdnman = DPDNVDEN_DONTCARE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_PREPARE_DUPLI_TO_QC_PREPARE,
.next = &prepare_dupli_to_qc_prepare,
};
muic_afc_data_t prepare_dupli_to_prepare_dupli = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC,
.afc_name = "AFC charger prepare (mrxrdy)",
.afc_irq = MUIC_AFC_IRQ_MRXRDY,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_PREPARE_DUPLI_TO_PREPARE_DUPLI,
.next = &prepare_dupli_to_qc_prepare,
};
#if defined(CONFIG_MUIC_HV_12V)
muic_afc_data_t prepare_dupli_to_afc_12v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_12V_MUIC,
.afc_name = "AFC charger 12V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_12V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_PREPARE_DUPLI_TO_AFC_12V,
.next = &prepare_dupli_to_prepare_dupli,
};
#endif
muic_afc_data_t prepare_dupli_to_afc_9v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
.afc_name = "AFC charger 9V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_9V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_PREPARE_DUPLI_TO_AFC_9V,
#if defined(CONFIG_MUIC_HV_12V)
.next = &prepare_dupli_to_afc_12v,
#else
.next = &prepare_dupli_to_prepare_dupli,
#endif
};
muic_afc_data_t prepare_dupli_to_afc_err_v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
.afc_name = "AFC charger ERR V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_ERR_V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_PREPARE_DUPLI_TO_AFC_ERR_V,
.next = &prepare_dupli_to_afc_9v,
};
muic_afc_data_t prepare_dupli_to_afc_5v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
.afc_name = "AFC charger 5V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_PREPARE_DUPLI_TO_AFC_5V,
.next = &prepare_dupli_to_afc_err_v,
};
/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_5V_MUIC] */
muic_afc_data_t afc_5v_to_qc_prepare = {
.new_dev = ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
.afc_name = "QC charger Prepare",
.afc_irq = MUIC_AFC_IRQ_MPNACK,
.bccontrol2_dpdnman = DPDNVDEN_DONTCARE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_5V_TO_QC_PREPARE,
.next = &afc_5v_to_qc_prepare,
};
#if defined(CONFIG_MUIC_HV_12V)
muic_afc_data_t afc_5v_to_afc_12v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_12V_MUIC,
.afc_name = "AFC charger 12V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_12V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_5V_TO_AFC_12V,
.next = &afc_5v_to_qc_prepare,
};
#endif
muic_afc_data_t afc_5v_to_afc_9v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
.afc_name = "AFC charger 9V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_9V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_5V_TO_AFC_9V,
#if defined(CONFIG_MUIC_HV_12V)
.next = &afc_5v_to_afc_12v,
#else
.next = &afc_5v_to_qc_prepare,
#endif
};
muic_afc_data_t afc_5v_to_afc_err_v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
.afc_name = "AFC charger ERR V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_ERR_V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_5V_TO_AFC_ERR_V,
.next = &afc_5v_to_afc_9v,
};
muic_afc_data_t afc_5v_to_afc_5v_dupli = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC,
.afc_name = "AFC charger 5V (mrxrdy)",
.afc_irq = MUIC_AFC_IRQ_MRXRDY,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_5V_TO_AFC_5V_DUPLI,
.next = &afc_5v_to_afc_err_v,
};
/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC] */
muic_afc_data_t afc_5v_dupli_to_qc_prepare = {
.new_dev = ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
.afc_name = "QC charger Prepare",
.afc_irq = MUIC_AFC_IRQ_MPNACK,
.bccontrol2_dpdnman = DPDNVDEN_DONTCARE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_5V_DUPLI_TO_QC_PREPARE,
.next = &afc_5v_dupli_to_qc_prepare,
};
#if defined(CONFIG_MUIC_HV_12V)
muic_afc_data_t afc_5v_dupli_to_afc_12v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_12V_MUIC,
.afc_name = "AFC charger 12V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_12V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_5V_DUPLI_TO_AFC_12V,
.next = &afc_5v_dupli_to_qc_prepare,
};
muic_afc_data_t afc_5v_dupli_to_afc_5v_dupli = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC,
.afc_name = "AFC charger 5V (mrxrdy)",
.afc_irq = MUIC_AFC_IRQ_MRXRDY,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_5V_DUPLI_TO_AFC_5V_DUPLI,
.next = &afc_5v_dupli_to_afc_12v,
};
#else
muic_afc_data_t afc_5v_dupli_to_afc_5v_dupli = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC,
.afc_name = "AFC charger 5V (mrxrdy)",
.afc_irq = MUIC_AFC_IRQ_MRXRDY,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_5V_DUPLI_TO_AFC_5V_DUPLI,
.next = &afc_5v_dupli_to_qc_prepare,
};
#endif
muic_afc_data_t afc_5v_dupli_to_afc_9v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
.afc_name = "AFC charger 9V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_9V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_5V_DUPLI_TO_AFC_9V,
.next = &afc_5v_dupli_to_afc_5v_dupli,
};
muic_afc_data_t afc_5v_dupli_to_afc_err_v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
.afc_name = "AFC charger ERR V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_ERR_V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_5V_DUPLI_TO_AFC_ERR_V,
.next = &afc_5v_dupli_to_afc_9v,
};
/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC] */
muic_afc_data_t afc_err_v_to_qc_prepare = {
.new_dev = ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
.afc_name = "QC charger Prepare",
.afc_irq = MUIC_AFC_IRQ_MPNACK,
.bccontrol2_dpdnman = DPDNVDEN_DONTCARE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_ERR_V_TO_QC_PREPARE,
.next = &afc_err_v_to_qc_prepare,
};
#if defined(CONFIG_MUIC_HV_12V)
muic_afc_data_t afc_err_v_to_afc_12v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_12V_MUIC,
.afc_name = "AFC charger 12V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_12V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_ERR_V_TO_AFC_12V,
.next = &afc_err_v_to_qc_prepare,
};
#endif
muic_afc_data_t afc_err_v_to_afc_5v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
.afc_name = "AFC charger 5V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_ERR_V_TO_AFC_5V,
#if defined(CONFIG_MUIC_HV_12V)
.next = &afc_err_v_to_afc_12v,
#else
.next = &afc_err_v_to_qc_prepare,
#endif
};
muic_afc_data_t afc_err_v_to_afc_9v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
.afc_name = "AFC charger 9V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_9V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_ERR_V_TO_AFC_9V,
.next = &afc_err_v_to_afc_5v,
};
muic_afc_data_t afc_err_v_to_afc_err_v_dupli = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC,
.afc_name = "AFC charger ERR V (mrxrdy)",
.afc_irq = MUIC_AFC_IRQ_MRXRDY,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_ERR_V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_ERR_V_TO_AFC_ERR_V_DUPLI,
.next = &afc_err_v_to_afc_9v,
};
/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC] */
muic_afc_data_t afc_err_v_dupli_to_qc_prepare = {
.new_dev = ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
.afc_name = "QC charger Prepare",
.afc_irq = MUIC_AFC_IRQ_MPNACK,
.bccontrol2_dpdnman = DPDNVDEN_DONTCARE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_ERR_V_DUPLI_TO_QC_PREPARE,
.next = &afc_err_v_dupli_to_qc_prepare,
};
#if defined(CONFIG_MUIC_HV_12V)
muic_afc_data_t afc_err_v_dupli_to_afc_12v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_12V_MUIC,
.afc_name = "AFC charger 12V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_12V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_ERR_V_DUPLI_TO_AFC_12V,
.next = &afc_err_v_dupli_to_qc_prepare,
};
#endif
muic_afc_data_t afc_err_v_dupli_to_afc_5v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
.afc_name = "AFC charger 5V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_ERR_V_DUPLI_TO_AFC_5V,
#if defined(CONFIG_MUIC_HV_12V)
.next = &afc_err_v_dupli_to_afc_12v,
#else
.next = &afc_err_v_dupli_to_qc_prepare,
#endif
};
muic_afc_data_t afc_err_v_dupli_to_afc_9v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
.afc_name = "AFC charger 9V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_9V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_ERR_V_DUPLI_TO_AFC_9V,
.next = &afc_err_v_dupli_to_afc_5v,
};
muic_afc_data_t afc_err_v_dupli_to_afc_err_v_dupli = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC,
.afc_name = "AFC charger ERR V (mrxrdy)",
.afc_irq = MUIC_AFC_IRQ_MRXRDY,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_ERR_V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_ERR_V_DUPLI_TO_AFC_ERR_V_DUPLI,
.next = &afc_err_v_dupli_to_afc_9v,
};
/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_9V_MUIC] */
muic_afc_data_t afc_9v_to_qc_prepare = {
.new_dev = ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
.afc_name = "QC charger Prepare",
.afc_irq = MUIC_AFC_IRQ_MPNACK,
.bccontrol2_dpdnman = DPDNVDEN_DONTCARE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_9V_TO_QC_PREPARE,
.next = &afc_9v_to_qc_prepare,
};
muic_afc_data_t afc_9v_to_afc_err_v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
.afc_name = "AFC charger ERR V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_ERR_V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_9V_TO_AFC_ERR_V,
.next = &afc_9v_to_qc_prepare,
};
#if defined(CONFIG_MUIC_HV_12V)
muic_afc_data_t afc_9v_to_afc_12v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_12V_MUIC,
.afc_name = "AFC charger 12V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_12V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_9V_TO_AFC_12V,
.next = &afc_9v_to_afc_err_v,
};
#endif
muic_afc_data_t afc_9v_to_afc_5v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
.afc_name = "AFC charger 5V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_9V_TO_AFC_5V,
#if defined(CONFIG_MUIC_HV_12V)
.next = &afc_9v_to_afc_12v,
#else
.next = &afc_9v_to_afc_err_v,
#endif
};
muic_afc_data_t afc_9v_to_afc_9v_dupli = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC,
.afc_name = "AFC charger 9V (mrxrdy)",
.afc_irq = MUIC_AFC_IRQ_MRXRDY,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_9V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_9V_TO_AFC_9V_DUPLI,
.next = &afc_9v_to_afc_5v,
};
/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC] */
muic_afc_data_t afc_9v_dupli_to_qc_prepare = {
.new_dev = ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
.afc_name = "QC charger Prepare",
.afc_irq = MUIC_AFC_IRQ_MPNACK,
.bccontrol2_dpdnman = DPDNVDEN_DONTCARE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_9V_DUPLI_TO_QC_PREPARE,
.next = &afc_9v_dupli_to_qc_prepare,
};
muic_afc_data_t afc_9v_dupli_to_afc_err_v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
.afc_name = "AFC charger ERR V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
#if defined(CONFIG_MUIC_HV_12V)
.gpstatus_vbadc = VBADC_DONTCARE, // 12V should be error
#else
.gpstatus_vbadc = VBADC_AFC_ERR_V,
#endif
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_9V_DUPLI_TO_AFC_ERR_V,
.next = &afc_9v_dupli_to_qc_prepare,
};
muic_afc_data_t afc_9v_dupli_to_afc_5v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
.afc_name = "AFC charger 5V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_9V_DUPLI_TO_AFC_5V,
.next = &afc_9v_dupli_to_afc_err_v,
};
muic_afc_data_t afc_9v_dupli_to_afc_9v_dupli = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC,
.afc_name = "AFC charger 9V (mrxrdy)",
.afc_irq = MUIC_AFC_IRQ_MRXRDY,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_9V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_9V_DUPLI_TO_AFC_9V_DUPLI,
.next = &afc_9v_dupli_to_afc_5v,
};
#if defined(CONFIG_MUIC_HV_12V)
/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_12V_MUIC] */
muic_afc_data_t afc_12v_to_qc_prepare = {
.new_dev = ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
.afc_name = "QC charger Prepare",
.afc_irq = MUIC_AFC_IRQ_MPNACK,
.bccontrol2_dpdnman = DPDNVDEN_DONTCARE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_12V_TO_QC_PREPARE,
.next = &afc_12v_to_qc_prepare,
};
muic_afc_data_t afc_12v_to_afc_9v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
.afc_name = "AFC charger 9V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_9V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_12V_TO_AFC_9V,
.next = &afc_12v_to_qc_prepare,
};
muic_afc_data_t afc_12v_to_afc_5v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
.afc_name = "AFC charger 5V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_12V_TO_AFC_5V,
.next = &afc_12v_to_afc_9v,
};
muic_afc_data_t afc_12v_to_afc_err_v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
.afc_name = "AFC charger ERR V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_ERR_V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_12V_TO_AFC_ERR_V,
.next = &afc_12v_to_afc_5v,
};
muic_afc_data_t afc_12v_to_afc_12v_dupli = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC,
.afc_name = "AFC charger 12V (mrxrdy)",
.afc_irq = MUIC_AFC_IRQ_MRXRDY,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_12V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_12V_TO_AFC_12V_DUPLI,
.next = &afc_12v_to_afc_err_v,
};
/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC] */
muic_afc_data_t afc_12v_dupli_to_qc_prepare = {
.new_dev = ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
.afc_name = "QC charger Prepare",
.afc_irq = MUIC_AFC_IRQ_MPNACK,
.bccontrol2_dpdnman = DPDNVDEN_DONTCARE,
.gpstatus_vbadc = VBADC_DONTCARE,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_12V_DUPLI_TO_QC_PREPARE,
.next = &afc_12v_dupli_to_qc_prepare,
};
muic_afc_data_t afc_12v_dupli_to_afc_12v_dupli = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC,
.afc_name = "AFC charger 12V (mrxrdy)",
.afc_irq = MUIC_AFC_IRQ_MRXRDY,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_12V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_12V_DUPLI_TO_AFC_12V_DUPLI,
.next = &afc_12v_dupli_to_qc_prepare,
};
muic_afc_data_t afc_12v_dupli_to_afc_9v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
.afc_name = "AFC charger 9V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_9V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_12V_DUPLI_TO_AFC_9V,
.next = &afc_12v_dupli_to_afc_12v_dupli,
};
muic_afc_data_t afc_12v_dupli_to_afc_5v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
.afc_name = "AFC charger 5V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_12V_DUPLI_TO_AFC_5V,
.next = &afc_12v_dupli_to_afc_9v,
};
muic_afc_data_t afc_12v_dupli_to_afc_err_v = {
.new_dev = ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
.afc_name = "AFC charger ERR V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_DISABLE,
.gpstatus_vbadc = VBADC_AFC_ERR_V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_12V_DUPLI_TO_AFC_ERR_V,
.next = &afc_12v_dupli_to_afc_5v,
};
#endif
/* afc_condition_checklist[ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC] */
muic_afc_data_t qc_prepare_to_qc_9v = {
.new_dev = ATTACHED_DEV_QC_CHARGER_9V_MUIC,
.afc_name = "QC charger 9V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_ENABLE,
.gpstatus_vbadc = VBADC_QC_9V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_QC_PREPARE_TO_QC_9V,
.next = &qc_prepare_to_qc_9v,
};
muic_afc_data_t qc_prepare_to_qc_5v = {
.new_dev = ATTACHED_DEV_QC_CHARGER_5V_MUIC,
.afc_name = "QC charger 5V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_ENABLE,
.gpstatus_vbadc = VBADC_QC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_QC_PREPARE_TO_QC_5V,
.next = &qc_prepare_to_qc_9v,
};
/* afc_condition_checklist[ATTACHED_DEV_QC_CHARGER_5V_MUIC] */
muic_afc_data_t qc_5v_to_qc_9v = {
.new_dev = ATTACHED_DEV_QC_CHARGER_9V_MUIC,
.afc_name = "QC charger 9V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_ENABLE,
.gpstatus_vbadc = VBADC_QC_9V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_QC_5V_TO_QC_9V,
.next = &qc_5v_to_qc_9v,
};
/* afc_condition_checklist[ATTACHED_DEV_QC_CHARGER_9V_MUIC] */
muic_afc_data_t qc_9v_to_qc_5v = {
.new_dev = ATTACHED_DEV_QC_CHARGER_5V_MUIC,
.afc_name = "QC charger 5V",
.afc_irq = MUIC_AFC_IRQ_VBADC,
.bccontrol2_dpdnman = DPDNVDEN_ENABLE,
.gpstatus_vbadc = VBADC_QC_5V,
.bcstatus2_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_QC_9V_TO_QC_5V,
.next = &qc_9v_to_qc_5v,
};
muic_afc_data_t *afc_condition_checklist[ATTACHED_DEV_NUM] = {
[ATTACHED_DEV_TA_MUIC] = &ta_to_prepare,
[ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC] = &prepare_to_prepare_dupli,
[ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC] = &prepare_dupli_to_afc_5v,
[ATTACHED_DEV_AFC_CHARGER_5V_MUIC] = &afc_5v_to_afc_5v_dupli,
[ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC] = &afc_5v_dupli_to_afc_err_v,
[ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC] = &afc_err_v_to_afc_err_v_dupli,
[ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC] = &afc_err_v_dupli_to_afc_err_v_dupli,
[ATTACHED_DEV_AFC_CHARGER_9V_MUIC] = &afc_9v_to_afc_9v_dupli,
[ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC] = &afc_9v_dupli_to_afc_9v_dupli,
#if defined(CONFIG_MUIC_HV_12V)
[ATTACHED_DEV_AFC_CHARGER_12V_MUIC] = &afc_12v_to_afc_12v_dupli,
[ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC] = &afc_12v_dupli_to_afc_err_v,
#endif
[ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC] = &qc_prepare_to_qc_9v,
[ATTACHED_DEV_QC_CHARGER_5V_MUIC] = &qc_5v_to_qc_9v,
[ATTACHED_DEV_QC_CHARGER_9V_MUIC] = &qc_9v_to_qc_5v,
};
struct afc_init_data_s {
struct work_struct muic_afc_init_work;
struct hv_data *phv;
};
struct afc_init_data_s afc_init_data;
static bool muic_check_is_hv_dev(struct hv_data *phv)
{
bool ret = false;
switch (phv->attached_dev) {
case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
case ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC:
case ATTACHED_DEV_AFC_CHARGER_5V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC:
case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC:
#if defined(CONFIG_MUIC_HV_12V)
case ATTACHED_DEV_AFC_CHARGER_12V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC:
#endif
case ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC:
case ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC:
case ATTACHED_DEV_QC_CHARGER_5V_MUIC:
case ATTACHED_DEV_QC_CHARGER_9V_MUIC:
case ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC:
case ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC:
case ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC:
ret = true;
break;
default:
ret = false;
break;
}
if (debug_en_checklist)
pr_info("%s attached_dev(%d)[%c]\n",
__func__, phv->attached_dev, (ret ? 'T' : 'F'));
return ret;
}
muic_attached_dev_t hv_muic_check_id_err
(struct hv_data *phv, muic_attached_dev_t new_dev)
{
muic_attached_dev_t after_new_dev = new_dev;
if (!muic_check_is_hv_dev(phv))
goto out;
switch(new_dev) {
case ATTACHED_DEV_TA_MUIC:
#if !defined(CONFIG_SEC_FACTORY) && defined(CONFIG_MUIC_SUPPORT_CCIC)
if (phv->pmuic->is_ccic_afc_enable == Rp_Abnormal)
goto out;
#endif
pr_info("%s failed to change HV(%d)->TA(%d)!\n",
__func__, phv->attached_dev, new_dev);
after_new_dev = phv->attached_dev;
break;
case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
pr_info("%s HV ID Err - Undefined\n", __func__);
after_new_dev = ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC;
break;
case ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC:
pr_info("%s HV ID Err - Unsupported\n", __func__);
after_new_dev = ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC;
break;
default:
pr_info("%s HV ID Err - Supported\n", __func__);
after_new_dev = ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC;
break;
}
out:
return after_new_dev;
}
/*
static int max77865_hv_muic_read_reg(struct i2c_client *i2c, u8 reg, u8 *value)
{
value = (u8 *)muic_i2c_read_byte(i2c, reg);
if (value < 0)
pr_err("%s err read REG(0x%02x) [%d]\n",
__func__, reg, *value);
return *value;
}
*/
static int max77865_hv_muic_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
{
u8 before_val, after_val;
int ret;
before_val = muic_i2c_read_byte(i2c, reg);
ret = muic_i2c_write_byte(i2c, reg, value);
after_val = muic_i2c_read_byte(i2c, reg);
pr_info("%s reg[0x%02x] = [0x%02x] + [0x%02x] -> [0x%02x]\n",
__func__, reg, before_val, value, after_val);
return ret;
}
int max77865_muic_hv_update_reg(struct i2c_client *i2c,
const u8 reg, const u8 val, const u8 mask, const bool debug_en)
{
u8 before_val, new_val, after_val =0;
int ret = 0;
before_val = muic_i2c_read_byte(i2c, reg);
if (before_val < 0)
pr_err("%s err read REG(0x%02x) [%d] \n",
__func__, reg, ret);
new_val = (val & mask) | (before_val & (~mask));
if (before_val ^ new_val) {
ret = max77865_hv_muic_write_reg(i2c, reg, new_val);
if (ret)
pr_err("%s err write REG(0x%02x) [%d]\n",
__func__, reg, ret);
} else if (debug_en) {
pr_info("%s REG(0x%02x): already [0x%02x], don't write reg\n",
__func__, reg, before_val);
goto out;
}
if (debug_en) {
after_val = muic_i2c_read_byte(i2c, reg);
if (after_val < 0)
pr_err("%s err read REG(0x%02x) [%d]\n",
__func__, reg, ret);
pr_info("%s REG(0x%02x): [0x%02x]+[0x%02x:0x%02x]=[0x%02x]\n",
__func__, reg, before_val,
val, mask, after_val);
}
out:
return after_val;
}
void max77865_hv_muic_reset_hvcontrol_reg(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC, 0x00);
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL1, 0x00);
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL2, 0x00);
}
static void max77865_muic_set_afc_ready(struct hv_data *phv, bool value)
{
bool before, after;
before = phv->is_afc_muic_ready;
phv->is_afc_muic_ready = value;
after = phv->is_afc_muic_ready;
pr_info("%s[%d->%d]\n", __func__, before, after);
}
static int max77865_hv_muic_state_maintain(struct hv_data *phv)
{
int ret = 0;
pr_info("%s \n", __func__);
if (phv->attached_dev == ATTACHED_DEV_NONE_MUIC) {
pr_info("%s Detached(%d), need to check after\n",
__func__, phv->attached_dev);
return ret;
}
return ret;
}
static void max77865_hv_muic_set_afc_after_prepare
(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
u8 value;
pr_info("%s HV charger is detected\n", __func__);
/* Set HVCONTROL2 = 0x02 */
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL2, HVCONTROL2_DP06EN_MASK);
/* Set HVCONTROL1 - Enable VbusADCEn / Disable DPVD, DPDNVDEN */
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL1, HVCONTROL1_VBUSADCEN_MASK);
// CIS - Need check, CL54: HI-Z -> CL65: OPEN??
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC, 0x0F);
/* Set TX DATA */
value = phv->tx_data;
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVTXBYTE, value);
/* Set HVCONTROL2 = Enable MPingEnb, MTxEn, MPing, DP06En, HVDigEn */
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL2, 0x5B);
}
static void max77865_hv_muic_set_afc_charger_handshaking
(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
u8 hvtxbyte=0;
u8 hvrxbyte[HVRXBYTE_MAX] = {0,};
u8 selecthvtxbyte=0;
int i, ret;
int j;
u8 hvrxbyte_str[HVRXBYTE_MAX * 4] = {0,};
u8 temp_buf[8] = {0,};
pr_info("%s \n", __func__);
ret = max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL2, 0x13);
if (IS_ERR_VALUE(ret))
pr_err("failed to write hvcontrol2(%d)\n", ret);
hvtxbyte = muic_i2c_read_byte(i2c, MAX77865_MUIC_REG_HVTXBYTE);
for (i = 0; i < HVRXBYTE_MAX; i++) {
hvrxbyte[i] = muic_i2c_read_byte(i2c, (MAX77865_MUIC_REG_HVRXBYTE1+i));
if (hvrxbyte[i] == 0x47)
hvrxbyte[i] = 0x46;
if (hvrxbyte[i] == 0)
break;
}
pr_info("HVTXBYTE: %02x\n", hvtxbyte);
for (j = 0; j < HVRXBYTE_MAX; j++) {
sprintf(temp_buf, " %02x", hvrxbyte[j]);
strcat(hvrxbyte_str, temp_buf);
}
pr_info(" => %s\n", hvrxbyte_str);
#if defined(CONFIG_MUIC_HV_12V)
if (hvrxbyte[0] != hvtxbyte) {
for (i = 0; (i < HVRXBYTE_MAX) && (hvrxbyte[i] != 0); i++) {
if (hvtxbyte > selecthvtxbyte) {
pr_info(" selected hvtxbyte = %02x at %d", hvrxbyte[i], i);
selecthvtxbyte = hvrxbyte[i];
}
}
/* W/A of RX byte error */
if ((phv->vps.hvcontrol[1] & 0x8) == 0) {
switch (selecthvtxbyte) {
case MUIC_HV_5V:
case MUIC_HV_9V:
case MUIC_HV_12V:
break;
default:
selecthvtxbyte = MUIC_HV_9V;
pr_info("%s RXBYTE Error! selected hvtxbyte = %02x\n",
__func__, selecthvtxbyte);
break;
}
}
if (selecthvtxbyte)
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVTXBYTE, selecthvtxbyte);
}
#else
if (hvrxbyte[0] != hvtxbyte) {
for (i = 0; (i < HVRXBYTE_MAX) && (hvrxbyte[i] != 0); i++) {
if (((hvrxbyte[i] & 0xF0) == 0x40) && (hvtxbyte > selecthvtxbyte)) {
pr_info(" selected hvtxbyte = %02x at %d", hvrxbyte[i], i);
selecthvtxbyte = hvrxbyte[i];
}
}
if (selecthvtxbyte != 0)
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVTXBYTE, selecthvtxbyte);
}
#endif
return;
}
static void max77865_hv_muic_afc_control_ping
(struct hv_data *phv, bool ping_continue)
{
int ret;
pr_info("%s[%d, %c]\n", __func__,
phv->afc_count, ping_continue ? 'T' : 'F');
if (ping_continue)
ret = max77865_hv_muic_write_reg(phv->i2c, MAX77865_MUIC_REG_HVCONTROL2, 0x5B);
else
ret = max77865_hv_muic_write_reg(phv->i2c, MAX77865_MUIC_REG_HVCONTROL2, 0x03);
if (ret)
pr_err("failed to write HVCONTROL2(%d)\n", ret);
}
static void max77865_hv_muic_qc_charger(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
int ret1 = 0, ret2 = 0;
u8 bcstatus2 = 0, gpstatus = 0;
bcstatus2 = muic_i2c_read_byte(i2c, MAX77865_MUIC_REG_STATUS2_BC);
gpstatus = muic_i2c_read_byte(i2c, MAX77865_MUIC_REG_STATUS_GP);
if ((bcstatus2 < 0) || (gpstatus < 0))
pr_err("%s failed to read STATUS reg\n", __func__);
pr_info("%s BC_STATUS2:0x%02x GP_STATUS:0x%02x qc_hv:%x\n",
__func__, bcstatus2, gpstatus, phv->qc_hv);
switch (phv->qc_hv) {
case HV_SUPPORT_QC_9V:
ret1 = max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL1, 0x01);
ret2 = max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC, 0x19);
break;
case HV_SUPPORT_QC_12V:
ret1 = max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL1, 0x01);
ret2 = max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC, 0x15);
break;
case HV_SUPPORT_QC_20V:
ret1 = max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL1, 0x01);
ret2 = max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC, 0x1A);
break;
case HV_SUPPORT_QC_5V:
ret1 = max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL1, 0x01);
ret2 = max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC, 0x14);
break;
default:
pr_err("%s not support QC Charger\n", __func__);
break;
}
ret1 += ret2;
if (ret1)
pr_err("%s failed to write HVCONTROL1 or BCCONTROL2 reg(%d)\n",
__func__, ret1);
}
static void max77865_hv_muic_after_qc_prepare(struct hv_data *phv)
{
pr_info("%s\n", __func__);
phv->is_qc_vb_settle = false;
schedule_delayed_work(&phv->hv_muic_qc_vb_work, msecs_to_jiffies(300));
}
static void max77865_hv_muic_adcmode_switch
(struct hv_data *phv, bool always_on)
{
struct i2c_client *i2c = phv->i2c;
int ret;
pr_info("%s always_on:%c\n", __func__, (always_on ? 'T' : 'F'));
if (always_on) {
/* set_adc_scan_mode(phv->pmuic, ADC_SCANMODE_CONTINUOUS); */
ret = max77865_muic_hv_update_reg(i2c, MAX77865_MUIC_REG_HVCONTROL1,
(MAX77865_ENABLE_BIT << HVCONTROL1_VBUSADCEN_SHIFT),
HVCONTROL1_VBUSADCEN_MASK, true);
} else {
/* set_adc_scan_mode(phv->pmuic, ADC_SCANMODE_ONESHOT); */
/* non MAXIM */
ret = max77865_muic_hv_update_reg(i2c, MAX77865_MUIC_REG_HVCONTROL1,
(MAX77865_DISABLE_BIT << HVCONTROL1_VBUSADCEN_SHIFT),
HVCONTROL1_VBUSADCEN_MASK, true);
}
if (ret < 0)
pr_err("%s failed to switch adcmode(%d)\n", __func__, ret);
}
static void max77865_hv_muic_adcmode_always_on(struct hv_data *phv)
{
pr_info("%s\n", __func__);
max77865_hv_muic_adcmode_switch(phv, true);
}
void max77865_hv_muic_adcmode_oneshot(struct hv_data *phv)
{
pr_info("%s\n", __func__);
max77865_hv_muic_adcmode_switch(phv, false);
}
void max77865_hv_muic_connect_start(struct hv_data *phv)
{
pr_info("%s\n", __func__);
phv->attached_dev = ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC;
/* update MUIC's attached_dev */
phv->pmuic->attached_dev = phv->attached_dev;
max77865_hv_muic_adcmode_always_on(phv);
max77865_hv_muic_set_afc_after_prepare(phv);
phv->afc_count = 0;
phv->is_afc_handshaking = false;
/*
* HW Issue(MPing miss)
* check HV state values after 2000ms(2s)
*/
schedule_delayed_work(&phv->hv_muic_mping_miss_wa,
msecs_to_jiffies(MPING_MISS_WA_TIME));
#if defined(CONFIG_MUIC_NOTIFIER)
muic_notifier_attach_attached_dev(phv->attached_dev);
#endif
#if defined(CONFIG_MUIC_SUPPORT_CCIC)
if (phv->pmuic->opmode & OPMODE_CCIC)
muic_set_legacy_dev(phv->pmuic, phv->attached_dev);
#endif
}
static int max77865_hv_muic_handle_attach
(struct hv_data *phv, const muic_afc_data_t *new_afc_data)
{
int ret = 0;
bool noti = true;
muic_attached_dev_t new_dev = new_afc_data->new_dev;
int mping_missed = (phv->vps.hvcontrol[1] & 0x8);
int tx_data = 0;
if (mping_missed)
phv->afc_count = 0;
pr_info("%s \n", __func__);
if (phv->is_charger_ready == false) {
if (new_afc_data->new_dev == ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC) {
phv->is_afc_muic_prepare = true;
pr_info("%s is_charger_ready[%c], is_afc_muic_prepare[%c]\n",
__func__,
(phv->is_charger_ready ? 'T' : 'F'),
(phv->is_afc_muic_prepare ? 'T' : 'F'));
return ret;
}
pr_info("%s is_charger_ready[%c], just return\n",
__func__, (phv->is_charger_ready ? 'T' : 'F'));
return ret;
}
tx_data = muic_i2c_read_byte(phv->i2c, MAX77865_MUIC_REG_HVTXBYTE);
switch (new_afc_data->function_num) {
case FUNC_TA_TO_PREPARE:
#if defined(CONFIG_SEC_FACTORY) || !defined(CONFIG_MUIC_SUPPORT_CCIC)
#if defined(CONFIG_MUIC_HV_12V)
pr_info("%s: FACTORY 12V HV Charging Start!\n", __func__);
phv->tx_data = MUIC_HV_12V;
#else
pr_info("%s: FACTORY 9V HV Charging Start!\n", __func__);
phv->tx_data = MUIC_HV_9V;
#endif
max77865_hv_muic_connect_start(phv);
#else /* defined(CONFIG_SEC_FACTORY) || !defined(CONFIG_MUIC_SUPPORT_CCIC) */
if (phv->pmuic->is_ccic_afc_enable == Rp_56K) {
#if defined(CONFIG_MUIC_HV_12V)
pr_info("%s: 12V HV Charging Start!\n", __func__);
phv->tx_data = MUIC_HV_12V;
#else
pr_info("%s: 9V HV Charging Start!\n", __func__);
phv->tx_data = MUIC_HV_9V;
#endif
max77865_hv_muic_connect_start(phv);
} else {
pr_info("%s First check PREPARE! AFC 5V noti.\n", __func__);
new_dev = ATTACHED_DEV_AFC_CHARGER_5V_MUIC;
noti = true;
}
#endif
break;
case FUNC_PREPARE_TO_PREPARE_DUPLI:
/* attached_dev is changed. MPING Missing did not happened
* Cancel delayed work */
pr_info("%s cancel_delayed_work(dev %d), Mping missing wa\n",
__func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
phv->afc_count++;
max77865_hv_muic_set_afc_charger_handshaking(phv);
if (!mping_missed)
phv->is_afc_handshaking = true;
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77865_hv_muic_afc_control_ping(phv, false);
} else {
max77865_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_PREPARE_TO_AFC_5V:
noti = false;
break;
case FUNC_PREPARE_TO_QC_PREPARE:
/* attached_dev is changed. MPING Missing did not happened
* Cancel delayed work */
pr_info("%s cancel_delayed_work(dev %d), Mping missing wa\n",
__func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
/* ping STOP */
ret = max77865_hv_muic_write_reg(phv->i2c, MAX77865_MUIC_REG_HVCONTROL2, 0x03);
if (ret)
pr_err("%s failed to write HVCONTROL2 reg(%d)\n",
__func__, ret);
max77865_hv_muic_qc_charger(phv);
max77865_hv_muic_after_qc_prepare(phv);
break;
case FUNC_PREPARE_DUPLI_TO_PREPARE_DUPLI:
phv->afc_count++;
if (!phv->is_afc_handshaking) {
max77865_hv_muic_set_afc_charger_handshaking(phv);
if (!mping_missed)
phv->is_afc_handshaking = true;
}
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77865_hv_muic_afc_control_ping(phv, false);
} else {
max77865_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_PREPARE_DUPLI_TO_AFC_5V:
noti = false;
break;
case FUNC_PREPARE_DUPLI_TO_AFC_ERR_V:
if (phv->afc_count > AFC_CHARGER_WA_PING)
max77865_hv_muic_afc_control_ping(phv, false);
else
noti = false;
break;
case FUNC_PREPARE_DUPLI_TO_AFC_9V:
#if defined(CONFIG_MUIC_HV_12V)
if (tx_data == MUIC_HV_9V) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
} else
noti = false;
#else
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
#endif
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_PREPARE_DUPLI_TO_AFC_12V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
#endif
case FUNC_PREPARE_DUPLI_TO_QC_PREPARE:
max77865_hv_muic_qc_charger(phv);
max77865_hv_muic_after_qc_prepare(phv);
break;
case FUNC_AFC_5V_TO_AFC_5V_DUPLI:
/* attached_dev is changed. MPING Missing did not happened
* Cancel delayed work */
pr_info("%s cancel_delayed_work(dev %d), Mping missing wa\n",
__func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
phv->afc_count++;
if (!phv->is_afc_handshaking) {
max77865_hv_muic_set_afc_charger_handshaking(phv);
if (!mping_missed)
phv->is_afc_handshaking = true;
}
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_always_on(phv);
} else {
max77865_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_5V_TO_AFC_ERR_V:
/* attached_dev is changed. MPING Missing did not happened
* Cancel delayed work */
pr_info("%s cancel_delayed_work(dev %d), Mping missing wa\n",
__func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
if (phv->afc_count > AFC_CHARGER_WA_PING)
max77865_hv_muic_afc_control_ping(phv, false);
else
noti = false;
break;
case FUNC_AFC_5V_TO_AFC_9V:
/* attached_dev is changed. MPING Missing did not happened
* Cancel delayed work */
pr_info("%s cancel_delayed_work(dev %d), Mping missing wa\n",
__func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
#if defined(CONFIG_MUIC_HV_12V)
if(tx_data == MUIC_HV_9V) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
} else
noti = false;
#else
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
#endif
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_AFC_5V_TO_AFC_12V:
/* attached_dev is changed. MPING Missing did not happened
* Cancel delayed work */
pr_info("%s cancel_delayed_work(dev %d), Mping missing wa\n",
__func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
#endif
case FUNC_AFC_5V_TO_QC_PREPARE:
/* attached_dev is changed. MPING Missing did not happened
* Cancel delayed work */
pr_info("%s cancel_delayed_work(dev %d), Mping missing wa\n",
__func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
max77865_hv_muic_qc_charger(phv);
max77865_hv_muic_after_qc_prepare(phv);
break;
case FUNC_AFC_5V_DUPLI_TO_AFC_5V_DUPLI:
phv->afc_count++;
if (!phv->is_afc_handshaking) {
max77865_hv_muic_set_afc_charger_handshaking(phv);
if (!mping_missed)
phv->is_afc_handshaking = true;
}
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_always_on(phv);
} else {
max77865_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_5V_DUPLI_TO_AFC_ERR_V:
if (phv->afc_count > AFC_CHARGER_WA_PING)
max77865_hv_muic_afc_control_ping(phv, false);
else
noti = false;
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_AFC_5V_DUPLI_TO_AFC_12V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
#endif
case FUNC_AFC_5V_DUPLI_TO_AFC_9V:
#if defined(CONFIG_MUIC_HV_12V)
if(tx_data == MUIC_HV_9V) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
} else
noti = false;
#else
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
#endif
break;
case FUNC_AFC_5V_DUPLI_TO_QC_PREPARE:
max77865_hv_muic_qc_charger(phv);
max77865_hv_muic_after_qc_prepare(phv);
break;
case FUNC_AFC_ERR_V_TO_AFC_ERR_V_DUPLI:
phv->afc_count++;
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_always_on(phv);
} else {
max77865_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_ERR_V_TO_AFC_5V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_ERR_V_TO_AFC_9V:
#if defined(CONFIG_MUIC_HV_12V)
if(tx_data == MUIC_HV_9V) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
} else
noti = false;
#else
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
#endif
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_AFC_ERR_V_TO_AFC_12V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
#endif
case FUNC_AFC_ERR_V_TO_QC_PREPARE:
max77865_hv_muic_qc_charger(phv);
max77865_hv_muic_after_qc_prepare(phv);
break;
case FUNC_AFC_ERR_V_DUPLI_TO_AFC_ERR_V_DUPLI:
phv->afc_count++;
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_always_on(phv);
} else {
max77865_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_ERR_V_DUPLI_TO_AFC_5V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_ERR_V_DUPLI_TO_AFC_9V:
#if defined(CONFIG_MUIC_HV_12V)
if (tx_data == MUIC_HV_9V) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
} else
noti = false;
#else
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
#endif
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_AFC_ERR_V_DUPLI_TO_AFC_12V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
#endif
case FUNC_AFC_ERR_V_DUPLI_TO_QC_PREPARE:
max77865_hv_muic_qc_charger(phv);
max77865_hv_muic_after_qc_prepare(phv);
break;
case FUNC_AFC_9V_TO_AFC_9V_DUPLI:
phv->afc_count++;
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_always_on(phv);
} else {
max77865_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_9V_TO_AFC_ERR_V:
if (phv->afc_count > AFC_CHARGER_WA_PING)
max77865_hv_muic_afc_control_ping(phv, false);
else
noti = false;
break;
case FUNC_AFC_9V_TO_AFC_5V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_AFC_9V_TO_AFC_12V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
#endif
case FUNC_AFC_9V_TO_QC_PREPARE:
max77865_hv_muic_qc_charger(phv);
max77865_hv_muic_after_qc_prepare(phv);
break;
case FUNC_AFC_9V_DUPLI_TO_AFC_ERR_V:
if (phv->afc_count > AFC_CHARGER_WA_PING)
max77865_hv_muic_afc_control_ping(phv, false);
else
noti = false;
break;
case FUNC_AFC_9V_DUPLI_TO_AFC_5V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_AFC_9V_DUPLI_TO_AFC_12V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
#endif
case FUNC_AFC_9V_DUPLI_TO_AFC_9V_DUPLI:
phv->afc_count++;
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_always_on(phv);
} else {
max77865_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_9V_DUPLI_TO_QC_PREPARE:
max77865_hv_muic_qc_charger(phv);
max77865_hv_muic_after_qc_prepare(phv);
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_AFC_12V_TO_AFC_12V_DUPLI:
phv->afc_count++;
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_always_on(phv);
} else {
max77865_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_12V_TO_AFC_ERR_V:
if (phv->afc_count > AFC_CHARGER_WA_PING)
max77865_hv_muic_afc_control_ping(phv, false);
else
noti = false;
break;
case FUNC_AFC_12V_TO_AFC_5V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_12V_TO_AFC_9V:
if(tx_data == MUIC_HV_9V) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
} else
noti = false;
break;
case FUNC_AFC_12V_TO_QC_PREPARE:
max77865_hv_muic_qc_charger(phv);
max77865_hv_muic_after_qc_prepare(phv);
break;
case FUNC_AFC_12V_DUPLI_TO_AFC_ERR_V:
if (phv->afc_count > AFC_CHARGER_WA_PING)
max77865_hv_muic_afc_control_ping(phv, false);
else
noti = false;
break;
case FUNC_AFC_12V_DUPLI_TO_AFC_5V:
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_12V_DUPLI_TO_AFC_9V:
if(tx_data == MUIC_HV_9V) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_oneshot(phv);
} else
noti = false;
break;
case FUNC_AFC_12V_DUPLI_TO_AFC_12V_DUPLI:
phv->afc_count++;
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77865_hv_muic_afc_control_ping(phv, false);
max77865_hv_muic_adcmode_always_on(phv);
} else {
max77865_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_12V_DUPLI_TO_QC_PREPARE:
max77865_hv_muic_qc_charger(phv);
max77865_hv_muic_after_qc_prepare(phv);
break;
#endif
case FUNC_QC_PREPARE_TO_QC_5V:
if (phv->is_qc_vb_settle == true)
max77865_hv_muic_adcmode_oneshot(phv);
else
noti = false;
break;
case FUNC_QC_PREPARE_TO_QC_9V:
phv->is_qc_vb_settle = true;
max77865_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_QC_5V_TO_QC_9V:
phv->is_qc_vb_settle = true;
max77865_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_QC_9V_TO_QC_5V:
max77865_hv_muic_adcmode_oneshot(phv);
break;
default:
pr_warn("%s undefinded hv function num(%d)\n",
__func__, new_afc_data->function_num);
ret = -ESRCH;
goto out;
}
#if defined(CONFIG_MUIC_NOTIFIER)
if (phv->attached_dev == new_dev)
noti = false;
else if (new_dev == ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC ||
new_dev == ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC ||
new_dev == ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC ||
#if defined(CONFIG_MUIC_HV_12V)
new_dev == ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC ||
#endif
new_dev == ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC)
noti = false;
if (noti)
muic_notifier_attach_attached_dev(new_dev);
#endif /* CONFIG_MUIC_NOTIFIER */
#if defined(CONFIG_MUIC_SUPPORT_CCIC)
if (phv->pmuic->opmode & OPMODE_CCIC)
muic_set_legacy_dev(phv->pmuic, new_dev);
#endif
phv->attached_dev = new_dev;
//Fixme.
/* update MUIC's attached_dev */
phv->pmuic->attached_dev = phv->attached_dev;
out:
return ret;
}
static bool muic_check_hv_irq
(struct hv_data *phv,
const muic_afc_data_t *tmp_afc_data, int irq)
{
int afc_irq = 0;
bool ret = false;
/* change irq num to muic_afc_irq_t */
if(irq == phv->irq_vdnmon)
afc_irq = MUIC_AFC_IRQ_VDNMON;
else if (irq == phv->irq_mrxrdy)
afc_irq = MUIC_AFC_IRQ_MRXRDY;
else if (irq == phv->irq_mpnack)
afc_irq = MUIC_AFC_IRQ_MPNACK;
else if (irq == phv->irq_vbadc)
afc_irq = MUIC_AFC_IRQ_VBADC;
else {
pr_err("%s failed to find irq #(%d)\n", __func__, irq);
ret = false;
goto out;
}
if (tmp_afc_data->afc_irq == afc_irq) {
ret = true;
goto out;
}
if (tmp_afc_data->afc_irq == MUIC_AFC_IRQ_DONTCARE) {
ret = true;
goto out;
}
out:
if (debug_en_checklist)
pr_info("%s check_data dev(%d) irq(%d:%d) ret(%c)\n",
__func__, tmp_afc_data->new_dev,
tmp_afc_data->afc_irq, afc_irq, ret ? 'T' : 'F');
return ret;
}
static bool muic_check_bccontrol2_dpdnman
(const muic_afc_data_t *tmp_afc_data, u8 dpdnvden)
{
bool ret = false;
if (tmp_afc_data->bccontrol2_dpdnman == dpdnvden) {
ret = true;
goto out;
}
if (tmp_afc_data->bccontrol2_dpdnman == DPDNVDEN_DONTCARE) {
ret = true;
goto out;
}
out:
if (debug_en_checklist)
pr_info("%s check_data dev(%d) dpdnvden(0x%x:0x%x) ret(%c)\n",
__func__, tmp_afc_data->new_dev,
tmp_afc_data->bccontrol2_dpdnman, dpdnvden,
ret ? 'T' : 'F');
return ret;
}
static bool muic_check_gpstatus_vbadc
(const muic_afc_data_t *tmp_afc_data, u8 vbadc)
{
bool ret = false;
if (tmp_afc_data->gpstatus_vbadc == vbadc) {
ret = true;
goto out;
}
if (tmp_afc_data->gpstatus_vbadc == VBADC_AFC_5V) {
switch (vbadc) {
case VBADC_4V_5V:
case VBADC_5V_6V:
case VBADC_6V_7V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->gpstatus_vbadc == VBADC_AFC_9V) {
switch (vbadc) {
case VBADC_7V_8V:
case VBADC_8V_9V:
case VBADC_9V_10V:
ret = true;
goto out;
default:
break;
}
}
#if defined(CONFIG_MUIC_HV_12V)
if (tmp_afc_data->gpstatus_vbadc == VBADC_AFC_12V) {
switch (vbadc) {
case VBADC_10V_11V:
case VBADC_11V_12V:
case VBADC_12V_13V:
ret = true;
goto out;
default:
break;
}
}
#endif
if (tmp_afc_data->gpstatus_vbadc == VBADC_AFC_ERR_V_NOT_0) {
switch (vbadc) {
#if !defined(CONFIG_MUIC_HV_12V)
case VBADC_10V_11V:
case VBADC_11V_12V:
case VBADC_12V_13V:
#endif
case VBADC_13V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->gpstatus_vbadc == VBADC_AFC_ERR_V) {
switch (vbadc) {
case VBADC_VBDET:
#if !defined(CONFIG_MUIC_HV_12V)
case VBADC_10V_11V:
case VBADC_11V_12V:
case VBADC_12V_13V:
#endif
case VBADC_13V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->gpstatus_vbadc == VBADC_QC_5V) {
switch (vbadc) {
case VBADC_4V_5V:
case VBADC_5V_6V:
case VBADC_6V_7V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->gpstatus_vbadc == VBADC_QC_9V) {
switch (vbadc) {
case VBADC_7V_8V:
case VBADC_8V_9V:
case VBADC_9V_10V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->gpstatus_vbadc == VBADC_ANY) {
switch (vbadc) {
case VBADC_4V_5V:
case VBADC_5V_6V:
case VBADC_6V_7V:
case VBADC_7V_8V:
case VBADC_8V_9V:
case VBADC_9V_10V:
case VBADC_10V_11V:
case VBADC_11V_12V:
case VBADC_12V_13V:
case VBADC_13V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->gpstatus_vbadc == VBADC_DONTCARE) {
ret = true;
goto out;
}
out:
if (debug_en_checklist)
pr_info("%s check_data dev(%d) vbadc(0x%x:0x%x) ret(%c)\n",
__func__, tmp_afc_data->new_dev,
tmp_afc_data->gpstatus_vbadc, vbadc, ret ? 'T' : 'F');
return ret;
}
static bool muic_check_bcstatus2_vdnmon
(const muic_afc_data_t *tmp_afc_data, u8 vdnmon)
{
bool ret = false;
if (tmp_afc_data->bcstatus2_vdnmon == vdnmon) {
ret = true;
goto out;
}
if (tmp_afc_data->bcstatus2_vdnmon == VDNMON_DONTCARE) {
ret = true;
goto out;
}
out:
if (debug_en_checklist)
pr_info("%s check_data dev(%d) vdnmon(0x%x:0x%x) ret(%c)\n",
__func__, tmp_afc_data->new_dev,
tmp_afc_data->bcstatus2_vdnmon, vdnmon, ret ? 'T' : 'F');
return ret;
}
#if 0
/*
* Keep charging for the non-AFC chargers
* instead of sending a detach noti.
*/
static bool muic_hv_is_nonafc_ta(u8 chgtyp)
{
switch (chgtyp) {
case CHGTYP_500MA:
case CHGTYP_1A:
case CHGTYP_SPECIAL_3_3V_CHARGER:
return true;
default:
break;
}
return false;
}
#endif
static bool muic_check_dev_ta(struct hv_data *phv)
{
u8 status1 = phv->vps.status1;
u8 status3 = phv->vps.status3;
u8 uid, vbvolt, chgdetrun, chgtyp;
uid = status3 & GP_STATUS_UID_MASK;
vbvolt = status1 & BC_STATUS1_VBVOLT_MASK;
chgdetrun = status1 & BC_STATUS1_CHGDETRUN_MASK;
chgtyp = status1 & BC_STATUS1_CHGTYP_MASK;
if (uid != UID_OPEN) {
max77865_muic_set_afc_ready(phv, false);
return false;
}
#if 0 // CIS - Need check
if (muic_hv_is_nonafc_ta(chgtyp)) {
max77865_muic_set_afc_ready(phv, false);
pr_info("%s non AFC Charger.(chgtyp=%d) \n",
__func__, chgtyp);
return false;
}
#endif
if (vbvolt == VB_LOW || chgdetrun == CHGDETRUN_TRUE || chgtyp != CHGTYP_DEDICATED_CHARGER) {
max77865_muic_set_afc_ready(phv, false);
#if defined(CONFIG_MUIC_NOTIFIER)
muic_notifier_detach_attached_dev(phv->attached_dev);
#endif
phv->attached_dev = ATTACHED_DEV_NONE_MUIC;
#if defined(CONFIG_MUIC_SUPPORT_CCIC)
if (phv->pmuic->opmode & OPMODE_CCIC)
muic_set_legacy_dev(phv->pmuic, phv->attached_dev);
#endif
//Fixme.
/* update MUIC's attached_dev */
phv->pmuic->attached_dev = phv->attached_dev;
return false;
}
return true;
}
void max77865_hv_muic_detect_dev(struct hv_data *phv, int irq)
{
struct i2c_client *i2c = phv->i2c;
const muic_afc_data_t *tmp_afc_data = afc_condition_checklist[phv->attached_dev];
int intr = MUIC_INTR_DETACH;
int ret;
int i;
u8 status[3];
u8 hvcontrol[2];
u8 vdnmon, dpdnvden, vbadc;//mpnack
bool flag_next = true;
bool muic_dev_ta = false;
pr_info("%s irq(%d), attache_dev(%d)\n", __func__, irq, phv->attached_dev);
if (tmp_afc_data == NULL) {
pr_info("%s non AFC Charger, just return!\n", __func__);
return;
}
ret = max77865_bulk_read(phv->i2c, MAX77865_MUIC_REG_STATUS1_BC, 3, status);
if (ret) {
pr_err("%s fail to read muic reg(%d)\n", __func__, ret);
return;
}
pr_info("BC STATUS1:0x%02x, 2:0x%02x, GP STATUS:0x%02x\n",
status[0], status[1], status[2]);
/* attached status */
phv->vps.status1 = status[0];
phv->vps.status2 = status[1];
phv->vps.status3 = status[2];
/* check TA type */
muic_dev_ta = muic_check_dev_ta(phv);
if (!muic_dev_ta) {
pr_err("%s device type is not TA!\n", __func__);
return;
}
/* attached status */
// mpnack = status[2] & STATUS3_MPNACK_MASK;
vdnmon = status[1] & BC_STATUS2_DNVDATREF_MASK;
vbadc = status[2] & GP_STATUS_VBADC_MASK;
phv->vps.bccontrol2 = muic_i2c_read_byte(i2c, MAX77865_MUIC_REG_CONTROL2_BC);
ret = max77865_bulk_read(i2c, MAX77865_MUIC_REG_HVCONTROL1, 2, hvcontrol);
if (ret) {
pr_err("%s fail to read muic reg(%d)\n",
__func__, ret);
return;
}
pr_info("BCCONTROL2: 0x%02x, HVCONTROL1:0x%02x, 2:0x%02x\n",
phv->vps.bccontrol2, hvcontrol[0], hvcontrol[1]);
/* attached - control */
phv->vps.hvcontrol[0] = hvcontrol[0];
phv->vps.hvcontrol[1] = hvcontrol[1];
dpdnvden = phv->vps.bccontrol2 & BC_CONTROL2_DPDNMAN_MASK;
pr_info("vdnmon:0x%x vbadc:0x%x dpdnvden:0x%x\n",
vdnmon, vbadc, dpdnvden);
for (i = 0; i < ATTACHED_DEV_NUM; i++, tmp_afc_data = tmp_afc_data->next) {
if (!flag_next) {
pr_info("not found new_dev in afc_condition_checklist\n");
break;
}
if (tmp_afc_data->next == tmp_afc_data)
flag_next = false;
if (!(muic_check_hv_irq(phv, tmp_afc_data, irq)))
continue;
if (!(muic_check_bccontrol2_dpdnman(tmp_afc_data, dpdnvden)))
continue;
if (!(muic_check_gpstatus_vbadc(tmp_afc_data, vbadc)))
continue;
if(!(muic_check_bcstatus2_vdnmon(tmp_afc_data, vdnmon)))
continue;
pr_info("checklist match found at i(%d), %s(%d)\n",
i, tmp_afc_data->afc_name,
tmp_afc_data->new_dev);
intr = MUIC_INTR_ATTACH;
break;
}
if (intr == MUIC_INTR_ATTACH) {
pr_info("AFC ATTACHED %d->%d\n",
phv->attached_dev, tmp_afc_data->new_dev);
ret = max77865_hv_muic_handle_attach(phv, tmp_afc_data);
if (ret)
pr_err("failed to handle attach(%d)\n", ret);
} else {
pr_info("AFC MAINTAIN (%d)\n", phv->attached_dev);
ret = max77865_hv_muic_state_maintain(phv);
if (ret)
pr_err("failed to maintain state(%d)\n", ret);
goto out;
}
out:
return;
}
/* TA setting in max77865-muic.c */
void max77865_muic_prepare_afc_charger(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
int ret;
pr_info("%s\n", __func__);
max77865_hv_muic_adcmode_oneshot(phv);
/* set HVCONTROL1=0x11 */
ret = max77865_muic_hv_update_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC,
(0x1 << BC_CONTROL2_DPDRV_SHIFT), BC_CONTROL2_DPDRV_MASK, true);
if (ret < 0 )
goto err_write;
ret = max77865_muic_hv_update_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC,
(MAX77865_ENABLE_BIT << BC_CONTROL2_DNMONEN_SHIFT),
BC_CONTROL2_DNMONEN_MASK, true);
if (ret < 0)
goto err_write;
ret = max77865_muic_hv_update_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC,
(MAX77865_ENABLE_BIT << BC_CONTROL2_DPDNMAN_SHIFT),
BC_CONTROL2_DPDNMAN_MASK, true);
if (ret < 0)
goto err_write;
/* Set VBusADCEn = 1 after the time of changing adcmode*/
max77865_muic_set_afc_ready(phv, true);
return;
err_write:
pr_err("fail to write muic reg(%d)\n", ret);
return;
}
/* TA setting in max77865-muic.c */
bool max77865_muic_check_change_dev_afc_charger
(struct hv_data *phv, muic_attached_dev_t new_dev)
{
bool ret = true;
if (new_dev == ATTACHED_DEV_TA_MUIC ||
new_dev == ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC ||
new_dev == ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC ||
new_dev == ATTACHED_DEV_AFC_CHARGER_5V_MUIC ||
new_dev == ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC ||
new_dev == ATTACHED_DEV_AFC_CHARGER_9V_MUIC ||
new_dev == ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC ||
#if defined(CONFIG_MUIC_HV_12V)
new_dev == ATTACHED_DEV_AFC_CHARGER_12V_MUIC ||
new_dev == ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC ||
#endif
new_dev == ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC ||
new_dev == ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC ||
new_dev == ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC ||
new_dev == ATTACHED_DEV_QC_CHARGER_5V_MUIC ||
new_dev == ATTACHED_DEV_QC_CHARGER_9V_MUIC) {
if(muic_check_dev_ta(phv)) {
ret = false;
}
}
return ret;
}
static void max77865_hv_muic_detect_after_charger_init(struct work_struct *work)
{
struct afc_init_data_s *init_data =
container_of(work, struct afc_init_data_s, muic_afc_init_work);
struct hv_data *phv = init_data->phv;
int ret;
u8 bcstatus2 = 0, gpstatus = 0;
pr_info("%s\n", __func__);
mutex_lock(phv->pmutex);
/* check vdnmon status value */
bcstatus2 = muic_i2c_read_byte(phv->i2c, MAX77865_MUIC_REG_STATUS2_BC);
gpstatus = muic_i2c_read_byte(phv->i2c, MAX77865_MUIC_REG_STATUS_GP);
if ((bcstatus2 < 0 ) || (gpstatus < 0)) {
pr_err("fail to read muic reg(%d)\n", ret);
return;
}
pr_info("BC STATUS2:0x%02x, GP STATUS:0x%02x\n",
bcstatus2, gpstatus);
if (phv->is_afc_muic_ready) {
if (phv->is_afc_muic_prepare)
max77865_hv_muic_detect_dev(phv, phv->irq_vdnmon);
else
max77865_hv_muic_detect_dev(phv, -1);
}
mutex_unlock(phv->pmutex);
}
static int is_hv_cable(muic_data_t *pmuic)
{
switch (pmuic->attached_dev) {
case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
case ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC:
case ATTACHED_DEV_AFC_CHARGER_5V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC:
case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC:
case ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC:
case ATTACHED_DEV_QC_CHARGER_5V_MUIC:
case ATTACHED_DEV_QC_CHARGER_9V_MUIC:
return 1;
default:
return 0;
}
}
void hv_muic_change_afc_voltage(muic_data_t *pmuic, int tx_data)
{
struct hv_data *phv = pmuic->phv;
struct i2c_client *i2c = phv->i2c;
int value;
pr_info("%s: change afc voltage(%x)\n", __func__, tx_data);
value = muic_i2c_read_byte(i2c, MAX77865_MUIC_REG_HVTXBYTE);
if (value == tx_data) {
pr_info("%s: same to current voltage %x\n", __func__, value);
return;
}
phv->afc_count = 0;
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVTXBYTE, tx_data);
/* QC Charger */
if (phv->attached_dev == ATTACHED_DEV_QC_CHARGER_5V_MUIC ||
phv->attached_dev == ATTACHED_DEV_QC_CHARGER_9V_MUIC) {
switch (tx_data) {
case MUIC_HV_5V:
// set_adc_scan_mode(phv->pmuic,ADC_SCANMODE_CONTINUOUS);
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL1, 0x01);
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC, 0x14);
break;
case MUIC_HV_9V:
// set_adc_scan_mode(phv->pmuic,ADC_SCANMODE_CONTINUOUS);
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL1, 0x01);
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC, 0x19);
break;
default:
break;
}
}
/* AFC Charger */
else {
max77865_hv_muic_adcmode_always_on(phv);
max77865_hv_muic_afc_control_ping(phv, true);
}
}
int muic_afc_set_voltage(int vol)
{
muic_data_t *pmuic = hv_afc.pmuic;
struct vendor_ops *pvendor = pmuic->regmapdesc->vendorops;
if (is_hv_cable(pmuic)) {
if (vol == 0) {
pr_info("%s: TSUB too hot. Chgdet Re-run.\n", __func__);
hv_muic_chgdet_ready(pmuic->phv);
if (pvendor && pvendor->run_chgdet)
pvendor->run_chgdet(pmuic->regmapdesc, 1);
} else if (vol == 5) {
hv_muic_change_afc_voltage(pmuic, MUIC_HV_5V);
} else if (vol == 9) {
hv_muic_change_afc_voltage(pmuic, MUIC_HV_9V);
#if defined(CONFIG_MUIC_HV_12V)
} else if (vol == 12) {
hv_muic_change_afc_voltage(pmuic, MUIC_HV_12V);
#endif
} else {
pr_warn("%s invalid value\n", __func__);
return 0;
}
} else {
pr_info("%s It's NOT HV cable type\n", __func__);
return 0;
}
return 1;
}
void max77865_hv_muic_charger_init(void)
{
pr_info("%s\n", __func__);
if (afc_init_data.phv) {
if (afc_init_data.phv->is_charger_ready) {
pr_info("charger is already ready.\n");
} else {
afc_init_data.phv->is_charger_ready = true;
schedule_work(&afc_init_data.muic_afc_init_work);
}
}
}
static void max77865_hv_muic_check_qc_vb(struct work_struct *work)
{
struct hv_data *phv = container_of(work, struct hv_data, hv_muic_qc_vb_work.work);
u8 gpstatus = 0, vbadc;
pr_info("%s\n", __func__);
if (!phv) {
pr_err("failed to read phv!\n");
return;
}
mutex_lock(phv->pmutex);
if (phv->is_qc_vb_settle == true) {
pr_info("already qc vb settled\n");
goto out;
}
gpstatus = muic_i2c_read_byte(phv->i2c, MAX77865_MUIC_REG_STATUS_GP);
vbadc = gpstatus & GP_STATUS_VBADC_MASK;
if (vbadc == VBADC_4V_5V || vbadc == VBADC_5V_6V) {
phv->is_qc_vb_settle = true;
max77865_hv_muic_detect_dev(phv, phv->irq_vbadc);
}
out:
mutex_unlock(phv->pmutex);
return;
}
static void max77865_hv_muic_check_mping_miss(struct work_struct *work)
{
struct hv_data *phv = container_of(work, struct hv_data, hv_muic_mping_miss_wa.work);
pr_info("%s\n", __func__);
if (!phv) {
pr_err("failed to read phv!\n");
return;
}
/* Check the current device */
if (phv->attached_dev != ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC &&
phv->attached_dev != ATTACHED_DEV_AFC_CHARGER_5V_MUIC) {
pr_info("MPing Missing did not happened but AFC protocol did not success\n");
return;
}
mutex_lock(phv->pmutex);
/* We make MPING NACK interrupt virtually */
max77865_hv_muic_detect_dev(phv, phv->irq_mpnack);
mutex_unlock(phv->pmutex);
}
void max77865_hv_muic_init_detect(struct hv_data *phv)
{
int ret;
u8 bcstatus2, vdnmon;
pr_info("%s\n", __func__);
mutex_lock(phv->pmutex);
if (phv->is_boot_dpdnvden == DPDNVDEN_ENABLE)
pr_info("dpdnvden already ENABLE\n");
else if (phv->is_boot_dpdnvden == DPDNVDEN_DISABLE) {
msleep(30);
pr_info("dpdnvden == DISABLE, 30ms delay\n");
} else {
pr_err("dpdnvden is not correct(0x%x)!\n", phv->is_boot_dpdnvden);
goto out;
}
ret = max77865_read_reg(phv->i2c, MAX77865_MUIC_REG_STATUS2_BC, &bcstatus2);
if (ret) {
pr_err("fail to read muic reg(%d)\n", ret);
vdnmon = VDNMON_DONTCARE;
} else
vdnmon = bcstatus2 & BC_STATUS2_DNVDATREF_MASK;
if (vdnmon == VDNMON_LOW)
max77865_hv_muic_detect_dev(phv, phv->irq_vdnmon);
else
pr_info("vdnmon != LOW(0x%x)\n", vdnmon);
out:
mutex_unlock(phv->pmutex);
}
static void max77865_hv_muic_init_check_dpdnvden(struct hv_data *phv)
{
u8 bccontrol2 = 0;
mutex_lock(phv->pmutex);
bccontrol2 = muic_i2c_read_byte(phv->i2c, MAX77865_MUIC_REG_CONTROL2_BC);
if (bccontrol2 < 0) {
pr_err("%s failed to read BC CONTROL2 reg!\n", __func__);
phv->is_boot_dpdnvden = DPDNVDEN_DONTCARE;
} else
phv->is_boot_dpdnvden = bccontrol2 & BC_CONTROL2_DPDNMAN_MASK;
mutex_unlock(phv->pmutex);
}
static void max77865_hv_muic_initialize(struct hv_data *phv)
{
pr_info("%s\n", __func__);
phv->is_afc_handshaking = false;
phv->is_afc_muic_prepare = false;
phv->is_boot_dpdnvden = DPDNVDEN_DONTCARE;
afc_init_data.phv = phv;
INIT_WORK(&afc_init_data.muic_afc_init_work,
max77865_hv_muic_detect_after_charger_init);
INIT_DELAYED_WORK(&phv->hv_muic_qc_vb_work,
max77865_hv_muic_check_qc_vb);
INIT_DELAYED_WORK(&phv->hv_muic_mping_miss_wa,
max77865_hv_muic_check_mping_miss);
}
void max77865_hv_muic_remove(struct hv_data *phv)
{
pr_info("%s\n", __func__);
cancel_delayed_work_sync(&phv->hv_muic_qc_vb_work);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
}
void max77865_hv_muic_remove_wo_free_irq(struct hv_data *phv)
{
pr_info("%s\n", __func__);
cancel_work_sync(&afc_init_data.muic_afc_init_work);
cancel_delayed_work_sync(&phv->hv_muic_qc_vb_work);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
}
/*
* APIs for muic to manage AFC.
*
*/
void hv_initialize(muic_data_t *pmuic, struct hv_data **pphv)
{
pr_info("%s\n", __func__);
hv_afc.attached_dev = 0;
hv_afc.i2c = pmuic->i2c;
hv_afc.pmutex = &pmuic->muic_mutex;
hv_afc.irq_gpio = pmuic->pdata->irq_gpio;
hv_afc.is_muic_ready = pmuic->is_muic_ready;
hv_afc.pmuic = pmuic;
*pphv = &hv_afc;
}
void hv_clear_hvcontrol(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC, 0x00);
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL1, 0x00);
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL2, 0x00);
}
void hv_configure_AFC(struct hv_data *phv)
{
pr_info("%s\n", __func__);
if (!phv) {
pr_err("hv is not ready.\n");
return;
}
max77865_muic_set_afc_ready(phv, false);
phv->afc_count = 0;
max77865_hv_muic_initialize(phv);
/* initial check dpdnvden before cable detection */
max77865_hv_muic_init_check_dpdnvden(phv);
}
void hv_update_status(struct hv_data *phv, int mdev)
{
if (!phv) {
pr_err("%s: hv is not ready.\n", __func__);
return;
}
pr_info("%s mdev %d->%d\n", __func__, phv->attached_dev, mdev);
phv->attached_dev = mdev;
}
bool hv_is_predetach_required(int mdev)
{
pr_info("%s\n", __func__);
switch (mdev) {
case ATTACHED_DEV_TA_MUIC:
case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
case ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC:
case ATTACHED_DEV_AFC_CHARGER_5V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC:
case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC:
#if defined(CONFIG_MUIC_HV_12V)
case ATTACHED_DEV_AFC_CHARGER_12V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC:
#endif
case ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC:
case ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC:
case ATTACHED_DEV_QC_CHARGER_5V_MUIC:
case ATTACHED_DEV_QC_CHARGER_9V_MUIC:
return true;
default:
break;
}
return false;
}
bool hv_do_predetach(struct hv_data *phv, int mdev)
{
bool noti;
pr_info("%s\n", __func__);
if (!phv) {
pr_err("hv is not ready.\n");
return false;
}
noti = max77865_muic_check_change_dev_afc_charger(phv, mdev);
#if !defined(CONFIG_SEC_FACTORY) && defined(CONFIG_MUIC_SUPPORT_CCIC)
if (phv->pmuic->is_ccic_afc_enable == Rp_Abnormal)
noti = true;
#endif
if (noti) {
max77865_muic_set_afc_ready(phv, false);
phv->is_afc_muic_prepare = false;
max77865_hv_muic_reset_hvcontrol_reg(phv);
cancel_delayed_work(&phv->hv_muic_qc_vb_work);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
}
return noti;
}
bool hv_is_running(struct hv_data *phv)
{
return phv->is_afc_muic_prepare;
}
void hv_do_detach(struct hv_data *phv)
{
pr_info("%s\n", __func__);
if (!phv) {
pr_err("hv is not ready.\n");
return;
}
max77865_muic_set_afc_ready(phv, false);
phv->is_afc_muic_prepare = false;
cancel_delayed_work(&phv->hv_muic_qc_vb_work);
pr_info("cancel_delayed_work, Mping missing wa\n");
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
}
/*
* onoff : starting AFC (1), stopping AFC(0)
*/
void hv_set_afc_by_user(struct hv_data *phv, bool onoff)
{
pr_info("%s onof(%d)\n", __func__, onoff);
if (!phv) {
pr_err("hv is not ready.\n");
return;
}
if (onoff)
#if defined(CONFIG_MUIC_HV_12V)
hv_muic_change_afc_voltage(phv->pmuic, MUIC_HV_12V);
#else
hv_muic_change_afc_voltage(phv->pmuic, MUIC_HV_9V);
#endif
else
hv_muic_change_afc_voltage(phv->pmuic, MUIC_HV_5V);
}
void hv_muic_chgdet_ready(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
u8 val = 0;
u8 before, after;
pr_info("%s\n", __func__);
before = muic_i2c_read_byte(i2c, MAX77865_MUIC_REG_CONTROL2_BC);
val |= (0x0 << BC_CONTROL2_DPDRV_SHIFT) |
(MAX77865_ENABLE_BIT << BC_CONTROL2_DPDNMAN_SHIFT);
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_CONTROL2_BC, val);
after = muic_i2c_read_byte(i2c, MAX77865_MUIC_REG_CONTROL2_BC);
pr_info("BCCTL2:[0x%02x]->[0x%02x]\n", before, after);
before = muic_i2c_read_byte(i2c, MAX77865_MUIC_REG_HVCONTROL2);
max77865_hv_muic_write_reg(i2c, MAX77865_MUIC_REG_HVCONTROL2, 0x00);
after = muic_i2c_read_byte(i2c, MAX77865_MUIC_REG_HVCONTROL2);
pr_info("HVCTL2:[0x%02x]->[0x%02x]\n", before, after);
msleep(80);
}