blob: 9fcad16e616fba7279cc1ba83efb0880d32c7202 [file] [log] [blame]
/*
* muic_hv_afc.c
*
* Copyright (C) 2012 Samsung Electronics
* Thomas Ryu <smilesr.ryu@samsung.com>
*
* 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
*/
#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/max77854.h>
#include <linux/mfd/max77854-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_hv.h"
#include "muic_hv_max77854.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"
#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,
FUNC_AFC_9V_TO_QC_PREPARE,
FUNC_AFC_9V_DUPLI_TO_AFC_ERR_V,
FUNC_AFC_9V_DUPLI_TO_AFC_5V,
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,
.hvcontrol1_dpdnvden = DPDNVDEN_ENABLE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DONTCARE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_5V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DONTCARE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_12V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_9V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_ERR_V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_5V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DONTCARE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_12V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_9V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_ERR_V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_5V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DONTCARE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_12V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_5V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_5V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_9V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_ERR_V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DONTCARE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_12V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_5V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_9V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_ERR_V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DONTCARE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_12V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_5V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_9V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_ERR_V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DONTCARE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
#if defined(CONFIG_MUIC_HV_12V)
.status3_vbadc = VBADC_DONTCARE, // 12V should be error
#else
.status3_vbadc = VBADC_AFC_ERR_V,
#endif
.status3_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_9V_TO_AFC_ERR_V,
.next = &afc_9v_to_qc_prepare,
};
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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_5V,
.status3_vdnmon = VDNMON_DONTCARE,
.function_num = FUNC_AFC_9V_TO_AFC_5V,
.next = &afc_9v_to_afc_err_v,
};
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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_9V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DONTCARE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
#if defined(CONFIG_MUIC_HV_12V)
.status3_vbadc = VBADC_DONTCARE, // 12V should be error
#else
.status3_vbadc = VBADC_AFC_ERR_V,
#endif
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_5V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_9V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DONTCARE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_9V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_5V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_ERR_V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_12V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DONTCARE,
.status3_vbadc = VBADC_DONTCARE,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_12V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_9V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_5V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_DISABLE,
.status3_vbadc = VBADC_AFC_ERR_V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_ENABLE,
.status3_vbadc = VBADC_QC_9V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_ENABLE,
.status3_vbadc = VBADC_QC_5V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_ENABLE,
.status3_vbadc = VBADC_QC_9V,
.status3_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,
.hvcontrol1_dpdnvden = DPDNVDEN_ENABLE,
.status3_vbadc = VBADC_QC_5V,
.status3_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:
case ATTACHED_DEV_AFC_CHARGER_12V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC:
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:%s attached_dev(%d)[%c]\n", MUIC_HV_DEV_NAME,
__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:
pr_info("%s:%s cannot change HV(%d)->TA(%d)!\n", MUIC_DEV_NAME,
__func__, phv->attached_dev, new_dev);
after_new_dev = phv->attached_dev;
break;
case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
pr_info("%s:%s HV ID Err - Undefined\n", MUIC_DEV_NAME, __func__);
after_new_dev = ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC;
break;
case ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC:
pr_info("%s:%s HV ID Err - Unsupported\n", MUIC_DEV_NAME, __func__);
after_new_dev = ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC;
break;
default:
pr_info("%s:%s HV ID Err - Supported\n", MUIC_DEV_NAME, __func__);
after_new_dev = ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC;
break;
}
out:
return after_new_dev;
}
/*
static int max77854_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:%s err read REG(0x%02x) [%d]\n", MUIC_HV_DEV_NAME,
__func__, reg, *value);
return *value;
}
*/
static int max77854_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:%s reg[0x%02x] = [0x%02x] + [0x%02x] -> [0x%02x]\n",
MUIC_HV_DEV_NAME, __func__, reg, before_val, value, after_val);
return ret;
}
int max77854_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:%s err read REG(0x%02x) [%d] \n", MUIC_DEV_NAME,
__func__, reg, ret);
new_val = (val & mask) | (before_val & (~mask));
if (before_val ^ new_val) {
ret = max77854_hv_muic_write_reg(i2c, reg, new_val);
if (ret)
pr_err("%s:%s err write REG(0x%02x) [%d]\n",
MUIC_DEV_NAME, __func__, reg, ret);
} else if (debug_en) {
pr_info("%s:%s REG(0x%02x): already [0x%02x], don't write reg\n",
MUIC_DEV_NAME, __func__, reg, before_val);
goto out;
}
if (debug_en) {
after_val = muic_i2c_read_byte(i2c, reg);
if (after_val < 0)
pr_err("%s:%s err read REG(0x%02x) [%d]\n",
MUIC_DEV_NAME, __func__, reg, ret);
pr_info("%s:%s REG(0x%02x): [0x%02x]+[0x%02x:0x%02x]=[0x%02x]\n",
MUIC_DEV_NAME, __func__, reg, before_val,
val, mask, after_val);
}
out:
return after_val;
}
void max77854_hv_muic_reset_hvcontrol_reg(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1, 0x00);
max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL2, 0x00);
}
static void max77854_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:%s afc_muic_ready[%d->%d]\n", MUIC_DEV_NAME, __func__, before, after);
}
static int max77854_hv_muic_state_maintain(struct hv_data *phv)
{
int ret = 0;
pr_info("%s:%s \n", MUIC_HV_DEV_NAME, __func__);
if (phv->attached_dev == ATTACHED_DEV_NONE_MUIC) {
pr_info("%s:%s Detached(%d), need to check after\n", MUIC_HV_DEV_NAME,
__func__, phv->attached_dev);
return ret;
}
return ret;
}
static void max77854_hv_muic_set_afc_after_prepare
(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
u8 value;
pr_info("%s:%s HV charger is detected\n", MUIC_HV_DEV_NAME, __func__);
/* Set HVCONTROL2 = 0x02 */
max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL2,
HVCONTROL2_DP06EN_MASK);
/* Set HVCONTROL1 - disable DPVD, DPDNVDEN */
max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1, 0x20);
/* Set TX DATA */
value = phv->tx_data;
max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVTXBYTE, value);
/* Set HVCONTROL2 = 0x1B */
max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL2, MAX77854_MUIC_HVCONTROL2_1B);
}
static void max77854_hv_muic_set_afc_charger_handshaking
(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
u8 hvtxbyte=0;
u8 hvrxbyte[HVRXBYTE_MAX];
u8 selecthvtxbyte=0;
int i, ret;
int j;
u8 hvrxbyte_str[HVRXBYTE_MAX * 4];
u8 temp_buf[8];
pr_info("%s:%s \n", MUIC_HV_DEV_NAME, __func__);
memset(hvrxbyte, 0x00, sizeof(hvrxbyte));
memset(hvrxbyte_str, 0x00, sizeof(hvrxbyte_str));
memset(temp_buf, 0x00, sizeof(temp_buf));
ret = max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL2, 0x13);
if (IS_ERR_VALUE(ret))
pr_err("%s:%s cannot write hvcontrol2(%d)\n", MUIC_HV_DEV_NAME, __func__, ret);
hvtxbyte = muic_i2c_read_byte(i2c, MAX77854_MUIC_REG_HVTXBYTE);
for(i = 0; i < HVRXBYTE_MAX; i++) {
hvrxbyte[i] = muic_i2c_read_byte(i2c, (MAX77854_MUIC_REG_HVRXBYTE1+i));
if(hvrxbyte[i] == 0x47)
hvrxbyte[i] = 0x46;
if(hvrxbyte[i] == 0)
break;
}
pr_info("%s:%s HVTXBYTE: %02x\n", MUIC_HV_DEV_NAME, __func__, hvtxbyte);
pr_info("%s:%s HVRXBYTE\n", MUIC_HV_DEV_NAME, __func__);
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:%s RXBYTE Error! selected hvtxbyte = %02x\n",
MUIC_HV_DEV_NAME, __func__, selecthvtxbyte);
break;
}
}
if(selecthvtxbyte != 0)
max77854_hv_muic_write_reg(i2c, MAX77854_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)
max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVTXBYTE, selecthvtxbyte);
}
#endif
return;
}
static void max77854_hv_muic_afc_control_ping
(struct hv_data *phv, bool ping_continue)
{
int ret;
pr_info("%s:%s control ping[%d, %c]\n", MUIC_HV_DEV_NAME, __func__,
phv->afc_count, ping_continue ? 'T' : 'F');
if (ping_continue)
ret = max77854_hv_muic_write_reg(phv->i2c, MAX77854_MUIC_REG_HVCONTROL2, 0x5B);
else
ret = max77854_hv_muic_write_reg(phv->i2c, MAX77854_MUIC_REG_HVCONTROL2, 0x03);
if (ret) {
pr_err("%s:%s cannot writing HVCONTROL2 reg(%d)\n",
MUIC_HV_DEV_NAME, __func__, ret);
}
}
static void max77854_hv_muic_qc_charger(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
int ret = 0;
u8 status3 =0;
status3 = muic_i2c_read_byte(i2c, MAX77854_MUIC_REG_STATUS3);
if (status3 < 0) {
pr_err("%s:%s cannot read STATUS3 reg(%d)\n", MUIC_HV_DEV_NAME,
__func__, ret);
}
pr_info("%s:%s STATUS3:0x%02x qc_hv:%x\n", MUIC_HV_DEV_NAME, __func__,
status3, phv->qc_hv);
switch (phv->qc_hv) {
case HV_SUPPORT_QC_9V:
ret = max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1, 0x3D);
break;
case HV_SUPPORT_QC_12V:
ret = max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1, 0x35);
break;
case HV_SUPPORT_QC_20V:
ret = max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1, 0x3F);
break;
case HV_SUPPORT_QC_5V:
ret = max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1, 0x33);
break;
default:
pr_err("%s:%s not support QC Charger\n", MUIC_HV_DEV_NAME, __func__);
break;
}
if (ret) {
pr_err("%s:%s cannot writing HVCONTROL2 reg(%d)\n", MUIC_HV_DEV_NAME,
__func__, ret);
}
}
static void max77854_hv_muic_after_qc_prepare(struct hv_data *phv)
{
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
phv->is_qc_vb_settle = false;
schedule_delayed_work(&phv->hv_muic_qc_vb_work, msecs_to_jiffies(300));
}
static void max77854_hv_muic_adcmode_switch
(struct hv_data *phv, bool always_on)
{
struct i2c_client *i2c = phv->i2c;
int ret;
pr_info("%s:%s always_on:%c\n", MUIC_HV_DEV_NAME, __func__, (always_on ? 'T' : 'F'));
if (always_on) {
set_adc_scan_mode(phv->pmuic,ADC_SCANMODE_CONTINUOUS);
ret = max77854_muic_hv_update_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1,
(MAX77854_ENABLE_BIT << HVCONTROL1_VBUSADCEN_SHIFT),
HVCONTROL1_VBUSADCEN_MASK, true);
} else {
set_adc_scan_mode(phv->pmuic,ADC_SCANMODE_ONESHOT);
/* non MAXIM */
ret = max77854_muic_hv_update_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1,
(MAX77854_DISABLE_BIT << HVCONTROL1_VBUSADCEN_SHIFT),
HVCONTROL1_VBUSADCEN_MASK, true);
}
if (ret < 0)
pr_err("%s:%s cannot switch adcmode(%d)\n", MUIC_HV_DEV_NAME, __func__, ret);
}
static void max77854_hv_muic_adcmode_always_on(struct hv_data *phv)
{
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
max77854_hv_muic_adcmode_switch(phv, true);
}
void max77854_hv_muic_adcmode_oneshot(struct hv_data *phv)
{
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
max77854_hv_muic_adcmode_switch(phv, false);
}
static int max77854_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);
if (mping_missed)
phv->afc_count = 0;
pr_info("%s:%s \n", MUIC_HV_DEV_NAME, __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:%s is_charger_ready[%c], is_afc_muic_prepare[%c]\n",
MUIC_HV_DEV_NAME, __func__,
(phv->is_charger_ready ? 'T' : 'F'),
(phv->is_afc_muic_prepare ? 'T' : 'F'));
return ret;
}
pr_info("%s:%s is_charger_ready[%c], just return\n", MUIC_HV_DEV_NAME,
__func__, (phv->is_charger_ready ? 'T' : 'F'));
return ret;
}
switch (new_afc_data->function_num) {
case FUNC_TA_TO_PREPARE:
max77854_hv_muic_adcmode_always_on(phv);
max77854_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));
break;
case FUNC_PREPARE_TO_PREPARE_DUPLI:
/* attached_dev is changed. MPING Missing did not happened
* Cancel delayed work */
pr_info("%s:%s cancel_delayed_work(dev %d), Mping missing wa\n",
MUIC_HV_DEV_NAME, __func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
phv->afc_count++;
max77854_hv_muic_set_afc_charger_handshaking(phv);
if (!mping_missed)
phv->is_afc_handshaking = true;
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77854_hv_muic_afc_control_ping(phv, false);
} else {
max77854_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:%s cancel_delayed_work(dev %d), Mping missing wa\n",
MUIC_HV_DEV_NAME, __func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
/* ping STOP */
ret = max77854_hv_muic_write_reg(phv->i2c, MAX77854_MUIC_REG_HVCONTROL2, 0x03);
if (ret) {
pr_err("%s:%s cannot writing HVCONTROL2 reg(%d)\n",
MUIC_HV_DEV_NAME, __func__, ret);
}
max77854_hv_muic_qc_charger(phv);
max77854_hv_muic_after_qc_prepare(phv);
break;
case FUNC_PREPARE_DUPLI_TO_PREPARE_DUPLI:
phv->afc_count++;
if (!phv->is_afc_handshaking) {
max77854_hv_muic_set_afc_charger_handshaking(phv);
if (!mping_missed)
phv->is_afc_handshaking = true;
}
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77854_hv_muic_afc_control_ping(phv, false);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_PREPARE_DUPLI_TO_AFC_5V:
if (!phv->is_afc_handshaking) {
max77854_hv_muic_set_afc_charger_handshaking(phv);
if (!mping_missed)
phv->is_afc_handshaking = true;
}
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77854_hv_muic_afc_control_ping(phv, false);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_PREPARE_DUPLI_TO_AFC_ERR_V:
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77854_hv_muic_afc_control_ping(phv, false);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_PREPARE_DUPLI_TO_AFC_9V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_PREPARE_DUPLI_TO_AFC_12V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
#endif
case FUNC_PREPARE_DUPLI_TO_QC_PREPARE:
max77854_hv_muic_qc_charger(phv);
max77854_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:%s cancel_delayed_work(dev %d), Mping missing wa\n",
MUIC_HV_DEV_NAME, __func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
phv->afc_count++;
if (!phv->is_afc_handshaking) {
max77854_hv_muic_set_afc_charger_handshaking(phv);
if (!mping_missed)
phv->is_afc_handshaking = true;
}
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_always_on(phv);
} else {
max77854_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:%s cancel_delayed_work(dev %d), Mping missing wa\n",
MUIC_HV_DEV_NAME, __func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77854_hv_muic_afc_control_ping(phv, false);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
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:%s cancel_delayed_work(dev %d), Mping missing wa\n",
MUIC_HV_DEV_NAME, __func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
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:%s cancel_delayed_work(dev %d), Mping missing wa\n",
MUIC_HV_DEV_NAME, __func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
max77854_hv_muic_afc_control_ping(phv, false);
max77854_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:%s cancel_delayed_work(dev %d), Mping missing wa\n",
MUIC_HV_DEV_NAME, __func__, new_dev);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
max77854_hv_muic_qc_charger(phv);
max77854_hv_muic_after_qc_prepare(phv);
break;
case FUNC_AFC_5V_DUPLI_TO_AFC_5V_DUPLI:
phv->afc_count++;
if (!phv->is_afc_handshaking) {
max77854_hv_muic_set_afc_charger_handshaking(phv);
if (!mping_missed)
phv->is_afc_handshaking = true;
}
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_always_on(phv);
} else {
max77854_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) {
max77854_hv_muic_afc_control_ping(phv, false);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_AFC_5V_DUPLI_TO_AFC_12V:
#endif
case FUNC_AFC_5V_DUPLI_TO_AFC_9V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_5V_DUPLI_TO_QC_PREPARE:
max77854_hv_muic_qc_charger(phv);
max77854_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) {
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_always_on(phv);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_ERR_V_TO_AFC_5V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_ERR_V_TO_AFC_9V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_AFC_ERR_V_TO_AFC_12V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
#endif
case FUNC_AFC_ERR_V_TO_QC_PREPARE:
max77854_hv_muic_qc_charger(phv);
max77854_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) {
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_always_on(phv);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_ERR_V_DUPLI_TO_AFC_5V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_ERR_V_DUPLI_TO_AFC_9V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
#if defined(CONFIG_MUIC_HV_12V)
case FUNC_AFC_ERR_V_DUPLI_TO_AFC_12V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
#endif
case FUNC_AFC_ERR_V_DUPLI_TO_QC_PREPARE:
max77854_hv_muic_qc_charger(phv);
max77854_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) {
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_always_on(phv);
} else {
max77854_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) {
max77854_hv_muic_afc_control_ping(phv, false);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_9V_TO_AFC_5V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_9V_TO_QC_PREPARE:
max77854_hv_muic_qc_charger(phv);
max77854_hv_muic_after_qc_prepare(phv);
break;
case FUNC_AFC_9V_DUPLI_TO_AFC_ERR_V:
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77854_hv_muic_afc_control_ping(phv, false);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_9V_DUPLI_TO_AFC_5V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_9V_DUPLI_TO_AFC_9V_DUPLI:
phv->afc_count++;
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_always_on(phv);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_9V_DUPLI_TO_QC_PREPARE:
max77854_hv_muic_qc_charger(phv);
max77854_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) {
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_always_on(phv);
} else {
max77854_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) {
max77854_hv_muic_afc_control_ping(phv, false);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_12V_TO_AFC_5V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_12V_TO_AFC_9V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_12V_TO_QC_PREPARE:
max77854_hv_muic_qc_charger(phv);
max77854_hv_muic_after_qc_prepare(phv);
break;
case FUNC_AFC_12V_DUPLI_TO_AFC_ERR_V:
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77854_hv_muic_afc_control_ping(phv, false);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_12V_DUPLI_TO_AFC_5V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_12V_DUPLI_TO_AFC_9V:
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_AFC_12V_DUPLI_TO_AFC_12V_DUPLI:
phv->afc_count++;
if (phv->afc_count > AFC_CHARGER_WA_PING) {
max77854_hv_muic_afc_control_ping(phv, false);
max77854_hv_muic_adcmode_always_on(phv);
} else {
max77854_hv_muic_afc_control_ping(phv, true);
noti = false;
}
break;
case FUNC_AFC_12V_DUPLI_TO_QC_PREPARE:
max77854_hv_muic_qc_charger(phv);
max77854_hv_muic_after_qc_prepare(phv);
break;
#endif
case FUNC_QC_PREPARE_TO_QC_5V:
if (phv->is_qc_vb_settle == true)
max77854_hv_muic_adcmode_oneshot(phv);
else
noti = false;
break;
case FUNC_QC_PREPARE_TO_QC_9V:
phv->is_qc_vb_settle = true;
max77854_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_QC_5V_TO_QC_9V:
phv->is_qc_vb_settle = true;
max77854_hv_muic_adcmode_oneshot(phv);
break;
case FUNC_QC_9V_TO_QC_5V:
max77854_hv_muic_adcmode_oneshot(phv);
break;
default:
pr_warn("%s:%s undefinded hv function num(%d)\n", MUIC_HV_DEV_NAME,
__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 || \
new_dev == ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC || \
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:%s cannot find irq #(%d)\n", MUIC_HV_DEV_NAME, __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:%s check_data dev(%d) irq(%d:%d) ret(%c)\n",
MUIC_HV_DEV_NAME, __func__, tmp_afc_data->new_dev,
tmp_afc_data->afc_irq, afc_irq, ret ? 'T' : 'F');
}
return ret;
}
static bool muic_check_hvcontrol1_dpdnvden
(const muic_afc_data_t *tmp_afc_data, u8 dpdnvden)
{
bool ret = false;
if (tmp_afc_data->hvcontrol1_dpdnvden == dpdnvden) {
ret = true;
goto out;
}
if (tmp_afc_data->hvcontrol1_dpdnvden == DPDNVDEN_DONTCARE) {
ret = true;
goto out;
}
out:
if (debug_en_checklist) {
pr_info("%s:%s check_data dev(%d) dpdnvden(0x%x:0x%x) ret(%c)\n",
MUIC_HV_DEV_NAME, __func__, tmp_afc_data->new_dev,
tmp_afc_data->hvcontrol1_dpdnvden, dpdnvden,
ret ? 'T' : 'F');
}
return ret;
}
static bool muic_check_status3_vbadc
(const muic_afc_data_t *tmp_afc_data, u8 vbadc)
{
bool ret = false;
if (tmp_afc_data->status3_vbadc == vbadc) {
ret = true;
goto out;
}
if (tmp_afc_data->status3_vbadc == VBADC_AFC_5V) {
switch (vbadc) {
case VBADC_4V_5V:
case VBADC_5V_6V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->status3_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->status3_vbadc == VBADC_AFC_12V) {
switch (vbadc) {
case VBADC_10V_12V:
case VBADC_12V_13V:
case VBADC_13V_14V:
ret = true;
goto out;
default:
break;
}
}
#endif
if (tmp_afc_data->status3_vbadc == VBADC_AFC_ERR_V_NOT_0) {
switch (vbadc) {
case VBADC_6V_7V:
case VBADC_7V_8V:
#if !defined(CONFIG_MUIC_HV_12V)
case VBADC_10V_12V:
case VBADC_12V_13V:
case VBADC_13V_14V:
#endif
case VBADC_14V_15V:
case VBADC_15V_16V:
case VBADC_16V_17V:
case VBADC_17V_18V:
case VBADC_18V_19V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->status3_vbadc == VBADC_AFC_ERR_V) {
switch (vbadc) {
case VBADC_VBDET:
case VBADC_6V_7V:
case VBADC_7V_8V:
#if !defined(CONFIG_MUIC_HV_12V)
case VBADC_10V_12V:
case VBADC_12V_13V:
case VBADC_13V_14V:
#endif
case VBADC_14V_15V:
case VBADC_15V_16V:
case VBADC_16V_17V:
case VBADC_17V_18V:
case VBADC_18V_19V:
case VBADC_19V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->status3_vbadc == VBADC_QC_5V) {
switch (vbadc) {
case VBADC_4V_5V:
case VBADC_5V_6V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->status3_vbadc == VBADC_QC_9V) {
switch (vbadc) {
case VBADC_6V_7V:
case VBADC_7V_8V:
case VBADC_8V_9V:
case VBADC_9V_10V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->status3_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:
#if !defined(CONFIG_MUIC_HV_12V)
case VBADC_10V_12V:
case VBADC_12V_13V:
case VBADC_13V_14V:
#endif
case VBADC_14V_15V:
case VBADC_15V_16V:
case VBADC_16V_17V:
case VBADC_17V_18V:
case VBADC_18V_19V:
case VBADC_19V:
ret = true;
goto out;
default:
break;
}
}
if (tmp_afc_data->status3_vbadc == VBADC_DONTCARE) {
ret = true;
goto out;
}
out:
if (debug_en_checklist) {
pr_info("%s:%s check_data dev(%d) vbadc(0x%x:0x%x) ret(%c)\n",
MUIC_HV_DEV_NAME, __func__, tmp_afc_data->new_dev,
tmp_afc_data->status3_vbadc, vbadc, ret ? 'T' : 'F');
}
return ret;
}
static bool muic_check_status3_vdnmon
(const muic_afc_data_t *tmp_afc_data, u8 vdnmon)
{
bool ret = false;
if (tmp_afc_data->status3_vdnmon == vdnmon) {
ret = true;
goto out;
}
if (tmp_afc_data->status3_vdnmon == VDNMON_DONTCARE) {
ret = true;
goto out;
}
out:
if (debug_en_checklist) {
pr_info("%s:%s check_data dev(%d) vdnmon(0x%x:0x%x) ret(%c)\n",
MUIC_HV_DEV_NAME, __func__, tmp_afc_data->new_dev,
tmp_afc_data->status3_vdnmon, vdnmon, ret ? 'T' : 'F');
}
return ret;
}
/*
* 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;
}
static bool muic_check_dev_ta(struct hv_data *phv)
{
u8 status1 = phv->vps.status1;
u8 status2 = phv->vps.status2;
u8 adc, vbvolt, chgdetrun, chgtyp;
adc = status1 & STATUS1_ADC_MASK;
vbvolt = status2 & STATUS2_VBVOLT_MASK;
chgdetrun = status2 & STATUS2_CHGDETRUN_MASK;
chgtyp = status2 & STATUS2_CHGTYP_MASK;
if (adc != ADC_OPEN) {
max77854_muic_set_afc_ready(phv, false);
return false;
}
if (muic_hv_is_nonafc_ta(chgtyp)) {
max77854_muic_set_afc_ready(phv, false);
pr_info("%s:%s non AFC Charger.(chgtyp=%d) \n",
MUIC_HV_DEV_NAME, __func__, chgtyp);
return false;
}
if (vbvolt == VB_LOW || chgdetrun == CHGDETRUN_TRUE || chgtyp != CHGTYP_DEDICATED_CHARGER) {
max77854_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;
}
static void max77854_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, mpnack, vbadc;
bool flag_next = true;
bool muic_dev_ta = false;
pr_info("%s:%s irq(%d), attache_dev(%d)\n", MUIC_HV_DEV_NAME, __func__, irq, phv->attached_dev);
if (tmp_afc_data == NULL) {
pr_info("%s:%s non AFC Charger, just return!\n", MUIC_HV_DEV_NAME, __func__);
return;
}
ret = max77854_bulk_read(phv->i2c, MAX77854_MUIC_REG_STATUS1, 3, status);
if (ret) {
pr_err("%s:%s fail to read muic reg(%d)\n", MUIC_DEV_NAME, __func__, ret);
return;
}
pr_info("%s:%s STATUS1:0x%02x, 2:0x%02x, 3:0x%02x\n", MUIC_DEV_NAME, __func__,
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:%s device type is not TA!\n", MUIC_HV_DEV_NAME, __func__);
return;
}
/* attached status */
mpnack = status[2] & STATUS3_MPNACK_MASK;
vdnmon = status[2] & STATUS3_VDNMON_MASK;
vbadc = status[2] & STATUS3_VBADC_MASK;
ret = max77854_bulk_read(i2c, MAX77854_MUIC_REG_HVCONTROL1, 2, hvcontrol);
if (ret) {
pr_err("%s:%s fail to read muic reg(%d)\n", MUIC_HV_DEV_NAME,
__func__, ret);
return;
}
pr_info("%s:%s HVCONTROL1:0x%02x, 2:0x%02x\n", MUIC_HV_DEV_NAME, __func__,
hvcontrol[0], hvcontrol[1]);
/* attached - control */
phv->vps.hvcontrol[0] = hvcontrol[0];
phv->vps.hvcontrol[1] = hvcontrol[1];
dpdnvden = hvcontrol[0] & HVCONTROL1_DPDNVDEN_MASK;
pr_info("%s:%s vdnmon:0x%x mpnack:0x%x vbadc:0x%x dpdnvden:0x%x\n",
MUIC_HV_DEV_NAME, __func__, vdnmon, mpnack, vbadc, dpdnvden);
for (i = 0; i < ATTACHED_DEV_NUM; i++, tmp_afc_data = tmp_afc_data->next) {
if (!flag_next) {
pr_info("%s:%s not found new_dev in afc_condition_checklist\n",
MUIC_HV_DEV_NAME, __func__);
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_hvcontrol1_dpdnvden(tmp_afc_data, dpdnvden)))
continue;
if (!(muic_check_status3_vbadc(tmp_afc_data, vbadc)))
continue;
if(!(muic_check_status3_vdnmon(tmp_afc_data, vdnmon)))
continue;
pr_info("%s:%s checklist match found at i(%d), %s(%d)\n",
MUIC_HV_DEV_NAME, __func__, i, tmp_afc_data->afc_name,
tmp_afc_data->new_dev);
intr = MUIC_INTR_ATTACH;
break;
}
if (intr == MUIC_INTR_ATTACH) {
pr_info("%s:%s AFC ATTACHED\n", MUIC_HV_DEV_NAME, __func__);
pr_info("%s:%s %d->%d\n", MUIC_HV_DEV_NAME, __func__,
phv->attached_dev, tmp_afc_data->new_dev);
ret = max77854_hv_muic_handle_attach(phv, tmp_afc_data);
if (ret)
pr_err("%s:%s cannot handle attach(%d)\n", MUIC_HV_DEV_NAME,
__func__, ret);
} else {
pr_info("%s:%s AFC MAINTAIN (%d)\n", MUIC_HV_DEV_NAME, __func__,
phv->attached_dev);
ret = max77854_hv_muic_state_maintain(phv);
if (ret)
pr_err("%s:%s cannot maintain state(%d)\n", MUIC_HV_DEV_NAME,
__func__, ret);
goto out;
}
out:
return;
}
/* TA setting in max77854-muic.c */
void max77854_muic_prepare_afc_charger(struct hv_data *phv)
{
struct i2c_client *i2c = phv->i2c;
int ret;
pr_info("%s:%s \n", MUIC_DEV_NAME, __func__);
max77854_hv_muic_adcmode_oneshot(phv);
/* set HVCONTROL1=0x11 */
ret = max77854_muic_hv_update_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1,
(0x2 << HVCONTROL1_DPVD_SHIFT), HVCONTROL1_DPVD_MASK, true);
if (ret < 0 )
goto err_write;
ret = max77854_muic_hv_update_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1,
(MAX77854_ENABLE_BIT << HVCONTROL1_DPDNVDEN_SHIFT),
HVCONTROL1_DPDNVDEN_MASK, true);
if (ret < 0)
goto err_write;
/* Set VBusADCEn = 1 after the time of changing adcmode*/
max77854_muic_set_afc_ready(phv, true);
return;
err_write:
pr_err("%s:%s fail to write muic reg(%d)\n", MUIC_DEV_NAME, __func__, ret);
return;
}
/* TA setting in max77854-muic.c */
bool max77854_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 || \
new_dev == ATTACHED_DEV_AFC_CHARGER_12V_MUIC || \
new_dev == ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC || \
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 max77854_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 status3 =0;
pr_info("%s:%s\n", MUIC_DEV_NAME, __func__);
mutex_lock(phv->pmutex);
/* check vdnmon status value */
status3 = muic_i2c_read_byte(phv->i2c, MAX77854_MUIC_REG_STATUS3);
if (status3 < 0 ) {
pr_err("%s:%s fail to read muic reg(%d)\n", MUIC_HV_DEV_NAME,
__func__, ret);
return;
}
pr_info("%s:%s STATUS3:0x%02x\n", MUIC_HV_DEV_NAME, __func__, status3);
if (phv->is_afc_muic_ready) {
if (phv->is_afc_muic_prepare)
max77854_hv_muic_detect_dev(phv, phv->irq_vdnmon);
else
max77854_hv_muic_detect_dev(phv, -1);
}
mutex_unlock(phv->pmutex);
}
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, MAX77854_MUIC_REG_HVTXBYTE);
if (value == tx_data) {
pr_info("%s: same to current voltage %x\n", __func__, value);
return;
}
phv->afc_count = 0;
max77854_hv_muic_write_reg(i2c, MAX77854_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);
max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1, 0x33);
break;
case MUIC_HV_9V:
set_adc_scan_mode(phv->pmuic,ADC_SCANMODE_CONTINUOUS);
max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1, 0x3D);
break;
default:
break;
}
}
/* AFC Charger */
else {
max77854_hv_muic_adcmode_always_on(phv);
max77854_hv_muic_afc_control_ping(phv, true);
}
}
int muic_afc_set_voltage(int vol)
{
muic_data_t *pmuic = hv_afc.pmuic;
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);
} else if (vol == 12) {
hv_muic_change_afc_voltage(pmuic, MUIC_HV_12V);
} else {
pr_warn("%s:%s invalid value\n", MUIC_DEV_NAME, __func__);
return 0;
}
return 1;
}
void max77854_hv_muic_charger_init(void)
{
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
if(afc_init_data.phv) {
if (afc_init_data.phv->is_charger_ready) {
pr_info("%s:%s charger is already ready.\n", MUIC_HV_DEV_NAME, __func__);
return;
}
afc_init_data.phv->is_charger_ready = true;
schedule_work(&afc_init_data.muic_afc_init_work);
}
}
static void max77854_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 status3 =0, vbadc;
if (!phv) {
pr_err("%s:%s cannot read phv!\n", MUIC_HV_DEV_NAME, __func__);
return;
}
mutex_lock(phv->pmutex);
if (phv->is_qc_vb_settle == true) {
pr_info("%s:%s already qc vb settled\n", MUIC_HV_DEV_NAME, __func__);
goto out;
}
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
status3 = muic_i2c_read_byte(phv->i2c, MAX77854_MUIC_REG_STATUS3);
vbadc = status3 & STATUS3_VBADC_MASK;
if (vbadc == VBADC_4V_5V || vbadc == VBADC_5V_6V) {
phv->is_qc_vb_settle = true;
max77854_hv_muic_detect_dev(phv, phv->irq_vbadc);
}
out:
mutex_unlock(phv->pmutex);
return;
}
static void max77854_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);
if (!phv) {
pr_err("%s:%s cannot read phv!\n", MUIC_HV_DEV_NAME, __func__);
return;
}
mutex_lock(phv->pmutex);
/* 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("%s:%s MPing Missing did not happened "
"but AFC protocol did not success\n",
MUIC_HV_DEV_NAME, __func__);
goto out;
}
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
/* We make MPING NACK interrupt virtually */
max77854_hv_muic_detect_dev(phv, phv->irq_mpnack);
out:
mutex_unlock(phv->pmutex);
return;
}
void max77854_hv_muic_init_detect(struct hv_data *phv)
{
int ret;
u8 status3, vdnmon;
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
mutex_lock(phv->pmutex);
if (phv->is_boot_dpdnvden == DPDNVDEN_ENABLE)
pr_info("%s:%s dpdnvden already ENABLE\n", MUIC_HV_DEV_NAME, __func__);
else if (phv->is_boot_dpdnvden == DPDNVDEN_DISABLE) {
mdelay(30);
pr_info("%s:%s dpdnvden == DISABLE, 30ms delay\n", MUIC_HV_DEV_NAME, __func__);
} else {
pr_err("%s:%s dpdnvden is not correct(0x%x)!\n", MUIC_HV_DEV_NAME,
__func__, phv->is_boot_dpdnvden);
goto out;
}
ret = max77854_read_reg(phv->i2c, MAX77854_MUIC_REG_STATUS3, &status3);
if (ret) {
pr_err("%s:%s fail to read muic reg(%d)\n", MUIC_DEV_NAME, __func__, ret);
vdnmon = VDNMON_DONTCARE;
} else
vdnmon = status3 & STATUS3_VDNMON_MASK;
if (vdnmon == VDNMON_LOW)
max77854_hv_muic_detect_dev(phv, phv->irq_vdnmon);
else
pr_info("%s:%s vdnmon != LOW(0x%x)\n", MUIC_HV_DEV_NAME, __func__, vdnmon);
out:
mutex_unlock(phv->pmutex);
}
static void max77854_hv_muic_init_check_dpdnvden(struct hv_data *phv)
{
u8 hvcontrol1 = 0;
//int ret;
mutex_lock(phv->pmutex);
hvcontrol1 = muic_i2c_read_byte(phv->i2c, MAX77854_MUIC_REG_HVCONTROL1);
if (hvcontrol1 < 0) {
pr_err("%s:%s cannot read HVCONTROL1 reg!\n", MUIC_HV_DEV_NAME, __func__);
phv->is_boot_dpdnvden = DPDNVDEN_DONTCARE;
} else
phv->is_boot_dpdnvden = hvcontrol1 & HVCONTROL1_DPDNVDEN_MASK;
mutex_unlock(phv->pmutex);
}
static irqreturn_t max77854_muic_hv_irq(int irq, void *data)
{
struct hv_data *phv = data;
pr_info("%s:%s irq:%d\n", MUIC_HV_DEV_NAME, __func__, irq);
mutex_lock(phv->pmutex);
if (phv->is_muic_ready == false)
pr_info("%s:%s MUIC is not ready, just return\n", MUIC_HV_DEV_NAME,
__func__);
else if (phv->is_afc_muic_ready == false)
pr_info("%s:%s not ready yet(afc_muic_ready[%c])\n", MUIC_HV_DEV_NAME,
__func__, (phv->is_afc_muic_ready ? 'T' : 'F'));
else if (phv->is_charger_ready == false && irq != phv->irq_vdnmon)
pr_info("%s:%s not ready yet(charger_ready[%c])\n", MUIC_HV_DEV_NAME,
__func__, (phv->is_charger_ready ? 'T' : 'F'));
else if (phv->afc_disable)
pr_info("%s:%s AFC disable by USER (afc_disable[%c]\n", MUIC_HV_DEV_NAME,
__func__, (phv->afc_disable ? 'T' : 'F'));
else
max77854_hv_muic_detect_dev(phv, irq);
mutex_unlock(phv->pmutex);
return IRQ_HANDLED;
}
#define REQUEST_HV_IRQ(_irq, _dev_id, _name) \
do { \
ret = request_threaded_irq(_irq, NULL, max77854_muic_hv_irq, \
IRQF_NO_SUSPEND, _name, _dev_id); \
if (ret < 0) { \
pr_err("%s:%s Failed to request IRQ #%d: %d\n", \
MUIC_HV_DEV_NAME, __func__, _irq, ret); \
_irq = 0; \
} \
} while (0)
static int max77854_afc_muic_irq_init(struct hv_data *phv)
{
int ret = 0;
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
if (phv->irq_gpio > 0) {
int irq_base = phv->irq_gpio;
/* request AFC MUIC IRQ */
phv->irq_vdnmon = irq_base + MAX77854_MUIC_IRQ_INT3_VDNMON;
REQUEST_HV_IRQ(phv->irq_vdnmon, phv, "muic-vdnmon");
phv->irq_mrxrdy = irq_base + MAX77854_MUIC_IRQ_MRXRDY;
REQUEST_HV_IRQ(phv->irq_mrxrdy, phv, "muic-mrxrdy");
phv->irq_mpnack = irq_base + MAX77854_MUIC_IRQ_INT3_MPNACK;
REQUEST_HV_IRQ(phv->irq_mpnack, phv, "muic-mpnack");
phv->irq_vbadc = irq_base + MAX77854_MUIC_IRQ_INT3_VBADC;
REQUEST_HV_IRQ(phv->irq_vbadc, phv, "muic-vbadc");
pr_info("%s:%s vdnmon(%d), mrxrdy(%d), mpnack(%d), vbadc(%d)\n",
MUIC_HV_DEV_NAME, __func__,
phv->irq_vdnmon, phv->irq_mrxrdy,
phv->irq_mpnack, phv->irq_vbadc);
}
return ret;
}
#define FREE_HV_IRQ(_irq, _dev_id, _name) \
do { \
if (_irq) { \
free_irq(_irq, _dev_id); \
pr_info("%s:%s IRQ(%d):%s free done\n", MUIC_HV_DEV_NAME, \
__func__, _irq, _name); \
} \
} while (0)
static void max77854_hv_muic_free_irqs(struct hv_data *phv)
{
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
/* free MUIC IRQ */
FREE_HV_IRQ(phv->irq_vdnmon, phv, "muic-vdnmon");
FREE_HV_IRQ(phv->irq_mrxrdy, phv, "muic-mrxrdy");
FREE_HV_IRQ(phv->irq_mpnack, phv, "muic-mpnack");
FREE_HV_IRQ(phv->irq_vbadc, phv, "muic-vbadc");
}
static void max77854_hv_muic_initialize(struct hv_data *phv)
{
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
phv->is_afc_handshaking = false;
phv->is_afc_muic_prepare = false;
//is_charger_ready is set by CHG on boot.
//phv->is_charger_ready = false;
phv->is_boot_dpdnvden = DPDNVDEN_DONTCARE;
afc_init_data.phv = phv;
INIT_WORK(&afc_init_data.muic_afc_init_work, max77854_hv_muic_detect_after_charger_init);
INIT_DELAYED_WORK(&phv->hv_muic_qc_vb_work, max77854_hv_muic_check_qc_vb);
INIT_DELAYED_WORK(&phv->hv_muic_mping_miss_wa, max77854_hv_muic_check_mping_miss);
}
void max77854_hv_muic_remove(struct hv_data *phv)
{
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
// if(afc_init_data.muic_afc_init_work != NULL)
// cancel_work_sync(&afc_init_data.muic_afc_init_work);
// else
// printk("sabin afc_init_data is NULL\n");
cancel_delayed_work_sync(&phv->hv_muic_qc_vb_work);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
max77854_hv_muic_free_irqs(phv);
}
void max77854_hv_muic_remove_wo_free_irq(struct hv_data *phv)
{
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __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:%s\n", MUIC_HV_DEV_NAME, __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;
max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL1, 0x00);
max77854_hv_muic_write_reg(i2c, MAX77854_MUIC_REG_HVCONTROL2, 0x00);
}
void hv_configure_AFC(struct hv_data *phv)
{
int ret = 0;
if (!phv) {
pr_err("%s:%s: hv is not ready.\n", __func__, MUIC_HV_DEV_NAME);
return;
}
pr_info("%s:%s\n", MUIC_HV_DEV_NAME, __func__);
ret = max77854_afc_muic_irq_init(phv);
if (ret < 0) {
pr_err("%s:%s Failed to initialize HV MUIC irq:%d\n", MUIC_DEV_NAME,
__func__, ret);
max77854_hv_muic_free_irqs(phv);
}
max77854_muic_set_afc_ready(phv, false);
phv->afc_count = 0;
max77854_hv_muic_initialize(phv);
/* initial check dpdnvden before cable detection */
max77854_hv_muic_init_check_dpdnvden(phv);
}
void hv_update_status(struct hv_data *phv, int mdev)
{
if (!phv) {
pr_err("%s:%s: hv is not ready.\n", __func__, MUIC_HV_DEV_NAME);
return;
}
pr_info("%s:%s mdev %d->%d\n", __func__, MUIC_HV_DEV_NAME, phv->attached_dev, mdev);
phv->attached_dev = mdev;
}
bool hv_is_predetach_required(int mdev)
{
pr_info("%s:%s\n", __func__, MUIC_HV_DEV_NAME);
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:
case ATTACHED_DEV_AFC_CHARGER_12V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC:
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;
if (!phv) {
pr_err("%s:%s: hv is not ready.\n", __func__, MUIC_HV_DEV_NAME);
return false;
}
pr_info("%s:%s\n", __func__, MUIC_HV_DEV_NAME);
noti = max77854_muic_check_change_dev_afc_charger(phv, mdev);
if (noti) {
max77854_muic_set_afc_ready(phv, false);
phv->is_afc_muic_prepare = false;
max77854_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)
{
if (!phv) {
pr_err("%s:%s: hv is not ready.\n", __func__, MUIC_HV_DEV_NAME);
return;
}
pr_info("%s:%s\n", __func__, MUIC_HV_DEV_NAME);
max77854_muic_set_afc_ready(phv, false);
phv->is_afc_muic_prepare = false;
cancel_delayed_work(&phv->hv_muic_qc_vb_work);
pr_info("%s:%s cancel_delayed_work, Mping missing wa\n",
MUIC_HV_DEV_NAME, __func__);
cancel_delayed_work(&phv->hv_muic_mping_miss_wa);
}
#define HV_DPRESET_VAL 0x01
#define HV_DNSRC_VAL 0x02
#define HV_DPRESET_BIT 3
#define HV_DPRESET_MASK (0x3 << HV_DPRESET_BIT)
#define HV_DPDNVdEN_VAL 1
#define HV_DPDNVdEN_BIT 0
#define HV_DPDNVdEN_MASK 0x01
static void hv_reset_afc(struct hv_data *phv)
{
u8 value = 0, mask = 0;
if (!phv) {
pr_err("%s:%s: hv is not ready.\n", __func__, MUIC_HV_DEV_NAME);
return;
}
pr_info("%s:%s\n", __func__, MUIC_HV_DEV_NAME);
value = HV_DPRESET_VAL << HV_DPRESET_BIT;
value |= HV_DPDNVdEN_VAL;
mask = HV_DPRESET_MASK | HV_DPDNVdEN_MASK;
max77854_muic_hv_update_reg(phv->i2c, MAX77854_MUIC_REG_HVCONTROL1,
value, mask, true);
msleep(60);
value = HV_DNSRC_VAL << HV_DPRESET_BIT;
max77854_muic_hv_update_reg(phv->i2c, MAX77854_MUIC_REG_HVCONTROL1,
value, mask, true);
msleep(60);
}
/*
* onoff : starting AFC (1), stopping AFC(0)
*/
void hv_set_afc_by_user(struct hv_data *phv, bool onoff)
{
pr_info("%s:%s onof(%d)\n", __func__, MUIC_HV_DEV_NAME, onoff);
if (!phv) {
pr_err("%s:%s: hv is not ready.\n", __func__, MUIC_HV_DEV_NAME);
return;
}
hv_reset_afc(phv);
if (onoff && phv->attached_dev == ATTACHED_DEV_TA_MUIC) {
/* Pre-configurations to start AFC from normal charge */
hv_do_detach(phv);
phv->attached_dev = ATTACHED_DEV_NONE_MUIC;
phv->pmuic->attached_dev = ATTACHED_DEV_NONE_MUIC;
}
}