blob: b06039e0352d91f04dba56f5e4fc208f819b774f [file] [log] [blame]
/*
* driver/../s2mm003.c - S2MM003 USBPD device 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/s2mm003.h>
#include <linux/power_supply.h>
#include <linux/battery/sec_charger.h>
#if defined(CONFIG_BATTERY_NOTIFIER)
#include <linux/battery/battery_notifier.h>
#endif
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
#include <linux/usb/class-dual-role.h>
#endif
/* switch device header */
#ifdef CONFIG_SWITCH
#include <linux/switch.h>
#endif /* CONFIG_SWITCH */
#ifdef CONFIG_SWITCH
static struct switch_dev switch_dock = {
.name = "ccic_dock",
};
#endif /* CONFIG_SWITCH */
/* CCIC Dock Observer Callback parameter */
enum {
CCIC_DOCK_DETACHED = 0,
CCIC_DOCK_HMT = 105,
CCIC_DOCK_ABNORMAL = 106,
};
extern struct device *ccic_device;
static void ccic_send_dock_intent(int type)
{
pr_info("%s: CCIC dock type(%d)\n", __func__, type);
#ifdef CONFIG_SWITCH
switch_set_state(&switch_dock, type);
#endif
}
static int ccic_dock_attach_notify(int type, const char *name)
{
pr_info("%s: %s\n", __func__, name);
ccic_send_dock_intent(type);
return NOTIFY_OK;
}
static int ccic_dock_detach_notify(void)
{
pr_info("%s\n", __func__);
ccic_send_dock_intent(CCIC_DOCK_DETACHED);
return NOTIFY_OK;
}
static int s2mm003_read_byte(const struct i2c_client *i2c, u8 reg, u8 *val)
{
int ret; u8 wbuf;
struct i2c_msg msg[2];
struct s2mm003_data *usbpd_data = i2c_get_clientdata(i2c);
mutex_lock(&usbpd_data->i2c_mutex);
msg[0].addr = i2c->addr;
msg[0].flags = i2c->flags;
msg[0].len = 1;
msg[0].buf = &wbuf;
msg[1].addr = i2c->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 2;
msg[1].buf = val;
wbuf = (reg & 0xFF);
ret = i2c_transfer(i2c->adapter, msg, 2);
if (ret < 0)
dev_err(&i2c->dev, "i2c reading fail reg(0x%x), error %d\n",
reg, ret);
mutex_unlock(&usbpd_data->i2c_mutex);
return ret;
}
static int s2mm003_read_byte_16(const struct i2c_client *i2c, u16 reg, u8 *val)
{
int ret; u8 wbuf[2], rbuf;
struct i2c_msg msg[2];
struct s2mm003_data *usbpd_data = i2c_get_clientdata(i2c);
mutex_lock(&usbpd_data->i2c_mutex);
msg[0].addr = 0x43;
msg[0].flags = i2c->flags;
msg[0].len = 2;
msg[0].buf = wbuf;
msg[1].addr = 0x43;
msg[1].flags = I2C_M_RD;
msg[1].len = 1;
msg[1].buf = &rbuf;
wbuf[0] = (reg & 0xFF00) >> 8;
wbuf[1] = (reg & 0xFF);
ret = i2c_transfer(i2c->adapter, msg, 2);
if (ret < 0)
dev_err(&i2c->dev, "i2c read16 fail reg(0x%x), error %d\n",
reg, ret);
mutex_unlock(&usbpd_data->i2c_mutex);
*val = rbuf;
return rbuf;
}
static int s2mm003_write_byte(const struct i2c_client *i2c, u8 reg, u8 val)
{
int ret = 0; u8 wbuf[2];
struct i2c_msg msg[1];
struct s2mm003_data *usbpd_data = i2c_get_clientdata(i2c);
mutex_lock(&usbpd_data->i2c_mutex);
msg[0].addr = i2c->addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = wbuf;
wbuf[0] = (reg & 0xFF);
wbuf[1] = (val & 0xFF);
ret = i2c_transfer(i2c->adapter, msg, 1);
if (ret < 0)
dev_err(&i2c->dev, "i2c write fail reg(0x%x:%x), error %d\n",
reg, val, ret);
mutex_unlock(&usbpd_data->i2c_mutex);
return ret;
}
static int s2mm003_write_byte_16(const struct i2c_client *i2c, u16 reg, u8 val)
{
int ret = 0; u8 wbuf[3];
struct i2c_msg msg[1];
struct s2mm003_data *usbpd_data = i2c_get_clientdata(i2c);
mutex_lock(&usbpd_data->i2c_mutex);
msg[0].addr = 0x43;
msg[0].flags = 0;
msg[0].len = 3;
msg[0].buf = wbuf;
wbuf[0] = (reg & 0xFF00) >> 8;
wbuf[1] = (reg & 0xFF);
wbuf[2] = (val & 0xFF);
ret = i2c_transfer(i2c->adapter, msg, 1);
if (ret < 0)
dev_err(&i2c->dev, "i2c write fail reg(0x%x:%x), error %d\n",
reg, val, ret);
mutex_unlock(&usbpd_data->i2c_mutex);
return ret;
}
static void s2mm003_int_clear(struct s2mm003_data *usbpd_data)
{
struct i2c_client *i2c = usbpd_data->i2c;
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x1);
s2mm003_write_byte(i2c, IRQ_RD_ADDR, 0xFF);
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x0);
}
static int s2mm003_indirect_read(struct s2mm003_data *usbpd_data, u8 address)
{
u8 value = 0;
int ret = 0;
struct i2c_client *i2c = usbpd_data->i2c;
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x1);
s2mm003_write_byte(i2c, IRQ_RD_ADDR, address);
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x0);
ret = s2mm003_read_byte(i2c, IRQ_RD_DATA, &value);
if (ret < 0)
dev_err(&i2c->dev, "indirect read error value:%02x ret:%d\n", value, ret);
return value;
}
static void s2mm003_indirect_write(struct s2mm003_data *usbpd_data, u8 address, u8 val)
{
struct i2c_client *i2c = usbpd_data->i2c;
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x1);
s2mm003_write_byte(i2c, IRQ_WR_ADDR, address);
s2mm003_write_byte(i2c, IRQ_WR_DATA, val);
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x0);
}
static int s2mm003_src_capacity_information(const struct i2c_client *i2c, uint32_t *RX_SRC_CAPA_MSG,
PDIC_SINK_STATUS * pd_sink_status)
{
uint32_t RdCnt;
uint32_t PDO_cnt;
uint32_t PDO_sel;
int available_pdo_num = 0;
MSG_HEADER_Type *MSG_HDR;
SRC_FIXED_SUPPLY_Typedef *MSG_FIXED_SUPPLY;
SRC_VAR_SUPPLY_Typedef *MSG_VAR_SUPPLY;
SRC_BAT_SUPPLY_Typedef *MSG_BAT_SUPPLY;
for(RdCnt=0;RdCnt<8;RdCnt++)
{
dev_info(&i2c->dev, "Rd_SRC_CAPA_%d : 0x%X\n", RdCnt, RX_SRC_CAPA_MSG[RdCnt]);
}
MSG_HDR = (MSG_HEADER_Type *)&RX_SRC_CAPA_MSG[0];
dev_info(&i2c->dev, "=======================================\n");
dev_info(&i2c->dev, " MSG Header\n");
dev_info(&i2c->dev, " Rsvd_msg_header : %d\n",MSG_HDR->Rsvd_msg_header );
dev_info(&i2c->dev, " Number_of_obj : %d\n",MSG_HDR->Number_of_obj );
dev_info(&i2c->dev, " Message_ID : %d\n",MSG_HDR->Message_ID );
dev_info(&i2c->dev, " Port_Power_Role : %d\n",MSG_HDR->Port_Power_Role );
dev_info(&i2c->dev, " Specification_Revision : %d\n",MSG_HDR->Specification_Revision );
dev_info(&i2c->dev, " Port_Data_Role : %d\n",MSG_HDR->Port_Data_Role );
dev_info(&i2c->dev, " Rsvd2_msg_header : %d\n",MSG_HDR->Rsvd2_msg_header );
dev_info(&i2c->dev, " Message_Type : %d\n",MSG_HDR->Message_Type );
for(PDO_cnt = 0;PDO_cnt < MSG_HDR->Number_of_obj;PDO_cnt++)
{
PDO_sel = (RX_SRC_CAPA_MSG[PDO_cnt + 1] >> 30) & 0x3;
dev_info(&i2c->dev, " =================\n");
dev_info(&i2c->dev, " PDO_Num : %d\n", (PDO_cnt + 1));
if(PDO_sel == 0) // *MSG_FIXED_SUPPLY
{
MSG_FIXED_SUPPLY = (SRC_FIXED_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1];
if(MSG_FIXED_SUPPLY->Voltage_Unit <= (AVAILABLE_VOLTAGE/UNIT_FOR_VOLTAGE))
available_pdo_num = PDO_cnt + 1;
pd_sink_status->power_list[PDO_cnt+1].max_voltage = MSG_FIXED_SUPPLY->Voltage_Unit * UNIT_FOR_VOLTAGE;
pd_sink_status->power_list[PDO_cnt+1].max_current = MSG_FIXED_SUPPLY->Maximum_Current * UNIT_FOR_CURRENT;
dev_info(&i2c->dev, " PDO_Parameter(FIXED_SUPPLY) : %d\n",MSG_FIXED_SUPPLY->PDO_Parameter );
dev_info(&i2c->dev, " Dual_Role_Power : %d\n",MSG_FIXED_SUPPLY->Dual_Role_Power );
dev_info(&i2c->dev, " USB_Suspend_Support : %d\n",MSG_FIXED_SUPPLY->USB_Suspend_Support );
dev_info(&i2c->dev, " Externally_POW : %d\n",MSG_FIXED_SUPPLY->Externally_POW );
dev_info(&i2c->dev, " USB_Comm_Capable : %d\n",MSG_FIXED_SUPPLY->USB_Comm_Capable );
dev_info(&i2c->dev, " Data_Role_Swap : %d\n",MSG_FIXED_SUPPLY->Data_Role_Swap );
dev_info(&i2c->dev, " Reserved : %d\n",MSG_FIXED_SUPPLY->Reserved );
dev_info(&i2c->dev, " Peak_Current : %d\n",MSG_FIXED_SUPPLY->Peak_Current );
dev_info(&i2c->dev, " Voltage_Unit : %d\n",MSG_FIXED_SUPPLY->Voltage_Unit );
dev_info(&i2c->dev, " Maximum_Current : %d\n",MSG_FIXED_SUPPLY->Maximum_Current );
}
else if(PDO_sel == 2) // *MSG_VAR_SUPPLY
{
MSG_VAR_SUPPLY = (SRC_VAR_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1];
dev_info(&i2c->dev, " PDO_Parameter(VAR_SUPPLY) : %d\n",MSG_VAR_SUPPLY->PDO_Parameter );
dev_info(&i2c->dev, " Maximum_Voltage : %d\n",MSG_VAR_SUPPLY->Maximum_Voltage );
dev_info(&i2c->dev, " Minimum_Voltage : %d\n",MSG_VAR_SUPPLY->Minimum_Voltage );
dev_info(&i2c->dev, " Maximum_Current : %d\n",MSG_VAR_SUPPLY->Maximum_Current );
}
else if(PDO_sel == 1) // *MSG_BAT_SUPPLY
{
MSG_BAT_SUPPLY = (SRC_BAT_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1];
dev_info(&i2c->dev, " PDO_Parameter(BAT_SUPPLY) : %d\n",MSG_BAT_SUPPLY->PDO_Parameter );
dev_info(&i2c->dev, " Maximum_Voltage : %d\n",MSG_BAT_SUPPLY->Maximum_Voltage );
dev_info(&i2c->dev, " Minimum_Voltage : %d\n",MSG_BAT_SUPPLY->Minimum_Voltage );
dev_info(&i2c->dev, " Maximum_Allow_Power : %d\n",MSG_BAT_SUPPLY->Maximum_Allow_Power );
}
}
/* the number of available pdo list */
pd_sink_status->available_pdo_num = available_pdo_num;
dev_info(&i2c->dev, "=======================================\n\r");
dev_info(&i2c->dev, "\n\r");
return available_pdo_num;
}
#if 0
static void s2mm003_request_select_type(uint32_t * REQ_MSG , int num)
{
REQUEST_FIXED_SUPPLY_STRUCT_Typedef *REQ_FIXED_SPL;
REQ_FIXED_SPL = (REQUEST_FIXED_SUPPLY_STRUCT_Typedef *)REQ_MSG;
//REQ_FIXED_SPL->Reserved_2 = 0;
REQ_FIXED_SPL->Object_Position = (num & 0x7);
//REQ_FIXED_SPL->GiveBack_Flag = 0; // GiveBack Support set to 1
//REQ_FIXED_SPL->Capa_Mismatch = 0;
//REQ_FIXED_SPL->USB_Comm_Capable = 0;
//REQ_FIXED_SPL->No_USB_Suspend = 0;
//REQ_FIXED_SPL->Reserved_1 = 0; // Set to Zero
REQ_FIXED_SPL->OP_Current = 200; // 10mA
REQ_FIXED_SPL->Maximum_OP_Current = 200; // 10mA
}
#endif
static void Modify_TX_SRC_CAPA(struct s2mm003_data *usbpd_data)
{
uint32_t i;
U_MSG_HEADER_Type *MSG_HEADER;
U_SRC_FIXED_SUPPLY_Typedef *MSG_TX_SRC_FIXED_SUPPLY;
uint8_t data_arr[8]={0,};
// Set Message Index - TX Source Capability
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_TX_SRC_CAPA);
// Read Default TX source Capability message - Size 8 Byte
for(i=0; i<8; i++)
data_arr[i] = s2mm003_indirect_read(usbpd_data, i+0x50);
// Link TX Source Capability Data struct
MSG_HEADER = (U_MSG_HEADER_Type *)&data_arr[0];
MSG_TX_SRC_FIXED_SUPPLY = (U_SRC_FIXED_SUPPLY_Typedef *)&data_arr[4];
// ===== For Debug Print
printk(KERN_ERR "MSG_TX_SRC_FIXED_SUPPLY->BITS.Maximum_Current) : %d\n", MSG_TX_SRC_FIXED_SUPPLY->BITS.Maximum_Current);
// =====================
// ===== For Modified Data Field
MSG_TX_SRC_FIXED_SUPPLY->BITS.Maximum_Current = 50; // 10mA unit * 50 (500mA)
// =====================
// Write TX source Capability message - Size 8 Byte
for(i=0; i<8; i++)
s2mm003_indirect_write(usbpd_data, (i+0x50), data_arr[i]);
return;
}
static void VBUS_TURN_ON_CTRL(bool enable)
{
union power_supply_propval val;
printk(KERN_ERR "%s -> enable : %d\n", __func__, enable);
val.intval = enable;
psy_do_property("otg", set,
POWER_SUPPLY_PROP_ONLINE, val);
}
static void PDIC_Function_State_for_VBUS(u8 val)
{
switch(val)
{
case 0: // PE_Initial_Detach
VBUS_TURN_ON_CTRL(0);
break;
// case 1: // PE_SRC_Startup
// VBUS_TURN_ON_CTRL(VBUS_ON);
// break;
// case 2: // PE_SRC_Discovery
// VBUS_TURN_ON_CTRL(VBUS_ON);
// break;
case 3: // PE_SRC_Send_Capabilities
VBUS_TURN_ON_CTRL(1);
break;
case 4: // PE_SRC_Negotiate_Capability
VBUS_TURN_ON_CTRL(1);
break;
case 5: // PE_SRC_Transition_Supply
VBUS_TURN_ON_CTRL(1);
break;
case 6: // PE_SRC_Ready
VBUS_TURN_ON_CTRL(1);
break;
case 7: // PE_SRC_Disabled
VBUS_TURN_ON_CTRL(1);
break;
// case 8: // PE_SRC_Capability_Response
// VBUS_TURN_ON_CTRL(VBUS_ON);
// break;
// case 9: // PE_SRC_Hard_Reset
// VBUS_TURN_ON_CTRL(VBUS_ON);
// break;
// case 10: // PE_SRC_Hard_Reset_Received
// VBUS_TURN_ON_CTRL(VBUS_ON);
// break;
// case 11: // PE_SRC_Transition_to_default
// VBUS_TURN_ON_CTRL(VBUS_ON);
// break;
// case 12: // PE_SRC_Give_Source_Cap
// VBUS_TURN_ON_CTRL(VBUS_ON);
// break;
// case 13: // PE_SRC_Get_Sink_Cap
// VBUS_TURN_ON_CTRL(VBUS_ON);
// break;
// case 14: // PE_SRC_Wait_New_Capabilities
// VBUS_TURN_ON_CTRL(VBUS_ON);
// break;
// case 15: // PE_SNK_Startup
// VBUS_TURN_ON_CTRL(VBUS_OFF);
// break;
// case 16: // PE_SNK_Discovery
// VBUS_TURN_ON_CTRL(VBUS_OFF);
// break;
case 17: // PE_SNK_Wait_for_Capabilities
VBUS_TURN_ON_CTRL(0);
break;
case 18: // PE_SNK_Evaluate_Capability
VBUS_TURN_ON_CTRL(0);
break;
case 19: // PE_SNK_Select_Capability
VBUS_TURN_ON_CTRL(0);
break;
case 20: // PE_SNK_Transition_Sink
VBUS_TURN_ON_CTRL(0);
break;
case 21: // PE_SNK_Ready
VBUS_TURN_ON_CTRL(0);
break;
// case 22: // PE_SNK_Hard_Reset
// VBUS_TURN_ON_CTRL(VBUS_OFF);
// break;
// case 23: // PE_SNK_Transition_to_default
// VBUS_TURN_ON_CTRL(VBUS_OFF);
// break;
// case 24: // PE_SNK_Give_Sink_Cap
// VBUS_TURN_ON_CTRL(VBUS_OFF);
// break;
// case 25: // PE_SNK_Get_Source_Cap
// VBUS_TURN_ON_CTRL(VBUS_OFF);
// break;
// case 26: // PE_SRC_CABLE_VDM_Identity_Request
// VBUS_TURN_ON_CTRL(VBUS_OFF);
// break;
// case 27: // PE_SRC_CABLE_VDM_Identity_ACKed
// VBUS_TURN_ON_CTRL(VBUS_OFF);
// break;
// case 28: // PE_SRC_CABLE_VDM_Identity_NAKed
// VBUS_TURN_ON_CTRL(VBUS_OFF);
// break;
case 29: // ErrorRecovery
VBUS_TURN_ON_CTRL(0);
break;
default:
VBUS_TURN_ON_CTRL(0);
break;
}
return;
}
static struct pdic_notifier_struct pd_noti;
static void send_usb_notify_message(struct s2mm003_data *usbpd_data, u8 mode)
{
CC_NOTI_USB_STATUS_TYPEDEF usb_status_notifier;
usb_status_notifier.id = CCIC_NOTIFY_ID_USB;
usb_status_notifier.src = CCIC_NOTIFY_DEV_CCIC;
usb_status_notifier.dest = CCIC_NOTIFY_DEV_USB;
if(mode == USB_STATUS_NOTIFY_DETACH)
usb_status_notifier.attach = CCIC_NOTIFY_DETACH;
else
usb_status_notifier.attach = CCIC_NOTIFY_ATTACH;
usb_status_notifier.drp = mode;
usbpd_data->Pdic_usb_state = mode;
ccic_notifier_notify((CC_NOTI_TYPEDEF*)&usb_status_notifier, NULL, 0);
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
if (usbpd_data->try_state_change && (usbpd_data->Pdic_usb_state != USB_STATUS_NOTIFY_DETACH))
{
// Role change try and new mode detected
complete(&usbpd_data->reverse_completion);
}
dual_role_instance_changed(usbpd_data->dual_role);
#endif
}
/* for alternate mode */
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
static void Msg_Read_Discover_Identity(struct s2mm003_data *usbpd_data)
{
uint32_t i;
U_MSG_HEADER_Type *MSG_HEADER;
U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM;
U_DATA_MSG_ID_HEADER_Type *DATA_MSG_ID;
U_CERT_STAT_VDO_Type *DATA_MSG_CERT;
U_PRODUCT_VDO_Type *DATA_MSG_PRODUCT;
uint8_t data_arr[32]={0,};
MSG_HEADER = (U_MSG_HEADER_Type *)&data_arr[0];
DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type *)&data_arr[4];
DATA_MSG_ID = (U_DATA_MSG_ID_HEADER_Type *)&data_arr[8];
DATA_MSG_CERT = (U_CERT_STAT_VDO_Type *)&data_arr[12];
DATA_MSG_PRODUCT = (U_PRODUCT_VDO_Type *)&data_arr[16];
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_RX_DISC_ID_RESP);
for(i=0; i<32; i++)
data_arr[i] = s2mm003_indirect_read(usbpd_data, i+0x50);
usbpd_data->Vendor_ID = DATA_MSG_ID->BITS.USB_Vendor_ID;
usbpd_data->Product_ID = DATA_MSG_PRODUCT->BITS.Product_ID;
printk(KERN_ERR "[%s] Vendor_ID : 0x%X, Product_ID : 0x%X\n", __func__, usbpd_data->Vendor_ID, usbpd_data->Product_ID);
if(usbpd_data->Vendor_ID ==0x04E8 && usbpd_data->Product_ID==0xA500) // Gear VR VID, PID
usbpd_data->acc_type = CCIC_DOCK_HMT;
if( (DATA_MSG_ID->BITS.Data_Capable_USB_Host == 0x01)
&& (DATA_MSG_ID->BITS.Data_Capable_USB_Device == 0x00)
&& (DATA_MSG_ID->BITS.Product_Type == 0x02)
){
printk(KERN_ERR "[%s] Working with USB Host Device.\n", __func__);
s2mm003_indirect_write(usbpd_data, 0x4C, 0x10); // Detach and re-attach
VBUS_TURN_ON_CTRL(0);
usbpd_data->Pdic_state_machine = b_Cmd_Intial_State;
usbpd_data->func_state = FUNCTION_STATUS_INITIAL_DETACH;
send_usb_notify_message(usbpd_data, USB_STATUS_NOTIFY_DETACH);
}
else
usbpd_data->Pdic_state_machine = b_Cmd_Discover_Identity;
}
static void Msg_Read_Discover_SVIDs(struct s2mm003_data *usbpd_data)
{
uint32_t i;
U_MSG_HEADER_Type *MSG_HEADER;
U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM;
U_VDO1_Type *DATA_MSG_VDO1;
uint8_t data_arr[32]={0,};
MSG_HEADER = (U_MSG_HEADER_Type *)&data_arr[0];
DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type *)&data_arr[4];
DATA_MSG_VDO1 = (U_VDO1_Type *)&data_arr[8];
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_RX_DISC_SVIDs_RESP);
for(i=0; i<32; i++)
data_arr[i] = s2mm003_indirect_read(usbpd_data, i+0x50);
usbpd_data->SVID_0 = DATA_MSG_VDO1->BITS.SVID_0;
usbpd_data->SVID_1 = DATA_MSG_VDO1->BITS.SVID_1;
printk(KERN_ERR "[%s] SVID_0 : 0x%X, SVID_1 : 0x%X\n", __func__, usbpd_data->SVID_0, usbpd_data->SVID_1);
usbpd_data->Pdic_state_machine = b_Cmd_Discover_SVIDs;
}
static void Msg_Read_Discover_Modes(struct s2mm003_data *usbpd_data)
{
uint32_t i;
U_MSG_HEADER_Type *MSG_HEADER;
U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM;
uint8_t data_arr[32]={0,};
MSG_HEADER = (U_MSG_HEADER_Type *)&data_arr[0];
DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type *)&data_arr[4];
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_RX_DISC_MODE_RESP);
for(i=0; i<32; i++)
data_arr[i] = s2mm003_indirect_read(usbpd_data, i+0x50);
printk(KERN_ERR "[%s]\n", __func__);
usbpd_data->Pdic_state_machine = b_Cmd_Discover_Modes;
}
static void Msg_Read_Enter_Mode(struct s2mm003_data *usbpd_data)
{
U_MSG_HEADER_Type *MSG_HEADER;
U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM;
int i;
uint8_t data_arr[32]={0,};
const char name[11] = "HMT Attach";
MSG_HEADER = (U_MSG_HEADER_Type *)&data_arr[0];
DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type *)&data_arr[4];
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_RX_DISC_ENTER_MODE_RESP);
for(i=0; i<32; i++)
data_arr[i] = s2mm003_indirect_read(usbpd_data, i+0x50);
if(DATA_MSG_VDM->BITS.VDM_command_type == 1)
printk(KERN_ERR "[%s] --> EnterMode Ack\n", __func__);
else
printk(KERN_ERR "[%s] --> EnterMode Nak\n", __func__);
usbpd_data->Pdic_state_machine = b_Cmd_Enter_Mode;
if (usbpd_data->acc_type != CCIC_DOCK_DETACHED)
ccic_dock_attach_notify(usbpd_data->acc_type, name);
}
static void Msg_Send_Discover_Idendity(struct s2mm003_data *usbpd_data)
{
uint32_t i=0;
U_MSG_HEADER_Type *MSG_HEADER;
U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM;
uint8_t data_arr[32]={0,};
printk(KERN_ERR "[%s]\n", __func__);
MSG_HEADER = (U_MSG_HEADER_Type *)&data_arr[0];
DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type *)&data_arr[4];
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_VDM_MSG_REQUEST);
for(i=0; i<8; i++)
data_arr[i] = s2mm003_indirect_read(usbpd_data, i+0x50);
MSG_HEADER->BITS.Number_of_obj = 1;
DATA_MSG_VDM->BITS.Standard_Vendor_ID = 0xFF00;
DATA_MSG_VDM->BITS.Object_Position = 0;
for(i = 0; i < 8; i++)
s2mm003_indirect_write(usbpd_data, i+0x50, data_arr[i]);
s2mm003_indirect_write(usbpd_data, 0x4E, 1);
}
static void Msg_Send_Discover_SVIDs(struct s2mm003_data *usbpd_data)
{
uint32_t i;
U_MSG_HEADER_Type *MSG_HEADER;
U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM;
uint8_t data_arr[32]={0,};
printk(KERN_ERR "[%s]\n", __func__);
MSG_HEADER = (U_MSG_HEADER_Type *)&data_arr[0];
DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type *)&data_arr[4];
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_VDM_MSG_REQUEST);
for(i=0; i<8; i++)
data_arr[i] = s2mm003_indirect_read(usbpd_data, i+0x50);
MSG_HEADER->BITS.Number_of_obj = 1;
DATA_MSG_VDM->BITS.Standard_Vendor_ID = 0xFF00;
DATA_MSG_VDM->BITS.Object_Position = 0;
for(i=0; i<8; i++)
s2mm003_indirect_write(usbpd_data, i+0x50, data_arr[i]);
s2mm003_indirect_write(usbpd_data, 0x4E, 2);
}
static void Msg_Send_Discover_Modes(struct s2mm003_data *usbpd_data)
{
uint32_t i;
U_MSG_HEADER_Type *MSG_HEADER;
U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM;
uint8_t data_arr[32]={0,};
printk(KERN_ERR "[%s]\n", __func__);
MSG_HEADER = (U_MSG_HEADER_Type *)&data_arr[0];
DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type *)&data_arr[4];
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_VDM_MSG_REQUEST);
for(i=0; i<8; i++)
data_arr[i] = s2mm003_indirect_read(usbpd_data, i+0x50);
MSG_HEADER->BITS.Number_of_obj = 1;
DATA_MSG_VDM->BITS.Standard_Vendor_ID = usbpd_data->SVID_1;
DATA_MSG_VDM->BITS.Object_Position = 0;
for(i=0; i<8; i++)
s2mm003_indirect_write(usbpd_data, i+0x50, data_arr[i]);
s2mm003_indirect_write(usbpd_data, 0x4E, 3);
}
static void Msg_Send_Enter_Mode(struct s2mm003_data *usbpd_data)
{
uint32_t i;
U_MSG_HEADER_Type *MSG_HEADER;
U_DATA_MSG_VDM_HEADER_Type *DATA_MSG_VDM;
uint8_t data_arr[32]={0,};
printk(KERN_ERR "[%s]\n", __func__);
MSG_HEADER = (U_MSG_HEADER_Type *)&data_arr[0];
DATA_MSG_VDM = (U_DATA_MSG_VDM_HEADER_Type *)&data_arr[4];
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_VDM_MSG_REQUEST);
for(i=0; i<8; i++)
data_arr[i] = s2mm003_indirect_read(usbpd_data, i+0x50);
MSG_HEADER->BITS.Number_of_obj = 1;
DATA_MSG_VDM->BITS.Standard_Vendor_ID = usbpd_data->SVID_1;
DATA_MSG_VDM->BITS.Object_Position = 1;
for(i=0; i<8; i++)
s2mm003_indirect_write(usbpd_data, i+0x50, data_arr[i]);
s2mm003_indirect_write(usbpd_data, 0x4E, 4);
}
#endif
static irqreturn_t s2mm003_usbpd_irq_thread(int irq, void *data)
{
struct s2mm003_data *usbpd_data = data;
struct i2c_client *i2c = usbpd_data->i2c;
unsigned char rid, plug_state_monitor;
int cc1_valid, cc2_valid, plug_attach_done;
int value, usbpd_state;
int pdic_attach = 0;
int is_dr_swap = 0;
static CC_NOTI_ATTACH_TYPEDEF attach_notifier;
static CC_NOTI_RID_TYPEDEF rid_notifier;
REQUEST_FIXED_SUPPLY_STRUCT_Typedef *request_power_number;
dev_err(&i2c->dev, "%d times\n", ++usbpd_data->wq_times);
usbpd_state = s2mm003_indirect_read(usbpd_data, IND_REG_PDIC_PD_FUNC_STATE);
dev_info(&i2c->dev, "USBPD_STATE I2C read: 0x%02d\n", usbpd_state);
plug_state_monitor = s2mm003_indirect_read(usbpd_data, IND_REG_PDIC_PLUG_STATE);
cc1_valid = S2MM003_REG_MASK(plug_state_monitor, S_CC1_VALID);
cc2_valid = S2MM003_REG_MASK(plug_state_monitor, S_CC2_VALID);
usbpd_data->plug_rprd_sel = S2MM003_REG_MASK(plug_state_monitor, PLUG_RPRD_SEL_MONITOR);
plug_attach_done = S2MM003_REG_MASK(plug_state_monitor, PLUG_ATTACH_DONE);
dev_info(&i2c->dev, "PLUG_STATE_MONITOR I2C read:%x\n"
"CC1:%x CC2:%x rprd:%x attach:%x\n",
plug_state_monitor,
cc1_valid, cc2_valid, usbpd_data->plug_rprd_sel, plug_attach_done);
/* To confirm whether PD charger is attached or not */
value = s2mm003_indirect_read(usbpd_data, IND_REG_PDIC_MAIN_INT_NUM);
dev_info(&i2c->dev, "INT_STATUS I2C read: 0x%02X\n", value);
if((value & b_RESET_START) != 0x00)
{
dev_err(&i2c->dev, "START\n");
s2mm003_indirect_write(usbpd_data, IND_REG_HOST_CMD_ON, 0x01);
return IRQ_HANDLED;
}
if( (value & b_PD_FUNC_FLAG) != 0x00) { // Need PD Function State Read
int address = IND_REG_PDIC_PD_FUNC_STATE; // Function State Number Read
int ret = 0x00;
ret = s2mm003_indirect_read(usbpd_data, address);
usbpd_data->func_state = ret; // store the function status
PDIC_Function_State_for_VBUS(usbpd_data->func_state); // For VBUS control
/* If it isn't PD charger, return value is 29*/
dev_info(&i2c->dev, " %s : func_state = %d\n", __func__, usbpd_data->func_state);
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
if(usbpd_data->func_state == FUNCTION_STATUS_INITIAL_DETACH)
{
usbpd_data->Pdic_state_machine = b_Cmd_Intial_State;
if(usbpd_data->acc_type != CCIC_DOCK_DETACHED)
{
ccic_dock_detach_notify();
usbpd_data->acc_type = CCIC_DOCK_DETACHED;
}
}
#endif
}
if( (value & b_INT_1_FLAG) != 0x00)
{
int INT_1_value = 0;
INT_1_value = s2mm003_indirect_read(usbpd_data, IND_REG_PDIC_1_INT); // Interrupt State 1 Read
dev_info(&i2c->dev, "INT_State1 = 0x%X\n", INT_1_value);
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
if(INT_1_value & b_Cmd_Discover_Identity){
Msg_Read_Discover_Identity(usbpd_data);
Msg_Send_Discover_SVIDs(usbpd_data);
}else if(INT_1_value & b_Cmd_Discover_SVIDs){
Msg_Read_Discover_SVIDs(usbpd_data);
Msg_Send_Discover_Modes(usbpd_data);
}else if(INT_1_value & b_Cmd_Discover_Modes){
Msg_Read_Discover_Modes(usbpd_data);
Msg_Send_Enter_Mode(usbpd_data);
}else if(INT_1_value & b_Cmd_Enter_Mode){
Msg_Read_Enter_Mode(usbpd_data);
}
#endif
}
if( (value & b_INT_2_FLAG) != 0x00)
{
int INT_2_value = 0;
INT_2_value = s2mm003_indirect_read(usbpd_data, IND_REG_PDIC_2_INT); // Interrupt State 2 Read
dev_info(&i2c->dev, "INT_State2 = 0x%X\n", INT_2_value);
if(INT_2_value & b_Msg_PR_SWAP)
{
dev_err(&i2c->dev, "PR_Swap Wait Delay (Any)ms\n");
// msleep(300);
s2mm003_indirect_write(usbpd_data, 0x4C, 6); // Call PS_RDY Command
VBUS_TURN_ON_CTRL(0);
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
usbpd_data->power_role = DUAL_ROLE_PROP_PR_SNK;
#endif
}
else if(INT_2_value & b_Msg_DR_SWAP)
{
dev_err(&i2c->dev, "DR_Swap\n");
is_dr_swap = 1;
// toggling the state for usb host and device
#if defined(CONFIG_CCIC_NOTIFIER)
if(usbpd_data->Pdic_usb_state == USB_STATUS_NOTIFY_ATTACH_DFP)
send_usb_notify_message(usbpd_data, USB_STATUS_NOTIFY_ATTACH_UFP);
else if(usbpd_data->Pdic_usb_state == USB_STATUS_NOTIFY_ATTACH_UFP)
send_usb_notify_message(usbpd_data, USB_STATUS_NOTIFY_ATTACH_DFP);
#endif
}
}
if((value & b_INT_3_FLAG) != 0x00)
{
int INT_3_value = 0;
INT_3_value = s2mm003_indirect_read(usbpd_data, IND_REG_PDIC_3_INT); // Interrupt State 3 Read
dev_info(&i2c->dev, "INT_State3 = 0x%X\n", INT_3_value);
if((INT_3_value & b_RID_Detect_done) != 0x00) // RID Message Detect
{
rid = s2mm003_indirect_read(usbpd_data, IND_REG_PDIC_RID); // RID Value Read
dev_info(&i2c->dev, "\nRID_Value = 0x%X\n", rid);
usbpd_data->cur_rid = rid;
if (usbpd_data->cur_rid == usbpd_data->prev_rid) {
dev_err(&i2c->dev, "same rid detected, -ignore-\n");
}
usbpd_data->p_prev_rid = usbpd_data->prev_rid;
usbpd_data->prev_rid = usbpd_data->cur_rid;
#if defined(CONFIG_CCIC_NOTIFIER)
rid_notifier.src = CCIC_NOTIFY_DEV_CCIC;
rid_notifier.dest = CCIC_NOTIFY_DEV_MUIC;
rid_notifier.id = CCIC_NOTIFY_ID_RID;
rid_notifier.rid = rid;
ccic_notifier_notify((CC_NOTI_TYPEDEF*)&rid_notifier, NULL, pdic_attach);
if(rid == RID_000K)
{
VBUS_TURN_ON_CTRL(1);
send_usb_notify_message(usbpd_data, USB_STATUS_NOTIFY_ATTACH_DFP);
}
else if(rid == RID_OPEN || rid == RID_UNDEFINED || rid == RID_523K || rid == RID_619K)
{
VBUS_TURN_ON_CTRL(0);
send_usb_notify_message(usbpd_data, USB_STATUS_NOTIFY_DETACH);
}
#endif
}
if((INT_3_value & b_Msg_SRC_CAP) != 0x00) // Read Source Capability MSG.
{
int cnt;
uint32_t ReadMSG[8];
int current_pdo_num;
u8 *p_MSG_BUF;
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_RX_SRC_CAPA);// Idx Transceiver Request
// Select PDO
if(usbpd_data->func_state == FUNCTION_STATUS_SINK_READY)
{
p_MSG_BUF = (u8 *)ReadMSG;
for(cnt = 0; cnt < 32; cnt++)
{
*(p_MSG_BUF + cnt) = s2mm003_indirect_read(usbpd_data, IND_REG_MSG_ACCESS_BASE+cnt);
}
current_pdo_num = s2mm003_src_capacity_information(i2c, ReadMSG, &pd_noti.sink_status);
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_TX_REQUEST);// Idx Transceiver Request
p_MSG_BUF = (u8 *)ReadMSG;
for(cnt = 0; cnt < MSG_BUF_REQUEST_SIZE; cnt++)
{
*(p_MSG_BUF + cnt) = s2mm003_indirect_read(usbpd_data, IND_REG_MSG_ACCESS_BASE+cnt);
}
request_power_number = (REQUEST_FIXED_SUPPLY_STRUCT_Typedef *)&p_MSG_BUF[4]; //
pr_info(" %s : Object_posision(%d),current pdo_num(%d), selected_pdo_num(%d) \n", __func__,
request_power_number->Object_Position, current_pdo_num, pd_noti.sink_status.selected_pdo_num);
if(current_pdo_num > 0) {
if((current_pdo_num != pd_noti.sink_status.selected_pdo_num) && (current_pdo_num > 0))
{
pd_noti.sink_status.selected_pdo_num = current_pdo_num;
// choice : 1, 2, 3
pr_info(" %s : PDO(%d) is selected\n", __func__, pd_noti.sink_status.selected_pdo_num);
/* Idx Transceiver Request */
s2mm003_indirect_write(usbpd_data, 0x40, pd_noti.sink_status.selected_pdo_num);
s2mm003_indirect_write(usbpd_data, 0x10, 0x11); // Function state 17 setting
} else {
pr_info(" %s : PDO(%d) is selected, but same with previous list, so skip\n",
__func__, pd_noti.sink_status.selected_pdo_num);
}
pdic_attach = 1;
} else {
pr_info(" %s : PDO is not selected\n", __func__);
}
}
}
}
if( (value & b_INT_4_FLAG) != 0x00)
{
int INT_4_value = 0;
INT_4_value = s2mm003_indirect_read(usbpd_data, IND_REG_PDIC_4_INT); // Interrupt State 4 Read
dev_info(&i2c->dev, "INT_State4 = 0x%X\n", INT_4_value);
}
if( (value & b_INT_5_FLAG) != 0x00)
{
int INT_5_value = 0;
INT_5_value = s2mm003_indirect_read(usbpd_data, IND_REG_PDIC_5_INT); // Interrupt State 5 Read
dev_info(&i2c->dev, "INT_State5 = 0x%X\n", INT_5_value);
}
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
if (usbpd_data->func_state == FUNCTION_STATUS_SRC_READY && usbpd_data->Pdic_state_machine == b_Cmd_Intial_State){
Msg_Send_Discover_Idendity(usbpd_data);
}
#endif
#if defined(CONFIG_CCIC_NOTIFIER)
attach_notifier.attach = plug_attach_done;
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
if(pdic_attach)
attach_notifier.id = CCIC_NOTIFY_ID_POWER_STATUS;
else
attach_notifier.id = CCIC_NOTIFY_ID_ATTACH;
#else
attach_notifier.id = CCIC_NOTIFY_ID_ATTACH;
#endif
attach_notifier.src = CCIC_NOTIFY_DEV_CCIC;
attach_notifier.dest = CCIC_NOTIFY_DEV_MUIC;
attach_notifier.rprd = usbpd_data->plug_rprd_sel;
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
ccic_notifier_notify((CC_NOTI_TYPEDEF*)&attach_notifier, &pd_noti, pdic_attach);
#else
ccic_notifier_notify((CC_NOTI_TYPEDEF*)&attach_notifier, NULL, pdic_attach);
#endif
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
if(usbpd_data->func_state == FUNCTION_STATUS_SRC_SEND_CAPABILITY && !is_dr_swap)
{
usbpd_data->power_role = DUAL_ROLE_PROP_PR_SRC;
send_usb_notify_message(usbpd_data, USB_STATUS_NOTIFY_ATTACH_DFP);
}
else if(usbpd_data->func_state == FUNCTION_STATUS_SINK_DISCOVERY && !is_dr_swap)
{
usbpd_data->power_role = DUAL_ROLE_PROP_PR_SNK;
send_usb_notify_message(usbpd_data, USB_STATUS_NOTIFY_ATTACH_UFP);
}
else if(usbpd_data->func_state == FUNCTION_STATUS_INITIAL_DETACH)
{
send_usb_notify_message(usbpd_data, USB_STATUS_NOTIFY_DETACH);
// OTP mode set to DRP when capble pluged out
if (!usbpd_data->try_state_change)
{
s2mm003_indirect_write(usbpd_data, IND_REG_PDIC_MODE, TYPE_C_ATTACH_DRP);
}
}
#else
if(usbpd_data->func_state == FUNCTION_STATUS_SRC_SEND_CAPABILITY && !is_dr_swap)
send_usb_notify_message(usbpd_data, USB_STATUS_NOTIFY_ATTACH_DFP);
else if(usbpd_data->func_state == FUNCTION_STATUS_SINK_DISCOVERY && !is_dr_swap)
send_usb_notify_message(usbpd_data, USB_STATUS_NOTIFY_ATTACH_UFP);
else if(usbpd_data->func_state == FUNCTION_STATUS_INITIAL_DETACH)
send_usb_notify_message(usbpd_data, USB_STATUS_NOTIFY_DETACH);
#endif
#ifndef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
if(plug_attach_done) {
/* PD notify */
if(pdic_attach)
pd_noti.event = PDIC_NOTIFY_EVENT_PD_SINK;
else
pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH;
} else {
pd_noti.sink_status.selected_pdo_num = 0;
pd_noti.event = PDIC_NOTIFY_EVENT_DETACH;
}
pdic_notifier_call(&pd_noti);
#endif
#endif
s2mm003_int_clear(usbpd_data);
return IRQ_HANDLED;
}
#if defined(CONFIG_OF)
static int of_s2mm003_usbpd_dt(struct device *dev,
struct s2mm003_data *usbpd_data)
{
struct device_node *np_usbpd = dev->of_node;
usbpd_data->irq_gpio = of_get_named_gpio(np_usbpd, "usbpd,usbpd_int", 0);
usbpd_data->redriver_en = of_get_named_gpio(np_usbpd, "usbpd,redriver_en", 0);
dev_err(dev, "usbpd_irq = %d redriver_en = %d\n",
usbpd_data->irq_gpio, usbpd_data->redriver_en);
return 0;
}
#endif /* CONFIG_OF */
#if defined(CONFIG_SEC_CCIC_FW_FIX)
void s2mm003_firmware_ver_check(struct s2mm003_data *usbpd_data, char *name)
{
struct i2c_client *i2c = usbpd_data->i2c;
usbpd_data->firm_ver[0] = (u8)s2mm003_indirect_read(usbpd_data, 0x01);
usbpd_data->firm_ver[1] = (u8)s2mm003_indirect_read(usbpd_data, 0x02);
usbpd_data->firm_ver[2] = (u8)s2mm003_indirect_read(usbpd_data, 0x03);
usbpd_data->firm_ver[3] = (u8)s2mm003_indirect_read(usbpd_data, 0x04);
dev_err(&i2c->dev, "%s %s version %02x %02x %02x %02x\n",
USBPD_DEV_NAME, name, usbpd_data->firm_ver[0], usbpd_data->firm_ver[1],
usbpd_data->firm_ver[2], usbpd_data->firm_ver[3]);
}
#endif
static int s2mm003_firmware_update(struct s2mm003_data *usbpd_data)
{
const struct firmware *fw_entry;
const unsigned char *p;
int ret, i;
struct i2c_client *i2c = usbpd_data->i2c;
u8 test, reg_check[3] = { 0, };
ret = s2mm003_read_byte(i2c, I2C_SYSREG_SET, &reg_check[0]);
ret = s2mm003_read_byte(i2c, I2C_SRAM_SET, &reg_check[1]);
ret = s2mm003_read_byte(i2c, IF_S_CODE_E, &reg_check[2]);
if ((reg_check[0] != 0) || (reg_check[1] != 0) || (reg_check[2] != 0))
{
dev_err(&usbpd_data->i2c->dev, "firmware register error %02x %02x %02x\n",
reg_check[0], reg_check[1], reg_check[2]);
s2mm003_write_byte(i2c, USB_PD_RST, 0x40); /*FULL RESET*/
msleep(20);
}
s2mm003_write_byte(i2c, I2C_SRAM_SET, 0x1); /* 32 */
s2mm003_read_byte_16(i2c, 0x1854, &test);
s2mm003_write_byte(i2c, I2C_SRAM_SET, 0x0); /* 32 */
if (test > 7 || test < 0)
dev_err(&usbpd_data->i2c->dev, "fw update err rid : %02x\n", test);
ret = request_firmware(&fw_entry,
FIRMWARE_PATH, usbpd_data->dev);
if (ret < 0)
goto done;
msleep(5);
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x1); /* 31 */
s2mm003_write_byte(i2c, OTP_DUMP_DISABLE, 0x1); /* 30 */
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x0); /* 31 */
msleep(10);
s2mm003_write_byte(i2c, I2C_SRAM_SET, 0x1); /* 32 */
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x1); /* 31 */
s2mm003_write_byte(i2c, IF_S_CODE_E, 0x4); /* sram update enable */
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x0); /* 31 */
/* update firmware */
for(p = fw_entry->data, i=0; i < fw_entry->size; p++, i++)
s2mm003_write_byte_16(i2c,(u16)i, *p);
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x1); /* 31 */
s2mm003_write_byte(i2c, IF_S_CODE_E, 0x0);
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x0); /* 31 */
s2mm003_write_byte(i2c, I2C_SRAM_SET, 0x0); /* 32 */
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x1); /* 31 */
s2mm003_write_byte(i2c, USB_PD_RST, 0x80); /* 0D */
s2mm003_write_byte(i2c, OTP_BLK_RST, 0x1); /* 0C OTP controller
system register reset
to stop otp copy */
msleep(5);
s2mm003_write_byte(i2c, USB_PD_RST, 0x80); /* 0D */
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x0); /* 31 */
done:
dev_err(&usbpd_data->i2c->dev, "firmware size: %d, error %d\n",
(int)fw_entry->size, ret);
if (fw_entry)
release_firmware(fw_entry);
return ret;
}
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
static enum dual_role_property fusb_drp_properties[] = {
DUAL_ROLE_PROP_MODE,
DUAL_ROLE_PROP_PR,
DUAL_ROLE_PROP_DR,
};
/* Callback for "cat /sys/class/dual_role_usb/otg_default/<property>" */
static int dual_role_get_local_prop(struct dual_role_phy_instance *dual_role,
enum dual_role_property prop,
unsigned int *val)
{
struct s2mm003_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->Pdic_usb_state;
power_role = usbpd_data->power_role;
pr_info("%s : request prop = %d , attached_state = %d, prop_mode =%d, prop_pr = %d \n",\
__func__, prop, attached_state,usbpd_data->Pdic_usb_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) {
if (power_role)
*val = DUAL_ROLE_PROP_PR_SNK;
else
*val = DUAL_ROLE_PROP_PR_SRC;
}
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) {
if (power_role)
*val = DUAL_ROLE_PROP_PR_SNK;
else
*val = DUAL_ROLE_PROP_PR_SRC;
}
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;
}
/* Decides whether userspace can change a specific property */
static 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;
}
int set_data_role(struct dual_role_phy_instance *dual_role,
enum dual_role_property prop,
const unsigned int *val)
{
struct s2mm003_data *usbpd_data = dual_role_get_drvdata(dual_role);
struct i2c_client *i2c = usbpd_data->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;
}
attached_state = usbpd_data->Pdic_usb_state;
pr_info("%s: request prop = %d , attached_state = %d , %d \n",__func__,\
prop, attached_state, usbpd_data->Pdic_usb_state);
if (attached_state != USB_STATUS_NOTIFY_ATTACH_DFP
&& attached_state != USB_STATUS_NOTIFY_ATTACH_UFP) {
pr_info("%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_info("%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_info("%s : current mode : %d - request mode : %d just return \n",__func__, attached_state, *val);
return 0;
}
/* Current mode DFP and Source */
if ( attached_state == USB_STATUS_NOTIFY_ATTACH_DFP)
{
pr_info("%s: try reversing, form Source to Sink\n", __func__);
disable_irq(usbpd_data->irq_gpio);
/* turns off VBUS first */
VBUS_TURN_ON_CTRL(0);
usbpd_data->power_role = 0;
/* exit from Disabled state and set mode to UFP */
mode = TYPE_C_ATTACH_UFP;
s2mm003_indirect_write(usbpd_data, IND_REG_PDIC_MODE, mode);
usbpd_data->try_state_change = TYPE_C_ATTACH_UFP;
enable_irq(usbpd_data->irq_gpio);
}
/* Current mode UFP and Sink */
else
{
pr_info("%s: try reversing, form Sink to Source\n", __func__);
/* transition to Disabled state */
disable_irq(usbpd_data->irq_gpio);
/* exit from Disabled state and set mode to UFP */
mode = TYPE_C_ATTACH_DFP;
s2mm003_indirect_write(usbpd_data, IND_REG_PDIC_MODE, mode);
usbpd_data->try_state_change = TYPE_C_ATTACH_DFP;
enable_irq(usbpd_data->irq_gpio);
}
reinit_completion(&usbpd_data->reverse_completion);
timeout =
wait_for_completion_timeout(&usbpd_data->reverse_completion,
msecs_to_jiffies
(DUAL_ROLE_SET_MODE_WAIT_MS));
// clear try_state_change
usbpd_data->try_state_change = 0;
if (!timeout) {
pr_err("%s: reverse failed, set mode to DRP\n", __func__);
disable_irq(usbpd_data->irq_gpio);
/* exit from Disabled state and set mode to DRP */
mode = TYPE_C_ATTACH_DRP;
s2mm003_indirect_write(usbpd_data, IND_REG_PDIC_MODE, mode);
usbpd_data->try_state_change = TYPE_C_ATTACH_DRP;
enable_irq(usbpd_data->irq_gpio);
pr_info("%s: wait for the attached state\n", __func__);
reinit_completion(&usbpd_data->reverse_completion);
wait_for_completion_timeout(&usbpd_data->reverse_completion,
msecs_to_jiffies
(DUAL_ROLE_SET_MODE_WAIT_MS));
usbpd_data->try_state_change = 0;
ret = -EIO;
}
dev_info(&i2c->dev, "%s -> data role : %d\n", __func__, *val);
return ret;
}
/* 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.
*/
static int dual_role_set_prop(struct dual_role_phy_instance *dual_role,
enum dual_role_property prop,
const unsigned int *val)
{
if (prop == DUAL_ROLE_PROP_MODE)
return set_data_role(dual_role, prop, val);
else
return -EINVAL;
}
#endif
static int s2mm003_usbpd_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent);
struct s2mm003_data *usbpd_data;
int ret = 0;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
struct dual_role_phy_desc *desc;
struct dual_role_phy_instance *dual_role;
#endif
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&i2c->dev, "i2c functionality check error\n");
return -EIO;
}
usbpd_data = devm_kzalloc(&i2c->dev, sizeof(struct s2mm003_data), GFP_KERNEL);
if (!usbpd_data) {
dev_err(&i2c->dev, "Failed to allocate driver data\n");
return -ENOMEM;
}
#if defined(CONFIG_OF)
if (i2c->dev.of_node)
of_s2mm003_usbpd_dt(&i2c->dev, usbpd_data);
else
dev_err(&i2c->dev, "not found ccic dt! ret:%d\n", ret);
#endif
ret = gpio_request(usbpd_data->irq_gpio, "s2mm003_irq");
if (ret)
goto err_free_irq_gpio;
ret = gpio_request(usbpd_data->redriver_en, "s2mm003_redriver_en");
if (ret)
goto err_free_redriver_gpio;
gpio_direction_input(usbpd_data->irq_gpio);
i2c->irq = gpio_to_irq(usbpd_data->irq_gpio);
dev_info(&i2c->dev, "%s:IRQ NUM %d\n", __func__, i2c->irq);
/* TODO REMOVE redriver always enable, Add sleep/resume */
ret = gpio_direction_output(usbpd_data->redriver_en, 1);
if (ret) {
dev_err(&i2c->dev, "Unable to set input gpio direction, error %d\n", ret);
goto err_free_redriver_gpio;
}
usbpd_data->i2c = i2c;
i2c_set_clientdata(i2c, usbpd_data);
dev_set_drvdata(ccic_device, usbpd_data);
device_init_wakeup(&i2c->dev, 1);
mutex_init(&usbpd_data->i2c_mutex);
/* Init */
usbpd_data->p_prev_rid = -1;
usbpd_data->prev_rid = -1;
usbpd_data->cur_rid = -1;
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
usbpd_data->Pdic_state_machine = 0;
usbpd_data->acc_type = 0;
#endif
usbpd_data->func_state = 0;
usbpd_data->Pdic_usb_state = 0;
#if defined(CONFIG_SEC_CCIC_FW_FIX)
s2mm003_firmware_ver_check(usbpd_data, "otp");
#else
usbpd_data->firm_ver[0] = s2mm003_indirect_read(usbpd_data, 0x01);
usbpd_data->firm_ver[1] = s2mm003_indirect_read(usbpd_data, 0x02);
usbpd_data->firm_ver[2] = s2mm003_indirect_read(usbpd_data, 0x03);
usbpd_data->firm_ver[3] = s2mm003_indirect_read(usbpd_data, 0x04);
dev_err(&i2c->dev, "%s otp version %02x %02x %02x %02x\n",
USBPD_DEV_NAME, usbpd_data->firm_ver[0], usbpd_data->firm_ver[1],
usbpd_data->firm_ver[2], usbpd_data->firm_ver[3]);
#endif
s2mm003_firmware_update(usbpd_data);
#if defined(CONFIG_SEC_CCIC_FW_FIX)
s2mm003_firmware_ver_check(usbpd_data, "fw");
#else
usbpd_data->firm_ver[0] = s2mm003_indirect_read(usbpd_data, 0x01);
usbpd_data->firm_ver[1] = s2mm003_indirect_read(usbpd_data, 0x02);
usbpd_data->firm_ver[2] = s2mm003_indirect_read(usbpd_data, 0x03);
usbpd_data->firm_ver[3] = s2mm003_indirect_read(usbpd_data, 0x04);
dev_err(&i2c->dev, "%s fw version %02x %02x %02x %02x\n",
USBPD_DEV_NAME, usbpd_data->firm_ver[0], usbpd_data->firm_ver[1],
usbpd_data->firm_ver[2], usbpd_data->firm_ver[3]);
#endif
Modify_TX_SRC_CAPA(usbpd_data);
ret = request_threaded_irq(i2c->irq, NULL, s2mm003_usbpd_irq_thread,
(IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND | IRQF_ONESHOT), "s2mm003-usbpd", usbpd_data);
if (ret) {
dev_err(&i2c->dev, "Failed to request IRQ %d, error %d\n", i2c->irq, ret);
goto err_init_irq;
}
dev_err(&i2c->dev, "probed, irq %d\n", usbpd_data->irq_gpio);
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
desc =
devm_kzalloc(&i2c->dev,
sizeof(struct dual_role_phy_desc), GFP_KERNEL);
if (!desc) {
pr_err("unable to allocate dual role descriptor\n");
goto err_init_irq;
}
desc->name = "otg_default";
desc->supported_modes = DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP;
desc->get_property = dual_role_get_local_prop;
desc->set_property = dual_role_set_prop;
desc->properties = fusb_drp_properties;
desc->num_properties = ARRAY_SIZE(fusb_drp_properties);
desc->property_is_writeable = dual_role_is_writeable;
dual_role =
devm_dual_role_instance_register(&i2c->dev, desc);
dual_role->drv_data = usbpd_data;
usbpd_data->dual_role = dual_role;
usbpd_data->desc = desc;
init_completion(&usbpd_data->reverse_completion);
#endif
#ifdef CONFIG_SWITCH
/* for DockObserver */
ret = switch_dev_register(&switch_dock);
if (ret < 0) {
pr_err("%s: Failed to register dock switch(%d)\n",
__func__, ret);
return -ENODEV;
}
#endif /* CONFIG_SWITCH */
return ret;
err_init_irq:
if (i2c->irq)
free_irq(i2c->irq, usbpd_data);
err_free_redriver_gpio:
gpio_free(usbpd_data->redriver_en);
err_free_irq_gpio:
gpio_free(usbpd_data->irq_gpio);
kfree(usbpd_data);
return ret;
}
static int s2mm003_usbpd_remove(struct i2c_client *i2c)
{
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
struct s2mm003_data *usbpd_data = dev_get_drvdata(ccic_device);
devm_dual_role_instance_unregister(usbpd_data->dev, usbpd_data->dual_role);
devm_kfree(usbpd_data->dev, usbpd_data->desc);
#endif
#ifdef CONFIG_SWITCH
/* for DockObserver */
switch_dev_unregister(&switch_dock);
#endif /* CONFIG_SWITCH */
return 0;
}
static const struct i2c_device_id s2mm003_usbpd_id[] = {
{ USBPD_DEV_NAME, 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, s2mm003_usbpd_id);
#if defined(CONFIG_OF)
static struct of_device_id s2mm003_i2c_dt_ids[] = {
{ .compatible = "sec-s2mm003,i2c" },
{ }
};
#endif /* CONFIG_OF */
static struct i2c_driver s2mm003_usbpd_driver = {
.driver = {
.name = USBPD_DEV_NAME,
#if defined(CONFIG_OF)
.of_match_table = s2mm003_i2c_dt_ids,
#endif /* CONFIG_OF */
},
.probe = s2mm003_usbpd_probe,
//.remove = __devexit_p(s2mm003_usbpd_remove),
.remove = s2mm003_usbpd_remove,
.id_table = s2mm003_usbpd_id,
};
static int __init s2mm003_usbpd_init(void)
{
return i2c_add_driver(&s2mm003_usbpd_driver);
}
module_init(s2mm003_usbpd_init);
static void __exit s2mm003_usbpd_exit(void)
{
i2c_del_driver(&s2mm003_usbpd_driver);
}
module_exit(s2mm003_usbpd_exit);
MODULE_DESCRIPTION("S2MM003 USB PD driver");
MODULE_LICENSE("GPL");