1342 lines
44 KiB
C
1342 lines
44 KiB
C
/*
|
|
* driver/../s2mm005.c - S2MM005 USB CC function driver
|
|
*
|
|
* Copyright (C) 2015 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
|
|
*
|
|
*/
|
|
|
|
#include <linux/ccic/s2mm005.h>
|
|
#include <linux/ccic/s2mm005_ext.h>
|
|
#if defined(CONFIG_CCIC_NOTIFIER)
|
|
#include <linux/workqueue.h>
|
|
#endif
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
#include <linux/ccic/ccic_alternate.h>
|
|
#endif
|
|
#if defined(CONFIG_USB_HOST_NOTIFY)
|
|
#include <linux/usb_notify.h>
|
|
#endif
|
|
#if defined(CONFIG_COMBO_REDRIVER)
|
|
#include <linux/combo_redriver/ptn36502.h>
|
|
#endif
|
|
|
|
#if defined(CONFIG_BATTERY_SAMSUNG)
|
|
extern unsigned int lpcharge;
|
|
#endif
|
|
|
|
extern struct pdic_notifier_struct pd_noti;
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// function definition
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void process_cc_water(void * data, LP_STATE_Type *Lp_DATA);
|
|
void process_cc_attach(void * data, u8 *plug_attach_done);
|
|
void process_cc_detach(void * data);
|
|
#if defined(CONFIG_TYPEC)
|
|
void process_message_role(void *data);
|
|
#endif
|
|
void process_cc_get_int_status(void *data, uint32_t *pPRT_MSG, MSG_IRQ_STATUS_Type *MSG_IRQ_State);
|
|
void process_cc_rid(void * data);
|
|
void ccic_event_work(void *data, int dest, int id, int attach, int event, int
|
|
sub);
|
|
void process_cc_water_det(void * data);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// modified by khoonk 2015.05.18
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// s2mm005.c --> s2mm005_cc.h
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static char MSG_IRQ_Print[32][40] =
|
|
{
|
|
{"bFlag_Ctrl_Reserved"},
|
|
{"bFlag_Ctrl_GoodCRC"},
|
|
{"bFlag_Ctrl_GotoMin"},
|
|
{"bFlag_Ctrl_Accept"},
|
|
{"bFlag_Ctrl_Reject"},
|
|
{"bFlag_Ctrl_Ping"},
|
|
{"bFlag_Ctrl_PS_RDY"},
|
|
{"bFlag_Ctrl_Get_Source_Cap"},
|
|
{"bFlag_Ctrl_Get_Sink_Cap"},
|
|
{"bFlag_Ctrl_DR_Swap"},
|
|
{"bFlag_Ctrl_PR_Swap"},
|
|
{"bFlag_Ctrl_VCONN_Swap"},
|
|
{"bFlag_Ctrl_Wait"},
|
|
{"bFlag_Ctrl_Soft_Reset"},
|
|
{"bFlag_Ctrl_Reserved_b14"},
|
|
{"bFlag_Ctrl_Reserved_b15"},
|
|
{"bFlag_Data_Reserved_b16"},
|
|
{"bFlag_Data_SRC_Capability"},
|
|
{"bFlag_Data_Request"},
|
|
{"bFlag_Data_BIST"},
|
|
{"bFlag_Data_SNK_Capability"},
|
|
{"bFlag_Data_Reserved_05"},
|
|
{"bFlag_Data_Reserved_06"},
|
|
{"bFlag_Data_Reserved_07"},
|
|
{"bFlag_Data_Reserved_08"},
|
|
{"bFlag_Data_Reserved_09"},
|
|
{"bFlag_Data_Reserved_10"},
|
|
{"bFlag_Data_Reserved_11"},
|
|
{"bFlag_Data_Reserved_12"},
|
|
{"bFlag_Data_Reserved_13"},
|
|
{"bFlag_Data_Reserved_14"},
|
|
{"bFlag_Data_Vender_Defined"},
|
|
};
|
|
|
|
#if defined(CONFIG_CCIC_NOTIFIER)
|
|
static void 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 :
|
|
pr_info("usb:%s, dest=%s, id=%s, attach=%s, drp=%s\n", __func__,
|
|
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 :
|
|
pr_info("usb:%s, dest=%s, id=%s, attach=%d, event=%d\n", __func__,
|
|
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 ccic_event_work(void *data, int dest, int id, int attach, int event, int sub)
|
|
{
|
|
struct s2mm005_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
|
|
|
|
pr_info("usb: %s\n", __func__);
|
|
event_work = kmalloc(sizeof(struct ccic_state_work), GFP_ATOMIC);
|
|
if (!event_work) {
|
|
pr_err("%s: failed to alloc for event_work\n", __func__);
|
|
return;
|
|
}
|
|
INIT_WORK(&event_work->ccic_work, 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) {
|
|
pr_info("usb: %s, dest=%d, event=%d, usbpd_data->data_role=%d, usbpd_data->try_state_change=%d\n",
|
|
__func__, dest, event, 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
|
|
pr_info("usb: %s, reverse_completion\n", __func__);
|
|
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) {
|
|
pr_info("%s: typec_register_partner power_role=%d data_role=%d event=%d",
|
|
__func__, usbpd_data->typec_power_role,usbpd_data->typec_data_role, event);
|
|
if (event == USB_STATUS_NOTIFY_ATTACH_UFP) {
|
|
mode = s2mm005_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_data_role(usbpd_data->port, TYPEC_DEVICE);
|
|
usbpd_data->partner = typec_register_partner(usbpd_data->port, &desc);
|
|
} else if (event == USB_STATUS_NOTIFY_ATTACH_DFP) {
|
|
mode = s2mm005_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_data_role(usbpd_data->port, TYPEC_HOST);
|
|
usbpd_data->partner = typec_register_partner(usbpd_data->port, &desc);
|
|
} else
|
|
pr_info("%s detach case\n", __func__);
|
|
} else {
|
|
pr_info("%s: data_role changed, power_role=%d data_role=%d, event=%d",
|
|
__func__, 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
|
|
pr_info("%s detach case\n", __func__);
|
|
}
|
|
if ((usbpd_data->typec_try_state_change == TRY_ROLE_SWAP_DR
|
|
|| usbpd_data->typec_try_state_change == TRY_ROLE_SWAP_TYPE)
|
|
&& (event != USB_STATUS_NOTIFY_DETACH)) {
|
|
// Role change try and new mode detected
|
|
pr_info("usb: %s, typec_reverse_completion\n", __func__);
|
|
complete(&usbpd_data->typec_reverse_completion);
|
|
}
|
|
}
|
|
#endif
|
|
queue_work(usbpd_data->ccic_wq, &event_work->ccic_work);
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
|
void role_swap_check(struct work_struct *wk)
|
|
{
|
|
struct delayed_work *delay_work =
|
|
container_of(wk, struct delayed_work, work);
|
|
struct s2mm005_data *usbpd_data =
|
|
container_of(delay_work, struct s2mm005_data, role_swap_work);
|
|
int mode;
|
|
|
|
pr_info("%s: ccic_set_dual_role check again usbpd_data->pd_state=%d\n",
|
|
__func__, usbpd_data->pd_state);
|
|
|
|
usbpd_data->try_state_change = 0;
|
|
|
|
if (usbpd_data->pd_state == State_PE_Initial_detach) {
|
|
pr_err("%s: ccic_set_dual_role reverse failed, set mode to DRP\n", __func__);
|
|
disable_irq(usbpd_data->irq);
|
|
/* exit from Disabled state and set mode to DRP */
|
|
mode = TYPE_C_ATTACH_DRP;
|
|
s2mm005_rprd_mode_change(usbpd_data, mode);
|
|
enable_irq(usbpd_data->irq);
|
|
}
|
|
}
|
|
|
|
static int ccic_set_dual_role(struct dual_role_phy_instance *dual_role,
|
|
enum dual_role_property prop,
|
|
const unsigned int *val)
|
|
{
|
|
struct s2mm005_data *usbpd_data = dual_role_get_drvdata(dual_role);
|
|
struct i2c_client *i2c;
|
|
|
|
USB_STATUS attached_state;
|
|
int mode;
|
|
int timeout = 0;
|
|
int ret = 0;
|
|
|
|
if (!usbpd_data) {
|
|
pr_err("%s : usbpd_data is null \n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
i2c = usbpd_data->i2c;
|
|
|
|
// Get Current Role //
|
|
attached_state = usbpd_data->data_role;
|
|
pr_info("%s : request prop = %d , attached_state = %d\n", __func__, prop, attached_state);
|
|
|
|
if (attached_state != USB_STATUS_NOTIFY_ATTACH_DFP
|
|
&& attached_state != USB_STATUS_NOTIFY_ATTACH_UFP) {
|
|
pr_err("%s : current mode : %d - just return \n",__func__, attached_state);
|
|
return 0;
|
|
}
|
|
|
|
if (attached_state == USB_STATUS_NOTIFY_ATTACH_DFP
|
|
&& *val == DUAL_ROLE_PROP_MODE_DFP) {
|
|
pr_err("%s : current mode : %d - request mode : %d just return \n",
|
|
__func__, attached_state, *val);
|
|
return 0;
|
|
}
|
|
|
|
if (attached_state == USB_STATUS_NOTIFY_ATTACH_UFP
|
|
&& *val == DUAL_ROLE_PROP_MODE_UFP) {
|
|
pr_err("%s : current mode : %d - request mode : %d just return \n",
|
|
__func__, attached_state, *val);
|
|
return 0;
|
|
}
|
|
|
|
if ( attached_state == USB_STATUS_NOTIFY_ATTACH_DFP) {
|
|
/* Current mode DFP and Source */
|
|
pr_info("%s: try reversing, from Source to Sink\n", __func__);
|
|
/* turns off VBUS first */
|
|
vbus_turn_on_ctrl(0);
|
|
#if defined(CONFIG_CCIC_NOTIFIER)
|
|
/* muic */
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH,
|
|
0/*attach*/, 0/*rprd*/, 0);
|
|
#endif
|
|
/* exit from Disabled state and set mode to UFP */
|
|
mode = TYPE_C_ATTACH_UFP;
|
|
usbpd_data->try_state_change = TYPE_C_ATTACH_UFP;
|
|
s2mm005_rprd_mode_change(usbpd_data, mode);
|
|
} else {
|
|
// Current mode UFP and Sink //
|
|
pr_info("%s: try reversing, from Sink to Source\n", __func__);
|
|
/* exit from Disabled state and set mode to UFP */
|
|
mode = TYPE_C_ATTACH_DFP;
|
|
usbpd_data->try_state_change = TYPE_C_ATTACH_DFP;
|
|
s2mm005_rprd_mode_change(usbpd_data, mode);
|
|
}
|
|
|
|
reinit_completion(&usbpd_data->reverse_completion);
|
|
timeout =
|
|
wait_for_completion_timeout(&usbpd_data->reverse_completion,
|
|
msecs_to_jiffies
|
|
(DUAL_ROLE_SET_MODE_WAIT_MS));
|
|
|
|
if (!timeout) {
|
|
usbpd_data->try_state_change = 0;
|
|
pr_err("%s: reverse failed, set mode to DRP\n", __func__);
|
|
disable_irq(usbpd_data->irq);
|
|
/* exit from Disabled state and set mode to DRP */
|
|
mode = TYPE_C_ATTACH_DRP;
|
|
s2mm005_rprd_mode_change(usbpd_data, mode);
|
|
enable_irq(usbpd_data->irq);
|
|
ret = -EIO;
|
|
} else {
|
|
pr_err("%s: reverse success, one more check\n", __func__);
|
|
schedule_delayed_work(&usbpd_data->role_swap_work, msecs_to_jiffies(DUAL_ROLE_SET_MODE_WAIT_MS));
|
|
}
|
|
|
|
dev_info(&i2c->dev, "%s -> data role : %d\n", __func__, *val);
|
|
return ret;
|
|
}
|
|
|
|
/* Decides whether userspace can change a specific property */
|
|
int dual_role_is_writeable(struct dual_role_phy_instance *drp,
|
|
enum dual_role_property prop)
|
|
{
|
|
if (prop == DUAL_ROLE_PROP_MODE)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Callback for "cat /sys/class/dual_role_usb/otg_default/<property>" */
|
|
int dual_role_get_local_prop(struct dual_role_phy_instance *dual_role,
|
|
enum dual_role_property prop,
|
|
unsigned int *val)
|
|
{
|
|
struct s2mm005_data *usbpd_data = dual_role_get_drvdata(dual_role);
|
|
|
|
USB_STATUS attached_state;
|
|
int power_role;
|
|
|
|
if (!usbpd_data) {
|
|
pr_err("%s : usbpd_data is null : request prop = %d \n",__func__, prop);
|
|
return -EINVAL;
|
|
}
|
|
attached_state = usbpd_data->data_role;
|
|
power_role = usbpd_data->power_role;
|
|
|
|
pr_info("%s : request prop = %d , attached_state = %d, power_role = %d\n",
|
|
__func__, prop, attached_state, power_role);
|
|
|
|
if (attached_state == USB_STATUS_NOTIFY_ATTACH_DFP) {
|
|
if (prop == DUAL_ROLE_PROP_MODE)
|
|
*val = DUAL_ROLE_PROP_MODE_DFP;
|
|
else if (prop == DUAL_ROLE_PROP_PR)
|
|
*val = power_role;
|
|
else if (prop == DUAL_ROLE_PROP_DR)
|
|
*val = DUAL_ROLE_PROP_DR_HOST;
|
|
else
|
|
return -EINVAL;
|
|
} else if (attached_state == USB_STATUS_NOTIFY_ATTACH_UFP) {
|
|
if (prop == DUAL_ROLE_PROP_MODE)
|
|
*val = DUAL_ROLE_PROP_MODE_UFP;
|
|
else if (prop == DUAL_ROLE_PROP_PR)
|
|
*val = power_role;
|
|
else if (prop == DUAL_ROLE_PROP_DR)
|
|
*val = DUAL_ROLE_PROP_DR_DEVICE;
|
|
else
|
|
return -EINVAL;
|
|
} else {
|
|
if (prop == DUAL_ROLE_PROP_MODE)
|
|
*val = DUAL_ROLE_PROP_MODE_NONE;
|
|
else if (prop == DUAL_ROLE_PROP_PR)
|
|
*val = DUAL_ROLE_PROP_PR_NONE;
|
|
else if (prop == DUAL_ROLE_PROP_DR)
|
|
*val = DUAL_ROLE_PROP_DR_NONE;
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Callback for "echo <value> >
|
|
* /sys/class/dual_role_usb/<name>/<property>"
|
|
* Block until the entire final state is reached.
|
|
* Blocking is one of the better ways to signal when the operation
|
|
* is done.
|
|
* This function tries to switch to Attached.SRC or Attached.SNK
|
|
* by forcing the mode into SRC or SNK.
|
|
* On failure, we fall back to Try.SNK state machine.
|
|
*/
|
|
int dual_role_set_prop(struct dual_role_phy_instance *dual_role,
|
|
enum dual_role_property prop,
|
|
const unsigned int *val)
|
|
{
|
|
pr_info("%s : request prop = %d , *val = %d \n",__func__, prop, *val);
|
|
if (prop == DUAL_ROLE_PROP_MODE)
|
|
return ccic_set_dual_role(dual_role, prop, val);
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
#elif defined(CONFIG_TYPEC)
|
|
int s2mm005_dr_set(const struct typec_capability *cap, enum typec_data_role role)
|
|
{
|
|
struct s2mm005_data *usbpd_data = container_of(cap, struct s2mm005_data, typec_cap);
|
|
if (!usbpd_data)
|
|
return -EINVAL;
|
|
|
|
pr_info("%s : typec_power_role=%d, typec_data_role=%d, role=%d\n", __func__,
|
|
usbpd_data->typec_power_role, usbpd_data->typec_data_role, role);
|
|
|
|
if (usbpd_data->typec_data_role != TYPEC_DEVICE
|
|
&& usbpd_data->typec_data_role != TYPEC_HOST)
|
|
return -EPERM;
|
|
else if (usbpd_data->typec_data_role == role)
|
|
return -EPERM;
|
|
|
|
reinit_completion(&usbpd_data->typec_reverse_completion);
|
|
if (role == TYPEC_DEVICE) {
|
|
pr_info("%s :try reversing, from DFP to UFP\n", __func__);
|
|
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_DR;
|
|
usbpd_data->is_dr_swap++;
|
|
send_role_swap_message(usbpd_data, 1);
|
|
} else if (role == TYPEC_HOST) {
|
|
pr_info("%s :try reversing, from UFP to DFP\n", __func__);
|
|
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_DR;
|
|
usbpd_data->is_dr_swap++;
|
|
send_role_swap_message(usbpd_data, 1);
|
|
} else {
|
|
pr_info("%s :invalid typec_role\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
if (!wait_for_completion_timeout(&usbpd_data->typec_reverse_completion,
|
|
msecs_to_jiffies(TRY_ROLE_SWAP_WAIT_MS))) {
|
|
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE;
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int s2mm005_pr_set(const struct typec_capability *cap, enum typec_role role)
|
|
{
|
|
struct s2mm005_data *usbpd_data = container_of(cap, struct s2mm005_data, typec_cap);
|
|
|
|
if (!usbpd_data)
|
|
return -EINVAL;
|
|
|
|
pr_info("%s : typec_power_role=%d, typec_data_role=%d, role=%d\n", __func__,
|
|
usbpd_data->typec_power_role, usbpd_data->typec_data_role, role);
|
|
|
|
if (usbpd_data->typec_power_role != TYPEC_SINK
|
|
&& usbpd_data->typec_power_role != TYPEC_SOURCE)
|
|
return -EPERM;
|
|
else if (usbpd_data->typec_power_role == role)
|
|
return -EPERM;
|
|
|
|
reinit_completion(&usbpd_data->typec_reverse_completion);
|
|
if (role == TYPEC_SINK) {
|
|
pr_info("%s :try reversing, from Source to Sink\n", __func__);
|
|
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_PR;
|
|
usbpd_data->is_pr_swap++;
|
|
send_role_swap_message(usbpd_data, 0);
|
|
} else if (role == TYPEC_SOURCE) {
|
|
pr_info("%s :try reversing, from Sink to Source\n", __func__);
|
|
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_PR;
|
|
usbpd_data->is_pr_swap++;
|
|
send_role_swap_message(usbpd_data, 0);
|
|
} else {
|
|
pr_info("%s :invalid typec_role\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
if (!wait_for_completion_timeout(&usbpd_data->typec_reverse_completion,
|
|
msecs_to_jiffies(TRY_ROLE_SWAP_WAIT_MS))) {
|
|
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE;
|
|
if (usbpd_data->typec_power_role != role)
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void typec_role_swap_check(struct work_struct *wk)
|
|
{
|
|
struct delayed_work *delay_work =
|
|
container_of(wk, struct delayed_work, work);
|
|
struct s2mm005_data *usbpd_data =
|
|
container_of(delay_work, struct s2mm005_data, typec_role_swap_work);
|
|
|
|
pr_info("%s: s2mm005_port_type_set check again usbpd_data->pd_state=%d\n",
|
|
__func__, usbpd_data->pd_state);
|
|
|
|
usbpd_data->typec_try_state_change = 0;
|
|
|
|
if (usbpd_data->pd_state == State_PE_Initial_detach) {
|
|
pr_err("%s: ccic_set_dual_role reverse failed, set mode to DRP\n", __func__);
|
|
disable_irq(usbpd_data->irq);
|
|
/* exit from Disabled state and set mode to DRP */
|
|
s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DRP);
|
|
enable_irq(usbpd_data->irq);
|
|
}
|
|
}
|
|
|
|
int s2mm005_port_type_set(const struct typec_capability *cap, enum typec_port_type port_type)
|
|
{
|
|
struct s2mm005_data *usbpd_data = container_of(cap, struct s2mm005_data, typec_cap);
|
|
int timeout = 0;
|
|
int ret = 0;
|
|
|
|
if (!usbpd_data) {
|
|
pr_err("%s : usbpd_data is null\n", __func__);
|
|
ret = -EINVAL;
|
|
goto skip;
|
|
}
|
|
|
|
pr_info("%s +: typec_power_role=%d, typec_data_role=%d, port_type=%d\n",
|
|
__func__, usbpd_data->typec_power_role, usbpd_data->typec_data_role, port_type);
|
|
|
|
switch (port_type) {
|
|
case TYPEC_PORT_DFP:
|
|
pr_info("%s : try reversing, from UFP(Sink) to DFP(Source)\n", __func__);
|
|
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_TYPE;
|
|
s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DFP);
|
|
break;
|
|
case TYPEC_PORT_UFP:
|
|
pr_info("%s : try reversing, from DFP(Source) to UFP(Sink)\n", __func__);
|
|
/* turns off VBUS first */
|
|
vbus_turn_on_ctrl(0);
|
|
#if defined(CONFIG_CCIC_NOTIFIER)
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH,
|
|
0/*attach*/, 0/*rprd*/, 0);
|
|
#endif
|
|
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_TYPE;
|
|
s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_UFP);
|
|
break;
|
|
case TYPEC_PORT_DRP:
|
|
pr_info("%s : set to DRP\n", __func__);
|
|
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE;
|
|
s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DRP);
|
|
goto skip;
|
|
default :
|
|
pr_info("%s : invalid typec_role\n", __func__);
|
|
ret = -EINVAL;
|
|
goto skip;
|
|
}
|
|
|
|
pr_info("%s typec_try_state_change=%d\n", __func__, usbpd_data->typec_try_state_change);
|
|
|
|
if (usbpd_data->typec_try_state_change) {
|
|
reinit_completion(&usbpd_data->typec_reverse_completion);
|
|
timeout =
|
|
wait_for_completion_timeout(&usbpd_data->typec_reverse_completion,
|
|
msecs_to_jiffies
|
|
(DUAL_ROLE_SET_MODE_WAIT_MS));
|
|
|
|
if (!timeout) {
|
|
pr_err("%s: reverse failed, set mode to DRP\n", __func__);
|
|
disable_irq(usbpd_data->irq);
|
|
/* exit from Disabled state and set mode to DRP */
|
|
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE;
|
|
s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DRP);
|
|
enable_irq(usbpd_data->irq);
|
|
ret = -EIO;
|
|
goto skip;
|
|
} else {
|
|
pr_err("%s: reverse success, one more check\n", __func__);
|
|
schedule_delayed_work(&usbpd_data->typec_role_swap_work, msecs_to_jiffies(DUAL_ROLE_SET_MODE_WAIT_MS));
|
|
}
|
|
}
|
|
|
|
skip:
|
|
pr_info("%s -\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
int s2mm005_get_pd_support(struct s2mm005_data *usbpd_data)
|
|
{
|
|
bool support_pd_role_swap = false;
|
|
struct device_node *np = NULL;
|
|
|
|
np = of_find_compatible_node(NULL, NULL, "sec-s2mm005,i2c");
|
|
|
|
if (np)
|
|
support_pd_role_swap = of_property_read_bool(np, "support_pd_role_swap");
|
|
else
|
|
pr_info("%s : np is null\n", __func__);
|
|
|
|
pr_info("%s : TYPEC_CLASS: support_pd_role_swap is %d, usbc_data->pd_support : %d\n", __func__,
|
|
support_pd_role_swap, usbpd_data->pd_support);
|
|
|
|
if (support_pd_role_swap && usbpd_data->pd_support)
|
|
return TYPEC_PWR_MODE_PD;
|
|
|
|
return usbpd_data->pwr_opmode;
|
|
}
|
|
#endif
|
|
|
|
void process_cc_water_det(void * data)
|
|
{
|
|
struct s2mm005_data *usbpd_data = data;
|
|
|
|
pr_info("%s\n",__func__);
|
|
s2mm005_int_clear(usbpd_data); // interrupt clear
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
if(!usbpd_data->fac_water_enable)
|
|
#endif
|
|
{
|
|
if(usbpd_data->water_det)
|
|
s2mm005_manual_LPM(usbpd_data, 0x9);
|
|
}
|
|
}
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
void dp_detach(void *data)
|
|
{
|
|
struct s2mm005_data *usbpd_data = data;
|
|
|
|
pr_info("%s: dp_is_connect %d\n",__func__, usbpd_data->dp_is_connect);
|
|
|
|
ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_USB_DP,
|
|
CCIC_NOTIFY_ID_USB_DP, 0/*attach*/,usbpd_data->dp_hs_connect/*drp*/, 0);
|
|
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;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
//////////////////////////////////////////// ////////////////////////////////////
|
|
// Moisture detection processing
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void process_cc_water(void * data, LP_STATE_Type *Lp_DATA)
|
|
{
|
|
struct s2mm005_data *usbpd_data = data;
|
|
struct i2c_client *i2c = usbpd_data->i2c;
|
|
uint32_t R_len;
|
|
uint16_t REG_ADD;
|
|
|
|
pr_info("%s\n",__func__);
|
|
/* read reg for water and dry state */
|
|
REG_ADD = 0x60;
|
|
R_len = 4;
|
|
usbpd_data->s2mm005_i2c_err = s2mm005_read_byte(i2c, REG_ADD, Lp_DATA->BYTE, R_len);
|
|
dev_info(&i2c->dev, "%s: WATER reg:0x%02X WATER=%d DRY=%d\n", __func__,
|
|
Lp_DATA->BYTE[0],
|
|
Lp_DATA->BITS.WATER_DET,
|
|
Lp_DATA->BITS.RUN_DRY);
|
|
|
|
if (!usbpd_data->water_detect_support) {
|
|
dev_info(&i2c->dev, "%s: It does not support water detection\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (usbpd_data->s2mm005_i2c_err < 0) {
|
|
pr_err("%s : i2c err : ret = %d\n", __func__, usbpd_data->s2mm005_i2c_err);
|
|
|
|
Lp_DATA->BITS.WATER_DET = 0;
|
|
Lp_DATA->BITS.RUN_DRY = 1;
|
|
Lp_DATA->BITS.BOOTING_RUN_DRY = usbpd_data->booting_run_dry;
|
|
|
|
} else {
|
|
if (Lp_DATA->BITS.WATER_DET == 0
|
|
&& Lp_DATA->BITS.RUN_DRY == 0
|
|
&& Lp_DATA->BITS.BOOTING_RUN_DRY == 0) {
|
|
|
|
usbpd_data->s2mm005_i2c_err = s2mm005_read_byte(i2c, REG_ADD, Lp_DATA->BYTE, R_len);
|
|
dev_info(&i2c->dev, "Re: %s: WATER reg:0x%02X WATER=%d DRY=%d\n", __func__,
|
|
Lp_DATA->BYTE[0],
|
|
Lp_DATA->BITS.WATER_DET,
|
|
Lp_DATA->BITS.RUN_DRY);
|
|
|
|
if (!Lp_DATA->BITS.AUTO_LP_ENABLE_BIT) {
|
|
if (Lp_DATA->BITS.WATER_DET == 1 || Lp_DATA->BITS.RUN_DRY == 0)
|
|
usbpd_data->s2mm005_i2c_err = -CCIC_I2C_VALUE_INVALID;
|
|
}
|
|
|
|
if (usbpd_data->s2mm005_i2c_err < 0) {
|
|
pr_err("%s : i2c err : ret = %d\n", __func__, usbpd_data->s2mm005_i2c_err);
|
|
Lp_DATA->BITS.WATER_DET = 0;
|
|
Lp_DATA->BITS.RUN_DRY = 1;
|
|
Lp_DATA->BITS.BOOTING_RUN_DRY = usbpd_data->booting_run_dry;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BATTERY_SAMSUNG)
|
|
if (lpcharge) {
|
|
dev_info(&i2c->dev, "%s: BOOTING_RUN_DRY=%d\n", __func__,
|
|
Lp_DATA->BITS.BOOTING_RUN_DRY);
|
|
usbpd_data->booting_run_dry = Lp_DATA->BITS.BOOTING_RUN_DRY;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
if (!Lp_DATA->BITS.WATER_DET) {
|
|
Lp_DATA->BITS.RUN_DRY = 1;
|
|
}
|
|
#endif
|
|
|
|
/* check for dry case */
|
|
if (Lp_DATA->BITS.RUN_DRY && !usbpd_data->run_dry) {
|
|
dev_info(&i2c->dev, "== WATER RUN-DRY DETECT ==\n");
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_WATER,
|
|
0/*attach*/, 0, 0);
|
|
}
|
|
|
|
usbpd_data->run_dry = Lp_DATA->BITS.RUN_DRY;
|
|
|
|
/* check for water case */
|
|
if ((Lp_DATA->BITS.WATER_DET & !usbpd_data->water_det)) {
|
|
dev_info(&i2c->dev, "== WATER DETECT ==\n");
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_WATER,
|
|
1/*attach*/, 0, 0);
|
|
}
|
|
|
|
usbpd_data->water_det = Lp_DATA->BITS.WATER_DET;
|
|
}
|
|
|
|
//////////////////////////////////////////// ////////////////////////////////////
|
|
// ATTACH processing
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void process_cc_attach(void * data,u8 *plug_attach_done)
|
|
{
|
|
struct s2mm005_data *usbpd_data = data;
|
|
struct i2c_client *i2c = usbpd_data->i2c;
|
|
LP_STATE_Type Lp_DATA;
|
|
FUNC_STATE_Type Func_DATA;
|
|
uint32_t R_len;
|
|
uint16_t REG_ADD;
|
|
#if defined(CONFIG_USB_HOST_NOTIFY)
|
|
struct otg_notify *o_notify = get_otg_notify();
|
|
#endif
|
|
#if defined(CONFIG_TYPEC)
|
|
enum typec_pwr_opmode mode = TYPEC_PWR_MODE_USB;
|
|
#endif
|
|
int is_dfp = 0;
|
|
int is_src = 0;
|
|
|
|
pr_info("%s\n",__func__);
|
|
|
|
// Check for moisture
|
|
process_cc_water(usbpd_data, &Lp_DATA);
|
|
|
|
if (usbpd_data->water_det) {
|
|
/* Moisture detection is only handled in the disconnected state(LPM). */
|
|
return;
|
|
} else if(!usbpd_data->run_dry || !usbpd_data->booting_run_dry) {
|
|
dev_info(&i2c->dev, " Water? No Dry\n");
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_WATER,
|
|
1/*attach*/, 0, 0);
|
|
|
|
if (usbpd_data->s2mm005_i2c_err >= 0) {
|
|
REG_ADD = 0x20;
|
|
R_len = 4;
|
|
s2mm005_read_byte(i2c, REG_ADD, Func_DATA.BYTE, R_len);
|
|
dev_info(&i2c->dev, "Rsvd_H:0x%02X PD_Nxt_State:0x%02X Rsvd_L:0x%02X PD_State:%02d\n",
|
|
Func_DATA.BYTES.RSP_BYTE2,
|
|
Func_DATA.BYTES.PD_Next_State,
|
|
Func_DATA.BYTES.RSP_BYTE1,
|
|
Func_DATA.BYTES.PD_State);
|
|
}
|
|
return;
|
|
} else {
|
|
if (usbpd_data->s2mm005_i2c_err < 0) {
|
|
if (usbpd_data->pd_state == State_PE_Initial_detach)
|
|
return;
|
|
|
|
Func_DATA.DATA = 0;
|
|
} else {
|
|
REG_ADD = 0x20;
|
|
R_len = 4;
|
|
|
|
s2mm005_read_byte(i2c, REG_ADD, Func_DATA.BYTE, R_len);
|
|
dev_info(&i2c->dev, "Rsvd_H:0x%02X PD_Nxt_State:0x%02X Rsvd_L:0x%02X PD_State:%02d\n",
|
|
Func_DATA.BYTES.RSP_BYTE2,
|
|
Func_DATA.BYTES.PD_Next_State,
|
|
Func_DATA.BYTES.RSP_BYTE1,
|
|
Func_DATA.BYTES.PD_State);
|
|
}
|
|
#if defined(CONFIG_USB_HW_PARAM)
|
|
if (!usbpd_data->pd_state && Func_DATA.BYTES.PD_State && Func_DATA.BITS.VBUS_CC_Short)
|
|
inc_hw_param(o_notify, USB_CCIC_VBUS_CC_SHORT_COUNT);
|
|
#endif
|
|
usbpd_data->pd_state = Func_DATA.BYTES.PD_State;
|
|
usbpd_data->func_state = Func_DATA.DATA;
|
|
|
|
is_dfp = usbpd_data->func_state & (0x1 << 26) ? 1 : 0;
|
|
is_src = usbpd_data->func_state & (0x1 << 25) ? 1 : 0;
|
|
dev_info(&i2c->dev, "func_state :0x%X, is_dfp : %d, is_src : %d\n", usbpd_data->func_state, \
|
|
is_dfp, is_src);
|
|
|
|
if (Func_DATA.BITS.RESET) {
|
|
dev_info(&i2c->dev, "ccic reset detected\n");
|
|
if (!Lp_DATA.BITS.AUTO_LP_ENABLE_BIT) {
|
|
/* AUTO LPM Enable */
|
|
s2mm005_manual_LPM(usbpd_data, 6);
|
|
}
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
set_enable_alternate_mode(ALTERNATE_MODE_START);
|
|
#endif
|
|
}
|
|
|
|
if(usbpd_data->pd_state == State_PE_SRC_Wait_New_Capabilities && Lp_DATA.BITS.Sleep_Cable_Detect)
|
|
{
|
|
s2mm005_manual_LPM(usbpd_data, 0x0D);
|
|
return;
|
|
}
|
|
|
|
}
|
|
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
|
|
store_usblog_notify(NOTIFY_FUNCSTATE, (void*)&usbpd_data->pd_state, NULL);
|
|
#endif
|
|
|
|
if(usbpd_data->pd_state != State_PE_Initial_detach)
|
|
{
|
|
*plug_attach_done = 1;
|
|
usbpd_data->plug_rprd_sel = 1;
|
|
if (usbpd_data->pd_state == State_PE_PRS_SRC_SNK_Transition_to_off) {
|
|
pr_info("%s State_PE_PRS_SRC_SNK_Transition_to_off! \n", __func__);
|
|
vbus_turn_on_ctrl(0);
|
|
} else if (usbpd_data->pd_state == State_PE_PRS_SNK_SRC_Source_on) {
|
|
pr_info("%s State_PE_PRS_SNK_SRC_Source_on! \n", __func__);
|
|
pd_noti.event = PDIC_NOTIFY_EVENT_PD_PRSWAP_SNKTOSRC;
|
|
pd_noti.sink_status.selected_pdo_num = 0;
|
|
pd_noti.sink_status.available_pdo_num = 0;
|
|
pd_noti.sink_status.current_pdo_num = 0;
|
|
ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_BATTERY,
|
|
CCIC_NOTIFY_ID_POWER_STATUS, 0, 0, 0);
|
|
vbus_turn_on_ctrl(1);
|
|
}
|
|
|
|
if (usbpd_data->is_dr_swap || usbpd_data->is_pr_swap) {
|
|
dev_info(&i2c->dev, "%s - ignore all pd_state by %s\n", __func__,(usbpd_data->is_dr_swap ? "dr_swap" : "pr_swap"));
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_TYPEC)
|
|
if (usbpd_data->pd_state == State_PE_SRC_Ready || usbpd_data->pd_state == State_PE_SNK_Ready)
|
|
{
|
|
usbpd_data->pd_support = true;
|
|
mode = s2mm005_get_pd_support(usbpd_data);
|
|
typec_set_pwr_opmode(usbpd_data->port, mode);
|
|
}
|
|
#endif
|
|
switch (usbpd_data->pd_state) {
|
|
case State_PE_SRC_Send_Capabilities:
|
|
case State_PE_SRC_Negotiate_Capability:
|
|
case State_PE_SRC_Transition_Supply:
|
|
case State_PE_SRC_Ready:
|
|
case State_PE_SRC_Disabled:
|
|
dev_info(&i2c->dev, "%s %d: pd_state:%02d, is_host = %d, is_client = %d\n",
|
|
__func__, __LINE__, usbpd_data->pd_state, usbpd_data->is_host, usbpd_data->is_client);
|
|
if (!is_dfp) {
|
|
dev_info(&i2c->dev, "%s No DFP! is_dfp:%d\n", __func__, is_dfp);
|
|
return;
|
|
}
|
|
/*for case after smart switch CtoC, device doesn't recharge*/
|
|
else if (usbpd_data->pd_state == State_PE_SRC_Send_Capabilities && usbpd_data->is_host == HOST_ON_BY_RD)
|
|
usbpd_data->is_host = HOST_OFF;
|
|
|
|
if (usbpd_data->is_client == CLIENT_ON) {
|
|
dev_info(&i2c->dev, "%s %d: pd_state:%02d, turn off client\n",
|
|
__func__, __LINE__, usbpd_data->pd_state);
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC,
|
|
CCIC_NOTIFY_ID_ATTACH,
|
|
0/*attach*/, 0/*rprd*/,0);
|
|
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
|
usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE;
|
|
#endif
|
|
#if defined(CONFIG_USB_HOST_NOTIFY)
|
|
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
|
|
#endif
|
|
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;
|
|
msleep(300);
|
|
}
|
|
if (usbpd_data->is_host == HOST_OFF) {
|
|
/* muic */
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC,
|
|
CCIC_NOTIFY_ID_ATTACH, 1/*attach*/, 1/*rprd*/,0);
|
|
/* otg */
|
|
usbpd_data->is_host = HOST_ON_BY_RD;
|
|
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
|
usbpd_data->power_role = DUAL_ROLE_PROP_PR_SRC;
|
|
#elif defined(CONFIG_TYPEC)
|
|
usbpd_data->typec_power_role = TYPEC_SOURCE;
|
|
typec_set_pwr_role(usbpd_data->port, TYPEC_SOURCE);
|
|
#endif
|
|
#if defined(CONFIG_USB_HOST_NOTIFY)
|
|
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 1);
|
|
#endif
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
|
|
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_DFP/*drp*/, 0);
|
|
#if defined(CONFIG_COMBO_REDRIVER)
|
|
mdelay(8);
|
|
ptn36502_config(USB3_ONLY_MODE, DFP);
|
|
#endif
|
|
/* add to turn on external 5V */
|
|
vbus_turn_on_ctrl(1);
|
|
#if defined(CONFIG_USB_HOST_NOTIFY)
|
|
if (is_blocked(o_notify, NOTIFY_BLOCK_TYPE_HOST))
|
|
s2mm005_set_upsm_mode();
|
|
#endif
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
// only start alternate mode at DFP state
|
|
// send_alternate_message(usbpd_data, VDM_DISCOVER_ID);
|
|
if (usbpd_data->acc_type != CCIC_DOCK_DETACHED) {
|
|
pr_info("%s: cancel_delayed_work_sync - pd_state : %d\n", __func__, usbpd_data->pd_state);
|
|
cancel_delayed_work_sync(&usbpd_data->acc_detach_work);
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
case State_PE_SNK_Wait_for_Capabilities:
|
|
case State_PE_SNK_Evaluate_Capability:
|
|
case State_PE_SNK_Ready:
|
|
case State_ErrorRecovery:
|
|
dev_info(&i2c->dev, "%s %d: pd_state:%02d, is_host = %d, is_client = %d\n",
|
|
__func__, __LINE__, usbpd_data->pd_state, usbpd_data->is_host, usbpd_data->is_client);
|
|
if (is_dfp) {
|
|
dev_info(&i2c->dev, "%s No UFP! is_dfp:%d\n", __func__, is_dfp);
|
|
return;
|
|
}
|
|
|
|
if (usbpd_data->is_host == HOST_ON_BY_RD) {
|
|
dev_info(&i2c->dev, "%s %d: pd_state:%02d, turn off host\n",
|
|
__func__, __LINE__, usbpd_data->pd_state);
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
if (usbpd_data->dp_is_connect == 1) {
|
|
dp_detach(usbpd_data);
|
|
}
|
|
#endif
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 0/*attach*/, 1/*rprd*/, 0);
|
|
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
|
usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE;
|
|
#endif
|
|
#if defined(CONFIG_USB_HOST_NOTIFY)
|
|
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
|
|
#endif
|
|
/* add to turn off external 5V */
|
|
vbus_turn_on_ctrl(0);
|
|
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;
|
|
msleep(300);
|
|
}
|
|
|
|
if (Lp_DATA.BITS.PDSTATE29_SBU_DONE) {
|
|
dev_info(&i2c->dev, "%s SBU check done\n", __func__);
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH,
|
|
1/*attach*/, 0/*rprd*/,
|
|
(Func_DATA.BITS.VBUS_CC_Short || Func_DATA.BITS.VBUS_SBU_Short) ? Rp_Abnormal:Func_DATA.BITS.RP_CurrentLvl);
|
|
} else {
|
|
/* muic */
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH,
|
|
1/*attach*/, 0/*rprd*/, Rp_Sbu_check);
|
|
}
|
|
|
|
if (usbpd_data->is_client == CLIENT_OFF && usbpd_data->is_host == HOST_OFF) {
|
|
/* usb */
|
|
usbpd_data->is_client = CLIENT_ON;
|
|
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
|
usbpd_data->power_role = DUAL_ROLE_PROP_PR_SNK;
|
|
#elif defined(CONFIG_TYPEC)
|
|
usbpd_data->typec_power_role = TYPEC_SINK;
|
|
typec_set_pwr_role(usbpd_data->port, TYPEC_SINK);
|
|
#endif
|
|
#if defined(CONFIG_USB_HOST_NOTIFY)
|
|
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
|
|
#endif
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
|
|
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_UFP/*drp*/, 0);
|
|
#if defined(CONFIG_COMBO_REDRIVER)
|
|
ptn36502_config(USB3_ONLY_MODE, UFP);
|
|
#endif
|
|
}
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
} else {
|
|
*plug_attach_done = 0;
|
|
usbpd_data->plug_rprd_sel = 0;
|
|
usbpd_data->is_dr_swap = 0;
|
|
usbpd_data->is_pr_swap = 0;
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
if (usbpd_data->dp_is_connect == 1) {
|
|
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));
|
|
}
|
|
}
|
|
#endif
|
|
/* muic */
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH,
|
|
0/*attach*/, 0/*rprd*/, 0);
|
|
#if defined(CONFIG_COMBO_REDRIVER)
|
|
ptn36502_config(INIT_MODE, 0);
|
|
#endif
|
|
if(usbpd_data->is_host > HOST_OFF || usbpd_data->is_client > CLIENT_OFF) {
|
|
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
|
if(usbpd_data->is_host > HOST_OFF || usbpd_data->power_role == DUAL_ROLE_PROP_PR_SRC)
|
|
vbus_turn_on_ctrl(0);
|
|
#elif defined(CONFIG_TYPEC)
|
|
if(usbpd_data->is_host > HOST_OFF || usbpd_data->typec_power_role == TYPEC_SOURCE)
|
|
vbus_turn_on_ctrl(0);
|
|
#endif
|
|
/* usb or otg */
|
|
dev_info(&i2c->dev, "%s %d: pd_state:%02d, is_host = %d, is_client = %d\n",
|
|
__func__, __LINE__, usbpd_data->pd_state, usbpd_data->is_host, usbpd_data->is_client);
|
|
usbpd_data->is_host = HOST_OFF;
|
|
usbpd_data->is_client = CLIENT_OFF;
|
|
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
|
usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE;
|
|
#elif defined(CONFIG_TYPEC)
|
|
if (usbpd_data->partner) {
|
|
pr_info("%s : typec_unregister_partner\n", __func__);
|
|
if (!IS_ERR(usbpd_data->partner))
|
|
typec_unregister_partner(usbpd_data->partner);
|
|
usbpd_data->partner = NULL;
|
|
usbpd_data->typec_power_role = TYPEC_SINK;
|
|
usbpd_data->typec_data_role = TYPEC_DEVICE;
|
|
usbpd_data->pwr_opmode = TYPEC_PWR_MODE_USB;
|
|
}
|
|
if (usbpd_data->typec_try_state_change == TRY_ROLE_SWAP_PR ||
|
|
usbpd_data->typec_try_state_change == TRY_ROLE_SWAP_DR) {
|
|
/* Role change try and new mode detected */
|
|
pr_info("%s : typec_reverse_completion, detached while pd_swap\n", __func__);
|
|
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE;
|
|
complete(&usbpd_data->typec_reverse_completion);
|
|
}
|
|
#endif
|
|
usbpd_data->pd_support = false;
|
|
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
|
|
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
|
|
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0);
|
|
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
|
if (!usbpd_data->try_state_change)
|
|
s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DRP);
|
|
#elif defined(CONFIG_TYPEC)
|
|
if (!usbpd_data->typec_try_state_change) {
|
|
s2mm005_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DRP);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Detach processing
|
|
// 1. Used when the s2mm005 unbind case
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void process_cc_detach(void * data)
|
|
{
|
|
struct s2mm005_data *usbpd_data = data;
|
|
#if defined(CONFIG_USB_HOST_NOTIFY)
|
|
struct otg_notify *o_notify = get_otg_notify();
|
|
#endif
|
|
|
|
if (usbpd_data->pd_state) {
|
|
usbpd_data->pd_state = State_PE_Initial_detach;
|
|
#if defined(CONFIG_CCIC_NOTIFIER)
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 0/*attach*/, 0/*rprd*/, 0);
|
|
#endif
|
|
if(usbpd_data->is_host > HOST_OFF)
|
|
vbus_turn_on_ctrl(0);
|
|
usbpd_data->is_host = HOST_OFF;
|
|
usbpd_data->is_client = CLIENT_OFF;
|
|
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
|
usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE;
|
|
#elif defined(CONFIG_TYPEC)
|
|
usbpd_data->typec_power_role = TYPEC_SINK;
|
|
typec_set_pwr_role(usbpd_data->port, TYPEC_SINK);
|
|
typec_set_data_role(usbpd_data->port, TYPEC_DEVICE);
|
|
typec_set_pwr_opmode(usbpd_data->port, TYPEC_PWR_MODE_USB);
|
|
#endif
|
|
#if defined(CONFIG_USB_HOST_NOTIFY)
|
|
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Get staus interrupt register
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void process_cc_get_int_status(void *data, uint32_t *pPRT_MSG, MSG_IRQ_STATUS_Type *MSG_IRQ_State)
|
|
{
|
|
struct s2mm005_data *usbpd_data = data;
|
|
struct i2c_client *i2c = usbpd_data->i2c;
|
|
uint8_t R_INT_STATUS[48];
|
|
uint16_t REG_ADD;
|
|
uint32_t cnt;
|
|
uint32_t IrqPrint;
|
|
VDM_MSG_IRQ_STATUS_Type VDM_MSG_IRQ_State;
|
|
SSM_MSG_IRQ_STATUS_Type SSM_MSG_IRQ_State;
|
|
AP_REQ_GET_STATUS_Type AP_REQ_GET_State;
|
|
FUNC_STATE_Type state_type;
|
|
uint32_t is_dfp;
|
|
|
|
pr_info("%s\n",__func__);
|
|
for(cnt = 0;cnt < 48;cnt++)
|
|
{
|
|
R_INT_STATUS[cnt] = 0;
|
|
}
|
|
|
|
REG_ADD = 0x30;
|
|
s2mm005_read_byte(i2c, REG_ADD, R_INT_STATUS, 48); // sram :
|
|
|
|
s2mm005_int_clear(usbpd_data); // interrupt clear
|
|
pPRT_MSG = (uint32_t *)&R_INT_STATUS[0];
|
|
dev_info(&i2c->dev, "SYNC Status = 0x%08X\n",pPRT_MSG[0]);
|
|
dev_info(&i2c->dev, "CTRL MSG Status = 0x%08X\n",pPRT_MSG[1]);
|
|
dev_info(&i2c->dev, "DATA MSG Status = 0x%08X\n",pPRT_MSG[2]);
|
|
dev_info(&i2c->dev, "EXTD MSG Status = 0x%08X\n",pPRT_MSG[3]);
|
|
dev_info(&i2c->dev, "MSG IRQ Status = 0x%08X\n",pPRT_MSG[4]);
|
|
dev_info(&i2c->dev, "VDM IRQ Status = 0x%08X\n",pPRT_MSG[5]);
|
|
dev_info(&i2c->dev, "SSM_MSG IRQ Status = 0x%08X\n",pPRT_MSG[6]);
|
|
dev_info(&i2c->dev, "AP REQ GET Status = 0x%08X\n",pPRT_MSG[7]);
|
|
|
|
dev_info(&i2c->dev, "0x50 IRQ Status = 0x%08X\n",pPRT_MSG[8]);
|
|
dev_info(&i2c->dev, "0x54 IRQ Status = 0x%08X\n",pPRT_MSG[9]);
|
|
dev_info(&i2c->dev, "0x58 IRQ Status = 0x%08X\n",pPRT_MSG[10]);
|
|
MSG_IRQ_State->DATA = pPRT_MSG[4];
|
|
VDM_MSG_IRQ_State.DATA = pPRT_MSG[5];
|
|
SSM_MSG_IRQ_State.DATA = pPRT_MSG[6];
|
|
AP_REQ_GET_State.DATA = pPRT_MSG[7];
|
|
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
if((AP_REQ_GET_State.BYTES[0] >> 5) > 0) {
|
|
dev_info(&i2c->dev, "FAC: Repeat_State:%d, Repeat_RID:%d, RID0:%d\n",
|
|
AP_REQ_GET_State.BITS.FAC_Abnormal_Repeat_State,
|
|
AP_REQ_GET_State.BITS.FAC_Abnormal_Repeat_RID,
|
|
AP_REQ_GET_State.BITS.FAC_Abnormal_RID0);
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_CCIC, CCIC_NOTIFY_ID_FAC,
|
|
AP_REQ_GET_State.BYTES[0] >> 5, 0, 0); // b5~b7
|
|
}
|
|
#endif
|
|
|
|
IrqPrint = 1;
|
|
for(cnt=0;cnt<32;cnt++)
|
|
{
|
|
if((MSG_IRQ_State->DATA&IrqPrint) != 0)
|
|
{
|
|
dev_info(&i2c->dev, " - IRQ %s \n",&MSG_IRQ_Print[cnt][0]);
|
|
}
|
|
IrqPrint = (IrqPrint<<1);
|
|
}
|
|
if (MSG_IRQ_State->BITS.Ctrl_Flag_DR_Swap)
|
|
{
|
|
usbpd_data->is_dr_swap++;
|
|
dev_info(&i2c->dev, "is_dr_swap count : 0x%x\n", usbpd_data->is_dr_swap);
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
if (usbpd_data->dp_is_connect)
|
|
{
|
|
dev_info(&i2c->dev, "dr_swap is skiped, current status is dp mode !!\n");
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
state_type.DATA = usbpd_data->func_state;
|
|
is_dfp = state_type.BITS.IS_DFP;
|
|
|
|
if (usbpd_data->is_host == HOST_ON_BY_RD) {
|
|
if (is_dfp) {
|
|
pr_err("%s error. is_dfp is true. but already dfp.\n",__func__);
|
|
goto error;
|
|
}
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
|
|
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0);
|
|
msleep(300);
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH,
|
|
1/*attach*/, 0/*rprd*/,0);
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
|
|
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_UFP/*drp*/, 0);
|
|
usbpd_data->is_host = HOST_OFF;
|
|
usbpd_data->is_client = CLIENT_ON;
|
|
} else if (usbpd_data->is_client == CLIENT_ON) {
|
|
if (!is_dfp) {
|
|
pr_err("%s error. is_dfp is false. but already ufp.\n",__func__);
|
|
goto error;
|
|
}
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
|
|
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0);
|
|
msleep(300);
|
|
ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_MUIC,
|
|
CCIC_NOTIFY_ID_ATTACH, 1/*attach*/, 1/*rprd*/,0);
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
|
|
1/*attach*/,
|
|
USB_STATUS_NOTIFY_ATTACH_DFP/*drp*/, 0);
|
|
usbpd_data->is_host = HOST_ON_BY_RD;
|
|
usbpd_data->is_client = CLIENT_OFF;
|
|
} else {
|
|
/* both of host and device not worked case.
|
|
rebooting case with dex connection */
|
|
|
|
if (is_dfp) {
|
|
pr_info("%s: all usb off case : is dfp=%d : run usb host \n", __func__, is_dfp);
|
|
ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_MUIC,
|
|
CCIC_NOTIFY_ID_ATTACH, 1/*attach*/, 1/*rprd*/,0);
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
|
|
1/*attach*/,
|
|
USB_STATUS_NOTIFY_ATTACH_DFP/*drp*/, 0);
|
|
usbpd_data->is_host = HOST_ON_BY_RD;
|
|
usbpd_data->is_client = CLIENT_OFF;
|
|
} else {
|
|
pr_info("%s: all usb off case : is dfp=%d : run usb client \n", __func__, is_dfp);
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH,
|
|
1/*attach*/, 0/*rprd*/,0);
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
|
|
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_UFP/*drp*/, 0);
|
|
usbpd_data->is_host = HOST_OFF;
|
|
usbpd_data->is_client = CLIENT_ON;
|
|
|
|
}
|
|
|
|
}
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
error:
|
|
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
if(VDM_MSG_IRQ_State.DATA && usbpd_data->is_host == HOST_ON_BY_RD)
|
|
receive_alternate_message(usbpd_data, &VDM_MSG_IRQ_State);
|
|
if(SSM_MSG_IRQ_State.BITS.Ssm_Flag_Unstructured_Data
|
|
&& usbpd_data->is_host == HOST_ON_BY_RD)
|
|
receive_unstructured_vdm_message(usbpd_data, &SSM_MSG_IRQ_State);
|
|
if(!AP_REQ_GET_State.BITS.Alt_Mode_By_I2C)
|
|
set_enable_alternate_mode(ALTERNATE_MODE_RESET);
|
|
#endif
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// RID processing
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void process_cc_rid(void *data)
|
|
{
|
|
struct s2mm005_data *usbpd_data = data;
|
|
struct i2c_client *i2c = usbpd_data->i2c;
|
|
static int prev_rid = RID_OPEN;
|
|
u8 rid;
|
|
#if defined(CONFIG_USB_HOST_NOTIFY)
|
|
struct otg_notify *o_notify = get_otg_notify();
|
|
#endif
|
|
|
|
pr_info("%s\n",__func__);
|
|
s2mm005_read_byte_16(i2c, 0x50, &rid); // fundtion read , 0x20 , 0x0:detach , not 0x0 attach : source 3,6,7 / sink 16:17:21:29(decimanl)
|
|
dev_info(&i2c->dev, "prev_rid:%x , RID:%x\n",prev_rid, rid);
|
|
usbpd_data->cur_rid = rid;
|
|
|
|
if(rid) {
|
|
if(prev_rid != rid)
|
|
{
|
|
#if defined(CONFIG_CCIC_NOTIFIER)
|
|
/* rid */
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_RID,
|
|
rid/*rid*/, 0, 0);
|
|
|
|
if( rid == RID_523K || rid == RID_619K) {
|
|
usbpd_data->is_client = CLIENT_OFF;
|
|
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
|
|
usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE;
|
|
#endif
|
|
#if defined(CONFIG_USB_HOST_NOTIFY)
|
|
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
|
|
#endif
|
|
/* usb or otg */
|
|
ccic_event_work(usbpd_data,
|
|
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
|
|
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/, 0);
|
|
}
|
|
#endif
|
|
}
|
|
prev_rid = rid;
|
|
}
|
|
return;
|
|
}
|
|
|