| /* |
| * 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/version.h> |
| #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/err.h> |
| #include <linux/mod_devicetable.h> |
| #include <linux/power_supply.h> |
| #include <linux/of.h> |
| #include <linux/irq.h> |
| #include <linux/gpio.h> |
| #include <linux/fs.h> |
| #include <linux/uaccess.h> |
| #include <linux/types.h> |
| #include <linux/device.h> |
| #include <linux/sysfs.h> |
| #include <linux/uaccess.h> |
| #include <linux/workqueue.h> |
| #include <linux/spinlock.h> |
| #include <linux/kthread.h> |
| #include <linux/sched.h> |
| #include <linux/time.h> |
| #include <linux/timer.h> |
| #include <linux/file.h> |
| #include <linux/syscalls.h> |
| #include <linux/sort.h> |
| #ifdef CONFIG_OF |
| #include <linux/of_gpio.h> |
| #endif |
| #include <linux/mfd/core.h> |
| #include <linux/mfd/max77705.h> |
| #include <linux/mfd/max77705-private.h> |
| #include <linux/ccic/max77705_usbc.h> |
| #include <linux/ccic/max77705_alternate.h> |
| #if defined(CONFIG_USB_HOST_NOTIFY) |
| #include <linux/usb_notify.h> |
| #endif |
| #if defined(CONFIG_CCIC_MAX77705_DEBUG) |
| #include <linux/ccic/max77705_debug.h> |
| #endif |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| #include <linux/ccic/ccic_sysfs.h> |
| #include <linux/ccic/ccic_core.h> |
| #include <linux/ccic/ccic_notifier.h> |
| #if defined(CONFIG_DUAL_ROLE_USB_INTF) |
| #include <linux/usb/class-dual-role.h> |
| #elif defined(CONFIG_TYPEC) |
| #include <linux/usb/typec.h> |
| #endif |
| #ifdef MAX77705_SYS_FW_UPDATE |
| #include <linux/spu-verify.h> |
| #endif |
| |
| #include "../battery_v2/include/sec_charging_common.h" |
| |
| static enum ccic_sysfs_property max77705_sysfs_properties[] = { |
| CCIC_SYSFS_PROP_CHIP_NAME, |
| CCIC_SYSFS_PROP_CUR_VERSION, |
| CCIC_SYSFS_PROP_SRC_VERSION, |
| CCIC_SYSFS_PROP_LPM_MODE, |
| CCIC_SYSFS_PROP_STATE, |
| CCIC_SYSFS_PROP_RID, |
| CCIC_SYSFS_PROP_CTRL_OPTION, |
| CCIC_SYSFS_PROP_BOOTING_DRY, |
| CCIC_SYSFS_PROP_FW_UPDATE, |
| CCIC_SYSFS_PROP_FW_UPDATE_STATUS, |
| CCIC_SYSFS_PROP_FW_WATER, |
| CCIC_SYSFS_PROP_DEX_FAN_UVDM, |
| CCIC_SYSFS_PROP_ACC_DEVICE_VERSION, |
| CCIC_SYSFS_PROP_DEBUG_OPCODE, |
| CCIC_SYSFS_PROP_CONTROL_GPIO, |
| CCIC_SYSFS_PROP_USBPD_IDS, |
| CCIC_SYSFS_PROP_USBPD_TYPE, |
| CCIC_SYSFS_PROP_CC_PIN_STATUS, |
| CCIC_SYSFS_PROP_RAM_TEST, |
| CCIC_SYSFS_PROP_SBU_ADC, |
| CCIC_SYSFS_PROP_VSAFE0V_STATUS, |
| CCIC_SYSFS_PROP_MAX_COUNT, |
| }; |
| #endif /* CONFIG_CCIC_NOTIFIER */ |
| #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, |
| DUAL_ROLE_PROP_VCONN_SUPPLY, |
| }; |
| #endif |
| |
| #define DRIVER_VER "1.2VER" |
| |
| #define MAX77705_MAX_APDCMD_TIME (10*HZ) |
| |
| #define MAX77705_PMIC_REG_INTSRC_MASK 0x23 |
| #define MAX77705_PMIC_REG_INTSRC 0x22 |
| |
| #define MAX77705_IRQSRC_CHG (1 << 0) |
| #define MAX77705_IRQSRC_FG (1 << 2) |
| #define MAX77705_IRQSRC_MUIC (1 << 3) |
| |
| #define MAX77705_RAM_TEST |
| |
| #ifdef MAX77705_RAM_TEST |
| #define MAX77705_RAM_TEST_RETRY_COUNT 1 |
| #define MAX77705_RAM_TEST_SUCCESS 0xA1 |
| #define MAX77705_RAM_TEST_FAIL 0x51 |
| |
| enum MAX77705_RAM_TEST_MODE { |
| MAX77705_RAM_TEST_STOP_MODE, |
| MAX77705_RAM_TEST_START_MODE, |
| MAX77705_RAM_TEST_RETRY_MODE, |
| }; |
| |
| enum MAX77705_RAM_TEST_RESULT { |
| MAX77705_RAM_TEST_RESULT_SUCCESS, |
| MAX77705_RAM_TEST_RESULT_FAIL_USBC_FUELGAUAGE, |
| MAX77705_RAM_TEST_RESULT_FAIL_USBC, |
| MAX77705_RAM_TEST_RESULT_FAIL_FUELGAUAGE, |
| }; |
| #endif |
| |
| struct max77705_usbc_platform_data *g_usbc_data; |
| |
| #ifdef MAX77705_SYS_FW_UPDATE |
| #define MAXIM_DEFAULT_FW "/sdcard/Firmware/usbpd/secure_max77705.bin" |
| #define MAXIM_SPU_FW "/spu/pdic/max77705.bin" |
| static void max77705_usbc_mask_irq(struct max77705_usbc_platform_data *usbc_data); |
| static void max77705_usbc_umask_irq(struct max77705_usbc_platform_data *usbc_data); |
| static void max77705_get_version_info(struct max77705_usbc_platform_data *usbc_data); |
| |
| struct pdic_fw_update { |
| char id[10]; |
| char path[50]; |
| uint fwsize_offset; |
| int enforce_do; |
| }; |
| |
| #ifdef CONFIG_MAX77705_GRL_ENABLE |
| static int max77705_i2c_master_write(struct max77705_usbc_platform_data *usbpd_data, |
| int slave_addr, u8 *reg_addr) |
| { |
| int err; |
| int tries = 0; |
| u8 buffer[2] = { reg_addr[0], reg_addr[1]}; |
| |
| struct i2c_msg msgs[] = { |
| { |
| .addr = slave_addr, |
| .flags = usbpd_data->muic->flags & I2C_M_TEN, |
| .len = 2, |
| .buf = buffer, |
| }, |
| }; |
| |
| do { |
| err = i2c_transfer(usbpd_data->muic->adapter, msgs, 1); |
| if (err < 0) |
| msg_maxim("i2c_transfer error:%d, addr : %x ,data : %x\n", err, reg_addr[0], reg_addr[1]); |
| } while ((err != 1) && (++tries < 20)); |
| |
| if (err != 1) { |
| msg_maxim("write transfer error:%d, addr : %x ,data : %x\n", err, reg_addr[0], reg_addr[1]); |
| err = -EIO; |
| return err; |
| } |
| |
| return 1; |
| } |
| #endif |
| |
| #ifdef MAX77705_RAM_TEST |
| static void max77705_verify_ram_bist_write(struct max77705_usbc_platform_data *usbc_data) |
| { |
| usbc_cmd_data write_data; |
| u8 irq_reg[MAX77705_IRQ_GROUP_NR] = {0}; |
| write_data.opcode = OPCODE_RAM_TEST_COMMAND; |
| write_data.write_data[0] = 0x0; |
| write_data.write_length = 0x1; |
| write_data.read_length = 0x6; |
| write_data.is_uvdm = 0x0; |
| /* clear all interrpts */ |
| max77705_bulk_read(usbc_data->muic, MAX77705_USBC_REG_UIC_INT, |
| 4, &irq_reg[USBC_INT]); |
| msg_maxim("[MAX77705] irq_reg, %x, %x, %x, %x", irq_reg[USBC_INT], irq_reg[CC_INT], irq_reg[PD_INT], irq_reg[VDM_INT]); |
| max77705_write_reg(usbc_data->muic, REG_UIC_INT_M, 0x3F); |
| max77705_write_reg(usbc_data->muic, REG_CC_INT_M, 0xFF); |
| max77705_write_reg(usbc_data->muic, REG_PD_INT_M, 0xFF); |
| max77705_write_reg(usbc_data->muic, REG_VDM_INT_M, 0xFF); |
| |
| max77705_usbc_opcode_write(usbc_data, &write_data); |
| if(usbc_data->ram_test_enable == MAX77705_RAM_TEST_STOP_MODE) { |
| usbc_data->ram_test_enable = MAX77705_RAM_TEST_START_MODE; |
| usbc_data->ram_test_retry = 0x0; |
| } |
| } |
| #endif |
| |
| int max77705_current_pr_state(struct max77705_usbc_platform_data *usbc_data) |
| { |
| int current_pr = usbc_data->cc_data->current_pr; |
| return current_pr; |
| |
| } |
| |
| void blocking_auto_vbus_control(int enable) |
| { |
| int current_pr = 0; |
| |
| msg_maxim("disable : %d", enable); |
| |
| if (enable) { |
| current_pr = max77705_current_pr_state(g_usbc_data); |
| switch (current_pr) { |
| case SRC: |
| /* turn off the vbus */ |
| max77705_vbus_turn_on_ctrl(g_usbc_data, OFF, false); |
| break; |
| default: |
| break; |
| } |
| g_usbc_data->mpsm_mode = MPSM_ON; |
| } else { |
| current_pr = max77705_current_pr_state(g_usbc_data); |
| switch (current_pr) { |
| case SRC: |
| max77705_vbus_turn_on_ctrl(g_usbc_data, ON, false); |
| break; |
| default: |
| break; |
| |
| } |
| g_usbc_data->mpsm_mode = MPSM_OFF; |
| } |
| msg_maxim("current_pr : %x disable : %x", current_pr, enable); |
| } |
| EXPORT_SYMBOL(blocking_auto_vbus_control); |
| |
| static void vbus_control_hard_reset(struct work_struct *work) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = g_usbc_data; |
| |
| msg_maxim("current_pr=%d", usbpd_data->cc_data->current_pr); |
| |
| if (usbpd_data->cc_data->current_pr == SRC) |
| max77705_vbus_turn_on_ctrl(usbpd_data, ON, false); |
| } |
| |
| void max77705_usbc_enable_auto_vbus(struct max77705_usbc_platform_data *usbc_data) |
| { |
| usbc_cmd_data write_data; |
| |
| init_usbc_cmd_data(&write_data); |
| write_data.opcode = OPCODE_SAMSUNG_FACTORY_TEST; |
| write_data.write_data[0] = 0x2; |
| write_data.write_length = 0x1; |
| write_data.read_length = 0x1; |
| max77705_usbc_opcode_write(usbc_data, &write_data); |
| msg_maxim("TURN ON THE AUTO VBUS"); |
| usbc_data->auto_vbus_en = true; |
| } |
| |
| void max77705_usbc_disable_auto_vbus(struct max77705_usbc_platform_data *usbc_data) |
| { |
| usbc_cmd_data write_data; |
| |
| init_usbc_cmd_data(&write_data); |
| write_data.opcode = OPCODE_SAMSUNG_FACTORY_TEST; |
| write_data.write_data[0] = 0x0; |
| write_data.write_length = 0x1; |
| write_data.read_length = 0x1; |
| max77705_usbc_opcode_write(usbc_data, &write_data); |
| msg_maxim("TURN OFF THE AUTO VBUS"); |
| usbc_data->auto_vbus_en = false; |
| } |
| |
| void max77705_usbc_enable_audio(struct max77705_usbc_platform_data *usbc_data) |
| { |
| usbc_cmd_data write_data; |
| |
| /* we need new function for BIT_CCDbgEn */ |
| usbc_data->op_ctrl1_w |= (BIT_CCDbgEn | BIT_CCAudEn); |
| |
| init_usbc_cmd_data(&write_data); |
| write_data.opcode = OPCODE_CCCTRL1_W; |
| write_data.write_data[0] = usbc_data->op_ctrl1_w; |
| write_data.write_length = 0x1; |
| write_data.read_length = 0x1; |
| max77705_usbc_opcode_write(usbc_data, &write_data); |
| msg_maxim("Enable Audio Detect"); |
| } |
| |
| static void max77705_usbc_debug_function(struct max77705_usbc_platform_data *usbc_data) |
| { |
| usbc_cmd_data write_data; |
| |
| msg_maxim("called"); |
| |
| init_usbc_cmd_data(&write_data); |
| write_data.opcode = 0x74; |
| write_data.write_data[0] = 0x0; |
| write_data.write_length = 0x1; |
| write_data.read_length = 0xA; |
| max77705_usbc_opcode_write(usbc_data, &write_data); |
| } |
| |
| #ifdef CONFIG_MAX77705_GRL_ENABLE |
| static void max77705_set_forcetrimi(struct max77705_usbc_platform_data *usbc_data) |
| { |
| u8 ArrSendData[2] = {0x00, 0x00}; |
| |
| msg_maxim("IN++"); |
| mutex_lock(&usbc_data->max77705->i2c_lock); |
| // ArrSendData[0] = 0xFE; |
| // ArrSendData[1] = 0xC5; |
| // max77705_i2c_master_write(usbc_data, 0x66, ArrSendData); |
| // ArrSendData[0] = 0xb3; |
| // ArrSendData[1] = 0x0c; |
| // max77705_i2c_master_write(usbc_data, 0x62, ArrSendData); |
| ArrSendData[0] = 0x1F; |
| ArrSendData[1] = 0x04; |
| max77705_i2c_master_write(usbc_data, 0x62, ArrSendData); |
| msleep(100); |
| mutex_unlock(&usbc_data->max77705->i2c_lock); |
| msg_maxim("OUT"); |
| } |
| #endif |
| |
| static void max77705_send_role_swap_message(struct max77705_usbc_platform_data *usbpd_data, u8 mode) |
| { |
| usbc_cmd_data write_data; |
| |
| max77705_usbc_clear_queue(usbpd_data); |
| init_usbc_cmd_data(&write_data); |
| write_data.opcode = 0x37; |
| /* 0x1 : DR_SWAP, 0x2 : PR_SWAP, 0x4: Manual Role Swap */ |
| 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_rprd_mode_change(struct max77705_usbc_platform_data *usbpd_data, u8 mode) |
| { |
| msg_maxim("mode = 0x%x", mode); |
| |
| switch (mode) { |
| case TYPE_C_ATTACH_DFP: |
| case TYPE_C_ATTACH_UFP: |
| max77705_send_role_swap_message(usbpd_data, MANUAL_ROLE_SWAP); |
| msleep(1000); |
| break; |
| default: |
| break; |
| }; |
| } |
| |
| void max77705_power_role_change(struct max77705_usbc_platform_data *usbpd_data, int power_role) |
| { |
| msg_maxim("power_role = 0x%x", power_role); |
| |
| switch (power_role) { |
| case TYPE_C_ATTACH_SRC: |
| case TYPE_C_ATTACH_SNK: |
| max77705_send_role_swap_message(usbpd_data, POWER_ROLE_SWAP); |
| break; |
| }; |
| } |
| |
| void max77705_data_role_change(struct max77705_usbc_platform_data *usbpd_data, int data_role) |
| { |
| msg_maxim("data_role = 0x%x", data_role); |
| |
| switch (data_role) { |
| case TYPE_C_ATTACH_DFP: |
| case TYPE_C_ATTACH_UFP: |
| max77705_send_role_swap_message(usbpd_data, DATA_ROLE_SWAP); |
| break; |
| }; |
| } |
| |
| #if defined(CONFIG_DUAL_ROLE_USB_INTF) |
| static int max77705_ccic_set_dual_role(struct dual_role_phy_instance *dual_role, |
| enum dual_role_property prop, |
| const unsigned int *val) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = dual_role_get_drvdata(dual_role); |
| USB_STATUS attached_state; |
| int timeout = 0; |
| |
| if (!usbpd_data) |
| return -EINVAL; |
| |
| attached_state = usbpd_data->data_role; |
| |
| if (prop == DUAL_ROLE_PROP_MODE) { |
| if (attached_state != USB_STATUS_NOTIFY_ATTACH_DFP |
| && attached_state != USB_STATUS_NOTIFY_ATTACH_UFP) |
| return -EPERM; |
| if (attached_state == USB_STATUS_NOTIFY_ATTACH_DFP |
| && *val == DUAL_ROLE_PROP_MODE_DFP) |
| return -EPERM; |
| if (attached_state == USB_STATUS_NOTIFY_ATTACH_UFP |
| && *val == DUAL_ROLE_PROP_MODE_UFP) |
| return -EPERM; |
| } else if (prop == DUAL_ROLE_PROP_DR) |
| return -EPERM; |
| |
| reinit_completion(&usbpd_data->reverse_completion); |
| |
| if (attached_state == USB_STATUS_NOTIFY_ATTACH_DFP) { |
| msg_maxim("try reversing, from DFP(Source) to UFP(Sink)"); |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| max77705_ccic_event_work(usbpd_data, |
| CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, |
| 0/*attach*/, 0/*rprd*/, 0); |
| #endif |
| usbpd_data->try_state_change = TYPE_C_ATTACH_UFP; |
| max77705_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_UFP); |
| } else { |
| msg_maxim("try reversing, from UFP(Sink) to DFP(Source)"); |
| usbpd_data->try_state_change = TYPE_C_ATTACH_DFP; |
| max77705_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DFP); |
| } |
| |
| timeout = wait_for_completion_timeout(&usbpd_data->reverse_completion, |
| msecs_to_jiffies(DUAL_ROLE_SET_MODE_WAIT_MS)); |
| |
| if (!timeout && usbpd_data->try_state_change) { |
| msg_maxim("reverse failed to change the role"); |
| return -EIO; |
| } else |
| msg_maxim("reverse success"); |
| |
| return 0; |
| } |
| |
| static int max77705_ccic_set_power_role(struct dual_role_phy_instance *dual_role, |
| enum dual_role_property prop, |
| const unsigned int *val) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = dual_role_get_drvdata(dual_role); |
| int power_role = DUAL_ROLE_PROP_PR_NONE; |
| USB_STATUS attached_state = USB_STATUS_NOTIFY_DETACH; |
| |
| if (!usbpd_data) |
| return -EINVAL; |
| |
| power_role = usbpd_data->power_role; |
| attached_state = usbpd_data->data_role; |
| msg_maxim("power_role=%d, *val=%d, attached_state=%d", power_role, *val, attached_state); |
| |
| if (power_role != DUAL_ROLE_PROP_PR_SRC |
| && power_role != DUAL_ROLE_PROP_PR_SNK) |
| return -EPERM; |
| if (power_role == DUAL_ROLE_PROP_PR_SRC |
| && *val == DUAL_ROLE_PROP_PR_SRC) |
| return -EPERM; |
| if (power_role == DUAL_ROLE_PROP_PR_SNK |
| && *val == DUAL_ROLE_PROP_PR_SNK) |
| return -EPERM; |
| |
| if (power_role == DUAL_ROLE_PROP_PR_SRC && attached_state == USB_STATUS_NOTIFY_ATTACH_DFP) |
| return max77705_ccic_set_dual_role(dual_role, prop, val); |
| else if (power_role == DUAL_ROLE_PROP_PR_SNK && attached_state == USB_STATUS_NOTIFY_ATTACH_UFP) |
| return max77705_ccic_set_dual_role(dual_role, prop, val); |
| |
| if (power_role == DUAL_ROLE_PROP_PR_SRC) { |
| msg_maxim("try reversing, from Source to Sink"); |
| usbpd_data->try_state_change = TYPE_C_ATTACH_SNK; |
| max77705_power_role_change(usbpd_data, TYPE_C_ATTACH_SNK); |
| } else if (power_role == DUAL_ROLE_PROP_PR_SNK) { |
| msg_maxim("try reversing, from Sink to Source"); |
| usbpd_data->try_state_change = TYPE_C_ATTACH_SRC; |
| max77705_power_role_change(usbpd_data, TYPE_C_ATTACH_SRC); |
| } |
| |
| return 0; |
| } |
| |
| static int max77705_ccic_set_data_role(struct dual_role_phy_instance *dual_role, |
| enum dual_role_property prop, |
| const unsigned int *val) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = dual_role_get_drvdata(dual_role); |
| USB_STATUS attached_state; |
| |
| if (!usbpd_data) |
| return -EINVAL; |
| |
| attached_state = usbpd_data->data_role; |
| msg_maxim("attached_state=%d, *val=%d", attached_state, *val); |
| |
| if (attached_state != USB_STATUS_NOTIFY_ATTACH_DFP |
| && attached_state != USB_STATUS_NOTIFY_ATTACH_UFP) |
| return -EPERM; |
| if (attached_state == USB_STATUS_NOTIFY_ATTACH_DFP |
| && *val == DUAL_ROLE_PROP_DR_HOST) |
| return -EPERM; |
| if (attached_state == USB_STATUS_NOTIFY_ATTACH_UFP |
| && *val == DUAL_ROLE_PROP_DR_DEVICE) |
| return -EPERM; |
| |
| if (attached_state == USB_STATUS_NOTIFY_ATTACH_DFP) { |
| msg_maxim("try reversing, from DFP to UFP"); |
| usbpd_data->try_state_change = TYPE_C_ATTACH_UFP; |
| max77705_data_role_change(usbpd_data, TYPE_C_ATTACH_UFP); |
| } else if (attached_state == USB_STATUS_NOTIFY_ATTACH_UFP) { |
| msg_maxim("try reversing, from UFP to DFP"); |
| usbpd_data->try_state_change = TYPE_C_ATTACH_DFP; |
| max77705_data_role_change(usbpd_data, TYPE_C_ATTACH_DFP); |
| } |
| |
| return 0; |
| } |
| |
| /* Decides whether userspace can change a specific property */ |
| int max77705_dual_role_is_writeable(struct dual_role_phy_instance *drp, |
| enum dual_role_property prop) |
| { |
| if (prop == DUAL_ROLE_PROP_MODE) |
| return 1; |
| else |
| return 0; |
| } |
| |
| /* Callback for "cat /sys/class/dual_role_usb/otg_default/<property>" */ |
| int max77705_dual_role_get_prop(struct dual_role_phy_instance *dual_role, |
| enum dual_role_property prop, |
| unsigned int *val) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = dual_role_get_drvdata(dual_role); |
| |
| USB_STATUS attached_state; |
| int power_role; |
| |
| if (!usbpd_data) |
| return -EINVAL; |
| |
| attached_state = usbpd_data->data_role; |
| power_role = usbpd_data->power_role; |
| |
| msg_maxim("request prop = %d , attached_state = %d, power_role = %d", |
| prop, attached_state, power_role); |
| |
| if (prop == DUAL_ROLE_PROP_VCONN_SUPPLY) { |
| if (usbpd_data->vconn_en) |
| *val = DUAL_ROLE_PROP_VCONN_SUPPLY_YES; |
| else |
| *val = DUAL_ROLE_PROP_VCONN_SUPPLY_NO; |
| return 0; |
| } |
| |
| if (attached_state == USB_STATUS_NOTIFY_ATTACH_DFP) { |
| if (prop == DUAL_ROLE_PROP_MODE) |
| *val = DUAL_ROLE_PROP_MODE_DFP; |
| else if (prop == DUAL_ROLE_PROP_PR) |
| *val = power_role; |
| else if (prop == DUAL_ROLE_PROP_DR) |
| *val = DUAL_ROLE_PROP_DR_HOST; |
| else |
| return -EINVAL; |
| } else if (attached_state == USB_STATUS_NOTIFY_ATTACH_UFP) { |
| if (prop == DUAL_ROLE_PROP_MODE) |
| *val = DUAL_ROLE_PROP_MODE_UFP; |
| else if (prop == DUAL_ROLE_PROP_PR) |
| *val = power_role; |
| else if (prop == DUAL_ROLE_PROP_DR) |
| *val = DUAL_ROLE_PROP_DR_DEVICE; |
| else |
| return -EINVAL; |
| } else { |
| if (prop == DUAL_ROLE_PROP_MODE) |
| *val = DUAL_ROLE_PROP_MODE_NONE; |
| else if (prop == DUAL_ROLE_PROP_PR) |
| *val = DUAL_ROLE_PROP_PR_NONE; |
| else if (prop == DUAL_ROLE_PROP_DR) |
| *val = DUAL_ROLE_PROP_DR_NONE; |
| else |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /* Callback for "echo <value> > |
| * /sys/class/dual_role_usb/<name>/<property>" |
| * Block until the entire final state is reached. |
| * Blocking is one of the better ways to signal when the operation |
| * is done. |
| * This function tries to switch to Attached.SRC or Attached.SNK |
| * by forcing the mode into SRC or SNK. |
| * On failure, we fall back to Try.SNK state machine. |
| */ |
| int max77705_dual_role_set_prop(struct dual_role_phy_instance *dual_role, |
| enum dual_role_property prop, |
| const unsigned int *val) |
| { |
| msg_maxim("request prop = %d , *val = %d", prop, *val); |
| |
| if (prop == DUAL_ROLE_PROP_MODE) |
| return max77705_ccic_set_dual_role(dual_role, prop, val); |
| else if (prop == DUAL_ROLE_PROP_PR) |
| return max77705_ccic_set_power_role(dual_role, prop, val); |
| else if (prop == DUAL_ROLE_PROP_DR) |
| return max77705_ccic_set_data_role(dual_role, prop, val); |
| else |
| return -EINVAL; |
| } |
| #elif defined(CONFIG_TYPEC) |
| static int max77705_dr_set(const struct typec_capability *cap, enum typec_data_role role) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = container_of(cap, struct max77705_usbc_platform_data, typec_cap); |
| |
| msg_maxim("typec_power_role=%d, typec_data_role=%d, role=%d", |
| usbpd_data->typec_power_role, usbpd_data->typec_data_role, role); |
| |
| if (usbpd_data->typec_data_role != TYPEC_DEVICE |
| && usbpd_data->typec_data_role != TYPEC_HOST) |
| return -EPERM; |
| else if (usbpd_data->typec_data_role == role) |
| return -EPERM; |
| |
| reinit_completion(&usbpd_data->typec_reverse_completion); |
| if (role == TYPEC_DEVICE) { |
| msg_maxim("try reversing, from DFP to UFP"); |
| usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_DR; |
| max77705_data_role_change(usbpd_data, TYPE_C_ATTACH_UFP); |
| } else if (role == TYPEC_HOST) { |
| msg_maxim("try reversing, from UFP to DFP"); |
| usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_DR; |
| max77705_data_role_change(usbpd_data, TYPE_C_ATTACH_DFP); |
| } else { |
| msg_maxim("invalid typec_role"); |
| return -EIO; |
| } |
| if (!wait_for_completion_timeout(&usbpd_data->typec_reverse_completion, |
| msecs_to_jiffies(TRY_ROLE_SWAP_WAIT_MS))) { |
| usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE; |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| |
| static int max77705_pr_set(const struct typec_capability *cap, enum typec_role role) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = container_of(cap, struct max77705_usbc_platform_data, typec_cap); |
| |
| msg_maxim("typec_power_role=%d, typec_data_role=%d, role=%d", |
| usbpd_data->typec_power_role, usbpd_data->typec_data_role, role); |
| |
| if (usbpd_data->typec_power_role != TYPEC_SINK |
| && usbpd_data->typec_power_role != TYPEC_SOURCE) |
| return -EPERM; |
| else if (usbpd_data->typec_power_role == role) |
| return -EPERM; |
| |
| reinit_completion(&usbpd_data->typec_reverse_completion); |
| if (role == TYPEC_SINK) { |
| msg_maxim("try reversing, from Source to Sink"); |
| usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_PR; |
| max77705_power_role_change(usbpd_data, TYPE_C_ATTACH_SNK); |
| } else if (role == TYPEC_SOURCE) { |
| msg_maxim("try reversing, from Sink to Source"); |
| usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_PR; |
| max77705_power_role_change(usbpd_data, TYPE_C_ATTACH_SRC); |
| } else { |
| msg_maxim("invalid typec_role"); |
| return -EIO; |
| } |
| if (!wait_for_completion_timeout(&usbpd_data->typec_reverse_completion, |
| msecs_to_jiffies(TRY_ROLE_SWAP_WAIT_MS))) { |
| usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE; |
| if (usbpd_data->typec_power_role != role) |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| |
| static int max77705_port_type_set(const struct typec_capability *cap, enum typec_port_type port_type) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = container_of(cap, struct max77705_usbc_platform_data, typec_cap); |
| |
| msg_maxim("typec_power_role=%d, typec_data_role=%d, port_type=%d", |
| usbpd_data->typec_power_role, usbpd_data->typec_data_role, port_type); |
| |
| reinit_completion(&usbpd_data->typec_reverse_completion); |
| if (port_type == TYPEC_PORT_DFP) { |
| msg_maxim("try reversing, from UFP(Sink) to DFP(Source)"); |
| usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_TYPE; |
| max77705_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_DFP); |
| } else if (port_type == TYPEC_PORT_UFP) { |
| msg_maxim("try reversing, from DFP(Source) to UFP(Sink)"); |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| max77705_ccic_event_work(usbpd_data, |
| CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, |
| 0/*attach*/, 0/*rprd*/, 0); |
| #endif |
| usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_TYPE; |
| max77705_rprd_mode_change(usbpd_data, TYPE_C_ATTACH_UFP); |
| } else { |
| msg_maxim("invalid typec_role"); |
| return 0; |
| } |
| |
| if (!wait_for_completion_timeout(&usbpd_data->typec_reverse_completion, |
| msecs_to_jiffies(TRY_ROLE_SWAP_WAIT_MS))) { |
| usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE; |
| return -ETIMEDOUT; |
| } |
| return 0; |
| } |
| |
| int max77705_get_pd_support(struct max77705_usbc_platform_data *usbc_data) |
| { |
| bool support_pd_role_swap = false; |
| struct device_node *np = NULL; |
| |
| np = of_find_compatible_node(NULL, NULL, "maxim,max77705"); |
| |
| if (np) |
| support_pd_role_swap = of_property_read_bool(np, "support_pd_role_swap"); |
| else |
| msg_maxim("np is null"); |
| |
| msg_maxim("TYPEC_CLASS: support_pd_role_swap is %d, usbc_data->pd_support : %d", |
| support_pd_role_swap, usbc_data->pd_support); |
| |
| if (support_pd_role_swap && usbc_data->pd_support) |
| return TYPEC_PWR_MODE_PD; |
| |
| return usbc_data->pwr_opmode; |
| } |
| #endif |
| |
| static int max77705_firmware_update_sys(struct max77705_usbc_platform_data *data, int fw_dir) |
| { |
| struct max77705_usbc_platform_data *usbc_data = data; |
| unsigned char *fw_data; |
| max77705_fw_header *fw_header; |
| struct file *fp; |
| mm_segment_t old_fs; |
| long fw_size, nread; |
| int ret = 0; |
| const u8 *fw_bin; |
| int fw_bin_len; |
| u8 pmic_rev = 0;/* pmic Rev */ |
| u8 fw_enable = 0; |
| struct pdic_fw_update fwup[3] = { |
| {"BUILT_IN", "", 0, 1}, |
| {"UMS", MAXIM_DEFAULT_FW, 0, 1}, |
| {"SPU", MAXIM_SPU_FW, SPU_METADATA_SIZE(PDIC), 0} |
| }; |
| |
| if (!usbc_data) { |
| msg_maxim("usbc_data is null!!"); |
| return -ENODEV; |
| } |
| |
| switch (fw_dir) { |
| case UMS: |
| case SPU: |
| pmic_rev = usbc_data->max77705->pmic_rev; |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| break; |
| case BUILT_IN: |
| max77705_usbc_fw_setting(usbc_data->max77705, fwup[fw_dir].enforce_do); |
| return 0; |
| default: |
| return -EINVAL; |
| } |
| |
| fp = filp_open(fwup[fw_dir].path, O_RDONLY, S_IRUSR); |
| if (IS_ERR(fp)) { |
| msg_maxim("failed to open fw file."); |
| ret = PTR_ERR(fp); |
| set_fs(old_fs); |
| return ret; |
| } |
| |
| fw_size = fp->f_path.dentry->d_inode->i_size; |
| if (fw_size > 0) { |
| fw_data = kzalloc(fw_size, GFP_KERNEL); |
| if (!fw_data) { |
| msg_maxim("Failed to allocate memory"); |
| ret = -ENOMEM; |
| goto out; |
| } |
| nread = vfs_read(fp, (char __user *)fw_data, fw_size, &fp->f_pos); |
| |
| msg_maxim("start, file path %d, size %ld Bytes", |
| fw_dir, fw_size); |
| |
| if (nread != fw_size) { |
| msg_maxim("failed to read firmware file, nread %ld Bytes", |
| nread); |
| ret = -EIO; |
| } else { |
| fw_bin_len = fw_size - fwup[fw_dir].fwsize_offset; |
| if (fw_dir == SPU) { |
| ret = spu_firmware_signature_verify( "PDIC" , fw_data, fw_size); |
| if (ret != fw_bin_len) |
| goto out; |
| } |
| |
| fw_bin = fw_data; |
| fw_header = (max77705_fw_header *)fw_bin; |
| max77705_read_reg(usbc_data->muic, |
| REG_UIC_FW_REV, &usbc_data->FW_Revision); |
| max77705_read_reg(usbc_data->muic, |
| REG_UIC_FW_MINOR, &usbc_data->FW_Minor_Revision); |
| usbc_data->FW_Minor_Revision &= MINOR_VERSION_MASK; |
| msg_maxim("chip %02X.%02X, fw %02X.%02X", |
| usbc_data->FW_Revision, usbc_data->FW_Minor_Revision, |
| fw_header->major, fw_header->minor); |
| switch (pmic_rev) { |
| case MAX77705_PASS4: |
| case MAX77705_PASS5: |
| fw_enable = 1; |
| break; |
| default: |
| msg_maxim("FAILED F/W via SYS and PMIC_REVISION isn't valid"); |
| break; |
| }; |
| |
| if (fw_enable) |
| ret = max77705_usbc_fw_update(usbc_data->max77705, fw_bin, fw_bin_len, fwup[fw_dir].enforce_do); |
| else |
| msg_maxim("FAILED F/W MISMATCH pmic_rev : 0x%x, fw_header->major : 0x%x", |
| pmic_rev, fw_header->major); |
| } |
| } |
| |
| out: |
| if (fw_data) |
| kfree(fw_data); |
| filp_close(fp, NULL); |
| set_fs(old_fs); |
| return ret; |
| } |
| |
| #endif |
| |
| void max77705_manual_jig_on(struct max77705_usbc_platform_data *usbpd_data, int mode) |
| { |
| usbc_cmd_data read_data; |
| usbc_cmd_data write_data; |
| |
| msg_maxim("usb: mode=%s", mode ? "High" : "Low"); |
| |
| init_usbc_cmd_data(&read_data); |
| init_usbc_cmd_data(&write_data); |
| read_data.opcode = OPCODE_CTRL3_R; |
| read_data.write_length = 0x0; |
| read_data.read_length = 0x1; |
| write_data.opcode = OPCODE_CTRL3_W; |
| if (mode) |
| write_data.write_data[0] = 0x1; |
| else |
| write_data.write_data[0] = 0x0; |
| |
| write_data.write_data[1] = 0x1; |
| write_data.write_length = 0x2; |
| |
| write_data.read_length = 0x0; |
| |
| max77705_usbc_opcode_read(usbpd_data, &read_data); |
| max77705_usbc_opcode_write(usbpd_data, &write_data); |
| } |
| |
| void max77705_control_option_command(struct max77705_usbc_platform_data *usbpd_data, int cmd) |
| { |
| struct max77705_cc_data *cc_data = usbpd_data->cc_data; |
| u8 ccstat = 0; |
| usbc_cmd_data write_data; |
| |
| /* for maxim request : they want to check ccstate here */ |
| max77705_read_reg(usbpd_data->muic, REG_CC_STATUS0, &cc_data->cc_status0); |
| ccstat = (cc_data->cc_status0 & BIT_CCStat) >> FFS(BIT_CCStat); |
| msg_maxim("usb: cmd=0x%x ccstat : %d", cmd, ccstat); |
| |
| init_usbc_cmd_data(&write_data); |
| /* 1 : Vconn control option command ON */ |
| /* 2 : Vconn control option command OFF */ |
| /* 3 : Water Detect option command ON */ |
| /* 4 : Water Detect option command OFF */ |
| if (cmd == 1) |
| usbpd_data->vconn_test = 0; |
| else if (cmd == 2) |
| usbpd_data->vconn_test = 0; /* do nothing */ |
| else if (cmd == 3) { /* if SBU pin is low, water interrupt is happened. */ |
| write_data.opcode = 0x54; |
| write_data.write_data[0] = 0x3; |
| write_data.write_length = 0x1; |
| write_data.read_length = 0x0; |
| max77705_usbc_opcode_write(usbpd_data, &write_data); |
| } else if (cmd == 4) { |
| write_data.opcode = 0x54; |
| write_data.write_data[0] = 0x2; |
| write_data.write_length = 0x1; |
| write_data.read_length = 0x0; |
| max77705_usbc_opcode_write(usbpd_data, &write_data); |
| } |
| if ((cmd & 0xF) == 0x3) |
| usbpd_data->fac_water_enable = 1; |
| else if ((cmd & 0xF) == 0x4) |
| usbpd_data->fac_water_enable = 0; |
| } |
| |
| void max77705_response_sbu_read(struct max77705_usbc_platform_data *usbpd_data, unsigned char *data) |
| { |
| u8 sbu1 = 0, sbu2 = 0; |
| |
| sbu1 = data[1]; |
| sbu2 = data[2]; |
| |
| msg_maxim("SBU1 = 0x%x, SBU2 = 0x%x", sbu1, sbu2); |
| |
| if (sbu1 == 0x0) |
| usbpd_data->sbu[0] = 0; |
| else |
| usbpd_data->sbu[0] = 1; |
| if (sbu2 == 0x0) |
| usbpd_data->sbu[1] = 0; |
| else |
| usbpd_data->sbu[1] = 1; |
| complete(&usbpd_data->ccic_sysfs_completion); |
| } |
| |
| void max77705_request_sbu_read(struct max77705_usbc_platform_data *usbpd_data) |
| { |
| usbc_cmd_data write_data; |
| |
| init_usbc_cmd_data(&write_data); |
| write_data.opcode = OPCODE_READ_SBU; |
| write_data.write_data[0] = 0x1; |
| write_data.write_length = 0x1; |
| write_data.read_length = 0x2; |
| max77705_usbc_opcode_write(usbpd_data, &write_data); |
| } |
| |
| void max77705_request_control3_reg_read(struct max77705_usbc_platform_data *usbpd_data) |
| { |
| usbc_cmd_data read_data; |
| |
| init_usbc_cmd_data(&read_data); |
| read_data.opcode = OPCODE_CTRLREG3_R; |
| read_data.write_length = 0x0; |
| read_data.read_length = 0x1; |
| max77705_usbc_opcode_read(g_usbc_data, &read_data); |
| } |
| |
| void max77705_set_CCForceError(struct max77705_usbc_platform_data *usbpd_data) |
| { |
| usbc_cmd_data write_data; |
| |
| init_usbc_cmd_data(&write_data); |
| write_data.opcode = OPCODE_CCCTRL2_W; |
| write_data.write_data[0] = 0x84; |
| write_data.write_length = 0x1; |
| write_data.read_length = 0x0; |
| max77705_usbc_opcode_write(usbpd_data, &write_data); |
| } |
| |
| void max77705_set_lockerroren(struct max77705_usbc_platform_data *usbpd_data, |
| unsigned char data, u8 en) |
| { |
| usbc_cmd_data write_data; |
| u8 control3_reg = data; |
| |
| init_usbc_cmd_data(&write_data); |
| write_data.opcode = OPCODE_CTRLREG3_W; |
| control3_reg &= ~(0x1 << 1); |
| write_data.write_data[0] = control3_reg | ((en & 0x1) << 1); |
| write_data.write_length = 0x1; |
| write_data.read_length = 0x0; |
| max77705_usbc_opcode_write(usbpd_data, &write_data); |
| } |
| |
| void max77705_control3_read_complete(struct max77705_usbc_platform_data *usbpd_data, |
| unsigned char *data) |
| { |
| usbpd_data->control3_reg = data[1]; |
| complete(&usbpd_data->op_completion); |
| } |
| |
| void pdic_manual_ccopen_request(int is_on) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = g_usbc_data; |
| |
| msg_maxim("is_on %d > %d", usbpd_data->cc_open_req, is_on); |
| if (usbpd_data->cc_open_req != is_on) { |
| usbpd_data->cc_open_req = is_on; |
| schedule_work(&usbpd_data->cc_open_req_work); |
| } |
| } |
| |
| static void max77705_cc_open_work_func( |
| struct work_struct *work) |
| { |
| struct max77705_usbc_platform_data *usbc_data; |
| u8 lock_err_en; |
| #ifdef CONFIG_USB_NOTIFY_PROC_LOG |
| int event; |
| #endif |
| |
| usbc_data = container_of(work, struct max77705_usbc_platform_data, cc_open_req_work); |
| msg_maxim("%s", usbc_data->cc_open_req? "set":"clear"); |
| |
| if (usbc_data->cc_open_req) { |
| reinit_completion(&usbc_data->op_completion); |
| max77705_request_control3_reg_read(usbc_data); /* ref 0x65 -> write 0x67*/ |
| if (!wait_for_completion_timeout(&usbc_data->op_completion, msecs_to_jiffies(1000))) { |
| msg_maxim("OPCMD COMPLETION TIMEOUT"); |
| return; |
| } |
| lock_err_en = GET_CONTROL3_LOCK_ERROR_EN(usbc_data->control3_reg); |
| msg_maxim("data: 0x%x lock_err_en=%d", usbc_data->control3_reg, lock_err_en); |
| if (!lock_err_en) { |
| #ifdef CONFIG_USB_NOTIFY_PROC_LOG |
| event = NOTIFY_EXTRA_CCOPEN_REQ_SET; |
| store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL); |
| #endif |
| max77705_set_lockerroren(usbc_data, usbc_data->control3_reg, 1); |
| } |
| max77705_set_CCForceError(usbc_data); |
| } else { |
| reinit_completion(&usbc_data->op_completion); |
| max77705_request_control3_reg_read(usbc_data); |
| if (!wait_for_completion_timeout(&usbc_data->op_completion, msecs_to_jiffies(1000))) |
| msg_maxim("OPCMD COMPLETION TIMEOUT"); |
| |
| lock_err_en = GET_CONTROL3_LOCK_ERROR_EN(usbc_data->control3_reg); |
| msg_maxim("data: 0x%x lock_err_en=%d", usbc_data->control3_reg, lock_err_en); |
| if (lock_err_en) { |
| #ifdef CONFIG_USB_NOTIFY_PROC_LOG |
| event = NOTIFY_EXTRA_CCOPEN_REQ_CLEAR; |
| store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL); |
| #endif |
| max77705_set_lockerroren(usbc_data, usbc_data->control3_reg, 0); |
| } |
| } |
| } |
| |
| void max77705_response_selftest_read(struct max77705_usbc_platform_data *usbpd_data, unsigned char *data) |
| { |
| u8 cc = 0; |
| |
| cc = data[1]; |
| usbpd_data->sbu[0] = data[2]; |
| usbpd_data->sbu[1] = data[3]; |
| |
| msg_maxim("SELFTEST CC = %x SBU1 = 0x%x, SBU2 = 0x%x", cc, |
| usbpd_data->sbu[0], usbpd_data->sbu[1]); |
| complete(&usbpd_data->ccic_sysfs_completion); |
| } |
| |
| void max77705_request_selftest_read(struct max77705_usbc_platform_data *usbpd_data) |
| { |
| usbc_cmd_data write_data; |
| |
| init_usbc_cmd_data(&write_data); |
| write_data.opcode = OPCODE_READ_SELFTEST; |
| write_data.write_length = 0x1; |
| write_data.write_data[0] = 0x1; |
| write_data.read_length = 0x3; |
| max77705_usbc_opcode_write(usbpd_data, &write_data); |
| } |
| |
| int max77705_firmware_update_sysfs(struct max77705_usbc_platform_data *usbpd_data, int fw_dir) |
| { |
| int ret = 0; |
| usbpd_data->fw_update = 1; |
| max77705_usbc_mask_irq(usbpd_data); |
| max77705_write_reg(usbpd_data->muic, REG_PD_INT_M, 0xFF); |
| max77705_write_reg(usbpd_data->muic, REG_CC_INT_M, 0xFF); |
| max77705_write_reg(usbpd_data->muic, REG_UIC_INT_M, 0xFF); |
| max77705_write_reg(usbpd_data->muic, REG_VDM_INT_M, 0xFF); |
| ret = max77705_firmware_update_sys(usbpd_data, fw_dir); |
| max77705_write_reg(usbpd_data->muic, REG_UIC_INT_M, REG_UIC_INT_M_INIT); |
| max77705_write_reg(usbpd_data->muic, REG_CC_INT_M, REG_CC_INT_M_INIT); |
| max77705_write_reg(usbpd_data->muic, REG_PD_INT_M, REG_PD_INT_M_INIT); |
| max77705_write_reg(usbpd_data->muic, REG_VDM_INT_M, REG_VDM_INT_M_INIT); |
| // max77705_usbc_enable_auto_vbus(usbpd_data); |
| max77705_set_enable_alternate_mode(ALTERNATE_MODE_START); |
| max77705_usbc_umask_irq(usbpd_data); |
| usbpd_data->fw_update = 0; |
| return ret; |
| } |
| |
| int max77705_request_vsafe0v_read(struct max77705_usbc_platform_data *usbpd_data) |
| { |
| u8 cc_status1 = 0; |
| int vsafe0v = 0; |
| |
| max77705_read_reg(usbpd_data->muic, REG_CC_STATUS1, &cc_status1); |
| |
| vsafe0v = (cc_status1 & BIT_VSAFE0V) >> FFS(BIT_VSAFE0V); |
| pr_info("%s: ccstatus1: 0x%x %d \n", __func__, cc_status1, vsafe0v); |
| return vsafe0v; |
| } |
| |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| static int max77705_sysfs_get_local_prop(struct _ccic_data_t *pccic_data, |
| enum ccic_sysfs_property prop, |
| char *buf) |
| { |
| int retval = -ENODEV, i = 0; |
| u8 cur_major = 0, cur_minor = 0, src_major = 0, src_minor = 0; |
| struct max77705_usbc_platform_data *usbpd_data = |
| (struct max77705_usbc_platform_data *)pccic_data->drv_data; |
| |
| if (!usbpd_data) { |
| msg_maxim("usbpd_data is null : request prop = %d", prop); |
| return -ENODEV; |
| } |
| |
| switch (prop) { |
| case CCIC_SYSFS_PROP_CUR_VERSION: |
| retval = max77705_read_reg(usbpd_data->muic, REG_UIC_FW_REV, &cur_major); |
| if (retval < 0) { |
| msg_maxim("Failed to read FW_REV"); |
| return retval; |
| } |
| retval = max77705_read_reg(usbpd_data->muic, REG_UIC_FW_MINOR, &cur_minor); |
| if (retval < 0) { |
| msg_maxim("Failed to read FW_MINOR_REV"); |
| return retval; |
| } |
| cur_minor &= MINOR_VERSION_MASK; |
| retval = sprintf(buf, "%02X.%02X\n", cur_major, cur_minor); |
| msg_maxim("usb: CCIC_SYSFS_PROP_CUR_VERSION : %02X.%02X", |
| cur_major, cur_minor); |
| break; |
| case CCIC_SYSFS_PROP_SRC_VERSION: |
| if (usbpd_data->max77705->pmic_rev == MAX77705_PASS3) { |
| src_major = BOOT_FLASH_FW_PASS3[4]; |
| src_minor = BOOT_FLASH_FW_PASS3[5]; |
| } else if (usbpd_data->max77705->pmic_rev == MAX77705_PASS4) { |
| src_major = BOOT_FLASH_FW_PASS4[4]; |
| src_minor = BOOT_FLASH_FW_PASS4[5] & MINOR_VERSION_MASK; |
| } else if (usbpd_data->max77705->pmic_rev == MAX77705_PASS5) { |
| src_major = BOOT_FLASH_FW_PASS2[4]; |
| src_minor = BOOT_FLASH_FW_PASS2[5] & MINOR_VERSION_MASK; |
| } else { |
| src_major = 0xFF; |
| src_minor = 0xFF; |
| } |
| retval = sprintf(buf, "%02X.%02X\n", src_major, src_minor); |
| msg_maxim("usb: CCIC_SYSFS_PROP_SRC_VERSION : %02X.%02X", |
| src_major, src_minor); |
| break; |
| case CCIC_SYSFS_PROP_LPM_MODE: |
| retval = sprintf(buf, "%d\n", usbpd_data->manual_lpm_mode); |
| msg_maxim("usb: CCIC_SYSFS_PROP_LPM_MODE : %d", |
| usbpd_data->manual_lpm_mode); |
| break; |
| case CCIC_SYSFS_PROP_STATE: |
| retval = sprintf(buf, "%d\n", usbpd_data->pd_state); |
| msg_maxim("usb: CCIC_SYSFS_PROP_STATE : %d", |
| usbpd_data->pd_state); |
| break; |
| case CCIC_SYSFS_PROP_RID: |
| retval = sprintf(buf, "%d\n", usbpd_data->cur_rid); |
| msg_maxim("usb: CCIC_SYSFS_PROP_RID : %d", |
| usbpd_data->cur_rid); |
| break; |
| case CCIC_SYSFS_PROP_BOOTING_DRY: |
| usbpd_data->sbu[0] = 0;usbpd_data->sbu[1] = 0; |
| reinit_completion(&usbpd_data->ccic_sysfs_completion); |
| max77705_request_selftest_read(usbpd_data); |
| i = wait_for_completion_timeout(&usbpd_data->ccic_sysfs_completion, msecs_to_jiffies(1000 * 5)); |
| if (i == 0) |
| msg_maxim("CCIC SYSFS COMPLETION TIMEOUT"); |
| msg_maxim("usb: CCIC_SYSFS_PROP_BOOTING_DRY timeout : %d", i); |
| if (usbpd_data->sbu[0] >= 7 && usbpd_data->sbu[1] >= 7) |
| retval = sprintf(buf, "%d\n", 1); |
| else |
| retval = sprintf(buf, "%d\n", 0); |
| break; |
| case CCIC_SYSFS_PROP_FW_UPDATE_STATUS: |
| retval = sprintf(buf, "%s\n", usbpd_data->fw_update == 1 ? "UPDATE" : "NORMAL"); |
| msg_maxim("usb: CCIC_SYSFS_PROP_FW_UPDATE_STATUS : %s", |
| usbpd_data->fw_update == 1 ? "UPDATE" : "NORMAL"); |
| break; |
| case CCIC_SYSFS_PROP_FW_WATER: |
| retval = sprintf(buf, "%d\n", usbpd_data->current_connstat == WATER ? 1 : 0); |
| msg_maxim("usb: CCIC_SYSFS_PROP_FW_WATER : %d", |
| usbpd_data->current_connstat == WATER ? 1 : 0); |
| break; |
| case CCIC_SYSFS_PROP_ACC_DEVICE_VERSION: |
| retval = sprintf(buf, "%04x\n", usbpd_data->Device_Version); |
| msg_maxim("usb: CCIC_SYSFS_PROP_ACC_DEVICE_VERSION : %d", |
| usbpd_data->Device_Version); |
| break; |
| case CCIC_SYSFS_PROP_CONTROL_GPIO: |
| usbpd_data->sbu[0] = 0;usbpd_data->sbu[1] = 0; |
| reinit_completion(&usbpd_data->ccic_sysfs_completion); |
| max77705_request_sbu_read(usbpd_data); |
| i = wait_for_completion_timeout(&usbpd_data->ccic_sysfs_completion, msecs_to_jiffies(200 * 5)); |
| if (i == 0) |
| msg_maxim("CCIC SYSFS COMPLETION TIMEOUT"); |
| /* compare SBU1, SBU2 values after interrupt */ |
| msg_maxim("usb: CCIC_SYSFS_PROP_CONTROL_GPIO SBU1 = 0x%x ,SBU2 = 0x%x timeout:%d", |
| usbpd_data->sbu[0], usbpd_data->sbu[1], i); |
| retval = sprintf(buf, "%d %d\n", usbpd_data->sbu[0], usbpd_data->sbu[1]); |
| break; |
| case CCIC_SYSFS_PROP_USBPD_IDS: |
| retval = sprintf(buf, "%04x:%04x\n", |
| le16_to_cpu(usbpd_data->Vendor_ID), |
| le16_to_cpu(usbpd_data->Product_ID)); |
| msg_maxim("usb: CCIC_SYSFS_USBPD_IDS : %s", buf); |
| break; |
| case CCIC_SYSFS_PROP_USBPD_TYPE: |
| retval = sprintf(buf, "%d\n", usbpd_data->acc_type); |
| msg_maxim("usb: CCIC_SYSFS_USBPD_TYPE : %d", |
| usbpd_data->acc_type); |
| break; |
| case CCIC_SYSFS_PROP_CC_PIN_STATUS: |
| retval = sprintf(buf, "%d\n", usbpd_data->cc_pin_status); |
| msg_maxim("usb: CCIC_SYSFS_PROP_PIN_STATUS : %d", |
| usbpd_data->cc_pin_status); |
| break; |
| #ifdef MAX77705_RAM_TEST |
| case CCIC_SYSFS_PROP_RAM_TEST: |
| max77705_verify_ram_bist_write(usbpd_data); |
| for (i = 0; i < 300; i++) { |
| msleep(10); |
| if (usbpd_data->ram_test_enable == MAX77705_RAM_TEST_STOP_MODE) { |
| msleep(3000); |
| break; |
| } |
| } |
| msg_maxim("usb: CCIC_SYSFS_PROP_RAM_TEST : %d", usbpd_data->ram_test_result); |
| retval = sprintf(buf, "%d\n", usbpd_data->ram_test_result); |
| break; |
| #endif |
| case CCIC_SYSFS_PROP_SBU_ADC: |
| usbpd_data->sbu[0] = 0;usbpd_data->sbu[1] = 0; |
| reinit_completion(&usbpd_data->ccic_sysfs_completion); |
| max77705_request_selftest_read(usbpd_data); |
| i = wait_for_completion_timeout(&usbpd_data->ccic_sysfs_completion, msecs_to_jiffies(1000 * 5)); |
| if (i == 0) |
| msg_maxim("CCIC SYSFS COMPLETION TIMEOUT"); |
| msg_maxim("usb: CCIC_SYSFS_PROP_SBU_ADC : %d %d timeout : %d", |
| usbpd_data->sbu[0], usbpd_data->sbu[1], i); |
| retval = sprintf(buf, "%d %d\n", usbpd_data->sbu[0], usbpd_data->sbu[1]); |
| break; |
| case CCIC_SYSFS_PROP_VSAFE0V_STATUS: |
| usbpd_data->vsafe0v_status = max77705_request_vsafe0v_read(usbpd_data); |
| retval = sprintf(buf, "%d\n", usbpd_data->vsafe0v_status); |
| msg_maxim("usb: CCIC_SYSFS_PROP_VSAFE0V_STATUS : %d", |
| usbpd_data->vsafe0v_status); |
| break; |
| default: |
| msg_maxim("prop read not supported prop (%d)", prop); |
| retval = -ENODATA; |
| break; |
| } |
| |
| return retval; |
| } |
| |
| static void max77705_control_gpio_for_sbu(int onoff) |
| { |
| struct otg_notify *o_notify = get_otg_notify(); |
| struct usb_notifier_platform_data *pdata = get_notify_data(o_notify); |
| |
| if (o_notify) |
| o_notify->set_ldo_onoff(pdata, onoff); |
| } |
| |
| static ssize_t max77705_sysfs_set_prop(struct _ccic_data_t *pccic_data, |
| enum ccic_sysfs_property prop, |
| const char *buf, size_t size) |
| { |
| ssize_t retval = size; |
| int mode = 0; |
| u8 FW_Revision = 0, FW_Minor_Revision = 0; |
| int ret = 0; |
| struct max77705_usbc_platform_data *usbpd_data = |
| (struct max77705_usbc_platform_data *)pccic_data->drv_data; |
| int rv; |
| |
| if (!usbpd_data) { |
| msg_maxim("usbpd_data is null : request prop = %d", prop); |
| return -ENODEV; |
| } |
| switch (prop) { |
| case CCIC_SYSFS_PROP_LPM_MODE: |
| rv = sscanf(buf, "%d", &mode); |
| msg_maxim("usb: CCIC_SYSFS_PROP_LPM_MODE mode=%d", mode); |
| switch (mode) { |
| case 0: |
| /* Disable Low Power Mode for App (SW JIGON Disable) */ |
| max77705_manual_jig_on(usbpd_data, 0); |
| usbpd_data->manual_lpm_mode = 0; |
| break; |
| case 1: |
| /* Enable Low Power Mode for App (SW JIGON Enable) */ |
| max77705_manual_jig_on(usbpd_data, 1); |
| usbpd_data->manual_lpm_mode = 1; |
| break; |
| case 2: |
| /* SW JIGON Enable */ |
| max77705_manual_jig_on(usbpd_data, 1); |
| usbpd_data->manual_lpm_mode = 1; |
| break; |
| default: |
| /* SW JIGON Disable */ |
| max77705_manual_jig_on(usbpd_data, 0); |
| usbpd_data->manual_lpm_mode = 0; |
| break; |
| } |
| break; |
| case CCIC_SYSFS_PROP_CTRL_OPTION: |
| rv = sscanf(buf, "%d", &mode); |
| msg_maxim("usb: CCIC_SYSFS_PROP_CTRL_OPTION mode=%d", mode); |
| max77705_control_option_command(usbpd_data, mode); |
| break; |
| case CCIC_SYSFS_PROP_FW_UPDATE: |
| rv = sscanf(buf, "%d", &mode); |
| msg_maxim("CCIC_SYSFS_PROP_FW_UPDATE mode=%d", mode); |
| |
| retval = max77705_read_reg(usbpd_data->muic, REG_UIC_FW_REV, &FW_Revision); |
| if (retval < 0) { |
| msg_maxim("Failed to read FW_REV"); |
| return retval; |
| } |
| retval = max77705_read_reg(usbpd_data->muic, REG_UIC_FW_MINOR, &FW_Minor_Revision); |
| if (retval < 0) { |
| msg_maxim("Failed to read FW_MINOR_REV"); |
| return retval; |
| } |
| FW_Minor_Revision &= MINOR_VERSION_MASK; |
| pr_info("%s before : FW_REV %02X.%02X\n", __func__, FW_Revision, FW_Minor_Revision); |
| |
| /* Factory cmd for firmware update |
| * argument represent what is source of firmware like below. |
| * |
| * 0 : [BUILT_IN] Getting firmware from source. |
| * 1 : [UMS] Getting firmware from sd card. |
| * 2 : [SPU] Getting firmware from SPU APP. |
| */ |
| switch (mode) { |
| case BUILT_IN: |
| case UMS: |
| case SPU: |
| ret = max77705_firmware_update_sysfs(usbpd_data, mode); |
| break; |
| default: |
| ret = -EINVAL; |
| msg_maxim("Not support command[%d]", mode); |
| break; |
| } |
| if (ret < 0) { |
| msg_maxim("Failed to update FW"); |
| return ret; |
| } |
| |
| max77705_get_version_info(usbpd_data); |
| break; |
| case CCIC_SYSFS_PROP_DEX_FAN_UVDM: |
| rv = sscanf(buf, "%d", &mode); |
| msg_maxim("CCIC_SYSFS_PROP_DEX_FAN_UVDM mode=%d", mode); |
| max77705_send_dex_fan_unstructured_vdm_message(usbpd_data, mode); |
| break; |
| case CCIC_SYSFS_PROP_DEBUG_OPCODE: |
| rv = sscanf(buf, "%d", &mode); |
| msg_maxim("CCIC_SYSFS_PROP_DEBUG_OPCODE mode=%d", mode); |
| if (mode) |
| max77705_usbc_debug_function(usbpd_data); |
| break; |
| case CCIC_SYSFS_PROP_CONTROL_GPIO: |
| rv = sscanf(buf, "%d", &mode); |
| msg_maxim("CCIC_SYSFS_PROP_CONTROL_GPIO mode=%d. do nothing for control gpio.", mode); |
| /* orignal concept : mode 0 : SBU1/SBU2 set as open-drain status |
| * mode 1 : SBU1/SBU2 set as default status - Pull up |
| * But, max77705 is always open-drain status so we don't need to control it. |
| */ |
| max77705_control_gpio_for_sbu(!mode); |
| break; |
| default: |
| pr_info("%s prop write not supported prop (%d)\n", __func__, prop); |
| retval = -ENODATA; |
| return retval; |
| } |
| return size; |
| } |
| |
| static int max77705_sysfs_is_writeable(struct _ccic_data_t *pccic_data, |
| enum ccic_sysfs_property prop) |
| { |
| switch (prop) { |
| case CCIC_SYSFS_PROP_LPM_MODE: |
| case CCIC_SYSFS_PROP_CTRL_OPTION: |
| case CCIC_SYSFS_PROP_DEBUG_OPCODE: |
| case CCIC_SYSFS_PROP_CONTROL_GPIO: |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| |
| static int max77705_sysfs_is_writeonly(struct _ccic_data_t *pccic_data, |
| enum ccic_sysfs_property prop) |
| { |
| switch (prop) { |
| case CCIC_SYSFS_PROP_FW_UPDATE: |
| case CCIC_SYSFS_PROP_DEX_FAN_UVDM: |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| #endif |
| static ssize_t max77705_fw_update(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| unsigned int start_fw_update = 0; |
| usbc_cmd_data read_data; |
| usbc_cmd_data write_data; |
| |
| init_usbc_cmd_data(&read_data); |
| init_usbc_cmd_data(&write_data); |
| read_data.opcode = OPCODE_CTRL1_R; |
| read_data.write_length = 0x0; |
| read_data.read_length = 0x1; |
| |
| write_data.opcode = OPCODE_CTRL1_W; |
| write_data.write_data[0] = 0x09; |
| write_data.write_length = 0x1; |
| write_data.read_length = 0x0; |
| |
| if (kstrtou32(buf, 0, &start_fw_update)) { |
| dev_err(dev, |
| "%s: Failed converting from str to u32.", __func__); |
| } |
| |
| msg_maxim("start_fw_update %d", start_fw_update); |
| |
| max77705_usbc_opcode_rw(g_usbc_data, &read_data, &write_data); |
| |
| switch (start_fw_update) { |
| case 1: |
| max77705_usbc_opcode_rw(g_usbc_data, &read_data, &write_data); |
| break; |
| case 2: |
| max77705_usbc_opcode_read(g_usbc_data, &read_data); |
| break; |
| case 3: |
| max77705_usbc_opcode_write(g_usbc_data, &write_data); |
| break; |
| default: |
| break; |
| } |
| return size; |
| } |
| static DEVICE_ATTR(fw_update, S_IRUGO | S_IWUSR | S_IWGRP, |
| NULL, max77705_fw_update); |
| |
| static struct attribute *max77705_attr[] = { |
| &dev_attr_fw_update.attr, |
| NULL, |
| }; |
| |
| static struct attribute_group max77705_attr_grp = { |
| .attrs = max77705_attr, |
| }; |
| |
| static void max77705_get_version_info(struct max77705_usbc_platform_data *usbc_data) |
| { |
| u8 hw_rev[4] = {0, }; |
| u8 sw_main[3] = {0, }; |
| u8 sw_boot = 0; |
| |
| max77705_read_reg(usbc_data->muic, REG_UIC_HW_REV, &hw_rev[0]); |
| max77705_read_reg(usbc_data->muic, REG_UIC_FW_MINOR, &sw_main[1]); |
| max77705_read_reg(usbc_data->muic, REG_UIC_FW_REV, &sw_main[0]); |
| |
| usbc_data->HW_Revision = hw_rev[0]; |
| usbc_data->FW_Minor_Revision = sw_main[1] & MINOR_VERSION_MASK; |
| usbc_data->FW_Revision = sw_main[0]; |
| |
| /* H/W, Minor, Major, Boot */ |
| msg_maxim("HW rev is %02Xh, FW rev is %02X.%02X!", |
| usbc_data->HW_Revision, usbc_data->FW_Revision, usbc_data->FW_Minor_Revision); |
| |
| store_ccic_version(&hw_rev[0], &sw_main[0], &sw_boot); |
| } |
| |
| static void max77705_init_opcode |
| (struct max77705_usbc_platform_data *usbc_data, int reset) |
| { |
| struct max77705_platform_data *pdata = usbc_data->max77705_data; |
| |
| max77705_usbc_disable_auto_vbus(usbc_data); |
| if (pdata && pdata->support_audio) |
| max77705_usbc_enable_audio(usbc_data); |
| if (reset) |
| max77705_set_enable_alternate_mode(ALTERNATE_MODE_START); |
| } |
| |
| static bool max77705_check_recover_opcode(u8 opcode) |
| { |
| bool ret = false; |
| |
| switch (opcode) { |
| case OPCODE_CCCTRL1_W: |
| case OPCODE_SAMSUNG_FACTORY_TEST: |
| case OPCODE_SET_ALTERNATEMODE: |
| ret = true; |
| break; |
| default: |
| ret = false; |
| break; |
| } |
| return ret; |
| } |
| static void max77705_recover_opcode |
| (struct max77705_usbc_platform_data *usbc_data, bool opcode_list[]) |
| { |
| int i; |
| |
| for (i = 0; i < OPCODE_NONE; i++) { |
| if (opcode_list[i]) { |
| msg_maxim("opcode = 0x%02x", i); |
| switch (i) { |
| case OPCODE_CCCTRL1_W: |
| if (usbc_data->op_ctrl1_w & BIT_CCAudEn) |
| max77705_usbc_enable_audio(usbc_data); |
| break; |
| case OPCODE_SAMSUNG_FACTORY_TEST: |
| if (usbc_data->auto_vbus_en) |
| max77705_usbc_enable_auto_vbus(usbc_data); |
| else |
| max77705_usbc_disable_auto_vbus(usbc_data); |
| break; |
| case OPCODE_SET_ALTERNATEMODE: |
| max77705_set_enable_alternate_mode |
| (usbc_data->set_altmode); |
| break; |
| default: |
| break; |
| } |
| opcode_list[i] = false; |
| } |
| } |
| } |
| |
| void init_usbc_cmd_data(usbc_cmd_data *cmd_data) |
| { |
| cmd_data->opcode = OPCODE_NONE; |
| cmd_data->prev_opcode = OPCODE_NONE; |
| cmd_data->response = OPCODE_NONE; |
| cmd_data->val = REG_NONE; |
| cmd_data->mask = REG_NONE; |
| cmd_data->reg = REG_NONE; |
| cmd_data->noti_cmd = OPCODE_NOTI_NONE; |
| cmd_data->write_length = 0; |
| cmd_data->read_length = 0; |
| cmd_data->seq = 0; |
| cmd_data->is_uvdm = 0; |
| memset(cmd_data->write_data, REG_NONE, OPCODE_DATA_LENGTH); |
| memset(cmd_data->read_data, REG_NONE, OPCODE_DATA_LENGTH); |
| } |
| |
| static void init_usbc_cmd_node(usbc_cmd_node *usbc_cmd_node) |
| { |
| usbc_cmd_data *cmd_data = &(usbc_cmd_node->cmd_data); |
| |
| pr_debug("%s:%s\n", "MAX77705", __func__); |
| |
| usbc_cmd_node->next = NULL; |
| |
| init_usbc_cmd_data(cmd_data); |
| } |
| |
| static void copy_usbc_cmd_data(usbc_cmd_data *from, usbc_cmd_data *to) |
| { |
| to->opcode = from->opcode; |
| to->response = from->response; |
| memcpy(to->read_data, from->read_data, OPCODE_DATA_LENGTH); |
| memcpy(to->write_data, from->write_data, OPCODE_DATA_LENGTH); |
| to->reg = from->reg; |
| to->mask = from->mask; |
| to->val = from->val; |
| to->seq = from->seq; |
| to->read_length = from->read_length; |
| to->write_length = from->write_length; |
| to->prev_opcode = from->prev_opcode; |
| to->is_uvdm = from->is_uvdm; |
| } |
| |
| bool is_empty_usbc_cmd_queue(usbc_cmd_queue_t *usbc_cmd_queue) |
| { |
| bool ret = false; |
| |
| if (usbc_cmd_queue->front == NULL) |
| ret = true; |
| |
| if (ret) |
| msg_maxim("usbc_cmd_queue Empty(%c)", ret ? 'T' : 'F'); |
| |
| return ret; |
| } |
| |
| void enqueue_usbc_cmd(usbc_cmd_queue_t *usbc_cmd_queue, usbc_cmd_data *cmd_data) |
| { |
| usbc_cmd_node *temp_node = kzalloc(sizeof(usbc_cmd_node), GFP_KERNEL); |
| |
| if (!temp_node) { |
| msg_maxim("failed to allocate usbc command queue"); |
| return; |
| } |
| |
| init_usbc_cmd_node(temp_node); |
| |
| copy_usbc_cmd_data(cmd_data, &(temp_node->cmd_data)); |
| |
| if (is_empty_usbc_cmd_queue(usbc_cmd_queue)) { |
| usbc_cmd_queue->front = temp_node; |
| usbc_cmd_queue->rear = temp_node; |
| } else { |
| usbc_cmd_queue->rear->next = temp_node; |
| usbc_cmd_queue->rear = temp_node; |
| } |
| } |
| |
| static void dequeue_usbc_cmd |
| (usbc_cmd_queue_t *usbc_cmd_queue, usbc_cmd_data *cmd_data) |
| { |
| usbc_cmd_node *temp_node; |
| |
| if (is_empty_usbc_cmd_queue(usbc_cmd_queue)) { |
| msg_maxim("Queue, Empty!"); |
| return; |
| } |
| |
| temp_node = usbc_cmd_queue->front; |
| copy_usbc_cmd_data(&(temp_node->cmd_data), cmd_data); |
| |
| msg_maxim("Opcode(0x%02x) Response(0x%02x)", cmd_data->opcode, cmd_data->response); |
| |
| if (usbc_cmd_queue->front->next == NULL) { |
| msg_maxim("front->next = NULL"); |
| usbc_cmd_queue->front = NULL; |
| } else |
| usbc_cmd_queue->front = usbc_cmd_queue->front->next; |
| |
| if (is_empty_usbc_cmd_queue(usbc_cmd_queue)) |
| usbc_cmd_queue->rear = NULL; |
| |
| kfree(temp_node); |
| } |
| |
| static bool front_usbc_cmd |
| (usbc_cmd_queue_t *cmd_queue, usbc_cmd_data *cmd_data) |
| { |
| if (is_empty_usbc_cmd_queue(cmd_queue)) { |
| msg_maxim("Queue, Empty!"); |
| return false; |
| } |
| |
| copy_usbc_cmd_data(&(cmd_queue->front->cmd_data), cmd_data); |
| msg_maxim("Opcode(0x%02x)", cmd_data->opcode); |
| return true; |
| } |
| |
| static bool is_usbc_notifier_opcode(u8 opcode) |
| { |
| bool noti = false; |
| |
| return noti; |
| } |
| |
| /* |
| * max77705_i2c_opcode_write - SMBus "opcode write" protocol |
| * @chip: max77705 platform data |
| * @command: OPcode |
| * @values: Byte array into which data will be read; big enough to hold |
| * the data returned by the slave. |
| * |
| * This executes the SMBus "opcode read" protocol, returning negative errno |
| * else the number of data bytes in the slave's response. |
| */ |
| int max77705_i2c_opcode_write(struct max77705_usbc_platform_data *usbc_data, |
| u8 opcode, u8 length, u8 *values) |
| { |
| u8 write_values[OPCODE_MAX_LENGTH] = { 0, }; |
| int ret = 0; |
| |
| if (length > OPCODE_DATA_LENGTH) |
| return -EMSGSIZE; |
| |
| write_values[0] = opcode; |
| if (length) |
| memcpy(&write_values[1], values, length); |
| |
| #if 0 |
| int i = 0; // To use this, move int i to the top to avoid build error |
| for (i = 0; i < length + OPCODE_SIZE; i++) |
| msg_maxim("[%d], 0x[%x]", i, write_values[i]); |
| #else |
| msg_maxim("opcode 0x%x, write_length %d", |
| opcode, length + OPCODE_SIZE); |
| print_hex_dump(KERN_INFO, "max77705: opcode_write: ", |
| DUMP_PREFIX_OFFSET, 16, 1, write_values, |
| length + OPCODE_SIZE, false); |
| #endif |
| |
| /* Write opcode and data */ |
| ret = max77705_bulk_write(usbc_data->muic, OPCODE_WRITE, |
| length + OPCODE_SIZE, write_values); |
| /* Write end of data by 0x00 */ |
| if (length < OPCODE_DATA_LENGTH) |
| max77705_write_reg(usbc_data->muic, OPCODE_WRITE_END, 0x00); |
| |
| if (opcode == OPCODE_SET_ALTERNATEMODE) |
| usbc_data->set_altmode_error = ret; |
| |
| if (ret == 0) |
| usbc_data->opcode_stamp = jiffies; |
| |
| return ret; |
| } |
| |
| /** |
| * max77705_i2c_opcode_read - SMBus "opcode read" protocol |
| * @chip: max77705 platform data |
| * @command: OPcode |
| * @values: Byte array into which data will be read; big enough to hold |
| * the data returned by the slave. |
| * |
| * This executes the SMBus "opcode read" protocol, returning negative errno |
| * else the number of data bytes in the slave's response. |
| */ |
| int max77705_i2c_opcode_read(struct max77705_usbc_platform_data *usbc_data, |
| u8 opcode, u8 length, u8 *values) |
| { |
| int size = 0; |
| |
| if (length > OPCODE_DATA_LENGTH) |
| return -EMSGSIZE; |
| |
| /* |
| * We don't need to use opcode to get any feedback |
| */ |
| |
| /* Read opcode data */ |
| size = max77705_bulk_read(usbc_data->muic, OPCODE_READ, |
| length + OPCODE_SIZE, values); |
| |
| #if 0 |
| int i = 0; // To use this, move int i to the top to avoid build error |
| for (i = 0; i < length + OPCODE_SIZE; i++) |
| msg_maxim("[%d], 0x[%x]", i, values[i]); |
| #else |
| msg_maxim("opcode 0x%x, read_length %d, ret_error %d", |
| opcode, length + OPCODE_SIZE, size); |
| print_hex_dump(KERN_INFO, "max77705: opcode_read: ", |
| DUMP_PREFIX_OFFSET, 16, 1, values, |
| length + OPCODE_SIZE, false); |
| #endif |
| return size; |
| } |
| |
| static void max77705_notify_execute(struct max77705_usbc_platform_data *usbc_data, |
| const usbc_cmd_data *cmd_data) |
| { |
| /* to do */ |
| } |
| |
| static void max77705_handle_update_opcode(struct max77705_usbc_platform_data *usbc_data, |
| const usbc_cmd_data *cmd_data, unsigned char *data) |
| { |
| usbc_cmd_data write_data; |
| u8 read_value = data[1]; |
| u8 write_value = (read_value & (~cmd_data->mask)) | (cmd_data->val & cmd_data->mask); |
| u8 opcode = cmd_data->response + 1; /* write opcode = read opocde + 1 */ |
| |
| pr_info("%s: value update [0x%x]->[0x%x] at OPCODE(0x%x)\n", __func__, |
| read_value, write_value, opcode); |
| |
| init_usbc_cmd_data(&write_data); |
| write_data.opcode = opcode; |
| write_data.write_length = 1; |
| write_data.write_data[0] = write_value; |
| write_data.read_length = 0; |
| |
| max77705_usbc_opcode_push(usbc_data, &write_data); |
| } |
| |
| static void max77705_irq_execute(struct max77705_usbc_platform_data *usbc_data, |
| const usbc_cmd_data *cmd_data) |
| { |
| int len = cmd_data->read_length; |
| unsigned char data[OPCODE_DATA_LENGTH] = {0,}; |
| u8 response = 0xff; |
| u8 vdm_opcode_header = 0x0; |
| UND_DATA_MSG_VDM_HEADER_Type vdm_header; |
| u8 vdm_command = 0x0; |
| u8 vdm_type = 0x0; |
| u8 vdm_response = 0x0; |
| u8 reqd_vdm_command = 0; |
| uint8_t W_DATA = 0x0; |
| |
| memset(&vdm_header, 0, sizeof(UND_DATA_MSG_VDM_HEADER_Type)); |
| max77705_i2c_opcode_read(usbc_data, cmd_data->opcode, |
| len, data); |
| |
| /* opcode identifying the messsage type. (0x51)*/ |
| response = data[0]; |
| |
| if (response != cmd_data->response) { |
| msg_maxim("Response [0x%02x] != [0x%02x]", |
| response, cmd_data->response); |
| #ifndef CONFIG_MAX77705_GRL_ENABLE |
| if (cmd_data->response == OPCODE_FW_OPCODE_CLEAR) { |
| msg_maxim("Response after FW opcode cleared, just return"); |
| return; |
| } |
| #endif |
| } |
| |
| /* to do(read switch case) */ |
| switch (response) { |
| case OPCODE_BCCTRL1_R: |
| case OPCODE_BCCTRL2_R: |
| case OPCODE_CTRL1_R: |
| case OPCODE_CTRL2_R: |
| case OPCODE_CTRL3_R: |
| case OPCODE_CCCTRL1_R: |
| case OPCODE_CCCTRL2_R: |
| case OPCODE_CCCTRL3_R: |
| case OPCODE_HVCTRL_R: |
| case OPCODE_OPCODE_VCONN_ILIM_R: |
| case OPCODE_CHGIN_ILIM_R: |
| case OPCODE_CHGIN_ILIM2_R: |
| if (cmd_data->seq == OPCODE_UPDATE_SEQ) |
| max77705_handle_update_opcode(usbc_data, cmd_data, data); |
| break; |
| #if defined(CONFIG_HV_MUIC_MAX77705_AFC) |
| case COMMAND_AFC_RESULT_READ: |
| case COMMAND_QC_2_0_SET: |
| max77705_muic_handle_detect_dev_hv(usbc_data->muic_data, data); |
| break; |
| #endif |
| case OPCODE_CURRENT_SRCCAP: |
| max77705_current_pdo(usbc_data, data); |
| break; |
| case OPCODE_GET_SRCCAP: |
| max77705_pdo_list(usbc_data, data); |
| break; |
| case OPCODE_SRCCAP_REQUEST: |
| /* |
| * If response of Source_Capablities message is SinkTxNg(0xFE) or Not in Ready State(0xFF) |
| * It means that the message can not be sent to Port Partner. |
| * After Attaching Rp 3.0A, send again the message. |
| */ |
| if (data[1] == 0xfe || data[1] == 0xff){ |
| usbc_data->srcccap_request_retry = true; |
| pr_info("%s : srcccap_request_retry is set\n", __func__); |
| } |
| break; |
| #if defined(CONFIG_PDIC_PD30) |
| case OPCODE_APDO_SRCCAP_REQUEST: |
| max77705_response_apdo_request(usbc_data, data); |
| break; |
| case OPCODE_SET_PPS: |
| max77705_response_set_pps(usbc_data, data); |
| break; |
| #endif |
| case OPCODE_SAMSUNG_READ_MESSAGE: |
| pr_info("@TA_ALERT: %s : OPCODE[%x] Data[1] = 0x%x Data[7] = 0x%x Data[9] = 0x%x\n", |
| __func__, OPCODE_SAMSUNG_READ_MESSAGE, data[1], data[7], data[9]); |
| #if defined(CONFIG_DIRECT_CHARGING) |
| if ((data[0] == 0x5D) && |
| /* OCP would be set to Alert or Status message */ |
| ((data[1] == 0x01 && data[7] == 0x04) || (data[1] == 0x02 && (data[9] & 0x02)))) { |
| union power_supply_propval value = {0,}; |
| value.intval = true; |
| psy_do_property("battery", set, |
| POWER_SUPPLY_EXT_PROP_DIRECT_TA_ALERT, value); |
| } |
| #endif |
| break; |
| case OPCODE_VDM_DISCOVER_GET_VDM_RESP: |
| max77705_vdm_message_handler(usbc_data, data, len + OPCODE_SIZE); |
| break; |
| case OPCODE_READ_SBU: |
| max77705_response_sbu_read(usbc_data, data); |
| break; |
| case OPCODE_VDM_DISCOVER_SET_VDM_REQ: |
| vdm_opcode_header = data[1]; |
| switch (vdm_opcode_header) { |
| case 0xFF: |
| msg_maxim("This isn't invalid response(OPCODE : 0x48, HEADER : 0xFF)"); |
| break; |
| default: |
| memcpy(&vdm_header, &data[2], sizeof(vdm_header)); |
| vdm_type = vdm_header.BITS.VDM_Type; |
| vdm_command = vdm_header.BITS.VDM_command; |
| vdm_response = vdm_header.BITS.VDM_command_type; |
| msg_maxim("vdm_type[%x], vdm_command[%x], vdm_response[%x]", |
| vdm_type, vdm_command, vdm_response); |
| switch (vdm_type) { |
| case STRUCTURED_VDM: |
| if (vdm_response == SEC_UVDM_RESPONDER_ACK) { |
| switch (vdm_command) { |
| case Discover_Identity: |
| msg_maxim("ignore Discover_Identity"); |
| break; |
| case Discover_SVIDs: |
| msg_maxim("ignore Discover_SVIDs"); |
| break; |
| case Discover_Modes: |
| /* work around. The discover mode irq is not happened */ |
| if (vdm_header.BITS.Standard_Vendor_ID |
| == SAMSUNG_VENDOR_ID) { |
| if (usbc_data->send_enter_mode_req == 0) { |
| /*Samsung Enter Mode */ |
| msg_maxim("dex: second enter mode request"); |
| usbc_data->send_enter_mode_req = 1; |
| max77705_vdm_process_set_Dex_enter_mode_req(usbc_data); |
| } |
| } else |
| msg_maxim("ignore Discover_Modes"); |
| break; |
| case Enter_Mode: |
| /* work around. The enter mode irq is not happened */ |
| if (vdm_header.BITS.Standard_Vendor_ID |
| == SAMSUNG_VENDOR_ID) { |
| usbc_data->is_samsung_accessory_enter_mode = 1; |
| msg_maxim("dex mode enter_mode ack status!"); |
| } else |
| msg_maxim("ignore Enter_Mode"); |
| break; |
| case Exit_Mode: |
| msg_maxim("ignore Exit_Mode"); |
| break; |
| case Attention: |
| msg_maxim("ignore Attention"); |
| break; |
| case Configure: |
| break; |
| default: |
| msg_maxim("vdm_command isn't valid[%x]", vdm_command); |
| break; |
| }; |
| } else if (vdm_response == SEC_UVDM_ININIATOR) { |
| switch (vdm_command) { |
| case Attention: |
| /* Attention message is not able to be received via 0x48 OPCode */ |
| /* Check requested vdm command and responded vdm command */ |
| { |
| /* Read requested vdm command */ |
| max77705_read_reg(usbc_data->muic, 0x23, &reqd_vdm_command); |
| reqd_vdm_command &= 0x1F; /* Command bit, b4...0 */ |
| |
| if (reqd_vdm_command == Configure) { |
| W_DATA = 1 << (usbc_data->dp_selected_pin - 1); |
| /* Retry Configure message */ |
| msg_maxim("Retry Configure message, W_DATA = %x, dp_selected_pin = %d", |
| W_DATA, usbc_data->dp_selected_pin); |
| max77705_vdm_process_set_DP_configure_mode_req(usbc_data, W_DATA); |
| } |
| } |
| break; |
| case Discover_Identity: |
| case Discover_SVIDs: |
| case Discover_Modes: |
| case Enter_Mode: |
| case Configure: |
| default: |
| /* Nothing */ |
| break; |
| }; |
| } else |
| msg_maxim("vdm_response is error value[%x]", vdm_response); |
| break; |
| case SEC_UVDM_UNSTRUCTURED_VDM: |
| max77705_sec_unstructured_message_handler(usbc_data, data, len + OPCODE_SIZE); |
| break; |
| default: |
| msg_maxim("vdm_type isn't valid error"); |
| break; |
| }; |
| break; |
| }; |
| break; |
| case OPCODE_SET_ALTERNATEMODE: |
| usbc_data->max77705->set_altmode = 1; |
| break; |
| case OPCODE_READ_SELFTEST: |
| max77705_response_selftest_read(usbc_data, data); |
| break; |
| #ifdef CONFIG_MAX77705_GRL_ENABLE |
| case OPCODE_GRL_COMMAND: |
| max77705_set_forcetrimi(usbc_data); |
| break; |
| #else |
| case OPCODE_FW_OPCODE_CLEAR: |
| msg_maxim("Cleared FW OPCODE"); |
| break; |
| #endif |
| #ifdef MAX77705_RAM_TEST |
| case OPCODE_RAM_TEST_COMMAND: |
| msg_maxim("MXIM DEBUG : [%x], [%x], [%x]", data[0], data[1], data[2]); |
| if (data[1] == MAX77705_RAM_TEST_SUCCESS && data[2] == MAX77705_RAM_TEST_SUCCESS) { |
| msg_maxim("MXIM DEBUG : the RAM Testing is OK"); |
| usbc_data->ram_test_result = MAX77705_RAM_TEST_RESULT_SUCCESS; |
| usbc_data->ram_test_retry = 0; |
| } else { |
| if (data[1] == MAX77705_RAM_TEST_FAIL && data[2] == MAX77705_RAM_TEST_FAIL) { |
| msg_maxim("MXIM DEBUG : the RAM Testing is FAIL : [%x], [%x], [%x]",data[0], data[1],data[2]); |
| usbc_data->ram_test_result = MAX77705_RAM_TEST_RESULT_FAIL_USBC_FUELGAUAGE; |
| usbc_data->ram_test_retry = 0; |
| } else if (data[1] == MAX77705_RAM_TEST_SUCCESS && data[2] == MAX77705_RAM_TEST_FAIL) { |
| msg_maxim("MXIM DEBUG : the USBC RAM Testing is FAIL : [%x], [%x], [%x]",data[0], data[1],data[2]); |
| usbc_data->ram_test_result = MAX77705_RAM_TEST_RESULT_FAIL_USBC; |
| usbc_data->ram_test_retry = 0; |
| } else if (data[1] == MAX77705_RAM_TEST_FAIL && data[2] == MAX77705_RAM_TEST_SUCCESS) { |
| msg_maxim("MXIM DEBUG : the FG RAM Testing is FAIL : [%x], [%x], [%x]",data[0], data[1],data[2]); |
| usbc_data->ram_test_result = MAX77705_RAM_TEST_RESULT_FAIL_FUELGAUAGE; |
| usbc_data->ram_test_retry = 0; |
| } else { |
| msg_maxim("MXIM DEBUG : the RAM Testing is WRONG : [%x], [%x], [%x], [%x], [%x],mode: [%x], cnt : [%x]", |
| data[0], data[1],data[2], data[3], data[4], |
| usbc_data->ram_test_enable,usbc_data->ram_test_retry); |
| if (usbc_data->ram_test_enable == MAX77705_RAM_TEST_START_MODE) { |
| usbc_data->ram_test_retry = MAX77705_RAM_TEST_RETRY_COUNT; |
| usbc_data->ram_test_enable = MAX77705_RAM_TEST_RETRY_MODE; |
| } |
| if (!usbc_data->ram_test_retry) { |
| usbc_data->ram_test_enable = MAX77705_RAM_TEST_STOP_MODE; |
| usbc_data->ram_test_retry = 0; |
| msg_maxim("MXIM DEBUG : the RAM Testing is FAIL : [%x], [%x], [%x], [%x], [%x], cnt : [%x]", |
| data[0], data[1],data[2],data[3], data[4],usbc_data->ram_test_retry); |
| } |
| usbc_data->ram_test_retry--; |
| } |
| |
| } |
| break; |
| #endif |
| case OPCODE_CTRLREG3_R: |
| max77705_control3_read_complete(usbc_data, data); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void max77705_usbc_dequeue_queue(struct max77705_usbc_platform_data *usbc_data) |
| { |
| usbc_cmd_data cmd_data; |
| usbc_cmd_queue_t *cmd_queue = NULL; |
| |
| cmd_queue = &(usbc_data->usbc_cmd_queue); |
| |
| init_usbc_cmd_data(&cmd_data); |
| |
| if (is_empty_usbc_cmd_queue(cmd_queue)) { |
| msg_maxim("Queue, Empty"); |
| return; |
| } |
| |
| dequeue_usbc_cmd(cmd_queue, &cmd_data); |
| msg_maxim("!! Dequeue queue : opcode : %x, 1st data : %x. 2st data : %x", |
| cmd_data.write_data[0], |
| cmd_data.read_data[0], |
| cmd_data.val); |
| } |
| |
| #ifndef CONFIG_MAX77705_GRL_ENABLE |
| static void max77705_usbc_clear_fw_queue(struct max77705_usbc_platform_data *usbc_data) |
| { |
| usbc_cmd_data write_data; |
| |
| msg_maxim("called"); |
| |
| init_usbc_cmd_data(&write_data); |
| write_data.opcode = OPCODE_FW_OPCODE_CLEAR; |
| max77705_usbc_opcode_write(usbc_data, &write_data); |
| } |
| #endif |
| |
| void max77705_usbc_clear_queue(struct max77705_usbc_platform_data *usbc_data) |
| { |
| usbc_cmd_data cmd_data; |
| usbc_cmd_queue_t *cmd_queue = NULL; |
| |
| mutex_lock(&usbc_data->op_lock); |
| msg_maxim("IN"); |
| cmd_queue = &(usbc_data->usbc_cmd_queue); |
| |
| while (!is_empty_usbc_cmd_queue(cmd_queue)) { |
| init_usbc_cmd_data(&cmd_data); |
| dequeue_usbc_cmd(cmd_queue, &cmd_data); |
| if (max77705_check_recover_opcode(cmd_data.opcode)) |
| usbc_data->recover_opcode_list[cmd_data.opcode] |
| = usbc_data->need_recover = true; |
| } |
| usbc_data->opcode_stamp = 0; |
| msg_maxim("OUT"); |
| mutex_unlock(&usbc_data->op_lock); |
| #ifndef CONFIG_MAX77705_GRL_ENABLE |
| /* also clear fw opcode queue to sync with driver */ |
| max77705_usbc_clear_fw_queue(usbc_data); |
| #endif |
| } |
| |
| static void max77705_usbc_cmd_run(struct max77705_usbc_platform_data *usbc_data) |
| { |
| usbc_cmd_queue_t *cmd_queue = NULL; |
| usbc_cmd_node *run_node; |
| usbc_cmd_data cmd_data; |
| int ret = 0; |
| |
| cmd_queue = &(usbc_data->usbc_cmd_queue); |
| |
| |
| run_node = kzalloc(sizeof(usbc_cmd_node), GFP_KERNEL); |
| if (!run_node) { |
| msg_maxim("failed to allocate muic command queue"); |
| return; |
| } |
| |
| init_usbc_cmd_node(run_node); |
| |
| init_usbc_cmd_data(&cmd_data); |
| |
| if (is_empty_usbc_cmd_queue(cmd_queue)) { |
| msg_maxim("Queue, Empty"); |
| kfree(run_node); |
| return; |
| } |
| |
| dequeue_usbc_cmd(cmd_queue, &cmd_data); |
| |
| if (is_usbc_notifier_opcode(cmd_data.opcode)) { |
| max77705_notify_execute(usbc_data, &cmd_data); |
| max77705_usbc_cmd_run(usbc_data); |
| } else if (cmd_data.opcode == OPCODE_NONE) {/* Apcmdres isr */ |
| msg_maxim("Apcmdres ISR !!!"); |
| max77705_irq_execute(usbc_data, &cmd_data); |
| usbc_data->opcode_stamp = 0; |
| max77705_usbc_cmd_run(usbc_data); |
| } else { /* No ISR */ |
| msg_maxim("No ISR"); |
| copy_usbc_cmd_data(&cmd_data, &(usbc_data->last_opcode)); |
| ret = max77705_i2c_opcode_write(usbc_data, cmd_data.opcode, |
| cmd_data.write_length, cmd_data.write_data); |
| if (ret < 0) { |
| msg_maxim("i2c write fail. dequeue opcode"); |
| max77705_usbc_dequeue_queue(usbc_data); |
| } |
| } |
| kfree(run_node); |
| } |
| |
| void max77705_usbc_opcode_write(struct max77705_usbc_platform_data *usbc_data, |
| usbc_cmd_data *write_op) |
| { |
| usbc_cmd_queue_t *cmd_queue = &(usbc_data->usbc_cmd_queue); |
| usbc_cmd_data execute_cmd_data; |
| usbc_cmd_data current_cmd; |
| |
| mutex_lock(&usbc_data->op_lock); |
| init_usbc_cmd_data(¤t_cmd); |
| |
| /* the messages sent to USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.opcode = write_op->opcode; |
| execute_cmd_data.write_length = write_op->write_length; |
| execute_cmd_data.is_uvdm = write_op->is_uvdm; |
| memcpy(execute_cmd_data.write_data, write_op->write_data, OPCODE_DATA_LENGTH); |
| execute_cmd_data.seq = OPCODE_WRITE_SEQ; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| /* the messages recevied From USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.response = write_op->opcode; |
| execute_cmd_data.read_length = write_op->read_length; |
| execute_cmd_data.is_uvdm = write_op->is_uvdm; |
| execute_cmd_data.seq = OPCODE_WRITE_SEQ; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| msg_maxim("W->W opcode[0x%02x] write_length[%d] read_length[%d]", |
| write_op->opcode, write_op->write_length, write_op->read_length); |
| |
| front_usbc_cmd(cmd_queue, ¤t_cmd); |
| if (current_cmd.opcode == write_op->opcode) |
| max77705_usbc_cmd_run(usbc_data); |
| else { |
| msg_maxim("!!!current_cmd.opcode [0x%02x][0x%02x], read_op->opcode[0x%02x]", |
| current_cmd.opcode, current_cmd.response, write_op->opcode); |
| if (usbc_data->opcode_stamp != 0 && current_cmd.opcode == OPCODE_NONE) { |
| if (time_after(jiffies, |
| usbc_data->opcode_stamp + MAX77705_MAX_APDCMD_TIME)) { |
| usbc_data->opcode_stamp = 0; |
| msg_maxim("error. we will dequeue response data"); |
| max77705_usbc_dequeue_queue(usbc_data); |
| max77705_usbc_cmd_run(usbc_data); |
| } |
| } |
| } |
| mutex_unlock(&usbc_data->op_lock); |
| } |
| |
| void max77705_usbc_opcode_read(struct max77705_usbc_platform_data *usbc_data, |
| usbc_cmd_data *read_op) |
| { |
| usbc_cmd_queue_t *cmd_queue = &(usbc_data->usbc_cmd_queue); |
| usbc_cmd_data execute_cmd_data; |
| usbc_cmd_data current_cmd; |
| |
| mutex_lock(&usbc_data->op_lock); |
| init_usbc_cmd_data(¤t_cmd); |
| |
| /* the messages sent to USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.opcode = read_op->opcode; |
| execute_cmd_data.write_length = read_op->write_length; |
| execute_cmd_data.is_uvdm = read_op->is_uvdm; |
| memcpy(execute_cmd_data.write_data, read_op->write_data, read_op->write_length); |
| execute_cmd_data.seq = OPCODE_READ_SEQ; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| /* the messages recevied From USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.response = read_op->opcode; |
| execute_cmd_data.read_length = read_op->read_length; |
| execute_cmd_data.is_uvdm = read_op->is_uvdm; |
| execute_cmd_data.seq = OPCODE_READ_SEQ; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| msg_maxim("R->R opcode[0x%02x] write_length[%d] read_length[%d]", |
| read_op->opcode, read_op->write_length, read_op->read_length); |
| |
| front_usbc_cmd(cmd_queue, ¤t_cmd); |
| if (current_cmd.opcode == read_op->opcode) |
| max77705_usbc_cmd_run(usbc_data); |
| else { |
| msg_maxim("!!!current_cmd.opcode [0x%02x][0x%02x], read_op->opcode[0x%02x]", |
| current_cmd.opcode, current_cmd.response, read_op->opcode); |
| if (usbc_data->opcode_stamp != 0 && current_cmd.opcode == OPCODE_NONE) { |
| if (time_after(jiffies, |
| usbc_data->opcode_stamp + MAX77705_MAX_APDCMD_TIME)) { |
| usbc_data->opcode_stamp = 0; |
| msg_maxim("error. we will dequeue response data"); |
| max77705_usbc_dequeue_queue(usbc_data); |
| max77705_usbc_cmd_run(usbc_data); |
| } |
| } |
| } |
| |
| mutex_unlock(&usbc_data->op_lock); |
| } |
| |
| void max77705_usbc_opcode_update(struct max77705_usbc_platform_data *usbc_data, |
| usbc_cmd_data *update_op) |
| { |
| usbc_cmd_queue_t *cmd_queue = &(usbc_data->usbc_cmd_queue); |
| usbc_cmd_data execute_cmd_data; |
| usbc_cmd_data current_cmd; |
| |
| switch (update_op->opcode) { |
| case OPCODE_BCCTRL1_R: |
| case OPCODE_BCCTRL2_R: |
| case OPCODE_CTRL1_R: |
| case OPCODE_CTRL2_R: |
| case OPCODE_CTRL3_R: |
| case OPCODE_CCCTRL1_R: |
| case OPCODE_CCCTRL2_R: |
| case OPCODE_CCCTRL3_R: |
| case OPCODE_HVCTRL_R: |
| case OPCODE_OPCODE_VCONN_ILIM_R: |
| case OPCODE_CHGIN_ILIM_R: |
| case OPCODE_CHGIN_ILIM2_R: |
| break; |
| default: |
| pr_err("%s: invalid usage(0x%x), return\n", __func__, update_op->opcode); |
| return; |
| } |
| |
| mutex_lock(&usbc_data->op_lock); |
| init_usbc_cmd_data(¤t_cmd); |
| |
| /* the messages sent to USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.opcode = update_op->opcode; |
| execute_cmd_data.write_length = 0; |
| execute_cmd_data.is_uvdm = update_op->is_uvdm; |
| memcpy(execute_cmd_data.write_data, update_op->write_data, update_op->write_length); |
| execute_cmd_data.seq = OPCODE_UPDATE_SEQ; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| /* the messages recevied From USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.response = update_op->opcode; |
| execute_cmd_data.read_length = 1; |
| execute_cmd_data.seq = OPCODE_UPDATE_SEQ; |
| execute_cmd_data.val = update_op->val; |
| execute_cmd_data.mask = update_op->mask; |
| execute_cmd_data.is_uvdm = update_op->is_uvdm; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| msg_maxim("U->U opcode[0x%02x] write_length[%d] read_length[%d]", |
| update_op->opcode, update_op->write_length, update_op->read_length); |
| |
| front_usbc_cmd(cmd_queue, ¤t_cmd); |
| if (current_cmd.opcode == update_op->opcode) |
| max77705_usbc_cmd_run(usbc_data); |
| else { |
| msg_maxim("!!! current_cmd.opcode [0x%02x], update_op->opcode[0x%02x]", |
| current_cmd.opcode, update_op->opcode); |
| if (usbc_data->opcode_stamp != 0 && current_cmd.opcode == OPCODE_NONE) { |
| if (time_after(jiffies, |
| usbc_data->opcode_stamp + MAX77705_MAX_APDCMD_TIME)) { |
| usbc_data->opcode_stamp = 0; |
| msg_maxim("error. we will dequeue response data"); |
| max77705_usbc_dequeue_queue(usbc_data); |
| max77705_usbc_cmd_run(usbc_data); |
| } |
| } |
| } |
| |
| mutex_unlock(&usbc_data->op_lock); |
| } |
| |
| void max77705_usbc_opcode_push(struct max77705_usbc_platform_data *usbc_data, |
| usbc_cmd_data *read_op) |
| { |
| usbc_cmd_queue_t *cmd_queue = &(usbc_data->usbc_cmd_queue); |
| usbc_cmd_data execute_cmd_data; |
| usbc_cmd_data current_cmd; |
| |
| init_usbc_cmd_data(¤t_cmd); |
| |
| /* the messages sent to USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.opcode = read_op->opcode; |
| execute_cmd_data.write_length = read_op->write_length; |
| execute_cmd_data.is_uvdm = read_op->is_uvdm; |
| memcpy(execute_cmd_data.write_data, read_op->write_data, read_op->write_length); |
| execute_cmd_data.seq = OPCODE_PUSH_SEQ; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| /* the messages recevied From USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.response = read_op->opcode; |
| execute_cmd_data.read_length = read_op->read_length; |
| execute_cmd_data.is_uvdm = read_op->is_uvdm; |
| execute_cmd_data.seq = OPCODE_PUSH_SEQ; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| msg_maxim("P->P opcode[0x%02x] write_length[%d] read_length[%d]", |
| read_op->opcode, read_op->write_length, read_op->read_length); |
| } |
| |
| void max77705_usbc_opcode_rw(struct max77705_usbc_platform_data *usbc_data, |
| usbc_cmd_data *read_op, usbc_cmd_data *write_op) |
| { |
| usbc_cmd_queue_t *cmd_queue = &(usbc_data->usbc_cmd_queue); |
| usbc_cmd_data execute_cmd_data; |
| usbc_cmd_data current_cmd; |
| |
| mutex_lock(&usbc_data->op_lock); |
| init_usbc_cmd_data(¤t_cmd); |
| |
| /* the messages sent to USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.opcode = read_op->opcode; |
| execute_cmd_data.write_length = read_op->write_length; |
| execute_cmd_data.is_uvdm = read_op->is_uvdm; |
| memcpy(execute_cmd_data.write_data, read_op->write_data, read_op->write_length); |
| execute_cmd_data.seq = OPCODE_RW_SEQ; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| /* the messages recevied From USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.response = read_op->opcode; |
| execute_cmd_data.read_length = read_op->read_length; |
| execute_cmd_data.is_uvdm = read_op->is_uvdm; |
| execute_cmd_data.seq = OPCODE_RW_SEQ; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| /* the messages sent to USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.opcode = write_op->opcode; |
| execute_cmd_data.write_length = write_op->write_length; |
| execute_cmd_data.is_uvdm = write_op->is_uvdm; |
| memcpy(execute_cmd_data.write_data, write_op->write_data, OPCODE_DATA_LENGTH); |
| execute_cmd_data.seq = OPCODE_RW_SEQ; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| /* the messages recevied From USBC. */ |
| init_usbc_cmd_data(&execute_cmd_data); |
| execute_cmd_data.response = write_op->opcode; |
| execute_cmd_data.read_length = write_op->read_length; |
| execute_cmd_data.is_uvdm = write_op->is_uvdm; |
| execute_cmd_data.seq = OPCODE_RW_SEQ; |
| enqueue_usbc_cmd(cmd_queue, &execute_cmd_data); |
| |
| msg_maxim("RW->R opcode[0x%02x] write_length[%d] read_length[%d]", |
| read_op->opcode, read_op->write_length, read_op->read_length); |
| msg_maxim("RW->W opcode[0x%02x] write_length[%d] read_length[%d]", |
| write_op->opcode, write_op->write_length, write_op->read_length); |
| |
| front_usbc_cmd(cmd_queue, ¤t_cmd); |
| if (current_cmd.opcode == read_op->opcode) |
| max77705_usbc_cmd_run(usbc_data); |
| else { |
| msg_maxim("!!! current_cmd.opcode [0x%02x], read_op->opcode[0x%02x]", |
| current_cmd.opcode, read_op->opcode); |
| if (usbc_data->opcode_stamp != 0 && current_cmd.opcode == OPCODE_NONE) { |
| if (time_after(jiffies, |
| usbc_data->opcode_stamp + MAX77705_MAX_APDCMD_TIME)) { |
| usbc_data->opcode_stamp = 0; |
| msg_maxim("error. we will dequeue response data"); |
| max77705_usbc_dequeue_queue(usbc_data); |
| max77705_usbc_cmd_run(usbc_data); |
| } |
| } |
| } |
| |
| mutex_unlock(&usbc_data->op_lock); |
| } |
| |
| /* |
| * max77705_uic_op_send_work_func func - Send OPCode |
| * @work: work_struct of max77705_i2c |
| * |
| * Wait for OPCode response |
| */ |
| static void max77705_uic_op_send_work_func( |
| struct work_struct *work) |
| { |
| struct max77705_usbc_platform_data *usbc_data; |
| struct max77705_opcode opcodes[] = { |
| { |
| .opcode = OPCODE_BCCTRL1_R, |
| .data = { 0, }, |
| .write_length = 0, |
| .read_length = 1, |
| }, |
| { |
| .opcode = OPCODE_BCCTRL1_W, |
| .data = { 0x20, }, |
| .write_length = 1, |
| .read_length = 0, |
| }, |
| { |
| .opcode = OPCODE_BCCTRL2_R, |
| .data = { 0, }, |
| .write_length = 0, |
| .read_length = 1, |
| }, |
| { |
| .opcode = OPCODE_BCCTRL2_W, |
| .data = { 0x10, }, |
| .write_length = 1, |
| .read_length = 0, |
| }, |
| { |
| .opcode = OPCODE_CURRENT_SRCCAP, |
| .data = { 0, }, |
| .write_length = 1, |
| .read_length = 4, |
| }, |
| { |
| .opcode = OPCODE_AFC_HV_W, |
| .data = { 0x46, }, |
| .write_length = 1, |
| .read_length = 2, |
| }, |
| { |
| .opcode = OPCODE_SRCCAP_REQUEST, |
| .data = { 0x00, }, |
| .write_length = 1, |
| .read_length = 32, |
| }, |
| }; |
| int op_loop; |
| |
| usbc_data = container_of(work, struct max77705_usbc_platform_data, op_send_work); |
| |
| msg_maxim("IN"); |
| for (op_loop = 0; op_loop < ARRAY_SIZE(opcodes); op_loop++) { |
| if (usbc_data->op_code == opcodes[op_loop].opcode) { |
| max77705_i2c_opcode_write(usbc_data, opcodes[op_loop].opcode, |
| opcodes[op_loop].write_length, opcodes[op_loop].data); |
| break; |
| } |
| } |
| msg_maxim("OUT"); |
| } |
| |
| static void max77705_reset_ic(struct max77705_usbc_platform_data *usbc_data) |
| { |
| max77705_write_reg(usbc_data->muic, 0x80, 0x0F); |
| msleep(100); /* need 100ms delay */ |
| } |
| |
| void max77705_usbc_check_sysmsg(struct max77705_usbc_platform_data *usbc_data, u8 sysmsg) |
| { |
| usbc_cmd_queue_t *cmd_queue = &(usbc_data->usbc_cmd_queue); |
| bool is_empty_queue = is_empty_usbc_cmd_queue(cmd_queue); |
| usbc_cmd_data cmd_data; |
| usbc_cmd_data next_cmd_data; |
| u8 next_opcode = 0xFF; |
| u8 interrupt; |
| #if defined(CONFIG_USB_HW_PARAM) |
| struct otg_notify *o_notify = get_otg_notify(); |
| #endif |
| #ifdef CONFIG_USB_NOTIFY_PROC_LOG |
| int event; |
| #endif |
| int ret = 0; |
| |
| if (usbc_data->shut_down) { |
| msg_maxim("IGNORE SYSTEM_MSG IN SHUTDOWN MODE!!"); |
| return; |
| } |
| |
| switch (sysmsg) { |
| case SYSERROR_NONE: |
| break; |
| case SYSERROR_BOOT_WDT: |
| usbc_data->watchdog_count++; |
| msg_maxim("SYSERROR_BOOT_WDT: %d", usbc_data->watchdog_count); |
| /*Turn off Vbus*/ |
| if (usbc_data->cc_data->current_pr == SRC) |
| max77705_vbus_turn_on_ctrl(usbc_data, OFF, false); |
| max77705_usbc_mask_irq(usbc_data); |
| /*Reset USBC Block*/ |
| max77705_reset_ic(usbc_data); |
| max77705_write_reg(usbc_data->muic, REG_UIC_INT_M, REG_UIC_INT_M_INIT); |
| max77705_write_reg(usbc_data->muic, REG_CC_INT_M, REG_CC_INT_M_INIT); |
| max77705_write_reg(usbc_data->muic, REG_PD_INT_M, REG_PD_INT_M_INIT); |
| max77705_write_reg(usbc_data->muic, REG_VDM_INT_M, REG_VDM_INT_M_INIT); |
| /* clear UIC_INT to prevent infinite sysmsg irq*/ |
| g_usbc_data->max77705->enable_nested_irq = 1; |
| max77705_read_reg(usbc_data->muic, MAX77705_USBC_REG_UIC_INT, &interrupt); |
| g_usbc_data->max77705->usbc_irq = interrupt & 0xBF; //clear the USBC SYSTEM IRQ |
| max77705_usbc_clear_queue(usbc_data); |
| usbc_data->is_first_booting = 1; |
| max77705_init_opcode(usbc_data, 1); |
| max77705_usbc_umask_irq(usbc_data); |
| #ifdef MAX77705_RAM_TEST |
| if(usbc_data->ram_test_enable == MAX77705_RAM_TEST_RETRY_MODE) { |
| mdelay(100); |
| max77705_verify_ram_bist_write(usbc_data); |
| } else { |
| usbc_data->ram_test_enable = MAX77705_RAM_TEST_STOP_MODE; |
| } |
| #endif |
| #ifdef CONFIG_USB_NOTIFY_PROC_LOG |
| event = NOTIFY_EXTRA_SYSERROR_BOOT_WDT; |
| store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL); |
| #endif |
| break; |
| case SYSERROR_BOOT_SWRSTREQ: |
| break; |
| case SYSMSG_BOOT_POR: |
| usbc_data->por_count++; |
| max77705_usbc_mask_irq(usbc_data); |
| max77705_reset_ic(usbc_data); |
| max77705_write_reg(usbc_data->muic, REG_UIC_INT_M, REG_UIC_INT_M_INIT); |
| max77705_write_reg(usbc_data->muic, REG_CC_INT_M, REG_CC_INT_M_INIT); |
| max77705_write_reg(usbc_data->muic, REG_PD_INT_M, REG_PD_INT_M_INIT); |
| max77705_write_reg(usbc_data->muic, REG_VDM_INT_M, REG_VDM_INT_M_INIT); |
| /* clear UIC_INT to prevent infinite sysmsg irq */ |
| g_usbc_data->max77705->enable_nested_irq = 1; |
| max77705_read_reg(usbc_data->muic, MAX77705_USBC_REG_UIC_INT, &interrupt); |
| g_usbc_data->max77705->usbc_irq = interrupt & 0xBF; //clear the USBC SYSTEM IRQ |
| msg_maxim("SYSERROR_BOOT_POR: %d, UIC_INT:0x%02x", usbc_data->por_count, interrupt); |
| max77705_usbc_clear_queue(usbc_data); |
| usbc_data->is_first_booting = 1; |
| max77705_init_opcode(usbc_data, 1); |
| max77705_usbc_umask_irq(usbc_data); |
| #ifdef CONFIG_USB_NOTIFY_PROC_LOG |
| event = NOTIFY_EXTRA_SYSMSG_BOOT_POR; |
| store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL); |
| #endif |
| break; |
| case SYSERROR_HV_NOVBUS: |
| break; |
| case SYSERROR_HV_FMETHOD_RXPERR: |
| break; |
| case SYSERROR_HV_FMETHOD_RXBUFOW: |
| break; |
| case SYSERROR_HV_FMETHOD_RXTFR: |
| break; |
| case SYSERROR_HV_FMETHOD_MPNACK: |
| break; |
| case SYSERROR_HV_FMETHOD_RESET_FAIL: |
| break; |
| case SYSMsg_AFC_Done: |
| break; |
| case SYSERROR_SYSPOS: |
| break; |
| case SYSERROR_APCMD_UNKNOWN: |
| break; |
| case SYSERROR_APCMD_INPROGRESS: |
| break; |
| case SYSERROR_APCMD_FAIL: |
| |
| init_usbc_cmd_data(&cmd_data); |
| init_usbc_cmd_data(&next_cmd_data); |
| |
| if (front_usbc_cmd(cmd_queue, &next_cmd_data)) |
| next_opcode = next_cmd_data.response; |
| |
| if (!is_empty_queue) { |
| copy_usbc_cmd_data(&(usbc_data->last_opcode), &cmd_data); |
| |
| #ifdef CONFIG_MAX77705_GRL_ENABLE |
| if (cmd_data.opcode == OPCODE_GRL_COMMAND || next_opcode == OPCODE_VDM_DISCOVER_SET_VDM_REQ) |
| #else |
| if (next_opcode == OPCODE_VDM_DISCOVER_SET_VDM_REQ) |
| #endif |
| { |
| usbc_data->opcode_stamp = 0; |
| max77705_usbc_dequeue_queue(usbc_data); |
| cmd_data.opcode = OPCODE_NONE; |
| } |
| |
| if ((cmd_data.opcode != OPCODE_NONE) && (cmd_data.opcode == next_opcode)) { |
| if (next_opcode != OPCODE_VDM_DISCOVER_SET_VDM_REQ) { |
| ret = max77705_i2c_opcode_write(usbc_data, |
| cmd_data.opcode, |
| cmd_data.write_length, |
| cmd_data.write_data); |
| if (ret) { |
| msg_maxim("i2c write fail. dequeue opcode"); |
| max77705_usbc_dequeue_queue(usbc_data); |
| } else |
| msg_maxim("RETRY SUCCESS : %x, %x", cmd_data.opcode, next_opcode); |
| } else |
| msg_maxim("IGNORE COMMAND : %x, %x", cmd_data.opcode, next_opcode); |
| } else { |
| msg_maxim("RETRY FAILED : %x, %x", cmd_data.opcode, next_opcode); |
| } |
| |
| } |
| |
| #if defined(CONFIG_HV_MUIC_MAX77705_AFC) |
| max77705_muic_disable_afc_protocol(usbc_data->muic_data); |
| #endif |
| /* TO DO DEQEUE MSG. */ |
| break; |
| #ifdef CONFIG_MAX77705_GRL_ENABLE |
| case SYSMSG_SET_GRL: |
| max77705_usbc_clear_queue(usbc_data); |
| msg_maxim("SYSTEM MESSAGE GRL COMMAND!!!"); |
| |
| init_usbc_cmd_data(&cmd_data); |
| cmd_data.opcode = OPCODE_GRL_COMMAND; |
| cmd_data.write_data[0] = 0x1; |
| cmd_data.write_length = 0x1; |
| cmd_data.read_length = 0x2; |
| max77705_usbc_opcode_write(usbc_data, &cmd_data); |
| // max77705_set_forcetrimi(usbc_data); |
| break; |
| #endif |
| case SYSMSG_CCx_5V_SHORT: |
| msg_maxim("CC-VBUS SHORT"); |
| #if defined(CONFIG_USB_HW_PARAM) |
| if (o_notify) |
| inc_hw_param(o_notify, USB_CCIC_VBUS_CC_SHORT_COUNT); |
| #endif |
| #ifdef CONFIG_USB_NOTIFY_PROC_LOG |
| event = NOTIFY_EXTRA_SYSMSG_CC_SHORT; |
| store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL); |
| #endif |
| usbc_data->cc_data->ccistat = CCI_SHORT; |
| max77705_notify_rp_current_level(usbc_data); |
| break; |
| case SYSMSG_SBUx_GND_SHORT: |
| msg_maxim("SBU-GND SHORT"); |
| #if defined(CONFIG_USB_HW_PARAM) |
| if (o_notify) |
| inc_hw_param(o_notify, USB_CCIC_GND_SBU_SHORT_COUNT); |
| #endif |
| #ifdef CONFIG_USB_NOTIFY_PROC_LOG |
| event = NOTIFY_EXTRA_SYSMSG_SBU_GND_SHORT; |
| store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL); |
| #endif |
| break; |
| case SYSMSG_SBUx_5V_SHORT: |
| msg_maxim("SBU-VBUS SHORT"); |
| #if defined(CONFIG_USB_HW_PARAM) |
| if (o_notify) |
| inc_hw_param(o_notify, USB_CCIC_VBUS_SBU_SHORT_COUNT); |
| #endif |
| #ifdef CONFIG_USB_NOTIFY_PROC_LOG |
| event = NOTIFY_EXTRA_SYSMSG_SBU_VBUS_SHORT; |
| store_usblog_notify(NOTIFY_EXTRA, (void *)&event, NULL); |
| #endif |
| break; |
| case SYSMSG_PD_CCx_5V_SHORT: |
| msg_maxim("PD_CC-VBUS SHORT"); |
| usbc_data->pd_data->cc_sbu_short = true; |
| break; |
| case SYSMSG_PD_SBUx_5V_SHORT: |
| msg_maxim("PD_SBU-VBUS SHORT"); |
| usbc_data->pd_data->cc_sbu_short = true; |
| break; |
| case SYSMSG_PD_SHORT_NONE: |
| msg_maxim("Cable detach"); |
| usbc_data->pd_data->cc_sbu_short = false; |
| break; |
| case SYSERROR_DROP5V_SRCRDY: |
| msg_maxim("vbus drop during source ready"); |
| break; |
| case SYSERROR_DROP5V_SNKRDY: |
| msg_maxim("vbus drop during sink ready"); |
| break; |
| case SYSMSG_PD_GENDER_SHORT: |
| msg_maxim("PD_GENDER_SHORT"); |
| usbc_data->pd_data->cc_sbu_short = true; |
| break; |
| case SYSERROR_POWER_NEGO: |
| if (!usbc_data->last_opcode.is_uvdm) { /* structured vdm */ |
| if (usbc_data->last_opcode.opcode == OPCODE_VDM_DISCOVER_SET_VDM_REQ) { |
| max77705_usbc_opcode_write(usbc_data, &usbc_data->last_opcode); |
| msg_maxim("SYSMSG PWR NEGO ERR : VDM request retry"); |
| } |
| } else { /* unstructured vdm */ |
| usbc_data->uvdm_error = -EACCES; |
| msg_maxim("SYSMSG PWR NEGO ERR : UVDM request error - dir : %d", |
| usbc_data->is_in_sec_uvdm_out); |
| if (usbc_data->is_in_sec_uvdm_out == DIR_OUT) |
| complete(&usbc_data->uvdm_longpacket_out_wait); |
| else if (usbc_data->is_in_sec_uvdm_out == DIR_IN) |
| complete(&usbc_data->uvdm_longpacket_in_wait); |
| else |
| ; |
| } |
| break; |
| #if defined(CONFIG_SEC_FACTORY) |
| case SYSERROR_FACTORY_RID0: |
| factory_execute_monitor(FAC_ABNORMAL_REPEAT_RID0); |
| break; |
| #endif |
| case SYSERROR_CCRP_HIGH: |
| msg_maxim("CCRP HIGH"); |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| if (usbc_data->ccrp_state != 1) { |
| usbc_data->ccrp_state = 1; |
| max77705_ccic_event_work(usbc_data, |
| CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_WATER_CABLE, |
| CCIC_NOTIFY_ATTACH, 0/*rprd*/, 0); |
| } |
| #endif |
| break; |
| case SYSERROR_CCRP_LOW: |
| msg_maxim("CCRP LOW"); |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| if (usbc_data->ccrp_state != 0) { |
| usbc_data->ccrp_state = 0; |
| max77705_ccic_event_work(usbc_data, |
| CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_WATER_CABLE, |
| CCIC_NOTIFY_DETACH, 0/*rprd*/, 0); |
| } |
| #endif |
| break; |
| case SYSMSG_10K_TO_22K ... SYSMSG_22K_TO_56K: |
| msg_maxim("TypeC earphone is attached"); |
| usbc_data->pd_data->cc_sbu_short = true; |
| max77705_check_pdo(usbc_data); |
| break; |
| case SYSMSG_56K_TO_22K ... SYSMSG_22K_TO_10K: |
| msg_maxim("TypeC earphone is detached"); |
| usbc_data->pd_data->cc_sbu_short = false; |
| max77705_check_pdo(usbc_data); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| |
| static irqreturn_t max77705_apcmd_irq(int irq, void *data) |
| { |
| struct max77705_usbc_platform_data *usbc_data = data; |
| u8 sysmsg = 0; |
| |
| msg_maxim("IRQ(%d)_IN", irq); |
| max77705_read_reg(usbc_data->muic, REG_USBC_STATUS2, &usbc_data->usbc_status2); |
| sysmsg = usbc_data->usbc_status2; |
| msg_maxim(" [IN] sysmsg : %d", sysmsg); |
| |
| mutex_lock(&usbc_data->op_lock); |
| max77705_usbc_cmd_run(usbc_data); |
| mutex_unlock(&usbc_data->op_lock); |
| |
| if (usbc_data->need_recover) { |
| max77705_recover_opcode(usbc_data, |
| usbc_data->recover_opcode_list); |
| usbc_data->need_recover = false; |
| } |
| |
| msg_maxim("IRQ(%d)_OUT", irq); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t max77705_sysmsg_irq(int irq, void *data) |
| { |
| struct max77705_usbc_platform_data *usbc_data = data; |
| u8 sysmsg = 0; |
| u8 i = 0; |
| u8 raw_data[3] = {0, }; |
| u8 usbc_status2 = 0; |
| u8 dump_reg[10] = {0, }; |
| |
| for (i = 0; i < 3; i++) { |
| usbc_status2 = 0; |
| max77705_read_reg(usbc_data->muic, REG_USBC_STATUS2, &usbc_status2); |
| raw_data[i] = usbc_status2; |
| } |
| if((raw_data[0] == raw_data[1]) && (raw_data[0] == raw_data[2])){ |
| sysmsg = raw_data[0]; |
| } else { |
| max77705_bulk_read(usbc_data->muic, REG_USBC_STATUS1, |
| 8, dump_reg); |
| msg_maxim("[ERROR ]sys_reg, %x, %x, %x", raw_data[0], raw_data[1],raw_data[2]); |
| msg_maxim("[ERROR ]dump_reg, %x, %x, %x, %x, %x, %x, %x, %x\n", dump_reg[0], dump_reg[1], |
| dump_reg[2], dump_reg[3], dump_reg[4], dump_reg[5], dump_reg[6], dump_reg[7]); |
| sysmsg = 0x6D; |
| } |
| msg_maxim("IRQ(%d)_IN sysmsg: %x", irq, sysmsg); |
| max77705_usbc_check_sysmsg(usbc_data, sysmsg); |
| usbc_data->sysmsg = sysmsg; |
| msg_maxim("IRQ(%d)_OUT sysmsg: %x", irq, sysmsg); |
| |
| return IRQ_HANDLED; |
| } |
| |
| |
| static irqreturn_t max77705_vdm_identity_irq(int irq, void *data) |
| { |
| struct max77705_usbc_platform_data *usbc_data = data; |
| MAX77705_VDM_MSG_IRQ_STATUS_Type VDM_MSG_IRQ_State; |
| |
| memset(&VDM_MSG_IRQ_State, 0, sizeof(VDM_MSG_IRQ_State)); |
| msg_maxim("IRQ(%d)_IN", irq); |
| VDM_MSG_IRQ_State.BITS.Vdm_Flag_Discover_ID = 1; |
| max77705_receive_alternate_message(usbc_data, &VDM_MSG_IRQ_State); |
| msg_maxim("IRQ(%d)_OUT", irq); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t max77705_vdm_svids_irq(int irq, void *data) |
| { |
| struct max77705_usbc_platform_data *usbc_data = data; |
| MAX77705_VDM_MSG_IRQ_STATUS_Type VDM_MSG_IRQ_State; |
| |
| memset(&VDM_MSG_IRQ_State, 0, sizeof(VDM_MSG_IRQ_State)); |
| msg_maxim("IRQ(%d)_IN", irq); |
| VDM_MSG_IRQ_State.BITS.Vdm_Flag_Discover_SVIDs = 1; |
| max77705_receive_alternate_message(usbc_data, &VDM_MSG_IRQ_State); |
| msg_maxim("IRQ(%d)_OUT", irq); |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t max77705_vdm_discover_mode_irq(int irq, void *data) |
| { |
| struct max77705_usbc_platform_data *usbc_data = data; |
| MAX77705_VDM_MSG_IRQ_STATUS_Type VDM_MSG_IRQ_State; |
| |
| memset(&VDM_MSG_IRQ_State, 0, sizeof(VDM_MSG_IRQ_State)); |
| msg_maxim("IRQ(%d)_IN", irq); |
| VDM_MSG_IRQ_State.BITS.Vdm_Flag_Discover_MODEs = 1; |
| max77705_receive_alternate_message(usbc_data, &VDM_MSG_IRQ_State); |
| msg_maxim("IRQ(%d)_OUT", irq); |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t max77705_vdm_enter_mode_irq(int irq, void *data) |
| { |
| struct max77705_usbc_platform_data *usbc_data = data; |
| MAX77705_VDM_MSG_IRQ_STATUS_Type VDM_MSG_IRQ_State; |
| |
| memset(&VDM_MSG_IRQ_State, 0, sizeof(VDM_MSG_IRQ_State)); |
| msg_maxim("IRQ(%d)_IN", irq); |
| VDM_MSG_IRQ_State.BITS.Vdm_Flag_Enter_Mode = 1; |
| max77705_receive_alternate_message(usbc_data, &VDM_MSG_IRQ_State); |
| msg_maxim("IRQ(%d)_OUT", irq); |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t max77705_vdm_dp_status_irq(int irq, void *data) |
| { |
| struct max77705_usbc_platform_data *usbc_data = data; |
| MAX77705_VDM_MSG_IRQ_STATUS_Type VDM_MSG_IRQ_State; |
| |
| memset(&VDM_MSG_IRQ_State, 0, sizeof(VDM_MSG_IRQ_State)); |
| msg_maxim("IRQ(%d)_IN", irq); |
| VDM_MSG_IRQ_State.BITS.Vdm_Flag_DP_Status_Update = 1; |
| max77705_receive_alternate_message(usbc_data, &VDM_MSG_IRQ_State); |
| msg_maxim("IRQ(%d)_OUT", irq); |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t max77705_vdm_dp_configure_irq(int irq, void *data) |
| { |
| struct max77705_usbc_platform_data *usbc_data = data; |
| MAX77705_VDM_MSG_IRQ_STATUS_Type VDM_MSG_IRQ_State; |
| |
| memset(&VDM_MSG_IRQ_State, 0, sizeof(VDM_MSG_IRQ_State)); |
| msg_maxim("IRQ(%d)_IN", irq); |
| VDM_MSG_IRQ_State.BITS.Vdm_Flag_DP_Configure = 1; |
| max77705_receive_alternate_message(usbc_data, &VDM_MSG_IRQ_State); |
| msg_maxim("IRQ(%d)_OUT", irq); |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t max77705_vdm_attention_irq(int irq, void *data) |
| { |
| struct max77705_usbc_platform_data *usbc_data = data; |
| MAX77705_VDM_MSG_IRQ_STATUS_Type VDM_MSG_IRQ_State; |
| |
| memset(&VDM_MSG_IRQ_State, 0, sizeof(VDM_MSG_IRQ_State)); |
| msg_maxim("IRQ(%d)_IN", irq); |
| VDM_MSG_IRQ_State.BITS.Vdm_Flag_Attention = 1; |
| max77705_receive_alternate_message(usbc_data, &VDM_MSG_IRQ_State); |
| msg_maxim("IRQ(%d)_OUT", irq); |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t max77705_vir_altmode_irq(int irq, void *data) |
| { |
| struct max77705_usbc_platform_data *usbc_data = data; |
| |
| msg_maxim("max77705_vir_altmode_irq"); |
| |
| if (usbc_data->shut_down) { |
| msg_maxim("%s doing shutdown. skip set alternate mode", __func__); |
| goto skip; |
| } |
| |
| max77705_set_enable_alternate_mode |
| (usbc_data->set_altmode); |
| |
| skip: |
| return IRQ_HANDLED; |
| } |
| |
| int max77705_init_irq_handler(struct max77705_usbc_platform_data *usbc_data) |
| { |
| int ret = 0; |
| |
| wake_lock_init(&usbc_data->apcmd_wake_lock, WAKE_LOCK_SUSPEND, |
| "usbc->apcmd"); |
| wake_lock_init(&usbc_data->sysmsg_wake_lock, WAKE_LOCK_SUSPEND, |
| "usbc->sysmsg"); |
| |
| usbc_data->irq_apcmd = usbc_data->irq_base + MAX77705_USBC_IRQ_APC_INT; |
| if (usbc_data->irq_apcmd) { |
| ret = request_threaded_irq(usbc_data->irq_apcmd, |
| NULL, max77705_apcmd_irq, |
| 0, |
| "usbc-apcmd-irq", usbc_data); |
| if (ret) { |
| pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret); |
| return ret; |
| } |
| } |
| |
| usbc_data->irq_sysmsg = usbc_data->irq_base + MAX77705_USBC_IRQ_SYSM_INT; |
| if (usbc_data->irq_sysmsg) { |
| ret = request_threaded_irq(usbc_data->irq_sysmsg, |
| NULL, max77705_sysmsg_irq, |
| 0, |
| "usbc-sysmsg-irq", usbc_data); |
| if (ret) { |
| pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret); |
| return ret; |
| } |
| } |
| |
| usbc_data->irq_vdm0 = usbc_data->irq_base + MAX77705_IRQ_VDM_DISCOVER_ID_INT; |
| if (usbc_data->irq_vdm0) { |
| ret = request_threaded_irq(usbc_data->irq_vdm0, |
| NULL, max77705_vdm_identity_irq, |
| 0, |
| "usbc-vdm0-irq", usbc_data); |
| if (ret) { |
| pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret); |
| return ret; |
| } |
| } |
| |
| usbc_data->irq_vdm1 = usbc_data->irq_base + MAX77705_IRQ_VDM_DISCOVER_SVIDS_INT; |
| if (usbc_data->irq_vdm1) { |
| ret = request_threaded_irq(usbc_data->irq_vdm1, |
| NULL, max77705_vdm_svids_irq, |
| 0, |
| "usbc-vdm1-irq", usbc_data); |
| if (ret) { |
| pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret); |
| return ret; |
| } |
| } |
| |
| usbc_data->irq_vdm2 = usbc_data->irq_base + MAX77705_IRQ_VDM_DISCOVER_MODES_INT; |
| if (usbc_data->irq_vdm2) { |
| ret = request_threaded_irq(usbc_data->irq_vdm2, |
| NULL, max77705_vdm_discover_mode_irq, |
| 0, |
| "usbc-vdm2-irq", usbc_data); |
| if (ret) { |
| pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret); |
| return ret; |
| } |
| } |
| |
| usbc_data->irq_vdm3 = usbc_data->irq_base + MAX77705_IRQ_VDM_ENTER_MODE_INT; |
| if (usbc_data->irq_vdm3) { |
| ret = request_threaded_irq(usbc_data->irq_vdm3, |
| NULL, max77705_vdm_enter_mode_irq, |
| 0, |
| "usbc-vdm3-irq", usbc_data); |
| if (ret) { |
| pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret); |
| return ret; |
| } |
| } |
| |
| usbc_data->irq_vdm4 = usbc_data->irq_base + MAX77705_IRQ_VDM_DP_STATUS_UPDATE_INT; |
| if (usbc_data->irq_vdm4) { |
| ret = request_threaded_irq(usbc_data->irq_vdm4, |
| NULL, max77705_vdm_dp_status_irq, |
| 0, |
| "usbc-vdm4-irq", usbc_data); |
| if (ret) { |
| pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret); |
| return ret; |
| } |
| } |
| |
| usbc_data->irq_vdm5 = usbc_data->irq_base + MAX77705_IRQ_VDM_DP_CONFIGURE_INT; |
| if (usbc_data->irq_vdm5) { |
| ret = request_threaded_irq(usbc_data->irq_vdm5, |
| NULL, max77705_vdm_dp_configure_irq, |
| 0, |
| "usbc-vdm5-irq", usbc_data); |
| if (ret) { |
| pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret); |
| return ret; |
| } |
| } |
| |
| usbc_data->irq_vdm6 = usbc_data->irq_base + MAX77705_IRQ_VDM_ATTENTION_INT; |
| if (usbc_data->irq_vdm6) { |
| ret = request_threaded_irq(usbc_data->irq_vdm6, |
| NULL, max77705_vdm_attention_irq, |
| 0, |
| "usbc-vdm6-irq", usbc_data); |
| if (ret) { |
| pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret); |
| return ret; |
| } |
| } |
| |
| usbc_data->irq_vir0 = usbc_data->irq_base + MAX77705_VIR_IRQ_ALTERROR_INT; |
| if (usbc_data->irq_vir0) { |
| ret = request_threaded_irq(usbc_data->irq_vir0, |
| NULL, max77705_vir_altmode_irq, |
| 0, |
| "usbc-vir0-irq", usbc_data); |
| if (ret) { |
| pr_err("%s: Failed to Request IRQ (%d)\n", __func__, ret); |
| return ret; |
| } |
| } |
| |
| return ret; |
| } |
| |
| static void max77705_usbc_umask_irq(struct max77705_usbc_platform_data *usbc_data) |
| { |
| int ret = 0; |
| u8 i2c_data = 0; |
| /* Unmask max77705 interrupt */ |
| ret = max77705_read_reg(usbc_data->i2c, 0x23, |
| &i2c_data); |
| if (ret) { |
| pr_err("%s fail to read muic reg\n", __func__); |
| return; |
| } |
| |
| i2c_data &= ~((1 << 3)); /* Unmask muic interrupt */ |
| max77705_write_reg(usbc_data->i2c, 0x23, |
| i2c_data); |
| } |
| static void max77705_usbc_mask_irq(struct max77705_usbc_platform_data *usbc_data) |
| { |
| int ret = 0; |
| u8 i2c_data = 0; |
| /* Unmask max77705 interrupt */ |
| ret = max77705_read_reg(usbc_data->i2c, 0x23, |
| &i2c_data); |
| if (ret) { |
| pr_err("%s fail to read muic reg\n", __func__); |
| return; |
| } |
| |
| i2c_data |= ((1 << 3)); /* Unmask muic interrupt */ |
| max77705_write_reg(usbc_data->i2c, 0x23, |
| i2c_data); |
| } |
| |
| static int pdic_handle_usb_external_notifier_notification(struct notifier_block *nb, |
| unsigned long action, void *data) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = g_usbc_data; |
| int ret = 0; |
| int enable = *(int *)data; |
| |
| pr_info("%s : action=%lu , enable=%d\n", __func__, action, enable); |
| switch (action) { |
| case EXTERNAL_NOTIFY_HOSTBLOCK_PRE: |
| if (enable) { |
| max77705_set_enable_alternate_mode(ALTERNATE_MODE_STOP); |
| if (usbpd_data->dp_is_connect) |
| max77705_dp_detach(usbpd_data); |
| } else { |
| if (usbpd_data->dp_is_connect) |
| max77705_dp_detach(usbpd_data); |
| } |
| break; |
| case EXTERNAL_NOTIFY_HOSTBLOCK_POST: |
| if (enable) { |
| } else { |
| max77705_set_enable_alternate_mode(ALTERNATE_MODE_START); |
| } |
| break; |
| case EXTERNAL_NOTIFY_DEVICEADD: |
| if (enable) { |
| usbpd_data->device_add = 1; |
| wake_up_interruptible(&usbpd_data->device_add_wait_q); |
| } |
| break; |
| case EXTERNAL_NOTIFY_MDMBLOCK_PRE: |
| if (enable && usbpd_data->dp_is_connect) { |
| usbpd_data->mdm_block = 1; |
| max77705_dp_detach(usbpd_data); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static void delayed_external_notifier_init(struct work_struct *work) |
| { |
| int ret = 0; |
| static int retry_count = 1; |
| int max_retry_count = 5; |
| struct max77705_usbc_platform_data *usbpd_data = g_usbc_data; |
| |
| pr_info("%s : %d = times!\n", __func__, retry_count); |
| |
| /* Register ccic handler to ccic notifier block list */ |
| ret = usb_external_notify_register(&usbpd_data->usb_external_notifier_nb, |
| pdic_handle_usb_external_notifier_notification, EXTERNAL_NOTIFY_DEV_PDIC); |
| if (ret < 0) { |
| pr_err("Manager notifier init time is %d.\n", retry_count); |
| if (retry_count++ != max_retry_count) |
| schedule_delayed_work(&usbpd_data->usb_external_notifier_register_work, msecs_to_jiffies(2000)); |
| else |
| pr_err("fail to init external notifier\n"); |
| } else |
| pr_info("%s : external notifier register done!\n", __func__); |
| } |
| |
| #if defined(CONFIG_SEC_FACTORY) |
| static void factory_check_abnormal_state(struct work_struct *work) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = g_usbc_data; |
| int state_cnt = usbpd_data->factory_mode.FAC_Abnormal_Repeat_State; |
| |
| msg_maxim("IN "); |
| |
| if (state_cnt >= FAC_ABNORMAL_REPEAT_STATE) { |
| msg_maxim("Notify the abnormal state [STATE] [ %d]", state_cnt); |
| max77705_ccic_event_work(usbpd_data, |
| CCIC_NOTIFY_DEV_CCIC, CCIC_NOTIFY_ID_FAC, 1, 0, 0); |
| } else |
| msg_maxim("[STATE] cnt : [%d]", state_cnt); |
| usbpd_data->factory_mode.FAC_Abnormal_Repeat_State = 0; |
| msg_maxim("OUT "); |
| } |
| |
| static void factory_check_normal_rid(struct work_struct *work) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = g_usbc_data; |
| int rid_cnt = usbpd_data->factory_mode.FAC_Abnormal_Repeat_RID; |
| |
| msg_maxim("IN "); |
| |
| if (rid_cnt >= FAC_ABNORMAL_REPEAT_RID) { |
| msg_maxim("Notify the abnormal state [RID] [ %d]", rid_cnt); |
| max77705_ccic_event_work(usbpd_data, |
| CCIC_NOTIFY_DEV_CCIC, CCIC_NOTIFY_ID_FAC, 1 << 1, 0, 0); |
| } else |
| msg_maxim("[RID] cnt : [%d]", rid_cnt); |
| |
| usbpd_data->factory_mode.FAC_Abnormal_Repeat_RID = 0; |
| msg_maxim("OUT "); |
| } |
| |
| void factory_execute_monitor(int type) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = g_usbc_data; |
| uint32_t state_cnt = usbpd_data->factory_mode.FAC_Abnormal_Repeat_State; |
| uint32_t rid_cnt = usbpd_data->factory_mode.FAC_Abnormal_Repeat_RID; |
| uint32_t rid0_cnt = usbpd_data->factory_mode.FAC_Abnormal_RID0; |
| |
| switch (type) { |
| case FAC_ABNORMAL_REPEAT_RID0: |
| if (!rid0_cnt) { |
| msg_maxim("Notify the abnormal state [RID0] [%d]!!", rid0_cnt); |
| usbpd_data->factory_mode.FAC_Abnormal_RID0++; |
| max77705_ccic_event_work(usbpd_data, |
| CCIC_NOTIFY_DEV_CCIC, CCIC_NOTIFY_ID_FAC, 1 << 2, 0, 0); |
| } else { |
| usbpd_data->factory_mode.FAC_Abnormal_RID0 = 0; |
| } |
| break; |
| case FAC_ABNORMAL_REPEAT_RID: |
| if (!rid_cnt) { |
| schedule_delayed_work(&usbpd_data->factory_rid_work, msecs_to_jiffies(1000)); |
| msg_maxim("start the factory_rid_work"); |
| } |
| usbpd_data->factory_mode.FAC_Abnormal_Repeat_RID++; |
| break; |
| case FAC_ABNORMAL_REPEAT_STATE: |
| if (!state_cnt) { |
| schedule_delayed_work(&usbpd_data->factory_state_work, msecs_to_jiffies(1000)); |
| msg_maxim("start the factory_state_work"); |
| } |
| usbpd_data->factory_mode.FAC_Abnormal_Repeat_State++; |
| break; |
| default: |
| msg_maxim("Never Calling [%d]", type); |
| break; |
| } |
| } |
| #endif |
| |
| #if defined(CONFIG_USB_AUDIO_ENHANCED_DETECT_TIME) |
| static struct kpp kpp_ta; |
| static struct kpp kpp_fg; |
| |
| static void remove_qos(void *req) |
| { |
| struct pm_qos_request *rq; |
| |
| rq = req; |
| if (pm_qos_request_active(rq)) |
| pm_qos_remove_request(rq); |
| else |
| msg_maxim("[PDIC Booster] is not activation"); |
| |
| } |
| |
| static void set_qos(void *req, int pm_qos_class, int val) |
| { |
| struct pm_qos_request *rq; |
| |
| rq = req; |
| if (val) { |
| if (pm_qos_request_active(rq)) { |
| msg_maxim("[PDIC Booster] update_req val:%d", val); |
| pm_qos_update_request(rq, val); |
| } else { |
| msg_maxim("[PDIC Booster] add_req class:%d val:%d", |
| pm_qos_class, val); |
| pm_qos_add_request(rq, pm_qos_class, val); |
| } |
| } else { |
| msg_maxim("[PDIC Booster] remove_qos\n"); |
| remove_qos(rq); |
| } |
| } |
| |
| void max77705_clk_booster_set(void *data, int on) |
| { |
| struct max77705_usbc_platform_data *usbpd_data = data; |
| |
| if (system_state < SYSTEM_RUNNING) { |
| msg_maxim("[PDIC Booster] %d is not ready", on); |
| return; |
| } |
| msg_maxim("[PDIC Booster] %d", on); |
| cancel_delayed_work_sync(&usbpd_data->acc_booster_off_work); |
| |
| if (on) { |
| set_qos(&usbpd_data->cpu1_qos, PM_QOS_CLUSTER1_FREQ_MIN, |
| PM_QOS_CLUSTER1_FREQ_MAX_DEFAULT_VALUE); |
| set_qos(&usbpd_data->cpu2_qos, PM_QOS_CLUSTER2_FREQ_MIN, |
| PM_QOS_CLUSTER2_FREQ_MAX_DEFAULT_VALUE); |
| set_qos(&usbpd_data->kfc_qos, PM_QOS_CLUSTER0_FREQ_MIN, |
| PM_QOS_CLUSTER0_FREQ_MAX_DEFAULT_VALUE); |
| set_qos(&usbpd_data->mif_qos, PM_QOS_BUS_THROUGHPUT, |
| PM_QOS_BUS_THROUGHPUT_MAX_DEFAULT_VALUE); |
| usbpd_data->set_booster = true; |
| |
| kpp_request(STUNE_TOPAPP, &kpp_ta, 2); |
| kpp_request(STUNE_FOREGROUND, &kpp_fg, 2); |
| |
| schedule_delayed_work(&usbpd_data->acc_booster_off_work, |
| msecs_to_jiffies(CLK_BOOSTER_OFF_WAIT_MS)); |
| } else { |
| if (usbpd_data->set_booster) { |
| schedule_delayed_work(&usbpd_data->acc_booster_off_work, |
| msecs_to_jiffies(0)); |
| } else |
| msg_maxim("[PDIC Booster]have already turned off"); |
| } |
| } |
| |
| void max77705_clk_booster_off(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_booster_off_work); |
| |
| msg_maxim("[PDIC Booster]+"); |
| usbpd_data->set_booster = false; |
| remove_qos(&usbpd_data->kfc_qos); |
| remove_qos(&usbpd_data->cpu1_qos); |
| remove_qos(&usbpd_data->cpu2_qos); |
| remove_qos(&usbpd_data->mif_qos); |
| kpp_request(STUNE_TOPAPP, &kpp_ta, 0); |
| kpp_request(STUNE_FOREGROUND, &kpp_fg, 0); |
| msg_maxim("[PDIC Booster]-"); |
| |
| } |
| #endif |
| |
| static int max77705_usbc_probe(struct platform_device *pdev) |
| { |
| struct max77705_dev *max77705 = dev_get_drvdata(pdev->dev.parent); |
| struct max77705_platform_data *pdata = dev_get_platdata(max77705->dev); |
| struct max77705_usbc_platform_data *usbc_data = NULL; |
| int ret; |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| pccic_data_t pccic_data; |
| pccic_sysfs_property_t pccic_sysfs_prop; |
| #endif |
| #if defined(CONFIG_DUAL_ROLE_USB_INTF) |
| struct dual_role_phy_desc *desc; |
| struct dual_role_phy_instance *dual_role; |
| #endif |
| #if defined(CONFIG_USB_HOST_NOTIFY) |
| struct otg_notify *o_notify = get_otg_notify(); |
| #endif |
| |
| msg_maxim("Probing : %d", max77705->irq); |
| usbc_data = kzalloc(sizeof(struct max77705_usbc_platform_data), GFP_KERNEL); |
| if (!usbc_data) |
| return -ENOMEM; |
| |
| g_usbc_data = usbc_data; |
| usbc_data->dev = &pdev->dev; |
| usbc_data->max77705 = max77705; |
| usbc_data->muic = max77705->muic; |
| usbc_data->charger = max77705->charger; |
| usbc_data->i2c = max77705->i2c; |
| usbc_data->max77705_data = pdata; |
| usbc_data->irq_base = pdata->irq_base; |
| |
| usbc_data->pd_data = kzalloc(sizeof(struct max77705_pd_data), GFP_KERNEL); |
| if (!usbc_data->pd_data) |
| return -ENOMEM; |
| |
| usbc_data->cc_data = kzalloc(sizeof(struct max77705_cc_data), GFP_KERNEL); |
| if (!usbc_data->cc_data) |
| return -ENOMEM; |
| |
| platform_set_drvdata(pdev, usbc_data); |
| |
| #if defined(CONFIG_CCIC_MAX77705_DEBUG) |
| mxim_debug_init(); |
| mxim_debug_set_i2c_client(usbc_data->muic); |
| #endif |
| |
| ret = sysfs_create_group(&max77705->dev->kobj, &max77705_attr_grp); |
| msg_maxim("created sysfs. ret=%d", ret); |
| |
| usbc_data->HW_Revision = 0x0; |
| usbc_data->FW_Revision = 0x0; |
| usbc_data->plug_attach_done = 0x0; |
| usbc_data->cc_data->current_pr = 0xFF; |
| usbc_data->pd_data->current_dr = 0xFF; |
| usbc_data->cc_data->current_vcon = 0xFF; |
| usbc_data->op_code_done = 0x0; |
| usbc_data->current_connstat = 0xFF; |
| usbc_data->prev_connstat = 0xFF; |
| usbc_data->usbc_cmd_queue.front = NULL; |
| usbc_data->usbc_cmd_queue.rear = NULL; |
| usbc_data->opcode_stamp = 0; |
| mutex_init(&usbc_data->op_lock); |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| pccic_data = devm_kzalloc(usbc_data->dev, sizeof(ccic_data_t), GFP_KERNEL); |
| pccic_sysfs_prop = devm_kzalloc(usbc_data->dev, sizeof(ccic_sysfs_property_t), GFP_KERNEL); |
| pccic_sysfs_prop->get_property = max77705_sysfs_get_local_prop; |
| pccic_sysfs_prop->set_property = max77705_sysfs_set_prop; |
| pccic_sysfs_prop->property_is_writeable = max77705_sysfs_is_writeable; |
| pccic_sysfs_prop->property_is_writeonly = max77705_sysfs_is_writeonly; |
| pccic_sysfs_prop->properties = max77705_sysfs_properties; |
| pccic_sysfs_prop->num_properties = ARRAY_SIZE(max77705_sysfs_properties); |
| pccic_data->ccic_syfs_prop = pccic_sysfs_prop; |
| pccic_data->drv_data = usbc_data; |
| pccic_data->name = "max77705"; |
| pccic_data->set_enable_alternate_mode = max77705_set_enable_alternate_mode; |
| ccic_core_register_chip(pccic_data); |
| usbc_data->vconn_en = 1; |
| usbc_data->cur_rid = RID_OPEN; |
| usbc_data->cc_pin_status = NO_DETERMINATION; |
| #if defined(CONFIG_DUAL_ROLE_USB_INTF) |
| usbc_data->power_role = DUAL_ROLE_PROP_PR_NONE; |
| desc = |
| devm_kzalloc(usbc_data->dev, |
| sizeof(struct dual_role_phy_desc), GFP_KERNEL); |
| if (!desc) { |
| pr_err("unable to allocate dual role descriptor\n"); |
| return -ENOMEM; |
| } |
| |
| desc->name = "otg_default"; |
| desc->supported_modes = DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP; |
| desc->get_property = max77705_dual_role_get_prop; |
| desc->set_property = max77705_dual_role_set_prop; |
| desc->properties = fusb_drp_properties; |
| desc->num_properties = ARRAY_SIZE(fusb_drp_properties); |
| desc->property_is_writeable = max77705_dual_role_is_writeable; |
| dual_role = |
| devm_dual_role_instance_register(usbc_data->dev, desc); |
| dual_role->drv_data = usbc_data; |
| usbc_data->dual_role = dual_role; |
| usbc_data->desc = desc; |
| init_completion(&usbc_data->reverse_completion); |
| #elif defined(CONFIG_TYPEC) |
| usbc_data->typec_cap.revision = USB_TYPEC_REV_1_2; |
| usbc_data->typec_cap.pd_revision = 0x300; |
| usbc_data->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; |
| usbc_data->typec_cap.pr_set = max77705_pr_set; |
| usbc_data->typec_cap.dr_set = max77705_dr_set; |
| usbc_data->typec_cap.port_type_set = max77705_port_type_set; |
| |
| usbc_data->typec_cap.type = TYPEC_PORT_DRP; |
| |
| usbc_data->typec_power_role = TYPEC_SINK;; |
| usbc_data->typec_try_state_change = TRY_ROLE_SWAP_NONE; |
| |
| usbc_data->port = typec_register_port(usbc_data->dev, &usbc_data->typec_cap); |
| if (IS_ERR(usbc_data->port)) |
| pr_err("unable to register typec_register_port\n"); |
| else |
| msg_maxim("success typec_register_port port=%pK", usbc_data->port); |
| init_completion(&usbc_data->typec_reverse_completion); |
| #endif |
| usbc_data->auto_vbus_en = false; |
| usbc_data->is_first_booting = 1; |
| usbc_data->pd_support = false; |
| usbc_data->ccrp_state = 0; |
| usbc_data->set_altmode = 0; |
| usbc_data->set_altmode_error = 0; |
| usbc_data->need_recover = false; |
| usbc_data->op_ctrl1_w = (BIT_CCSrcSnk | BIT_CCSnkSrc | BIT_CCDetEn); |
| usbc_data->srcccap_request_retry = false; |
| #if defined(CONFIG_USB_AUDIO_ENHANCED_DETECT_TIME) |
| usbc_data->set_booster = false; |
| #endif |
| #if defined(CONFIG_USB_HOST_NOTIFY) |
| send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0); |
| #endif |
| |
| #endif |
| init_completion(&usbc_data->op_completion); |
| init_completion(&usbc_data->ccic_sysfs_completion); |
| init_completion(&usbc_data->psrdy_wait); |
| usbc_data->op_wait_queue = create_singlethread_workqueue("op_wait"); |
| if (usbc_data->op_wait_queue == NULL) |
| return -ENOMEM; |
| usbc_data->op_send_queue = create_singlethread_workqueue("op_send"); |
| if (usbc_data->op_send_queue == NULL) |
| return -ENOMEM; |
| |
| INIT_WORK(&usbc_data->op_send_work, max77705_uic_op_send_work_func); |
| INIT_WORK(&usbc_data->cc_open_req_work, max77705_cc_open_work_func); |
| |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| /* Create a work queue for the ccic irq thread */ |
| usbc_data->ccic_wq |
| = create_singlethread_workqueue("ccic_irq_event"); |
| if (!usbc_data->ccic_wq) { |
| pr_err("%s failed to create work queue\n", __func__); |
| return -ENOMEM; |
| } |
| #endif |
| #if defined(CONFIG_SEC_FACTORY) |
| INIT_DELAYED_WORK(&usbc_data->factory_state_work, |
| factory_check_abnormal_state); |
| INIT_DELAYED_WORK(&usbc_data->factory_rid_work, |
| factory_check_normal_rid); |
| #endif |
| max77705_get_version_info(usbc_data); |
| max77705_init_irq_handler(usbc_data); |
| max77705_muic_probe(usbc_data); |
| max77705_cc_init(usbc_data); |
| max77705_pd_init(usbc_data); |
| max77705_write_reg(usbc_data->muic, REG_PD_INT_M, 0x1C); |
| max77705_write_reg(usbc_data->muic, REG_VDM_INT_M, 0xFF); |
| max77705_init_opcode(usbc_data, 0); |
| INIT_DELAYED_WORK(&usbc_data->vbus_hard_reset_work, |
| vbus_control_hard_reset); |
| /* turn on the VBUS automatically. */ |
| // max77705_usbc_enable_auto_vbus(usbc_data); |
| INIT_DELAYED_WORK(&usbc_data->acc_detach_work, max77705_acc_detach_check); |
| #if defined(CONFIG_USB_AUDIO_ENHANCED_DETECT_TIME) |
| INIT_DELAYED_WORK(&usbc_data->acc_booster_off_work, max77705_clk_booster_off); |
| #endif |
| ccic_register_switch_device(1); |
| INIT_DELAYED_WORK(&usbc_data->usb_external_notifier_register_work, |
| delayed_external_notifier_init); |
| |
| init_completion(&usbc_data->uvdm_longpacket_out_wait); |
| init_completion(&usbc_data->uvdm_longpacket_in_wait); |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| ccic_misc_init(pccic_data); |
| pccic_data->misc_dev->uvdm_read = max77705_sec_uvdm_in_request_message; |
| pccic_data->misc_dev->uvdm_write = max77705_sec_uvdm_out_request_message; |
| pccic_data->misc_dev->uvdm_ready = max77705_sec_uvdm_ready; |
| pccic_data->misc_dev->uvdm_close = max77705_sec_uvdm_close; |
| pccic_data->misc_dev->pps_control = max77705_sec_pps_control; |
| #endif |
| /* Register ccic handler to ccic notifier block list */ |
| ret = usb_external_notify_register(&usbc_data->usb_external_notifier_nb, |
| pdic_handle_usb_external_notifier_notification, EXTERNAL_NOTIFY_DEV_PDIC); |
| if (ret < 0) |
| schedule_delayed_work(&usbc_data->usb_external_notifier_register_work, msecs_to_jiffies(2000)); |
| else |
| pr_info("%s : external notifier register done!\n", __func__); |
| |
| max77705->cc_booting_complete = 1; |
| max77705_usbc_umask_irq(usbc_data); |
| init_waitqueue_head(&usbc_data->host_turn_on_wait_q); |
| init_waitqueue_head(&usbc_data->device_add_wait_q); |
| max77705_set_host_turn_on_event(0); |
| usbc_data->host_turn_on_wait_time = 3; |
| |
| usbc_data->cc_open_req = 1; |
| pdic_manual_ccopen_request(0); |
| |
| msg_maxim("probing Complete.."); |
| return 0; |
| } |
| |
| static int max77705_usbc_remove(struct platform_device *pdev) |
| { |
| struct max77705_usbc_platform_data *usbc_data = |
| platform_get_drvdata(pdev); |
| struct max77705_dev *max77705 = usbc_data->max77705; |
| |
| #if defined(CONFIG_CCIC_MAX77705_DEBUG) |
| mxim_debug_exit(); |
| #endif |
| sysfs_remove_group(&max77705->dev->kobj, &max77705_attr_grp); |
| mutex_destroy(&usbc_data->op_lock); |
| ccic_core_unregister_chip(); |
| #if defined(CONFIG_DUAL_ROLE_USB_INTF) |
| devm_dual_role_instance_unregister(usbc_data->dev, usbc_data->dual_role); |
| devm_kfree(usbc_data->dev, usbc_data->desc); |
| #elif defined(CONFIG_TYPEC) |
| typec_unregister_port(usbc_data->port); |
| #endif |
| ccic_register_switch_device(0); |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| ccic_misc_exit(); |
| #endif |
| usb_external_notify_unregister(&usbc_data->usb_external_notifier_nb); |
| max77705_muic_remove(usbc_data); |
| |
| wake_lock_destroy(&usbc_data->apcmd_wake_lock); |
| wake_lock_destroy(&usbc_data->sysmsg_wake_lock); |
| wake_lock_destroy(&usbc_data->pd_data->pdmsg_wake_lock); |
| wake_lock_destroy(&usbc_data->pd_data->datarole_wake_lock); |
| wake_lock_destroy(&usbc_data->pd_data->ssacc_wake_lock); |
| wake_lock_destroy(&usbc_data->pd_data->fct_id_wake_lock); |
| wake_lock_destroy(&usbc_data->cc_data->vconncop_wake_lock); |
| wake_lock_destroy(&usbc_data->cc_data->vsafe0v_wake_lock); |
| wake_lock_destroy(&usbc_data->cc_data->detabrt_wake_lock); |
| wake_lock_destroy(&usbc_data->cc_data->vconnsc_wake_lock); |
| wake_lock_destroy(&usbc_data->cc_data->ccpinstat_wake_lock); |
| wake_lock_destroy(&usbc_data->cc_data->ccistat_wake_lock); |
| wake_lock_destroy(&usbc_data->cc_data->ccvcnstat_wake_lock); |
| wake_lock_destroy(&usbc_data->cc_data->ccstat_wake_lock); |
| |
| free_irq(usbc_data->irq_apcmd, usbc_data); |
| free_irq(usbc_data->irq_sysmsg, usbc_data); |
| free_irq(usbc_data->irq_vdm0, usbc_data); |
| free_irq(usbc_data->irq_vdm1, usbc_data); |
| free_irq(usbc_data->irq_vdm2, usbc_data); |
| free_irq(usbc_data->irq_vdm3, usbc_data); |
| free_irq(usbc_data->irq_vdm4, usbc_data); |
| free_irq(usbc_data->irq_vdm5, usbc_data); |
| free_irq(usbc_data->irq_vdm6, usbc_data); |
| free_irq(usbc_data->irq_vdm7, usbc_data); |
| free_irq(usbc_data->pd_data->irq_pdmsg, usbc_data); |
| free_irq(usbc_data->pd_data->irq_datarole, usbc_data); |
| free_irq(usbc_data->pd_data->irq_ssacc, usbc_data); |
| free_irq(usbc_data->pd_data->irq_fct_id, usbc_data); |
| free_irq(usbc_data->cc_data->irq_vconncop, usbc_data); |
| free_irq(usbc_data->cc_data->irq_vsafe0v, usbc_data); |
| free_irq(usbc_data->cc_data->irq_detabrt, usbc_data); |
| free_irq(usbc_data->cc_data->irq_vconnsc, usbc_data); |
| free_irq(usbc_data->cc_data->irq_ccpinstat, usbc_data); |
| free_irq(usbc_data->cc_data->irq_ccistat, usbc_data); |
| free_irq(usbc_data->cc_data->irq_ccvcnstat, usbc_data); |
| free_irq(usbc_data->cc_data->irq_ccstat, usbc_data); |
| |
| kfree(usbc_data->cc_data); |
| kfree(usbc_data->pd_data); |
| kfree(usbc_data); |
| return 0; |
| } |
| |
| #if defined CONFIG_PM |
| static int max77705_usbc_suspend(struct device *dev) |
| { |
| struct max77705_usbc_platform_data *usbc_data = |
| dev_get_drvdata(dev); |
| |
| max77705_muic_suspend(usbc_data); |
| |
| return 0; |
| } |
| |
| static int max77705_usbc_resume(struct device *dev) |
| { |
| struct max77705_usbc_platform_data *usbc_data = |
| dev_get_drvdata(dev); |
| |
| max77705_muic_resume(usbc_data); |
| if (usbc_data->set_altmode_error) { |
| msg_maxim("set alternate mode"); |
| max77705_set_enable_alternate_mode |
| (usbc_data->set_altmode); |
| } |
| |
| return 0; |
| } |
| #else |
| #define max77705_usbc_suspend NULL |
| #define max77705_usbc_resume NULL |
| #endif |
| |
| static void max77705_usbc_shutdown(struct platform_device *pdev) |
| { |
| struct max77705_usbc_platform_data *usbc_data = |
| platform_get_drvdata(pdev); |
| struct device_node *np; |
| int gpio_dp_sw_oe; |
| u8 uic_int = 0; |
| |
| msg_maxim("max77705 usbc driver shutdown++++"); |
| if (!usbc_data->muic) { |
| msg_maxim("no max77705 i2c client"); |
| return; |
| } |
| usbc_data->shut_down = 1; |
| max77705_muic_shutdown(usbc_data); |
| max77705_usbc_mask_irq(usbc_data); |
| /* unmask */ |
| max77705_write_reg(usbc_data->muic, REG_PD_INT_M, 0xFF); |
| max77705_write_reg(usbc_data->muic, REG_CC_INT_M, 0xFF); |
| max77705_write_reg(usbc_data->muic, REG_UIC_INT_M, 0xFF); |
| max77705_write_reg(usbc_data->muic, REG_VDM_INT_M, 0xFF); |
| |
| /* send the reset command */ |
| if (usbc_data->current_connstat == WATER) |
| msg_maxim("no call the reset function(WATER STATE)"); |
| else if (usbc_data->cur_rid != RID_OPEN && usbc_data->cur_rid != RID_UNDEFINED) |
| msg_maxim("no call the reset function(RID)"); |
| else { |
| max77705_reset_ic(usbc_data); |
| if (usbc_data->dp_is_connect) { |
| pr_info("aux_sw_oe pin set to high\n"); |
| np = of_find_node_by_name(NULL, "displayport"); |
| gpio_dp_sw_oe = of_get_named_gpio(np, "dp,aux_sw_oe", 0); |
| gpio_direction_output(gpio_dp_sw_oe, 1); |
| } |
| max77705_write_reg(usbc_data->muic, REG_PD_INT_M, 0xFF); |
| max77705_write_reg(usbc_data->muic, REG_CC_INT_M, 0xFF); |
| max77705_write_reg(usbc_data->muic, REG_UIC_INT_M, 0xFF); |
| max77705_write_reg(usbc_data->muic, REG_VDM_INT_M, 0xFF); |
| max77705_read_reg(usbc_data->muic, |
| MAX77705_USBC_REG_UIC_INT, &uic_int); |
| } |
| msg_maxim("max77705 usbc driver shutdown----"); |
| } |
| |
| static SIMPLE_DEV_PM_OPS(max77705_usbc_pm_ops, max77705_usbc_suspend, |
| max77705_usbc_resume); |
| |
| static struct platform_driver max77705_usbc_driver = { |
| .driver = { |
| .name = "max77705-usbc", |
| .owner = THIS_MODULE, |
| #ifdef CONFIG_PM |
| .pm = &max77705_usbc_pm_ops, |
| #endif |
| }, |
| .shutdown = max77705_usbc_shutdown, |
| .probe = max77705_usbc_probe, |
| .remove = max77705_usbc_remove, |
| }; |
| |
| static int __init max77705_usbc_init(void) |
| { |
| msg_maxim("init"); |
| return platform_driver_register(&max77705_usbc_driver); |
| } |
| module_init(max77705_usbc_init); |
| |
| static void __exit max77705_usbc_exit(void) |
| { |
| platform_driver_unregister(&max77705_usbc_driver); |
| } |
| module_exit(max77705_usbc_exit); |
| |
| MODULE_DESCRIPTION("max77705 USBPD driver"); |
| MODULE_LICENSE("GPL"); |