blob: 1551377075d5eefdf944a300fa3c86f82d98b542 [file] [log] [blame]
/*
* Copyrights (C) 2017 Samsung Electronics, Inc.
* Copyrights (C) 2017 Maxim Integrated Products, Inc.
*
* 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
#include <linux/power_supply.h>
#include <linux/of.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/mfd/max77705-private.h>
#include <linux/platform_device.h>
#include <linux/ccic/max77705_usbc.h>
#if defined(CONFIG_CCIC_NOTIFIER)
#include <linux/workqueue.h>
#include <linux/ccic/ccic_notifier.h>
#endif
#include <linux/ccic/ccic_core.h>
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
#include <linux/usb/class-dual-role.h>
#elif defined(CONFIG_TYPEC)
#include <linux/usb/typec.h>
#endif
#if defined(CONFIG_USB_HOST_NOTIFY)
#include <linux/usb_notify.h>
#endif
#include "../battery_v2/include/sec_charging_common.h"
#include <linux/ccic/max77705_alternate.h>
#if defined(CONFIG_COMBO_REDRIVER_PTN36502)
#include <linux/combo_redriver/ptn36502.h>
#endif
extern struct pdic_notifier_struct pd_noti;
#if defined(CONFIG_CCIC_NOTIFIER)
static void max77705_ccic_event_notifier(struct work_struct *data)
{
struct ccic_state_work *event_work =
container_of(data, struct ccic_state_work, ccic_work);
CC_NOTI_TYPEDEF ccic_noti;
switch (event_work->dest) {
case CCIC_NOTIFY_DEV_USB:
msg_maxim("usb: dest=%s, id=%s, attach=%s, drp=%s",
CCIC_NOTI_DEST_Print[event_work->dest],
CCIC_NOTI_ID_Print[event_work->id],
event_work->attach ? "Attached" : "Detached",
CCIC_NOTI_USB_STATUS_Print[event_work->event]);
break;
default:
msg_maxim("usb: dest=%s, id=%s, attach=%d, event=%d",
CCIC_NOTI_DEST_Print[event_work->dest],
CCIC_NOTI_ID_Print[event_work->id],
event_work->attach,
event_work->event);
break;
}
ccic_noti.src = CCIC_NOTIFY_DEV_CCIC;
ccic_noti.dest = event_work->dest;
ccic_noti.id = event_work->id;
ccic_noti.sub1 = event_work->attach;
ccic_noti.sub2 = event_work->event;
ccic_noti.sub3 = event_work->sub;
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
ccic_noti.pd = &pd_noti;
#endif
ccic_notifier_notify((CC_NOTI_TYPEDEF *) &ccic_noti, NULL, 0);
kfree(event_work);
}
void max77705_ccic_event_work(void *data, int dest, int id, int attach, int event, int sub)
{
struct max77705_usbc_platform_data *usbpd_data = data;
struct ccic_state_work *event_work;
#if defined(CONFIG_TYPEC)
struct typec_partner_desc desc;
enum typec_pwr_opmode mode = TYPEC_PWR_MODE_USB;
#endif
msg_maxim("usb: DIAES %d-%d-%d-%d-%d", dest, id, attach, event, sub);
event_work = kmalloc(sizeof(struct ccic_state_work), GFP_ATOMIC);
INIT_WORK(&event_work->ccic_work, max77705_ccic_event_notifier);
event_work->dest = dest;
event_work->id = id;
event_work->attach = attach;
event_work->event = event;
event_work->sub = sub;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
if (id == CCIC_NOTIFY_ID_USB) {
msg_maxim("usb: dest=%d, event=%d", dest, event);
msg_maxim("usb: pd->data_role=%d, pd->try_state_change=%d",
usbpd_data->data_role,
usbpd_data->try_state_change);
usbpd_data->data_role = event;
if (usbpd_data->dual_role != NULL)
dual_role_instance_changed(usbpd_data->dual_role);
if (usbpd_data->try_state_change &&
(usbpd_data->data_role != USB_STATUS_NOTIFY_DETACH)) {
/* Role change try and new mode detected */
msg_maxim("usb: reverse_completion");
usbpd_data->try_state_change = TYPE_C_DETACH;
complete(&usbpd_data->reverse_completion);
}
} else if (id == CCIC_NOTIFY_ID_ROLE_SWAP) {
if (usbpd_data->dual_role != NULL)
dual_role_instance_changed(usbpd_data->dual_role);
}
#elif defined(CONFIG_TYPEC)
if (id == CCIC_NOTIFY_ID_USB) {
if (usbpd_data->partner == NULL) {
msg_maxim("typec_register_partner, typec_power_role=%d typec_data_role=%d event=%d",
usbpd_data->typec_power_role,usbpd_data->typec_data_role, event);
if (event == USB_STATUS_NOTIFY_ATTACH_UFP) {
mode = max77705_get_pd_support(usbpd_data);
typec_set_pwr_opmode(usbpd_data->port, mode);
desc.usb_pd = mode == TYPEC_PWR_MODE_PD;
desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
desc.identity = NULL;
usbpd_data->typec_data_role = TYPEC_DEVICE;
typec_set_pwr_role(usbpd_data->port, usbpd_data->typec_power_role);
typec_set_data_role(usbpd_data->port, usbpd_data->typec_data_role);
usbpd_data->partner = typec_register_partner(usbpd_data->port, &desc);
} else if (event == USB_STATUS_NOTIFY_ATTACH_DFP) {
mode = max77705_get_pd_support(usbpd_data);
typec_set_pwr_opmode(usbpd_data->port, mode);
desc.usb_pd = mode == TYPEC_PWR_MODE_PD;
desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
desc.identity = NULL;
usbpd_data->typec_data_role = TYPEC_HOST;
typec_set_pwr_role(usbpd_data->port, usbpd_data->typec_power_role);
typec_set_data_role(usbpd_data->port, usbpd_data->typec_data_role);
usbpd_data->partner = typec_register_partner(usbpd_data->port, &desc);
} else
msg_maxim("detach case");
} else {
msg_maxim("data_role changed, typec_power_role=%d typec_data_role=%d, event=%d",
usbpd_data->typec_power_role,usbpd_data->typec_data_role, event);
if (event == USB_STATUS_NOTIFY_ATTACH_UFP) {
usbpd_data->typec_data_role = TYPEC_DEVICE;
typec_set_data_role(usbpd_data->port, usbpd_data->typec_data_role);
} else if (event == USB_STATUS_NOTIFY_ATTACH_DFP) {
usbpd_data->typec_data_role = TYPEC_HOST;
typec_set_data_role(usbpd_data->port, usbpd_data->typec_data_role);
} else
msg_maxim("detach case");
}
}
#endif
queue_work(usbpd_data->ccic_wq, &event_work->ccic_work);
}
#endif
void max77705_dp_detach(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
pr_info("%s: dp_is_connect %d\n", __func__, usbpd_data->dp_is_connect);
max77705_ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_USB_DP,
CCIC_NOTIFY_ID_USB_DP, 0/*attach*/, usbpd_data->dp_hs_connect/*drp*/, 0);
max77705_ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_DP,
CCIC_NOTIFY_ID_DP_CONNECT, 0/*attach*/, 0/*drp*/, 0);
usbpd_data->dp_is_connect = 0;
usbpd_data->dp_hs_connect = 0;
usbpd_data->is_sent_pin_configuration = 0;
}
void max77705_notify_dr_status(struct max77705_usbc_platform_data *usbpd_data, uint8_t attach)
{
struct max77705_pd_data *pd_data = usbpd_data->pd_data;
msg_maxim("Data Role: %s Power Role: %s State: %s",
pd_data->current_dr ? "DFP":"UFP",
usbpd_data->cc_data->current_pr ? "SRC":"SNK",
attach ? "ATTACHED":"DETACHED");
if (attach == CCIC_NOTIFY_ATTACH) {
if (usbpd_data->current_connstat == WATER) {
pr_info("%s: blocked by WATER\n", __func__);
return;
}
if (pd_data->current_dr == UFP) {
if (usbpd_data->is_host == HOST_ON) {
msg_maxim("pd_state:%02d, turn off host",
usbpd_data->pd_state);
if (usbpd_data->dp_is_connect == 1)
max77705_dp_detach(usbpd_data);
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0);
usbpd_data->is_host = HOST_OFF;
}
if (usbpd_data->is_client == CLIENT_OFF) {
usbpd_data->is_client = CLIENT_ON;
/* muic */
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH,
1/*attach*/, 0/*rprd*/, 0);
/* USB */
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_UFP/*drp*/, 0);
}
} else if (pd_data->current_dr == DFP) {
if (usbpd_data->is_client == CLIENT_ON) {
msg_maxim("pd_state:%02d, turn off client",
usbpd_data->pd_state);
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0);
usbpd_data->is_client = CLIENT_OFF;
}
if (usbpd_data->is_host == HOST_OFF) {
usbpd_data->is_host = HOST_ON;
#if defined(CONFIG_USB_AUDIO_ENHANCED_DETECT_TIME)
max77705_clk_booster_set(usbpd_data, 1);
#endif
/* muic */
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_MUIC,
CCIC_NOTIFY_ID_ATTACH, 1/*attach*/, 1/*rprd*/, 0);
/* USB */
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_DFP/*drp*/, 0);
}
} else {
msg_maxim("Unknown dr type (%d) no action",
pd_data->current_dr);
}
} else { /* CCIC_NOTIFY_DETACH */
if (usbpd_data->dp_is_connect == 1)
max77705_dp_detach(usbpd_data);
if (usbpd_data->acc_type != CCIC_DOCK_DETACHED) {
pr_info("%s: schedule_delayed_work - pd_state : %d\n",
__func__, usbpd_data->pd_state);
if (usbpd_data->acc_type == CCIC_DOCK_HMT)
schedule_delayed_work(&usbpd_data->acc_detach_work,
msecs_to_jiffies(GEAR_VR_DETACH_WAIT_MS));
else
schedule_delayed_work(&usbpd_data->acc_detach_work,
msecs_to_jiffies(0));
}
#if defined(CONFIG_USB_AUDIO_ENHANCED_DETECT_TIME)
max77705_clk_booster_set(usbpd_data, 0);
#endif
usbpd_data->mdm_block = 0;
usbpd_data->is_host = HOST_OFF;
usbpd_data->is_client = CLIENT_OFF;
/* muic */
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH,
0/*attach*/, 0/*rprd*/, 0);
/* USB */
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0);
}
}
static irqreturn_t max77705_vconncop_irq(int irq, void *data)
{
struct max77705_usbc_platform_data *usbc_data = data;
struct max77705_cc_data *cc_data = usbc_data->cc_data;
max77705_read_reg(usbc_data->muic, REG_CC_STATUS1, &cc_data->cc_status1);
cc_data->vconnocp = (cc_data->cc_status1 & BIT_VCONNOCPI)
>> FFS(BIT_VCONNOCPI);
msg_maxim("New VCONNOCP Status Interrupt (%d)",
cc_data->vconnocp);
return IRQ_HANDLED;
}
static irqreturn_t max77705_vsafe0v_irq(int irq, void *data)
{
struct max77705_usbc_platform_data *usbc_data = data;
struct max77705_cc_data *cc_data = usbc_data->cc_data;
u8 ccpinstat = 0;
u8 connstat = 0;
pr_debug("%s: IRQ(%d)_IN\n", __func__, irq);
max77705_read_reg(usbc_data->muic, REG_BC_STATUS, &cc_data->bc_status);
max77705_read_reg(usbc_data->muic, REG_CC_STATUS0, &cc_data->cc_status0);
max77705_read_reg(usbc_data->muic, REG_CC_STATUS1, &cc_data->cc_status1);
ccpinstat = (cc_data->cc_status0 & BIT_CCPinStat)
>> FFS(BIT_CCPinStat);
cc_data->vsafe0v = (cc_data->cc_status1 & BIT_VSAFE0V)
>> FFS(BIT_VSAFE0V);
connstat = (cc_data->cc_status1 & BIT_ConnStat)
>> FFS(BIT_ConnStat);
msg_maxim("New VSAFE0V Status Interrupt (%d)",
cc_data->vsafe0v);
pr_debug("%s: IRQ(%d)_OUT\n", __func__, irq);
return IRQ_HANDLED;
}
static irqreturn_t max77705_vconnsc_irq(int irq, void *data)
{
struct max77705_usbc_platform_data *usbc_data = data;
struct max77705_cc_data *cc_data = usbc_data->cc_data;
u8 connstat = 0;
pr_debug("%s: IRQ(%d)_IN\n", __func__, irq);
max77705_read_reg(usbc_data->muic, REG_CC_STATUS1, &cc_data->cc_status1);
connstat = (cc_data->cc_status1 & BIT_ConnStat)
>> FFS(BIT_ConnStat);
switch (connstat) {
case DRY:
msg_maxim("== WATER RUN-DRY DETECT ==");
if (usbc_data->current_connstat != DRY) {
usbc_data->prev_connstat = usbc_data->current_connstat;
usbc_data->current_connstat = DRY;
if(!usbc_data->max77705->blocking_waterevent)
max77705_ccic_event_work(usbc_data,
CCIC_NOTIFY_DEV_BATTERY,
CCIC_NOTIFY_ID_WATER,
0/*attach*/,
0,
0);
}
break;
case WATER:
msg_maxim("== WATER DETECT ==");
if (usbc_data->current_connstat != WATER) {
usbc_data->prev_connstat = usbc_data->current_connstat;
usbc_data->current_connstat = WATER;
if(!usbc_data->max77705->blocking_waterevent)
max77705_ccic_event_work(usbc_data,
CCIC_NOTIFY_DEV_BATTERY,
CCIC_NOTIFY_ID_WATER,
1/*attach*/,
0,
0);
}
break;
default:
break;
}
pr_debug("%s: IRQ(%d)_OUT\n", __func__, irq);
return IRQ_HANDLED;
}
static irqreturn_t max77705_ccpinstat_irq(int irq, void *data)
{
struct max77705_usbc_platform_data *usbc_data = data;
struct max77705_cc_data *cc_data = usbc_data->cc_data;
u8 ccpinstat = 0;
max77705_read_reg(usbc_data->muic, REG_CC_STATUS0, &cc_data->cc_status0);
pr_debug("%s: IRQ(%d)_IN\n", __func__, irq);
ccpinstat = (cc_data->cc_status0 & BIT_CCPinStat)
>> FFS(BIT_CCPinStat);
switch (ccpinstat) {
case NO_DETERMINATION:
msg_maxim("CCPINSTAT (NO_DETERMINATION)");
#if defined(CONFIG_CCIC_NOTIFIER)
if (usbc_data->ccrp_state) {
usbc_data->ccrp_state = 0;
max77705_ccic_event_work(usbc_data,
CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_WATER_CABLE,
CCIC_NOTIFY_DETACH, 0/*rprd*/, 0);
}
#endif
break;
case CC1_ACTIVE:
msg_maxim("CCPINSTAT (CC1_ACTIVE)");
break;
case CC2_ACTVIE:
msg_maxim("CCPINSTAT (CC2_ACTIVE)");
break;
case AUDIO_ACCESSORY:
msg_maxim("CCPINSTAT (AUDIO_ACCESSORY)");
break;
default:
msg_maxim("CCPINSTAT [%d]", ccpinstat);
break;
}
cc_data->ccpinstat = ccpinstat;
usbc_data->cc_pin_status = ccpinstat;
#if defined(CONFIG_SEC_FACTORY)
max77705_ccic_event_work(usbc_data, CCIC_NOTIFY_DEV_CCIC,
CCIC_NOTIFY_ID_CC_PIN_STATUS, ccpinstat, 0, 0);
#endif
pr_debug("%s: IRQ(%d)_OUT\n", __func__, irq);
return IRQ_HANDLED;
}
static irqreturn_t max77705_ccistat_irq(int irq, void *data)
{
struct max77705_usbc_platform_data *usbc_data = data;
struct max77705_cc_data *cc_data = usbc_data->cc_data;
u8 ccistat = 0;
#if defined(CONFIG_TYPEC)
enum typec_pwr_opmode mode = TYPEC_PWR_MODE_USB;
#endif
usbc_cmd_data value;
max77705_read_reg(usbc_data->muic, REG_CC_STATUS0, &cc_data->cc_status0);
pr_debug("%s: IRQ(%d)_IN\n", __func__, irq);
ccistat = (cc_data->cc_status0 & BIT_CCIStat) >> FFS(BIT_CCIStat);
switch (ccistat) {
case NOT_IN_UFP_MODE:
msg_maxim("Not in UFP");
break;
case CCI_500mA:
msg_maxim("Vbus Current is 500mA!");
break;
case CCI_1_5A:
msg_maxim("Vbus Current is 1.5A!");
#if defined(CONFIG_TYPEC)
mode = TYPEC_PWR_MODE_1_5A;
#endif
break;
case CCI_3_0A:
msg_maxim("Vbus Current is 3.0A!");
#if defined(CONFIG_TYPEC)
mode = TYPEC_PWR_MODE_3_0A;
#endif
if (usbc_data->srcccap_request_retry) {
usbc_data->pn_flag = false;
usbc_data->srcccap_request_retry = false;
value.opcode = OPCODE_SRCCAP_REQUEST;
value.write_data[0] = pd_noti.sink_status.selected_pdo_num;
value.write_length = 1;
value.read_length = 1;
max77705_usbc_opcode_write(usbc_data, &value);
pr_info("%s : OPCODE(0x%02x) W_LENGTH(%d) R_LENGTH(%d) NUM(%d)\n",
__func__, value.opcode, value.write_length, value.read_length,
pd_noti.sink_status.selected_pdo_num);
}
break;
default:
msg_maxim("CCINSTAT(Never Call this routine) !");
break;
}
cc_data->ccistat = ccistat;
pr_debug("%s: IRQ(%d)_OUT\n", __func__, irq);
max77705_notify_rp_current_level(usbc_data);
#if defined(CONFIG_TYPEC)
if (!usbc_data->pd_support) {
usbc_data->pwr_opmode = mode;
typec_set_pwr_opmode(usbc_data->port, mode);
}
#endif
return IRQ_HANDLED;
}
static irqreturn_t max77705_ccvnstat_irq(int irq, void *data)
{
struct max77705_usbc_platform_data *usbc_data = data;
struct max77705_cc_data *cc_data = usbc_data->cc_data;
u8 ccvcnstat = 0;
max77705_read_reg(usbc_data->muic, REG_CC_STATUS0, &cc_data->cc_status0);
pr_debug("%s: IRQ(%d)_IN\n", __func__, irq);
ccvcnstat = (cc_data->cc_status0 & BIT_CCVcnStat) >> FFS(BIT_CCVcnStat);
switch (ccvcnstat) {
case 0:
msg_maxim("Vconn Disabled");
if (cc_data->current_vcon != OFF) {
cc_data->previous_vcon = cc_data->current_vcon;
cc_data->current_vcon = OFF;
}
break;
case 1:
msg_maxim("Vconn Enabled");
if (cc_data->current_vcon != ON) {
cc_data->previous_vcon = cc_data->current_vcon;
cc_data->current_vcon = ON;
}
break;
default:
msg_maxim("ccvnstat(Never Call this routine) !");
break;
}
cc_data->ccvcnstat = ccvcnstat;
pr_debug("%s: IRQ(%d)_OUT\n", __func__, irq);
return IRQ_HANDLED;
}
static void max77705_ccstat_irq_handler(void *data, int irq)
{
struct power_supply *psy_charger;
union power_supply_propval val;
struct max77705_usbc_platform_data *usbc_data = data;
struct max77705_cc_data *cc_data = usbc_data->cc_data;
u8 ccstat = 0;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
int prev_power_role = usbc_data->power_role;
#elif defined(CONFIG_TYPEC)
int prev_power_role = usbc_data->typec_power_role;
#endif
#if defined(CONFIG_USB_HOST_NOTIFY)
struct otg_notify *o_notify = get_otg_notify();
#endif
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
int event;
#endif
max77705_read_reg(usbc_data->muic, REG_CC_STATUS0, &cc_data->cc_status0);
ccstat = (cc_data->cc_status0 & BIT_CCStat) >> FFS(BIT_CCStat);
if (irq == CCIC_IRQ_INIT_DETECT) {
if (ccstat == cc_SINK)
msg_maxim("initial time : SNK");
else
return;
}
#if defined(CONFIG_CCIC_NOTIFIER)
if (ccstat == cc_No_Connection)
usbc_data->pd_state = max77705_State_PE_Initial_detach;
else if (ccstat == cc_SOURCE)
usbc_data->pd_state = max77705_State_PE_SRC_Send_Capabilities;
else if (ccstat == cc_SINK)
usbc_data->pd_state = max77705_State_PE_SNK_Wait_for_Capabilities;
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
store_usblog_notify(NOTIFY_FUNCSTATE, (void *)&usbc_data->pd_state, NULL);
#endif
#endif
if (!ccstat) {
if (usbc_data->plug_attach_done) {
msg_maxim("PLUG_DETACHED ---");
#if defined(CONFIG_TYPEC)
if (usbc_data->partner) {
msg_maxim("ccstat : typec_unregister_partner");
if (!IS_ERR(usbc_data->partner))
typec_unregister_partner(usbc_data->partner);
usbc_data->partner = NULL;
usbc_data->typec_power_role = TYPEC_SINK;
usbc_data->typec_data_role = TYPEC_DEVICE;
usbc_data->pwr_opmode = TYPEC_PWR_MODE_USB;
}
if (usbc_data->typec_try_state_change == TRY_ROLE_SWAP_PR ||
usbc_data->typec_try_state_change == TRY_ROLE_SWAP_DR) {
/* Role change try and new mode detected */
msg_maxim("typec_reverse_completion, detached while pd_swap");
usbc_data->typec_try_state_change = TRY_ROLE_SWAP_NONE;
complete(&usbc_data->typec_reverse_completion);
}
#endif
max77705_notify_dr_status(usbc_data, 0);
usbc_data->plug_attach_done = 0;
usbc_data->cc_data->current_pr = 0xFF;
usbc_data->pd_data->current_dr = 0xFF;
usbc_data->cc_data->current_vcon = 0xFF;
usbc_data->detach_done_wait = 1;
#if defined(CONFIG_COMBO_REDRIVER_PTN36502)
ptn36502_config(SAFE_STATE, 0);
#endif
}
} else {
if (!usbc_data->plug_attach_done) {
msg_maxim("PLUG_ATTACHED +++");
usbc_data->plug_attach_done = 1;
}
}
switch (ccstat) {
case cc_No_Connection:
msg_maxim("ccstat : cc_No_Connection");
usbc_data->pd_data->cc_status = CC_NO_CONN;
usbc_data->is_samsung_accessory_enter_mode = 0;
usbc_data->pn_flag = false;
usbc_data->pd_support = false;
usbc_data->srcccap_request_retry = false;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
if (!usbc_data->try_state_change)
#elif defined(CONFIG_TYPEC)
if (!usbc_data->typec_try_state_change)
#endif
max77705_usbc_clear_queue(usbc_data);
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
usbc_data->power_role = DUAL_ROLE_PROP_PR_NONE;
#elif defined(CONFIG_TYPEC)
usbc_data->typec_power_role = TYPEC_SINK;
#endif
#if defined(CONFIG_USB_HOST_NOTIFY)
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
#endif
max77705_detach_pd(usbc_data);
usbc_data->pd_pr_swap = cc_No_Connection;
max77705_vbus_turn_on_ctrl(usbc_data, OFF, false);
#if defined(CONFIG_SEC_FACTORY)
factory_execute_monitor(FAC_ABNORMAL_REPEAT_STATE);
#endif
break;
case cc_SINK:
msg_maxim("ccstat : cc_SINK");
usbc_data->pd_data->cc_status = CC_SNK;
usbc_data->pn_flag = false;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
usbc_data->power_role = DUAL_ROLE_PROP_PR_SNK;
if (usbc_data->dual_role != NULL &&
usbc_data->data_role != USB_STATUS_NOTIFY_DETACH)
dual_role_instance_changed(usbc_data->dual_role);
#elif defined(CONFIG_TYPEC)
usbc_data->typec_power_role = TYPEC_SINK;
typec_set_pwr_role(usbc_data->port, TYPEC_SINK);
#endif
#if defined(CONFIG_USB_HOST_NOTIFY)
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
#endif
if (cc_data->current_pr != SNK) {
cc_data->previous_pr = cc_data->current_pr;
cc_data->current_pr = SNK;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
if (prev_power_role == DUAL_ROLE_PROP_PR_SRC)
#elif defined(CONFIG_TYPEC)
if (prev_power_role == TYPEC_SOURCE)
#endif
max77705_vbus_turn_on_ctrl(usbc_data, OFF, true);
}
psy_charger = power_supply_get_by_name("max77705-charger");
if (psy_charger) {
val.intval = 1;
psy_do_property("max77705-charger", set, POWER_SUPPLY_EXT_PROP_CHGINSEL, val);
} else {
pr_err("%s: Fail to get psy charger\n", __func__);
}
max77705_notify_rp_current_level(usbc_data);
#if defined(CONFIG_SEC_FACTORY)
factory_execute_monitor(FAC_ABNORMAL_REPEAT_STATE);
#endif
break;
case cc_SOURCE:
msg_maxim("ccstat : cc_SOURCE");
usbc_data->pd_data->cc_status = CC_SRC;
usbc_data->pn_flag = false;
usbc_data->srcccap_request_retry = false;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
usbc_data->power_role = DUAL_ROLE_PROP_PR_SRC;
if (usbc_data->dual_role != NULL &&
usbc_data->data_role != USB_STATUS_NOTIFY_DETACH)
dual_role_instance_changed(usbc_data->dual_role);
#elif defined(CONFIG_TYPEC)
usbc_data->typec_power_role = TYPEC_SOURCE;
typec_set_pwr_role(usbc_data->port, TYPEC_SOURCE);
#endif
#if defined(CONFIG_USB_HOST_NOTIFY)
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 1);
#endif
if (cc_data->current_pr != SRC) {
cc_data->previous_pr = cc_data->current_pr;
cc_data->current_pr = SRC;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
if (prev_power_role == DUAL_ROLE_PROP_PR_NONE)
#elif defined(CONFIG_TYPEC)
if (prev_power_role == TYPEC_SINK)
#endif
max77705_vbus_turn_on_ctrl(usbc_data, ON, false);
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
else if (prev_power_role == DUAL_ROLE_PROP_PR_SNK)
max77705_vbus_turn_on_ctrl(usbc_data, ON, true);
#endif
}
break;
case cc_Audio_Accessory:
msg_maxim("ccstat : cc_Audio_Accessory");
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
event = NOTIFY_EXTRA_USB_ANALOGAUDIO;
store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL);
#endif
usbc_data->acc_type = CCIC_DOCK_TYPEC_ANALOG_EARPHONE;
max77705_process_check_accessory(usbc_data);
break;
case cc_Debug_Accessory:
msg_maxim("ccstat : cc_Debug_Accessory");
break;
case cc_Error:
msg_maxim("ccstat : cc_Error");
break;
case cc_Disabled:
msg_maxim("ccstat : cc_Disabled");
break;
case cc_RFU:
msg_maxim("ccstat : cc_RFU");
break;
default:
break;
}
}
static irqreturn_t max77705_ccstat_irq(int irq, void *data)
{
pr_debug("%s: IRQ(%d)_IN\n", __func__, irq);
max77705_ccstat_irq_handler(data, irq);
pr_debug("%s: IRQ(%d)_OUT\n", __func__, irq);
return IRQ_HANDLED;
}
int max77705_cc_init(struct max77705_usbc_platform_data *usbc_data)
{
struct max77705_cc_data *cc_data = NULL;
int ret;
msg_maxim("IN");
cc_data = usbc_data->cc_data;
wake_lock_init(&cc_data->vconncop_wake_lock, WAKE_LOCK_SUSPEND,
"cc->vconncop");
wake_lock_init(&cc_data->vsafe0v_wake_lock, WAKE_LOCK_SUSPEND,
"cc->vsafe0v");
wake_lock_init(&cc_data->detabrt_wake_lock, WAKE_LOCK_SUSPEND,
"cc->detabrt");
wake_lock_init(&cc_data->vconnsc_wake_lock, WAKE_LOCK_SUSPEND,
"cc->vconnsc");
wake_lock_init(&cc_data->ccpinstat_wake_lock, WAKE_LOCK_SUSPEND,
"cc->ccpinstat");
wake_lock_init(&cc_data->ccistat_wake_lock, WAKE_LOCK_SUSPEND,
"cc->ccistat");
wake_lock_init(&cc_data->ccvcnstat_wake_lock, WAKE_LOCK_SUSPEND,
"cc->ccvcnstat");
wake_lock_init(&cc_data->ccstat_wake_lock, WAKE_LOCK_SUSPEND,
"cc->ccstat");
cc_data->irq_vconncop = usbc_data->irq_base + MAX77705_CC_IRQ_VCONNCOP_INT;
if (cc_data->irq_vconncop) {
ret = request_threaded_irq(cc_data->irq_vconncop,
NULL, max77705_vconncop_irq,
0,
"cc-vconncop-irq", usbc_data);
if (ret) {
pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret);
goto err_irq;
}
}
cc_data->irq_vsafe0v = usbc_data->irq_base + MAX77705_CC_IRQ_VSAFE0V_INT;
if (cc_data->irq_vsafe0v) {
ret = request_threaded_irq(cc_data->irq_vsafe0v,
NULL, max77705_vsafe0v_irq,
0,
"cc-vsafe0v-irq", usbc_data);
if (ret) {
pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret);
goto err_irq;
}
}
cc_data->irq_vconnsc = usbc_data->irq_base + MAX77705_CC_IRQ_VCONNSC_INT;
if (cc_data->irq_vconnsc) {
ret = request_threaded_irq(cc_data->irq_vconnsc,
NULL, max77705_vconnsc_irq,
0,
"cc-vconnsc-irq", usbc_data);
if (ret) {
pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret);
goto err_irq;
}
}
cc_data->irq_ccpinstat = usbc_data->irq_base + MAX77705_CC_IRQ_CCPINSTAT_INT;
if (cc_data->irq_ccpinstat) {
ret = request_threaded_irq(cc_data->irq_ccpinstat,
NULL, max77705_ccpinstat_irq,
0,
"cc-ccpinstat-irq", usbc_data);
if (ret) {
pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret);
goto err_irq;
}
}
cc_data->irq_ccistat = usbc_data->irq_base + MAX77705_CC_IRQ_CCISTAT_INT;
if (cc_data->irq_ccistat) {
ret = request_threaded_irq(cc_data->irq_ccistat,
NULL, max77705_ccistat_irq,
0,
"cc-ccistat-irq", usbc_data);
if (ret) {
pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret);
goto err_irq;
}
}
cc_data->irq_ccvcnstat = usbc_data->irq_base + MAX77705_CC_IRQ_CCVCNSTAT_INT;
if (cc_data->irq_ccvcnstat) {
ret = request_threaded_irq(cc_data->irq_ccvcnstat,
NULL, max77705_ccvnstat_irq,
0,
"cc-ccvcnstat-irq", usbc_data);
if (ret) {
pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret);
goto err_irq;
}
}
cc_data->irq_ccstat = usbc_data->irq_base + MAX77705_CC_IRQ_CCSTAT_INT;
if (cc_data->irq_ccstat) {
ret = request_threaded_irq(cc_data->irq_ccstat,
NULL, max77705_ccstat_irq,
0,
"cc-ccstat-irq", usbc_data);
if (ret) {
pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret);
goto err_irq;
}
}
/* check CC Pin state for cable attach booting scenario */
max77705_ccstat_irq_handler(usbc_data, CCIC_IRQ_INIT_DETECT);
max77705_read_reg(usbc_data->muic, REG_CC_STATUS1, &cc_data->cc_status1);
usbc_data->current_connstat = (cc_data->cc_status1 & BIT_ConnStat)
>> FFS(BIT_ConnStat);
pr_info("%s: water state : %s\n", __func__, usbc_data->current_connstat ? "WATER" : "DRY");
if (usbc_data->current_connstat) {
if(!usbc_data->max77705->blocking_waterevent)
max77705_ccic_event_work(usbc_data,
CCIC_NOTIFY_DEV_BATTERY,
CCIC_NOTIFY_ID_WATER,
1/*attach*/,
0,
0);
}
msg_maxim("OUT");
return 0;
err_irq:
kfree(cc_data);
return ret;
}