/* * 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 #include #include #if defined(CONFIG_BATTERY_NOTIFIER) #include #endif #if defined(CONFIG_DUAL_ROLE_USB_INTF) #include #endif /* switch device header */ #ifdef CONFIG_SWITCH #include #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, ®_check[0]); ret = s2mm003_read_byte(i2c, I2C_SRAM_SET, ®_check[1]); ret = s2mm003_read_byte(i2c, IF_S_CODE_E, ®_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/" */ 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 > * /sys/class/dual_role_usb//" * 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");