1158 lines
32 KiB
C
1158 lines
32 KiB
C
|
/*
|
||
|
* driver/muic/s2mu106-afc.c - S2MU106 micro USB switch device driver
|
||
|
*
|
||
|
* Copyright (C) 2020 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/wakelock.h>
|
||
|
|
||
|
#include <linux/mfd/samsung/s2mu106.h>
|
||
|
|
||
|
/* MUIC header file */
|
||
|
#include <linux/muic/muic.h>
|
||
|
#include <linux/muic/s2mu106-muic.h>
|
||
|
#include <linux/muic/s2mu106-muic-hv.h>
|
||
|
#ifdef CONFIG_MUIC_MANAGER
|
||
|
#include <linux/muic/muic_interface.h>
|
||
|
#endif
|
||
|
|
||
|
#if defined(CONFIG_MUIC_NOTIFIER)
|
||
|
#include <linux/muic/muic_notifier.h>
|
||
|
#endif /* CONFIG_MUIC_NOTIFIER */
|
||
|
#include <linux/delay.h>
|
||
|
|
||
|
/* powermeter */
|
||
|
#if defined(CONFIG_PM_S2MU106)
|
||
|
#if defined(CONFIG_BATTERY_SAMSUNG_V2)
|
||
|
#include "../battery_v2/include/s2mu106_pmeter.h"
|
||
|
#else
|
||
|
#include "../battery/charger/s2mu106_pmeter.h"
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
static struct s2mu106_muic_data *static_data;
|
||
|
|
||
|
/*
|
||
|
* Functions Prototype
|
||
|
*/
|
||
|
static void s2mu106_hv_muic_handle_attach(struct s2mu106_muic_data *muic_data,
|
||
|
muic_attached_dev_t new_dev);
|
||
|
static void _s2mu106_hv_muic_reset(struct s2mu106_muic_data *muic_data);
|
||
|
static void s2mu106_hv_muic_set_chgtype_usrcmd(struct s2mu106_muic_data *muic_data);
|
||
|
#if defined(CONFIG_CHARGER_S2MU106)
|
||
|
static int s2mu106_hv_muic_set_chg_lv_mode(struct s2mu106_muic_data *muic_data,
|
||
|
t_afc_vol_change afc_status);
|
||
|
#endif
|
||
|
|
||
|
muic_attached_dev_t s2mu106_hv_muic_check_id_err(struct s2mu106_muic_data *muic_data,
|
||
|
muic_attached_dev_t new_dev)
|
||
|
{
|
||
|
muic_attached_dev_t after_new_dev = new_dev;
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
|
||
|
if (!muic_core_hv_is_hv_dev(muic_pdata))
|
||
|
goto out;
|
||
|
|
||
|
switch (new_dev) {
|
||
|
case ATTACHED_DEV_TA_MUIC:
|
||
|
pr_info("%s cannot change HV(%d)->TA(%d)!\n",
|
||
|
__func__, muic_pdata->attached_dev, new_dev);
|
||
|
after_new_dev = muic_pdata->attached_dev;
|
||
|
break;
|
||
|
case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
|
||
|
pr_info("%s Undefined\n", __func__);
|
||
|
after_new_dev = ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC;
|
||
|
break;
|
||
|
case ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC:
|
||
|
pr_info("%s Unsupported\n", __func__);
|
||
|
after_new_dev = ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC;
|
||
|
break;
|
||
|
default:
|
||
|
pr_info("%s Supported\n", __func__);
|
||
|
after_new_dev = ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC;
|
||
|
break;
|
||
|
}
|
||
|
out:
|
||
|
return after_new_dev;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Unit functions
|
||
|
*/
|
||
|
static int s2mu106_hv_muic_write_reg(struct s2mu106_muic_data *muic_data, u8 reg, u8 value)
|
||
|
{
|
||
|
u8 before_val, after_val;
|
||
|
int ret;
|
||
|
struct i2c_client *i2c;
|
||
|
|
||
|
if (muic_data == NULL) {
|
||
|
pr_err("%s data NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
i2c = muic_data->i2c;
|
||
|
|
||
|
s2mu106_read_reg(i2c, reg, &before_val);
|
||
|
ret = s2mu106_write_reg(i2c, reg, value);
|
||
|
s2mu106_read_reg(i2c, reg, &after_val);
|
||
|
pr_info("%s reg[0x%02x] = [0x%02x] + [0x%02x] -> [0x%02x]\n",
|
||
|
__func__, reg, before_val, value, after_val);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int s2mu106_hv_muic_read_reg(struct s2mu106_muic_data *muic_data, u8 reg)
|
||
|
{
|
||
|
u8 reg_val = 0;
|
||
|
struct i2c_client *i2c;
|
||
|
|
||
|
if (muic_data == NULL) {
|
||
|
pr_err("%s data NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
i2c = muic_data->i2c;
|
||
|
|
||
|
s2mu106_read_reg(i2c, reg, ®_val);
|
||
|
pr_info("%s reg[0x%02x] = [0x%02x]\n", __func__, reg, reg_val);
|
||
|
|
||
|
return reg_val;
|
||
|
}
|
||
|
|
||
|
#if defined(CONFIG_MUIC_SUPPORT_POWERMETER)
|
||
|
static int s2mu106_hv_muic_get_vchgin(struct s2mu106_muic_data *muic_data)
|
||
|
{
|
||
|
struct power_supply *psy_pm;
|
||
|
union power_supply_propval val = {0, };
|
||
|
int ret = 0;
|
||
|
|
||
|
if (muic_data == NULL) {
|
||
|
pr_err("%s data NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
psy_pm = muic_data->psy_pm;
|
||
|
|
||
|
#if defined(CONFIG_PM_S2MU106)
|
||
|
if (psy_pm) {
|
||
|
ret = psy_pm->desc->get_property(psy_pm, POWER_SUPPLY_PROP_VCHGIN, &val);
|
||
|
} else {
|
||
|
pr_err("%s: Fail to get pmeter\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (ret) {
|
||
|
pr_err("%s: fail to set power_suppy pmeter property(%d)\n", __func__, ret);
|
||
|
} else {
|
||
|
return val.intval;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/**
|
||
|
* @type: 0 = QC, 1 = AFC (protocol_sw_t)
|
||
|
*/
|
||
|
static void s2mu106_hv_muic_set_protocol_sw(struct s2mu106_muic_data* muic_data,
|
||
|
int type)
|
||
|
{
|
||
|
u8 r_val = 0, w_val = 0;
|
||
|
|
||
|
w_val = r_val = s2mu106_hv_muic_read_reg(muic_data, S2MU106_REG_AFC_LOGIC_CTRL2);
|
||
|
switch (type) {
|
||
|
case MU106_QC_PROTOCOL:
|
||
|
w_val &= (0x4);
|
||
|
break;
|
||
|
case MU106_AFC_PROTOCOL:
|
||
|
w_val |= (0x4);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (r_val == w_val)
|
||
|
return;
|
||
|
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_LOGIC_CTRL2, w_val);
|
||
|
msleep(30);
|
||
|
}
|
||
|
|
||
|
static inline void s2mu106_hv_muic_set_afc_tx_data(struct s2mu106_muic_data* muic_data,
|
||
|
int tx_data)
|
||
|
{
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_TX_BYTE, tx_data);
|
||
|
}
|
||
|
|
||
|
static inline int s2mu106_hv_muic_get_vdnmon_status(struct s2mu106_muic_data* muic_data)
|
||
|
{
|
||
|
return ((s2mu106_hv_muic_read_reg(muic_data, S2MU106_REG_AFC_STATUS) >> STATUS_VDNMON_SHIFT) & 0x1);
|
||
|
}
|
||
|
|
||
|
#if defined(CONFIG_MUIC_SUPPORT_POWERMETER)
|
||
|
static int s2mu106_hv_muic_get_vbus_voltage(struct s2mu106_muic_data *muic_data)
|
||
|
{
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
int val = 0, vchgin = 0;
|
||
|
|
||
|
vchgin = (s2mu106_hv_muic_get_vchgin(muic_data));
|
||
|
pr_info("%s vchgin:(%d)mV, cable:(%d)\n", __func__, vchgin, muic_pdata->attached_dev);
|
||
|
|
||
|
if (IS_VCHGIN_9V(vchgin)) {
|
||
|
val = 9;
|
||
|
} else if (IS_VCHGIN_5V(vchgin)) {
|
||
|
val = 5;
|
||
|
}
|
||
|
|
||
|
return val;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/**
|
||
|
* @irq: 1 ~ 7 = AFC_INT_MASK bit, 8 = PM_INT_VCHGIN (afc_int_t)
|
||
|
* @mask: 1 = Mask, 0 = Not Masked (int_mask_t)
|
||
|
*/
|
||
|
static void s2mu106_hv_muic_irq_mask(struct s2mu106_muic_data* muic_data,
|
||
|
afc_int_t irq, int_mask_t mask)
|
||
|
{
|
||
|
u8 reg_val = 0;
|
||
|
|
||
|
if (MU106_IRQ_VDNMON <= irq && irq <= MU106_IRQ_MRXRDY) {
|
||
|
reg_val = s2mu106_hv_muic_read_reg(muic_data, S2MU106_REG_AFC_INT_MASK);
|
||
|
if (mask == ((reg_val >> irq) & 0x1))
|
||
|
return;
|
||
|
|
||
|
reg_val &= ~(0x1 << irq);
|
||
|
reg_val |= (mask << irq);
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_INT_MASK, reg_val);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void s2mu106_hv_muic_set_qc_voltage(struct s2mu106_muic_data *muic_data, int qc_type)
|
||
|
{
|
||
|
u8 r_val = 0, w_val = 0;
|
||
|
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
w_val = r_val = s2mu106_hv_muic_read_reg(muic_data, S2MU106_REG_AFC_CTRL1);
|
||
|
w_val &= ~(AFCCTRL1_DNVD_MASK | AFCCTRL1_DPVD_MASK);
|
||
|
|
||
|
switch (qc_type) {
|
||
|
case QC_5V:
|
||
|
w_val |= (DP_0p6V_MASK | DN_GND_MASK);
|
||
|
break;
|
||
|
case QC_9V:
|
||
|
w_val |= (DP_3p3V_MASK | DN_0p6V_MASK);
|
||
|
break;
|
||
|
case QC_12V:
|
||
|
w_val |= (DP_0p6V_MASK | DN_0p6V_MASK);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (r_val != w_val) {
|
||
|
#if defined(CONFIG_CHARGER_S2MU106)
|
||
|
if (qc_type == QC_5V)
|
||
|
s2mu106_hv_muic_set_chg_lv_mode(muic_data, S2MU106_AFC_9V_to_5V);
|
||
|
else if (qc_type == QC_9V)
|
||
|
s2mu106_hv_muic_set_chg_lv_mode(muic_data, S2MU106_AFC_5V_to_9V);
|
||
|
#endif
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_CTRL1, w_val);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void s2mu106_hv_muic_send_mping(struct s2mu106_muic_data* muic_data)
|
||
|
{
|
||
|
u8 reg_val = 0;
|
||
|
|
||
|
msleep(30);
|
||
|
|
||
|
(muic_data->mping_cnt)++;
|
||
|
pr_info("%s mping_cnt(%d)\n", __func__, muic_data->mping_cnt);
|
||
|
|
||
|
reg_val = s2mu106_hv_muic_read_reg(muic_data, S2MU106_REG_AFC_CTRL2);
|
||
|
reg_val |= AFCCTRL2_MTXEN_MASK;
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_CTRL2, reg_val);
|
||
|
|
||
|
cancel_delayed_work(&muic_data->mping_retry_work);
|
||
|
schedule_delayed_work(&muic_data->mping_retry_work, msecs_to_jiffies(90));
|
||
|
|
||
|
/* TBD mping W/A for mrxtrf
|
||
|
reg_val |= (AFCCTRL2_MTXEN_MASK | AFCCTRL2_DNRESEN_MASK);
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_CTRL2, reg_val);
|
||
|
usleep_range(10000, 12000);
|
||
|
reg_val |= AFCCTRL2_MPING_MASK;*/
|
||
|
}
|
||
|
|
||
|
static void _s2mu106_hv_muic_reset(struct s2mu106_muic_data *muic_data)
|
||
|
{
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
/* set reg default value */
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_CTRL1, 0);
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_CTRL2, 0);
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_TX_BYTE, 0);
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_LOGIC_CTRL2, 0x1);
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_LDOADC_VSETL, 0x7c);
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_INT_MASK, 0);
|
||
|
|
||
|
muic_pdata->hv_state = HV_STATE_IDLE;
|
||
|
muic_data->is_dp_drive = false;
|
||
|
muic_data->is_hvcharger_detected = false;
|
||
|
muic_data->mrxrdy_cnt = 0;
|
||
|
muic_data->mping_cnt = 0;
|
||
|
muic_data->qc_retry_cnt = 0;
|
||
|
muic_data->qc_retry_wait_cnt = 0;
|
||
|
//muic_data->received_tx_data = MUIC_HV_9V;
|
||
|
|
||
|
cancel_delayed_work(&muic_data->reset_work);
|
||
|
cancel_delayed_work(&muic_data->mping_retry_work);
|
||
|
cancel_delayed_work(&muic_data->qc_retry_work);
|
||
|
}
|
||
|
|
||
|
#if !IS_ENABLED(CONFIG_SEC_FACTORY)
|
||
|
static bool _s2mu106_hv_muic_check_afc_enabled(struct s2mu106_muic_data *muic_data)
|
||
|
{
|
||
|
char *str = NULL;
|
||
|
#if IS_ENABLED(CONFIG_MUIC_MANAGER)
|
||
|
struct muic_interface_t *muic_if;
|
||
|
|
||
|
if (muic_data == NULL) {
|
||
|
pr_err("%s data NULL\n", __func__);
|
||
|
return false;
|
||
|
}
|
||
|
muic_if = (struct muic_interface_t *)muic_data->if_data;
|
||
|
#endif
|
||
|
|
||
|
if (muic_data->afc_check == false) {
|
||
|
str = "Unsupported DCP";
|
||
|
} else if (muic_data->pdata->afc_disable == true) {
|
||
|
str = "User Disable";
|
||
|
s2mu106_hv_muic_set_chgtype_usrcmd(muic_data);
|
||
|
#if IS_ENABLED(CONFIG_MUIC_MANAGER)
|
||
|
} else if (muic_if->is_afc_pdic_ready == false) {
|
||
|
str = "VBUS-CC Short";
|
||
|
pr_info("%s short detected, revert dev to TA\n", __func__);
|
||
|
s2mu106_hv_muic_handle_attach(muic_data, ATTACHED_DEV_TA_MUIC);
|
||
|
#endif
|
||
|
#if defined(CONFIG_LEDS_S2MU106_FLASH)
|
||
|
} else if (muic_data->is_requested_step_down == true) {
|
||
|
str = "Flash from CAM";
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (str) {
|
||
|
pr_info("%s afc disable reason:%s\n", __func__, str);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static void _s2mu106_hv_muic_dcp_charger_attach(struct s2mu106_muic_data *muic_data)
|
||
|
{
|
||
|
struct muic_platform_data *muic_pdata;
|
||
|
|
||
|
if (muic_data == NULL) {
|
||
|
pr_err("%s data NULL\n", __func__);
|
||
|
return;
|
||
|
}
|
||
|
muic_pdata = muic_data->pdata;
|
||
|
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_DCP_DETECTED);
|
||
|
}
|
||
|
|
||
|
#if defined(CONFIG_CHARGER_S2MU106)
|
||
|
static int s2mu106_hv_muic_set_chg_lv_mode(struct s2mu106_muic_data *muic_data,
|
||
|
t_afc_vol_change afc_status)
|
||
|
{
|
||
|
struct power_supply *psy_chg;
|
||
|
union power_supply_propval val;
|
||
|
int ret = 0;
|
||
|
|
||
|
if (muic_data == NULL) {
|
||
|
pr_err("%s data NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
psy_chg = muic_data->psy_chg;
|
||
|
if (!muic_data->psy_chg)
|
||
|
psy_chg = muic_data->psy_chg = get_power_supply_by_name("s2mu106-charger");
|
||
|
|
||
|
if (psy_chg) {
|
||
|
if (afc_status == S2MU106_AFC_5V_to_9V) {
|
||
|
val.intval = 1;
|
||
|
} else if (afc_status == S2MU106_AFC_9V_to_5V) {
|
||
|
val.intval = 0;
|
||
|
} else {
|
||
|
pr_err("%s invalid pram, afc_status:%d\n", __func__, afc_status);
|
||
|
return -1;
|
||
|
}
|
||
|
ret = psy_chg->desc->set_property(psy_chg, POWER_SUPPLY_PROP_2LV_3LV_CHG_MODE, &val);
|
||
|
usleep_range(10000, 11000);
|
||
|
} else {
|
||
|
pr_err("%s: Fail to get charger\n", __func__);
|
||
|
ret = -1;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static void s2mu106_hv_muic_set_chgtype_usrcmd(struct s2mu106_muic_data *muic_data)
|
||
|
{
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
int vbus = 0;
|
||
|
u8 device_typ1 = 0;
|
||
|
|
||
|
vbus = s2mu106_hv_muic_get_vchgin(muic_data);
|
||
|
device_typ1 = s2mu106_i2c_read_byte(muic_data->i2c, S2MU106_REG_DEVICE_TYP1);
|
||
|
pr_info("%s vbus = %d, afc_disable = %d, DEVICE_TYPE1 = %#x\n",
|
||
|
__func__, vbus, muic_pdata->afc_disable, device_typ1);
|
||
|
|
||
|
if ((device_typ1 & DEVICE_TYP1_DCPCHG_MASK) == 0)
|
||
|
return;
|
||
|
|
||
|
if (muic_pdata->afc_disable == true) {
|
||
|
/* Set DCP 5V type, because user turned off high-voltage charging */
|
||
|
_s2mu106_hv_muic_reset(muic_data);
|
||
|
s2mu106_hv_muic_handle_attach(muic_data, ATTACHED_DEV_AFC_CHARGER_DISABLED_MUIC);
|
||
|
} else {
|
||
|
/* Since user activate high-voltage charging again, do it again from HVDCP detection */
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_DCP_DETECTED);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void s2mu106_if_hv_muic_reset(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
(struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
_s2mu106_hv_muic_reset(muic_data);
|
||
|
}
|
||
|
|
||
|
static void s2mu106_if_hv_muic_dcp_charger(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = (struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
msleep(200);
|
||
|
|
||
|
/* Enable Afc Block */
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_CTRL1,
|
||
|
(AFCCTRL1_AFCEN_MASK | AFCCTRL1_DPDNVDEN_MASK));
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_LDOADC_VSETL, 0x63);
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_CTRL2,
|
||
|
(AFCCTRL2_DP06EN_MASK | AFCCTRL2_DNRESEN_MASK));
|
||
|
muic_data->is_dp_drive = true;
|
||
|
}
|
||
|
|
||
|
static void s2mu106_if_hv_muic_fast_charge_adaptor(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = (struct s2mu106_muic_data *)mdata;
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
bool afc_enabled = true;
|
||
|
|
||
|
pr_info("%s attached_dev:%d, hv_state:%d\n", __func__,
|
||
|
muic_pdata->attached_dev, muic_pdata->hv_state);
|
||
|
|
||
|
if (muic_data->is_hvcharger_detected == false) {
|
||
|
s2mu106_hv_muic_irq_mask(muic_data, MU106_IRQ_VDNMON, MU106_MASK);
|
||
|
s2mu106_hv_muic_irq_mask(muic_data, MU106_IRQ_MPNACK, MU106_MASK);
|
||
|
s2mu106_hv_muic_set_protocol_sw(muic_data, MU106_AFC_PROTOCOL);
|
||
|
|
||
|
muic_data->is_hvcharger_detected = true;
|
||
|
muic_data->tx_data = ((AFCTXBYTE_9V << 4) | AFCTXBYTE_1p65A);
|
||
|
s2mu106_hv_muic_set_afc_tx_data(muic_data, muic_data->tx_data);
|
||
|
muic_data->mrxrdy_cnt = 0;
|
||
|
muic_data->mping_cnt = 0;
|
||
|
s2mu106_hv_muic_handle_attach(muic_data, ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC);
|
||
|
#if defined(CONFIG_CHARGER_S2MU106)
|
||
|
s2mu106_hv_muic_set_chg_lv_mode(muic_data, S2MU106_AFC_5V_to_9V);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#if !IS_ENABLED(CONFIG_SEC_FACTORY)
|
||
|
afc_enabled = _s2mu106_hv_muic_check_afc_enabled(muic_data);
|
||
|
#endif
|
||
|
if (afc_enabled) {
|
||
|
/* 1st mping */
|
||
|
s2mu106_hv_muic_send_mping(muic_data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void s2mu106_if_hv_muic_fast_charge_communication(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
(struct s2mu106_muic_data *)mdata;
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
cancel_delayed_work(&muic_data->mping_retry_work);
|
||
|
pr_info("%s mrxrdy_cnt(%d)\n", __func__, muic_data->mrxrdy_cnt);
|
||
|
|
||
|
if (muic_pdata->hv_state == HV_STATE_IDLE) {
|
||
|
pr_info("%s emergency exit\n", __func__);
|
||
|
} else if (muic_data->mping_cnt < AFC_MPING_RETRY_CNT_LIMIT) {
|
||
|
s2mu106_hv_muic_send_mping(muic_data);
|
||
|
} else {
|
||
|
muic_data->mrxrdy_cnt = 0;
|
||
|
muic_data->mping_cnt = 0;
|
||
|
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_NO_RESPONSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void s2mu106_if_hv_muic_afc_5v_charger(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
(struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
s2mu106_hv_muic_handle_attach(muic_data, ATTACHED_DEV_AFC_CHARGER_5V_MUIC);
|
||
|
}
|
||
|
|
||
|
static void s2mu106_if_hv_muic_afc_9v_charger(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
(struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
cancel_delayed_work(&muic_data->mping_retry_work);
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
msleep(30);
|
||
|
|
||
|
s2mu106_hv_muic_handle_attach(muic_data, ATTACHED_DEV_AFC_CHARGER_9V_MUIC);
|
||
|
}
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_MUIC_NOT_SUPPORT_QC)
|
||
|
static void s2mu106_if_hv_muic_qc_charger(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = (struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
s2mu106_hv_muic_handle_attach(muic_data, ATTACHED_DEV_TA_MUIC);
|
||
|
}
|
||
|
#else
|
||
|
static void s2mu106_if_hv_muic_qc_charger(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
(struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
cancel_delayed_work(&muic_data->mping_retry_work);
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
msleep(30);
|
||
|
|
||
|
s2mu106_hv_muic_handle_attach(muic_data, ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC);
|
||
|
|
||
|
s2mu106_hv_muic_set_protocol_sw(muic_data, MU106_QC_PROTOCOL);
|
||
|
s2mu106_hv_muic_set_qc_voltage(muic_data, QC_9V);
|
||
|
|
||
|
schedule_delayed_work(&muic_data->qc_retry_work, msecs_to_jiffies(150));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static void s2mu106_if_hv_muic_qc_5v_charger(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
(struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
s2mu106_hv_muic_handle_attach(muic_data, ATTACHED_DEV_QC_CHARGER_5V_MUIC);
|
||
|
}
|
||
|
|
||
|
static void s2mu106_if_hv_muic_qc_9v_charger(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
(struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
s2mu106_hv_muic_handle_attach(muic_data, ATTACHED_DEV_QC_CHARGER_9V_MUIC);
|
||
|
}
|
||
|
|
||
|
static void s2mu106_hv_muic_handle_attach(struct s2mu106_muic_data* muic_data,
|
||
|
muic_attached_dev_t new_dev)
|
||
|
{
|
||
|
pr_info("%s new_dev(%d)\n", __func__, new_dev);
|
||
|
|
||
|
muic_data->pdata->attached_dev = new_dev;
|
||
|
|
||
|
switch (new_dev) {
|
||
|
case ATTACHED_DEV_TA_MUIC:
|
||
|
case ATTACHED_DEV_AFC_CHARGER_DISABLED_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_QC_CHARGER_PREPARE_MUIC:
|
||
|
case ATTACHED_DEV_QC_CHARGER_5V_MUIC:
|
||
|
case ATTACHED_DEV_QC_CHARGER_ERR_V_MUIC:
|
||
|
case ATTACHED_DEV_QC_CHARGER_9V_MUIC:
|
||
|
MUIC_SEND_NOTI_ATTACH(new_dev);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void s2mu106_hv_muic_set_ready(struct s2mu106_muic_data* muic_data)
|
||
|
{
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
pr_info("%s attached_dev:%d, hv_state:%d\n", __func__,
|
||
|
muic_pdata->attached_dev, muic_pdata->hv_state);
|
||
|
switch (muic_pdata->attached_dev) {
|
||
|
case ATTACHED_DEV_TA_MUIC:
|
||
|
case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
|
||
|
case ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC:
|
||
|
if (muic_pdata->hv_state == HV_STATE_IDLE) {
|
||
|
_s2mu106_hv_muic_dcp_charger_attach(muic_data);
|
||
|
} else if (muic_pdata->hv_state == HV_STATE_FAST_CHARGE_ADAPTOR)
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_FAST_CHARGE_REOPEN);
|
||
|
break;
|
||
|
case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
|
||
|
if (muic_pdata->hv_state == HV_STATE_FAST_CHARGE_ADAPTOR) {
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_FAST_CHARGE_REOPEN);
|
||
|
}
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* MUIC Driver Interface functions
|
||
|
*/
|
||
|
static int s2mu106_if_check_afc_ready(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = (struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
s2mu106_hv_muic_set_ready(muic_data);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int s2mu106_if_reset_hvcontrol_reg(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = (struct s2mu106_muic_data *)mdata;
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
muic_core_hv_init(muic_pdata);
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static muic_attached_dev_t s2mu106_if_check_id_err(void *mdata,
|
||
|
muic_attached_dev_t new_dev)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = (struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
return s2mu106_hv_muic_check_id_err(muic_data, new_dev);
|
||
|
}
|
||
|
|
||
|
static void s2mu106_if_set_afc_ready(void *mdata, bool en)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = (struct s2mu106_muic_data *)mdata;
|
||
|
if (en)
|
||
|
s2mu106_hv_muic_set_ready(muic_data);
|
||
|
}
|
||
|
|
||
|
#if defined(CONFIG_MUIC_SUPPORT_POWERMETER)
|
||
|
static int s2mu106_if_get_vbus_voltage(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
(struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
return s2mu106_hv_muic_get_vbus_voltage(muic_data);
|
||
|
}
|
||
|
|
||
|
static int s2mu106_if_vchgin_isr(void *mdata, int voltage)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
(struct s2mu106_muic_data *)mdata;
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
int vchgin = 0;
|
||
|
|
||
|
if (muic_data->is_hvcharger_detected == false)
|
||
|
return 0;
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
|
||
|
usleep_range(10000, 12000);
|
||
|
vchgin = s2mu106_hv_muic_get_vchgin(muic_data);
|
||
|
if (IS_VCHGIN_9V(vchgin)) {
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_VBUS_BOOST);
|
||
|
} else if (IS_VCHGIN_5V(vchgin)) {
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_VBUS_REDUCE);
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef CONFIG_HV_MUIC_VOLTAGE_CTRL
|
||
|
static inline int s2mu106_if_set_afc_voltage(void *mdata, int vol)
|
||
|
{
|
||
|
return s2mu106_muic_afc_set_voltage(vol);
|
||
|
}
|
||
|
|
||
|
void s2mu106_if_hv_muic_afc_ping(struct s2mu106_muic_data *muic_data)
|
||
|
{
|
||
|
u8 reg_val;
|
||
|
int i;
|
||
|
muic_data->mping_cnt = 0;
|
||
|
s2mu106_hv_muic_set_afc_tx_data(muic_data, muic_data->tx_data);
|
||
|
|
||
|
for (i=0; i<4; i++) {
|
||
|
msleep(50);
|
||
|
(muic_data->mping_cnt)++;
|
||
|
reg_val = (AFCCTRL2_MTXEN_MASK | AFCCTRL2_DNRESEN_MASK |
|
||
|
AFCCTRL2_DP06EN_MASK);
|
||
|
s2mu106_hv_muic_write_reg(muic_data, S2MU106_REG_AFC_CTRL2, reg_val);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void s2mu106_hv_muic_change_afc_voltage(struct s2mu106_muic_data *muic_data, int tx_data)
|
||
|
{
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
|
||
|
pr_info("%s attached_dev:%d, tx_data:%#x, hv_state:%d\n", __func__,
|
||
|
muic_pdata->attached_dev, tx_data, muic_pdata->hv_state);
|
||
|
|
||
|
#if defined(CONFIG_LEDS_S2MU106_FLASH)
|
||
|
if (tx_data == MUIC_HV_5V) {
|
||
|
muic_data->is_requested_step_down = true;
|
||
|
} else if (tx_data == MUIC_HV_9V) {
|
||
|
muic_data->is_requested_step_down = false;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
switch (muic_pdata->attached_dev) {
|
||
|
case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
|
||
|
if (tx_data == MUIC_HV_9V)
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_FAST_CHARGE_REOPEN);
|
||
|
break;
|
||
|
case ATTACHED_DEV_QC_CHARGER_9V_MUIC:
|
||
|
if (tx_data == MUIC_HV_5V)
|
||
|
s2mu106_hv_muic_set_qc_voltage(muic_data, QC_5V);
|
||
|
break;
|
||
|
case ATTACHED_DEV_QC_CHARGER_5V_MUIC:
|
||
|
if (tx_data == MUIC_HV_9V)
|
||
|
s2mu106_hv_muic_set_qc_voltage(muic_data, QC_9V);
|
||
|
break;
|
||
|
case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
|
||
|
if (tx_data == MUIC_HV_5V) {
|
||
|
muic_data->tx_data = ((AFCTXBYTE_5V << 4) | AFCTXBYTE_1p95A);
|
||
|
s2mu106_if_hv_muic_afc_ping(muic_data);
|
||
|
}
|
||
|
break;
|
||
|
case ATTACHED_DEV_AFC_CHARGER_5V_MUIC:
|
||
|
if (tx_data == MUIC_HV_9V) {
|
||
|
muic_data->tx_data = ((AFCTXBYTE_9V << 4) | AFCTXBYTE_1p65A);
|
||
|
s2mu106_if_hv_muic_afc_ping(muic_data);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int s2mu106_muic_afc_get_voltage(void)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = static_data;
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
int ret = -1;
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
switch (muic_pdata->attached_dev) {
|
||
|
case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
|
||
|
case ATTACHED_DEV_QC_CHARGER_9V_MUIC:
|
||
|
ret = 9;
|
||
|
break;
|
||
|
case ATTACHED_DEV_TA_MUIC:
|
||
|
case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
|
||
|
case ATTACHED_DEV_AFC_CHARGER_5V_MUIC:
|
||
|
case ATTACHED_DEV_AFC_CHARGER_DISABLED_MUIC:
|
||
|
case ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC:
|
||
|
case ATTACHED_DEV_QC_CHARGER_5V_MUIC:
|
||
|
ret = 5;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
pr_info("%s attached_dev(%d)\n", __func__, muic_pdata->attached_dev);
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int s2mu106_muic_afc_set_voltage(int vol)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = static_data;
|
||
|
int ret;
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
if (vol == 5) {
|
||
|
#if defined(CONFIG_CHARGER_S2MU106)
|
||
|
s2mu106_hv_muic_set_chg_lv_mode(muic_data, S2MU106_AFC_9V_to_5V);
|
||
|
#endif
|
||
|
s2mu106_hv_muic_change_afc_voltage(muic_data, MUIC_HV_5V);
|
||
|
ret = 1;
|
||
|
} else if (vol == 9) {
|
||
|
#if defined(CONFIG_CHARGER_S2MU106)
|
||
|
s2mu106_hv_muic_set_chg_lv_mode(muic_data, S2MU106_AFC_5V_to_9V);
|
||
|
#endif
|
||
|
s2mu106_hv_muic_change_afc_voltage(muic_data, MUIC_HV_9V);
|
||
|
ret = 1;
|
||
|
} else {
|
||
|
pr_warn("%s invalid value\n", __func__);
|
||
|
ret = 0;
|
||
|
}
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#endif /* CONFIG_HV_MUIC_VOLTAGE_CTRL */
|
||
|
|
||
|
static void s2mu106_if_set_chgtype_usrcmd(void *mdata)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = (struct s2mu106_muic_data *)mdata;
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
s2mu106_hv_muic_set_chgtype_usrcmd(muic_data);
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Work queue functions
|
||
|
*/
|
||
|
/* No response - timeout */
|
||
|
static void s2mu106_hv_muic_reset_work(struct work_struct *work)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
container_of(work, struct s2mu106_muic_data, reset_work.work);
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_NO_RESPONSE);
|
||
|
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
}
|
||
|
|
||
|
static void s2mu106_hv_muic_mping_retry_work(struct work_struct *work)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
container_of(work, struct s2mu106_muic_data, mping_retry_work.work);
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
cancel_delayed_work(&muic_data->mping_retry_work);
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
if (muic_data->mping_cnt < AFC_MPING_RETRY_CNT_LIMIT) {
|
||
|
s2mu106_hv_muic_send_mping(muic_data);
|
||
|
} else {
|
||
|
/* No response about mping by 20 times */
|
||
|
muic_data->mrxrdy_cnt = 0;
|
||
|
muic_data->mping_cnt = 0;
|
||
|
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_NO_RESPONSE);
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
}
|
||
|
|
||
|
static void s2mu106_hv_muic_qc_retry_work(struct work_struct *work)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data =
|
||
|
container_of(work, struct s2mu106_muic_data, qc_retry_work.work);
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
#if defined(CONFIG_MUIC_SUPPORT_POWERMETER)
|
||
|
int vchgin = 0;
|
||
|
#endif
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
cancel_delayed_work(&muic_data->qc_retry_work);
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
#if defined(CONFIG_MUIC_SUPPORT_POWERMETER)
|
||
|
msleep(50);
|
||
|
vchgin = s2mu106_hv_muic_get_vchgin(muic_data);
|
||
|
if (vchgin >= 5500 && vchgin < 8000) {
|
||
|
pr_info("%s vchgin(%d) wait_cnt(%d)\n", __func__, vchgin,
|
||
|
muic_data->qc_retry_wait_cnt);
|
||
|
if (muic_data->qc_retry_wait_cnt < AFC_QC_RETRY_WAIT_CNT_LIMIT) {
|
||
|
schedule_delayed_work(&muic_data->qc_retry_work, msecs_to_jiffies(150));
|
||
|
}
|
||
|
muic_data->qc_retry_wait_cnt++;
|
||
|
goto exit;
|
||
|
} else if (IS_VCHGIN_9V(vchgin)) {
|
||
|
muic_data->qc_retry_wait_cnt = 0;
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_VBUS_BOOST);
|
||
|
goto exit;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
muic_data->qc_retry_cnt++;
|
||
|
|
||
|
if (muic_data->qc_retry_cnt < AFC_QC_RETRY_CNT_LIMIT) {
|
||
|
s2mu106_hv_muic_set_qc_voltage(muic_data, QC_5V);
|
||
|
msleep(30);
|
||
|
s2mu106_hv_muic_set_qc_voltage(muic_data, QC_9V);
|
||
|
|
||
|
schedule_delayed_work(&muic_data->qc_retry_work, msecs_to_jiffies(150));
|
||
|
} else {
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_NO_RESPONSE);
|
||
|
}
|
||
|
#if defined(CONFIG_MUIC_SUPPORT_POWERMETER)
|
||
|
exit:
|
||
|
#endif
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* ISR functions
|
||
|
*/
|
||
|
static irqreturn_t s2mu106_hv_muic_vdnmon_isr(int irq, void *data)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = data;
|
||
|
u8 vdnmon = 0;
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
int vbus = 0;
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
|
||
|
vdnmon = s2mu106_hv_muic_get_vdnmon_status(muic_data);
|
||
|
pr_info("%s vdnmon(%s)\n", __func__, (vdnmon ? "High" : "Low"));
|
||
|
|
||
|
if (muic_data->is_dp_drive && !vdnmon) {
|
||
|
msleep(110);
|
||
|
vbus = s2mu106_i2c_read_byte(muic_data->i2c, S2MU106_REG_DEVICE_APPLE);
|
||
|
vbus &= DEVICE_APPLE_VBUS_WAKEUP_MASK;
|
||
|
if (vbus) {
|
||
|
if (muic_data->pdata->afc_disable == false)
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_VDNMON_LOW);
|
||
|
else
|
||
|
s2mu106_hv_muic_set_chgtype_usrcmd(muic_data);
|
||
|
} else
|
||
|
pr_err("%s skip to handle vdnmon: invalid vbus\n", __func__);
|
||
|
} else {
|
||
|
pr_err("%s afc blocked is_dp_drive:%d, afc_disable:%d\n",
|
||
|
__func__, muic_data->is_dp_drive, muic_data->pdata->afc_disable);
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
static irqreturn_t s2mu106_hv_muic_mpnack_isr(int irq, void *data)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = data;
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
if (muic_data->is_hvcharger_detected == false) {
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
return IRQ_NONE;
|
||
|
}
|
||
|
|
||
|
pr_info("%s \n", __func__);
|
||
|
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
static irqreturn_t s2mu106_hv_muic_mrxtrf_isr(int irq, void *data)
|
||
|
{
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
static irqreturn_t s2mu106_hv_muic_mrxrdy_isr(int irq, void *data)
|
||
|
{
|
||
|
struct s2mu106_muic_data *muic_data = data;
|
||
|
struct muic_platform_data *muic_pdata = muic_data->pdata;
|
||
|
|
||
|
mutex_lock(&muic_data->afc_mutex);
|
||
|
if (muic_data->is_hvcharger_detected == false) {
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
return IRQ_NONE;
|
||
|
}
|
||
|
|
||
|
cancel_delayed_work(&muic_data->mping_retry_work);
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
muic_data->mrxrdy_cnt++;
|
||
|
muic_core_hv_state_manager(muic_pdata, HV_TRANS_FAST_CHARGE_PING_RESPONSE);
|
||
|
|
||
|
mutex_unlock(&muic_data->afc_mutex);
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Init functions
|
||
|
*/
|
||
|
static int s2mu106_hv_muic_irq_init(struct s2mu106_muic_data *muic_data)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
if (muic_data->mfd_pdata && (muic_data->mfd_pdata->irq_base > 0)) {
|
||
|
int irq_base = muic_data->mfd_pdata->irq_base;
|
||
|
|
||
|
muic_data->irq_vdnmon = irq_base + S2MU106_AFC_IRQ_VDNMon;
|
||
|
REQUEST_IRQ(muic_data->irq_vdnmon, muic_data,
|
||
|
"muic-hv-vdnmon", &s2mu106_hv_muic_vdnmon_isr);
|
||
|
|
||
|
muic_data->irq_mpnack = irq_base + S2MU106_AFC_IRQ_MPNack;
|
||
|
REQUEST_IRQ(muic_data->irq_mpnack, muic_data,
|
||
|
"muic-hv-mpnack", &s2mu106_hv_muic_mpnack_isr);
|
||
|
|
||
|
muic_data->irq_mrxtrf = irq_base + S2MU106_AFC_IRQ_MRxTrf;
|
||
|
REQUEST_IRQ(muic_data->irq_mrxtrf, muic_data,
|
||
|
"muic-hv-mrxtrf", &s2mu106_hv_muic_mrxtrf_isr);
|
||
|
|
||
|
muic_data->irq_mrxrdy = irq_base + S2MU106_AFC_IRQ_MRxRdy;
|
||
|
REQUEST_IRQ(muic_data->irq_mrxrdy, muic_data,
|
||
|
"muic-hv-mrxrdy", &s2mu106_hv_muic_mrxrdy_isr);
|
||
|
|
||
|
pr_info("%s muic-hv-vdnmon(%d), muic-hv-mpnack(%d)\n",
|
||
|
__func__, muic_data->irq_vdnmon, muic_data->irq_mpnack);
|
||
|
pr_info("muic-hv-mrxtrf(%d), muic-hv-mrxrdy(%d)\n",
|
||
|
muic_data->irq_mrxtrf, muic_data->irq_mrxrdy);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void s2mu106_hv_muic_free_irqs(struct s2mu106_muic_data *muic_data)
|
||
|
{
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
FREE_IRQ(muic_data->irq_vdnmon, muic_data, "muic-hv-vdnmon");
|
||
|
FREE_IRQ(muic_data->irq_mpnack, muic_data, "muic-hv-mpanck");
|
||
|
FREE_IRQ(muic_data->irq_mrxtrf, muic_data, "muic-hv-mrxtrf");
|
||
|
FREE_IRQ(muic_data->irq_mrxrdy, muic_data, "muic-hv-mrxrdy");
|
||
|
}
|
||
|
|
||
|
int s2mu106_hv_muic_init(struct s2mu106_muic_data *muic_data)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct muic_interface_t *muic_if;
|
||
|
struct muic_platform_data *muic_pdata;
|
||
|
|
||
|
if (muic_data == NULL) {
|
||
|
pr_err("%s data NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
pr_info("%s start\n", __func__);
|
||
|
|
||
|
muic_pdata = muic_data->pdata;
|
||
|
static_data = muic_data;
|
||
|
|
||
|
mutex_init(&muic_data->afc_mutex);
|
||
|
|
||
|
muic_if = muic_data->if_data;
|
||
|
if (muic_if == NULL) {
|
||
|
pr_err("%s data NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
muic_data->is_requested_step_down = false;
|
||
|
|
||
|
muic_if->check_afc_ready = s2mu106_if_check_afc_ready;
|
||
|
muic_if->reset_hvcontrol_reg = s2mu106_if_reset_hvcontrol_reg;
|
||
|
muic_if->check_id_err = s2mu106_if_check_id_err;
|
||
|
muic_if->set_afc_ready = s2mu106_if_set_afc_ready;
|
||
|
#ifdef CONFIG_HV_MUIC_VOLTAGE_CTRL
|
||
|
muic_if->set_afc_voltage = s2mu106_if_set_afc_voltage;
|
||
|
#endif
|
||
|
muic_if->set_chgtype_usrcmd = s2mu106_if_set_chgtype_usrcmd;
|
||
|
muic_if->hv_reset = s2mu106_if_hv_muic_reset;
|
||
|
muic_if->hv_dcp_charger = s2mu106_if_hv_muic_dcp_charger;
|
||
|
muic_if->hv_fast_charge_adaptor = s2mu106_if_hv_muic_fast_charge_adaptor;
|
||
|
muic_if->hv_fast_charge_communication = s2mu106_if_hv_muic_fast_charge_communication;
|
||
|
muic_if->hv_afc_5v_charger = s2mu106_if_hv_muic_afc_5v_charger;
|
||
|
muic_if->hv_afc_9v_charger = s2mu106_if_hv_muic_afc_9v_charger;
|
||
|
muic_if->hv_qc_charger = s2mu106_if_hv_muic_qc_charger;
|
||
|
muic_if->hv_qc_5v_charger = s2mu106_if_hv_muic_qc_5v_charger;
|
||
|
muic_if->hv_qc_9v_charger = s2mu106_if_hv_muic_qc_9v_charger;
|
||
|
|
||
|
#if defined(CONFIG_MUIC_SUPPORT_POWERMETER)
|
||
|
muic_if->get_vbus_voltage = s2mu106_if_get_vbus_voltage;
|
||
|
muic_if->pm_chgin_irq = s2mu106_if_vchgin_isr;
|
||
|
#if defined(CONFIG_PM_S2MU106)
|
||
|
muic_data->psy_pm = get_power_supply_by_name("s2mu106_pmeter");
|
||
|
#endif
|
||
|
if (!muic_data->psy_pm) {
|
||
|
pr_err("%s: Fail to get pmeter\n", __func__);
|
||
|
}
|
||
|
#endif
|
||
|
#if defined(CONFIG_CHARGER_S2MU106)
|
||
|
muic_data->psy_chg = get_power_supply_by_name("s2mu106-charger");
|
||
|
if (!muic_data->psy_chg)
|
||
|
pr_err("%s: Fail to get charger\n", __func__);
|
||
|
#endif
|
||
|
|
||
|
ret = s2mu106_hv_muic_irq_init(muic_data);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s Failed to initialize HV MUIC irq:%d\n", __func__, ret);
|
||
|
s2mu106_hv_muic_free_irqs(muic_data);
|
||
|
}
|
||
|
|
||
|
INIT_DELAYED_WORK(&muic_data->reset_work, s2mu106_hv_muic_reset_work);
|
||
|
INIT_DELAYED_WORK(&muic_data->mping_retry_work, s2mu106_hv_muic_mping_retry_work);
|
||
|
INIT_DELAYED_WORK(&muic_data->qc_retry_work, s2mu106_hv_muic_qc_retry_work);
|
||
|
|
||
|
muic_core_hv_init(muic_pdata);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void s2mu106_hv_muic_remove(struct s2mu106_muic_data *muic_data)
|
||
|
{
|
||
|
pr_info("%s\n", __func__);
|
||
|
|
||
|
mutex_destroy(&muic_data->afc_mutex);
|
||
|
_s2mu106_hv_muic_reset(muic_data);
|
||
|
s2mu106_hv_muic_free_irqs(muic_data);
|
||
|
}
|