blob: c97b478c9a189f3bddd606b2cb51dd61352f6be3 [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/mfd/max77705-private.h>
#include <linux/ccic/max77705_usbc.h>
#include <linux/ccic/max77705_alternate.h>
#include <linux/completion.h>
#if defined(CONFIG_USB_HOST_NOTIFY)
#include <linux/usb_notify.h>
#endif
#if defined(CONFIG_CCIC_NOTIFIER)
#include <linux/ccic/ccic_notifier.h>
#include <linux/ccic/ccic_core.h>
#endif
#define UVDM_DEBUG (0)
#define SEC_UVDM_ALIGN (4)
#define MAX_DATA_FIRST_UVDMSET 12
#define MAX_DATA_NORMAL_UVDMSET 16
#define CHECKSUM_DATA_COUNT 20
#define MAX_INPUT_DATA (255)
extern struct max77705_usbc_platform_data *g_usbc_data;
const struct DP_DP_DISCOVER_IDENTITY DP_DISCOVER_IDENTITY = {
{
.BITS.Num_Of_VDO = 1,
.BITS.Cmd_Type = ACK,
.BITS.Reserved = 0
},
{
.BITS.VDM_command = Discover_Identity,
.BITS.Rsvd2_VDM_header = 0,
.BITS.VDM_command_type = REQ,
.BITS.Object_Position = 0,
.BITS.Rsvd_VDM_header = 0,
.BITS.Structured_VDM_Version = Version_1_0,
.BITS.VDM_Type = STRUCTURED_VDM,
.BITS.Standard_Vendor_ID = 0xFF00
}
};
const struct DP_DP_DISCOVER_ENTER_MODE DP_DISCOVER_ENTER_MODE = {
{
.BITS.Num_Of_VDO = 1,
.BITS.Cmd_Type = ACK,
.BITS.Reserved = 0
},
{
.BITS.VDM_command = Enter_Mode,
.BITS.Rsvd2_VDM_header = 0,
.BITS.VDM_command_type = REQ,
.BITS.Object_Position = 1,
.BITS.Rsvd_VDM_header = 0,
.BITS.Structured_VDM_Version = Version_1_0,
.BITS.VDM_Type = STRUCTURED_VDM,
.BITS.Standard_Vendor_ID = 0xFF01
}
};
struct DP_DP_CONFIGURE DP_CONFIGURE = {
{
.BITS.Num_Of_VDO = 2,
.BITS.Cmd_Type = ACK,
.BITS.Reserved = 0
},
{
.BITS.VDM_command = 17, /* SVID Specific Command */
.BITS.Rsvd2_VDM_header = 0,
.BITS.VDM_command_type = REQ,
.BITS.Object_Position = 1,
.BITS.Rsvd_VDM_header = 0,
.BITS.Structured_VDM_Version = Version_1_0,
.BITS.VDM_Type = STRUCTURED_VDM,
.BITS.Standard_Vendor_ID = 0xFF01
},
{
.BITS.SEL_Configuration = num_Cfg_UFP_U_as_UFP_D,
.BITS.Select_DP_V1p3 = 1,
.BITS.Select_USB_Gen2 = 0,
.BITS.Select_Reserved_1 = 0,
.BITS.Select_Reserved_2 = 0,
.BITS.DFP_D_PIN_Assign_A = 0,
.BITS.DFP_D_PIN_Assign_B = 0,
.BITS.DFP_D_PIN_Assign_C = 0,
.BITS.DFP_D_PIN_Assign_D = 1,
.BITS.DFP_D_PIN_Assign_E = 0,
.BITS.DFP_D_PIN_Assign_F = 0,
.BITS.DFP_D_PIN_Reserved = 0,
.BITS.UFP_D_PIN_Assign_A = 0,
.BITS.UFP_D_PIN_Assign_B = 0,
.BITS.UFP_D_PIN_Assign_C = 0,
.BITS.UFP_D_PIN_Assign_D = 0,
.BITS.UFP_D_PIN_Assign_E = 0,
.BITS.UFP_D_PIN_Assign_F = 0,
.BITS.UFP_D_PIN_Reserved = 0,
.BITS.DP_MODE_Reserved = 0
}
};
struct SS_DEX_DISCOVER_MODE SS_DEX_DISCOVER_MODE_MODE = {
{
.BITS.Num_Of_VDO = 1,
.BITS.Cmd_Type = ACK,
.BITS.Reserved = 0
},
{
.BITS.VDM_command = Discover_Modes,
.BITS.Rsvd2_VDM_header = 0,
.BITS.VDM_command_type = REQ,
.BITS.Object_Position = 0,
.BITS.Rsvd_VDM_header = 0,
.BITS.Structured_VDM_Version = Version_1_0,
.BITS.VDM_Type = STRUCTURED_VDM,
.BITS.Standard_Vendor_ID = SAMSUNG_VENDOR_ID
}
};
struct SS_DEX_ENTER_MODE SS_DEX_DISCOVER_ENTER_MODE = {
{
.BITS.Num_Of_VDO = 1,
.BITS.Cmd_Type = ACK,
.BITS.Reserved = 0
},
{
.BITS.VDM_command = Enter_Mode,
.BITS.Rsvd2_VDM_header = 0,
.BITS.VDM_command_type = REQ,
.BITS.Object_Position = 1,
.BITS.Rsvd_VDM_header = 0,
.BITS.Structured_VDM_Version = Version_1_0,
.BITS.VDM_Type = STRUCTURED_VDM,
.BITS.Standard_Vendor_ID = SAMSUNG_VENDOR_ID
}
};
struct SS_UNSTRUCTURED_VDM_MSG SS_DEX_UNSTRUCTURED_VDM;
static char VDM_MSG_IRQ_State_Print[9][40] = {
{"bFLAG_Vdm_Reserve_b0"},
{"bFLAG_Vdm_Discover_ID"},
{"bFLAG_Vdm_Discover_SVIDs"},
{"bFLAG_Vdm_Discover_MODEs"},
{"bFLAG_Vdm_Enter_Mode"},
{"bFLAG_Vdm_Exit_Mode"},
{"bFLAG_Vdm_Attention"},
{"bFlag_Vdm_DP_Status_Update"},
{"bFlag_Vdm_DP_Configure"},
};
static char DP_Pin_Assignment_Print[7][40] = {
{"DP_Pin_Assignment_None"},
{"DP_Pin_Assignment_A"},
{"DP_Pin_Assignment_B"},
{"DP_Pin_Assignment_C"},
{"DP_Pin_Assignment_D"},
{"DP_Pin_Assignment_E"},
{"DP_Pin_Assignment_F"},
};
static uint8_t DP_Pin_Assignment_Data[7] = {
DP_PIN_ASSIGNMENT_NODE,
DP_PIN_ASSIGNMENT_A,
DP_PIN_ASSIGNMENT_B,
DP_PIN_ASSIGNMENT_C,
DP_PIN_ASSIGNMENT_D,
DP_PIN_ASSIGNMENT_E,
DP_PIN_ASSIGNMENT_F,
};
int max77705_process_check_accessory(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
#if defined(CONFIG_USB_HW_PARAM)
struct otg_notify *o_notify = get_otg_notify();
#endif
uint16_t vid = usbpd_data->Vendor_ID;
uint16_t pid = usbpd_data->Product_ID;
uint16_t acc_type = CCIC_DOCK_DETACHED;
/* detect Gear VR */
if (usbpd_data->acc_type == CCIC_DOCK_DETACHED) {
if (vid == SAMSUNG_VENDOR_ID) {
switch (pid) {
/* GearVR: Reserved GearVR PID+6 */
case GEARVR_PRODUCT_ID:
case GEARVR_PRODUCT_ID_1:
case GEARVR_PRODUCT_ID_2:
case GEARVR_PRODUCT_ID_3:
case GEARVR_PRODUCT_ID_4:
case GEARVR_PRODUCT_ID_5:
acc_type = CCIC_DOCK_HMT;
msg_maxim("Samsung Gear VR connected.");
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_VR_USE_COUNT);
#endif
break;
case DEXDOCK_PRODUCT_ID:
acc_type = CCIC_DOCK_DEX;
msg_maxim("Samsung DEX connected.");
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_DEX_USE_COUNT);
#endif
break;
case DEXPAD_PRODUCT_ID:
acc_type = CCIC_DOCK_DEXPAD;
msg_maxim("Samsung DEX PAD connected.");
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_DEX_USE_COUNT);
#endif
break;
case HDMI_PRODUCT_ID:
acc_type = CCIC_DOCK_HDMI;
msg_maxim("Samsung HDMI connected.");
break;
default:
msg_maxim("default device connected.");
acc_type = CCIC_DOCK_NEW;
break;
}
} else if (vid == SAMSUNG_MPA_VENDOR_ID) {
switch (pid) {
case MPA_PRODUCT_ID:
acc_type = CCIC_DOCK_MPA;
msg_maxim("Samsung MPA connected.");
break;
default:
msg_maxim("default device connected.");
acc_type = CCIC_DOCK_NEW;
break;
}
}
usbpd_data->acc_type = acc_type;
} else
acc_type = usbpd_data->acc_type;
if (acc_type != CCIC_DOCK_NEW)
ccic_send_dock_intent(acc_type);
ccic_send_dock_uevent(vid, pid, acc_type);
return 1;
}
void max77705_vdm_process_printf(char *type, char *vdm_data, int len)
{
#if 0
int i = 0;
for (i = 2; i < len; i++)
msg_maxim("[%s] , %d, [0x%x]", type, i, vdm_data[i]);
#endif
}
void max77705_vdm_process_set_identity_req(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
/* send the opcode */
usbc_cmd_data write_data;
int len = sizeof(DP_DISCOVER_IDENTITY);
int vdm_header_num = sizeof(UND_DATA_MSG_VDM_HEADER_Type);
int vdo0_num = 0;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_SET_VDM_REQ;
memcpy(write_data.write_data, &DP_DISCOVER_IDENTITY, sizeof(DP_DISCOVER_IDENTITY));
write_data.write_length = len;
vdo0_num = DP_DISCOVER_IDENTITY.byte_data.BITS.Num_Of_VDO * 4;
write_data.read_length = OPCODE_SIZE + OPCODE_HEADER_SIZE + vdm_header_num + vdo0_num;
max77705_usbc_opcode_write(usbpd_data, &write_data);
}
void max77705_vdm_process_set_DP_enter_mode_req(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
usbc_cmd_data write_data;
int len = sizeof(DP_DISCOVER_ENTER_MODE);
int vdm_header_num = sizeof(UND_DATA_MSG_VDM_HEADER_Type);
int vdo0_num = 0;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_SET_VDM_REQ;
memcpy(write_data.write_data, &DP_DISCOVER_ENTER_MODE, sizeof(DP_DISCOVER_ENTER_MODE));
write_data.write_length = len;
vdo0_num = DP_DISCOVER_ENTER_MODE.byte_data.BITS.Num_Of_VDO * 4;
write_data.read_length = OPCODE_SIZE + OPCODE_HEADER_SIZE + vdm_header_num + vdo0_num;
max77705_usbc_opcode_push(usbpd_data, &write_data);
}
void max77705_vdm_process_set_DP_configure_mode_req(void *data, uint8_t W_DATA)
{
struct max77705_usbc_platform_data *usbpd_data = data;
usbc_cmd_data write_data;
int len = sizeof(DP_CONFIGURE);
int vdm_header_num = sizeof(UND_DATA_MSG_VDM_HEADER_Type);
int vdo0_num = 0;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_SET_VDM_REQ;
memcpy(write_data.write_data, &DP_CONFIGURE, sizeof(DP_CONFIGURE));
write_data.write_data[6] = W_DATA;
write_data.write_length = len;
vdo0_num = DP_CONFIGURE.byte_data.BITS.Num_Of_VDO * 4;
write_data.read_length = OPCODE_SIZE + OPCODE_HEADER_SIZE + vdm_header_num + vdo0_num;
max77705_usbc_opcode_push(usbpd_data, &write_data);
}
void max77705_vdm_process_set_Dex_Disover_mode_req(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
usbc_cmd_data write_data;
int len = sizeof(SS_DEX_DISCOVER_MODE_MODE);
int vdm_header_num = sizeof(UND_DATA_MSG_VDM_HEADER_Type);
int vdo0_num = 0;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_SET_VDM_REQ;
memcpy(write_data.write_data, &SS_DEX_DISCOVER_MODE_MODE, sizeof(SS_DEX_DISCOVER_MODE_MODE));
write_data.write_length = len;
vdo0_num = SS_DEX_DISCOVER_MODE_MODE.byte_data.BITS.Num_Of_VDO * 4;
write_data.read_length = OPCODE_SIZE + OPCODE_HEADER_SIZE + vdm_header_num + vdo0_num;
max77705_usbc_opcode_push(usbpd_data, &write_data);
}
void max77705_vdm_process_set_Dex_enter_mode_req(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
usbc_cmd_data write_data;
int len = sizeof(SS_DEX_DISCOVER_ENTER_MODE);
int vdm_header_num = sizeof(UND_DATA_MSG_VDM_HEADER_Type);
int vdo0_num = 0;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_SET_VDM_REQ;
memcpy(write_data.write_data, &SS_DEX_DISCOVER_ENTER_MODE, sizeof(SS_DEX_DISCOVER_ENTER_MODE));
write_data.write_length = len;
vdo0_num = SS_DEX_DISCOVER_ENTER_MODE.byte_data.BITS.Num_Of_VDO * 4;
write_data.read_length = OPCODE_SIZE + OPCODE_HEADER_SIZE + vdm_header_num + vdo0_num;
max77705_usbc_opcode_push(usbpd_data, &write_data);
}
static int max77705_vdm_process_discover_svids(void *data, char *vdm_data, int len)
{
struct max77705_usbc_platform_data *usbpd_data = data;
int timeleft = 0, i = 0;
uint16_t svid = 0;
#if defined(CONFIG_USB_HOST_NOTIFY)
struct otg_notify *o_notify = get_otg_notify();
#endif
DIS_MODE_DP_CAPA_Type *pDP_DIS_MODE = (DIS_MODE_DP_CAPA_Type *)&vdm_data[0];
int num_of_vdos = (pDP_DIS_MODE->MSG_HEADER.BITS.Number_of_obj - 2) * 2;
UND_VDO1_Type *DATA_MSG_VDO1 = (UND_VDO1_Type *)&vdm_data[8];
usbpd_data->SVID_DP = 0;
usbpd_data->SVID_0 = DATA_MSG_VDO1->BITS.SVID_0;
usbpd_data->SVID_1 = DATA_MSG_VDO1->BITS.SVID_1;
for (i = 0; i < num_of_vdos; i++) {
memcpy(&svid, &vdm_data[8 + i * 2], 2);
if (svid == TypeC_DP_SUPPORT) {
msg_maxim("svid_%d : 0x%X", i, svid);
usbpd_data->SVID_DP = svid;
break;
}
}
if (usbpd_data->SVID_DP == TypeC_DP_SUPPORT) {
if (usbpd_data->is_client == CLIENT_ON) {
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_MUIC,
CCIC_NOTIFY_ID_ATTACH, 0/*attach*/, 0/*rprd*/, 0);
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) {
/* muic */
max77705_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;
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_DFP/*drp*/, 0);
}
usbpd_data->dp_is_connect = 1;
/* If you want to support USB SuperSpeed when you connect
* Display port dongle, You should change dp_hs_connect depend
* on Pin assignment.If DP use 4lane(Pin Assignment C,E,A),
* dp_hs_connect is 1. USB can support HS.If DP use 2lane(Pin Assignment B,D,F), dp_hs_connect is 0. USB
* can support SS
*/
usbpd_data->dp_hs_connect = 1;
#if 1
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_DP, CCIC_NOTIFY_ID_DP_CONNECT,
CCIC_NOTIFY_ATTACH, usbpd_data->Vendor_ID, usbpd_data->Product_ID);
#if defined(CONFIG_USB_HOST_NOTIFY)
if (o_notify) {
#if defined(CONFIG_USB_HW_PARAM)
inc_hw_param(o_notify, USB_CCIC_DP_USE_COUNT);
#endif
timeleft = wait_event_interruptible_timeout(usbpd_data->host_turn_on_wait_q,
usbpd_data->host_turn_on_event && !usbpd_data->detach_done_wait, (usbpd_data->host_turn_on_wait_time)*HZ);
msg_maxim("%s host turn on wait = %d\n", __func__, timeleft);
}
#endif
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_USB_DP, CCIC_NOTIFY_ID_USB_DP,
usbpd_data->dp_is_connect /*attach*/, usbpd_data->dp_hs_connect, 0);
#endif
}
msg_maxim("SVID_0 : 0x%X, SVID_1 : 0x%X",
usbpd_data->SVID_0, usbpd_data->SVID_1);
return 0;
}
static int max77705_vdm_process_discover_mode(void *data, char *vdm_data, int len)
{
struct max77705_usbc_platform_data *usbpd_data = data;
DIS_MODE_DP_CAPA_Type *pDP_DIS_MODE = (DIS_MODE_DP_CAPA_Type *)&vdm_data[0];
UND_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM = (UND_DATA_MSG_VDM_HEADER_Type *)&vdm_data[4];
msg_maxim("vendor_id = 0x%04x , svid_1 = 0x%04x", DATA_MSG_VDM->BITS.Standard_Vendor_ID, usbpd_data->SVID_1);
if (DATA_MSG_VDM->BITS.Standard_Vendor_ID == TypeC_DP_SUPPORT && usbpd_data->SVID_DP == TypeC_DP_SUPPORT) {
/* pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS. */
msg_maxim("pDP_DIS_MODE->MSG_HEADER.DATA = 0x%08X", pDP_DIS_MODE->MSG_HEADER.DATA);
msg_maxim("pDP_DIS_MODE->DATA_MSG_VDM_HEADER.DATA = 0x%08X", pDP_DIS_MODE->DATA_MSG_VDM_HEADER.DATA);
msg_maxim("pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.DATA = 0x%08X", pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.DATA);
if (pDP_DIS_MODE->MSG_HEADER.BITS.Number_of_obj > 1) {
if ((pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Port_Capability == num_UFP_D_Capable)
&& (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Receptacle_Indication == num_USB_TYPE_C_Receptacle)) {
usbpd_data->pin_assignment = pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.UFP_D_Pin_Assignments;
msg_maxim("1. support UFP_D 0x%08x", usbpd_data->pin_assignment);
} else if (((pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Port_Capability == num_UFP_D_Capable)
&& (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Receptacle_Indication == num_USB_TYPE_C_PLUG))) {
usbpd_data->pin_assignment = pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.DFP_D_Pin_Assignments;
msg_maxim("2. support DFP_D 0x%08x", usbpd_data->pin_assignment);
} else if (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Port_Capability == num_DFP_D_and_UFP_D_Capable) {
if (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Receptacle_Indication == num_USB_TYPE_C_PLUG) {
usbpd_data->pin_assignment = pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.DFP_D_Pin_Assignments;
msg_maxim("3. support DFP_D 0x%08x", usbpd_data->pin_assignment);
} else {
usbpd_data->pin_assignment = pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.UFP_D_Pin_Assignments;
msg_maxim("4. support UFP_D 0x%08x", usbpd_data->pin_assignment);
}
} else if (pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS.Port_Capability == num_DFP_D_Capable) {
usbpd_data->pin_assignment = DP_PIN_ASSIGNMENT_NODE;
msg_maxim("do not support Port_Capability num_DFP_D_Capable!!!");
return -EINVAL;
} else {
usbpd_data->pin_assignment = DP_PIN_ASSIGNMENT_NODE;
msg_maxim("there is not valid object information!!!");
return -EINVAL;
}
}
}
if (DATA_MSG_VDM->BITS.Standard_Vendor_ID == SAMSUNG_VENDOR_ID) {
msg_maxim("dex mode discover_mode ack status!");
/* pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.BITS. */
msg_maxim("pDP_DIS_MODE->MSG_HEADER.DATA = 0x%08X", pDP_DIS_MODE->MSG_HEADER.DATA);
msg_maxim("pDP_DIS_MODE->DATA_MSG_VDM_HEADER.DATA = 0x%08X", pDP_DIS_MODE->DATA_MSG_VDM_HEADER.DATA);
msg_maxim("pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.DATA = 0x%08X", pDP_DIS_MODE->DATA_MSG_MODE_VDO_DP.DATA);
/*Samsung Enter Mode */
if (usbpd_data->send_enter_mode_req == 0) {
msg_maxim("dex: second enter mode request");
usbpd_data->send_enter_mode_req = 1;
max77705_vdm_process_set_Dex_enter_mode_req(usbpd_data);
}
} else {
max77705_vdm_process_set_DP_enter_mode_req(usbpd_data);
}
return 0;
}
static int max77705_vdm_process_enter_mode(void *data, char *vdm_data, int len)
{
struct max77705_usbc_platform_data *usbpd_data = data;
UND_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM = (UND_DATA_MSG_VDM_HEADER_Type *)&vdm_data[4];
if (DATA_MSG_VDM->BITS.VDM_command_type == 1) {
msg_maxim("EnterMode ACK.");
if (DATA_MSG_VDM->BITS.Standard_Vendor_ID == SAMSUNG_VENDOR_ID) {
usbpd_data->is_samsung_accessory_enter_mode = 1;
msg_maxim("dex mode enter_mode ack status!");
}
} else {
msg_maxim("EnterMode NAK.");
}
return 0;
}
static int max77705_vdm_dp_select_pin(void *data, int multi)
{
struct max77705_usbc_platform_data *usbpd_data = data;
int pin_sel = 0;
if (multi) {
if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_D)
pin_sel = CCIC_NOTIFY_DP_PIN_D;
else if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_B)
pin_sel = CCIC_NOTIFY_DP_PIN_B;
else if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_F)
pin_sel = CCIC_NOTIFY_DP_PIN_F;
else if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_C)
pin_sel = CCIC_NOTIFY_DP_PIN_C;
else if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_E)
pin_sel = CCIC_NOTIFY_DP_PIN_E;
else if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_A)
pin_sel = CCIC_NOTIFY_DP_PIN_A;
else
msg_maxim("wrong pin assignment value");
} else {
if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_C)
pin_sel = CCIC_NOTIFY_DP_PIN_C;
else if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_E)
pin_sel = CCIC_NOTIFY_DP_PIN_E;
else if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_A)
pin_sel = CCIC_NOTIFY_DP_PIN_A;
else if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_D)
pin_sel = CCIC_NOTIFY_DP_PIN_D;
else if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_B)
pin_sel = CCIC_NOTIFY_DP_PIN_B;
else if (usbpd_data->pin_assignment & DP_PIN_ASSIGNMENT_F)
pin_sel = CCIC_NOTIFY_DP_PIN_F;
else
msg_maxim("wrong pin assignment value");
}
return pin_sel;
}
static int max77705_vdm_dp_status_update(void *data, char *vdm_data, int len)
{
struct max77705_usbc_platform_data *usbpd_data = data;
int i;
uint8_t multi_func = 0;
int pin_sel = 0;
int hpd = 0;
int hpdirq = 0;
VDO_MESSAGE_Type *VDO_MSG;
DP_STATUS_UPDATE_Type *DP_STATUS;
uint8_t W_DATA = 0x0;
if (usbpd_data->SVID_DP == TypeC_DP_SUPPORT) {
DP_STATUS = (DP_STATUS_UPDATE_Type *)&vdm_data[0];
msg_maxim("DP_STATUS_UPDATE = 0x%08X", DP_STATUS->DATA_DP_STATUS_UPDATE.DATA);
if (DP_STATUS->DATA_DP_STATUS_UPDATE.BITS.Port_Connected == 0x00) {
msg_maxim("port disconnected!");
} else {
if (usbpd_data->is_sent_pin_configuration == 0) {
multi_func = DP_STATUS->DATA_DP_STATUS_UPDATE.BITS.Multi_Function_Preference;
pin_sel = max77705_vdm_dp_select_pin(usbpd_data, multi_func);
usbpd_data->dp_selected_pin = pin_sel;
W_DATA = DP_Pin_Assignment_Data[pin_sel];
msg_maxim("multi_func_preference %d, %s, W_DATA : %d",
multi_func, DP_Pin_Assignment_Print[pin_sel], W_DATA);
max77705_vdm_process_set_DP_configure_mode_req(data, W_DATA);
usbpd_data->is_sent_pin_configuration = 1;
} else {
msg_maxim("pin configuration is already sent as %s!",
DP_Pin_Assignment_Print[usbpd_data->dp_selected_pin]);
}
}
if (DP_STATUS->DATA_DP_STATUS_UPDATE.BITS.HPD_State == 1)
hpd = CCIC_NOTIFY_HIGH;
else if (DP_STATUS->DATA_DP_STATUS_UPDATE.BITS.HPD_State == 0)
hpd = CCIC_NOTIFY_LOW;
if (DP_STATUS->DATA_DP_STATUS_UPDATE.BITS.HPD_Interrupt == 1)
hpdirq = CCIC_NOTIFY_IRQ;
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_DP, CCIC_NOTIFY_ID_DP_HPD,
hpd, hpdirq, 0);
} else {
/* need to check F/W code */
VDO_MSG = (VDO_MESSAGE_Type *)&vdm_data[8];
for (i = 0; i < 6; i++)
msg_maxim("VDO_%d : %d", i+1, VDO_MSG->VDO[i]);
}
return 0;
}
static int max77705_vdm_dp_attention(void *data, char *vdm_data, int len)
{
struct max77705_usbc_platform_data *usbpd_data = data;
int i;
int hpd = 0;
int hpdirq = 0;
uint8_t multi_func = 0;
int pin_sel = 0;
VDO_MESSAGE_Type *VDO_MSG;
DIS_ATTENTION_MESSAGE_DP_STATUS_Type *DP_ATTENTION;
uint8_t W_DATA = 0;
if (usbpd_data->SVID_DP == TypeC_DP_SUPPORT) {
DP_ATTENTION = (DIS_ATTENTION_MESSAGE_DP_STATUS_Type *)&vdm_data[0];
msg_maxim("%s DP_ATTENTION = 0x%08X\n", __func__,
DP_ATTENTION->DATA_MSG_DP_STATUS.DATA);
if (usbpd_data->is_sent_pin_configuration == 0) {
multi_func = DP_ATTENTION->DATA_MSG_DP_STATUS.BITS.Multi_Function_Preference;
pin_sel = max77705_vdm_dp_select_pin(usbpd_data, multi_func);
usbpd_data->dp_selected_pin = pin_sel;
W_DATA = DP_Pin_Assignment_Data[pin_sel];
msg_maxim("multi_func_preference %d, %s, W_DATA : %d\n",
multi_func, DP_Pin_Assignment_Print[pin_sel], W_DATA);
max77705_vdm_process_set_DP_configure_mode_req(data, W_DATA);
usbpd_data->is_sent_pin_configuration = 1;
} else {
msg_maxim("%s : pin configuration is already sent as %s!\n", __func__,
DP_Pin_Assignment_Print[usbpd_data->dp_selected_pin]);
}
if (DP_ATTENTION->DATA_MSG_DP_STATUS.BITS.HPD_State == 1)
hpd = CCIC_NOTIFY_HIGH;
else if (DP_ATTENTION->DATA_MSG_DP_STATUS.BITS.HPD_State == 0)
hpd = CCIC_NOTIFY_LOW;
if (DP_ATTENTION->DATA_MSG_DP_STATUS.BITS.HPD_Interrupt == 1)
hpdirq = CCIC_NOTIFY_IRQ;
max77705_ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_DP, CCIC_NOTIFY_ID_DP_HPD,
hpd, hpdirq, 0);
} else {
/* need to check the F/W code. */
VDO_MSG = (VDO_MESSAGE_Type *)&vdm_data[8];
for (i = 0; i < 6; i++)
msg_maxim("%s : VDO_%d : %d\n", __func__,
i+1, VDO_MSG->VDO[i]);
}
return 0;
}
static int max77705_vdm_dp_configure(void *data, char *vdm_data, int len)
{
struct max77705_usbc_platform_data *usbpd_data = data;
UND_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM = (UND_DATA_MSG_VDM_HEADER_Type *)&vdm_data[4];
int timeleft = 0;
msg_maxim("vendor_id = 0x%04x , svid_1 = 0x%04x", DATA_MSG_VDM->BITS.Standard_Vendor_ID, usbpd_data->SVID_1);
if (usbpd_data->SVID_DP == TypeC_DP_SUPPORT) {
timeleft = wait_event_interruptible_timeout(usbpd_data->device_add_wait_q,
usbpd_data->device_add, HZ/2);
msg_maxim("%s timeleft = %d\n", __func__, timeleft);
max77705_ccic_event_work(usbpd_data, CCIC_NOTIFY_DEV_DP,
CCIC_NOTIFY_ID_DP_LINK_CONF, usbpd_data->dp_selected_pin, 0, 0);
}
if (DATA_MSG_VDM->BITS.Standard_Vendor_ID == TypeC_DP_SUPPORT && usbpd_data->SVID_1 == TypeC_Dex_SUPPORT) {
/* Samsung Discover Modes packet */
usbpd_data->send_enter_mode_req = 0;
max77705_vdm_process_set_Dex_Disover_mode_req(usbpd_data);
}
return 0;
}
void max77705_vdm_message_handler(struct max77705_usbc_platform_data *usbpd_data,
char *opcode_data, int len)
{
unsigned char vdm_data[OPCODE_DATA_LENGTH] = {0,};
UND_DATA_MSG_ID_HEADER_Type *DATA_MSG_ID = NULL;
UND_PRODUCT_VDO_Type *DATA_MSG_PRODUCT = NULL;
UND_DATA_MSG_VDM_HEADER_Type vdm_header;
memset(&vdm_header, 0, sizeof(UND_DATA_MSG_VDM_HEADER_Type));
memcpy(vdm_data, opcode_data, len);
memcpy(&vdm_header, &vdm_data[4], sizeof(vdm_header));
if ((vdm_header.BITS.VDM_command_type) == SEC_UVDM_RESPONDER_NAK) {
msg_maxim("IGNORE THE NAK RESPONSE !!![%d]", vdm_data[1]);
return;
}
switch (vdm_data[1]) {
case OPCODE_ID_VDM_DISCOVER_IDENTITY:
max77705_vdm_process_printf("VDM_DISCOVER_IDENTITY", vdm_data, len);
/* Message Type Definition */
DATA_MSG_ID = (UND_DATA_MSG_ID_HEADER_Type *)&vdm_data[8];
DATA_MSG_PRODUCT = (UND_PRODUCT_VDO_Type *)&vdm_data[16];
usbpd_data->is_sent_pin_configuration = 0;
usbpd_data->is_samsung_accessory_enter_mode = 0;
usbpd_data->Vendor_ID = DATA_MSG_ID->BITS.USB_Vendor_ID;
usbpd_data->Product_ID = DATA_MSG_PRODUCT->BITS.Product_ID;
usbpd_data->Device_Version = DATA_MSG_PRODUCT->BITS.Device_Version;
msg_maxim("Vendor_ID : 0x%X, Product_ID : 0x%X Device Version 0x%X",
usbpd_data->Vendor_ID, usbpd_data->Product_ID, usbpd_data->Device_Version);
if (max77705_process_check_accessory(usbpd_data))
msg_maxim("Samsung Accessory Connected.");
break;
case OPCODE_ID_VDM_DISCOVER_SVIDS:
max77705_vdm_process_printf("VDM_DISCOVER_SVIDS", vdm_data, len);
max77705_vdm_process_discover_svids(usbpd_data, vdm_data, len);
break;
case OPCODE_ID_VDM_DISCOVER_MODES:
max77705_vdm_process_printf("VDM_DISCOVER_MODES", vdm_data, len);
vdm_data[0] = vdm_data[2];
vdm_data[1] = vdm_data[3];
max77705_vdm_process_discover_mode(usbpd_data, vdm_data, len);
break;
case OPCODE_ID_VDM_ENTER_MODE:
max77705_vdm_process_printf("VDM_ENTER_MODE", vdm_data, len);
max77705_vdm_process_enter_mode(usbpd_data, vdm_data, len);
break;
case OPCODE_ID_VDM_SVID_DP_STATUS:
max77705_vdm_process_printf("VDM_SVID_DP_STATUS", vdm_data, len);
vdm_data[0] = vdm_data[2];
vdm_data[1] = vdm_data[3];
max77705_vdm_dp_status_update(usbpd_data, vdm_data, len);
break;
case OPCODE_ID_VDM_SVID_DP_CONFIGURE:
max77705_vdm_process_printf("VDM_SVID_DP_CONFIGURE", vdm_data, len);
max77705_vdm_dp_configure(usbpd_data, vdm_data, len);
break;
case OPCODE_ID_VDM_ATTENTION:
max77705_vdm_process_printf("VDM_ATTENTION", vdm_data, len);
vdm_data[0] = vdm_data[2];
vdm_data[1] = vdm_data[3];
max77705_vdm_dp_attention(usbpd_data, vdm_data, len);
break;
case OPCODE_ID_VDM_EXIT_MODE:
break;
default:
break;
}
}
static int max77705_process_discover_identity(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
/* send the opcode */
usbc_cmd_data write_data;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_GET_VDM_RESP;
write_data.write_data[0] = OPCODE_ID_VDM_DISCOVER_IDENTITY;
write_data.write_length = 0x1;
write_data.read_length = 31;
max77705_usbc_opcode_write(usbpd_data, &write_data);
return 0;
}
static int max77705_process_discover_svids(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
/* send the opcode */
usbc_cmd_data write_data;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_GET_VDM_RESP;
write_data.write_data[0] = OPCODE_ID_VDM_DISCOVER_SVIDS;
write_data.write_length = 0x1;
write_data.read_length = 31;
max77705_usbc_opcode_write(usbpd_data, &write_data);
return 0;
}
static int max77705_process_discover_modes(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
/* send the opcode */
usbc_cmd_data write_data;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_GET_VDM_RESP;
write_data.write_data[0] = OPCODE_ID_VDM_DISCOVER_MODES;
write_data.write_length = 0x1;
write_data.read_length = 11;
max77705_usbc_opcode_write(usbpd_data, &write_data);
return 0;
}
static int max77705_process_enter_mode(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
/* send the opcode */
usbc_cmd_data write_data;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_GET_VDM_RESP;
write_data.write_data[0] = OPCODE_ID_VDM_ENTER_MODE;
write_data.write_length = 0x1;
write_data.read_length = 7;
max77705_usbc_opcode_write(usbpd_data, &write_data);
return 0;
}
static int max77705_process_attention(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
/* send the opcode */
usbc_cmd_data write_data;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_GET_VDM_RESP;
write_data.write_data[0] = OPCODE_ID_VDM_ATTENTION;
write_data.write_length = 0x1;
write_data.read_length = 11;
max77705_usbc_opcode_write(usbpd_data, &write_data);
return 0;
}
static int max77705_process_dp_status_update(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
/* send the opcode */
usbc_cmd_data write_data;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_GET_VDM_RESP;
write_data.write_data[0] = OPCODE_ID_VDM_SVID_DP_STATUS;
write_data.write_length = 0x1;
write_data.read_length = 11;
max77705_usbc_opcode_write(usbpd_data, &write_data);
return 0;
}
static int max77705_process_dp_configure(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
/* send the opcode */
usbc_cmd_data write_data;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_GET_VDM_RESP;
write_data.write_data[0] = OPCODE_ID_VDM_SVID_DP_CONFIGURE;
write_data.write_length = 0x1;
write_data.read_length = 11;
max77705_usbc_opcode_write(usbpd_data, &write_data);
return 0;
}
static void max77705_process_alternate_mode(void *data)
{
struct max77705_usbc_platform_data *usbpd_data = data;
uint32_t mode = usbpd_data->alternate_state;
int ret = 0;
#if defined(CONFIG_USB_HOST_NOTIFY)
struct otg_notify *o_notify = get_otg_notify();
#endif
if (mode) {
msg_maxim("mode : 0x%x", mode);
#if defined(CONFIG_USB_HOST_NOTIFY)
if (is_blocked(o_notify, NOTIFY_BLOCK_TYPE_HOST)) {
msg_maxim("host is blocked, skip all the alternate mode.");
goto process_error;
}
#endif
if (usbpd_data->mdm_block) {
msg_maxim("is blocked by mdm, skip all the alternate mode.");
goto process_error;
}
if (mode & VDM_DISCOVER_ID)
ret = max77705_process_discover_identity(usbpd_data);
if (ret)
goto process_error;
if (mode & VDM_DISCOVER_SVIDS)
ret = max77705_process_discover_svids(usbpd_data);
if (ret)
goto process_error;
if (mode & VDM_DISCOVER_MODES)
ret = max77705_process_discover_modes(usbpd_data);
if (ret)
goto process_error;
if (mode & VDM_ENTER_MODE)
ret = max77705_process_enter_mode(usbpd_data);
if (ret)
goto process_error;
if (mode & VDM_DP_STATUS_UPDATE)
ret = max77705_process_dp_status_update(usbpd_data);
if (ret)
goto process_error;
if (mode & VDM_DP_CONFIGURE)
ret = max77705_process_dp_configure(usbpd_data);
if (ret)
goto process_error;
if (mode & VDM_ATTENTION)
ret = max77705_process_attention(usbpd_data);
process_error:
usbpd_data->alternate_state = 0;
}
}
void max77705_receive_alternate_message(struct max77705_usbc_platform_data *data, MAX77705_VDM_MSG_IRQ_STATUS_Type *VDM_MSG_IRQ_State)
{
struct max77705_usbc_platform_data *usbpd_data = data;
static int last_alternate = 0;
DISCOVER_ID:
if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Discover_ID) {
msg_maxim(": %s", &VDM_MSG_IRQ_State_Print[1][0]);
usbpd_data->alternate_state |= VDM_DISCOVER_ID;
last_alternate = VDM_DISCOVER_ID;
}
DISCOVER_SVIDS:
if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Discover_SVIDs) {
msg_maxim(": %s", &VDM_MSG_IRQ_State_Print[2][0]);
if (last_alternate != VDM_DISCOVER_ID) {
msg_maxim("%s vdm miss\n", __func__);
VDM_MSG_IRQ_State->BITS.Vdm_Flag_Discover_ID = 1;
goto DISCOVER_ID;
}
usbpd_data->alternate_state |= VDM_DISCOVER_SVIDS;
last_alternate = VDM_DISCOVER_SVIDS;
}
DISCOVER_MODES:
if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Discover_MODEs) {
msg_maxim(": %s", &VDM_MSG_IRQ_State_Print[3][0]);
if (last_alternate != VDM_DISCOVER_SVIDS &&
last_alternate != VDM_DP_CONFIGURE) {
msg_maxim("%s vdm miss\n", __func__);
VDM_MSG_IRQ_State->BITS.Vdm_Flag_Discover_SVIDs = 1;
goto DISCOVER_SVIDS;
}
usbpd_data->alternate_state |= VDM_DISCOVER_MODES;
last_alternate = VDM_DISCOVER_MODES;
}
if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Enter_Mode) {
msg_maxim(": %s", &VDM_MSG_IRQ_State_Print[4][0]);
if (last_alternate != VDM_DISCOVER_MODES) {
msg_maxim("%s vdm miss\n", __func__);
VDM_MSG_IRQ_State->BITS.Vdm_Flag_Discover_MODEs = 1;
goto DISCOVER_MODES;
}
usbpd_data->alternate_state |= VDM_ENTER_MODE;
last_alternate = VDM_ENTER_MODE;
}
if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Exit_Mode) {
msg_maxim(": %s", &VDM_MSG_IRQ_State_Print[5][0]);
usbpd_data->alternate_state |= VDM_EXIT_MODE;
}
if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_Attention) {
msg_maxim(": %s", &VDM_MSG_IRQ_State_Print[6][0]);
usbpd_data->alternate_state |= VDM_ATTENTION;
}
if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_DP_Status_Update) {
msg_maxim(": %s", &VDM_MSG_IRQ_State_Print[7][0]);
usbpd_data->alternate_state |= VDM_DP_STATUS_UPDATE;
last_alternate = VDM_DP_STATUS_UPDATE;
}
if (VDM_MSG_IRQ_State->BITS.Vdm_Flag_DP_Configure) {
msg_maxim(": %s", &VDM_MSG_IRQ_State_Print[8][0]);
usbpd_data->alternate_state |= VDM_DP_CONFIGURE;
last_alternate = VDM_DP_CONFIGURE;
}
max77705_process_alternate_mode(usbpd_data);
}
void max77705_send_dex_fan_unstructured_vdm_message(void *data, int cmd)
{
struct max77705_usbc_platform_data *usbpd_data = data;
uint8_t SendMSG[32] = {0,};
uint32_t message = (uint32_t)cmd;
usbc_cmd_data write_data;
int len = sizeof(SS_DEX_UNSTRUCTURED_VDM);
VDO_MESSAGE_Type *VDO_MSG = (VDO_MESSAGE_Type *)&SendMSG[5];
SS_DEX_UNSTRUCTURED_VDM.byte_data.BITS.Num_Of_VDO = 2;
SS_DEX_UNSTRUCTURED_VDM.byte_data.BITS.Cmd_Type = ACK;
SS_DEX_UNSTRUCTURED_VDM.byte_data.BITS.Reserved = 0;
SS_DEX_UNSTRUCTURED_VDM.VDO_MSG.VDO[0] = 0x04E80000;
SS_DEX_UNSTRUCTURED_VDM.VDO_MSG.VDO[1] = 0xA0200110;
pr_info("%s: cmd : 0x%x\n", __func__, cmd);
memcpy(SendMSG, &SS_DEX_UNSTRUCTURED_VDM, len);
VDO_MSG->VDO[0] = message;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_SET_VDM_REQ;
memcpy(write_data.write_data, SendMSG, sizeof(SendMSG));
write_data.write_length = len;
write_data.read_length = 31;
max77705_usbc_opcode_write(usbpd_data, &write_data);
msg_maxim("message : 0x%x", message);
}
void max77705_acc_detach_check(struct work_struct *wk)
{
struct delayed_work *delay_work =
container_of(wk, struct delayed_work, work);
struct max77705_usbc_platform_data *usbpd_data =
container_of(delay_work, struct max77705_usbc_platform_data, acc_detach_work);
pr_info("%s : pd_state : %d, acc_type : %d\n", __func__,
usbpd_data->pd_state, usbpd_data->acc_type);
if (usbpd_data->pd_state == max77705_State_PE_Initial_detach) {
if (usbpd_data->acc_type != CCIC_DOCK_DETACHED) {
if (usbpd_data->acc_type != CCIC_DOCK_NEW)
ccic_send_dock_intent(CCIC_DOCK_DETACHED);
ccic_send_dock_uevent(usbpd_data->Vendor_ID,
usbpd_data->Product_ID,
CCIC_DOCK_DETACHED);
usbpd_data->acc_type = CCIC_DOCK_DETACHED;
usbpd_data->Vendor_ID = 0;
usbpd_data->Product_ID = 0;
usbpd_data->send_enter_mode_req = 0;
}
}
}
void max77705_vdm_process_set_samsung_alternate_mode(void *data, int mode)
{
struct max77705_usbc_platform_data *usbpd_data = data;
usbc_cmd_data write_data;
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_SET_ALTERNATEMODE;
write_data.write_data[0] = mode;
write_data.write_length = 0x1;
write_data.read_length = 0x1;
max77705_usbc_opcode_write(usbpd_data, &write_data);
}
void max77705_set_enable_alternate_mode(int mode)
{
struct max77705_usbc_platform_data *usbpd_data = NULL;
static int check_is_driver_loaded;
static int prev_alternate_mode;
int is_first_booting = 0;
struct max77705_pd_data *pd_data = NULL;
u8 status[11] = {0, };
usbpd_data = g_usbc_data;
if (!usbpd_data)
return;
is_first_booting = usbpd_data->is_first_booting;
pd_data = usbpd_data->pd_data;
msg_maxim("is_first_booting : %x mode %x",
usbpd_data->is_first_booting, mode);
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
store_usblog_notify(NOTIFY_ALTERNATEMODE, (void *)&mode, NULL);
#endif
usbpd_data->set_altmode = mode;
if ((mode & ALTERNATE_MODE_NOT_READY) &&
(mode & ALTERNATE_MODE_READY)) {
msg_maxim("mode is invalid!");
return;
}
if ((mode & ALTERNATE_MODE_START) && (mode & ALTERNATE_MODE_STOP)) {
msg_maxim("mode is invalid!");
return;
}
if (mode & ALTERNATE_MODE_RESET) {
msg_maxim("mode is reset! check_is_driver_loaded=%d, prev_alternate_mode=%d",
check_is_driver_loaded, prev_alternate_mode);
if (check_is_driver_loaded &&
(prev_alternate_mode == ALTERNATE_MODE_START)) {
msg_maxim("[No process] alternate mode is reset as start!");
prev_alternate_mode = ALTERNATE_MODE_START;
} else if (check_is_driver_loaded &&
(prev_alternate_mode == ALTERNATE_MODE_STOP)) {
msg_maxim("[No process] alternate mode is reset as stop!");
prev_alternate_mode = ALTERNATE_MODE_STOP;
} else {
;
}
} else {
if (mode & ALTERNATE_MODE_NOT_READY) {
check_is_driver_loaded = 0;
msg_maxim("alternate mode is not ready!");
} else if (mode & ALTERNATE_MODE_READY) {
check_is_driver_loaded = 1;
msg_maxim("alternate mode is ready!");
} else {
;
}
if (check_is_driver_loaded) {
switch (is_first_booting) {
case 0: /*this routine is calling after complete a booting.*/
if (mode & ALTERNATE_MODE_START) {
max77705_vdm_process_set_samsung_alternate_mode(usbpd_data,
MAXIM_ENABLE_ALTERNATE_SRC_VDM);
msg_maxim("[NO BOOTING TIME] !!!alternate mode is started!");
if (usbpd_data->cc_data->current_pr == SNK && (pd_data->current_dr == DFP)) {
max77705_vdm_process_set_identity_req(usbpd_data);
msg_maxim("[NO BOOTING TIME] SEND THE PACKET (DEX HUB) ");
}
} else if (mode & ALTERNATE_MODE_STOP) {
max77705_vdm_process_set_samsung_alternate_mode(usbpd_data,
MAXIM_ENABLE_ALTERNATE_SRCCAP);
msg_maxim("[NO BOOTING TIME] alternate mode is stopped!");
}
break;
case 1:
if (mode & ALTERNATE_MODE_START) {
msg_maxim("[ON BOOTING TIME] !!!alternate mode is started!");
prev_alternate_mode = ALTERNATE_MODE_START;
max77705_vdm_process_set_samsung_alternate_mode(usbpd_data,
MAXIM_ENABLE_ALTERNATE_SRC_VDM);
msg_maxim("!![ON BOOTING TIME] SEND THE PACKET REGARDING IN CASE OF VR/DP ");
/* FOR THE DEX FUNCTION. */
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_USBC_STATUS1, &status[0]);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_USBC_STATUS2, &status[1]);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_BC_STATUS, &status[2]);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_CC_STATUS0, &status[3]);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_CC_STATUS1, &status[4]);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_PD_STATUS0, &status[5]);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_PD_STATUS1, &status[6]);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_UIC_INT_M, &status[7]);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_CC_INT_M, &status[8]);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_PD_INT_M, &status[9]);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_VDM_INT_M, &status[10]);
msg_maxim("USBC1:0x%02x, USBC2:0x%02x, BC:0x%02x",
status[0], status[1], status[2]);
msg_maxim("CC_STATUS0:0x%x, CC_STATUS1:0x%x, PD_STATUS0:0x%x, PD_STATUS1:0x%x",
status[3], status[4], status[5], status[6]);
msg_maxim("UIC_INT_M:0x%x, CC_INT_M:0x%x, PD_INT_M:0x%x, VDM_INT_M:0x%x",
status[7], status[8], status[9], status[10]);
if (usbpd_data->cc_data->current_pr == SNK && (pd_data->current_dr == DFP)
&& usbpd_data->is_first_booting) {
max77705_vdm_process_set_identity_req(usbpd_data);
msg_maxim("[ON BOOTING TIME] SEND THE PACKET (DEX HUB) ");
}
max77705_write_reg(usbpd_data->muic, REG_VDM_INT_M, 0xF0);
max77705_write_reg(usbpd_data->muic, REG_PD_INT_M, 0x0);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_PD_INT_M, &status[9]);
max77705_read_reg(usbpd_data->muic, MAX77705_USBC_REG_VDM_INT_M, &status[10]);
msg_maxim("UIC_INT_M:0x%x, CC_INT_M:0x%x, PD_INT_M:0x%x, VDM_INT_M:0x%x",
status[7], status[8], status[9], status[10]);
usbpd_data->is_first_booting = 0;
} else if (mode & ALTERNATE_MODE_STOP) {
msg_maxim("[ON BOOTING TIME] alternate mode is stopped!");
}
break;
default:
msg_maxim("Never calling");
msg_maxim("[Never calling] is_first_booting [ %d]", is_first_booting);
break;
}
}
}
}
void max77705_set_host_turn_on_event(int mode)
{
struct max77705_usbc_platform_data *usbpd_data = NULL;
usbpd_data = g_usbc_data;
if (!usbpd_data)
return;
pr_info("%s : current_set is %d!\n", __func__, mode);
if (mode) {
usbpd_data->device_add = 0;
usbpd_data->detach_done_wait = 0;
usbpd_data->host_turn_on_event = 1;
wake_up_interruptible(&usbpd_data->host_turn_on_wait_q);
} else {
usbpd_data->device_add = 0;
usbpd_data->detach_done_wait = 0;
usbpd_data->host_turn_on_event = 0;
}
}
static void set_endian(char *src, char *dest, int size)
{
int i, j;
int loop;
int dest_pos;
int src_pos;
loop = size / SAMSUNGUVDM_ALIGN;
loop += (((size % SAMSUNGUVDM_ALIGN) > 0) ? 1:0);
for (i = 0 ; i < loop ; i++)
for (j = 0 ; j < SAMSUNGUVDM_ALIGN ; j++) {
src_pos = SAMSUNGUVDM_ALIGN * i + j;
dest_pos = SAMSUNGUVDM_ALIGN * i + SAMSUNGUVDM_ALIGN - j - 1;
dest[dest_pos] = src[src_pos];
}
}
static int get_checksum(char *data, int start_addr, int size)
{
int checksum = 0;
int i;
// printk("[MAX77705] %s", __func__);
for (i = 0; i < size; i++) {
checksum += data[start_addr+i];
//printk(" %x", (uint32_t)data[start_addr+i]);
}
// printk("\n");
return checksum;
}
static int set_uvdmset_count(int size)
{
int ret = 0;
if (size <= SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET)
ret = 1;
else {
ret = ((size-SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET) / SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET);
if (((size-SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET) % SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET) == 0)
ret += 1;
else
ret += 2;
}
return ret;
}
static int get_datasize_of_currentset(int first_set, int remained_data_size)
{
int ret = 0;
if (first_set)
ret = (remained_data_size <= SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET) ?
remained_data_size : SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET;
else
ret = (remained_data_size <= SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET) ?
remained_data_size : SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET;
return ret;
}
static void set_sec_uvdm_txdataheader(void *data, int first_set, int cur_uvdmset,
int total_data_size, int remained_data_size)
{
U_SEC_TX_DATA_HEADER *SEC_TX_DATA_HEADER;
uint8_t *SendMSG = (uint8_t *)data;
if (first_set)
SEC_TX_DATA_HEADER = (U_SEC_TX_DATA_HEADER *)&SendMSG[12];
else
SEC_TX_DATA_HEADER = (U_SEC_TX_DATA_HEADER *)&SendMSG[8];
SEC_TX_DATA_HEADER->BITS.data_size_of_current_set =
get_datasize_of_currentset(first_set, remained_data_size);
SEC_TX_DATA_HEADER->BITS.total_data_size = total_data_size;
SEC_TX_DATA_HEADER->BITS.order_of_current_uvdm_set = cur_uvdmset;
SEC_TX_DATA_HEADER->BITS.reserved = 0;
}
static void set_msgheader(void *data, int msg_type, int obj_num)
{
/* Common : Fill the VDM OpCode MSGHeader */
SEND_VDM_BYTE_DATA *MSG_HDR;
uint8_t *SendMSG = (uint8_t *)data;
MSG_HDR = (SEND_VDM_BYTE_DATA *)&SendMSG[0];
MSG_HDR->BITS.Num_Of_VDO = obj_num;
if (msg_type == NAK)
MSG_HDR->BITS.Reserved = 7;
MSG_HDR->BITS.Cmd_Type = msg_type;
}
static void set_uvdmheader(void *data, int vendor_id, int vdm_type)
{
/* Common : Fill the UVDMHeader */
UND_UNSTRUCTURED_VDM_HEADER_Type *UVDM_HEADER;
U_DATA_MSG_VDM_HEADER_Type *VDM_HEADER;
uint8_t *SendMSG = (uint8_t *)data;
UVDM_HEADER = (UND_UNSTRUCTURED_VDM_HEADER_Type *)&SendMSG[4];
UVDM_HEADER->BITS.USB_Vendor_ID = vendor_id;
UVDM_HEADER->BITS.VDM_TYPE = vdm_type;
UVDM_HEADER->BITS.VENDOR_DEFINED_MESSAGE = SEC_UVDM_UNSTRUCTURED_VDM;
VDM_HEADER = (U_DATA_MSG_VDM_HEADER_Type *)&SendMSG[4];
VDM_HEADER->BITS.VDM_command = 4; //from s2mm005 concept
}
static void set_sec_uvdmheader(void *data, int pid, bool data_type, int cmd_type,
bool dir, int total_uvdmset_num, uint8_t received_data)
{
U_SEC_UVDM_HEADER *SEC_VDM_HEADER;
uint8_t *SendMSG = (uint8_t *)data;
SEC_VDM_HEADER = (U_SEC_UVDM_HEADER *)&SendMSG[8];
SEC_VDM_HEADER->BITS.pid = pid;
SEC_VDM_HEADER->BITS.data_type = data_type;
SEC_VDM_HEADER->BITS.command_type = cmd_type;
SEC_VDM_HEADER->BITS.direction = dir;
if (dir == DIR_OUT)
SEC_VDM_HEADER->BITS.total_number_of_uvdm_set = total_uvdmset_num;
if (data_type == TYPE_SHORT)
SEC_VDM_HEADER->BITS.data = received_data;
else
SEC_VDM_HEADER->BITS.data = 0;
#if 0
msg_maxim("pid = 0x%x data_type=%d ,cmd_type =%d,direction= %d, total_num_of_uvdm_set = %d",
SEC_VDM_HEADER->BITS.pid,
SEC_VDM_HEADER->BITS.data_type,
SEC_VDM_HEADER->BITS.command_type,
SEC_VDM_HEADER->BITS.direction,
SEC_VDM_HEADER->BITS.total_number_of_uvdm_set);
#endif
}
static void set_sec_uvdm_txdata_tailer(void *data)
{
U_SEC_TX_DATA_TAILER *SEC_TX_DATA_TAILER;
uint8_t *SendMSG = (uint8_t *)data;
SEC_TX_DATA_TAILER = (U_SEC_TX_DATA_TAILER *)&SendMSG[28];
SEC_TX_DATA_TAILER->BITS.checksum = get_checksum(SendMSG, 8, SAMSUNGUVDM_CHECKSUM_DATA_COUNT);
SEC_TX_DATA_TAILER->BITS.reserved = 0;
}
static void set_sec_uvdm_rxdata_header(void *data, int cur_uvdmset_num, int cur_uvdmset_data, int ack)
{
U_SEC_RX_DATA_HEADER *SEC_UVDM_RX_HEADER;
uint8_t *SendMSG = (uint8_t *)data;
SEC_UVDM_RX_HEADER = (U_SEC_RX_DATA_HEADER *)&SendMSG[8];
SEC_UVDM_RX_HEADER->BITS.order_of_current_uvdm_set = cur_uvdmset_num;
SEC_UVDM_RX_HEADER->BITS.received_data_size_of_current_set = cur_uvdmset_data;
SEC_UVDM_RX_HEADER->BITS.result_value = ack;
SEC_UVDM_RX_HEADER->BITS.reserved = 0;
}
static int check_is_wait_ack_accessroy(int vid, int pid, int svid)
{
int should_wait = true;
if (vid == SAMSUNG_VENDOR_ID && pid == DEXDOCK_PRODUCT_ID && svid == TypeC_DP_SUPPORT) {
msg_maxim("no need to wait ack response!");
should_wait = false;
}
return should_wait;
}
static int max77705_send_vdm_write_message(void *data)
{
struct SS_UNSTRUCTURED_VDM_MSG vdm_opcode_msg;
struct max77705_usbc_platform_data *usbpd_data = g_usbc_data;
uint8_t *SendMSG = (uint8_t *)data;
usbc_cmd_data write_data;
int len = sizeof(struct SS_UNSTRUCTURED_VDM_MSG);
memset(&vdm_opcode_msg, 0, len);
/* Common : MSGHeader */
memcpy(&vdm_opcode_msg.byte_data, &SendMSG[0], 1);
/* 8. Copy the data from SendMSG buffer */
memcpy(&vdm_opcode_msg.VDO_MSG.VDO[0], &SendMSG[4], 28);
/* 9. Write SendMSG buffer via I2C */
init_usbc_cmd_data(&write_data);
write_data.opcode = OPCODE_VDM_DISCOVER_SET_VDM_REQ;
write_data.is_uvdm = 1;
memcpy(write_data.write_data, &vdm_opcode_msg, sizeof(vdm_opcode_msg));
write_data.write_length = len;
write_data.read_length = len;
max77705_usbc_opcode_write(usbpd_data, &write_data);
msg_maxim("opcode is sent");
return 0;
}
static int max77705_send_sec_unstructured_short_vdm_message(void *data, void *buf, size_t size)
{
struct max77705_usbc_platform_data *usbpd_data = data;
uint8_t SendMSG[32] = {0,};
/* Message Type Definition */
uint8_t received_data = 0;
int time_left;
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
int event;
#endif
if ((buf == NULL) || size <= 0) {
msg_maxim("given data is not valid !");
return -EINVAL;
}
if (!usbpd_data->is_samsung_accessory_enter_mode) {
msg_maxim("samsung_accessory mode is not ready!");
return -ENXIO;
}
/* 1. Calc the receivced data size and determin the uvdm set count and last data of uvdm set. */
received_data = *(char *)buf;
/* 2. Common : Fill the MSGHeader */
set_msgheader(SendMSG, ACK, 2);
/* 3. Common : Fill the UVDMHeader*/
set_uvdmheader(SendMSG, SAMSUNG_VENDOR_ID, 0);
/* 4. Common : Fill the First SEC_VDMHeader*/
set_sec_uvdmheader(SendMSG, usbpd_data->Product_ID, TYPE_SHORT, SEC_UVDM_ININIATOR, DIR_OUT, 1, received_data);
usbpd_data->is_in_first_sec_uvdm_req = true;
usbpd_data->uvdm_error = 0;
max77705_send_vdm_write_message(SendMSG);
if (check_is_wait_ack_accessroy(usbpd_data->Vendor_ID, usbpd_data->Product_ID, usbpd_data->SVID_DP)) {
reinit_completion(&usbpd_data->uvdm_longpacket_out_wait);
/* Wait Response*/
time_left =
wait_for_completion_interruptible_timeout(&usbpd_data->uvdm_longpacket_out_wait,
msecs_to_jiffies(SAMSUNGUVDM_WAIT_MS));
if (time_left <= 0) {
usbpd_data->is_in_first_sec_uvdm_req = false;
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
event = NOTIFY_EXTRA_UVDM_TIMEOUT;
store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL);
#endif
return -ETIME;
}
if (usbpd_data->uvdm_error) {
usbpd_data->is_in_first_sec_uvdm_req = false;
return usbpd_data->uvdm_error;
}
}
msg_maxim("exit : short data transfer complete!");
usbpd_data->is_in_first_sec_uvdm_req = false;
return size;
}
static int max77705_send_sec_unstructured_long_vdm_message(void *data, void *buf, size_t size)
{
struct max77705_usbc_platform_data *usbpd_data;
uint8_t SendMSG[32] = {0,};
uint8_t *SEC_DATA;
int need_uvdmset_count = 0;
int cur_uvdmset_data = 0;
int cur_uvdmset_num = 0;
int accumulated_data_size = 0;
int remained_data_size = 0;
uint8_t received_data[MAX_INPUT_DATA] = {0,};
int time_left;
int i;
int received_data_index;
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
int event;
#endif
usbpd_data = data;
if (!usbpd_data)
return -ENXIO;
if (!buf) {
msg_maxim("given data is not valid !");
return -EINVAL;
}
/* 1. Calc the receivced data size and determin the uvdm set count and last data of uvdm set. */
set_endian(buf, received_data, size);
need_uvdmset_count = set_uvdmset_count(size);
msg_maxim("need_uvdmset_count = %d", need_uvdmset_count);
usbpd_data->is_in_first_sec_uvdm_req = true;
usbpd_data->is_in_sec_uvdm_out = DIR_OUT;
cur_uvdmset_num = 1;
accumulated_data_size = 0;
remained_data_size = size;
received_data_index = 0;
/* 2. Common : Fill the MSGHeader */
set_msgheader(SendMSG, ACK, 7);
/* 3. Common : Fill the UVDMHeader*/
set_uvdmheader(SendMSG, SAMSUNG_VENDOR_ID, 0);
/* 4. Common : Fill the First SEC_VDMHeader*/
if (usbpd_data->is_in_first_sec_uvdm_req)
set_sec_uvdmheader(SendMSG, usbpd_data->Product_ID,
TYPE_LONG, SEC_UVDM_ININIATOR, DIR_OUT, need_uvdmset_count, 0);
while (cur_uvdmset_num <= need_uvdmset_count) {
cur_uvdmset_data = 0;
time_left = 0;
set_sec_uvdm_txdataheader(SendMSG, usbpd_data->is_in_first_sec_uvdm_req,
cur_uvdmset_num, size, remained_data_size);
cur_uvdmset_data = get_datasize_of_currentset(usbpd_data->is_in_first_sec_uvdm_req, remained_data_size);
msg_maxim("data_size_of_current_set = %d ,total_data_size = %ld, order_of_current_uvdm_set = %d",
cur_uvdmset_data, size, cur_uvdmset_num);
/* 6. Common : Fill the DATA */
if (usbpd_data->is_in_first_sec_uvdm_req) {
SEC_DATA = (uint8_t *)&SendMSG[8+8];
for (i = 0; i < SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET; i++)
SEC_DATA[i] = received_data[received_data_index++];
} else {
SEC_DATA = (uint8_t *)&SendMSG[8+4];
for (i = 0; i < SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET; i++)
SEC_DATA[i] = received_data[received_data_index++];
}
/* 7. Common : Fill the TX_DATA_Tailer */
set_sec_uvdm_txdata_tailer(SendMSG);
/* 8. Send data to PDIC */
usbpd_data->uvdm_error = 0;
max77705_send_vdm_write_message(SendMSG);
/* 9. Wait Response*/
reinit_completion(&usbpd_data->uvdm_longpacket_out_wait);
time_left =
wait_for_completion_interruptible_timeout(&usbpd_data->uvdm_longpacket_out_wait,
msecs_to_jiffies(SAMSUNGUVDM_WAIT_MS));
if (time_left <= 0) {
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
event = NOTIFY_EXTRA_UVDM_TIMEOUT;
store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL);
#endif
return -ETIME;
}
if (usbpd_data->uvdm_error)
return usbpd_data->uvdm_error;
accumulated_data_size += cur_uvdmset_data;
remained_data_size -= cur_uvdmset_data;
if (usbpd_data->is_in_first_sec_uvdm_req)
usbpd_data->is_in_first_sec_uvdm_req = false;
cur_uvdmset_num++;
}
return size;
}
void max77705_sec_unstructured_message_handler(struct max77705_usbc_platform_data *usbpd_data,
char *opcode_data, int len)
{
int unstructured_len = sizeof(struct SS_UNSTRUCTURED_VDM_MSG);
uint8_t ReadMSG[32] = {0,};
U_SEC_UVDM_RESPONSE_HEADER *SEC_UVDM_RESPONSE_HEADER;
U_SEC_RX_DATA_HEADER *SEC_UVDM_RX_HEADER;
U_SEC_TX_DATA_HEADER *SEC_UVDM_TX_HEADER;
if (len != unstructured_len + OPCODE_SIZE) {
msg_maxim("This isn't UVDM message!");
return;
}
memcpy(ReadMSG, opcode_data, OPCODE_DATA_LENGTH);
usbpd_data->uvdm_error = 0;
switch (usbpd_data->is_in_sec_uvdm_out) {
case DIR_OUT:
if (usbpd_data->is_in_first_sec_uvdm_req) {
SEC_UVDM_RESPONSE_HEADER = (U_SEC_UVDM_RESPONSE_HEADER *)&ReadMSG[6];
#if (UVDM_DEBUG)
msg_maxim("DIR_OUT SEC_UVDM_RESPONSE_HEADER : 0x%x", SEC_UVDM_RESPONSE_HEADER->data);
#endif
if (SEC_UVDM_RESPONSE_HEADER->BITS.data_type == TYPE_LONG) {
if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type == SEC_UVDM_RESPONDER_ACK) {
SEC_UVDM_RX_HEADER = (U_SEC_RX_DATA_HEADER *)&ReadMSG[10];
#if (UVDM_DEBUG)
msg_maxim("DIR_OUT SEC_UVDM_RX_HEADER : 0x%x", SEC_UVDM_RX_HEADER->data);
#endif
if (SEC_UVDM_RX_HEADER->BITS.result_value == RX_ACK) {
/* do nothing */
} else if (SEC_UVDM_RX_HEADER->BITS.result_value == RX_NAK) {
msg_maxim("DIR_OUT RX_NAK received");
usbpd_data->uvdm_error = -ENODATA;
} else if (SEC_UVDM_RX_HEADER->BITS.result_value == RX_BUSY) {
msg_maxim("DIR_OUT RX_BUSY received");
usbpd_data->uvdm_error = -EBUSY;
} else {
msg_maxim("DIR_OUT Undefined RX value");
usbpd_data->uvdm_error = -EPROTO;
}
} else if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type == SEC_UVDM_RESPONDER_NAK) {
msg_maxim("DIR_OUT SEC_UVDM_RESPONDER_NAK received");
usbpd_data->uvdm_error = -ENODATA;
} else if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type == SEC_UVDM_RESPONDER_BUSY) {
msg_maxim("DIR_OUT SEC_UVDM_RESPONDER_BUSY received");
usbpd_data->uvdm_error = -EBUSY;
} else {
msg_maxim("DIR_OUT Undefined RESPONDER value");
usbpd_data->uvdm_error = -EPROTO;
}
} else { /* TYPE_SHORT */
if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type == SEC_UVDM_RESPONDER_ACK) {
/* do nothing */
} else if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type == SEC_UVDM_RESPONDER_NAK) {
msg_maxim("DIR_OUT SHORT SEC_UVDM_RESPONDER_NAK received");
usbpd_data->uvdm_error = -ENODATA;
} else if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type == SEC_UVDM_RESPONDER_BUSY) {
msg_maxim("DIR_OUT SHORT SEC_UVDM_RESPONDER_BUSY received");
usbpd_data->uvdm_error = -EBUSY;
} else {
msg_maxim("DIR_OUT Undefined RESPONDER value");
usbpd_data->uvdm_error = -EPROTO;
}
}
} else { /* after 2nd packet for TYPE_LONG */
SEC_UVDM_RX_HEADER = (U_SEC_RX_DATA_HEADER *)&ReadMSG[6];
if (SEC_UVDM_RX_HEADER->BITS.result_value == RX_ACK) {
/* do nothing */
} else if (SEC_UVDM_RX_HEADER->BITS.result_value == RX_NAK) {
msg_maxim("DIR_OUT RX_NAK received");
usbpd_data->uvdm_error = -ENODATA;
} else if (SEC_UVDM_RX_HEADER->BITS.result_value == RX_BUSY) {
msg_maxim("DIR_OUT RX_BUSY received");
usbpd_data->uvdm_error = -EBUSY;
} else {
msg_maxim("DIR_OUT Undefined RX value");
usbpd_data->uvdm_error = -EPROTO;
}
}
complete(&usbpd_data->uvdm_longpacket_out_wait);
msg_maxim("DIR_OUT complete!");
break;
case DIR_IN:
if (usbpd_data->is_in_first_sec_uvdm_req) { /* LONG and SHORT response is same */
SEC_UVDM_RESPONSE_HEADER = (U_SEC_UVDM_RESPONSE_HEADER *)&ReadMSG[6];
SEC_UVDM_TX_HEADER = (U_SEC_TX_DATA_HEADER *)&ReadMSG[10];
#if (UVDM_DEBUG)
msg_maxim("DIR_IN data_type = %d , command_type = %d, direction=%d, total_number_of_uvdm_set=%d",
SEC_UVDM_RESPONSE_HEADER->BITS.data_type,
SEC_UVDM_RESPONSE_HEADER->BITS.command_type,
SEC_UVDM_RESPONSE_HEADER->BITS.direction,
SEC_UVDM_RESPONSE_HEADER->BITS.total_number_of_uvdm_set);
#endif
if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type == SEC_UVDM_RESPONDER_ACK) {
memcpy(usbpd_data->ReadMSG, ReadMSG, OPCODE_DATA_LENGTH);
#if (UVDM_DEBUG)
msg_maxim("DIR_IN order_of_current_uvdm_set = %d , total_data_size = %d, data_size_of_current_set=%d",
SEC_UVDM_TX_HEADER->BITS.order_of_current_uvdm_set,
SEC_UVDM_TX_HEADER->BITS.total_data_size,
SEC_UVDM_TX_HEADER->BITS.data_size_of_current_set);
#endif
msg_maxim("DIR_IN 1st Response");
} else if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type == SEC_UVDM_RESPONDER_NAK) {
msg_maxim("DIR_IN SEC_UVDM_RESPONDER_NAK received");
usbpd_data->uvdm_error = -ENODATA;
} else if (SEC_UVDM_RESPONSE_HEADER->BITS.command_type == SEC_UVDM_RESPONDER_BUSY) {
msg_maxim("DIR_IN SEC_UVDM_RESPONDER_BUSY received");
usbpd_data->uvdm_error = -EBUSY;
} else {
msg_maxim("DIR_IN Undefined RESPONDER value");
usbpd_data->uvdm_error = -EPROTO;
}
} else {
/* don't have ack packet after 2nd sec_tx_data_header */
memcpy(usbpd_data->ReadMSG, ReadMSG, OPCODE_DATA_LENGTH);
SEC_UVDM_TX_HEADER = (U_SEC_TX_DATA_HEADER *)&usbpd_data->ReadMSG[6];
#if (UVDM_DEBUG)
msg_maxim("DIR_IN order_of_current_uvdm_set = %d , total_data_size = %d, data_size_of_current_set=%d",
SEC_UVDM_TX_HEADER->BITS.order_of_current_uvdm_set,
SEC_UVDM_TX_HEADER->BITS.total_data_size,
SEC_UVDM_TX_HEADER->BITS.data_size_of_current_set);
#endif
if (SEC_UVDM_TX_HEADER->data != 0)
msg_maxim("DIR_IN %dth Response", SEC_UVDM_TX_HEADER->BITS.order_of_current_uvdm_set);
else
msg_maxim("DIR_IN Last Response. It's zero");
}
complete(&usbpd_data->uvdm_longpacket_in_wait);
msg_maxim("DIR_IN complete!");
break;
default:
msg_maxim("Never Call!!!");
break;
}
}
int max77705_sec_uvdm_out_request_message(void *data, int size)
{
struct max77705_usbc_platform_data *usbpd_data;
struct i2c_client *i2c;
int ret;
usbpd_data = g_usbc_data;
if (!usbpd_data)
return -ENXIO;
i2c = usbpd_data->muic;
if (i2c == NULL) {
msg_maxim("usbpd_data->i2c is not valid!");
return -EINVAL;
}
if (data == NULL) {
msg_maxim("given data is not valid !");
return -EINVAL;
}
if (size >= SAMSUNGUVDM_MAX_LONGPACKET_SIZE) {
msg_maxim("size %d is too big to send data", size);
ret = -EFBIG;
} else if (size <= SAMSUNGUVDM_MAX_SHORTPACKET_SIZE)
ret = max77705_send_sec_unstructured_short_vdm_message(usbpd_data, data, size);
else
ret = max77705_send_sec_unstructured_long_vdm_message(usbpd_data, data, size);
return ret;
}
int max77705_sec_uvdm_in_request_message(void *data)
{
struct max77705_usbc_platform_data *usbpd_data;
uint8_t SendMSG[32] = {0,};
uint8_t ReadMSG[32] = {0,};
uint8_t IN_DATA[MAX_INPUT_DATA] = {0,};
/* Send Request */
/* 1 Message Type Definition */
U_SEC_UVDM_RESPONSE_HEADER *SEC_RES_HEADER;
U_SEC_TX_DATA_HEADER *SEC_UVDM_TX_HEADER;
U_SEC_TX_DATA_TAILER *SEC_UVDM_TX_TAILER;
int cur_uvdmset_data = 0;
int cur_uvdmset_num = 0;
int total_uvdmset_num = 0;
int received_data_size = 0;
int total_received_data_size = 0;
int ack = 0;
int size = 0;
int time_left = 0;
int cal_checksum = 0;
int i = 0;
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
int event;
#endif
usbpd_data = g_usbc_data;
if (!usbpd_data)
return -ENXIO;
if (data == NULL) {
msg_maxim("%s given data is not valid !", __func__);
return -EINVAL;
}
usbpd_data->is_in_sec_uvdm_out = DIR_IN;
usbpd_data->is_in_first_sec_uvdm_req = true;
/* 1. Common : Fill the MSGHeader */
set_msgheader(SendMSG, ACK, 2);
/* 2. Common : Fill the UVDMHeader*/
set_uvdmheader(SendMSG, SAMSUNG_VENDOR_ID, 0);
/* 3. Common : Fill the First SEC_VDMHeader*/
if (usbpd_data->is_in_first_sec_uvdm_req)
set_sec_uvdmheader(SendMSG, usbpd_data->Product_ID, TYPE_LONG, SEC_UVDM_ININIATOR, DIR_IN, 0, 0);
/* 8. Send data to PDIC */
usbpd_data->uvdm_error = 0;
max77705_send_vdm_write_message(SendMSG);
cur_uvdmset_num = 0;
total_uvdmset_num = 1;
do {
reinit_completion(&usbpd_data->uvdm_longpacket_in_wait);
time_left =
wait_for_completion_interruptible_timeout(&usbpd_data->uvdm_longpacket_in_wait,
msecs_to_jiffies(SAMSUNGUVDM_WAIT_MS));
if (time_left <= 0) {
msg_maxim("timeout");
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
event = NOTIFY_EXTRA_UVDM_TIMEOUT;
store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL);
#endif
return -ETIME;
}
if (usbpd_data->uvdm_error)
return usbpd_data->uvdm_error;
/* read data */
memset(ReadMSG, 0, 32);
/* read data */
memcpy(ReadMSG, usbpd_data->ReadMSG, OPCODE_DATA_LENGTH);
if (usbpd_data->is_in_first_sec_uvdm_req) {
SEC_RES_HEADER = (U_SEC_UVDM_RESPONSE_HEADER *)&ReadMSG[6];
SEC_UVDM_TX_HEADER = (U_SEC_TX_DATA_HEADER *)&ReadMSG[10];
#if (UVDM_DEBUG)
msg_maxim("SEC_RES_HEADER : 0x%x, SEC_UVDM_TX_HEADER : 0x%x", SEC_RES_HEADER->data, SEC_UVDM_TX_HEADER->data);
#endif
if (SEC_RES_HEADER->BITS.data_type == TYPE_LONG) {
/* 1. check the data size received */
size = SEC_UVDM_TX_HEADER->BITS.total_data_size;
cur_uvdmset_data = SEC_UVDM_TX_HEADER->BITS.data_size_of_current_set;
cur_uvdmset_num = SEC_UVDM_TX_HEADER->BITS.order_of_current_uvdm_set;
total_uvdmset_num =
SEC_RES_HEADER->BITS.total_number_of_uvdm_set;
usbpd_data->is_in_first_sec_uvdm_req = false;
/* 2. copy data to buffer */
for (i = 0; i < SAMSUNGUVDM_MAXDATA_FIRST_UVDMSET; i++)
IN_DATA[received_data_size++] = ReadMSG[14 + i];
total_received_data_size += cur_uvdmset_data;
usbpd_data->is_in_first_sec_uvdm_req = false;
} else {
IN_DATA[received_data_size++] = SEC_RES_HEADER->BITS.data;
return received_data_size;
}
} else {
SEC_UVDM_TX_HEADER = (U_SEC_TX_DATA_HEADER *)&ReadMSG[6];
cur_uvdmset_data = SEC_UVDM_TX_HEADER->BITS.data_size_of_current_set;
cur_uvdmset_num = SEC_UVDM_TX_HEADER->BITS.order_of_current_uvdm_set;
/* 2. copy data to buffer */
for (i = 0 ; i < SAMSUNGUVDM_MAXDATA_NORMAL_UVDMSET ; i++)
IN_DATA[received_data_size++] = ReadMSG[10 + i];
total_received_data_size += cur_uvdmset_data;
}
/* 3. Check Checksum */
SEC_UVDM_TX_TAILER = (U_SEC_TX_DATA_TAILER *)&ReadMSG[26];
cal_checksum = get_checksum(ReadMSG, 6, SAMSUNGUVDM_CHECKSUM_DATA_COUNT);
ack = (cal_checksum == SEC_UVDM_TX_TAILER->BITS.checksum) ?
SEC_UVDM_RX_HEADER_ACK : SEC_UVDM_RX_HEADER_NAK;
#if (UVDM_DEBUG)
msg_maxim("SEC_UVDM_TX_TAILER : 0x%x, cal_checksum : 0x%x, size : %d", SEC_UVDM_TX_TAILER->data, cal_checksum, size);
#endif
/* 1. Common : Fill the MSGHeader */
if (cur_uvdmset_num == total_uvdmset_num)
set_msgheader(SendMSG, NAK, 2);
else
set_msgheader(SendMSG, ACK, 2);
/* 2. Common : Fill the UVDMHeader*/
set_uvdmheader(SendMSG, SAMSUNG_VENDOR_ID, 0);
/* 5-3. Common : Fill the SEC RXHeader */
set_sec_uvdm_rxdata_header(SendMSG, cur_uvdmset_num, cur_uvdmset_data, ack);
/* 8. Send data to PDIC */
usbpd_data->uvdm_error = 0;
max77705_send_vdm_write_message(SendMSG);
} while (cur_uvdmset_num < total_uvdmset_num);
set_endian(IN_DATA, data, size);
reinit_completion(&usbpd_data->uvdm_longpacket_in_wait);
time_left =
wait_for_completion_interruptible_timeout(&usbpd_data->uvdm_longpacket_in_wait,
msecs_to_jiffies(SAMSUNGUVDM_WAIT_MS));
if (time_left <= 0) {
msg_maxim("last in request timeout");
#ifdef CONFIG_USB_NOTIFY_PROC_LOG
event = NOTIFY_EXTRA_UVDM_TIMEOUT;
store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL);
#endif
return -ETIME;
}
if (usbpd_data->uvdm_error)
return usbpd_data->uvdm_error;
return size;
}
int max77705_sec_uvdm_ready(void)
{
int uvdm_ready = false;
struct max77705_usbc_platform_data *usbpd_data;
usbpd_data = g_usbc_data;
msg_maxim("power_nego is%s done, accessory_enter_mode is%s done",
usbpd_data->pn_flag ? "" : " not",
usbpd_data->is_samsung_accessory_enter_mode? "" : " not");
if (usbpd_data->pn_flag &&
usbpd_data->is_samsung_accessory_enter_mode)
uvdm_ready = true;
msg_maxim("uvdm ready = %d", uvdm_ready);
return uvdm_ready;
}
void max77705_sec_uvdm_close(void)
{
struct max77705_usbc_platform_data *usbpd_data;
usbpd_data = g_usbc_data;
complete(&usbpd_data->uvdm_longpacket_out_wait);
complete(&usbpd_data->uvdm_longpacket_in_wait);
msg_maxim("success");
}